Message ID | 20230925194932.1329483-24-mwen@igalia.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | drm/amd/display: add AMD driver-specific properties for color mgmt | expand |
On 2023-09-25 15:49, Melissa Wen wrote: > Map DC shaper LUT to DM plane color management. Shaper LUT can be used > to delinearize and/or normalize the color space for computational > efficiency and achiving specific visual styles. If a plane degamma is > apply to linearize the color space, a custom shaper 1D LUT can be used > just before applying 3D LUT. > > v2: > - use DPP color caps to verify plane 3D LUT support > - add debug message if shaper LUT programming fails > > Reviewed-by: Harry Wentland <harry.wentland@amd.com> > Signed-off-by: Melissa Wen <mwen@igalia.com> > --- > .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 1 + > .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 2 + > .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 108 +++++++++++++++++- > 3 files changed, 107 insertions(+), 4 deletions(-) > > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c > index 455b690d6185..af18c523c431 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c > @@ -8185,6 +8185,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, > bundle->surface_updates[planes_count].in_transfer_func = dc_plane->in_transfer_func; > bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix; > bundle->surface_updates[planes_count].hdr_mult = dc_plane->hdr_mult; > + bundle->surface_updates[planes_count].func_shaper = dc_plane->in_shaper_func; > } > > amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state, > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h > index b7b1d67f87a0..ad583cc97f15 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h > @@ -897,6 +897,8 @@ void amdgpu_dm_trigger_timing_sync(struct drm_device *dev); > /* 3D LUT max size is 17x17x17 */ > #define MAX_COLOR_3DLUT_ENTRIES 4913 > #define MAX_COLOR_3DLUT_BITDEPTH 12 > +int amdgpu_dm_verify_lut3d_size(struct amdgpu_device *adev, > + struct drm_plane_state *plane_state); > /* 1D LUT size */ > #define MAX_COLOR_LUT_ENTRIES 4096 > /* Legacy gamm LUT users such as X doesn't like large LUT sizes */ > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c > index 577a9dc669a5..8c991b5cf473 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c > @@ -594,6 +594,74 @@ amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf) > } > } > > +static int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *shaper_lut, > + uint32_t shaper_size, > + struct dc_transfer_func *func_shaper) > +{ > + int ret = 0; > + > + if (shaper_size) { > + /* If DRM shaper LUT is set, we assume a linear color space > + * (linearized by DRM degamma 1D LUT or not) > + */ > + func_shaper->type = TF_TYPE_DISTRIBUTED_POINTS; > + func_shaper->tf = TRANSFER_FUNCTION_LINEAR; > + > + ret = __set_output_tf(func_shaper, shaper_lut, shaper_size, false); > + } else { > + func_shaper->type = TF_TYPE_BYPASS; > + func_shaper->tf = TRANSFER_FUNCTION_LINEAR; > + } > + > + return ret; > +} > + > +/* amdgpu_dm_lut3d_size - get expected size according to hw color caps > + * @adev: amdgpu device > + * @lut_size: default size > + * > + * Return: > + * lut_size if DC 3D LUT is supported, zero otherwise. > + */ > +static uint32_t amdgpu_dm_get_lut3d_size(struct amdgpu_device *adev, The function name is a bit confusing since this just returns the lut_size if we have a hw_3d_lut caps field. Might be clearer to simply drop the function and do the below line of code in its place directly. No need to drop the RB, just a minor thing. Harry > + uint32_t lut_size) > +{ > + return adev->dm.dc->caps.color.dpp.hw_3d_lut ? lut_size : 0; > +} > + > +/** > + * amdgpu_dm_verify_lut3d_size - verifies if 3D LUT is supported and if DRM 3D > + * LUT matches the hw supported size > + * @adev: amdgpu device > + * @crtc_state: the DRM CRTC state > + * > + * Verifies if post-blending (MPC) 3D LUT is supported by the HW (DCN 3.0 or > + * newer) and if the DRM 3D LUT matches the supported size. > + * > + * Returns: > + * 0 on success. -EINVAL if lut size are invalid. > + */ > +int amdgpu_dm_verify_lut3d_size(struct amdgpu_device *adev, > + struct drm_plane_state *plane_state) > +{ > + struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); > + const struct drm_color_lut *shaper = NULL; > + uint32_t exp_size, size; > + > + /* shaper LUT is only available if 3D LUT color caps*/ > + exp_size = amdgpu_dm_get_lut3d_size(adev, MAX_COLOR_LUT_ENTRIES); > + shaper = __extract_blob_lut(dm_plane_state->shaper_lut, &size); > + > + if (shaper && size != exp_size) { > + drm_dbg(&adev->ddev, > + "Invalid Shaper LUT size. Should be %u but got %u.\n", > + exp_size, size); > + return -EINVAL; > + } > + > + return 0; > +} > + > /** > * amdgpu_dm_verify_lut_sizes - verifies if DRM luts match the hw supported sizes > * @crtc_state: the DRM CRTC state > @@ -881,6 +949,34 @@ __set_dm_plane_degamma(struct drm_plane_state *plane_state, > return 0; > } > > +static int > +amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state, > + struct dc_plane_state *dc_plane_state) > +{ > + struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); > + const struct drm_color_lut *shaper_lut; > + uint32_t shaper_size; > + int ret; > + > + /* We have nothing to do here, return */ > + if (!plane_state->color_mgmt_changed) > + return 0; > + > + dc_plane_state->hdr_mult = dc_fixpt_from_s3132(dm_plane_state->hdr_mult); > + > + shaper_lut = __extract_blob_lut(dm_plane_state->shaper_lut, &shaper_size); > + shaper_size = shaper_lut != NULL ? shaper_size : 0; > + > + ret = amdgpu_dm_atomic_shaper_lut(shaper_lut, shaper_size, > + dc_plane_state->in_shaper_func); > + if (ret) > + drm_dbg_kms(plane_state->plane->dev, > + "setting plane %d shaper LUT failed.\n", > + plane_state->plane->index); > + > + return ret; > +} > + > /** > * amdgpu_dm_update_plane_color_mgmt: Maps DRM color management to DC plane. > * @crtc: amdgpu_dm crtc state > @@ -898,10 +994,16 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, > struct drm_plane_state *plane_state, > struct dc_plane_state *dc_plane_state) > { > - struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); > + struct amdgpu_device *adev = drm_to_adev(crtc->base.state->dev); > bool has_crtc_cm_degamma; > int ret; > > + ret = amdgpu_dm_verify_lut3d_size(adev, plane_state); > + if (ret) { > + drm_dbg_driver(&adev->ddev, "amdgpu_dm_verify_lut3d_size() failed\n"); > + return ret; > + } > + > /* Initially, we can just bypass the DGM block. */ > dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS; > dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR; > @@ -909,8 +1011,6 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, > /* After, we start to update values according to color props */ > has_crtc_cm_degamma = (crtc->cm_has_degamma || crtc->cm_is_degamma_srgb); > > - dc_plane_state->hdr_mult = dc_fixpt_from_s3132(dm_plane_state->hdr_mult); > - > ret = __set_dm_plane_degamma(plane_state, dc_plane_state); > if (ret == -ENOMEM) > return ret; > @@ -942,5 +1042,5 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, > return ret; > } > > - return 0; > + return amdgpu_dm_plane_set_color_properties(plane_state, dc_plane_state); > }
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 455b690d6185..af18c523c431 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -8185,6 +8185,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, bundle->surface_updates[planes_count].in_transfer_func = dc_plane->in_transfer_func; bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix; bundle->surface_updates[planes_count].hdr_mult = dc_plane->hdr_mult; + bundle->surface_updates[planes_count].func_shaper = dc_plane->in_shaper_func; } amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index b7b1d67f87a0..ad583cc97f15 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -897,6 +897,8 @@ void amdgpu_dm_trigger_timing_sync(struct drm_device *dev); /* 3D LUT max size is 17x17x17 */ #define MAX_COLOR_3DLUT_ENTRIES 4913 #define MAX_COLOR_3DLUT_BITDEPTH 12 +int amdgpu_dm_verify_lut3d_size(struct amdgpu_device *adev, + struct drm_plane_state *plane_state); /* 1D LUT size */ #define MAX_COLOR_LUT_ENTRIES 4096 /* Legacy gamm LUT users such as X doesn't like large LUT sizes */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index 577a9dc669a5..8c991b5cf473 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -594,6 +594,74 @@ amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf) } } +static int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *shaper_lut, + uint32_t shaper_size, + struct dc_transfer_func *func_shaper) +{ + int ret = 0; + + if (shaper_size) { + /* If DRM shaper LUT is set, we assume a linear color space + * (linearized by DRM degamma 1D LUT or not) + */ + func_shaper->type = TF_TYPE_DISTRIBUTED_POINTS; + func_shaper->tf = TRANSFER_FUNCTION_LINEAR; + + ret = __set_output_tf(func_shaper, shaper_lut, shaper_size, false); + } else { + func_shaper->type = TF_TYPE_BYPASS; + func_shaper->tf = TRANSFER_FUNCTION_LINEAR; + } + + return ret; +} + +/* amdgpu_dm_lut3d_size - get expected size according to hw color caps + * @adev: amdgpu device + * @lut_size: default size + * + * Return: + * lut_size if DC 3D LUT is supported, zero otherwise. + */ +static uint32_t amdgpu_dm_get_lut3d_size(struct amdgpu_device *adev, + uint32_t lut_size) +{ + return adev->dm.dc->caps.color.dpp.hw_3d_lut ? lut_size : 0; +} + +/** + * amdgpu_dm_verify_lut3d_size - verifies if 3D LUT is supported and if DRM 3D + * LUT matches the hw supported size + * @adev: amdgpu device + * @crtc_state: the DRM CRTC state + * + * Verifies if post-blending (MPC) 3D LUT is supported by the HW (DCN 3.0 or + * newer) and if the DRM 3D LUT matches the supported size. + * + * Returns: + * 0 on success. -EINVAL if lut size are invalid. + */ +int amdgpu_dm_verify_lut3d_size(struct amdgpu_device *adev, + struct drm_plane_state *plane_state) +{ + struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); + const struct drm_color_lut *shaper = NULL; + uint32_t exp_size, size; + + /* shaper LUT is only available if 3D LUT color caps*/ + exp_size = amdgpu_dm_get_lut3d_size(adev, MAX_COLOR_LUT_ENTRIES); + shaper = __extract_blob_lut(dm_plane_state->shaper_lut, &size); + + if (shaper && size != exp_size) { + drm_dbg(&adev->ddev, + "Invalid Shaper LUT size. Should be %u but got %u.\n", + exp_size, size); + return -EINVAL; + } + + return 0; +} + /** * amdgpu_dm_verify_lut_sizes - verifies if DRM luts match the hw supported sizes * @crtc_state: the DRM CRTC state @@ -881,6 +949,34 @@ __set_dm_plane_degamma(struct drm_plane_state *plane_state, return 0; } +static int +amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state, + struct dc_plane_state *dc_plane_state) +{ + struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); + const struct drm_color_lut *shaper_lut; + uint32_t shaper_size; + int ret; + + /* We have nothing to do here, return */ + if (!plane_state->color_mgmt_changed) + return 0; + + dc_plane_state->hdr_mult = dc_fixpt_from_s3132(dm_plane_state->hdr_mult); + + shaper_lut = __extract_blob_lut(dm_plane_state->shaper_lut, &shaper_size); + shaper_size = shaper_lut != NULL ? shaper_size : 0; + + ret = amdgpu_dm_atomic_shaper_lut(shaper_lut, shaper_size, + dc_plane_state->in_shaper_func); + if (ret) + drm_dbg_kms(plane_state->plane->dev, + "setting plane %d shaper LUT failed.\n", + plane_state->plane->index); + + return ret; +} + /** * amdgpu_dm_update_plane_color_mgmt: Maps DRM color management to DC plane. * @crtc: amdgpu_dm crtc state @@ -898,10 +994,16 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state) { - struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state); + struct amdgpu_device *adev = drm_to_adev(crtc->base.state->dev); bool has_crtc_cm_degamma; int ret; + ret = amdgpu_dm_verify_lut3d_size(adev, plane_state); + if (ret) { + drm_dbg_driver(&adev->ddev, "amdgpu_dm_verify_lut3d_size() failed\n"); + return ret; + } + /* Initially, we can just bypass the DGM block. */ dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS; dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR; @@ -909,8 +1011,6 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, /* After, we start to update values according to color props */ has_crtc_cm_degamma = (crtc->cm_has_degamma || crtc->cm_is_degamma_srgb); - dc_plane_state->hdr_mult = dc_fixpt_from_s3132(dm_plane_state->hdr_mult); - ret = __set_dm_plane_degamma(plane_state, dc_plane_state); if (ret == -ENOMEM) return ret; @@ -942,5 +1042,5 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, return ret; } - return 0; + return amdgpu_dm_plane_set_color_properties(plane_state, dc_plane_state); }