xref: /linux/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c (revision 49bda4826843be0ef97a162009a29ea3a63f3935)
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, &degamma_size);
1278 	regamma_lut = __extract_blob_lut(crtc->base.gamma_lut, &regamma_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 						 &degamma_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 					 &degamma_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