Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

drm/amd/display: add shaper and blend colorops for 1D Curve Custom LUT

This patch adds colorops for custom 1D LUTs in the SHAPER and
BLND HW blocks.

With this change the following IGT tests pass:
kms_colorop --run plane-XR30-XR30-srgb_inv_eotf_lut
kms_colorop --run plane-XR30-XR30-srgb_inv_eotf_lut-srgb_eotf_lut

The color pipeline now consists of the following colorops:
1. 1D curve colorop
2. 1D curve colorop
3. 1D LUT
4. 1D curve colorop
5. 1D LUT

The 1D curve colorops support sRGB, BT2020, and PQ scaled to 125.0.

Signed-off-by: Alex Hung <alex.hung@amd.com>
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Daniel Stone <daniels@collabora.com>
Signed-off-by: Simon Ser <contact@emersion.fr>
Link: https://patch.msgid.link/20251115000237.3561250-39-alex.hung@amd.com

authored by

Alex Hung and committed by
Simon Ser
5ed78b44 99a4e4f0

+252 -78
+222 -78
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
··· 73 73 */ 74 74 75 75 #define MAX_DRM_LUT_VALUE 0xFFFF 76 + #define MAX_DRM_LUT32_VALUE 0xFFFFFFFF 76 77 #define SDR_WHITE_LEVEL_INIT_VALUE 80 77 78 78 79 /** ··· 344 343 } 345 344 346 345 /** 346 + * __extract_blob_lut32 - Extracts the DRM lut and lut size from a blob. 347 + * @blob: DRM color mgmt property blob 348 + * @size: lut size 349 + * 350 + * Returns: 351 + * DRM LUT or NULL 352 + */ 353 + static const struct drm_color_lut32 * 354 + __extract_blob_lut32(const struct drm_property_blob *blob, uint32_t *size) 355 + { 356 + *size = blob ? drm_color_lut32_size(blob) : 0; 357 + return blob ? (struct drm_color_lut32 *)blob->data : NULL; 358 + } 359 + 360 + /** 347 361 * __is_lut_linear - check if the given lut is a linear mapping of values 348 362 * @lut: given lut to check values 349 363 * @size: lut size ··· 428 412 gamma->entries.red[i] = dc_fixpt_from_fraction(r, MAX_DRM_LUT_VALUE); 429 413 gamma->entries.green[i] = dc_fixpt_from_fraction(g, MAX_DRM_LUT_VALUE); 430 414 gamma->entries.blue[i] = dc_fixpt_from_fraction(b, MAX_DRM_LUT_VALUE); 415 + } 416 + } 417 + 418 + /** 419 + * __drm_lut32_to_dc_gamma - convert the drm_color_lut to dc_gamma. 420 + * @lut: DRM lookup table for color conversion 421 + * @gamma: DC gamma to set entries 422 + * 423 + * The conversion depends on the size of the lut - whether or not it's legacy. 424 + */ 425 + static void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, struct dc_gamma *gamma) 426 + { 427 + int i; 428 + 429 + for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) { 430 + gamma->entries.red[i] = dc_fixpt_from_fraction(lut[i].red, MAX_DRM_LUT32_VALUE); 431 + gamma->entries.green[i] = dc_fixpt_from_fraction(lut[i].green, MAX_DRM_LUT32_VALUE); 432 + gamma->entries.blue[i] = dc_fixpt_from_fraction(lut[i].blue, MAX_DRM_LUT32_VALUE); 431 433 } 432 434 } 433 435 ··· 601 567 return res ? 0 : -ENOMEM; 602 568 } 603 569 570 + /** 571 + * __set_output_tf_32 - calculates the output transfer function based on expected input space. 572 + * @func: transfer function 573 + * @lut: lookup table that defines the color space 574 + * @lut_size: size of respective lut 575 + * @has_rom: if ROM can be used for hardcoded curve 576 + * 577 + * Returns: 578 + * 0 in case of success. -ENOMEM if fails. 579 + */ 580 + static int __set_output_tf_32(struct dc_transfer_func *func, 581 + const struct drm_color_lut32 *lut, uint32_t lut_size, 582 + bool has_rom) 583 + { 584 + struct dc_gamma *gamma = NULL; 585 + struct calculate_buffer cal_buffer = {0}; 586 + bool res; 587 + 588 + cal_buffer.buffer_index = -1; 589 + 590 + if (lut_size) { 591 + gamma = dc_create_gamma(); 592 + if (!gamma) 593 + return -ENOMEM; 594 + 595 + gamma->num_entries = lut_size; 596 + __drm_lut32_to_dc_gamma(lut, gamma); 597 + } 598 + 599 + if (func->tf == TRANSFER_FUNCTION_LINEAR) { 600 + /* 601 + * Color module doesn't like calculating regamma params 602 + * on top of a linear input. But degamma params can be used 603 + * instead to simulate this. 604 + */ 605 + if (gamma) 606 + gamma->type = GAMMA_CUSTOM; 607 + res = mod_color_calculate_degamma_params(NULL, func, 608 + gamma, gamma != NULL); 609 + } else { 610 + /* 611 + * Assume sRGB. The actual mapping will depend on whether the 612 + * input was legacy or not. 613 + */ 614 + if (gamma) 615 + gamma->type = GAMMA_CS_TFM_1D; 616 + res = mod_color_calculate_regamma_params(func, gamma, gamma != NULL, 617 + has_rom, NULL, &cal_buffer); 618 + } 619 + 620 + if (gamma) 621 + dc_gamma_release(&gamma); 622 + 623 + return res ? 0 : -ENOMEM; 624 + } 625 + 626 + 604 627 static int amdgpu_dm_set_atomic_regamma(struct dc_transfer_func *out_tf, 605 628 const struct drm_color_lut *regamma_lut, 606 629 uint32_t regamma_size, bool has_rom, ··· 720 629 gamma->num_entries = lut_size; 721 630 722 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 + 642 + /** 643 + * __set_input_tf_32 - calculates the input transfer function based on expected 644 + * input space. 645 + * @caps: dc color capabilities 646 + * @func: transfer function 647 + * @lut: lookup table that defines the color space 648 + * @lut_size: size of respective lut. 649 + * 650 + * Returns: 651 + * 0 in case of success. -ENOMEM if fails. 652 + */ 653 + static int __set_input_tf_32(struct dc_color_caps *caps, struct dc_transfer_func *func, 654 + const struct drm_color_lut32 *lut, uint32_t lut_size) 655 + { 656 + struct dc_gamma *gamma = NULL; 657 + bool res; 658 + 659 + if (lut_size) { 660 + gamma = dc_create_gamma(); 661 + if (!gamma) 662 + return -ENOMEM; 663 + 664 + gamma->type = GAMMA_CUSTOM; 665 + gamma->num_entries = lut_size; 666 + 667 + __drm_lut32_to_dc_gamma(lut, gamma); 723 668 } 724 669 725 670 res = mod_color_calculate_degamma_params(caps, func, gamma, gamma != NULL); ··· 1379 1252 } 1380 1253 1381 1254 static int 1382 - __set_colorop_in_shaper_1d_curve(struct dc_plane_state *dc_plane_state, 1383 - struct drm_colorop_state *colorop_state) 1384 - { 1385 - struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func; 1386 - struct drm_colorop *colorop = colorop_state->colorop; 1387 - struct drm_device *drm = colorop->dev; 1388 - 1389 - if (colorop->type != DRM_COLOROP_1D_CURVE) 1390 - return -EINVAL; 1391 - 1392 - if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) 1393 - return -EINVAL; 1394 - 1395 - if (colorop_state->bypass) { 1396 - tf->type = TF_TYPE_BYPASS; 1397 - tf->tf = TRANSFER_FUNCTION_LINEAR; 1398 - return 0; 1399 - } 1400 - 1401 - drm_dbg(drm, "Shaper colorop with ID: %d\n", colorop->base.id); 1402 - 1403 - if (colorop->type == DRM_COLOROP_1D_CURVE) { 1404 - tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1405 - tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); 1406 - tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; 1407 - return __set_output_tf(tf, 0, 0, false); 1408 - } 1409 - 1410 - return -EINVAL; 1411 - } 1412 - 1413 - static int 1414 1255 __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, 1415 1256 struct dc_plane_state *dc_plane_state, 1416 1257 struct drm_colorop *colorop) ··· 1386 1291 struct drm_colorop *old_colorop; 1387 1292 struct drm_colorop_state *colorop_state = NULL, *new_colorop_state; 1388 1293 struct drm_atomic_state *state = plane_state->state; 1389 - int i = 0; 1294 + enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR; 1295 + struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func; 1296 + const struct drm_color_lut32 *shaper_lut; 1297 + struct drm_device *dev = colorop->dev; 1298 + u32 shaper_size; 1299 + int i = 0, ret = 0; 1390 1300 1301 + /* 1D Curve - SHAPER TF */ 1391 1302 old_colorop = colorop; 1392 - 1393 - /* 2nd op: 1d curve - shaper */ 1394 1303 for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { 1395 1304 if (new_colorop_state->colorop == old_colorop && 1396 1305 (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) { 1397 1306 colorop_state = new_colorop_state; 1398 1307 break; 1399 1308 } 1309 + } 1400 1310 1401 - if (new_colorop_state->colorop == old_colorop) { 1311 + if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_CURVE) { 1312 + drm_dbg(dev, "Shaper TF colorop with ID: %d\n", colorop->base.id); 1313 + tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1314 + tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); 1315 + tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; 1316 + ret = __set_output_tf(tf, 0, 0, false); 1317 + if (ret) 1318 + return ret; 1319 + } 1320 + 1321 + /* 1D LUT - SHAPER LUT */ 1322 + colorop = old_colorop->next; 1323 + if (!colorop) { 1324 + drm_dbg(dev, "no Shaper LUT colorop found\n"); 1325 + return -EINVAL; 1326 + } 1327 + 1328 + old_colorop = colorop; 1329 + for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { 1330 + if (new_colorop_state->colorop == old_colorop && 1331 + new_colorop_state->colorop->type == DRM_COLOROP_1D_LUT) { 1402 1332 colorop_state = new_colorop_state; 1403 1333 break; 1404 1334 } 1405 1335 } 1406 1336 1407 - if (!colorop_state) 1408 - return -EINVAL; 1409 - 1410 - return __set_colorop_in_shaper_1d_curve(dc_plane_state, colorop_state); 1411 - } 1412 - 1413 - static int 1414 - __set_colorop_1d_curve_blend_tf_lut(struct dc_plane_state *dc_plane_state, 1415 - struct drm_colorop_state *colorop_state) 1416 - { 1417 - struct dc_transfer_func *tf = &dc_plane_state->blend_tf; 1418 - struct drm_colorop *colorop = colorop_state->colorop; 1419 - struct drm_device *drm = colorop->dev; 1420 - const struct drm_color_lut *blend_lut = NULL; 1421 - u32 blend_size = 0; 1422 - 1423 - if (colorop->type != DRM_COLOROP_1D_CURVE) 1424 - return -EINVAL; 1425 - 1426 - if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) 1427 - return -EINVAL; 1428 - 1429 - if (colorop_state->bypass) { 1430 - tf->type = TF_TYPE_BYPASS; 1431 - tf->tf = TRANSFER_FUNCTION_LINEAR; 1432 - return 0; 1433 - } 1434 - 1435 - drm_dbg(drm, "Blend colorop with ID: %d\n", colorop->base.id); 1436 - 1437 - if (colorop->type == DRM_COLOROP_1D_CURVE) { 1337 + if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_LUT) { 1338 + drm_dbg(dev, "Shaper LUT colorop with ID: %d\n", colorop->base.id); 1438 1339 tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1439 - tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); 1340 + tf->tf = default_tf; 1440 1341 tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; 1441 - return __set_input_tf(NULL, tf, blend_lut, blend_size); 1342 + shaper_lut = __extract_blob_lut32(colorop_state->data, &shaper_size); 1343 + shaper_size = shaper_lut != NULL ? shaper_size : 0; 1344 + 1345 + /* Custom LUT size must be the same as supported size */ 1346 + if (shaper_size == colorop->size) { 1347 + ret = __set_output_tf_32(tf, shaper_lut, shaper_size, false); 1348 + if (ret) 1349 + return ret; 1350 + } 1442 1351 } 1443 1352 1444 - return -EINVAL; 1353 + return 0; 1445 1354 } 1446 1355 1447 1356 static int ··· 1456 1357 struct drm_colorop *old_colorop; 1457 1358 struct drm_colorop_state *colorop_state = NULL, *new_colorop_state; 1458 1359 struct drm_atomic_state *state = plane_state->state; 1360 + enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR; 1361 + struct dc_transfer_func *tf = &dc_plane_state->blend_tf; 1362 + const struct drm_color_lut32 *blend_lut = NULL; 1363 + struct drm_device *dev = colorop->dev; 1364 + uint32_t blend_size = 0; 1459 1365 int i = 0; 1460 1366 1367 + /* 1D Curve - BLND TF */ 1461 1368 old_colorop = colorop; 1462 - 1463 - /* 3nd op: 1d curve - blend */ 1464 1369 for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { 1465 1370 if (new_colorop_state->colorop == old_colorop && 1466 1371 (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) { 1467 1372 colorop_state = new_colorop_state; 1468 1373 break; 1469 1374 } 1375 + } 1470 1376 1471 - if (new_colorop_state->colorop == old_colorop) { 1377 + if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_CURVE && 1378 + (BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) { 1379 + drm_dbg(dev, "Blend TF colorop with ID: %d\n", colorop->base.id); 1380 + tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1381 + tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); 1382 + tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; 1383 + __set_input_tf_32(NULL, tf, blend_lut, blend_size); 1384 + } 1385 + 1386 + /* 1D Curve - BLND LUT */ 1387 + colorop = old_colorop->next; 1388 + if (!colorop) { 1389 + drm_dbg(dev, "no Blend LUT colorop found\n"); 1390 + return -EINVAL; 1391 + } 1392 + 1393 + old_colorop = colorop; 1394 + for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { 1395 + if (new_colorop_state->colorop == old_colorop && 1396 + new_colorop_state->colorop->type == DRM_COLOROP_1D_LUT) { 1472 1397 colorop_state = new_colorop_state; 1473 1398 break; 1474 1399 } 1475 1400 } 1476 1401 1477 - if (!colorop_state) 1478 - return -EINVAL; 1402 + if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_LUT && 1403 + (BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) { 1404 + drm_dbg(dev, "Blend LUT colorop with ID: %d\n", colorop->base.id); 1405 + tf->type = TF_TYPE_DISTRIBUTED_POINTS; 1406 + tf->tf = default_tf; 1407 + tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; 1408 + blend_lut = __extract_blob_lut32(colorop_state->data, &blend_size); 1409 + blend_size = blend_lut != NULL ? blend_size : 0; 1479 1410 1480 - return __set_colorop_1d_curve_blend_tf_lut(dc_plane_state, colorop_state); 1411 + /* Custom LUT size must be the same as supported size */ 1412 + if (blend_size == colorop->size) 1413 + __set_input_tf_32(NULL, tf, blend_lut, blend_size); 1414 + } 1415 + 1416 + return 0; 1481 1417 } 1482 1418 1483 1419 static int ··· 1581 1447 if (ret) 1582 1448 return ret; 1583 1449 1584 - /* 1D Curve - SHAPER TF */ 1450 + /* 1D Curve & LUT - SHAPER TF & LUT */ 1585 1451 colorop = colorop->next; 1586 1452 if (!colorop) { 1587 1453 drm_dbg(dev, "no Shaper TF colorop found\n"); ··· 1592 1458 if (ret) 1593 1459 return ret; 1594 1460 1595 - /* 1D Curve - BLND TF */ 1461 + /* Shaper LUT colorop is already handled, just skip here */ 1462 + colorop = colorop->next; 1463 + if (!colorop) 1464 + return -EINVAL; 1465 + 1466 + /* 1D Curve & LUT - BLND TF & LUT */ 1596 1467 colorop = colorop->next; 1597 1468 if (!colorop) { 1598 1469 drm_dbg(dev, "no Blend TF colorop found\n"); ··· 1607 1468 ret = __set_dm_plane_colorop_blend(plane_state, dc_plane_state, colorop); 1608 1469 if (ret) 1609 1470 return ret; 1471 + 1472 + /* BLND LUT colorop is already handled, just skip here */ 1473 + colorop = colorop->next; 1474 + if (!colorop) 1475 + return -EINVAL; 1610 1476 1611 1477 return 0; 1612 1478 }
+30
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
··· 29 29 #include <drm/drm_property.h> 30 30 #include <drm/drm_colorop.h> 31 31 32 + #include "amdgpu.h" 32 33 #include "amdgpu_dm_colorop.h" 33 34 34 35 const u64 amdgpu_dm_supported_degam_tfs = ··· 89 88 90 89 i++; 91 90 91 + /* 1D LUT - SHAPER LUT */ 92 + ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); 93 + if (!ops[i]) { 94 + ret = -ENOMEM; 95 + goto cleanup; 96 + } 97 + 98 + ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES); 99 + if (ret) 100 + goto cleanup; 101 + 102 + drm_colorop_set_next_property(ops[i-1], ops[i]); 103 + 104 + i++; 105 + 92 106 /* 1D curve - BLND TF */ 93 107 ops[i] = kzalloc(sizeof(*ops[0]), GFP_KERNEL); 94 108 if (!ops[i]) { ··· 117 101 118 102 drm_colorop_set_next_property(ops[i - 1], ops[i]); 119 103 104 + i++; 105 + 106 + /* 1D LUT - BLND LUT */ 107 + ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); 108 + if (!ops[i]) { 109 + ret = -ENOMEM; 110 + goto cleanup; 111 + } 112 + 113 + ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES); 114 + if (ret) 115 + goto cleanup; 116 + 117 + drm_colorop_set_next_property(ops[i-1], ops[i]); 120 118 return 0; 121 119 122 120 cleanup: