Re: [PATCH v3 3/5] drm/amd/display: use a separate helper to translate degamma curves

Alex Hung <[email protected]> Fri, 31 Jul 2026 19:41:12 -0600
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>

On 7/23/26 07:29, Melissa Wen wrote:
> 
> 
> On 22/07/2026 20:27, Alex Hung wrote:
>>
>>
>> On 6/23/26 09:58, Melissa Wen wrote:
>>> In newer DCN families, there is no hw predefined curves for shaper,
>>> blend and regamma. When userspace sets pre-defined curves for these
>>> blocks, the driver uses AMD color module to program predefined curve as
>>> LUT. However, it was using the same LUT segmentation for EOTF and
>>> inverse EOTF by using the same color management helper. This is causing
>>> banding on blend when PQ predefined curve is set. Besides that, degamma
>>> predefined HW curve cannot be used with subsampled 4:2:0/4:2:2 formats
>>> as it affects the linearity of color space in which HW scaler operates.
>>>
>>> To mitigate banding when using the blend block and better support
>>> subsampled format on degamma, use different translation helpers when
>>> linearizing and delinearizing.
>>>
>>> Tested-by: Matthew Schwartz <[email protected]>
>>> Reviewed-by: Harry Wentland <[email protected]>
>>> Signed-off-by: Melissa Wen <[email protected]>
>>> ---
>>>   .../gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c   | 11 ++++-------
>>>   1 file changed, 4 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c 
>>> b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c
>>> index 1340f673ec3b..c2ea25927765 100644
>>> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c
>>> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c
>>> @@ -493,11 +493,9 @@ bool dcn32_set_mcm_luts(
>>>       if (plane_state->cm.blend_func.type == TF_TYPE_HWPWL)
>>>           lut_params = &plane_state->cm.blend_func.pwl;
>>>       else if (plane_state->cm.blend_func.type == 
>>> TF_TYPE_DISTRIBUTED_POINTS) {
>>> -        result = cm3_helper_translate_curve_to_hw_format(
>>> -            plane_state->ctx,
>>> +        result = cm3_helper_translate_curve_to_degamma_hw_format(
>>>               &plane_state->cm.blend_func,
>>> -            &dpp_base->regamma_params,
>>> -            false);
>>> +            &dpp_base->regamma_params);
>>>           if (!result)
>>>               return result;
>>
>> The above change broke two IGT's kms_colorop subtests (tested on 
>> DCN3.5) as below
> 
> Hi Alex,
> 
> Thanks for pointing it out.
> I tested with DCN301 (all tests pass) and DCN321 (same failures).
> I'm a bit curious about this difference, probably some differences in 
> precision, etc. I'll leave that out.
>>
>> =====================================
>> Starting subtest: plane-XR30-XR30-srgb_inv_eotf_lut-srgb_eotf_lut
>> i 2569 j 0 shift 10 mask 3ff comp1 3bf comp2 3ff, pixel1 3bfeffbf 
>> pixel2 3fffffff
>> [79.216137] (kms_colorop:10824) CRITICAL: Test assertion failure 
>> function colorop_plane_test, file ../tests/kms_colorop.c:281:
>> [79.216143] (kms_colorop:10824) CRITICAL: Failed assertion: 
>> compare_with_bracket(&sw_transform_fb, output_fb)
>> [79.216165] (kms_colorop:10824) CRITICAL: Last errno: 38, Function not 
>> implemented
>> Stack trace: not implemented
>> Subtest plane-XR30-XR30-srgb_inv_eotf_lut-srgb_eotf_lut failed.
>>
>> Starting subtest: plane-XR30-XR30-gamma_2_2-gamma_2_2_inv-gamma_2_2
>> i 15390 j 0 shift 10 mask 3ff comp1 398 comp2 3b8, pixel1 398e6398 
>> pixel2 3b8ee3b8
>> [81.747233] (kms_colorop:10824) CRITICAL: Test assertion failure 
>> function colorop_plane_test, file ../tests/kms_colorop.c:281:
>> [81.747239] (kms_colorop:10824) CRITICAL: Failed assertion: 
>> compare_with_bracket(&sw_transform_fb, output_fb)
>> [81.747256] (kms_colorop:10824) CRITICAL: Last errno: 38, Function not 
>> implemented
>> Stack trace: not implemented
>> Subtest plane-XR30-XR30-gamma_2_2-gamma_2_2_inv-gamma_2_2 failed.
>> =====================================
>>
>> The root cause is from color delta increases, as also confirmed by the 
>> following IGT POC change:
>>
>> --- tests/kms_colorop.c
>> +++ tests/kms_colorop.c
>> @@ -182,7 +182,7 @@ static bool compare_with_bracket(igt_fb_t *in, 
>> igt_fb_t *out)
>>                 return igt_cmp_fb_pixels(in, out, 1, 1);
>>
>>         if (is_amdgpu_device(in->fd))
>> -               return igt_cmp_fb_pixels(in, out, 13, 13);
>> +               return igt_cmp_fb_pixels(in, out, 70, 13);
>>
>>
>> Is there any concerns on reverting above changes (or this patch)?
>>
>> Note: I am looking for early feedback because I have not yet tested on 
>> other DCN hardware.
> 
> Reverting this patch is not ideal because it addresses an issue on HDR 
> Ori game on gamescope with external HDR monitor for DCN301, DCN321 and 
> also DCN35 (IIRC Matt has verified the issue and this series in this hw 
> version).
> 
> I think the possible solution is making 
> cm3_helper_translate_curve_to_degamma_hw_format() take the same segment 
> distribution for PQ/SRGB to any pre-defined TF or LUT.
> Something like removing the if/else clause in dcn30_cm_common and 
> applying PQ/SRGB distribution to all cases, as below:
> 
> diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c b/ 
> drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
> index 66fe7f313ea3..1b4ce77fdd91 100644
> --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
> +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
> @@ -332,6 +332,7 @@ bool cm3_helper_translate_curve_to_degamma_hw_format(
>          int32_t region_start, region_end;
>          int32_t i;
>          uint32_t j, k, seg_distr[MAX_REGIONS_NUMBER], increment, 
> start_index, hw_points;
> +       const uint8_t SEG_COUNT = 9;  // 9 segments, segments are from 
> 2^-9 to 0
> 
>          if (output_tf == NULL || lut_params == NULL || output_tf->type 
> == TF_TYPE_BYPASS)
>                  return false;
> @@ -343,31 +344,12 @@ bool cm3_helper_translate_curve_to_degamma_hw_format(
>          memset(lut_params, 0, sizeof(struct pwl_params));
>          memset(seg_distr, 0, sizeof(seg_distr));
> 
> -       if (output_tf->tf == TRANSFER_FUNCTION_PQ ||
> -           output_tf->tf == TRANSFER_FUNCTION_SRGB) {
> -               /* 9 segments
> -                * segments are from 2^-9 to 0
> -                */
> -               const uint8_t SEG_COUNT = 9;
> -               seg_distr[0] = 0; // Since we only have one point in 
> darkest region
> -               for (k = 1; k < SEG_COUNT; k++)
> -                       seg_distr[k] = k - 1; // 2^(k-1) points per 
> region; halves as k decreases
> +       seg_distr[0] = 0; // Since we only have one point in darkest region
> +       for (k = 1; k < SEG_COUNT; k++)
> +               seg_distr[k] = k - 1; // 2^(k-1) points per region; 
> halves as k decreases
> 
> -               region_start = -SEG_COUNT;
> -               region_end = 0;
> -       } else {
> -               /* 12 segments
> -                * segments are from 2^-12 to 2^0
> -                * There are less than 256 points, for optimization
> -                */
> -               const uint8_t SEG_COUNT = 12;
> -
> -               for (i = 0; i < SEG_COUNT; i++)
> -                       seg_distr[i] = 4;
> -
> -               region_start = -SEG_COUNT;
> -               region_end = 0;
> -       }
> +       region_start = -SEG_COUNT;
> +       region_end = 0;
> 
>          for (i = region_end - region_start; i < MAX_REGIONS_NUMBER ; i++)
>                  seg_distr[i] = -1;
> 
> I'll check if there is any side effect on gamescope with this change 
> ASAP, but I don't think so.
> 
> WDYT?

I tested above on
  - DCN35 - issue fiexed
  - DCN401 - no issue and no regression.

Does it pass DCN321 that you mentioned abouve?

DCN301 doesn't use cm3_helper_translate_curve_to_degamma_hw_format() so 
it wasn't affected.

The changes look good to me, but let's ask @Wentland, Harry for review too.

> 
> Melissa
> 
>>
>>
>>> @@ -554,9 +552,8 @@ bool dcn32_set_input_transfer_func(struct dc *dc,
>>>       if (plane_state->in_transfer_func.type == TF_TYPE_HWPWL)
>>>           params = &plane_state->in_transfer_func.pwl;
>>>       else if (plane_state->in_transfer_func.type == 
>>> TF_TYPE_DISTRIBUTED_POINTS &&
>>> - cm3_helper_translate_curve_to_hw_format(plane_state->ctx,
>>> - &plane_state->in_transfer_func,
>>> -                            &dpp_base->degamma_params, false))
>>> + cm3_helper_translate_curve_to_degamma_hw_format(&plane_state- 
>>> >in_transfer_func,
>>> + &dpp_base->degamma_params))
>>>           params = &dpp_base->degamma_params;
>>>         dpp_base->funcs->dpp_program_gamcor_lut(dpp_base, params);
>>
>