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

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
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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,
+2 −0
Original line number Diff line number Diff line
@@ -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 */
+89 −4
Original line number Diff line number Diff line
@@ -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);
}