1/*
2 * Copyright 2018 Advanced Micro Devices, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: AMD
23 *
24 */
25#include "amdgpu.h"
26#include "amdgpu_mode.h"
27#include "amdgpu_dm.h"
28#include "dc.h"
29#include "modules/color/color_gamma.h"
30#include "basics/conversion.h"
31
32/**
33 * DOC: overview
34 *
35 * The DC interface to HW gives us the following color management blocks
36 * per pipe (surface):
37 *
38 * - Input gamma LUT (de-normalized)
39 * - Input CSC (normalized)
40 * - Surface degamma LUT (normalized)
41 * - Surface CSC (normalized)
42 * - Surface regamma LUT (normalized)
43 * - Output CSC (normalized)
44 *
45 * But these aren't a direct mapping to DRM color properties. The current DRM
46 * interface exposes CRTC degamma, CRTC CTM and CRTC regamma while our hardware
47 * is essentially giving:
48 *
49 * Plane CTM -> Plane degamma -> Plane CTM -> Plane regamma -> Plane CTM
50 *
51 * The input gamma LUT block isn't really applicable here since it operates
52 * on the actual input data itself rather than the HW fp representation. The
53 * input and output CSC blocks are technically available to use as part of
54 * the DC interface but are typically used internally by DC for conversions
55 * between color spaces. These could be blended together with user
56 * adjustments in the future but for now these should remain untouched.
57 *
58 * The pipe blending also happens after these blocks so we don't actually
59 * support any CRTC props with correct blending with multiple planes - but we
60 * can still support CRTC color management properties in DM in most single
61 * plane cases correctly with clever management of the DC interface in DM.
62 *
63 * As per DRM documentation, blocks should be in hardware bypass when their
64 * respective property is set to NULL. A linear DGM/RGM LUT should also
65 * considered as putting the respective block into bypass mode.
66 *
67 * This means that the following
68 * configuration is assumed to be the default:
69 *
70 * Plane DGM Bypass -> Plane CTM Bypass -> Plane RGM Bypass -> ...
71 * CRTC DGM Bypass -> CRTC CTM Bypass -> CRTC RGM Bypass
72 */
73
74#define MAX_DRM_LUT_VALUE 0xFFFF
75#define SDR_WHITE_LEVEL_INIT_VALUE 80
76
77/**
78 * amdgpu_dm_init_color_mod - Initialize the color module.
79 *
80 * We're not using the full color module, only certain components.
81 * Only call setup functions for components that we need.
82 */
83void amdgpu_dm_init_color_mod(void)
84{
85	setup_x_points_distribution();
86}
87
88static inline struct fixed31_32 amdgpu_dm_fixpt_from_s3132(__u64 x)
89{
90	struct fixed31_32 val;
91
92	/* If negative, convert to 2's complement. */
93	if (x & (1ULL << 63))
94		x = -(x & ~(1ULL << 63));
95
96	val.value = x;
97	return val;
98}
99
100#ifdef AMD_PRIVATE_COLOR
101/* Pre-defined Transfer Functions (TF)
102 *
103 * AMD driver supports pre-defined mathematical functions for transferring
104 * between encoded values and optical/linear space. Depending on HW color caps,
105 * ROMs and curves built by the AMD color module support these transforms.
106 *
107 * The driver-specific color implementation exposes properties for pre-blending
108 * degamma TF, shaper TF (before 3D LUT), and blend(dpp.ogam) TF and
109 * post-blending regamma (mpc.ogam) TF. However, only pre-blending degamma
110 * supports ROM curves. AMD color module uses pre-defined coefficients to build
111 * curves for the other blocks. What can be done by each color block is
112 * described by struct dpp_color_capsand struct mpc_color_caps.
113 *
114 * AMD driver-specific color API exposes the following pre-defined transfer
115 * functions:
116 *
117 * - Identity: linear/identity relationship between pixel value and
118 *   luminance value;
119 * - Gamma 2.2, Gamma 2.4, Gamma 2.6: pure power functions;
120 * - sRGB: 2.4: The piece-wise transfer function from IEC 61966-2-1:1999;
121 * - BT.709: has a linear segment in the bottom part and then a power function
122 *   with a 0.45 (~1/2.22) gamma for the rest of the range; standardized by
123 *   ITU-R BT.709-6;
124 * - PQ (Perceptual Quantizer): used for HDR display, allows luminance range
125 *   capability of 0 to 10,000 nits; standardized by SMPTE ST 2084.
126 *
127 * The AMD color model is designed with an assumption that SDR (sRGB, BT.709,
128 * Gamma 2.2, etc.) peak white maps (normalized to 1.0 FP) to 80 nits in the PQ
129 * system. This has the implication that PQ EOTF (non-linear to linear) maps to
130 * [0.0..125.0] where 125.0 = 10,000 nits / 80 nits.
131 *
132 * Non-linear and linear forms are described in the table below:
133 *
134 * ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
135 * ���           ���     Non-linear      ���   Linear             ���
136 * ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
137 * ���      sRGB ��� UNORM or [0.0, 1.0] ��� [0.0, 1.0]           ���
138 * ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
139 * ���     BT709 ��� UNORM or [0.0, 1.0] ��� [0.0, 1.0]           ���
140 * ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
141 * ��� Gamma 2.x ��� UNORM or [0.0, 1.0] ��� [0.0, 1.0]           ���
142 * ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
143 * ���        PQ ��� UNORM or FP16 CCCS* ��� [0.0, 125.0]         ���
144 * ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
145 * ���  Identity ��� UNORM or FP16 CCCS* ��� [0.0, 1.0] or CCCS** ���
146 * ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
147 * * CCCS: Windows canonical composition color space
148 * ** Respectively
149 *
150 * In the driver-specific API, color block names attached to TF properties
151 * suggest the intention regarding non-linear encoding pixel's luminance
152 * values. As some newer encodings don't use gamma curve, we make encoding and
153 * decoding explicit by defining an enum list of transfer functions supported
154 * in terms of EOTF and inverse EOTF, where:
155 *
156 * - EOTF (electro-optical transfer function): is the transfer function to go
157 *   from the encoded value to an optical (linear) value. De-gamma functions
158 *   traditionally do this.
159 * - Inverse EOTF (simply the inverse of the EOTF): is usually intended to go
160 *   from an optical/linear space (which might have been used for blending)
161 *   back to the encoded values. Gamma functions traditionally do this.
162 */
163static const char * const
164amdgpu_transfer_function_names[] = {
165	[AMDGPU_TRANSFER_FUNCTION_DEFAULT]		= "Default",
166	[AMDGPU_TRANSFER_FUNCTION_IDENTITY]		= "Identity",
167	[AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF]		= "sRGB EOTF",
168	[AMDGPU_TRANSFER_FUNCTION_BT709_INV_OETF]	= "BT.709 inv_OETF",
169	[AMDGPU_TRANSFER_FUNCTION_PQ_EOTF]		= "PQ EOTF",
170	[AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF]		= "Gamma 2.2 EOTF",
171	[AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF]		= "Gamma 2.4 EOTF",
172	[AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF]		= "Gamma 2.6 EOTF",
173	[AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF]	= "sRGB inv_EOTF",
174	[AMDGPU_TRANSFER_FUNCTION_BT709_OETF]		= "BT.709 OETF",
175	[AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF]		= "PQ inv_EOTF",
176	[AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF]	= "Gamma 2.2 inv_EOTF",
177	[AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF]	= "Gamma 2.4 inv_EOTF",
178	[AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF]	= "Gamma 2.6 inv_EOTF",
179};
180
181static const u32 amdgpu_eotf =
182	BIT(AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF) |
183	BIT(AMDGPU_TRANSFER_FUNCTION_BT709_INV_OETF) |
184	BIT(AMDGPU_TRANSFER_FUNCTION_PQ_EOTF) |
185	BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF) |
186	BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF) |
187	BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF);
188
189static const u32 amdgpu_inv_eotf =
190	BIT(AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF) |
191	BIT(AMDGPU_TRANSFER_FUNCTION_BT709_OETF) |
192	BIT(AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF) |
193	BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF) |
194	BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF) |
195	BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF);
196
197static struct drm_property *
198amdgpu_create_tf_property(struct drm_device *dev,
199			  const char *name,
200			  u32 supported_tf)
201{
202	u32 transfer_functions = supported_tf |
203				 BIT(AMDGPU_TRANSFER_FUNCTION_DEFAULT) |
204				 BIT(AMDGPU_TRANSFER_FUNCTION_IDENTITY);
205	struct drm_prop_enum_list enum_list[AMDGPU_TRANSFER_FUNCTION_COUNT];
206	int i, len;
207
208	len = 0;
209	for (i = 0; i < AMDGPU_TRANSFER_FUNCTION_COUNT; i++) {
210		if ((transfer_functions & BIT(i)) == 0)
211			continue;
212
213		enum_list[len].type = i;
214		enum_list[len].name = amdgpu_transfer_function_names[i];
215		len++;
216	}
217
218	return drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
219					name, enum_list, len);
220}
221
222int
223amdgpu_dm_create_color_properties(struct amdgpu_device *adev)
224{
225	struct drm_property *prop;
226
227	prop = drm_property_create(adev_to_drm(adev),
228				   DRM_MODE_PROP_BLOB,
229				   "AMD_PLANE_DEGAMMA_LUT", 0);
230	if (!prop)
231		return -ENOMEM;
232	adev->mode_info.plane_degamma_lut_property = prop;
233
234	prop = drm_property_create_range(adev_to_drm(adev),
235					 DRM_MODE_PROP_IMMUTABLE,
236					 "AMD_PLANE_DEGAMMA_LUT_SIZE",
237					 0, UINT_MAX);
238	if (!prop)
239		return -ENOMEM;
240	adev->mode_info.plane_degamma_lut_size_property = prop;
241
242	prop = amdgpu_create_tf_property(adev_to_drm(adev),
243					 "AMD_PLANE_DEGAMMA_TF",
244					 amdgpu_eotf);
245	if (!prop)
246		return -ENOMEM;
247	adev->mode_info.plane_degamma_tf_property = prop;
248
249	prop = drm_property_create_range(adev_to_drm(adev),
250					 0, "AMD_PLANE_HDR_MULT", 0, U64_MAX);
251	if (!prop)
252		return -ENOMEM;
253	adev->mode_info.plane_hdr_mult_property = prop;
254
255	prop = drm_property_create(adev_to_drm(adev),
256				   DRM_MODE_PROP_BLOB,
257				   "AMD_PLANE_CTM", 0);
258	if (!prop)
259		return -ENOMEM;
260	adev->mode_info.plane_ctm_property = prop;
261
262	prop = drm_property_create(adev_to_drm(adev),
263				   DRM_MODE_PROP_BLOB,
264				   "AMD_PLANE_SHAPER_LUT", 0);
265	if (!prop)
266		return -ENOMEM;
267	adev->mode_info.plane_shaper_lut_property = prop;
268
269	prop = drm_property_create_range(adev_to_drm(adev),
270					 DRM_MODE_PROP_IMMUTABLE,
271					 "AMD_PLANE_SHAPER_LUT_SIZE", 0, UINT_MAX);
272	if (!prop)
273		return -ENOMEM;
274	adev->mode_info.plane_shaper_lut_size_property = prop;
275
276	prop = amdgpu_create_tf_property(adev_to_drm(adev),
277					 "AMD_PLANE_SHAPER_TF",
278					 amdgpu_inv_eotf);
279	if (!prop)
280		return -ENOMEM;
281	adev->mode_info.plane_shaper_tf_property = prop;
282
283	prop = drm_property_create(adev_to_drm(adev),
284				   DRM_MODE_PROP_BLOB,
285				   "AMD_PLANE_LUT3D", 0);
286	if (!prop)
287		return -ENOMEM;
288	adev->mode_info.plane_lut3d_property = prop;
289
290	prop = drm_property_create_range(adev_to_drm(adev),
291					 DRM_MODE_PROP_IMMUTABLE,
292					 "AMD_PLANE_LUT3D_SIZE", 0, UINT_MAX);
293	if (!prop)
294		return -ENOMEM;
295	adev->mode_info.plane_lut3d_size_property = prop;
296
297	prop = drm_property_create(adev_to_drm(adev),
298				   DRM_MODE_PROP_BLOB,
299				   "AMD_PLANE_BLEND_LUT", 0);
300	if (!prop)
301		return -ENOMEM;
302	adev->mode_info.plane_blend_lut_property = prop;
303
304	prop = drm_property_create_range(adev_to_drm(adev),
305					 DRM_MODE_PROP_IMMUTABLE,
306					 "AMD_PLANE_BLEND_LUT_SIZE", 0, UINT_MAX);
307	if (!prop)
308		return -ENOMEM;
309	adev->mode_info.plane_blend_lut_size_property = prop;
310
311	prop = amdgpu_create_tf_property(adev_to_drm(adev),
312					 "AMD_PLANE_BLEND_TF",
313					 amdgpu_eotf);
314	if (!prop)
315		return -ENOMEM;
316	adev->mode_info.plane_blend_tf_property = prop;
317
318	prop = amdgpu_create_tf_property(adev_to_drm(adev),
319					 "AMD_CRTC_REGAMMA_TF",
320					 amdgpu_inv_eotf);
321	if (!prop)
322		return -ENOMEM;
323	adev->mode_info.regamma_tf_property = prop;
324
325	return 0;
326}
327#endif
328
329/**
330 * __extract_blob_lut - Extracts the DRM lut and lut size from a blob.
331 * @blob: DRM color mgmt property blob
332 * @size: lut size
333 *
334 * Returns:
335 * DRM LUT or NULL
336 */
337static const struct drm_color_lut *
338__extract_blob_lut(const struct drm_property_blob *blob, uint32_t *size)
339{
340	*size = blob ? drm_color_lut_size(blob) : 0;
341	return blob ? (struct drm_color_lut *)blob->data : NULL;
342}
343
344/**
345 * __is_lut_linear - check if the given lut is a linear mapping of values
346 * @lut: given lut to check values
347 * @size: lut size
348 *
349 * It is considered linear if the lut represents:
350 * f(a) = (0xFF00/MAX_COLOR_LUT_ENTRIES-1)a; for integer a in [0,
351 * MAX_COLOR_LUT_ENTRIES)
352 *
353 * Returns:
354 * True if the given lut is a linear mapping of values, i.e. it acts like a
355 * bypass LUT. Otherwise, false.
356 */
357static bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size)
358{
359	int i;
360	uint32_t expected;
361	int delta;
362
363	for (i = 0; i < size; i++) {
364		/* All color values should equal */
365		if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue))
366			return false;
367
368		expected = i * MAX_DRM_LUT_VALUE / (size-1);
369
370		/* Allow a +/-1 error. */
371		delta = lut[i].red - expected;
372		if (delta < -1 || 1 < delta)
373			return false;
374	}
375	return true;
376}
377
378/**
379 * __drm_lut_to_dc_gamma - convert the drm_color_lut to dc_gamma.
380 * @lut: DRM lookup table for color conversion
381 * @gamma: DC gamma to set entries
382 * @is_legacy: legacy or atomic gamma
383 *
384 * The conversion depends on the size of the lut - whether or not it's legacy.
385 */
386static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut,
387				  struct dc_gamma *gamma, bool is_legacy)
388{
389	uint32_t r, g, b;
390	int i;
391
392	if (is_legacy) {
393		for (i = 0; i < MAX_COLOR_LEGACY_LUT_ENTRIES; i++) {
394			r = drm_color_lut_extract(lut[i].red, 16);
395			g = drm_color_lut_extract(lut[i].green, 16);
396			b = drm_color_lut_extract(lut[i].blue, 16);
397
398			gamma->entries.red[i] = dc_fixpt_from_int(r);
399			gamma->entries.green[i] = dc_fixpt_from_int(g);
400			gamma->entries.blue[i] = dc_fixpt_from_int(b);
401		}
402		return;
403	}
404
405	/* else */
406	for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) {
407		r = drm_color_lut_extract(lut[i].red, 16);
408		g = drm_color_lut_extract(lut[i].green, 16);
409		b = drm_color_lut_extract(lut[i].blue, 16);
410
411		gamma->entries.red[i] = dc_fixpt_from_fraction(r, MAX_DRM_LUT_VALUE);
412		gamma->entries.green[i] = dc_fixpt_from_fraction(g, MAX_DRM_LUT_VALUE);
413		gamma->entries.blue[i] = dc_fixpt_from_fraction(b, MAX_DRM_LUT_VALUE);
414	}
415}
416
417/**
418 * __drm_ctm_to_dc_matrix - converts a DRM CTM to a DC CSC float matrix
419 * @ctm: DRM color transformation matrix
420 * @matrix: DC CSC float matrix
421 *
422 * The matrix needs to be a 3x4 (12 entry) matrix.
423 */
424static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
425				   struct fixed31_32 *matrix)
426{
427	int i;
428
429	/*
430	 * DRM gives a 3x3 matrix, but DC wants 3x4. Assuming we're operating
431	 * with homogeneous coordinates, augment the matrix with 0's.
432	 *
433	 * The format provided is S31.32, using signed-magnitude representation.
434	 * Our fixed31_32 is also S31.32, but is using 2's complement. We have
435	 * to convert from signed-magnitude to 2's complement.
436	 */
437	for (i = 0; i < 12; i++) {
438		/* Skip 4th element */
439		if (i % 4 == 3) {
440			matrix[i] = dc_fixpt_zero;
441			continue;
442		}
443
444		/* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */
445		matrix[i] = amdgpu_dm_fixpt_from_s3132(ctm->matrix[i - (i / 4)]);
446	}
447}
448
449/**
450 * __drm_ctm_3x4_to_dc_matrix - converts a DRM CTM 3x4 to a DC CSC float matrix
451 * @ctm: DRM color transformation matrix with 3x4 dimensions
452 * @matrix: DC CSC float matrix
453 *
454 * The matrix needs to be a 3x4 (12 entry) matrix.
455 */
456static void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm,
457				       struct fixed31_32 *matrix)
458{
459	int i;
460
461	/* The format provided is S31.32, using signed-magnitude representation.
462	 * Our fixed31_32 is also S31.32, but is using 2's complement. We have
463	 * to convert from signed-magnitude to 2's complement.
464	 */
465	for (i = 0; i < 12; i++) {
466		/* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */
467		matrix[i] = amdgpu_dm_fixpt_from_s3132(ctm->matrix[i]);
468	}
469}
470
471/**
472 * __set_legacy_tf - Calculates the legacy transfer function
473 * @func: transfer function
474 * @lut: lookup table that defines the color space
475 * @lut_size: size of respective lut
476 * @has_rom: if ROM can be used for hardcoded curve
477 *
478 * Only for sRGB input space
479 *
480 * Returns:
481 * 0 in case of success, -ENOMEM if fails
482 */
483static int __set_legacy_tf(struct dc_transfer_func *func,
484			   const struct drm_color_lut *lut, uint32_t lut_size,
485			   bool has_rom)
486{
487	struct dc_gamma *gamma = NULL;
488	struct calculate_buffer cal_buffer = {0};
489	bool res;
490
491	ASSERT(lut && lut_size == MAX_COLOR_LEGACY_LUT_ENTRIES);
492
493	cal_buffer.buffer_index = -1;
494
495	gamma = dc_create_gamma();
496	if (!gamma)
497		return -ENOMEM;
498
499	gamma->type = GAMMA_RGB_256;
500	gamma->num_entries = lut_size;
501	__drm_lut_to_dc_gamma(lut, gamma, true);
502
503	res = mod_color_calculate_regamma_params(func, gamma, true, has_rom,
504						 NULL, &cal_buffer);
505
506	dc_gamma_release(&gamma);
507
508	return res ? 0 : -ENOMEM;
509}
510
511/**
512 * __set_output_tf - calculates the output transfer function based on expected input space.
513 * @func: transfer function
514 * @lut: lookup table that defines the color space
515 * @lut_size: size of respective lut
516 * @has_rom: if ROM can be used for hardcoded curve
517 *
518 * Returns:
519 * 0 in case of success. -ENOMEM if fails.
520 */
521static int __set_output_tf(struct dc_transfer_func *func,
522			   const struct drm_color_lut *lut, uint32_t lut_size,
523			   bool has_rom)
524{
525	struct dc_gamma *gamma = NULL;
526	struct calculate_buffer cal_buffer = {0};
527	bool res;
528
529	cal_buffer.buffer_index = -1;
530
531	if (lut_size) {
532		ASSERT(lut && lut_size == MAX_COLOR_LUT_ENTRIES);
533
534		gamma = dc_create_gamma();
535		if (!gamma)
536			return -ENOMEM;
537
538		gamma->num_entries = lut_size;
539		__drm_lut_to_dc_gamma(lut, gamma, false);
540	}
541
542	if (func->tf == TRANSFER_FUNCTION_LINEAR) {
543		/*
544		 * Color module doesn't like calculating regamma params
545		 * on top of a linear input. But degamma params can be used
546		 * instead to simulate this.
547		 */
548		if (gamma)
549			gamma->type = GAMMA_CUSTOM;
550		res = mod_color_calculate_degamma_params(NULL, func,
551							 gamma, gamma != NULL);
552	} else {
553		/*
554		 * Assume sRGB. The actual mapping will depend on whether the
555		 * input was legacy or not.
556		 */
557		if (gamma)
558			gamma->type = GAMMA_CS_TFM_1D;
559		res = mod_color_calculate_regamma_params(func, gamma, gamma != NULL,
560							 has_rom, NULL, &cal_buffer);
561	}
562
563	if (gamma)
564		dc_gamma_release(&gamma);
565
566	return res ? 0 : -ENOMEM;
567}
568
569static int amdgpu_dm_set_atomic_regamma(struct dc_stream_state *stream,
570					const struct drm_color_lut *regamma_lut,
571					uint32_t regamma_size, bool has_rom,
572					enum dc_transfer_func_predefined tf)
573{
574	struct dc_transfer_func *out_tf = stream->out_transfer_func;
575	int ret = 0;
576
577	if (regamma_size || tf != TRANSFER_FUNCTION_LINEAR) {
578		/*
579		 * CRTC RGM goes into RGM LUT.
580		 *
581		 * Note: there is no implicit sRGB regamma here. We are using
582		 * degamma calculation from color module to calculate the curve
583		 * from a linear base if gamma TF is not set. However, if gamma
584		 * TF (!= Linear) and LUT are set at the same time, we will use
585		 * regamma calculation, and the color module will combine the
586		 * pre-defined TF and the custom LUT values into the LUT that's
587		 * actually programmed.
588		 */
589		out_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
590		out_tf->tf = tf;
591		out_tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
592
593		ret = __set_output_tf(out_tf, regamma_lut, regamma_size, has_rom);
594	} else {
595		/*
596		 * No CRTC RGM means we can just put the block into bypass
597		 * since we don't have any plane level adjustments using it.
598		 */
599		out_tf->type = TF_TYPE_BYPASS;
600		out_tf->tf = TRANSFER_FUNCTION_LINEAR;
601	}
602
603	return ret;
604}
605
606/**
607 * __set_input_tf - calculates the input transfer function based on expected
608 * input space.
609 * @caps: dc color capabilities
610 * @func: transfer function
611 * @lut: lookup table that defines the color space
612 * @lut_size: size of respective lut.
613 *
614 * Returns:
615 * 0 in case of success. -ENOMEM if fails.
616 */
617static int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *func,
618			  const struct drm_color_lut *lut, uint32_t lut_size)
619{
620	struct dc_gamma *gamma = NULL;
621	bool res;
622
623	if (lut_size) {
624		gamma = dc_create_gamma();
625		if (!gamma)
626			return -ENOMEM;
627
628		gamma->type = GAMMA_CUSTOM;
629		gamma->num_entries = lut_size;
630
631		__drm_lut_to_dc_gamma(lut, gamma, false);
632	}
633
634	res = mod_color_calculate_degamma_params(caps, func, gamma, gamma != NULL);
635
636	if (gamma)
637		dc_gamma_release(&gamma);
638
639	return res ? 0 : -ENOMEM;
640}
641
642static enum dc_transfer_func_predefined
643amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf)
644{
645	switch (tf) {
646	default:
647	case AMDGPU_TRANSFER_FUNCTION_DEFAULT:
648	case AMDGPU_TRANSFER_FUNCTION_IDENTITY:
649		return TRANSFER_FUNCTION_LINEAR;
650	case AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF:
651	case AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF:
652		return TRANSFER_FUNCTION_SRGB;
653	case AMDGPU_TRANSFER_FUNCTION_BT709_OETF:
654	case AMDGPU_TRANSFER_FUNCTION_BT709_INV_OETF:
655		return TRANSFER_FUNCTION_BT709;
656	case AMDGPU_TRANSFER_FUNCTION_PQ_EOTF:
657	case AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF:
658		return TRANSFER_FUNCTION_PQ;
659	case AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF:
660	case AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF:
661		return TRANSFER_FUNCTION_GAMMA22;
662	case AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF:
663	case AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF:
664		return TRANSFER_FUNCTION_GAMMA24;
665	case AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF:
666	case AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF:
667		return TRANSFER_FUNCTION_GAMMA26;
668	}
669}
670
671static void __to_dc_lut3d_color(struct dc_rgb *rgb,
672				const struct drm_color_lut lut,
673				int bit_precision)
674{
675	rgb->red = drm_color_lut_extract(lut.red, bit_precision);
676	rgb->green = drm_color_lut_extract(lut.green, bit_precision);
677	rgb->blue  = drm_color_lut_extract(lut.blue, bit_precision);
678}
679
680static void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut,
681				    uint32_t lut3d_size,
682				    struct tetrahedral_params *params,
683				    bool use_tetrahedral_9,
684				    int bit_depth)
685{
686	struct dc_rgb *lut0;
687	struct dc_rgb *lut1;
688	struct dc_rgb *lut2;
689	struct dc_rgb *lut3;
690	int lut_i, i;
691
692
693	if (use_tetrahedral_9) {
694		lut0 = params->tetrahedral_9.lut0;
695		lut1 = params->tetrahedral_9.lut1;
696		lut2 = params->tetrahedral_9.lut2;
697		lut3 = params->tetrahedral_9.lut3;
698	} else {
699		lut0 = params->tetrahedral_17.lut0;
700		lut1 = params->tetrahedral_17.lut1;
701		lut2 = params->tetrahedral_17.lut2;
702		lut3 = params->tetrahedral_17.lut3;
703	}
704
705	for (lut_i = 0, i = 0; i < lut3d_size - 4; lut_i++, i += 4) {
706		/*
707		 * We should consider the 3D LUT RGB values are distributed
708		 * along four arrays lut0-3 where the first sizes 1229 and the
709		 * other 1228. The bit depth supported for 3dlut channel is
710		 * 12-bit, but DC also supports 10-bit.
711		 *
712		 * TODO: improve color pipeline API to enable the userspace set
713		 * bit depth and 3D LUT size/stride, as specified by VA-API.
714		 */
715		__to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth);
716		__to_dc_lut3d_color(&lut1[lut_i], lut[i + 1], bit_depth);
717		__to_dc_lut3d_color(&lut2[lut_i], lut[i + 2], bit_depth);
718		__to_dc_lut3d_color(&lut3[lut_i], lut[i + 3], bit_depth);
719	}
720	/* lut0 has 1229 points (lut_size/4 + 1) */
721	__to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth);
722}
723
724/* amdgpu_dm_atomic_lut3d - set DRM 3D LUT to DC stream
725 * @drm_lut3d: user 3D LUT
726 * @drm_lut3d_size: size of 3D LUT
727 * @lut3d: DC 3D LUT
728 *
729 * Map user 3D LUT data to DC 3D LUT and all necessary bits to program it
730 * on DCN accordingly.
731 */
732static void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut3d,
733				   uint32_t drm_lut3d_size,
734				   struct dc_3dlut *lut)
735{
736	if (!drm_lut3d_size) {
737		lut->state.bits.initialized = 0;
738	} else {
739		/* Stride and bit depth are not programmable by API yet.
740		 * Therefore, only supports 17x17x17 3D LUT (12-bit).
741		 */
742		lut->lut_3d.use_tetrahedral_9 = false;
743		lut->lut_3d.use_12bits = true;
744		lut->state.bits.initialized = 1;
745		__drm_3dlut_to_dc_3dlut(drm_lut3d, drm_lut3d_size, &lut->lut_3d,
746					lut->lut_3d.use_tetrahedral_9,
747					MAX_COLOR_3DLUT_BITDEPTH);
748	}
749}
750
751static int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *shaper_lut,
752				       bool has_rom,
753				       enum dc_transfer_func_predefined tf,
754				       uint32_t shaper_size,
755				       struct dc_transfer_func *func_shaper)
756{
757	int ret = 0;
758
759	if (shaper_size || tf != TRANSFER_FUNCTION_LINEAR) {
760		/*
761		 * If user shaper LUT is set, we assume a linear color space
762		 * (linearized by degamma 1D LUT or not).
763		 */
764		func_shaper->type = TF_TYPE_DISTRIBUTED_POINTS;
765		func_shaper->tf = tf;
766		func_shaper->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
767
768		ret = __set_output_tf(func_shaper, shaper_lut, shaper_size, has_rom);
769	} else {
770		func_shaper->type = TF_TYPE_BYPASS;
771		func_shaper->tf = TRANSFER_FUNCTION_LINEAR;
772	}
773
774	return ret;
775}
776
777static int amdgpu_dm_atomic_blend_lut(const struct drm_color_lut *blend_lut,
778				       bool has_rom,
779				       enum dc_transfer_func_predefined tf,
780				       uint32_t blend_size,
781				       struct dc_transfer_func *func_blend)
782{
783	int ret = 0;
784
785	if (blend_size || tf != TRANSFER_FUNCTION_LINEAR) {
786		/*
787		 * DRM plane gamma LUT or TF means we are linearizing color
788		 * space before blending (similar to degamma programming). As
789		 * we don't have hardcoded curve support, or we use AMD color
790		 * module to fill the parameters that will be translated to HW
791		 * points.
792		 */
793		func_blend->type = TF_TYPE_DISTRIBUTED_POINTS;
794		func_blend->tf = tf;
795		func_blend->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
796
797		ret = __set_input_tf(NULL, func_blend, blend_lut, blend_size);
798	} else {
799		func_blend->type = TF_TYPE_BYPASS;
800		func_blend->tf = TRANSFER_FUNCTION_LINEAR;
801	}
802
803	return ret;
804}
805
806/**
807 * amdgpu_dm_verify_lut3d_size - verifies if 3D LUT is supported and if user
808 * shaper and 3D LUTs match the hw supported size
809 * @adev: amdgpu device
810 * @plane_state: the DRM plane state
811 *
812 * Verifies if pre-blending (DPP) 3D LUT is supported by the HW (DCN 2.0 or
813 * newer) and if the user shaper and 3D LUTs match the supported size.
814 *
815 * Returns:
816 * 0 on success. -EINVAL if lut size are invalid.
817 */
818int amdgpu_dm_verify_lut3d_size(struct amdgpu_device *adev,
819				struct drm_plane_state *plane_state)
820{
821	struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
822	const struct drm_color_lut *shaper = NULL, *lut3d = NULL;
823	uint32_t exp_size, size, dim_size = MAX_COLOR_3DLUT_SIZE;
824	bool has_3dlut = adev->dm.dc->caps.color.dpp.hw_3d_lut;
825
826	/* shaper LUT is only available if 3D LUT color caps */
827	exp_size = has_3dlut ? MAX_COLOR_LUT_ENTRIES : 0;
828	shaper = __extract_blob_lut(dm_plane_state->shaper_lut, &size);
829
830	if (shaper && size != exp_size) {
831		drm_dbg(&adev->ddev,
832			"Invalid Shaper LUT size. Should be %u but got %u.\n",
833			exp_size, size);
834		return -EINVAL;
835	}
836
837	/* The number of 3D LUT entries is the dimension size cubed */
838	exp_size = has_3dlut ? dim_size * dim_size * dim_size : 0;
839	lut3d = __extract_blob_lut(dm_plane_state->lut3d, &size);
840
841	if (lut3d && size != exp_size) {
842		drm_dbg(&adev->ddev,
843			"Invalid 3D LUT size. Should be %u but got %u.\n",
844			exp_size, size);
845		return -EINVAL;
846	}
847
848	return 0;
849}
850
851/**
852 * amdgpu_dm_verify_lut_sizes - verifies if DRM luts match the hw supported sizes
853 * @crtc_state: the DRM CRTC state
854 *
855 * Verifies that the Degamma and Gamma LUTs attached to the &crtc_state
856 * are of the expected size.
857 *
858 * Returns:
859 * 0 on success. -EINVAL if any lut sizes are invalid.
860 */
861int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state)
862{
863	const struct drm_color_lut *lut = NULL;
864	uint32_t size = 0;
865
866	lut = __extract_blob_lut(crtc_state->degamma_lut, &size);
867	if (lut && size != MAX_COLOR_LUT_ENTRIES) {
868		DRM_DEBUG_DRIVER(
869			"Invalid Degamma LUT size. Should be %u but got %u.\n",
870			MAX_COLOR_LUT_ENTRIES, size);
871		return -EINVAL;
872	}
873
874	lut = __extract_blob_lut(crtc_state->gamma_lut, &size);
875	if (lut && size != MAX_COLOR_LUT_ENTRIES &&
876	    size != MAX_COLOR_LEGACY_LUT_ENTRIES) {
877		DRM_DEBUG_DRIVER(
878			"Invalid Gamma LUT size. Should be %u (or %u for legacy) but got %u.\n",
879			MAX_COLOR_LUT_ENTRIES, MAX_COLOR_LEGACY_LUT_ENTRIES,
880			size);
881		return -EINVAL;
882	}
883
884	return 0;
885}
886
887/**
888 * amdgpu_dm_update_crtc_color_mgmt: Maps DRM color management to DC stream.
889 * @crtc: amdgpu_dm crtc state
890 *
891 * With no plane level color management properties we're free to use any
892 * of the HW blocks as long as the CRTC CTM always comes before the
893 * CRTC RGM and after the CRTC DGM.
894 *
895 * - The CRTC RGM block will be placed in the RGM LUT block if it is non-linear.
896 * - The CRTC DGM block will be placed in the DGM LUT block if it is non-linear.
897 * - The CRTC CTM will be placed in the gamut remap block if it is non-linear.
898 *
899 * The RGM block is typically more fully featured and accurate across
900 * all ASICs - DCE can't support a custom non-linear CRTC DGM.
901 *
902 * For supporting both plane level color management and CRTC level color
903 * management at once we have to either restrict the usage of CRTC properties
904 * or blend adjustments together.
905 *
906 * Returns:
907 * 0 on success. Error code if setup fails.
908 */
909int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc)
910{
911	struct dc_stream_state *stream = crtc->stream;
912	struct amdgpu_device *adev = drm_to_adev(crtc->base.state->dev);
913	bool has_rom = adev->asic_type <= CHIP_RAVEN;
914	struct drm_color_ctm *ctm = NULL;
915	const struct drm_color_lut *degamma_lut, *regamma_lut;
916	uint32_t degamma_size, regamma_size;
917	bool has_regamma, has_degamma;
918	enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_LINEAR;
919	bool is_legacy;
920	int r;
921
922	tf = amdgpu_tf_to_dc_tf(crtc->regamma_tf);
923
924	r = amdgpu_dm_verify_lut_sizes(&crtc->base);
925	if (r)
926		return r;
927
928	degamma_lut = __extract_blob_lut(crtc->base.degamma_lut, &degamma_size);
929	regamma_lut = __extract_blob_lut(crtc->base.gamma_lut, &regamma_size);
930
931	has_degamma =
932		degamma_lut && !__is_lut_linear(degamma_lut, degamma_size);
933
934	has_regamma =
935		regamma_lut && !__is_lut_linear(regamma_lut, regamma_size);
936
937	is_legacy = regamma_size == MAX_COLOR_LEGACY_LUT_ENTRIES;
938
939	/* Reset all adjustments. */
940	crtc->cm_has_degamma = false;
941	crtc->cm_is_degamma_srgb = false;
942
943	/* Setup regamma and degamma. */
944	if (is_legacy) {
945		/*
946		 * Legacy regamma forces us to use the sRGB RGM as a base.
947		 * This also means we can't use linear DGM since DGM needs
948		 * to use sRGB as a base as well, resulting in incorrect CRTC
949		 * DGM and CRTC CTM.
950		 *
951		 * TODO: Just map this to the standard regamma interface
952		 * instead since this isn't really right. One of the cases
953		 * where this setup currently fails is trying to do an
954		 * inverse color ramp in legacy userspace.
955		 */
956		crtc->cm_is_degamma_srgb = true;
957		stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
958		stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
959		/*
960		 * Note: although we pass has_rom as parameter here, we never
961		 * actually use ROM because the color module only takes the ROM
962		 * path if transfer_func->type == PREDEFINED.
963		 *
964		 * See more in mod_color_calculate_regamma_params()
965		 */
966		r = __set_legacy_tf(stream->out_transfer_func, regamma_lut,
967				    regamma_size, has_rom);
968		if (r)
969			return r;
970	} else {
971		regamma_size = has_regamma ? regamma_size : 0;
972		r = amdgpu_dm_set_atomic_regamma(stream, regamma_lut,
973						 regamma_size, has_rom, tf);
974		if (r)
975			return r;
976	}
977
978	/*
979	 * CRTC DGM goes into DGM LUT. It would be nice to place it
980	 * into the RGM since it's a more featured block but we'd
981	 * have to place the CTM in the OCSC in that case.
982	 */
983	crtc->cm_has_degamma = has_degamma;
984
985	/* Setup CRTC CTM. */
986	if (crtc->base.ctm) {
987		ctm = (struct drm_color_ctm *)crtc->base.ctm->data;
988
989		/*
990		 * Gamut remapping must be used for gamma correction
991		 * since it comes before the regamma correction.
992		 *
993		 * OCSC could be used for gamma correction, but we'd need to
994		 * blend the adjustments together with the required output
995		 * conversion matrix - so just use the gamut remap block
996		 * for now.
997		 */
998		__drm_ctm_to_dc_matrix(ctm, stream->gamut_remap_matrix.matrix);
999
1000		stream->gamut_remap_matrix.enable_remap = true;
1001		stream->csc_color_matrix.enable_adjustment = false;
1002	} else {
1003		/* Bypass CTM. */
1004		stream->gamut_remap_matrix.enable_remap = false;
1005		stream->csc_color_matrix.enable_adjustment = false;
1006	}
1007
1008	return 0;
1009}
1010
1011static int
1012map_crtc_degamma_to_dc_plane(struct dm_crtc_state *crtc,
1013			     struct dc_plane_state *dc_plane_state,
1014			     struct dc_color_caps *caps)
1015{
1016	const struct drm_color_lut *degamma_lut;
1017	enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
1018	uint32_t degamma_size;
1019	int r;
1020
1021	/* Get the correct base transfer function for implicit degamma. */
1022	switch (dc_plane_state->format) {
1023	case SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr:
1024	case SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb:
1025		/* DC doesn't have a transfer function for BT601 specifically. */
1026		tf = TRANSFER_FUNCTION_BT709;
1027		break;
1028	default:
1029		break;
1030	}
1031
1032	if (crtc->cm_has_degamma) {
1033		degamma_lut = __extract_blob_lut(crtc->base.degamma_lut,
1034						 &degamma_size);
1035		ASSERT(degamma_size == MAX_COLOR_LUT_ENTRIES);
1036
1037		dc_plane_state->in_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
1038
1039		/*
1040		 * This case isn't fully correct, but also fairly
1041		 * uncommon. This is userspace trying to use a
1042		 * legacy gamma LUT + atomic degamma LUT
1043		 * at the same time.
1044		 *
1045		 * Legacy gamma requires the input to be in linear
1046		 * space, so that means we need to apply an sRGB
1047		 * degamma. But color module also doesn't support
1048		 * a user ramp in this case so the degamma will
1049		 * be lost.
1050		 *
1051		 * Even if we did support it, it's still not right:
1052		 *
1053		 * Input -> CRTC DGM -> sRGB DGM -> CRTC CTM ->
1054		 * sRGB RGM -> CRTC RGM -> Output
1055		 *
1056		 * The CSC will be done in the wrong space since
1057		 * we're applying an sRGB DGM on top of the CRTC
1058		 * DGM.
1059		 *
1060		 * TODO: Don't use the legacy gamma interface and just
1061		 * map these to the atomic one instead.
1062		 */
1063		if (crtc->cm_is_degamma_srgb)
1064			dc_plane_state->in_transfer_func->tf = tf;
1065		else
1066			dc_plane_state->in_transfer_func->tf =
1067				TRANSFER_FUNCTION_LINEAR;
1068
1069		r = __set_input_tf(caps, dc_plane_state->in_transfer_func,
1070				   degamma_lut, degamma_size);
1071		if (r)
1072			return r;
1073	} else {
1074		/*
1075		 * For legacy gamma support we need the regamma input
1076		 * in linear space. Assume that the input is sRGB.
1077		 */
1078		dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED;
1079		dc_plane_state->in_transfer_func->tf = tf;
1080
1081		if (tf != TRANSFER_FUNCTION_SRGB &&
1082		    !mod_color_calculate_degamma_params(caps,
1083							dc_plane_state->in_transfer_func,
1084							NULL, false))
1085			return -ENOMEM;
1086	}
1087
1088	return 0;
1089}
1090
1091static int
1092__set_dm_plane_degamma(struct drm_plane_state *plane_state,
1093		       struct dc_plane_state *dc_plane_state,
1094		       struct dc_color_caps *color_caps)
1095{
1096	struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
1097	const struct drm_color_lut *degamma_lut;
1098	enum amdgpu_transfer_function tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
1099	uint32_t degamma_size;
1100	bool has_degamma_lut;
1101	int ret;
1102
1103	degamma_lut = __extract_blob_lut(dm_plane_state->degamma_lut,
1104					 &degamma_size);
1105
1106	has_degamma_lut = degamma_lut &&
1107			  !__is_lut_linear(degamma_lut, degamma_size);
1108
1109	tf = dm_plane_state->degamma_tf;
1110
1111	/* If we don't have plane degamma LUT nor TF to set on DC, we have
1112	 * nothing to do here, return.
1113	 */
1114	if (!has_degamma_lut && tf == AMDGPU_TRANSFER_FUNCTION_DEFAULT)
1115		return -EINVAL;
1116
1117	dc_plane_state->in_transfer_func->tf = amdgpu_tf_to_dc_tf(tf);
1118
1119	if (has_degamma_lut) {
1120		ASSERT(degamma_size == MAX_COLOR_LUT_ENTRIES);
1121
1122		dc_plane_state->in_transfer_func->type =
1123			TF_TYPE_DISTRIBUTED_POINTS;
1124
1125		ret = __set_input_tf(color_caps, dc_plane_state->in_transfer_func,
1126				     degamma_lut, degamma_size);
1127		if (ret)
1128			return ret;
1129       } else {
1130		dc_plane_state->in_transfer_func->type =
1131			TF_TYPE_PREDEFINED;
1132
1133		if (!mod_color_calculate_degamma_params(color_caps,
1134		    dc_plane_state->in_transfer_func, NULL, false))
1135			return -ENOMEM;
1136	}
1137	return 0;
1138}
1139
1140static int
1141amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state,
1142				     struct dc_plane_state *dc_plane_state)
1143{
1144	struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
1145	enum amdgpu_transfer_function shaper_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
1146	enum amdgpu_transfer_function blend_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
1147	const struct drm_color_lut *shaper_lut, *lut3d, *blend_lut;
1148	uint32_t shaper_size, lut3d_size, blend_size;
1149	int ret;
1150
1151	dc_plane_state->hdr_mult = amdgpu_dm_fixpt_from_s3132(dm_plane_state->hdr_mult);
1152
1153	shaper_lut = __extract_blob_lut(dm_plane_state->shaper_lut, &shaper_size);
1154	shaper_size = shaper_lut != NULL ? shaper_size : 0;
1155	shaper_tf = dm_plane_state->shaper_tf;
1156	lut3d = __extract_blob_lut(dm_plane_state->lut3d, &lut3d_size);
1157	lut3d_size = lut3d != NULL ? lut3d_size : 0;
1158
1159	amdgpu_dm_atomic_lut3d(lut3d, lut3d_size, dc_plane_state->lut3d_func);
1160	ret = amdgpu_dm_atomic_shaper_lut(shaper_lut, false,
1161					  amdgpu_tf_to_dc_tf(shaper_tf),
1162					  shaper_size,
1163					  dc_plane_state->in_shaper_func);
1164	if (ret) {
1165		drm_dbg_kms(plane_state->plane->dev,
1166			    "setting plane %d shaper LUT failed.\n",
1167			    plane_state->plane->index);
1168
1169		return ret;
1170	}
1171
1172	blend_tf = dm_plane_state->blend_tf;
1173	blend_lut = __extract_blob_lut(dm_plane_state->blend_lut, &blend_size);
1174	blend_size = blend_lut != NULL ? blend_size : 0;
1175
1176	ret = amdgpu_dm_atomic_blend_lut(blend_lut, false,
1177					 amdgpu_tf_to_dc_tf(blend_tf),
1178					 blend_size, dc_plane_state->blend_tf);
1179	if (ret) {
1180		drm_dbg_kms(plane_state->plane->dev,
1181			    "setting plane %d gamma lut failed.\n",
1182			    plane_state->plane->index);
1183
1184		return ret;
1185	}
1186
1187	return 0;
1188}
1189
1190/**
1191 * amdgpu_dm_update_plane_color_mgmt: Maps DRM color management to DC plane.
1192 * @crtc: amdgpu_dm crtc state
1193 * @plane_state: DRM plane state
1194 * @dc_plane_state: target DC surface
1195 *
1196 * Update the underlying dc_stream_state's input transfer function (ITF) in
1197 * preparation for hardware commit. The transfer function used depends on
1198 * the preparation done on the stream for color management.
1199 *
1200 * Returns:
1201 * 0 on success. -ENOMEM if mem allocation fails.
1202 */
1203int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
1204				      struct drm_plane_state *plane_state,
1205				      struct dc_plane_state *dc_plane_state)
1206{
1207	struct amdgpu_device *adev = drm_to_adev(crtc->base.state->dev);
1208	struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
1209	struct drm_color_ctm_3x4 *ctm = NULL;
1210	struct dc_color_caps *color_caps = NULL;
1211	bool has_crtc_cm_degamma;
1212	int ret;
1213
1214	ret = amdgpu_dm_verify_lut3d_size(adev, plane_state);
1215	if (ret) {
1216		drm_dbg_driver(&adev->ddev, "amdgpu_dm_verify_lut3d_size() failed\n");
1217		return ret;
1218	}
1219
1220	if (dc_plane_state->ctx && dc_plane_state->ctx->dc)
1221		color_caps = &dc_plane_state->ctx->dc->caps.color;
1222
1223	/* Initially, we can just bypass the DGM block. */
1224	dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;
1225	dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
1226
1227	/* After, we start to update values according to color props */
1228	has_crtc_cm_degamma = (crtc->cm_has_degamma || crtc->cm_is_degamma_srgb);
1229
1230	ret = __set_dm_plane_degamma(plane_state, dc_plane_state, color_caps);
1231	if (ret == -ENOMEM)
1232		return ret;
1233
1234	/* We only have one degamma block available (pre-blending) for the
1235	 * whole color correction pipeline, so that we can't actually perform
1236	 * plane and CRTC degamma at the same time. Explicitly reject atomic
1237	 * updates when userspace sets both plane and CRTC degamma properties.
1238	 */
1239	if (has_crtc_cm_degamma && ret != -EINVAL) {
1240		drm_dbg_kms(crtc->base.crtc->dev,
1241			    "doesn't support plane and CRTC degamma at the same time\n");
1242		return -EINVAL;
1243	}
1244
1245	/* If we are here, it means we don't have plane degamma settings, check
1246	 * if we have CRTC degamma waiting for mapping to pre-blending degamma
1247	 * block
1248	 */
1249	if (has_crtc_cm_degamma) {
1250		/*
1251		 * AMD HW doesn't have post-blending degamma caps. When DRM
1252		 * CRTC atomic degamma is set, we maps it to DPP degamma block
1253		 * (pre-blending) or, on legacy gamma, we use DPP degamma to
1254		 * linearize (implicit degamma) from sRGB/BT709 according to
1255		 * the input space.
1256		 */
1257		ret = map_crtc_degamma_to_dc_plane(crtc, dc_plane_state, color_caps);
1258		if (ret)
1259			return ret;
1260	}
1261
1262	/* Setup CRTC CTM. */
1263	if (dm_plane_state->ctm) {
1264		ctm = (struct drm_color_ctm_3x4 *)dm_plane_state->ctm->data;
1265		/*
1266		 * DCN2 and older don't support both pre-blending and
1267		 * post-blending gamut remap. For this HW family, if we have
1268		 * the plane and CRTC CTMs simultaneously, CRTC CTM takes
1269		 * priority, and we discard plane CTM, as implemented in
1270		 * dcn10_program_gamut_remap(). However, DCN3+ has DPP
1271		 * (pre-blending) and MPC (post-blending) `gamut remap` blocks;
1272		 * therefore, we can program plane and CRTC CTMs together by
1273		 * mapping CRTC CTM to MPC and keeping plane CTM setup at DPP,
1274		 * as it's done by dcn30_program_gamut_remap().
1275		 */
1276		__drm_ctm_3x4_to_dc_matrix(ctm, dc_plane_state->gamut_remap_matrix.matrix);
1277
1278		dc_plane_state->gamut_remap_matrix.enable_remap = true;
1279		dc_plane_state->input_csc_color_matrix.enable_adjustment = false;
1280	} else {
1281		/* Bypass CTM. */
1282		dc_plane_state->gamut_remap_matrix.enable_remap = false;
1283		dc_plane_state->input_csc_color_matrix.enable_adjustment = false;
1284	}
1285
1286	return amdgpu_dm_plane_set_color_properties(plane_state, dc_plane_state);
1287}
1288