Commit aba8b76b authored by Melissa Wen's avatar Melissa Wen Committed by Alex Deucher

drm/amd/display: add plane shaper LUT support

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

v4:
- remove helper to check 3D LUT color caps (Harry)
- update desc of lut3d-setup helper from MPC to DPP

v5:
- remove color_mgmt_changed check that prevents color updates (Joshua)
Reviewed-by: default avatarHarry Wentland <harry.wentland@amd.com>
Signed-off-by: default avatarMelissa Wen <mwen@igalia.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent 4bc59ddf
......@@ -8269,6 +8269,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,
......
......@@ -908,6 +908,8 @@ void amdgpu_dm_trigger_timing_sync(struct drm_device *dev);
/* 3D LUT max size is 17x17x17 (4913 entries) */
#define MAX_COLOR_3DLUT_SIZE 17
#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 */
......
......@@ -623,6 +623,63 @@ 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 user shaper LUT is set, we assume a linear color space
* (linearized by 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_verify_lut3d_size - verifies if 3D LUT is supported and if user
* shaper and 3D LUTs match the hw supported size
* @adev: amdgpu device
* @crtc_state: the DRM CRTC state
*
* Verifies if pre-blending (DPP) 3D LUT is supported by the HW (DCN 2.0 or
* newer) and if the user shaper and 3D LUTs match 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;
bool has_3dlut = adev->dm.dc->caps.color.dpp.hw_3d_lut;
/* shaper LUT is only available if 3D LUT color caps */
exp_size = has_3dlut ? MAX_COLOR_LUT_ENTRIES : 0;
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
......@@ -910,6 +967,30 @@ __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;
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
......@@ -927,10 +1008,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;
......@@ -938,8 +1025,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;
......@@ -972,5 +1057,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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment