1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright 2018 Advanced Micro Devices, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21 * OTHER DEALINGS IN THE SOFTWARE.
22 *
23 * Authors: AMD
24 *
25 */
26
27 #include <drm/drm_colorop.h>
28
29 #include "amdgpu.h"
30 #include "amdgpu_mode.h"
31 #include "amdgpu_dm.h"
32 #include "amdgpu_dm_color.h"
33 #include "amdgpu_dm_colorop.h"
34 #include "dc.h"
35 #include "modules/color/color_gamma.h"
36 #include "dm_helpers.h"
37
38
39 /**
40 * DOC: overview
41 *
42 * We have three types of color management in the AMD display driver.
43 * 1. the legacy &drm_crtc DEGAMMA, CTM, and GAMMA properties
44 * 2. AMD driver private color management on &drm_plane and &drm_crtc
45 * 3. AMD plane color pipeline
46 *
47 * The CRTC properties are the original color management. When they were
48 * implemented per-plane color management was not a thing yet. Because
49 * of that we could get away with plumbing the DEGAMMA and CTM
50 * properties to pre-blending HW functions. This is incompatible with
51 * per-plane color management, such as via the AMD private properties or
52 * the new drm_plane color pipeline. The only compatible CRTC property
53 * with per-plane color management is the GAMMA property as it is
54 * applied post-blending.
55 *
56 * The AMD driver private color management properties are only exposed
57 * when the kernel is built explicitly with -DAMD_PRIVATE_COLOR. They
58 * are temporary building blocks on the path to full-fledged &drm_plane
59 * and &drm_crtc color pipelines and lay the driver's groundwork for the
60 * color pipelines.
61 *
62 * The AMD plane color pipeline describes AMD's &drm_colorops via the
63 * &drm_plane's COLOR_PIPELINE property.
64 *
65 * drm_crtc Properties
66 * -------------------
67 *
68 * The DC interface to HW gives us the following color management blocks
69 * per pipe (surface):
70 *
71 * - Input gamma LUT (de-normalized)
72 * - Input CSC (normalized)
73 * - Surface degamma LUT (normalized)
74 * - Surface CSC (normalized)
75 * - Surface regamma LUT (normalized)
76 * - Output CSC (normalized)
77 *
78 * But these aren't a direct mapping to DRM color properties. The
79 * current DRM interface exposes CRTC degamma, CRTC CTM and CRTC regamma
80 * while our hardware is essentially giving:
81 *
82 * Plane CTM -> Plane degamma -> Plane CTM -> Plane regamma -> Plane CTM
83 *
84 * The input gamma LUT block isn't really applicable here since it
85 * operates on the actual input data itself rather than the HW fp
86 * representation. The input and output CSC blocks are technically
87 * available to use as part of the DC interface but are typically used
88 * internally by DC for conversions between color spaces. These could be
89 * blended together with user adjustments in the future but for now
90 * these should remain untouched.
91 *
92 * The pipe blending also happens after these blocks so we don't
93 * actually support any CRTC props with correct blending with multiple
94 * planes - but we can still support CRTC color management properties in
95 * DM in most single plane cases correctly with clever management of the
96 * DC interface in DM.
97 *
98 * As per DRM documentation, blocks should be in hardware bypass when
99 * their respective property is set to NULL. A linear DGM/RGM LUT should
100 * also considered as putting the respective block into bypass mode.
101 *
102 * This means that the following configuration is assumed to be the
103 * default:
104 *
105 * Plane DGM Bypass -> Plane CTM Bypass -> Plane RGM Bypass -> ... CRTC
106 * DGM Bypass -> CRTC CTM Bypass -> CRTC RGM Bypass
107 *
108 * AMD Private Color Management on drm_plane
109 * -----------------------------------------
110 *
111 * The AMD private color management properties on a &drm_plane are:
112 *
113 * - AMD_PLANE_DEGAMMA_LUT
114 * - AMD_PLANE_DEGAMMA_LUT_SIZE
115 * - AMD_PLANE_DEGAMMA_TF
116 * - AMD_PLANE_HDR_MULT
117 * - AMD_PLANE_CTM
118 * - AMD_PLANE_SHAPER_LUT
119 * - AMD_PLANE_SHAPER_LUT_SIZE
120 * - AMD_PLANE_SHAPER_TF
121 * - AMD_PLANE_LUT3D
122 * - AMD_PLANE_LUT3D_SIZE
123 * - AMD_PLANE_BLEND_LUT
124 * - AMD_PLANE_BLEND_LUT_SIZE
125 * - AMD_PLANE_BLEND_TF
126 *
127 * The AMD private color management property on a &drm_crtc is:
128 *
129 * - AMD_CRTC_REGAMMA_TF
130 *
131 * Use of these properties is discouraged.
132 *
133 * AMD plane color pipeline
134 * ------------------------
135 *
136 * The AMD &drm_plane color pipeline is advertised for DCN generations
137 * 3.0 and newer. It exposes these elements in this order:
138 *
139 * 1. 1D curve colorop
140 * 2. Multiplier
141 * 3. 3x4 CTM
142 * 4. 1D curve colorop
143 * 5. 1D LUT
144 * 6. 3D LUT
145 * 7. 1D curve colorop
146 * 8. 1D LUT
147 *
148 * The multiplier (#2) is a simple multiplier that is applied to all
149 * channels.
150 *
151 * The 3x4 CTM (#3) is a simple 3x4 matrix.
152 *
153 * #1, and #7 are non-linear to linear curves. #4 is a linear to
154 * non-linear curve. They support sRGB, PQ, and BT.709/BT.2020 EOTFs or
155 * their inverse.
156 *
157 * The 1D LUTs (#5 and #8) are plain 4096 entry LUTs.
158 *
159 * The 3DLUT (#6) is a tetrahedrally interpolated 17 cube LUT.
160 *
161 */
162
163 #define SDR_WHITE_LEVEL_INIT_VALUE 80
164
165 /**
166 * amdgpu_dm_init_color_mod - Initialize the color module.
167 *
168 * We're not using the full color module, only certain components.
169 * Only call setup functions for components that we need.
170 */
amdgpu_dm_init_color_mod(void)171 void amdgpu_dm_init_color_mod(void)
172 {
173 setup_x_points_distribution();
174 }
175 EXPORT_IF_KUNIT(amdgpu_dm_init_color_mod);
176
177 STATIC_IFN_KUNIT INLINE_IFN_KUNIT
amdgpu_dm_fixpt_from_s3132(__u64 x)178 struct fixed31_32 amdgpu_dm_fixpt_from_s3132(__u64 x)
179 {
180 struct fixed31_32 val;
181
182 /* If negative, convert to 2's complement. */
183 if (x & (1ULL << 63))
184 x = -(x & ~(1ULL << 63));
185
186 val.value = x;
187 return val;
188 }
189 EXPORT_IF_KUNIT(amdgpu_dm_fixpt_from_s3132);
190
191 #ifdef AMD_PRIVATE_COLOR
192 /* Pre-defined Transfer Functions (TF)
193 *
194 * AMD driver supports pre-defined mathematical functions for transferring
195 * between encoded values and optical/linear space. Depending on HW color caps,
196 * ROMs and curves built by the AMD color module support these transforms.
197 *
198 * The driver-specific color implementation exposes properties for pre-blending
199 * degamma TF, shaper TF (before 3D LUT), and blend(dpp.ogam) TF and
200 * post-blending regamma (mpc.ogam) TF. However, only pre-blending degamma
201 * supports ROM curves. AMD color module uses pre-defined coefficients to build
202 * curves for the other blocks. What can be done by each color block is
203 * described by struct dpp_color_capsand struct mpc_color_caps.
204 *
205 * AMD driver-specific color API exposes the following pre-defined transfer
206 * functions:
207 *
208 * - Identity: linear/identity relationship between pixel value and
209 * luminance value;
210 * - Gamma 2.2, Gamma 2.4, Gamma 2.6: pure power functions;
211 * - sRGB: 2.4: The piece-wise transfer function from IEC 61966-2-1:1999;
212 * - BT.709: has a linear segment in the bottom part and then a power function
213 * with a 0.45 (~1/2.22) gamma for the rest of the range; standardized by
214 * ITU-R BT.709-6;
215 * - PQ (Perceptual Quantizer): used for HDR display, allows luminance range
216 * capability of 0 to 10,000 nits; standardized by SMPTE ST 2084.
217 *
218 * The AMD color model is designed with an assumption that SDR (sRGB, BT.709,
219 * Gamma 2.2, etc.) peak white maps (normalized to 1.0 FP) to 80 nits in the PQ
220 * system. This has the implication that PQ EOTF (non-linear to linear) maps to
221 * [0.0..125.0] where 125.0 = 10,000 nits / 80 nits.
222 *
223 * Non-linear and linear forms are described in the table below:
224 *
225 * ┌───────────┬─────────────────────┬──────────────────────┐
226 * │ │ Non-linear │ Linear │
227 * ├───────────┼─────────────────────┼──────────────────────┤
228 * │ sRGB │ UNORM or [0.0, 1.0] │ [0.0, 1.0] │
229 * ├───────────┼─────────────────────┼──────────────────────┤
230 * │ BT709 │ UNORM or [0.0, 1.0] │ [0.0, 1.0] │
231 * ├───────────┼─────────────────────┼──────────────────────┤
232 * │ Gamma 2.x │ UNORM or [0.0, 1.0] │ [0.0, 1.0] │
233 * ├───────────┼─────────────────────┼──────────────────────┤
234 * │ PQ │ UNORM or FP16 CCCS* │ [0.0, 125.0] │
235 * ├───────────┼─────────────────────┼──────────────────────┤
236 * │ Identity │ UNORM or FP16 CCCS* │ [0.0, 1.0] or CCCS** │
237 * └───────────┴─────────────────────┴──────────────────────┘
238 * * CCCS: Windows canonical composition color space
239 * ** Respectively
240 *
241 * In the driver-specific API, color block names attached to TF properties
242 * suggest the intention regarding non-linear encoding pixel's luminance
243 * values. As some newer encodings don't use gamma curve, we make encoding and
244 * decoding explicit by defining an enum list of transfer functions supported
245 * in terms of EOTF and inverse EOTF, where:
246 *
247 * - EOTF (electro-optical transfer function): is the transfer function to go
248 * from the encoded value to an optical (linear) value. De-gamma functions
249 * traditionally do this.
250 * - Inverse EOTF (simply the inverse of the EOTF): is usually intended to go
251 * from an optical/linear space (which might have been used for blending)
252 * back to the encoded values. Gamma functions traditionally do this.
253 */
254 static const char * const
255 amdgpu_transfer_function_names[] = {
256 [AMDGPU_TRANSFER_FUNCTION_DEFAULT] = "Default",
257 [AMDGPU_TRANSFER_FUNCTION_IDENTITY] = "Identity",
258 [AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF] = "sRGB EOTF",
259 [AMDGPU_TRANSFER_FUNCTION_BT709_INV_OETF] = "BT.709 inv_OETF",
260 [AMDGPU_TRANSFER_FUNCTION_PQ_EOTF] = "PQ EOTF",
261 [AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF] = "Gamma 2.2 EOTF",
262 [AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF] = "Gamma 2.4 EOTF",
263 [AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF] = "Gamma 2.6 EOTF",
264 [AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF] = "sRGB inv_EOTF",
265 [AMDGPU_TRANSFER_FUNCTION_BT709_OETF] = "BT.709 OETF",
266 [AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF] = "PQ inv_EOTF",
267 [AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF] = "Gamma 2.2 inv_EOTF",
268 [AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF] = "Gamma 2.4 inv_EOTF",
269 [AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF] = "Gamma 2.6 inv_EOTF",
270 };
271
272 static const u32 amdgpu_eotf =
273 BIT(AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF) |
274 BIT(AMDGPU_TRANSFER_FUNCTION_BT709_INV_OETF) |
275 BIT(AMDGPU_TRANSFER_FUNCTION_PQ_EOTF) |
276 BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF) |
277 BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF) |
278 BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF);
279
280 static const u32 amdgpu_inv_eotf =
281 BIT(AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF) |
282 BIT(AMDGPU_TRANSFER_FUNCTION_BT709_OETF) |
283 BIT(AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF) |
284 BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF) |
285 BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF) |
286 BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF);
287
288 static struct drm_property *
amdgpu_create_tf_property(struct drm_device * dev,const char * name,u32 supported_tf)289 amdgpu_create_tf_property(struct drm_device *dev,
290 const char *name,
291 u32 supported_tf)
292 {
293 u32 transfer_functions = supported_tf |
294 BIT(AMDGPU_TRANSFER_FUNCTION_DEFAULT) |
295 BIT(AMDGPU_TRANSFER_FUNCTION_IDENTITY);
296 struct drm_prop_enum_list enum_list[AMDGPU_TRANSFER_FUNCTION_COUNT];
297 int i, len;
298
299 len = 0;
300 for (i = 0; i < AMDGPU_TRANSFER_FUNCTION_COUNT; i++) {
301 if ((transfer_functions & BIT(i)) == 0)
302 continue;
303
304 enum_list[len].type = i;
305 enum_list[len].name = amdgpu_transfer_function_names[i];
306 len++;
307 }
308
309 return drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
310 name, enum_list, len);
311 }
312
313 int
amdgpu_dm_create_color_properties(struct amdgpu_device * adev)314 amdgpu_dm_create_color_properties(struct amdgpu_device *adev)
315 {
316 struct drm_property *prop;
317
318 prop = drm_property_create(adev_to_drm(adev),
319 DRM_MODE_PROP_BLOB,
320 "AMD_PLANE_DEGAMMA_LUT", 0);
321 if (!prop)
322 return -ENOMEM;
323 adev->mode_info.plane_degamma_lut_property = prop;
324
325 prop = drm_property_create_range(adev_to_drm(adev),
326 DRM_MODE_PROP_IMMUTABLE,
327 "AMD_PLANE_DEGAMMA_LUT_SIZE",
328 0, UINT_MAX);
329 if (!prop)
330 return -ENOMEM;
331 adev->mode_info.plane_degamma_lut_size_property = prop;
332
333 prop = amdgpu_create_tf_property(adev_to_drm(adev),
334 "AMD_PLANE_DEGAMMA_TF",
335 amdgpu_eotf);
336 if (!prop)
337 return -ENOMEM;
338 adev->mode_info.plane_degamma_tf_property = prop;
339
340 prop = drm_property_create_range(adev_to_drm(adev),
341 0, "AMD_PLANE_HDR_MULT", 0, U64_MAX);
342 if (!prop)
343 return -ENOMEM;
344 adev->mode_info.plane_hdr_mult_property = prop;
345
346 prop = drm_property_create(adev_to_drm(adev),
347 DRM_MODE_PROP_BLOB,
348 "AMD_PLANE_CTM", 0);
349 if (!prop)
350 return -ENOMEM;
351 adev->mode_info.plane_ctm_property = prop;
352
353 prop = drm_property_create(adev_to_drm(adev),
354 DRM_MODE_PROP_BLOB,
355 "AMD_PLANE_SHAPER_LUT", 0);
356 if (!prop)
357 return -ENOMEM;
358 adev->mode_info.plane_shaper_lut_property = prop;
359
360 prop = drm_property_create_range(adev_to_drm(adev),
361 DRM_MODE_PROP_IMMUTABLE,
362 "AMD_PLANE_SHAPER_LUT_SIZE", 0, UINT_MAX);
363 if (!prop)
364 return -ENOMEM;
365 adev->mode_info.plane_shaper_lut_size_property = prop;
366
367 prop = amdgpu_create_tf_property(adev_to_drm(adev),
368 "AMD_PLANE_SHAPER_TF",
369 amdgpu_inv_eotf);
370 if (!prop)
371 return -ENOMEM;
372 adev->mode_info.plane_shaper_tf_property = prop;
373
374 prop = drm_property_create(adev_to_drm(adev),
375 DRM_MODE_PROP_BLOB,
376 "AMD_PLANE_LUT3D", 0);
377 if (!prop)
378 return -ENOMEM;
379 adev->mode_info.plane_lut3d_property = prop;
380
381 prop = drm_property_create_range(adev_to_drm(adev),
382 DRM_MODE_PROP_IMMUTABLE,
383 "AMD_PLANE_LUT3D_SIZE", 0, UINT_MAX);
384 if (!prop)
385 return -ENOMEM;
386 adev->mode_info.plane_lut3d_size_property = prop;
387
388 prop = drm_property_create(adev_to_drm(adev),
389 DRM_MODE_PROP_BLOB,
390 "AMD_PLANE_BLEND_LUT", 0);
391 if (!prop)
392 return -ENOMEM;
393 adev->mode_info.plane_blend_lut_property = prop;
394
395 prop = drm_property_create_range(adev_to_drm(adev),
396 DRM_MODE_PROP_IMMUTABLE,
397 "AMD_PLANE_BLEND_LUT_SIZE", 0, UINT_MAX);
398 if (!prop)
399 return -ENOMEM;
400 adev->mode_info.plane_blend_lut_size_property = prop;
401
402 prop = amdgpu_create_tf_property(adev_to_drm(adev),
403 "AMD_PLANE_BLEND_TF",
404 amdgpu_eotf);
405 if (!prop)
406 return -ENOMEM;
407 adev->mode_info.plane_blend_tf_property = prop;
408
409 prop = amdgpu_create_tf_property(adev_to_drm(adev),
410 "AMD_CRTC_REGAMMA_TF",
411 amdgpu_inv_eotf);
412 if (!prop)
413 return -ENOMEM;
414 adev->mode_info.regamma_tf_property = prop;
415
416 return 0;
417 }
418 #endif
419
420 /**
421 * __extract_blob_lut - Extracts the DRM lut and lut size from a blob.
422 * @blob: DRM color mgmt property blob
423 * @size: lut size
424 *
425 * Returns:
426 * DRM LUT or NULL
427 */
428 STATIC_IFN_KUNIT
429 const struct drm_color_lut *
__extract_blob_lut(const struct drm_property_blob * blob,uint32_t * size)430 __extract_blob_lut(const struct drm_property_blob *blob, uint32_t *size)
431 {
432 *size = blob ? drm_color_lut_size(blob) : 0;
433 return blob ? (struct drm_color_lut *)blob->data : NULL;
434 }
435 EXPORT_IF_KUNIT(__extract_blob_lut);
436
437 /**
438 * __extract_blob_lut32 - Extracts the DRM lut and lut size from a blob.
439 * @blob: DRM color mgmt property blob
440 * @size: lut size
441 *
442 * Returns:
443 * DRM LUT or NULL
444 */
445 STATIC_IFN_KUNIT
446 const struct drm_color_lut32 *
__extract_blob_lut32(const struct drm_property_blob * blob,uint32_t * size)447 __extract_blob_lut32(const struct drm_property_blob *blob, uint32_t *size)
448 {
449 *size = blob ? drm_color_lut32_size(blob) : 0;
450 return blob ? (struct drm_color_lut32 *)blob->data : NULL;
451 }
452 EXPORT_IF_KUNIT(__extract_blob_lut32);
453
454 /**
455 * __is_lut_linear - check if the given lut is a linear mapping of values
456 * @lut: given lut to check values
457 * @size: lut size
458 *
459 * It is considered linear if the lut represents:
460 * f(a) = (0xFF00/MAX_COLOR_LUT_ENTRIES-1)a; for integer a in [0,
461 * MAX_COLOR_LUT_ENTRIES)
462 *
463 * Returns:
464 * True if the given lut is a linear mapping of values, i.e. it acts like a
465 * bypass LUT. Otherwise, false.
466 */
467 STATIC_IFN_KUNIT
__is_lut_linear(const struct drm_color_lut * lut,uint32_t size)468 bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size)
469 {
470 int i;
471 uint32_t expected;
472 int delta;
473
474 /* A LUT with fewer than two entries can't be interpolated and would
475 * divide by zero below (size - 1); it can't be treated as linear.
476 */
477 if (size < 2)
478 return false;
479
480 for (i = 0; i < size; i++) {
481 /* All color values should equal */
482 if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue))
483 return false;
484
485 expected = i * MAX_DRM_LUT_VALUE / (size-1);
486
487 /* Allow a +/-1 error. */
488 delta = lut[i].red - expected;
489 if (delta < -1 || 1 < delta)
490 return false;
491 }
492 return true;
493 }
494 EXPORT_IF_KUNIT(__is_lut_linear);
495
496 /**
497 * __drm_lut_to_dc_gamma - convert the drm_color_lut to dc_gamma.
498 * @lut: DRM lookup table for color conversion
499 * @gamma: DC gamma to set entries
500 * @is_legacy: legacy or atomic gamma
501 *
502 * The conversion depends on the size of the lut - whether or not it's legacy.
503 */
504 STATIC_IFN_KUNIT
__drm_lut_to_dc_gamma(const struct drm_color_lut * lut,struct dc_gamma * gamma,bool is_legacy)505 void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut,
506 struct dc_gamma *gamma, bool is_legacy)
507 {
508 uint32_t r, g, b;
509 int i;
510
511 if (is_legacy) {
512 for (i = 0; i < MAX_COLOR_LEGACY_LUT_ENTRIES; i++) {
513 r = drm_color_lut_extract(lut[i].red, 16);
514 g = drm_color_lut_extract(lut[i].green, 16);
515 b = drm_color_lut_extract(lut[i].blue, 16);
516
517 gamma->entries.red[i] = dc_fixpt_from_int(r);
518 gamma->entries.green[i] = dc_fixpt_from_int(g);
519 gamma->entries.blue[i] = dc_fixpt_from_int(b);
520 }
521 return;
522 }
523
524 /* else */
525 for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) {
526 r = drm_color_lut_extract(lut[i].red, 16);
527 g = drm_color_lut_extract(lut[i].green, 16);
528 b = drm_color_lut_extract(lut[i].blue, 16);
529
530 gamma->entries.red[i] = dc_fixpt_from_fraction(r, MAX_DRM_LUT_VALUE);
531 gamma->entries.green[i] = dc_fixpt_from_fraction(g, MAX_DRM_LUT_VALUE);
532 gamma->entries.blue[i] = dc_fixpt_from_fraction(b, MAX_DRM_LUT_VALUE);
533 }
534 }
535 EXPORT_IF_KUNIT(__drm_lut_to_dc_gamma);
536
537 /**
538 * __drm_lut32_to_dc_gamma - convert the drm_color_lut to dc_gamma.
539 * @lut: DRM lookup table for color conversion
540 * @gamma: DC gamma to set entries
541 *
542 * The conversion depends on the size of the lut - whether or not it's legacy.
543 */
544 STATIC_IFN_KUNIT
__drm_lut32_to_dc_gamma(const struct drm_color_lut32 * lut,struct dc_gamma * gamma)545 void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, struct dc_gamma *gamma)
546 {
547 int i;
548
549 for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) {
550 gamma->entries.red[i] = dc_fixpt_from_fraction(lut[i].red, MAX_DRM_LUT32_VALUE);
551 gamma->entries.green[i] = dc_fixpt_from_fraction(lut[i].green, MAX_DRM_LUT32_VALUE);
552 gamma->entries.blue[i] = dc_fixpt_from_fraction(lut[i].blue, MAX_DRM_LUT32_VALUE);
553 }
554 }
555 EXPORT_IF_KUNIT(__drm_lut32_to_dc_gamma);
556
557 /**
558 * __drm_ctm_to_dc_matrix - converts a DRM CTM to a DC CSC float matrix
559 * @ctm: DRM color transformation matrix
560 * @matrix: DC CSC float matrix
561 *
562 * The matrix needs to be a 3x4 (12 entry) matrix.
563 */
564 STATIC_IFN_KUNIT
__drm_ctm_to_dc_matrix(const struct drm_color_ctm * ctm,struct fixed31_32 * matrix)565 void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
566 struct fixed31_32 *matrix)
567 {
568 int i;
569
570 /*
571 * DRM gives a 3x3 matrix, but DC wants 3x4. Assuming we're operating
572 * with homogeneous coordinates, augment the matrix with 0's.
573 *
574 * The format provided is S31.32, using signed-magnitude representation.
575 * Our fixed31_32 is also S31.32, but is using 2's complement. We have
576 * to convert from signed-magnitude to 2's complement.
577 */
578 for (i = 0; i < 12; i++) {
579 /* Skip 4th element */
580 if (i % 4 == 3) {
581 matrix[i] = dc_fixpt_zero;
582 continue;
583 }
584
585 /* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */
586 matrix[i] = amdgpu_dm_fixpt_from_s3132(ctm->matrix[i - (i / 4)]);
587 }
588 }
589 EXPORT_IF_KUNIT(__drm_ctm_to_dc_matrix);
590
591 /**
592 * __drm_ctm_3x4_to_dc_matrix - converts a DRM CTM 3x4 to a DC CSC float matrix
593 * @ctm: DRM color transformation matrix with 3x4 dimensions
594 * @matrix: DC CSC float matrix
595 *
596 * The matrix needs to be a 3x4 (12 entry) matrix.
597 */
598 STATIC_IFN_KUNIT
__drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 * ctm,struct fixed31_32 * matrix)599 void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm,
600 struct fixed31_32 *matrix)
601 {
602 int i;
603
604 /* The format provided is S31.32, using signed-magnitude representation.
605 * Our fixed31_32 is also S31.32, but is using 2's complement. We have
606 * to convert from signed-magnitude to 2's complement.
607 */
608 for (i = 0; i < 12; i++) {
609 /* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */
610 matrix[i] = amdgpu_dm_fixpt_from_s3132(ctm->matrix[i]);
611 }
612 }
613 EXPORT_IF_KUNIT(__drm_ctm_3x4_to_dc_matrix);
614
615 /**
616 * __set_legacy_tf - Calculates the legacy transfer function
617 * @func: transfer function
618 * @lut: lookup table that defines the color space
619 * @lut_size: size of respective lut
620 * @has_rom: if ROM can be used for hardcoded curve
621 *
622 * Only for sRGB input space
623 *
624 * Returns:
625 * 0 in case of success, -ENOMEM if fails
626 */
627 STATIC_IFN_KUNIT int
__set_legacy_tf(struct dc_transfer_func * func,const struct drm_color_lut * lut,uint32_t lut_size,bool has_rom)628 __set_legacy_tf(struct dc_transfer_func *func,
629 const struct drm_color_lut *lut, uint32_t lut_size,
630 bool has_rom)
631 {
632 struct dc_gamma *gamma = NULL;
633 struct calculate_buffer cal_buffer = {0};
634 bool res;
635
636 ASSERT(lut && lut_size == MAX_COLOR_LEGACY_LUT_ENTRIES);
637
638 cal_buffer.buffer_index = -1;
639
640 gamma = dc_create_gamma();
641 if (!gamma)
642 return -ENOMEM;
643
644 gamma->type = GAMMA_RGB_256;
645 gamma->num_entries = lut_size;
646 __drm_lut_to_dc_gamma(lut, gamma, true);
647
648 res = mod_color_calculate_regamma_params(func, gamma, true, has_rom,
649 NULL, &cal_buffer);
650
651 dc_gamma_release(&gamma);
652
653 return res ? 0 : -ENOMEM;
654 }
655 EXPORT_IF_KUNIT(__set_legacy_tf);
656
657 /**
658 * __set_output_tf - calculates the output transfer function based on expected input space.
659 * @func: transfer function
660 * @lut: lookup table that defines the color space
661 * @lut_size: size of respective lut
662 * @has_rom: if ROM can be used for hardcoded curve
663 *
664 * Returns:
665 * 0 in case of success. -ENOMEM if fails.
666 */
667 STATIC_IFN_KUNIT int
__set_output_tf(struct dc_transfer_func * func,const struct drm_color_lut * lut,uint32_t lut_size,bool has_rom)668 __set_output_tf(struct dc_transfer_func *func,
669 const struct drm_color_lut *lut, uint32_t lut_size,
670 bool has_rom)
671 {
672 struct dc_gamma *gamma = NULL;
673 struct calculate_buffer cal_buffer = {0};
674 bool res;
675
676 cal_buffer.buffer_index = -1;
677
678 if (lut_size) {
679 ASSERT(lut && lut_size == MAX_COLOR_LUT_ENTRIES);
680
681 gamma = dc_create_gamma();
682 if (!gamma)
683 return -ENOMEM;
684
685 gamma->num_entries = lut_size;
686 __drm_lut_to_dc_gamma(lut, gamma, false);
687 }
688
689 if (func->tf == TRANSFER_FUNCTION_LINEAR) {
690 /*
691 * Color module doesn't like calculating regamma params
692 * on top of a linear input. But degamma params can be used
693 * instead to simulate this.
694 */
695 if (gamma)
696 gamma->type = GAMMA_CUSTOM;
697 res = mod_color_calculate_degamma_params(NULL, func,
698 gamma, gamma != NULL);
699 } else {
700 /*
701 * Assume sRGB. The actual mapping will depend on whether the
702 * input was legacy or not.
703 */
704 if (gamma)
705 gamma->type = GAMMA_CS_TFM_1D;
706 res = mod_color_calculate_regamma_params(func, gamma, gamma != NULL,
707 has_rom, NULL, &cal_buffer);
708 }
709
710 if (gamma)
711 dc_gamma_release(&gamma);
712
713 return res ? 0 : -ENOMEM;
714 }
715 EXPORT_IF_KUNIT(__set_output_tf);
716
717 /**
718 * __set_output_tf_32 - calculates the output transfer function based on expected input space.
719 * @func: transfer function
720 * @lut: lookup table that defines the color space
721 * @lut_size: size of respective lut
722 * @has_rom: if ROM can be used for hardcoded curve
723 *
724 * Returns:
725 * 0 in case of success. -ENOMEM if fails.
726 */
727 STATIC_IFN_KUNIT int
__set_output_tf_32(struct dc_transfer_func * func,const struct drm_color_lut32 * lut,uint32_t lut_size,bool has_rom)728 __set_output_tf_32(struct dc_transfer_func *func,
729 const struct drm_color_lut32 *lut, uint32_t lut_size,
730 bool has_rom)
731 {
732 struct dc_gamma *gamma = NULL;
733 struct calculate_buffer cal_buffer = {0};
734 bool res;
735
736 cal_buffer.buffer_index = -1;
737
738 if (lut_size) {
739 gamma = dc_create_gamma();
740 if (!gamma)
741 return -ENOMEM;
742
743 gamma->num_entries = lut_size;
744 __drm_lut32_to_dc_gamma(lut, gamma);
745 }
746
747 if (func->tf == TRANSFER_FUNCTION_LINEAR) {
748 /*
749 * Color module doesn't like calculating regamma params
750 * on top of a linear input. But degamma params can be used
751 * instead to simulate this.
752 */
753 if (gamma)
754 gamma->type = GAMMA_CUSTOM;
755 res = mod_color_calculate_degamma_params(NULL, func,
756 gamma, gamma != NULL);
757 } else {
758 /*
759 * Assume sRGB. The actual mapping will depend on whether the
760 * input was legacy or not.
761 */
762 if (gamma)
763 gamma->type = GAMMA_CS_TFM_1D;
764 res = mod_color_calculate_regamma_params(func, gamma, gamma != NULL,
765 has_rom, NULL, &cal_buffer);
766 }
767
768 if (gamma)
769 dc_gamma_release(&gamma);
770
771 return res ? 0 : -ENOMEM;
772 }
773 EXPORT_IF_KUNIT(__set_output_tf_32);
774
__set_tf_bypass(struct dc_transfer_func * tf)775 STATIC_IFN_KUNIT void __set_tf_bypass(struct dc_transfer_func *tf)
776 {
777 tf->type = TF_TYPE_BYPASS;
778 tf->tf = TRANSFER_FUNCTION_LINEAR;
779 }
780 EXPORT_IF_KUNIT(__set_tf_bypass);
781
__set_tf_distributed_points(struct dc_transfer_func * tf,enum dc_transfer_func_predefined predefined_tf)782 STATIC_IFN_KUNIT void __set_tf_distributed_points(struct dc_transfer_func *tf,
783 enum dc_transfer_func_predefined predefined_tf)
784 {
785 tf->type = TF_TYPE_DISTRIBUTED_POINTS;
786 tf->tf = predefined_tf;
787 tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
788 }
789 EXPORT_IF_KUNIT(__set_tf_distributed_points);
790
amdgpu_dm_set_atomic_regamma(struct dc_transfer_func * out_tf,const struct drm_color_lut * regamma_lut,uint32_t regamma_size,bool has_rom,enum dc_transfer_func_predefined tf)791 STATIC_IFN_KUNIT int amdgpu_dm_set_atomic_regamma(struct dc_transfer_func *out_tf,
792 const struct drm_color_lut *regamma_lut,
793 uint32_t regamma_size, bool has_rom,
794 enum dc_transfer_func_predefined tf)
795 {
796 int ret = 0;
797
798 if (regamma_size || tf != TRANSFER_FUNCTION_LINEAR) {
799 /*
800 * CRTC RGM goes into RGM LUT.
801 *
802 * Note: there is no implicit sRGB regamma here. We are using
803 * degamma calculation from color module to calculate the curve
804 * from a linear base if gamma TF is not set. However, if gamma
805 * TF (!= Linear) and LUT are set at the same time, we will use
806 * regamma calculation, and the color module will combine the
807 * pre-defined TF and the custom LUT values into the LUT that's
808 * actually programmed.
809 */
810 __set_tf_distributed_points(out_tf, tf);
811 ret = __set_output_tf(out_tf, regamma_lut, regamma_size, has_rom);
812 } else {
813 /*
814 * No CRTC RGM means we can just put the block into bypass
815 * since we don't have any plane level adjustments using it.
816 */
817 __set_tf_bypass(out_tf);
818 }
819
820 return ret;
821 }
822 EXPORT_IF_KUNIT(amdgpu_dm_set_atomic_regamma);
823
824 /**
825 * __set_input_tf - calculates the input transfer function based on expected
826 * input space.
827 * @caps: dc color capabilities
828 * @func: transfer function
829 * @lut: lookup table that defines the color space
830 * @lut_size: size of respective lut.
831 *
832 * Returns:
833 * 0 in case of success. -ENOMEM if fails.
834 */
__set_input_tf(struct dc_color_caps * caps,struct dc_transfer_func * func,const struct drm_color_lut * lut,uint32_t lut_size)835 STATIC_IFN_KUNIT int __set_input_tf(struct dc_color_caps *caps,
836 struct dc_transfer_func *func,
837 const struct drm_color_lut *lut, uint32_t lut_size)
838 {
839 struct dc_gamma *gamma = NULL;
840 bool res;
841
842 if (lut_size) {
843 gamma = dc_create_gamma();
844 if (!gamma)
845 return -ENOMEM;
846
847 gamma->type = GAMMA_CUSTOM;
848 gamma->num_entries = lut_size;
849
850 __drm_lut_to_dc_gamma(lut, gamma, false);
851 }
852
853 res = mod_color_calculate_degamma_params(caps, func, gamma, gamma != NULL);
854
855 if (gamma)
856 dc_gamma_release(&gamma);
857
858 return res ? 0 : -ENOMEM;
859 }
860 EXPORT_IF_KUNIT(__set_input_tf);
861
862 /**
863 * __set_input_tf_32 - calculates the input transfer function based on expected
864 * input space.
865 * @caps: dc color capabilities
866 * @func: transfer function
867 * @lut: lookup table that defines the color space
868 * @lut_size: size of respective lut.
869 *
870 * Returns:
871 * 0 in case of success. -ENOMEM if fails.
872 */
__set_input_tf_32(struct dc_color_caps * caps,struct dc_transfer_func * func,const struct drm_color_lut32 * lut,uint32_t lut_size)873 STATIC_IFN_KUNIT int __set_input_tf_32(struct dc_color_caps *caps,
874 struct dc_transfer_func *func,
875 const struct drm_color_lut32 *lut, uint32_t lut_size)
876 {
877 struct dc_gamma *gamma = NULL;
878 bool res;
879
880 if (lut_size) {
881 gamma = dc_create_gamma();
882 if (!gamma)
883 return -ENOMEM;
884
885 gamma->type = GAMMA_CUSTOM;
886 gamma->num_entries = lut_size;
887
888 __drm_lut32_to_dc_gamma(lut, gamma);
889 }
890
891 res = mod_color_calculate_degamma_params(caps, func, gamma, gamma != NULL);
892
893 if (gamma)
894 dc_gamma_release(&gamma);
895
896 return res ? 0 : -ENOMEM;
897 }
898 EXPORT_IF_KUNIT(__set_input_tf_32);
899
900 STATIC_IFN_KUNIT
901 enum dc_transfer_func_predefined
amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf)902 amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf)
903 {
904 switch (tf) {
905 default:
906 case AMDGPU_TRANSFER_FUNCTION_DEFAULT:
907 case AMDGPU_TRANSFER_FUNCTION_IDENTITY:
908 return TRANSFER_FUNCTION_LINEAR;
909 case AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF:
910 case AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF:
911 return TRANSFER_FUNCTION_SRGB;
912 case AMDGPU_TRANSFER_FUNCTION_BT709_OETF:
913 case AMDGPU_TRANSFER_FUNCTION_BT709_INV_OETF:
914 return TRANSFER_FUNCTION_BT709;
915 case AMDGPU_TRANSFER_FUNCTION_PQ_EOTF:
916 case AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF:
917 return TRANSFER_FUNCTION_PQ;
918 case AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF:
919 case AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF:
920 return TRANSFER_FUNCTION_GAMMA22;
921 case AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF:
922 case AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF:
923 return TRANSFER_FUNCTION_GAMMA24;
924 case AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF:
925 case AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF:
926 return TRANSFER_FUNCTION_GAMMA26;
927 }
928 }
929 EXPORT_IF_KUNIT(amdgpu_tf_to_dc_tf);
930
931 STATIC_IFN_KUNIT
932 enum dc_transfer_func_predefined
amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf)933 amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf)
934 {
935 switch (tf) {
936 case DRM_COLOROP_1D_CURVE_SRGB_EOTF:
937 case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF:
938 return TRANSFER_FUNCTION_SRGB;
939 case DRM_COLOROP_1D_CURVE_PQ_125_EOTF:
940 case DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF:
941 return TRANSFER_FUNCTION_PQ;
942 case DRM_COLOROP_1D_CURVE_BT2020_INV_OETF:
943 case DRM_COLOROP_1D_CURVE_BT2020_OETF:
944 return TRANSFER_FUNCTION_BT709;
945 case DRM_COLOROP_1D_CURVE_GAMMA22:
946 case DRM_COLOROP_1D_CURVE_GAMMA22_INV:
947 return TRANSFER_FUNCTION_GAMMA22;
948 default:
949 return TRANSFER_FUNCTION_LINEAR;
950 }
951 }
952 EXPORT_IF_KUNIT(amdgpu_colorop_tf_to_dc_tf);
953
954 STATIC_IFN_KUNIT
__to_dc_lut3d_color(struct dc_rgb * rgb,const struct drm_color_lut lut,int bit_precision)955 void __to_dc_lut3d_color(struct dc_rgb *rgb,
956 const struct drm_color_lut lut,
957 int bit_precision)
958 {
959 rgb->red = drm_color_lut_extract(lut.red, bit_precision);
960 rgb->green = drm_color_lut_extract(lut.green, bit_precision);
961 rgb->blue = drm_color_lut_extract(lut.blue, bit_precision);
962 }
963 EXPORT_IF_KUNIT(__to_dc_lut3d_color);
964
965 STATIC_IFN_KUNIT
__drm_3dlut_to_dc_3dlut(const struct drm_color_lut * lut,uint32_t lut3d_size,struct tetrahedral_params * params,bool use_tetrahedral_9,int bit_depth)966 void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut,
967 uint32_t lut3d_size,
968 struct tetrahedral_params *params,
969 bool use_tetrahedral_9,
970 int bit_depth)
971 {
972 struct dc_rgb *lut0;
973 struct dc_rgb *lut1;
974 struct dc_rgb *lut2;
975 struct dc_rgb *lut3;
976 int lut_i, i;
977
978
979 if (use_tetrahedral_9) {
980 lut0 = params->tetrahedral_9.lut0;
981 lut1 = params->tetrahedral_9.lut1;
982 lut2 = params->tetrahedral_9.lut2;
983 lut3 = params->tetrahedral_9.lut3;
984 } else {
985 lut0 = params->tetrahedral_17.lut0;
986 lut1 = params->tetrahedral_17.lut1;
987 lut2 = params->tetrahedral_17.lut2;
988 lut3 = params->tetrahedral_17.lut3;
989 }
990
991 for (lut_i = 0, i = 0; i < lut3d_size - 4; lut_i++, i += 4) {
992 /*
993 * We should consider the 3D LUT RGB values are distributed
994 * along four arrays lut0-3 where the first sizes 1229 and the
995 * other 1228. The bit depth supported for 3dlut channel is
996 * 12-bit, but DC also supports 10-bit.
997 *
998 * TODO: improve color pipeline API to enable the userspace set
999 * bit depth and 3D LUT size/stride, as specified by VA-API.
1000 */
1001 __to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth);
1002 __to_dc_lut3d_color(&lut1[lut_i], lut[i + 1], bit_depth);
1003 __to_dc_lut3d_color(&lut2[lut_i], lut[i + 2], bit_depth);
1004 __to_dc_lut3d_color(&lut3[lut_i], lut[i + 3], bit_depth);
1005 }
1006 /* lut0 has 1229 points (lut_size/4 + 1) */
1007 __to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth);
1008 }
1009 EXPORT_IF_KUNIT(__drm_3dlut_to_dc_3dlut);
1010
1011 STATIC_IFN_KUNIT
__to_dc_lut3d_32_color(struct dc_rgb * rgb,const struct drm_color_lut32 lut,int bit_precision)1012 void __to_dc_lut3d_32_color(struct dc_rgb *rgb,
1013 const struct drm_color_lut32 lut,
1014 int bit_precision)
1015 {
1016 rgb->red = drm_color_lut32_extract(lut.red, bit_precision);
1017 rgb->green = drm_color_lut32_extract(lut.green, bit_precision);
1018 rgb->blue = drm_color_lut32_extract(lut.blue, bit_precision);
1019 }
1020 EXPORT_IF_KUNIT(__to_dc_lut3d_32_color);
1021
1022 STATIC_IFN_KUNIT
__drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 * lut,uint32_t lut3d_size,struct tetrahedral_params * params,bool use_tetrahedral_9,int bit_depth)1023 void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut,
1024 uint32_t lut3d_size,
1025 struct tetrahedral_params *params,
1026 bool use_tetrahedral_9,
1027 int bit_depth)
1028 {
1029 struct dc_rgb *lut0;
1030 struct dc_rgb *lut1;
1031 struct dc_rgb *lut2;
1032 struct dc_rgb *lut3;
1033 int lut_i, i;
1034
1035
1036 if (use_tetrahedral_9) {
1037 lut0 = params->tetrahedral_9.lut0;
1038 lut1 = params->tetrahedral_9.lut1;
1039 lut2 = params->tetrahedral_9.lut2;
1040 lut3 = params->tetrahedral_9.lut3;
1041 } else {
1042 lut0 = params->tetrahedral_17.lut0;
1043 lut1 = params->tetrahedral_17.lut1;
1044 lut2 = params->tetrahedral_17.lut2;
1045 lut3 = params->tetrahedral_17.lut3;
1046 }
1047
1048 for (lut_i = 0, i = 0; i < lut3d_size - 4; lut_i++, i += 4) {
1049 /*
1050 * We should consider the 3D LUT RGB values are distributed
1051 * along four arrays lut0-3 where the first sizes 1229 and the
1052 * other 1228. The bit depth supported for 3dlut channel is
1053 * 12-bit, but DC also supports 10-bit.
1054 *
1055 * TODO: improve color pipeline API to enable the userspace set
1056 * bit depth and 3D LUT size/stride, as specified by VA-API.
1057 */
1058 __to_dc_lut3d_32_color(&lut0[lut_i], lut[i], bit_depth);
1059 __to_dc_lut3d_32_color(&lut1[lut_i], lut[i + 1], bit_depth);
1060 __to_dc_lut3d_32_color(&lut2[lut_i], lut[i + 2], bit_depth);
1061 __to_dc_lut3d_32_color(&lut3[lut_i], lut[i + 3], bit_depth);
1062 }
1063 /* lut0 has 1229 points (lut_size/4 + 1) */
1064 __to_dc_lut3d_32_color(&lut0[lut_i], lut[i], bit_depth);
1065 }
1066 EXPORT_IF_KUNIT(__drm_3dlut32_to_dc_3dlut);
1067
1068 /* amdgpu_dm_atomic_lut3d - set DRM 3D LUT to DC stream
1069 * @drm_lut3d: user 3D LUT
1070 * @drm_lut3d_size: size of 3D LUT
1071 * @cm: DC Color Manager (includes 3D LUT)
1072 *
1073 * Map user 3D LUT data to DC 3D LUT and all necessary bits to program it
1074 * on DCN accordingly.
1075 */
amdgpu_dm_atomic_lut3d(const struct drm_color_lut * drm_lut3d,uint32_t drm_lut3d_size,struct dc_plane_cm * cm)1076 STATIC_IFN_KUNIT void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut3d,
1077 uint32_t drm_lut3d_size,
1078 struct dc_plane_cm *cm)
1079 {
1080 if (!drm_lut3d_size) {
1081 cm->lut3d_func.state.bits.initialized = 0;
1082 cm->flags.bits.lut3d_enable = 0;
1083 } else {
1084 /* Stride and bit depth are not programmable by API yet.
1085 * Therefore, only supports 17x17x17 3D LUT (12-bit).
1086 */
1087 cm->lut3d_func.lut_3d.use_tetrahedral_9 = false;
1088 cm->lut3d_func.lut_3d.use_12bits = true;
1089 cm->lut3d_func.state.bits.initialized = 1;
1090 cm->flags.bits.lut3d_enable = 1;
1091 __drm_3dlut_to_dc_3dlut(drm_lut3d, drm_lut3d_size, &cm->lut3d_func.lut_3d,
1092 cm->lut3d_func.lut_3d.use_tetrahedral_9,
1093 MAX_COLOR_3DLUT_BITDEPTH);
1094 }
1095 }
1096 EXPORT_IF_KUNIT(amdgpu_dm_atomic_lut3d);
1097
amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut * shaper_lut,bool has_rom,enum dc_transfer_func_predefined tf,uint32_t shaper_size,struct dc_plane_cm * cm)1098 STATIC_IFN_KUNIT int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *shaper_lut,
1099 bool has_rom,
1100 enum dc_transfer_func_predefined tf,
1101 uint32_t shaper_size,
1102 struct dc_plane_cm *cm)
1103 {
1104 int ret = 0;
1105
1106 if (shaper_size || tf != TRANSFER_FUNCTION_LINEAR) {
1107 /*
1108 * If user shaper LUT is set, we assume a linear color space
1109 * (linearized by degamma 1D LUT or not).
1110 */
1111 __set_tf_distributed_points(&cm->shaper_func, tf);
1112 cm->flags.bits.shaper_enable = 1;
1113
1114 ret = __set_output_tf(&cm->shaper_func, shaper_lut, shaper_size, has_rom);
1115 } else {
1116 __set_tf_bypass(&cm->shaper_func);
1117 cm->flags.bits.shaper_enable = 0;
1118 }
1119
1120 return ret;
1121 }
1122 EXPORT_IF_KUNIT(amdgpu_dm_atomic_shaper_lut);
1123
amdgpu_dm_atomic_blend_lut(const struct drm_color_lut * blend_lut,bool has_rom,enum dc_transfer_func_predefined tf,uint32_t blend_size,struct dc_plane_cm * cm)1124 STATIC_IFN_KUNIT int amdgpu_dm_atomic_blend_lut(const struct drm_color_lut *blend_lut,
1125 bool has_rom,
1126 enum dc_transfer_func_predefined tf,
1127 uint32_t blend_size,
1128 struct dc_plane_cm *cm)
1129 {
1130 int ret = 0;
1131
1132 if (blend_size || tf != TRANSFER_FUNCTION_LINEAR) {
1133 /*
1134 * DRM plane gamma LUT or TF means we are linearizing color
1135 * space before blending (similar to degamma programming). As
1136 * we don't have hardcoded curve support, or we use AMD color
1137 * module to fill the parameters that will be translated to HW
1138 * points.
1139 */
1140 __set_tf_distributed_points(&cm->blend_func, tf);
1141 cm->flags.bits.blend_enable = 1;
1142
1143 ret = __set_input_tf(NULL, &cm->blend_func, blend_lut, blend_size);
1144 } else {
1145 __set_tf_bypass(&cm->blend_func);
1146 cm->flags.bits.blend_enable = 0;
1147 }
1148
1149 return ret;
1150 }
1151 EXPORT_IF_KUNIT(amdgpu_dm_atomic_blend_lut);
1152
1153 /**
1154 * amdgpu_dm_verify_lut3d_size - verifies if 3D LUT is supported and if user
1155 * shaper and 3D LUTs match the hw supported size
1156 * @adev: amdgpu device
1157 * @plane_state: the DRM plane state
1158 *
1159 * Verifies if pre-blending (DPP) 3D LUT is supported by the HW (DCN 2.0 or
1160 * newer) and if the user shaper and 3D LUTs match the supported size.
1161 *
1162 * Returns:
1163 * 0 on success. -EINVAL if lut size are invalid.
1164 */
amdgpu_dm_verify_lut3d_size(struct amdgpu_device * adev,struct drm_plane_state * plane_state)1165 int amdgpu_dm_verify_lut3d_size(struct amdgpu_device *adev,
1166 struct drm_plane_state *plane_state)
1167 {
1168 struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
1169 const struct drm_color_lut *shaper = NULL, *lut3d = NULL;
1170 uint32_t exp_size, size, dim_size = MAX_COLOR_3DLUT_SIZE;
1171 bool has_3dlut = adev->dm.dc->caps.color.dpp.hw_3d_lut || adev->dm.dc->caps.color.mpc.preblend;
1172
1173 /* shaper LUT is only available if 3D LUT color caps */
1174 exp_size = has_3dlut ? MAX_COLOR_LUT_ENTRIES : 0;
1175 shaper = __extract_blob_lut(dm_plane_state->shaper_lut, &size);
1176
1177 if (shaper && size != exp_size) {
1178 drm_dbg(&adev->ddev,
1179 "Invalid Shaper LUT size. Should be %u but got %u.\n",
1180 exp_size, size);
1181 return -EINVAL;
1182 }
1183
1184 /* The number of 3D LUT entries is the dimension size cubed */
1185 exp_size = has_3dlut ? dim_size * dim_size * dim_size : 0;
1186 lut3d = __extract_blob_lut(dm_plane_state->lut3d, &size);
1187
1188 if (lut3d && size != exp_size) {
1189 drm_dbg(&adev->ddev,
1190 "Invalid 3D LUT size. Should be %u but got %u.\n",
1191 exp_size, size);
1192 return -EINVAL;
1193 }
1194
1195 return 0;
1196 }
1197 EXPORT_IF_KUNIT(amdgpu_dm_verify_lut3d_size);
1198
1199 /**
1200 * amdgpu_dm_verify_lut_sizes - verifies if DRM luts match the hw supported sizes
1201 * @crtc_state: the DRM CRTC state
1202 *
1203 * Verifies that the Degamma and Gamma LUTs attached to the &crtc_state
1204 * are of the expected size.
1205 *
1206 * Returns:
1207 * 0 on success. -EINVAL if any lut sizes are invalid.
1208 */
amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state * crtc_state)1209 int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state)
1210 {
1211 const struct drm_color_lut *lut = NULL;
1212 uint32_t size = 0;
1213
1214 lut = __extract_blob_lut(crtc_state->degamma_lut, &size);
1215 if (lut && size != MAX_COLOR_LUT_ENTRIES) {
1216 DRM_DEBUG_DRIVER(
1217 "Invalid Degamma LUT size. Should be %u but got %u.\n",
1218 MAX_COLOR_LUT_ENTRIES, size);
1219 return -EINVAL;
1220 }
1221
1222 lut = __extract_blob_lut(crtc_state->gamma_lut, &size);
1223 if (lut && size != MAX_COLOR_LUT_ENTRIES &&
1224 size != MAX_COLOR_LEGACY_LUT_ENTRIES) {
1225 DRM_DEBUG_DRIVER(
1226 "Invalid Gamma LUT size. Should be %u (or %u for legacy) but got %u.\n",
1227 MAX_COLOR_LUT_ENTRIES, MAX_COLOR_LEGACY_LUT_ENTRIES,
1228 size);
1229 return -EINVAL;
1230 }
1231
1232 return 0;
1233 }
1234 EXPORT_IF_KUNIT(amdgpu_dm_verify_lut_sizes);
1235
1236 /**
1237 * amdgpu_dm_check_crtc_color_mgmt: Check if DRM color props are programmable by DC.
1238 * @crtc: amdgpu_dm crtc state
1239 * @check_only: only check color state without update dc stream
1240 *
1241 * This function just verifies CRTC LUT sizes, if there is enough space for
1242 * output transfer function and if its parameters can be calculated by AMD
1243 * color module. It also adjusts some settings for programming CRTC degamma at
1244 * plane stage, using plane DGM block.
1245 *
1246 * The RGM block is typically more fully featured and accurate across
1247 * all ASICs - DCE can't support a custom non-linear CRTC DGM.
1248 *
1249 * For supporting both plane level color management and CRTC level color
1250 * management at once we have to either restrict the usage of some CRTC
1251 * properties or blend adjustments together.
1252 *
1253 * Returns:
1254 * 0 on success. Error code if validation fails.
1255 */
1256
amdgpu_dm_check_crtc_color_mgmt(struct dm_crtc_state * crtc,bool check_only)1257 int amdgpu_dm_check_crtc_color_mgmt(struct dm_crtc_state *crtc,
1258 bool check_only)
1259 {
1260 struct dc_stream_state *stream = crtc->stream;
1261 struct amdgpu_device *adev = drm_to_adev(crtc->base.state->dev);
1262 bool has_rom = adev->asic_type <= CHIP_RAVEN;
1263 struct dc_transfer_func *out_tf;
1264 const struct drm_color_lut *degamma_lut, *regamma_lut;
1265 uint32_t degamma_size, regamma_size;
1266 bool has_regamma, has_degamma;
1267 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_LINEAR;
1268 bool is_legacy;
1269 int r;
1270
1271 tf = amdgpu_tf_to_dc_tf(crtc->regamma_tf);
1272
1273 r = amdgpu_dm_verify_lut_sizes(&crtc->base);
1274 if (r)
1275 return r;
1276
1277 degamma_lut = __extract_blob_lut(crtc->base.degamma_lut, °amma_size);
1278 regamma_lut = __extract_blob_lut(crtc->base.gamma_lut, ®amma_size);
1279
1280 has_degamma =
1281 degamma_lut && !__is_lut_linear(degamma_lut, degamma_size);
1282
1283 has_regamma =
1284 regamma_lut && !__is_lut_linear(regamma_lut, regamma_size);
1285
1286 is_legacy = regamma_size == MAX_COLOR_LEGACY_LUT_ENTRIES;
1287
1288 /* Reset all adjustments. */
1289 crtc->cm_has_degamma = false;
1290 crtc->cm_is_degamma_srgb = false;
1291
1292 if (check_only) {
1293 out_tf = kvzalloc_obj(*out_tf);
1294 if (!out_tf)
1295 return -ENOMEM;
1296 } else {
1297 out_tf = &stream->out_transfer_func;
1298 }
1299
1300 /* Setup regamma and degamma. */
1301 if (is_legacy) {
1302 /*
1303 * Legacy regamma forces us to use the sRGB RGM as a base.
1304 * This also means we can't use linear DGM since DGM needs
1305 * to use sRGB as a base as well, resulting in incorrect CRTC
1306 * DGM and CRTC CTM.
1307 *
1308 * TODO: Just map this to the standard regamma interface
1309 * instead since this isn't really right. One of the cases
1310 * where this setup currently fails is trying to do an
1311 * inverse color ramp in legacy userspace.
1312 */
1313 crtc->cm_is_degamma_srgb = true;
1314 out_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1315 out_tf->tf = TRANSFER_FUNCTION_SRGB;
1316 /*
1317 * Note: although we pass has_rom as parameter here, we never
1318 * actually use ROM because the color module only takes the ROM
1319 * path if transfer_func->type == PREDEFINED.
1320 *
1321 * See more in mod_color_calculate_regamma_params()
1322 */
1323 r = __set_legacy_tf(out_tf, regamma_lut,
1324 regamma_size, has_rom);
1325 } else {
1326 regamma_size = has_regamma ? regamma_size : 0;
1327 r = amdgpu_dm_set_atomic_regamma(out_tf, regamma_lut,
1328 regamma_size, has_rom, tf);
1329 }
1330
1331 /*
1332 * CRTC DGM goes into DGM LUT. It would be nice to place it
1333 * into the RGM since it's a more featured block but we'd
1334 * have to place the CTM in the OCSC in that case.
1335 */
1336 crtc->cm_has_degamma = has_degamma;
1337 if (check_only)
1338 kvfree(out_tf);
1339
1340 return r;
1341 }
1342 EXPORT_IF_KUNIT(amdgpu_dm_check_crtc_color_mgmt);
1343
1344 /**
1345 * amdgpu_dm_update_crtc_color_mgmt: Maps DRM color management to DC stream.
1346 * @crtc: amdgpu_dm crtc state
1347 *
1348 * With no plane level color management properties we're free to use any
1349 * of the HW blocks as long as the CRTC CTM always comes before the
1350 * CRTC RGM and after the CRTC DGM.
1351 *
1352 * - The CRTC RGM block will be placed in the RGM LUT block if it is non-linear.
1353 * - The CRTC DGM block will be placed in the DGM LUT block if it is non-linear.
1354 * - The CRTC CTM will be placed in the gamut remap block if it is non-linear.
1355 *
1356 * The RGM block is typically more fully featured and accurate across
1357 * all ASICs - DCE can't support a custom non-linear CRTC DGM.
1358 *
1359 * For supporting both plane level color management and CRTC level color
1360 * management at once we have to either restrict the usage of CRTC properties
1361 * or blend adjustments together.
1362 *
1363 * Returns:
1364 * 0 on success. Error code if setup fails.
1365 */
amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state * crtc)1366 int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc)
1367 {
1368 struct dc_stream_state *stream = crtc->stream;
1369 struct drm_color_ctm *ctm = NULL;
1370 int ret;
1371
1372 ret = amdgpu_dm_check_crtc_color_mgmt(crtc, false);
1373 if (ret)
1374 return ret;
1375
1376 /* Setup CRTC CTM. */
1377 if (crtc->base.ctm) {
1378 ctm = (struct drm_color_ctm *)crtc->base.ctm->data;
1379
1380 /*
1381 * Gamut remapping must be used for gamma correction
1382 * since it comes before the regamma correction.
1383 *
1384 * OCSC could be used for gamma correction, but we'd need to
1385 * blend the adjustments together with the required output
1386 * conversion matrix - so just use the gamut remap block
1387 * for now.
1388 */
1389 __drm_ctm_to_dc_matrix(ctm, stream->gamut_remap_matrix.matrix);
1390
1391 stream->gamut_remap_matrix.enable_remap = true;
1392 stream->csc_color_matrix.enable_adjustment = false;
1393 } else {
1394 /* Bypass CTM. */
1395 stream->gamut_remap_matrix.enable_remap = false;
1396 stream->csc_color_matrix.enable_adjustment = false;
1397 }
1398
1399 return 0;
1400 }
1401 EXPORT_IF_KUNIT(amdgpu_dm_update_crtc_color_mgmt);
1402
1403 static int
map_crtc_degamma_to_dc_plane(struct dm_crtc_state * crtc,struct dc_plane_state * dc_plane_state,struct dc_color_caps * caps)1404 map_crtc_degamma_to_dc_plane(struct dm_crtc_state *crtc,
1405 struct dc_plane_state *dc_plane_state,
1406 struct dc_color_caps *caps)
1407 {
1408 const struct drm_color_lut *degamma_lut;
1409 enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1410 uint32_t degamma_size;
1411 int r;
1412
1413 /* Get the correct base transfer function for implicit degamma. */
1414 switch (dc_plane_state->format) {
1415 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr:
1416 case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb:
1417 /* DC doesn't have a transfer function for BT601 specifically. */
1418 tf = TRANSFER_FUNCTION_BT709;
1419 break;
1420 default:
1421 break;
1422 }
1423
1424 if (crtc->cm_has_degamma) {
1425 degamma_lut = __extract_blob_lut(crtc->base.degamma_lut,
1426 °amma_size);
1427 ASSERT(degamma_size == MAX_COLOR_LUT_ENTRIES);
1428
1429 dc_plane_state->in_transfer_func.type = TF_TYPE_DISTRIBUTED_POINTS;
1430
1431 /*
1432 * This case isn't fully correct, but also fairly
1433 * uncommon. This is userspace trying to use a
1434 * legacy gamma LUT + atomic degamma LUT
1435 * at the same time.
1436 *
1437 * Legacy gamma requires the input to be in linear
1438 * space, so that means we need to apply an sRGB
1439 * degamma. But color module also doesn't support
1440 * a user ramp in this case so the degamma will
1441 * be lost.
1442 *
1443 * Even if we did support it, it's still not right:
1444 *
1445 * Input -> CRTC DGM -> sRGB DGM -> CRTC CTM ->
1446 * sRGB RGM -> CRTC RGM -> Output
1447 *
1448 * The CSC will be done in the wrong space since
1449 * we're applying an sRGB DGM on top of the CRTC
1450 * DGM.
1451 *
1452 * TODO: Don't use the legacy gamma interface and just
1453 * map these to the atomic one instead.
1454 */
1455 if (crtc->cm_is_degamma_srgb)
1456 dc_plane_state->in_transfer_func.tf = tf;
1457 else
1458 dc_plane_state->in_transfer_func.tf =
1459 TRANSFER_FUNCTION_LINEAR;
1460
1461 r = __set_input_tf(caps, &dc_plane_state->in_transfer_func,
1462 degamma_lut, degamma_size);
1463 if (r)
1464 return r;
1465 } else {
1466 /*
1467 * For legacy gamma support we need the regamma input
1468 * in linear space. Assume that the input is sRGB.
1469 */
1470 dc_plane_state->in_transfer_func.type = TF_TYPE_PREDEFINED;
1471 dc_plane_state->in_transfer_func.tf = tf;
1472
1473 if (tf != TRANSFER_FUNCTION_SRGB &&
1474 !mod_color_calculate_degamma_params(caps,
1475 &dc_plane_state->in_transfer_func,
1476 NULL, false))
1477 return -ENOMEM;
1478 }
1479
1480 return 0;
1481 }
1482
1483 static int
__set_dm_plane_degamma(struct drm_plane_state * plane_state,struct dc_plane_state * dc_plane_state,struct dc_color_caps * color_caps)1484 __set_dm_plane_degamma(struct drm_plane_state *plane_state,
1485 struct dc_plane_state *dc_plane_state,
1486 struct dc_color_caps *color_caps)
1487 {
1488 struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
1489 const struct drm_color_lut *degamma_lut;
1490 enum amdgpu_transfer_function tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
1491 uint32_t degamma_size;
1492 bool has_degamma_lut, is_subsampled_format;
1493 int ret;
1494
1495 degamma_lut = __extract_blob_lut(dm_plane_state->degamma_lut,
1496 °amma_size);
1497
1498 if (degamma_lut && degamma_size != MAX_COLOR_LUT_ENTRIES) {
1499 drm_dbg(plane_state->state->dev,
1500 "Invalid Plane Degamma LUT size. Should be %u but got %u.\n",
1501 MAX_COLOR_LUT_ENTRIES, degamma_size);
1502 return -EINVAL;
1503 }
1504
1505 has_degamma_lut = degamma_lut &&
1506 !__is_lut_linear(degamma_lut, degamma_size);
1507
1508 tf = dm_plane_state->degamma_tf;
1509
1510 /* If we don't have plane degamma LUT nor TF to set on DC, we have
1511 * nothing to do here, return.
1512 */
1513 if (!has_degamma_lut && tf == AMDGPU_TRANSFER_FUNCTION_DEFAULT)
1514 return -EINVAL;
1515
1516 dc_plane_state->in_transfer_func.tf = amdgpu_tf_to_dc_tf(tf);
1517
1518 if (has_degamma_lut) {
1519 ASSERT(degamma_size == MAX_COLOR_LUT_ENTRIES);
1520
1521 dc_plane_state->in_transfer_func.type =
1522 TF_TYPE_DISTRIBUTED_POINTS;
1523
1524 ret = __set_input_tf(color_caps, &dc_plane_state->in_transfer_func,
1525 degamma_lut, degamma_size);
1526 if (ret)
1527 return ret;
1528 } else {
1529 /* Check if format requires post-scale color processing (subsampled formats) */
1530 is_subsampled_format = (dc_plane_state->format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN &&
1531 dc_plane_state->format < SURFACE_PIXEL_FORMAT_SUBSAMPLE_END);
1532
1533 dc_plane_state->in_transfer_func.type = TF_TYPE_PREDEFINED;
1534
1535 if (!mod_color_calculate_degamma_params(color_caps,
1536 &dc_plane_state->in_transfer_func,
1537 NULL,
1538 is_subsampled_format)) {
1539 drm_err(plane_state->state->dev,
1540 "Failed to calculate degamma params.\n");
1541 return -ENOMEM;
1542 }
1543 }
1544 return 0;
1545 }
1546
1547 STATIC_IFN_KUNIT int
__set_colorop_in_tf_1d_curve(struct dc_plane_state * dc_plane_state,struct drm_colorop_state * colorop_state)1548 __set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state,
1549 struct drm_colorop_state *colorop_state)
1550 {
1551 struct dc_transfer_func *tf = &dc_plane_state->in_transfer_func;
1552 struct drm_colorop *colorop = colorop_state->colorop;
1553 struct drm_device *drm = colorop->dev;
1554
1555 if (colorop->type != DRM_COLOROP_1D_CURVE)
1556 return -EINVAL;
1557
1558 if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_degam_tfs))
1559 return -EINVAL;
1560
1561 if (colorop_state->bypass) {
1562 __set_tf_bypass(tf);
1563 return 0;
1564 }
1565
1566 drm_dbg(drm, "Degamma colorop with ID: %d\n", colorop->base.id);
1567
1568 tf->type = TF_TYPE_PREDEFINED;
1569 tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
1570
1571 return 0;
1572 }
1573 EXPORT_IF_KUNIT(__set_colorop_in_tf_1d_curve);
1574
1575 STATIC_IFN_KUNIT int
__set_dm_plane_colorop_degamma(struct drm_plane_state * plane_state,struct dc_plane_state * dc_plane_state,struct drm_colorop * colorop)1576 __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state,
1577 struct dc_plane_state *dc_plane_state,
1578 struct drm_colorop *colorop)
1579 {
1580 struct drm_colorop *old_colorop;
1581 struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
1582 struct drm_atomic_commit *state = plane_state->state;
1583 int i = 0;
1584
1585 old_colorop = colorop;
1586
1587 /* 1st op: 1d curve - degamma */
1588 for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
1589 if (new_colorop_state->colorop == old_colorop &&
1590 (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_degam_tfs)) {
1591 colorop_state = new_colorop_state;
1592 break;
1593 }
1594 }
1595
1596 if (!colorop_state)
1597 return -EINVAL;
1598
1599 return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state);
1600 }
1601 EXPORT_IF_KUNIT(__set_dm_plane_colorop_degamma);
1602
1603 STATIC_IFN_KUNIT int
__set_dm_plane_colorop_3x4_matrix(struct drm_plane_state * plane_state,struct dc_plane_state * dc_plane_state,struct drm_colorop * colorop)1604 __set_dm_plane_colorop_3x4_matrix(struct drm_plane_state *plane_state,
1605 struct dc_plane_state *dc_plane_state,
1606 struct drm_colorop *colorop)
1607 {
1608 struct drm_colorop *old_colorop;
1609 struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
1610 struct drm_atomic_commit *state = plane_state->state;
1611 const struct drm_device *dev = colorop->dev;
1612 const struct drm_property_blob *blob;
1613 struct drm_color_ctm_3x4 *ctm = NULL;
1614 int i = 0;
1615
1616 /* 3x4 matrix */
1617 old_colorop = colorop;
1618 for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
1619 if (new_colorop_state->colorop == old_colorop &&
1620 new_colorop_state->colorop->type == DRM_COLOROP_CTM_3X4) {
1621 colorop_state = new_colorop_state;
1622 break;
1623 }
1624 }
1625
1626 if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_CTM_3X4) {
1627 drm_dbg(dev, "3x4 matrix colorop with ID: %d\n", colorop->base.id);
1628 blob = colorop_state->data;
1629 if (blob->length == sizeof(struct drm_color_ctm_3x4)) {
1630 ctm = (struct drm_color_ctm_3x4 *) blob->data;
1631 __drm_ctm_3x4_to_dc_matrix(ctm, dc_plane_state->gamut_remap_matrix.matrix);
1632 dc_plane_state->gamut_remap_matrix.enable_remap = true;
1633 dc_plane_state->input_csc_color_matrix.enable_adjustment = false;
1634 } else {
1635 drm_warn(dev, "blob->length (%zu) isn't equal to drm_color_ctm_3x4 (%zu)\n",
1636 blob->length, sizeof(struct drm_color_ctm_3x4));
1637 return -EINVAL;
1638 }
1639 }
1640
1641 return 0;
1642 }
1643 EXPORT_IF_KUNIT(__set_dm_plane_colorop_3x4_matrix);
1644
1645 STATIC_IFN_KUNIT int
__set_dm_plane_colorop_multiplier(struct drm_plane_state * plane_state,struct dc_plane_state * dc_plane_state,struct drm_colorop * colorop)1646 __set_dm_plane_colorop_multiplier(struct drm_plane_state *plane_state,
1647 struct dc_plane_state *dc_plane_state,
1648 struct drm_colorop *colorop)
1649 {
1650 struct drm_colorop *old_colorop;
1651 struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
1652 struct drm_atomic_commit *state = plane_state->state;
1653 const struct drm_device *dev = colorop->dev;
1654 int i = 0;
1655
1656 /* Multiplier */
1657 old_colorop = colorop;
1658 for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
1659 if (new_colorop_state->colorop == old_colorop &&
1660 new_colorop_state->colorop->type == DRM_COLOROP_MULTIPLIER) {
1661 colorop_state = new_colorop_state;
1662 break;
1663 }
1664 }
1665
1666 if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_MULTIPLIER) {
1667 drm_dbg(dev, "Multiplier colorop with ID: %d\n", colorop->base.id);
1668 dc_plane_state->hdr_mult = amdgpu_dm_fixpt_from_s3132(colorop_state->multiplier);
1669 }
1670
1671 return 0;
1672 }
1673 EXPORT_IF_KUNIT(__set_dm_plane_colorop_multiplier);
1674
1675 static int
__set_dm_plane_colorop_shaper(struct drm_plane_state * plane_state,struct dc_plane_state * dc_plane_state,struct drm_colorop * colorop)1676 __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state,
1677 struct dc_plane_state *dc_plane_state,
1678 struct drm_colorop *colorop)
1679 {
1680 struct drm_colorop *old_colorop;
1681 struct drm_colorop_state *new_colorop_state;
1682 struct drm_colorop_state *tf_state = NULL, *lut_state = NULL;
1683 struct drm_atomic_commit *state = plane_state->state;
1684 struct drm_colorop *lut_colorop;
1685 enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR;
1686 struct dc_transfer_func *tf = &dc_plane_state->cm.shaper_func;
1687 const struct drm_color_lut32 *shaper_lut;
1688 struct drm_device *dev = colorop->dev;
1689 bool enabled = false;
1690 u32 shaper_size;
1691 int i = 0, ret = 0;
1692
1693 /* 1D Curve - SHAPER TF: find state */
1694 old_colorop = colorop;
1695 for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
1696 if (new_colorop_state->colorop == old_colorop &&
1697 (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) {
1698 tf_state = new_colorop_state;
1699 break;
1700 }
1701 }
1702
1703 /* 1D LUT - SHAPER LUT: find state */
1704 lut_colorop = old_colorop->next;
1705 if (!lut_colorop) {
1706 drm_dbg(dev, "no Shaper LUT colorop found\n");
1707 return -EINVAL;
1708 }
1709
1710 for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
1711 if (new_colorop_state->colorop == lut_colorop &&
1712 new_colorop_state->colorop->type == DRM_COLOROP_1D_LUT) {
1713 lut_state = new_colorop_state;
1714 break;
1715 }
1716 }
1717
1718 if (tf_state && !tf_state->bypass) {
1719 drm_dbg(dev, "Shaper TF colorop with ID: %d\n", old_colorop->base.id);
1720 tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1721 tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(tf_state->curve_1d_type);
1722 tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
1723 ret = __set_output_tf(tf, 0, 0, false);
1724 if (ret)
1725 return ret;
1726 enabled = true;
1727 }
1728
1729 if (lut_state && !lut_state->bypass) {
1730 drm_dbg(dev, "Shaper LUT colorop with ID: %d\n", lut_colorop->base.id);
1731 tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1732 tf->tf = default_tf;
1733 tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
1734 shaper_lut = __extract_blob_lut32(lut_state->data, &shaper_size);
1735 shaper_size = shaper_lut != NULL ? shaper_size : 0;
1736
1737 /* Custom LUT size must be the same as supported size */
1738 if (shaper_size == lut_colorop->size) {
1739 ret = __set_output_tf_32(tf, shaper_lut, shaper_size, false);
1740 if (ret)
1741 return ret;
1742 enabled = true;
1743 }
1744 }
1745
1746 if (!enabled) {
1747 tf->type = TF_TYPE_BYPASS;
1748 dc_plane_state->cm.flags.bits.shaper_enable = 0;
1749 } else {
1750 dc_plane_state->cm.flags.bits.shaper_enable = 1;
1751 }
1752
1753 return 0;
1754 }
1755
1756 /* __set_colorop_3dlut - set DRM 3D LUT to DC stream
1757 * @drm_lut3d: user 3D LUT
1758 * @drm_lut3d_size: size of 3D LUT
1759 * @lut3d: DC 3D LUT
1760 *
1761 * Map user 3D LUT data to DC 3D LUT and all necessary bits to program it
1762 * on DCN accordingly.
1763 *
1764 * Returns:
1765 * 0 on success. -EINVAL if drm_lut3d_size is zero.
1766 */
__set_colorop_3dlut(const struct drm_color_lut32 * drm_lut3d,uint32_t drm_lut3d_size,struct dc_3dlut * lut)1767 STATIC_IFN_KUNIT int __set_colorop_3dlut(const struct drm_color_lut32 *drm_lut3d,
1768 uint32_t drm_lut3d_size,
1769 struct dc_3dlut *lut)
1770 {
1771 if (!drm_lut3d_size) {
1772 lut->state.bits.initialized = 0;
1773 return -EINVAL;
1774 }
1775
1776 /* Only supports 17x17x17 3D LUT (12-bit) now */
1777 lut->lut_3d.use_12bits = true;
1778 lut->lut_3d.use_tetrahedral_9 = false;
1779
1780 lut->state.bits.initialized = 1;
1781 __drm_3dlut32_to_dc_3dlut(drm_lut3d, drm_lut3d_size, &lut->lut_3d,
1782 lut->lut_3d.use_tetrahedral_9, 12);
1783
1784 return 0;
1785 }
1786 EXPORT_IF_KUNIT(__set_colorop_3dlut);
1787
1788 static int
__set_dm_plane_colorop_3dlut(struct drm_plane_state * plane_state,struct dc_plane_state * dc_plane_state,struct drm_colorop * colorop)1789 __set_dm_plane_colorop_3dlut(struct drm_plane_state *plane_state,
1790 struct dc_plane_state *dc_plane_state,
1791 struct drm_colorop *colorop)
1792 {
1793 struct drm_colorop *old_colorop;
1794 struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
1795 struct dc_transfer_func *tf = &dc_plane_state->cm.shaper_func;
1796 struct drm_atomic_commit *state = plane_state->state;
1797 const struct amdgpu_device *adev = drm_to_adev(colorop->dev);
1798 bool has_3dlut = adev->dm.dc->caps.color.dpp.hw_3d_lut || adev->dm.dc->caps.color.mpc.preblend;
1799 const struct drm_device *dev = colorop->dev;
1800 const struct drm_color_lut32 *lut3d;
1801 uint32_t lut3d_size;
1802 int i = 0, ret = 0;
1803
1804 /* 3D LUT */
1805 old_colorop = colorop;
1806 for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
1807 if (new_colorop_state->colorop == old_colorop &&
1808 new_colorop_state->colorop->type == DRM_COLOROP_3D_LUT) {
1809 colorop_state = new_colorop_state;
1810 break;
1811 }
1812 }
1813
1814 if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_3D_LUT) {
1815 if (!has_3dlut) {
1816 drm_dbg(dev, "3D LUT is not supported by hardware\n");
1817 return -EINVAL;
1818 }
1819
1820 drm_dbg(dev, "3D LUT colorop with ID: %d\n", colorop->base.id);
1821 lut3d = __extract_blob_lut32(colorop_state->data, &lut3d_size);
1822 lut3d_size = lut3d != NULL ? lut3d_size : 0;
1823 ret = __set_colorop_3dlut(lut3d, lut3d_size, &dc_plane_state->cm.lut3d_func);
1824 if (ret) {
1825 drm_dbg(dev, "3D LUT colorop with ID: %d has LUT size = %d\n",
1826 colorop->base.id, lut3d_size);
1827 return ret;
1828 }
1829
1830 dc_plane_state->cm.flags.bits.lut3d_enable = 1;
1831
1832 /* 3D LUT requires shaper. If shaper colorop is bypassed, enable shaper curve
1833 * with TRANSFER_FUNCTION_LINEAR
1834 */
1835 if (tf->type == TF_TYPE_BYPASS) {
1836 tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1837 tf->tf = TRANSFER_FUNCTION_LINEAR;
1838 tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
1839 ret = __set_output_tf_32(tf, NULL, 0, false);
1840 }
1841 } else {
1842 dc_plane_state->cm.flags.bits.lut3d_enable = 0;
1843 }
1844
1845 return ret;
1846 }
1847
1848 static int
__set_dm_plane_colorop_blend(struct drm_plane_state * plane_state,struct dc_plane_state * dc_plane_state,struct drm_colorop * colorop)1849 __set_dm_plane_colorop_blend(struct drm_plane_state *plane_state,
1850 struct dc_plane_state *dc_plane_state,
1851 struct drm_colorop *colorop)
1852 {
1853 struct drm_colorop *old_colorop;
1854 struct drm_colorop_state *new_colorop_state;
1855 struct drm_colorop_state *tf_state = NULL, *lut_state = NULL;
1856 struct drm_atomic_commit *state = plane_state->state;
1857 struct drm_colorop *lut_colorop;
1858 enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR;
1859 struct dc_transfer_func *tf = &dc_plane_state->cm.blend_func;
1860 const struct drm_color_lut32 *blend_lut = NULL;
1861 struct drm_device *dev = colorop->dev;
1862 uint32_t blend_size = 0;
1863 int i = 0;
1864
1865 dc_plane_state->cm.flags.bits.blend_enable = 0;
1866
1867 /* 1D Curve - BLND TF: find state */
1868 old_colorop = colorop;
1869 for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
1870 if (new_colorop_state->colorop == old_colorop &&
1871 (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) {
1872 tf_state = new_colorop_state;
1873 break;
1874 }
1875 }
1876
1877 /* 1D LUT - BLND LUT: find state */
1878 lut_colorop = old_colorop->next;
1879 if (!lut_colorop) {
1880 drm_dbg(dev, "no Blend LUT colorop found\n");
1881 return -EINVAL;
1882 }
1883
1884 for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
1885 if (new_colorop_state->colorop == lut_colorop &&
1886 new_colorop_state->colorop->type == DRM_COLOROP_1D_LUT) {
1887 lut_state = new_colorop_state;
1888 break;
1889 }
1890 }
1891
1892 if (tf_state && !tf_state->bypass) {
1893 drm_dbg(dev, "Blend TF colorop with ID: %d\n", old_colorop->base.id);
1894 tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1895 tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(tf_state->curve_1d_type);
1896 tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
1897 dc_plane_state->cm.flags.bits.blend_enable = 1;
1898 __set_input_tf_32(NULL, tf, blend_lut, blend_size);
1899 }
1900
1901 if (lut_state && !lut_state->bypass) {
1902 drm_dbg(dev, "Blend LUT colorop with ID: %d\n", lut_colorop->base.id);
1903 tf->type = TF_TYPE_DISTRIBUTED_POINTS;
1904 tf->tf = default_tf;
1905 tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
1906 dc_plane_state->cm.flags.bits.blend_enable = 1;
1907 blend_lut = __extract_blob_lut32(lut_state->data, &blend_size);
1908 blend_size = blend_lut != NULL ? blend_size : 0;
1909
1910 /* Custom LUT size must be the same as supported size */
1911 if (blend_size == lut_colorop->size)
1912 __set_input_tf_32(NULL, tf, blend_lut, blend_size);
1913 }
1914
1915 return 0;
1916 }
1917
1918 static int
amdgpu_dm_plane_set_color_properties(struct drm_plane_state * plane_state,struct dc_plane_state * dc_plane_state)1919 amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state,
1920 struct dc_plane_state *dc_plane_state)
1921 {
1922 struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
1923 enum amdgpu_transfer_function shaper_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
1924 enum amdgpu_transfer_function blend_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
1925 const struct drm_color_lut *shaper_lut, *lut3d, *blend_lut;
1926 uint32_t shaper_size, lut3d_size, blend_size;
1927 int ret;
1928
1929 dc_plane_state->hdr_mult = amdgpu_dm_fixpt_from_s3132(dm_plane_state->hdr_mult);
1930
1931 shaper_lut = __extract_blob_lut(dm_plane_state->shaper_lut, &shaper_size);
1932 shaper_size = shaper_lut != NULL ? shaper_size : 0;
1933 shaper_tf = dm_plane_state->shaper_tf;
1934 lut3d = __extract_blob_lut(dm_plane_state->lut3d, &lut3d_size);
1935 lut3d_size = lut3d != NULL ? lut3d_size : 0;
1936
1937 amdgpu_dm_atomic_lut3d(lut3d, lut3d_size, &dc_plane_state->cm);
1938 ret = amdgpu_dm_atomic_shaper_lut(shaper_lut, false,
1939 amdgpu_tf_to_dc_tf(shaper_tf),
1940 shaper_size,
1941 &dc_plane_state->cm);
1942 if (ret) {
1943 drm_dbg_kms(plane_state->plane->dev,
1944 "setting plane %d shaper LUT failed.\n",
1945 plane_state->plane->index);
1946
1947 return ret;
1948 }
1949
1950 blend_tf = dm_plane_state->blend_tf;
1951 blend_lut = __extract_blob_lut(dm_plane_state->blend_lut, &blend_size);
1952 blend_size = blend_lut != NULL ? blend_size : 0;
1953
1954 ret = amdgpu_dm_atomic_blend_lut(blend_lut, false,
1955 amdgpu_tf_to_dc_tf(blend_tf),
1956 blend_size, &dc_plane_state->cm);
1957
1958 if (ret) {
1959 drm_dbg_kms(plane_state->plane->dev,
1960 "setting plane %d gamma lut failed.\n",
1961 plane_state->plane->index);
1962
1963 return ret;
1964 }
1965
1966 return 0;
1967 }
1968
1969 static int
amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state * plane_state,struct dc_plane_state * dc_plane_state)1970 amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state,
1971 struct dc_plane_state *dc_plane_state)
1972 {
1973 struct drm_colorop *colorop = plane_state->color_pipeline;
1974 struct drm_device *dev = plane_state->plane->dev;
1975 struct amdgpu_device *adev = drm_to_adev(dev);
1976 bool has_3dlut = adev->dm.dc->caps.color.dpp.hw_3d_lut || adev->dm.dc->caps.color.mpc.preblend;
1977 int ret;
1978
1979 /* 1D Curve - DEGAM TF */
1980 if (!colorop)
1981 return -EINVAL;
1982
1983 ret = __set_dm_plane_colorop_degamma(plane_state, dc_plane_state, colorop);
1984 if (ret)
1985 return ret;
1986
1987 /* Multiplier */
1988 colorop = colorop->next;
1989 if (!colorop) {
1990 drm_dbg(dev, "no multiplier colorop found\n");
1991 return -EINVAL;
1992 }
1993
1994 ret = __set_dm_plane_colorop_multiplier(plane_state, dc_plane_state, colorop);
1995 if (ret)
1996 return ret;
1997
1998 /* 3x4 matrix */
1999 colorop = colorop->next;
2000 if (!colorop) {
2001 drm_dbg(dev, "no 3x4 matrix colorop found\n");
2002 return -EINVAL;
2003 }
2004
2005 ret = __set_dm_plane_colorop_3x4_matrix(plane_state, dc_plane_state, colorop);
2006 if (ret)
2007 return ret;
2008
2009 if (has_3dlut) {
2010 /* 1D Curve & LUT - SHAPER TF & LUT */
2011 colorop = colorop->next;
2012 if (!colorop) {
2013 drm_dbg(dev, "no Shaper TF colorop found\n");
2014 return -EINVAL;
2015 }
2016
2017 ret = __set_dm_plane_colorop_shaper(plane_state, dc_plane_state, colorop);
2018 if (ret)
2019 return ret;
2020
2021 /* Shaper LUT colorop is already handled, just skip here */
2022 colorop = colorop->next;
2023 if (!colorop)
2024 return -EINVAL;
2025
2026 /* 3D LUT */
2027 colorop = colorop->next;
2028 if (!colorop) {
2029 drm_dbg(dev, "no 3D LUT colorop found\n");
2030 return -EINVAL;
2031 }
2032
2033 ret = __set_dm_plane_colorop_3dlut(plane_state, dc_plane_state, colorop);
2034 if (ret)
2035 return ret;
2036 }
2037
2038 /* 1D Curve & LUT - BLND TF & LUT */
2039 colorop = colorop->next;
2040 if (!colorop) {
2041 drm_dbg(dev, "no Blend TF colorop found\n");
2042 return -EINVAL;
2043 }
2044
2045 ret = __set_dm_plane_colorop_blend(plane_state, dc_plane_state, colorop);
2046 if (ret)
2047 return ret;
2048
2049 /* BLND LUT colorop is already handled, just skip here */
2050 colorop = colorop->next;
2051 if (!colorop)
2052 return -EINVAL;
2053
2054 return 0;
2055 }
2056
2057 /**
2058 * amdgpu_dm_update_plane_color_mgmt: Maps DRM color management to DC plane.
2059 * @crtc: amdgpu_dm crtc state
2060 * @plane_state: DRM plane state
2061 * @dc_plane_state: target DC surface
2062 *
2063 * Update the underlying dc_stream_state's input transfer function (ITF) in
2064 * preparation for hardware commit. The transfer function used depends on
2065 * the preparation done on the stream for color management.
2066 *
2067 * Returns:
2068 * 0 on success. -ENOMEM if mem allocation fails.
2069 */
amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state * crtc,struct drm_plane_state * plane_state,struct dc_plane_state * dc_plane_state)2070 int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
2071 struct drm_plane_state *plane_state,
2072 struct dc_plane_state *dc_plane_state)
2073 {
2074 struct amdgpu_device *adev = drm_to_adev(crtc->base.state->dev);
2075 struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
2076 struct drm_color_ctm_3x4 *ctm = NULL;
2077 struct dc_color_caps *color_caps = NULL;
2078 bool has_crtc_cm_degamma;
2079 int ret;
2080
2081 ret = amdgpu_dm_verify_lut3d_size(adev, plane_state);
2082 if (ret) {
2083 drm_dbg_driver(&adev->ddev, "amdgpu_dm_verify_lut3d_size() failed\n");
2084 return ret;
2085 }
2086
2087 if (dc_plane_state->ctx && dc_plane_state->ctx->dc)
2088 color_caps = &dc_plane_state->ctx->dc->caps.color;
2089
2090 /* Initially, we can just bypass the DGM block. */
2091 dc_plane_state->in_transfer_func.type = TF_TYPE_BYPASS;
2092 dc_plane_state->in_transfer_func.tf = TRANSFER_FUNCTION_LINEAR;
2093
2094 /* After, we start to update values according to color props */
2095 has_crtc_cm_degamma = (crtc->cm_has_degamma || crtc->cm_is_degamma_srgb);
2096
2097 ret = __set_dm_plane_degamma(plane_state, dc_plane_state, color_caps);
2098 if (ret == -ENOMEM)
2099 return ret;
2100
2101 /* We only have one degamma block available (pre-blending) for the
2102 * whole color correction pipeline, so that we can't actually perform
2103 * plane and CRTC degamma at the same time. Explicitly reject atomic
2104 * updates when userspace sets both plane and CRTC degamma properties.
2105 */
2106 if (has_crtc_cm_degamma && ret != -EINVAL) {
2107 drm_dbg_kms(crtc->base.crtc->dev,
2108 "doesn't support plane and CRTC degamma at the same time\n");
2109 return -EINVAL;
2110 }
2111
2112 /* If we are here, it means we don't have plane degamma settings, check
2113 * if we have CRTC degamma waiting for mapping to pre-blending degamma
2114 * block
2115 */
2116 if (has_crtc_cm_degamma) {
2117 /*
2118 * AMD HW doesn't have post-blending degamma caps. When DRM
2119 * CRTC atomic degamma is set, we maps it to DPP degamma block
2120 * (pre-blending) or, on legacy gamma, we use DPP degamma to
2121 * linearize (implicit degamma) from sRGB/BT709 according to
2122 * the input space.
2123 */
2124 ret = map_crtc_degamma_to_dc_plane(crtc, dc_plane_state, color_caps);
2125 if (ret)
2126 return ret;
2127 }
2128
2129 /* Setup CRTC CTM. */
2130 if (dm_plane_state->ctm) {
2131 ctm = (struct drm_color_ctm_3x4 *)dm_plane_state->ctm->data;
2132 /*
2133 * DCN2 and older don't support both pre-blending and
2134 * post-blending gamut remap. For this HW family, if we have
2135 * the plane and CRTC CTMs simultaneously, CRTC CTM takes
2136 * priority, and we discard plane CTM, as implemented in
2137 * dcn10_program_gamut_remap(). However, DCN3+ has DPP
2138 * (pre-blending) and MPC (post-blending) `gamut remap` blocks;
2139 * therefore, we can program plane and CRTC CTMs together by
2140 * mapping CRTC CTM to MPC and keeping plane CTM setup at DPP,
2141 * as it's done by dcn30_program_gamut_remap().
2142 */
2143 __drm_ctm_3x4_to_dc_matrix(ctm, dc_plane_state->gamut_remap_matrix.matrix);
2144
2145 dc_plane_state->gamut_remap_matrix.enable_remap = true;
2146 dc_plane_state->input_csc_color_matrix.enable_adjustment = false;
2147 } else {
2148 /* Bypass CTM. */
2149 dc_plane_state->gamut_remap_matrix.enable_remap = false;
2150 dc_plane_state->input_csc_color_matrix.enable_adjustment = false;
2151 }
2152
2153 if (!amdgpu_dm_plane_set_colorop_properties(plane_state, dc_plane_state))
2154 return 0;
2155
2156 return amdgpu_dm_plane_set_color_properties(plane_state, dc_plane_state);
2157 }
2158 EXPORT_IF_KUNIT(amdgpu_dm_update_plane_color_mgmt);
2159