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, °amma_size); 929 regamma_lut = __extract_blob_lut(crtc->base.gamma_lut, ®amma_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 °amma_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 °amma_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