This adds support for a 3D LUT. The color pipeline now consists of the following colorops: 1. 1D curve colorop 2. Multiplier 3. 3x4 CTM 4. 1D curve colorop 5. 1D LUT 6. 3D LUT 7. 1D curve colorop 8. 1D LUT
Signed-off-by: Alex Hung <alex.h...@amd.com> Reviewed-by: Daniel Stone <dani...@collabora.com> --- V10: - Support 32BIT RGB in 3D LUT with drm_color_lut_32 (Harry Wentland) - Remove redundant DRM_ERROR(...) V9: - Return a value in __set_dm_plane_colorop_3dlut v8: - Set initialized to 0 and return when drm_lut3d_size is 0 (Harry Wentland) - Rework tf->type = TF_TYPE_BYPASS for shaper (Harry Wentland & Leo Li) v7: - Simplify 3D LUT according to drm_colorop changes (Simon Ser) .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 147 ++++++++++++++++++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 19 +++ 2 files changed, 166 insertions(+) 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 47414f9c5757..a2e76df039d6 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 @@ -873,6 +873,59 @@ static void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut, __to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth); } +static void __to_dc_lut3d_32_color(struct dc_rgb *rgb, + const struct drm_color_lut_32 lut, + int bit_precision) +{ + rgb->red = drm_color_lut_extract(lut.red, bit_precision); + rgb->green = drm_color_lut_extract(lut.green, bit_precision); + rgb->blue = drm_color_lut_extract(lut.blue, bit_precision); +} + +static void __drm_3dlut_32_to_dc_3dlut(const struct drm_color_lut_32 *lut, + uint32_t lut3d_size, + struct tetrahedral_params *params, + bool use_tetrahedral_9, + int bit_depth) +{ + struct dc_rgb *lut0; + struct dc_rgb *lut1; + struct dc_rgb *lut2; + struct dc_rgb *lut3; + int lut_i, i; + + + if (use_tetrahedral_9) { + lut0 = params->tetrahedral_9.lut0; + lut1 = params->tetrahedral_9.lut1; + lut2 = params->tetrahedral_9.lut2; + lut3 = params->tetrahedral_9.lut3; + } else { + lut0 = params->tetrahedral_17.lut0; + lut1 = params->tetrahedral_17.lut1; + lut2 = params->tetrahedral_17.lut2; + lut3 = params->tetrahedral_17.lut3; + } + + for (lut_i = 0, i = 0; i < lut3d_size - 4; lut_i++, i += 4) { + /* + * We should consider the 3D LUT RGB values are distributed + * along four arrays lut0-3 where the first sizes 1229 and the + * other 1228. The bit depth supported for 3dlut channel is + * 12-bit, but DC also supports 10-bit. + * + * TODO: improve color pipeline API to enable the userspace set + * bit depth and 3D LUT size/stride, as specified by VA-API. + */ + __to_dc_lut3d_32_color(&lut0[lut_i], lut[i], bit_depth); + __to_dc_lut3d_32_color(&lut1[lut_i], lut[i + 1], bit_depth); + __to_dc_lut3d_32_color(&lut2[lut_i], lut[i + 2], bit_depth); + __to_dc_lut3d_32_color(&lut3[lut_i], lut[i + 3], bit_depth); + } + /* lut0 has 1229 points (lut_size/4 + 1) */ + __to_dc_lut3d_32_color(&lut0[lut_i], lut[i], bit_depth); +} + /* amdgpu_dm_atomic_lut3d - set DRM 3D LUT to DC stream * @drm_lut3d: user 3D LUT * @drm_lut3d_size: size of 3D LUT @@ -1426,6 +1479,7 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func; const struct drm_color_lut_32 *shaper_lut; struct drm_device *dev = colorop->dev; + bool enabled = false; uint32_t shaper_size; int i = 0, ret = 0; @@ -1447,6 +1501,7 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, ret = __set_output_tf(tf, 0, 0, false); if (ret) return ret; + enabled = true; } /* 1D LUT - SHAPER LUT */ @@ -1478,12 +1533,93 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, ret = __set_output_tf_32(tf, shaper_lut, shaper_size, false); if (ret) return ret; + enabled = true; } } + if (!enabled) + tf->type = TF_TYPE_BYPASS; + return 0; } +/* __set_colorop_3dlut - set DRM 3D LUT to DC stream + * @drm_lut3d: user 3D LUT + * @drm_lut3d_size: size of 3D LUT + * @lut3d: DC 3D LUT + * + * Map user 3D LUT data to DC 3D LUT and all necessary bits to program it + * on DCN accordingly. + */ +static void __set_colorop_3dlut(const struct drm_color_lut_32 *drm_lut3d, + uint32_t drm_lut3d_size, + struct dc_3dlut *lut) +{ + if (!drm_lut3d_size) { + lut->state.bits.initialized = 0; + return; + } + + /* Only supports 17x17x17 3D LUT (12-bit) now */ + lut->lut_3d.use_12bits = true; + lut->lut_3d.use_tetrahedral_9 = false; + + lut->state.bits.initialized = 1; + __drm_3dlut_32_to_dc_3dlut(drm_lut3d, drm_lut3d_size, &lut->lut_3d, + lut->lut_3d.use_tetrahedral_9, 12); + +} + +static int +__set_dm_plane_colorop_3dlut(struct drm_plane_state *plane_state, + struct dc_plane_state *dc_plane_state, + struct drm_colorop *colorop) +{ + struct drm_colorop *old_colorop; + struct drm_colorop_state *colorop_state = NULL, *new_colorop_state; + struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func; + struct drm_atomic_state *state = plane_state->state; + const struct amdgpu_device *adev = drm_to_adev(colorop->dev); + const struct drm_device *dev = colorop->dev; + const struct drm_color_lut_32 *lut3d; + uint32_t lut3d_size; + int i = 0, ret = 0; + + /* 3D LUT */ + old_colorop = colorop; + for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { + if (new_colorop_state->colorop == old_colorop && + new_colorop_state->colorop->type == DRM_COLOROP_3D_LUT) { + colorop_state = new_colorop_state; + break; + } + } + + if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_3D_LUT) { + if (!adev->dm.dc->caps.color.dpp.hw_3d_lut) { + drm_dbg(dev, "3D LUT is not supported by hardware\n"); + return -EINVAL; + } + + drm_dbg(dev, "3D LUT colorop with ID: %d\n", colorop->base.id); + lut3d = __extract_blob_lut_32(colorop_state->data, &lut3d_size); + lut3d_size = lut3d != NULL ? lut3d_size : 0; + __set_colorop_3dlut(lut3d, lut3d_size, &dc_plane_state->lut3d_func); + + /* 3D LUT requires shaper. If shaper colorop is bypassed, enable shaper curve + * with TRANSFER_FUNCTION_LINEAR + */ + if (tf->type == TF_TYPE_BYPASS) { + tf->type = TF_TYPE_DISTRIBUTED_POINTS; + tf->tf = TRANSFER_FUNCTION_LINEAR; + tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; + ret = __set_output_tf_32(tf, NULL, 0, false); + } + } + + return ret; +} + static int __set_dm_plane_colorop_blend(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, @@ -1655,6 +1791,17 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state, if (!colorop) return -EINVAL; + /* 3D LUT */ + colorop = colorop->next; + if (!colorop) { + drm_dbg(dev, "no 3D LUT colorop found\n"); + return -EINVAL; + } + + ret = __set_dm_plane_colorop_3dlut(plane_state, dc_plane_state, colorop); + if (ret) + return ret; + /* 1D Curve & LUT - BLND TF & LUT */ colorop = colorop->next; if (!colorop) { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c index 868d6bd0a5db..680b4e783959 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c @@ -49,6 +49,8 @@ const u64 amdgpu_dm_supported_blnd_tfs = #define MAX_COLOR_PIPELINE_OPS 10 +#define LUT3D_SIZE 17 + int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list) { struct drm_colorop *ops[MAX_COLOR_PIPELINE_OPS]; @@ -140,6 +142,23 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr i++; + /* 3D LUT */ + ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); + if (!ops[i]) { + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_plane_colorop_3dlut_init(dev, ops[i], plane, LUT3D_SIZE, + DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL, + DRM_COLOROP_FLAG_ALLOW_BYPASS); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i-1], ops[i]); + + i++; + /* 1D curve - BLND TF */ ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); if (!ops[i]) { -- 2.43.0