Re: [PATCH v6 42/44] drm/colorop: Add 3D LUT supports to color pipeline
On 10/13/24 09:58, Simon Ser wrote: On Thursday, October 3rd, 2024 at 22:01, Harry Wentland wrote: From: Alex Hung It is to be used to enable HDR by allowing userpace to create and pass 3D LUTs to kernel and hardware. 1. new drm_colorop_type: DRM_COLOROP_3D_LUT. 2. 3D LUT modes define hardware capabilities to userspace applications. 3. mode index points to current 3D LUT mode in lut_3d_modes. Do we really need all of this complexity here? User-space needs to copy over its 3D LUT to the KMS blob. Kernel needs to copy from the KMS blob when programming hardware. Why do we need a list of different modes with negotiation? I've heard that some of this complexity has been introduced to add in the future BO-backed LUTs. That would be a nice addition, but it's not here right now, so how can we design for that case when we haven't actually tried implementing it and made sure it actually works in practice? It would be easy to introduce "modes" (or something different) when the BO-based 3D LUT uAPI is introduced. There are many ways to handle backwards compatibility: new properties can have their defaults set to the previously fixed format/swizzle/etc, a new colorop can be introduced if there are too many conflicts, and worst case new functionality can be gated behind a DRM cap (although I don't think we'd need to resort to this here). I'd recommend just having one fixed supported format, like we have for 1D LUTs. We can have a read-only props for the size and the color depth, as well as a read-write prop for the data blob. That's a good point. I will simplify DRM_COLOROP_3D_LUT implementation and remove 3D LUT mode. It can be used for future BO-based 3D LUT if necessary In summary: The attributes to be kept now: lut_size, interpolation and color_depth. The rests can be fixed and documented for userspace. diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 5ef87cb5b242..290c2e32f692 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -913,6 +913,90 @@ enum drm_colorop_type { * property. */ DRM_COLOROP_MULTIPLIER, + /** +* @DRM_COLOROP_3D_LUT: +* +* A 3D LUT of &drm_color_lut entries, +* packed into a blob via the DATA property. The driver's expected +* LUT size is advertised via the SIZE property. +*/ + DRM_COLOROP_3D_LUT, User-space docs are missing many details I believe. +}; + +/** + * enum drm_colorop_lut3d_interpolation_type - type of 3DLUT interpolation + * + */ +enum drm_colorop_lut3d_interpolation_type { + /** +* @DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL: +* +* Tetrahedral 3DLUT interpolation +*/ + DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL, +}; + +/** + * enum drm_colorop_lut3d_traversal_order - traversal order of the 3D LUT + * + * This enum describes the order of traversal of 3DLUT elements. + */ +enum drm_colorop_lut3d_traversal_order { + /** +* @DRM_COLOROP_LUT3D_TRAVERSAL_RGB: +* +* the LUT elements are traversed like so: +* for R in range 0..n +* for G in range 0..n +* for B in range 0..n +* color = lut3d[R][G][B] +*/ + DRM_COLOROP_LUT3D_TRAVERSAL_RGB, + /** +* @DRM_COLOROP_LUT3D_TRAVERSAL_BGR: +* +* the LUT elements are traversed like so: +* for R in range 0..n +* for G in range 0..n +* for B in range 0..n +* color = lut3d[B][G][R] +*/ + DRM_COLOROP_LUT3D_TRAVERSAL_BGR, +}; + +/** + * struct drm_mode_3dlut_mode - 3D LUT mode + * + * The mode describes the supported and selected format of a 3DLUT. + */ +struct drm_mode_3dlut_mode { + /** +* @lut_size: 3D LUT size - can be 9, 17 or 33 +*/ + __u16 lut_size; Are "9, 17 or 33" just example values? Or does the kernel actually guarantee that the advertised size can never be something else? It doesn't seem like there is a check, and enforcing it would hinder extensibility (adding new values would be a breaking uAPI change). + /** +* @lut_stride: dimensions of 3D LUT. Must be larger than lut_size +*/ + __u16 lut_stride[3]; It sounds a bit weird to have the driver dictate the stride which must be used. Usually user-space picks and sends the stride to the driver. + /** +* @interpolation: interpolation algorithm for 3D LUT. See drm_colorop_lut3d_interpolation_type +*/ + __u16 interpolation; + /** +* @color_depth: color depth - can be 8, 10 or 12 +*/ + __u16 color_depth; Ditto: reading these docs, user-space might not handle any other value. It would be nice to better explain what this means exactly. Are the output color values trunca
[V7 24/45] drm/amd/display: Add bypass COLOR PIPELINE
From: Harry Wentland Add the default Bypass pipeline and ensure it passes the kms_colorop test plane-XR30-XR30-bypass. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 19 +++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index 495e3cd70426..22ff9a31b592 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -1756,6 +1756,20 @@ dm_atomic_plane_get_property(struct drm_plane *plane, } #endif +#define MAX_COLOR_PIPELINES 5 + +static int +dm_plane_init_colorops(struct drm_plane *plane) +{ + struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES]; + int len = 0; + + /* Create COLOR_PIPELINE property and attach */ + drm_plane_create_color_pipeline_property(plane, pipelines, len); + + return 0; +} + static const struct drm_plane_funcs dm_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, @@ -1859,7 +1873,12 @@ int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm, #ifdef AMD_PRIVATE_COLOR dm_atomic_plane_attach_color_mgmt_properties(dm, plane); +#else + res = dm_plane_init_colorops(plane); + if (res) + return res; #endif + /* Create (reset) the plane state */ if (plane->funcs->reset) plane->funcs->reset(plane); -- 2.43.0
[V7 25/45] drm/amd/display: Skip color pipeline initialization for cursor plane
cursor plane does not need to have color pipeline. Signed-off-by: Alex Hung --- v7: - Add a commit messages drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index 22ff9a31b592..1bfb9f340c24 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -1764,6 +1764,9 @@ dm_plane_init_colorops(struct drm_plane *plane) struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES]; int len = 0; + if (plane->type == DRM_PLANE_TYPE_CURSOR) + return 0; + /* Create COLOR_PIPELINE property and attach */ drm_plane_create_color_pipeline_property(plane, pipelines, len); -- 2.43.0
[V7 21/45] drm/colorop: pass plane_color_pipeline client cap to atomic check
From: Harry Wentland Drivers will need to know whether an atomic check/commit originated from a client with DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE so they can ignore deprecated properties, like COLOR_ENCODING and COLOR_RANGE. Pass the plane_color_pipeline bit to drm_atomic_state. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v5: - Fix kernel docs drivers/gpu/drm/drm_atomic_uapi.c | 1 + include/drm/drm_atomic.h | 18 ++ 2 files changed, 19 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 7bc4978e5441..a3e1fcad47ad 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -1572,6 +1572,7 @@ int drm_mode_atomic_ioctl(struct drm_device *dev, drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); state->acquire_ctx = &ctx; state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET); + state->plane_color_pipeline = file_priv->plane_color_pipeline; retry: copied_objs = 0; diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index feef16fd89e2..f3afc1ef3f81 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -414,6 +414,24 @@ struct drm_atomic_state { */ bool duplicated : 1; + /** +* @plane_color_pipeline: +* +* Indicates whether this atomic state originated with a client that +* set the DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE. +* +* Drivers and helper functions should use this to ignore legacy +* properties that are incompatible with the drm_plane COLOR_PIPELINE +* behavior, such as: +* +* - COLOR_RANGE +* - COLOR_ENCODING +* +* or any other driver-specific properties that might affect pixel +* values. +*/ + bool plane_color_pipeline : 1; + /** * @colorops: * -- 2.43.0
[V7 22/45] drm/colorop: define a new macro for_each_new_colorop_in_state
Create a new macro for_each_new_colorop_in_state to access new drm_colorop_state updated from uapi. Signed-off-by: Alex Hung --- include/drm/drm_atomic.h | 20 1 file changed, 20 insertions(+) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index f3afc1ef3f81..c926f1924edc 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -1035,6 +1035,26 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (new_colorop_state) = (__state)->colorops[__i].new_state, 1)) +/** + * for_each_new_colorop_in_state - iterate over all colorops in an atomic update + * @__state: &struct drm_atomic_state pointer + * @colorop: &struct drm_colorop iteration cursor + * @new_colorop_state: &struct drm_colorop_state iteration cursor for the new state + * @__i: int iteration cursor, for macro-internal use + * + * This iterates over all colorops in an atomic update, tracking new state. This is + * useful is useful in places where the state delta needs to be considered, for + * example in atomic check functions. + */ +#define for_each_new_colorop_in_state(__state, colorop, new_colorop_state, __i) \ + for ((__i) = 0; \ +(__i) < (__state)->dev->mode_config.num_colorop; \ +(__i)++) \ + for_each_if ((__state)->colorops[__i].ptr &&\ +((colorop) = (__state)->colorops[__i].ptr, \ + (void)(colorop) /* Only to avoid unused-but-set-variable warning */, \ + (new_colorop_state) = (__state)->colorops[__i].new_state, 1)) + /** * for_each_oldnew_plane_in_state - iterate over all planes in an atomic update * @__state: &struct drm_atomic_state pointer -- 2.43.0
[V7 33/45] drm/colorop: Add 1D Curve Custom LUT type
We've previously introduced DRM_COLOROP_1D_CURVE for pre-defined 1D curves. But we also have HW that supports custom curves and userspace needs the ability to pass custom curves, aka LUTs. This patch introduces a new colorop type, called DRM_COLOROP_1D_LUT that provides a SIZE property which is used by a driver to advertise the supported SIZE of the LUT, as well as a DATA property which userspace uses to set the LUT. DATA and size function in the same way as current drm_crtc GAMMA and DEGAMMA LUTs. Signed-off-by: Alex Hung Co-developed-by: Harry Wentland Signed-off-by: Harry Wentland --- v7: - Change "size" to "lut_size" (this affects multiple following commits) - Move "lut_size" from drm_colorop_state to drm_colorop - Modify other files accordingly (i.e. from drm_colorop_state->size to drm_colorop->lut_size) v5: - Add kernel doc - Define SIZE in similar manner to GAMMA_SIZE on drm_crtc (Melissa) drivers/gpu/drm/drm_atomic.c | 4 +++ drivers/gpu/drm/drm_atomic_uapi.c | 5 drivers/gpu/drm/drm_colorop.c | 47 +-- include/drm/drm_colorop.h | 16 +++ include/uapi/drm/drm_mode.h | 9 ++ 5 files changed, 79 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index c58663327e6b..b7a05132ee01 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -793,6 +793,10 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, drm_printf(p, "\tcurve_1d_type=%s\n", drm_get_colorop_curve_1d_type_name(state->curve_1d_type)); break; + case DRM_COLOROP_1D_LUT: + drm_printf(p, "\tsize=%d\n", colorop->lut_size); + drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); + break; case DRM_COLOROP_CTM_3X4: drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); break; diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index a3e1fcad47ad..4744c12e429d 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -701,6 +701,9 @@ static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, bool replaced = false; switch (colorop->type) { + case DRM_COLOROP_1D_LUT: + size = colorop->lut_size * sizeof(struct drm_color_lut); + break; case DRM_COLOROP_CTM_3X4: size = sizeof(struct drm_color_ctm_3x4); break; @@ -750,6 +753,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->bypass; } else if (property == colorop->curve_1d_type_property) { *val = state->curve_1d_type; + } else if (property == colorop->lut_size_property) { + *val = colorop->lut_size; } else if (property == colorop->data_property) { *val = (state->data) ? state->data->base.id : 0; } else { diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 665b23900cc0..e6dea2713490 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -64,6 +64,7 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_CURVE, "1D Curve" }, + { DRM_COLOROP_1D_LUT, "1D Curve Custom LUT" }, { DRM_COLOROP_CTM_3X4, "3x4 Matrix"} }; @@ -219,11 +220,52 @@ static int drm_colorop_create_data_prop(struct drm_device *dev, struct drm_color colorop->data_property = prop; drm_object_attach_property(&colorop->base, - colorop->data_property, - 0); + colorop->data_property, + 0); + + return 0; +} + +/** + * drm_colorop_curve_1d_lut_init - Initialize a DRM_COLOROP_1D_LUT + * + * @dev: DRM device + * @colorop: The drm_colorop object to initialize + * @plane: The associated drm_plane + * @lut_size: LUT size supported by driver + * @return zero on success, -E value on failure + */ +int drm_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, uint32_t lut_size) +{ + struct drm_property *prop; + int ret; + + ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_1D_LUT); + if (ret) + return ret; + + /* initialize 1D LUT only attribute */ + /* LUT size */ + prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "SIZE", +0, U
[V7 34/45] drm/amd/display: add shaper and blend colorops for 1D Curve Custom LUT
This patch adds colorops for custom 1D LUTs in the SHAPER and BLND HW blocks. With this change the following IGT tests pass: kms_colorop --run plane-XR30-XR30-srgb_inv_eotf_lut kms_colorop --run plane-XR30-XR30-srgb_inv_eotf_lut-srgb_eotf_lut The color pipeline now consists of the following colorops: 1. 1D curve colorop 2. 1D curve colorop 3. 1D LUT 4. 1D curve colorop 5. 1D LUT The 1D curve colorops support sRGB, BT2020, and PQ scaled to 125.0. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Initialize uint32_t blend_size = 0 by default (kernel test robot) - Modify state->size to colorop->lut_size .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 166 ++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 32 2 files changed, 120 insertions(+), 78 deletions(-) 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 1765402bc122..0bea52eede39 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 @@ -1211,38 +1211,6 @@ __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state, return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state); } -static int -__set_colorop_in_shaper_1d_curve(struct dc_plane_state *dc_plane_state, - struct drm_colorop_state *colorop_state) -{ - struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func; - struct drm_colorop *colorop = colorop_state->colorop; - struct drm_device *drm = colorop->dev; - - if (colorop->type != DRM_COLOROP_1D_CURVE) - return -EINVAL; - - if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) - return -EINVAL; - - if (colorop_state->bypass) { - tf->type = TF_TYPE_BYPASS; - tf->tf = TRANSFER_FUNCTION_LINEAR; - return 0; - } - - drm_dbg(drm, "Shaper colorop with ID: %d\n", colorop->base.id); - - if (colorop->type == DRM_COLOROP_1D_CURVE) { - tf->type = TF_TYPE_DISTRIBUTED_POINTS; - tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); - tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; - return __set_output_tf(tf, 0, 0, false); - } - - return -EINVAL; -} - static int __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, @@ -1251,64 +1219,61 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, struct drm_colorop *old_colorop; struct drm_colorop_state *colorop_state = NULL, *new_colorop_state; struct drm_atomic_state *state = plane_state->state; + enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR; + struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func; + const struct drm_color_lut *shaper_lut; + struct drm_device *dev = colorop->dev; + uint32_t shaper_size; int i = 0; + /* 1D Curve - SHAPER TF */ old_colorop = colorop; - - /* 2nd op: 1d curve - shaper */ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == old_colorop && (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) { colorop_state = new_colorop_state; break; } - - if (new_colorop_state->colorop == old_colorop) { - colorop_state = new_colorop_state; - break; - } } - if (!colorop_state) - return -EINVAL; - - return __set_colorop_in_shaper_1d_curve(dc_plane_state, colorop_state); -} - - -static int -__set_colorop_1d_curve_blend_tf_lut(struct dc_plane_state *dc_plane_state, - struct drm_colorop_state *colorop_state) -{ - - struct dc_transfer_func *tf = &dc_plane_state->blend_tf; - struct drm_colorop *colorop = colorop_state->colorop; - struct drm_device *drm = colorop->dev; - const struct drm_color_lut *blend_lut; - uint32_t blend_size = 0; - - if (colorop->type != DRM_COLOROP_1D_CURVE) - return -EINVAL; + if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_CURVE) { + drm_dbg(dev, "Shaper TF colorop with ID: %d\n", colorop->base.id); + tf->type = TF_TYPE_DISTRIBUTED_POINTS; + tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); + tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_
[V7 32/45] drm/amd/display: Add support for BT.709 and BT.2020 TFs
From: Harry Wentland This adds support for the BT.709/BT.2020 transfer functions on all current 1D curve plane colorops, i.e., on DEGAM, SHAPER, and BLND blocks. With this change the following IGT subtests pass: kms_colorop --run plane-XR30-XR30-bt2020_inv_oetf kms_colorop --run plane-XR30-XR30-bt2020_oetf Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c | 11 --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 10 +++--- 2 files changed, 15 insertions(+), 6 deletions(-) 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 63044e0296cb..1765402bc122 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 @@ -676,6 +676,9 @@ amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf) case DRM_COLOROP_1D_CURVE_SRGB_EOTF: case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF: return TRANSFER_FUNCTION_SRGB; + case DRM_COLOROP_1D_CURVE_BT2020_INV_OETF: + case DRM_COLOROP_1D_CURVE_BT2020_OETF: + return TRANSFER_FUNCTION_BT709; case DRM_COLOROP_1D_CURVE_PQ_125_EOTF: case DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF: return TRANSFER_FUNCTION_PQ; @@ -1284,8 +1287,10 @@ __set_colorop_1d_curve_blend_tf_lut(struct dc_plane_state *dc_plane_state, const struct drm_color_lut *blend_lut; uint32_t blend_size = 0; - if (colorop->type != DRM_COLOROP_1D_CURVE && - colorop_state->curve_1d_type != DRM_COLOROP_1D_CURVE_SRGB_EOTF) + if (colorop->type != DRM_COLOROP_1D_CURVE) + return -EINVAL; + + if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) return -EINVAL; if (colorop_state->bypass) { @@ -1321,7 +1326,7 @@ __set_dm_plane_colorop_blend(struct drm_plane_state *plane_state, /* 3nd op: 1d curve - blend */ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == old_colorop && - new_colorop_state->curve_1d_type == DRM_COLOROP_1D_CURVE_SRGB_EOTF) { + (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) { colorop_state = new_colorop_state; break; } 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 a9d94018a207..ff5828a1e8cd 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 @@ -33,14 +33,18 @@ const u64 amdgpu_dm_supported_degam_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | - BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF); + BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF); const u64 amdgpu_dm_supported_shaper_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) | - BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF); + BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF); const u64 amdgpu_dm_supported_blnd_tfs = - BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF); + BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF); #define MAX_COLOR_PIPELINE_OPS 10 -- 2.43.0
[V7 40/45] drm/colorop: allow non-bypass colorops
From: Harry Wentland Not all HW will be able to do bypass on all color operations. Introduce an 'allow_bypass' boolean for all colorop init functions and only create the BYPASS property when it's true. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 22 +--- drivers/gpu/drm/drm_atomic.c | 3 +- drivers/gpu/drm/drm_colorop.c | 53 --- drivers/gpu/drm/vkms/vkms_colorop.c | 8 +-- include/drm/drm_colorop.h | 10 ++-- 5 files changed, 61 insertions(+), 35 deletions(-) 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 92c9f3fb0254..ec94ff887886 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 @@ -66,7 +66,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_init(dev, ops[i], plane, amdgpu_dm_supported_degam_tfs); + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, + amdgpu_dm_supported_degam_tfs, + true); if (ret) goto cleanup; @@ -83,7 +85,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_mult_init(dev, ops[i], plane); + ret = drm_colorop_mult_init(dev, ops[i], plane, true); if (ret) goto cleanup; @@ -99,7 +101,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane, true); if (ret) goto cleanup; @@ -115,7 +117,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_init(dev, ops[i], plane, amdgpu_dm_supported_shaper_tfs); + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, + amdgpu_dm_supported_shaper_tfs, + true); if (ret) goto cleanup; @@ -132,7 +136,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr } ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES, - DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR); + DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, + true); if (ret) goto cleanup; @@ -148,7 +153,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_init(dev, ops[i], plane, amdgpu_dm_supported_blnd_tfs); + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, + amdgpu_dm_supported_blnd_tfs, + true); if (ret) goto cleanup; @@ -165,7 +172,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr } ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES, - DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR); + DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, + true); if (ret) goto cleanup; diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 9a17fe11bcb3..88219b095d99 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -786,7 +786,8 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, drm_printf(p, "colorop[%u]:\n", colorop->base.id); drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type)); - drm_printf(p, "\tbypass=%u\n", state->bypass); + if (colorop->bypass_property) + drm_printf(p, "\tbypass=%u\n", state->bypass); switch (colorop->type) { case DRM_COLOROP_1D_CURVE: diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index e8d8806e94cf..dfff8e37ff05 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -85,7 +85,8 @@ static const struct drm_prop_enum_list drm_colorop_lut1d_interpolation_list[] = /* Init Helpers */ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, -
[V7 27/45] drm/amd/display: Add support for sRGB Inverse EOTF in SHAPER block
Expose a 2nd curve colorop with support for DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF and program HW to perform the sRGB Inverse EOTF on the shaper block when the colorop is not in bypass. With this change the follow IGT tests pass: kms_colorop --run plane-XR30-XR30-srgb_inv_eotf kms_colorop --run plane-XR30-XR30-srgb_eotf-srgb_inv_eotf The color pipeline now consists of the following colorops: 1. 1D curve colorop w/ sRGB EOTF support 2. 1D curve colorop w/ sRGB Inverse EOTF support Signed-off-by: Alex Hung Co-developed-by: Harry Wentland Signed-off-by: Harry Wentland --- v6: - don't pass uninitialized variable into __set_output_tf .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 74 +++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 19 + .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h | 1 + 3 files changed, 94 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 566035af00cd..65f681b0c31c 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 @@ -1202,6 +1202,68 @@ __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state, return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state); } +static int +__set_colorop_in_shaper_1d_curve(struct dc_plane_state *dc_plane_state, + struct drm_colorop_state *colorop_state) +{ + struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func; + struct drm_colorop *colorop = colorop_state->colorop; + struct drm_device *drm = colorop->dev; + + if (colorop->type != DRM_COLOROP_1D_CURVE && + colorop_state->curve_1d_type != DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) + return -EINVAL; + + if (colorop_state->bypass) { + tf->type = TF_TYPE_BYPASS; + tf->tf = TRANSFER_FUNCTION_LINEAR; + return 0; + } + + drm_dbg(drm, "Shaper colorop with ID: %d\n", colorop->base.id); + + if (colorop->type == DRM_COLOROP_1D_CURVE) { + tf->type = TF_TYPE_DISTRIBUTED_POINTS; + tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); + tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; + return __set_output_tf(tf, 0, 0, false); + } + + return -EINVAL; +} + +static int +__set_dm_plane_colorop_shaper(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 drm_atomic_state *state = plane_state->state; + int i = 0; + + old_colorop = colorop; + + /* 2nd op: 1d curve - shaper */ + for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { + if (new_colorop_state->colorop == old_colorop && + new_colorop_state->curve_1d_type == DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) { + colorop_state = new_colorop_state; + break; + } + + if (new_colorop_state->colorop == old_colorop) { + colorop_state = new_colorop_state; + break; + } + } + + if (!colorop_state) + return -EINVAL; + + return __set_colorop_in_shaper_1d_curve(dc_plane_state, colorop_state); +} + static int amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state) @@ -1257,6 +1319,7 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state) { struct drm_colorop *colorop = plane_state->color_pipeline; + struct drm_device *dev = plane_state->plane->dev; int ret; /* 1D Curve - DEGAM TF */ @@ -1267,6 +1330,17 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state, if (ret) return ret; + /* 1D Curve - SHAPER TF */ + colorop = colorop->next; + if (!colorop) { + drm_dbg(dev, "no Shaper TF colorop found\n"); + return -EINVAL; + } + + ret = __set_dm_plane_colorop_shaper(plane_state, dc_plane_state, colorop); + if (ret) + return ret; + return 0; } 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 3be0cb19ebc7..718544b11863 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c +++ b/drivers/gpu/drm/amd
[V7 28/45] drm/amd/display: Add support for sRGB EOTF in BLND block
Expose a 3rd 1D curve colorop, with support for DRM_COLOROP_1D_CURVE_SRGB_EOTF and program the BLND block to perform the sRGB transform when the colorop is not in bypass With this change the following IGT test passes: kms_colorop --run plane-XR30-XR30-srgb_eotf-srgb_inv_eotf-srgb_eotf The color pipeline now consists of the following colorops: 1. 1D curve colorop w/ sRGB EOTF support 2. 1D curve colorop w/ sRGB Inverse EOTF support 3. 1D curve colorop w/ sRGB EOTF support Signed-off-by: Alex Hung Co-developed-by: Harry Wentland Signed-off-by: Harry Wentland --- v7: - Initialized uint32_t blend_size = 0 by default (kernel test robot) .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 77 +++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 19 + .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h | 1 + 3 files changed, 97 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 65f681b0c31c..ddc8318f6201 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 @@ -1264,6 +1264,72 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, return __set_colorop_in_shaper_1d_curve(dc_plane_state, colorop_state); } + +static int +__set_colorop_1d_curve_blend_tf_lut(struct dc_plane_state *dc_plane_state, + struct drm_colorop_state *colorop_state) +{ + + struct dc_transfer_func *tf = &dc_plane_state->blend_tf; + struct drm_colorop *colorop = colorop_state->colorop; + struct drm_device *drm = colorop->dev; + const struct drm_color_lut *blend_lut; + uint32_t blend_size = 0; + + if (colorop->type != DRM_COLOROP_1D_CURVE && + colorop_state->curve_1d_type != DRM_COLOROP_1D_CURVE_SRGB_EOTF) + return -EINVAL; + + if (colorop_state->bypass) { + tf->type = TF_TYPE_BYPASS; + tf->tf = TRANSFER_FUNCTION_LINEAR; + return 0; + } + + drm_dbg(drm, "Blend colorop with ID: %d\n", colorop->base.id); + + if (colorop->type == DRM_COLOROP_1D_CURVE) { + tf->type = TF_TYPE_DISTRIBUTED_POINTS; + tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); + tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; + return __set_input_tf(NULL, tf, blend_lut, blend_size); + } + + return -EINVAL; +} + +static int +__set_dm_plane_colorop_blend(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 drm_atomic_state *state = plane_state->state; + int i = 0; + + old_colorop = colorop; + + /* 3nd op: 1d curve - blend */ + for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { + if (new_colorop_state->colorop == old_colorop && + new_colorop_state->curve_1d_type == DRM_COLOROP_1D_CURVE_SRGB_EOTF) { + colorop_state = new_colorop_state; + break; + } + + if (new_colorop_state->colorop == old_colorop) { + colorop_state = new_colorop_state; + break; + } + } + + if (!colorop_state) + return -EINVAL; + + return __set_colorop_1d_curve_blend_tf_lut(dc_plane_state, colorop_state); +} + static int amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state) @@ -1341,6 +1407,17 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state, if (ret) return ret; + /* 1D Curve - BLND TF */ + colorop = colorop->next; + if (!colorop) { + drm_dbg(dev, "no Blend TF colorop found\n"); + return -EINVAL; + } + + ret = __set_dm_plane_colorop_blend(plane_state, dc_plane_state, colorop); + if (ret) + return ret; + return 0; } 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 718544b11863..c5154e1430f5 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 @@ -37,6 +37,9 @@ const u64 amdgpu_dm_supported_degam_tfs = const u64 amdgpu_dm_supported_shaper_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF); +const u64 amdgpu_dm_supported_blnd_tfs = + BIT(DRM_COLOROP_1D_CURVE_SRG
[V7 03/45] drm/vkms: Add kunit tests for VKMS LUT handling
From: Harry Wentland Debugging LUT math is much easier when we can unit test it. Add kunit functionality to VKMS and add tests for - get_lut_index - lerp_u16 Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland Cc: Arthur Grillo --- v7: - Fix checkpatch warnings and errors (Louis Chauvet) - Change SPDX-License-Identifier: GPL-2.0+ from /* */ to // - Fix checkpatch errors and warnings (new line at EOF, redundant spaces, and long lines) - Add static to const struct vkms_color_lut test_linear_lut - Add "MODULE_DESCRIPTION" (Jeff Johnson) v6: - Eliminate need to include test as .c file (Louis Chauvet) v5: - Bring back static for lerp_u16 and get_lut_index (Arthur) v4: - Test the critical points of the lerp function (Pekka) v3: - Use include way of testing static functions (Arthur) drivers/gpu/drm/vkms/Kconfig | 15 ++ drivers/gpu/drm/vkms/Makefile| 1 + drivers/gpu/drm/vkms/tests/.kunitconfig | 4 + drivers/gpu/drm/vkms/tests/Makefile | 3 + drivers/gpu/drm/vkms/tests/vkms_color_test.c | 172 +++ drivers/gpu/drm/vkms/vkms_composer.c | 8 +- drivers/gpu/drm/vkms/vkms_composer.h | 13 ++ 7 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/vkms/tests/.kunitconfig create mode 100644 drivers/gpu/drm/vkms/tests/Makefile create mode 100644 drivers/gpu/drm/vkms/tests/vkms_color_test.c create mode 100644 drivers/gpu/drm/vkms/vkms_composer.h diff --git a/drivers/gpu/drm/vkms/Kconfig b/drivers/gpu/drm/vkms/Kconfig index b9ecdebecb0b..59c4a32adb9d 100644 --- a/drivers/gpu/drm/vkms/Kconfig +++ b/drivers/gpu/drm/vkms/Kconfig @@ -13,3 +13,18 @@ config DRM_VKMS a VKMS. If M is selected the module will be called vkms. + +config DRM_VKMS_KUNIT_TESTS + tristate "KUnit tests for VKMS." if !KUNIT_ALL_TESTS + depends on DRM_VKMS=y && KUNIT + default KUNIT_ALL_TESTS + help + This builds unit tests for VKMS. This option is not useful for + distributions or general kernels, but only for kernel + developers working on VKMS. + + For more information on KUnit and unit tests in general, + please refer to the KUnit documentation in + Documentation/dev-tools/kunit/. + + If in doubt, say "N". diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile index 1b28a6a32948..8d3e46dde635 100644 --- a/drivers/gpu/drm/vkms/Makefile +++ b/drivers/gpu/drm/vkms/Makefile @@ -9,3 +9,4 @@ vkms-y := \ vkms_writeback.o obj-$(CONFIG_DRM_VKMS) += vkms.o +obj-$(CONFIG_DRM_VKMS_KUNIT_TESTS) += tests/ diff --git a/drivers/gpu/drm/vkms/tests/.kunitconfig b/drivers/gpu/drm/vkms/tests/.kunitconfig new file mode 100644 index ..70e378228cbd --- /dev/null +++ b/drivers/gpu/drm/vkms/tests/.kunitconfig @@ -0,0 +1,4 @@ +CONFIG_KUNIT=y +CONFIG_DRM=y +CONFIG_DRM_VKMS=y +CONFIG_DRM_VKMS_KUNIT_TESTS=y diff --git a/drivers/gpu/drm/vkms/tests/Makefile b/drivers/gpu/drm/vkms/tests/Makefile new file mode 100644 index ..7876ca7a3c42 --- /dev/null +++ b/drivers/gpu/drm/vkms/tests/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_DRM_VKMS_KUNIT_TESTS) += vkms_color_test.o \ No newline at end of file diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c new file mode 100644 index ..b53beaac2703 --- /dev/null +++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include + +#include +#include +#include "../vkms_drv.h" +#include "../vkms_composer.h" + +#define TEST_LUT_SIZE 16 + +MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); + +static struct drm_color_lut test_linear_array[TEST_LUT_SIZE] = { + { 0x0, 0x0, 0x0, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, +}; + +static const struct vkms_color_lut test_linear_lut = { + .base = test_linear_array, + .lut_length = TEST_LUT_SIZE, + .channel_value2index_ratio = 0xf000fll +}; + + +static void vkms_color_test_get_lut_index(struct kunit *test) +{ + s64 lut_index; + int i; + + lut_index = get_lut_index(&test_linear_lut, test_linear_array[0].red); + KUNIT_EXPECT_EQ(test, drm_fixp2int(lut_index), 0); + + f
[V7 30/45] drm/amd/display: Enable support for PQ 125 EOTF and Inverse
From: Harry Wentland This patchset enables support for the PQ_125 EOTF and its inverse on all existing plane 1D curve colorops, i.e., on DEGAM, SHAPER, and BLND blocks. With this patchset the following IGT subtests are passing: kms_colorop --run plane-XR30-XR30-pq_125_eotf kms_colorop --run plane-XR30-XR30-pq_125_inv_eotf kms_colorop --run plane-XR30-XR30-pq_125_eotf-pq_125_inv_eotf kms_colorop --run plane-XR30-XR30-pq_125_eotf-pq_125_inv_eotf-pq_125_eotf Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 20 +-- .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 6 -- 2 files changed, 18 insertions(+), 8 deletions(-) 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 ddc8318f6201..63044e0296cb 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 @@ -25,6 +25,7 @@ #include "amdgpu.h" #include "amdgpu_mode.h" #include "amdgpu_dm.h" +#include "amdgpu_dm_colorop.h" #include "dc.h" #include "modules/color/color_gamma.h" #include "basics/conversion.h" @@ -675,6 +676,9 @@ amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf) case DRM_COLOROP_1D_CURVE_SRGB_EOTF: case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF: return TRANSFER_FUNCTION_SRGB; + case DRM_COLOROP_1D_CURVE_PQ_125_EOTF: + case DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF: + return TRANSFER_FUNCTION_PQ; default: return TRANSFER_FUNCTION_LINEAR; } @@ -1157,8 +1161,10 @@ __set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state, struct drm_colorop *colorop = colorop_state->colorop; struct drm_device *drm = colorop->dev; - if (colorop->type != DRM_COLOROP_1D_CURVE && - colorop_state->curve_1d_type != DRM_COLOROP_1D_CURVE_SRGB_EOTF) + if (colorop->type != DRM_COLOROP_1D_CURVE) + return -EINVAL; + + if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_degam_tfs)) return -EINVAL; if (colorop_state->bypass) { @@ -1190,7 +1196,7 @@ __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state, /* 1st op: 1d curve - degamma */ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == old_colorop && - new_colorop_state->curve_1d_type == DRM_COLOROP_1D_CURVE_SRGB_EOTF) { + (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_degam_tfs)) { colorop_state = new_colorop_state; break; } @@ -1210,8 +1216,10 @@ __set_colorop_in_shaper_1d_curve(struct dc_plane_state *dc_plane_state, struct drm_colorop *colorop = colorop_state->colorop; struct drm_device *drm = colorop->dev; - if (colorop->type != DRM_COLOROP_1D_CURVE && - colorop_state->curve_1d_type != DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) + if (colorop->type != DRM_COLOROP_1D_CURVE) + return -EINVAL; + + if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) return -EINVAL; if (colorop_state->bypass) { @@ -1247,7 +1255,7 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, /* 2nd op: 1d curve - shaper */ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == old_colorop && - new_colorop_state->curve_1d_type == DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) { + (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) { colorop_state = new_colorop_state; break; } 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 c5154e1430f5..a9d94018a207 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 @@ -32,10 +32,12 @@ #include "amdgpu_dm_colorop.h" const u64 amdgpu_dm_supported_degam_tfs = - BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF); + BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF); const u64 amdgpu_dm_supported_shaper_tfs = - BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF); + BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF); const u64 amdgpu_dm_supported_blnd_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF); -- 2.43.0
[V7 31/45] drm/colorop: add BT2020/BT709 OETF and Inverse OETF
From: Harry Wentland The BT.709 and BT.2020 OETFs are the same, the only difference being that the BT.2020 variant is defined with more precision for 10 and 12-bit per color encodings. Both are used as encoding functions for video content, and are therefore defined as OETF (opto-electronic transfer function) instead of as EOTF (electro-optical transfer function). Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- drivers/gpu/drm/drm_colorop.c | 2 ++ include/drm/drm_colorop.h | 19 +++ 2 files changed, 21 insertions(+) diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index f3391602a577..665b23900cc0 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -70,6 +70,8 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { static const char * const colorop_curve_1d_type_names[] = { [DRM_COLOROP_1D_CURVE_SRGB_EOTF] = "sRGB EOTF", [DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF] = "sRGB Inverse EOTF", + [DRM_COLOROP_1D_CURVE_BT2020_INV_OETF] = "BT.2020 Inverse OETF", + [DRM_COLOROP_1D_CURVE_BT2020_OETF] = "BT.2020 OETF", [DRM_COLOROP_1D_CURVE_PQ_125_EOTF] = "PQ 125 EOTF", [DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF] = "PQ 125 Inverse EOTF", }; diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index 4084a7438032..83a6bd64d48a 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -55,6 +55,25 @@ enum drm_colorop_curve_1d_type { */ DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + /** +* @DRM_COLOROP_1D_CURVE_BT2020_INV_OETF: +* +* The inverse of &DRM_COLOROP_1D_CURVE_BT2020_OETF +*/ + DRM_COLOROP_1D_CURVE_BT2020_INV_OETF, + + /** +* @DRM_COLOROP_1D_CURVE_BT2020_OETF: +* +* The BT.2020/BT.709 transfer function. The BT.709 and BT.2020 +* transfer functions are the same, the only difference is that +* BT.2020 is defined with more precision for 10 and 12-bit +* encodings. +* +* +*/ + DRM_COLOROP_1D_CURVE_BT2020_OETF, + /** * @DRM_COLOROP_1D_CURVE_PQ_125_EOTF: * -- 2.43.0
[V7 29/45] drm/colorop: Add PQ 125 EOTF and its inverse
From: Harry Wentland The PQ function defines a mapping of code values to nits (cd/m^2). The max code value maps to 10,000 nits. Windows DWM's canonical composition color space (CCCS) defaults to composing SDR contents to 80 nits and uses a float value of 1.0 to represent this. For this reason AMD HW hard-codes a PQ function that is scaled by 125, yielding 80 nit PQ values for 1.0 and 10,000 nits at 125.0. This patch introduces this scaled PQ EOTF and its inverse as 1D curve types. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- drivers/gpu/drm/drm_colorop.c | 2 ++ include/drm/drm_colorop.h | 21 - 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 7d4b29c0a0cc..f3391602a577 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -70,6 +70,8 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { static const char * const colorop_curve_1d_type_names[] = { [DRM_COLOROP_1D_CURVE_SRGB_EOTF] = "sRGB EOTF", [DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF] = "sRGB Inverse EOTF", + [DRM_COLOROP_1D_CURVE_PQ_125_EOTF] = "PQ 125 EOTF", + [DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF] = "PQ 125 Inverse EOTF", }; diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index e9f5c1adc2fe..4084a7438032 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -36,7 +36,6 @@ * enum drm_colorop_curve_1d_type - type of 1D curve * * Describes a 1D curve to be applied by the DRM_COLOROP_1D_CURVE colorop. - * */ enum drm_colorop_curve_1d_type { /** @@ -56,6 +55,26 @@ enum drm_colorop_curve_1d_type { */ DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + /** +* @DRM_COLOROP_1D_CURVE_PQ_125_EOTF: +* +* The PQ transfer function, scaled by 125.0f, so that 10,000 +* nits correspond to 125.0f. +* +* Transfer characteristics of the PQ function as defined by +* SMPTE ST 2084 (2014) for 10-, 12-, 14-, and 16-bit systems +* and Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system. +* Equivalent to H.273 TransferCharacteristics code point 16. +*/ + DRM_COLOROP_1D_CURVE_PQ_125_EOTF, + + /** +* @DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF: +* +* The inverse of DRM_COLOROP_1D_CURVE_PQ_125_EOTF. +*/ + DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF, + /** * @DRM_COLOROP_1D_CURVE_COUNT: * -- 2.43.0
[V7 38/45] drm/amd/display: Swap matrix and multiplier
Swap the order of matrix and multiplier as designed in hardware. Signed-off-by: Alex Hung --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c | 12 ++-- .../drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c| 8 2 files changed, 10 insertions(+), 10 deletions(-) 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 edf05290d441..54ec12c1352f 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 @@ -1477,25 +1477,25 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state, if (ret) return ret; - /* 3x4 matrix */ + /* Multiplier */ colorop = colorop->next; if (!colorop) { - drm_dbg(dev, "no 3x4 matrix colorop found\n"); + drm_dbg(dev, "no multiplier colorop found\n"); return -EINVAL; } - ret = __set_dm_plane_colorop_3x4_matrix(plane_state, dc_plane_state, colorop); + ret = __set_dm_plane_colorop_multiplier(plane_state, dc_plane_state, colorop); if (ret) return ret; - /* Multiplier */ + /* 3x4 matrix */ colorop = colorop->next; if (!colorop) { - drm_dbg(dev, "no multiplier colorop found\n"); + drm_dbg(dev, "no 3x4 matrix colorop found\n"); return -EINVAL; } - ret = __set_dm_plane_colorop_multiplier(plane_state, dc_plane_state, colorop); + ret = __set_dm_plane_colorop_3x4_matrix(plane_state, dc_plane_state, colorop); if (ret) return ret; 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 33156531d751..d0777691862b 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 @@ -75,7 +75,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr i++; - /* 3x4 matrix */ + /* Multiplier */ ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); if (!ops[i]) { DRM_ERROR("KMS: Failed to allocate colorop\n"); @@ -83,7 +83,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + ret = drm_colorop_mult_init(dev, ops[i], plane); if (ret) goto cleanup; @@ -91,7 +91,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr i++; - /* Multiplier */ + /* 3x4 matrix */ ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); if (!ops[i]) { DRM_ERROR("KMS: Failed to allocate colorop\n"); @@ -99,7 +99,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_mult_init(dev, ops[i], plane); + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); if (ret) goto cleanup; -- 2.43.0
[V7 39/45] drm/colorop: Define LUT_1D interpolation
From: Harry Wentland We want to make sure userspace is aware of the 1D LUT interpolation. While linear interpolation is common it might not be supported on all HW. Give driver implementers a way to specify their interpolation. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix a checkpatch long-line warning - Modify state->size to colorop->lut_size .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 6 ++-- drivers/gpu/drm/drm_atomic.c | 2 ++ drivers/gpu/drm/drm_colorop.c | 36 ++- include/drm/drm_colorop.h | 19 +- include/uapi/drm/drm_mode.h | 13 +++ 5 files changed, 72 insertions(+), 4 deletions(-) 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 d0777691862b..92c9f3fb0254 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 @@ -131,7 +131,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES); + ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES, + DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR); if (ret) goto cleanup; @@ -163,7 +164,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES); + ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES, + DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR); if (ret) goto cleanup; diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 1fa6818794d6..9a17fe11bcb3 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -795,6 +795,8 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, break; case DRM_COLOROP_1D_LUT: drm_printf(p, "\tsize=%d\n", colorop->lut_size); + drm_printf(p, "\tinterpolation=%s\n", + drm_get_colorop_lut1d_interpolation_name(colorop->lut1d_interpolation)); drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); break; case DRM_COLOROP_CTM_3X4: diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 9fbfb2b89b1b..e8d8806e94cf 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -78,6 +78,9 @@ static const char * const colorop_curve_1d_type_names[] = { [DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF] = "PQ 125 Inverse EOTF", }; +static const struct drm_prop_enum_list drm_colorop_lut1d_interpolation_list[] = { + { DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, "Linear" }, +}; /* Init Helpers */ @@ -234,10 +237,12 @@ static int drm_colorop_create_data_prop(struct drm_device *dev, struct drm_color * @colorop: The drm_colorop object to initialize * @plane: The associated drm_plane * @lut_size: LUT size supported by driver + * @lut1d_interpolation: 1D LUT interpolation type * @return zero on success, -E value on failure */ int drm_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_colorop *colorop, - struct drm_plane *plane, uint32_t lut_size) + struct drm_plane *plane, uint32_t lut_size, + enum drm_colorop_lut1d_interpolation_type lut1d_interpolation) { struct drm_property *prop; int ret; @@ -257,6 +262,17 @@ int drm_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_colorop *co drm_object_attach_property(&colorop->base, colorop->lut_size_property, lut_size); colorop->lut_size = lut_size; + /* Interpolation */ + prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, "LUT1D_INTERPOLATION", + drm_colorop_lut1d_interpolation_list, + ARRAY_SIZE(drm_colorop_lut1d_interpolation_list)); + if (!prop) + return -ENOMEM; + + colorop->lut1d_interpolation_property = prop; + drm_object_attach_property(&colorop->base, prop, lut1d_interpolation); + colorop->lut1d_interpolation = lut1d_interpolation; + /* data */ ret = drm_colorop_create_data_prop(dev, colorop); if (ret) @@ -411,6 +427,9 @@ static const char * const colorop_type_name[] = { [DRM_COLOR
[V7 37/45] drm/amd/display: add multiplier colorop
This adds support for a multiplier. This multiplier is programmed via the HDR Multiplier in DCN. With this change the following IGT tests pass: kms_colorop --run plane-XR30-XR30-multiply_125 kms_colorop --run plane-XR30-XR30-multiply_inv_125 The color pipeline now consists of the following colorops: 1. 1D curve colorop 2. 3x4 CTM 3. Multiplier 4. 1D curve colorop 5. 1D LUT 6. 1D curve colorop 7. 1D LUT Signed-off-by: Alex Hung --- .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 40 +++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 16 2 files changed, 56 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 5d6effe6f90e..edf05290d441 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 @@ -1250,6 +1250,35 @@ __set_dm_plane_colorop_3x4_matrix(struct drm_plane_state *plane_state, return 0; } +static int +__set_dm_plane_colorop_multiplier(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 drm_atomic_state *state = plane_state->state; + const struct drm_device *dev = colorop->dev; + int i = 0; + + /* Multiplier */ + 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_MULTIPLIER) { + colorop_state = new_colorop_state; + break; + } + } + + if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_MULTIPLIER) { + drm_dbg(dev, "Multiplier colorop with ID: %d\n", colorop->base.id); + dc_plane_state->hdr_mult = amdgpu_dm_fixpt_from_s3132(colorop_state->multiplier); + } + + return 0; +} + static int __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, @@ -1459,6 +1488,17 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state, if (ret) return ret; + /* Multiplier */ + colorop = colorop->next; + if (!colorop) { + drm_dbg(dev, "no multiplier colorop found\n"); + return -EINVAL; + } + + ret = __set_dm_plane_colorop_multiplier(plane_state, dc_plane_state, colorop); + if (ret) + return ret; + /* 1D Curve & LUT - SHAPER 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 9a9386bf85ec..33156531d751 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 @@ -91,6 +91,22 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr i++; + /* Multiplier */ + ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); + if (!ops[i]) { + DRM_ERROR("KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_colorop_mult_init(dev, ops[i], plane); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i-1], ops[i]); + + i++; + /* 1D curve - SHAPER TF */ ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); if (!ops[i]) { -- 2.43.0
[V7 36/45] drm/colorop: Add mutliplier type
This introduces a new drm_colorop_type: DRM_COLOROP_MULTIPLIER. It's a simple multiplier to all pixel values. The value is specified via a S31.32 fixed point provided via the "MULTIPLIER" property. Signed-off-by: Alex Hung --- v7: - Modify size_property to lut_size_property drivers/gpu/drm/drm_atomic.c | 3 +++ drivers/gpu/drm/drm_atomic_uapi.c | 4 drivers/gpu/drm/drm_colorop.c | 37 +-- include/drm/drm_colorop.h | 16 + include/uapi/drm/drm_mode.h | 9 5 files changed, 67 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index b7a05132ee01..1fa6818794d6 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -800,6 +800,9 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, case DRM_COLOROP_CTM_3X4: drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); break; + case DRM_COLOROP_MULTIPLIER: + drm_printf(p, "\tmultiplier=%llu\n", state->multiplier); + break; default: break; } diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 4744c12e429d..3c815a2a8d9d 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -728,6 +728,8 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, state->bypass = val; } else if (property == colorop->curve_1d_type_property) { state->curve_1d_type = val; + } else if (property == colorop->multiplier_property) { + state->multiplier = val; } else if (property == colorop->data_property) { return drm_atomic_color_set_data_property(colorop, state, property, val); @@ -753,6 +755,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->bypass; } else if (property == colorop->curve_1d_type_property) { *val = state->curve_1d_type; + } else if (property == colorop->multiplier_property) { + *val = state->multiplier; } else if (property == colorop->lut_size_property) { *val = colorop->lut_size; } else if (property == colorop->data_property) { diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index e6dea2713490..9fbfb2b89b1b 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -65,7 +65,8 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_CURVE, "1D Curve" }, { DRM_COLOROP_1D_LUT, "1D Curve Custom LUT" }, - { DRM_COLOROP_CTM_3X4, "3x4 Matrix"} + { DRM_COLOROP_CTM_3X4, "3x4 Matrix"}, + { DRM_COLOROP_MULTIPLIER, "Multiplier"}, }; static const char * const colorop_curve_1d_type_names[] = { @@ -286,6 +287,37 @@ int drm_colorop_ctm_3x4_init(struct drm_device *dev, struct drm_colorop *colorop } EXPORT_SYMBOL(drm_colorop_ctm_3x4_init); +/** + * drm_colorop_mult_init - Initialize a DRM_COLOROP_MULTIPLIER + * + * @dev: DRM device + * @colorop: The drm_colorop object to initialize + * @plane: The associated drm_plane + * @return zero on success, -E value on failure + */ +int drm_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane) +{ + struct drm_property *prop; + int ret; + + ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_MULTIPLIER); + if (ret) + return ret; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, "MULTIPLIER", 0, U64_MAX); + if (!prop) + return -ENOMEM; + + colorop->multiplier_property = prop; + drm_object_attach_property(&colorop->base, colorop->multiplier_property, 0); + + drm_colorop_reset(colorop); + + return 0; +} +EXPORT_SYMBOL(drm_colorop_mult_init); + static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop, struct drm_colorop_state *state) { @@ -376,7 +408,8 @@ void drm_colorop_reset(struct drm_colorop *colorop) static const char * const colorop_type_name[] = { [DRM_COLOROP_1D_CURVE] = "1D Curve", [DRM_COLOROP_1D_LUT] = "1D Curve Custom LUT", - [DRM_COLOROP_CTM_3X4] = "3x4 Matrix" + [DRM_COLOROP_CTM_3X4] = "3x4 Matrix", + [DRM_COLOROP_MULTIPLIER] = "Multiplier", }; const char *drm_get_colorop_type_name(enum drm_colorop_type type) diff --git a/include/drm/drm_colorop.h b/include/drm/drm_
[V7 26/45] drm/amd/display: Add support for sRGB EOTF in DEGAM block
Expose one 1D curve colorop with support for DRM_COLOROP_1D_CURVE_SRGB_EOTF and program HW to perform the sRGB transform when the colorop is not in bypass. With this change the following IGT test passes: kms_colorop --run plane-XR30-XR30-srgb_eotf The color pipeline now consists of a single colorop: 1. 1D curve colorop w/ sRGB EOTF Signed-off-by: Alex Hung Co-developed-by: Harry Wentland Signed-off-by: Harry Wentland --- v7: - Fix checkpatch warnings - Change switch "{ }" position - Delete double ";" - Delete "{ }" for single-line if-statement - Add a new line at EOF - Change SPDX-License-Identifier: GPL-2.0+ from // to /* */ v6: - cleanup if colorop alloc or init fails .../gpu/drm/amd/display/amdgpu_dm/Makefile| 3 +- .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 86 +++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 70 +++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h | 34 .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 10 +++ 5 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c create mode 100644 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile index ab2a97e354da..46158d67ab12 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile @@ -38,7 +38,8 @@ AMDGPUDM = \ amdgpu_dm_pp_smu.o \ amdgpu_dm_psr.o \ amdgpu_dm_replay.o \ - amdgpu_dm_wb.o + amdgpu_dm_wb.o \ + amdgpu_dm_colorop.o ifdef CONFIG_DRM_AMD_DC_FP AMDGPUDM += dc_fpu.o 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 ebabfe3a512f..566035af00cd 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 @@ -668,6 +668,18 @@ amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf) } } +static enum dc_transfer_func_predefined +amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf) +{ + switch (tf) { + case DRM_COLOROP_1D_CURVE_SRGB_EOTF: + case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF: + return TRANSFER_FUNCTION_SRGB; + default: + return TRANSFER_FUNCTION_LINEAR; + } +} + static void __to_dc_lut3d_color(struct dc_rgb *rgb, const struct drm_color_lut lut, int bit_precision) @@ -1137,6 +1149,59 @@ __set_dm_plane_degamma(struct drm_plane_state *plane_state, return 0; } +static int +__set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state, + struct drm_colorop_state *colorop_state) +{ + struct dc_transfer_func *tf = &dc_plane_state->in_transfer_func; + struct drm_colorop *colorop = colorop_state->colorop; + struct drm_device *drm = colorop->dev; + + if (colorop->type != DRM_COLOROP_1D_CURVE && + colorop_state->curve_1d_type != DRM_COLOROP_1D_CURVE_SRGB_EOTF) + return -EINVAL; + + if (colorop_state->bypass) { + tf->type = TF_TYPE_BYPASS; + tf->tf = TRANSFER_FUNCTION_LINEAR; + return 0; + } + + drm_dbg(drm, "Degamma colorop with ID: %d\n", colorop->base.id); + + tf->type = TF_TYPE_PREDEFINED; + tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); + + return 0; +} + +static int +__set_dm_plane_colorop_degamma(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 drm_atomic_state *state = plane_state->state; + int i = 0; + + old_colorop = colorop; + + /* 1st op: 1d curve - degamma */ + for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { + if (new_colorop_state->colorop == old_colorop && + new_colorop_state->curve_1d_type == DRM_COLOROP_1D_CURVE_SRGB_EOTF) { + colorop_state = new_colorop_state; + break; + } + } + + if (!colorop_state) + return -EINVAL; + + return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state); +} + static int amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state) @@ -1187,6 +1252,24 @@ amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state, return 0; } +static int +
[V7 43/45] drm/amd/display: Add AMD color pipeline doc
From: Harry Wentland Add kernel doc for AMD color pipeline. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 122 +++--- 1 file changed, 102 insertions(+), 20 deletions(-) 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 5e8c5c0657c4..56fb0870a2fc 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 @@ -33,6 +33,32 @@ /** * DOC: overview * + * We have three types of color management in the AMD display driver. + * 1. the legacy &drm_crtc DEGAMMA, CTM, and GAMMA properties + * 2. AMD driver private color management on &drm_plane and &drm_crtc + * 3. AMD plane color pipeline + * + * The CRTC properties are the original color management. When they were + * implemented per-plane color management was not a thing yet. Because + * of that we could get away with plumbing the DEGAMMA and CTM + * properties to pre-blending HW functions. This is incompatible with + * per-plane color management, such as via the AMD private properties or + * the new drm_plane color pipeline. The only compatible CRTC property + * with per-plane color management is the GAMMA property as it is + * applied post-blending. + * + * The AMD driver private color management properties are only exposed + * when the kernel is built explicitly with -DAMD_PRIVATE_COLOR. They + * are temporary building blocks on the path to full-fledged &drm_plane + * and &drm_crtc color pipelines and lay the driver's groundwork for the + * color pipelines. + * + * The AMD plane color pipeline describes AMD's &drm_colorops via the + * &drm_plane's COLOR_PIPELINE property. + * + * drm_crtc Properties + * --- + * * The DC interface to HW gives us the following color management blocks * per pipe (surface): * @@ -43,33 +69,89 @@ * - Surface regamma LUT (normalized) * - Output CSC (normalized) * - * But these aren't a direct mapping to DRM color properties. The current DRM - * interface exposes CRTC degamma, CRTC CTM and CRTC regamma while our hardware - * is essentially giving: + * But these aren't a direct mapping to DRM color properties. The + * current DRM interface exposes CRTC degamma, CRTC CTM and CRTC regamma + * while our hardware is essentially giving: * * Plane CTM -> Plane degamma -> Plane CTM -> Plane regamma -> Plane CTM * - * The input gamma LUT block isn't really applicable here since it operates - * on the actual input data itself rather than the HW fp representation. The - * input and output CSC blocks are technically available to use as part of - * the DC interface but are typically used internally by DC for conversions - * between color spaces. These could be blended together with user - * adjustments in the future but for now these should remain untouched. + * The input gamma LUT block isn't really applicable here since it + * operates on the actual input data itself rather than the HW fp + * representation. The input and output CSC blocks are technically + * available to use as part of the DC interface but are typically used + * internally by DC for conversions between color spaces. These could be + * blended together with user adjustments in the future but for now + * these should remain untouched. + * + * The pipe blending also happens after these blocks so we don't + * actually support any CRTC props with correct blending with multiple + * planes - but we can still support CRTC color management properties in + * DM in most single plane cases correctly with clever management of the + * DC interface in DM. + * + * As per DRM documentation, blocks should be in hardware bypass when + * their respective property is set to NULL. A linear DGM/RGM LUT should + * also considered as putting the respective block into bypass mode. + * + * This means that the following configuration is assumed to be the + * default: + * + * Plane DGM Bypass -> Plane CTM Bypass -> Plane RGM Bypass -> ... CRTC + * DGM Bypass -> CRTC CTM Bypass -> CRTC RGM Bypass + * + * AMD Private Color Management on drm_plane + * - + * + * The AMD private color management properties on a &drm_plane are: + * + * - AMD_PLANE_DEGAMMA_LUT + * - AMD_PLANE_DEGAMMA_LUT_SIZE + * - AMD_PLANE_DEGAMMA_TF + * - AMD_PLANE_HDR_MULT + * - AMD_PLANE_CTM + * - AMD_PLANE_SHAPER_LUT + * - AMD_PLANE_SHAPER_LUT_SIZE + * - AMD_PLANE_SHAPER_TF + * - AMD_PLANE_LUT3D + * - AMD_PLANE_LUT3D_SIZE + * - AMD_PLANE_BLEND_LUT + * - AMD_PLANE_BLEND_LUT_SIZE + * - AMD_PLANE_BLEND_TF + * + * The AMD private color management property on a &drm_crtc is: + * + * - AMD_CRTC_REGAMMA_TF + * + * Use of these properties is discouraged. + * + * AMD plane color pipeline + * + * + * The AMD &drm_
[V7 44/45] drm/colorop: Add kernel doc for data blob
Add layout of data blob for colorop types: DRM_COLOROP_1D_LUT, DRM_COLOROP_CTM_3X4 and DRM_COLOROP_3D_LUT Signed-off-by: Alex Hung --- include/drm/drm_colorop.h | 18 ++ 1 file changed, 18 insertions(+) diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index 7b58dcf0615a..11b474a9b10a 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -146,6 +146,24 @@ struct drm_colorop_state { * * Data blob for any TYPE that requires such a blob. The * interpretation of the blob is TYPE-specific. +* +* For 1D LUTs (DRM_COLOROP_1D_LUT), the blob is an array of +* struct drm_color_lut with size of "lut_size". +* +* For 3x4 matrix (DRM_COLOROP_CTM_3X4), the blob is a float[12]: +* out matrix in +* |R| |0 1 2 3 | | R | +* |G| = |4 5 6 7 | x | G | +* |B| |8 9 10 12| | B | +* +* For 3D LUTs (DRM_COLOROP_3D_LUT), the blob is a 3D array of +* struct drm_color_lut with dimension length of "lut_size". +* The LUT elements are traversed like so: +* for R in range 0..n +* for G in range 0..n +* for B in range 0..n +* color = lut3d[R][G][B] +* */ struct drm_property_blob *data; -- 2.43.0
[V7 42/45] drm/amd/display: add 3D LUT colorop
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 --- v7: - Simplify 3D LUT according to drm_colorop changes (Simon Ser) .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 100 +- .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 19 2 files changed, 116 insertions(+), 3 deletions(-) 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 54ec12c1352f..5e8c5c0657c4 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 @@ -1282,7 +1282,8 @@ __set_dm_plane_colorop_multiplier(struct drm_plane_state *plane_state, static int __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, - struct drm_colorop *colorop) + struct drm_colorop *colorop, + bool *enabled) { struct drm_colorop *old_colorop; struct drm_colorop_state *colorop_state = NULL, *new_colorop_state; @@ -1310,6 +1311,7 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; __set_output_tf(tf, 0, 0, false); + *enabled = true; } /* 1D LUT - SHAPER LUT */ @@ -1337,8 +1339,88 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, shaper_size = shaper_lut != NULL ? shaper_size : 0; /* Custom LUT size must be the same as supported size */ - if (shaper_size == colorop->lut_size) + if (shaper_size == colorop->lut_size) { __set_output_tf(tf, shaper_lut, shaper_size, false); + *enabled = true; + } + } + + 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 *drm_lut3d, + uint32_t drm_lut3d_size, + struct dc_3dlut *lut) +{ + if (!drm_lut3d_size) + return; + + lut->state.bits.initialized = 0; + + /* 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_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, +bool shaper_enabled) +{ + 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 *lut3d; + uint32_t lut3d_size; + int i = 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 0; + } + + drm_dbg(dev, "3D LUT colorop with ID: %d\n", colorop->base.id); + lut3d = __extract_blob_lut(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
[V7 41/45] drm/colorop: Add 3D LUT supports to color pipeline
It is to be used to enable HDR by allowing userpace to create and pass 3D LUTs to kernel and hardware. new drm_colorop_type: DRM_COLOROP_3D_LUT. Signed-off-by: Alex Hung --- v7: - Simplify 3D LUT by removing lut_3d_modes and related functions (Simon Ser) drivers/gpu/drm/drm_atomic.c | 6 +++ drivers/gpu/drm/drm_atomic_uapi.c | 6 +++ drivers/gpu/drm/drm_colorop.c | 72 +++ include/drm/drm_colorop.h | 21 + include/uapi/drm/drm_mode.h | 21 + 5 files changed, 126 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 88219b095d99..d94ea4e11888 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -806,6 +806,12 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, case DRM_COLOROP_MULTIPLIER: drm_printf(p, "\tmultiplier=%llu\n", state->multiplier); break; + case DRM_COLOROP_3D_LUT: + drm_printf(p, "\tsize=%d\n", colorop->lut_size); + drm_printf(p, "\tinterpolation=%s\n", + drm_get_colorop_lut3d_interpolation_name(colorop->lut3d_interpolation)); + drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); + break; default: break; } diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 3c815a2a8d9d..f742a6235c12 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -707,6 +707,10 @@ static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, case DRM_COLOROP_CTM_3X4: size = sizeof(struct drm_color_ctm_3x4); break; + case DRM_COLOROP_3D_LUT: + size = colorop->lut_size * colorop->lut_size * colorop->lut_size * + sizeof(struct drm_color_lut); + break; default: /* should never get here */ return -EINVAL; @@ -759,6 +763,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->multiplier; } else if (property == colorop->lut_size_property) { *val = colorop->lut_size; + } else if (property == colorop->lut3d_interpolation_property) { + *val = colorop->lut3d_interpolation; } else if (property == colorop->data_property) { *val = (state->data) ? state->data->base.id : 0; } else { diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index dfff8e37ff05..e3dd49c0080d 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -67,6 +67,7 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_LUT, "1D Curve Custom LUT" }, { DRM_COLOROP_CTM_3X4, "3x4 Matrix"}, { DRM_COLOROP_MULTIPLIER, "Multiplier"}, + { DRM_COLOROP_3D_LUT, "3D LUT"}, }; static const char * const colorop_curve_1d_type_names[] = { @@ -82,6 +83,11 @@ static const struct drm_prop_enum_list drm_colorop_lut1d_interpolation_list[] = { DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, "Linear" }, }; + +static const struct drm_prop_enum_list drm_colorop_lut3d_interpolation_list[] = { + { DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL, "Tetrahedral" }, +}; + /* Init Helpers */ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, @@ -349,6 +355,51 @@ int drm_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop, } EXPORT_SYMBOL(drm_colorop_mult_init); +int drm_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + uint32_t lut_size, + enum drm_colorop_lut3d_interpolation_type interpolation, + bool allow_bypass) +{ + struct drm_property *prop; + int ret; + + ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_3D_LUT, allow_bypass); + if (ret) + return ret; + + /* LUT size */ + prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "SIZE", +0, UINT_MAX); + if (!prop) + return -ENOMEM; + + colorop->lut_size_property = prop; + drm_object_attach_property(&colorop->base, colorop->lut_size_property, lut_size); + colorop->lut_size = lut_size; + + /* interpolation */ + prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, "LUT3D_INTERPOLATION", + drm_colorop_lut3d_interpolation_list, +
[V7 45/45] drm/colorop: Add destroy functions for color pipeline
The functions are to clean up color pipeline when a device driver fails to create its color pipeline. Signed-off-by: Alex Hung --- .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 3 +- drivers/gpu/drm/drm_colorop.c | 41 +++ drivers/gpu/drm/vkms/vkms_colorop.c | 3 +- include/drm/drm_colorop.h | 2 + 4 files changed, 45 insertions(+), 4 deletions(-) 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 e03e6044f937..80173f00dfd0 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 @@ -200,8 +200,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr return 0; cleanup: - for (; i >= 0; i--) - kfree(ops[i]); + drm_colorop_pipeline_destroy(plane); return ret; } diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index e3dd49c0080d..5245d6a4da39 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -154,6 +154,47 @@ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, return ret; } +/** + * drm_colorop_cleanup - Cleanup a drm_colorop object in color_pipeline + * + * @colorop: The drm_colorop object to be cleaned + */ +void drm_colorop_cleanup(struct drm_colorop *colorop) +{ + struct drm_device *dev = colorop->dev; + struct drm_mode_config *config = &dev->mode_config; + + list_del(&colorop->head); + config->num_colorop--; + + if (colorop->state && colorop->state->data) { + drm_property_blob_put(colorop->state->data); + colorop->state->data = NULL; + } + + kfree(colorop->state); + kfree(colorop); +} + +/** + * drm_colorop_pipeline_destroy - Helper for color pipeline destruction + * + * @plane: - The drm_plane structure containing the color_pipeline + * + * Provides a default color pipeline destroy handler for a planes. + */ +void drm_colorop_pipeline_destroy(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + struct drm_colorop *colorop, *next; + + list_for_each_entry_safe(colorop, next, &config->colorop_list, head) { + drm_colorop_cleanup(colorop); + } +} +EXPORT_SYMBOL(drm_colorop_pipeline_destroy); + /** * drm_colorop_curve_1d_init - Initialize a DRM_COLOROP_1D_CURVE * diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c index c418926d5bc0..5cf47a6ef79e 100644 --- a/drivers/gpu/drm/vkms/vkms_colorop.c +++ b/drivers/gpu/drm/vkms/vkms_colorop.c @@ -89,8 +89,7 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr return 0; cleanup: - for (; i >= 0; i--) - kfree(ops[i]); + drm_colorop_pipeline_destroy(plane); return ret; } diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index 11b474a9b10a..953eea079708 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -367,6 +367,8 @@ static inline struct drm_colorop *drm_colorop_find(struct drm_device *dev, return mo ? obj_to_colorop(mo) : NULL; } +void drm_colorop_pipeline_destroy(struct drm_plane *plane); + int drm_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop *colorop, struct drm_plane *plane, u64 supported_tfs, bool allow_bypass); -- 2.43.0
[V7 35/45] drm/amd/display: add 3x4 matrix colorop
This adds support for a 3x4 color transformation matrix. With this change the following IGT tests pass: kms_colorop --run plane-XR30-XR30-ctm_3x4_50_desat kms_colorop --run plane-XR30-XR30-ctm_3x4_overdrive kms_colorop --run plane-XR30-XR30-ctm_3x4_oversaturate kms_colorop --run plane-XR30-XR30-ctm_3x4_bt709_enc kms_colorop --run plane-XR30-XR30-ctm_3x4_bt709_dec The color pipeline now consists of the following colorops: 1. 1D curve colorop 2. 3x4 CTM 3. 1D curve colorop 4. 1D LUT 5. 1D curve colorop 6. 1D LUT Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Change %lu to %zu for sizeof() when In drm_warn v6: - fix warnings in dbg prints .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 50 +++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 16 ++ 2 files changed, 66 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 0bea52eede39..5d6effe6f90e 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 @@ -1211,6 +1211,45 @@ __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state, return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state); } +static int +__set_dm_plane_colorop_3x4_matrix(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 drm_atomic_state *state = plane_state->state; + const struct drm_device *dev = colorop->dev; + const struct drm_property_blob *blob; + struct drm_color_ctm_3x4 *ctm = NULL; + int i = 0; + + /* 3x4 matrix */ + 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_CTM_3X4) { + colorop_state = new_colorop_state; + break; + } + } + + if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_CTM_3X4) { + drm_dbg(dev, "3x4 matrix colorop with ID: %d\n", colorop->base.id); + blob = colorop_state->data; + if (blob->length == sizeof(struct drm_color_ctm_3x4)) { + ctm = blob ? (struct drm_color_ctm_3x4 *) blob->data : NULL; + __drm_ctm_3x4_to_dc_matrix(ctm, dc_plane_state->gamut_remap_matrix.matrix); + dc_plane_state->gamut_remap_matrix.enable_remap = true; + dc_plane_state->input_csc_color_matrix.enable_adjustment = false; + } else + drm_warn(dev, "blob->length (%lu) isn't equal to drm_color_ctm_3x4 (%zu)\n", +blob->length, sizeof(struct drm_color_ctm_3x4)); + } + + return 0; +} + static int __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, @@ -1409,6 +1448,17 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state, if (ret) return ret; + /* 3x4 matrix */ + colorop = colorop->next; + if (!colorop) { + drm_dbg(dev, "no 3x4 matrix colorop found\n"); + return -EINVAL; + } + + ret = __set_dm_plane_colorop_3x4_matrix(plane_state, dc_plane_state, colorop); + if (ret) + return ret; + /* 1D Curve & LUT - SHAPER 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 8a5e15083f11..9a9386bf85ec 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 @@ -75,6 +75,22 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr i++; + /* 3x4 matrix */ + ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); + if (!ops[i]) { + DRM_ERROR("KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i-1], ops[i]); + + i++; + /* 1D curve - SHAPER TF */ ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); if (!ops[i]) { -- 2.43.0
Re: [PATCH v6 41/44] drm/colorop: allow non-bypass colorops
On 10/4/24 05:43, Louis Chauvet wrote: On 03/10/24 - 16:01, Harry Wentland wrote: Not all HW will be able to do bypass on all color operations. Introduce an 'allow_bypass' boolean for all colorop init functions and only create the BYPASS property when it's true. Signed-off-by: Harry Wentland --- .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 22 +--- drivers/gpu/drm/drm_atomic.c | 3 +- drivers/gpu/drm/drm_colorop.c | 51 --- drivers/gpu/drm/vkms/vkms_colorop.c | 8 +-- include/drm/drm_colorop.h | 10 ++-- 5 files changed, 60 insertions(+), 34 deletions(-) [...] --- a/drivers/gpu/drm/vkms/vkms_colorop.c +++ b/drivers/gpu/drm/vkms/vkms_colorop.c @@ -31,7 +31,7 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs); + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs, true); if (ret) goto cleanup; @@ -48,7 +48,7 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane, true); if (ret) goto cleanup; @@ -64,7 +64,7 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane, true); if (ret) goto cleanup; @@ -80,7 +80,7 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs); + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs, true); if (ret) goto cleanup; You allow the bypass here, but you forgot to add a check in apply_colorop to bypass the colorop when this is set. It seems to be the case in the AMD driver too. Or maybe you wanted to pass false in "allow_bypass"? The bypass is used in each color transform. For example, it is used in pre_blend_color_transform() of vkms_composer.c, before calling apply_colorop(). Similarly, it is checked before calling each color transformation in amdgpu_dm_color.c, such as __set_colorop_in_tf_1d_curve(), __set_dm_plane_colorop_3x4_matrix() and etc. diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index d3a00296973d..b8c1c4da3444 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -333,14 +333,16 @@ static inline struct drm_colorop *drm_colorop_find(struct drm_device *dev, } int drm_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop *colorop, - struct drm_plane *plane, u64 supported_tfs); + struct drm_plane *plane, u64 supported_tfs, + bool allow_bypass); int drm_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_colorop *colorop, struct drm_plane *plane, uint32_t lut_size, - enum drm_colorop_lut1d_interpolation_type lut1d_interpolation); + enum drm_colorop_lut1d_interpolation_type lut1d_interpolation, + bool allow_bypass); int drm_colorop_ctm_3x4_init(struct drm_device *dev, struct drm_colorop *colorop, -struct drm_plane *plane); +struct drm_plane *plane, bool allow_bypass); int drm_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop, - struct drm_plane *plane); + struct drm_plane *plane, bool allow_bypass); struct drm_colorop_state * drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop); -- 2.46.2
[V7 14/45] drm/vkms: Add enumerated 1D curve colorop
From: Harry Wentland This patch introduces a VKMS color pipeline that includes two drm_colorops for named transfer functions. For now the only ones supported are sRGB EOTF, sRGB Inverse EOTF, and a Linear TF. We will expand this in the future but I don't want to do so without accompanying IGT tests. We introduce a new vkms_luts.c file that hard-codes sRGB EOTF, sRGB Inverse EOTF, and a linear EOTF LUT. These have been generated with 256 entries each as IGT is currently testing only 8 bpc surfaces. We will likely need higher precision but I'm reluctant to make that change without clear indication that we need it. We'll revisit and, if necessary, regenerate the LUTs when we have IGT tests for higher precision buffers. Signed-off-by: Harry Wentland Signed-off-by: Alex Hung --- v7: - Fix checkpatch warnings (Louis Chauvet) - Change kzalloc(sizeof(struct drm_colorop) ...) to kzalloc(sizeof(*ops[i]) ...) - Remove if (ops[i]) before kfree(ops[i]) - Fix styles by adding and removing spaces (new lines, tabs and so on) v6: - drop 'len' var (Louis Chauvet) - cleanup if colorop alloc or init fails (Louis Chauvet) - switch loop in pre_blend_transform (Louis Chauvet) - drop extraneous if (colorop) inside while (colorop) (Louis Chauvet) v5: - Squash with "Pull apply_colorop out of pre_blend_color_transform" (Sebastian) - Fix warnings - Fix include - Drop TODOs v4: - Drop _tf_ from color_pipeline init function - Pass supported TFs into colorop init - Create bypass pipeline in DRM helper (Pekka) v2: - Add commit description - Fix sRGB EOTF LUT definition - Add linear and sRGB inverse EOTF LUTs drivers/gpu/drm/vkms/Makefile| 4 +- drivers/gpu/drm/vkms/vkms_colorop.c | 82 +++ drivers/gpu/drm/vkms/vkms_composer.c | 50 ++ drivers/gpu/drm/vkms/vkms_drv.h | 3 + drivers/gpu/drm/vkms/vkms_luts.c | 801 +++ drivers/gpu/drm/vkms/vkms_luts.h | 12 + drivers/gpu/drm/vkms/vkms_plane.c| 2 + 7 files changed, 953 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/vkms/vkms_colorop.c create mode 100644 drivers/gpu/drm/vkms/vkms_luts.c create mode 100644 drivers/gpu/drm/vkms/vkms_luts.h diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile index 8d3e46dde635..0bf3c116f1ae 100644 --- a/drivers/gpu/drm/vkms/Makefile +++ b/drivers/gpu/drm/vkms/Makefile @@ -6,7 +6,9 @@ vkms-y := \ vkms_formats.o \ vkms_crtc.o \ vkms_composer.o \ - vkms_writeback.o + vkms_writeback.o \ + vkms_colorop.o \ + vkms_luts.o obj-$(CONFIG_DRM_VKMS) += vkms.o obj-$(CONFIG_DRM_VKMS_KUNIT_TESTS) += tests/ diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c new file mode 100644 index ..af319cd3de23 --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_colorop.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include +#include +#include + +#include "vkms_drv.h" + +static const u64 supported_tfs = + BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF); + +#define MAX_COLOR_PIPELINE_OPS 2 + +static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list) +{ + struct drm_colorop *ops[MAX_COLOR_PIPELINE_OPS]; + struct drm_device *dev = plane->dev; + int ret; + int i = 0; + + memset(ops, 0, sizeof(ops)); + + /* 1st op: 1d curve */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + DRM_ERROR("KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs); + if (ret) + goto cleanup; + + list->type = ops[i]->base.id; + list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", ops[i]->base.id); + + i++; + + /* 2nd op: 1d curve */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + DRM_ERROR("KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i - 1], ops[i]); + + return 0; + +cleanup: + for (; i >= 0; i--) + kfree(ops[i]); + + return ret; +} + +int vkms_initialize_colorops(struct drm_plane *plane) +{ + struct drm_prop_enum_list pipeline; + int ret; + + /* Add color pipeline */ + ret = vkms_initialize_color_pipeline(plane, &pipeline); + if (ret) + return ret; + + /* Create COLOR_PIPELINE property and attach */ + ret = drm_plane_create_color_pipeline_property(plane
[V7 12/45] drm/plane: Add COLOR PIPELINE property
From: Harry Wentland We're adding a new enum COLOR PIPELINE property. This property will have entries for each COLOR PIPELINE by referencing the DRM object ID of the first drm_colorop of the pipeline. 0 disables the entire COLOR PIPELINE. Userspace can use this to discover the available color pipelines, as well as set the desired one. The color pipelines are programmed via properties on the actual drm_colorop objects. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix a checkpatch warning - a new line after variable declaration v4: - Add pipeline property creation helper (Pekka) - Fix function comment for drm_atomic_set_colorop_for_plane (Pekka) - Always create Bypass pipeline (Pekka) - Add missing function declaration (Chaitanya Kumar Borah) drivers/gpu/drm/drm_atomic.c | 46 drivers/gpu/drm/drm_atomic_state_helper.c | 5 +++ drivers/gpu/drm/drm_atomic_uapi.c | 43 +++ drivers/gpu/drm/drm_plane.c | 52 +++ include/drm/drm_atomic.h | 3 ++ include/drm/drm_atomic_uapi.h | 2 + include/drm/drm_plane.h | 11 + 7 files changed, 162 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 736fa6a11c08..35136987d5e8 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1461,6 +1461,52 @@ drm_atomic_add_affected_planes(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_add_affected_planes); +/** + * drm_atomic_add_affected_colorops - add colorops for plane + * @state: atomic state + * @plane: DRM plane + * + * This function walks the current configuration and adds all colorops + * currently used by @plane to the atomic configuration @state. This is useful + * when an atomic commit also needs to check all currently enabled colorop on + * @plane, e.g. when changing the mode. It's also useful when re-enabling a plane + * to avoid special code to force-enable all colorops. + * + * Since acquiring a colorop state will always also acquire the w/w mutex of the + * current plane for that colorop (if there is any) adding all the colorop states for + * a plane will not reduce parallelism of atomic updates. + * + * Returns: + * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK + * then the w/w mutex code has detected a deadlock and the entire atomic + * sequence must be restarted. All other errors are fatal. + */ +int +drm_atomic_add_affected_colorops(struct drm_atomic_state *state, +struct drm_plane *plane) +{ + struct drm_colorop *colorop; + struct drm_colorop_state *colorop_state; + + WARN_ON(!drm_atomic_get_new_plane_state(state, plane)); + + drm_dbg_atomic(plane->dev, + "Adding all current colorops for [plane:%d:%s] to %p\n", + plane->base.id, plane->name, state); + + drm_for_each_colorop(colorop, plane->dev) { + if (colorop->plane != plane) + continue; + + colorop_state = drm_atomic_get_colorop_state(state, colorop); + if (IS_ERR(colorop_state)) + return PTR_ERR(colorop_state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_add_affected_colorops); + /** * drm_atomic_check_only - check whether a given config would work * @state: atomic configuration to check diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 519228eb1095..d1dd082b1286 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -267,6 +267,11 @@ void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state, plane_state->color_range = val; } + if (plane->color_pipeline_property) { + /* default is always NULL, i.e., bypass */ + plane_state->color_pipeline = NULL; + } + if (plane->zpos_property) { if (!drm_object_property_get_default_value(&plane->base, plane->zpos_property, diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 8a038e04be58..f2503f56fc82 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -256,6 +256,36 @@ drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, } EXPORT_SYMBOL(drm_atomic_set_fb_for_plane); + +/** + * drm_atomic_set_colorop_for_plane - set colorop for plane + * @plane_state: atomic state object for the plane + * @colorop: colorop to use for the plane + * + * Helper function to select the color pipeline on a plane by setting + * it to the first drm_colorop element of the pipeline. + */ +void +drm_atomic_
[V7 15/45] drm/vkms: Add kunit tests for linear and sRGB LUTs
From: Harry Wentland Two tests are added to VKMS LUT handling: - linear - inv_srgb Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix checkpatch warnings (Louis Chauvet) - Adde a commit messages - Fix code styles by adding and removing spaces (new lines, tabs and so on) drivers/gpu/drm/vkms/tests/vkms_color_test.c | 39 +++- drivers/gpu/drm/vkms/vkms_composer.c | 17 ++--- drivers/gpu/drm/vkms/vkms_composer.h | 13 +++ 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c index b53beaac2703..d765c5eb5d88 100644 --- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c +++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c @@ -6,6 +6,7 @@ #include #include "../vkms_drv.h" #include "../vkms_composer.h" +#include "../vkms_luts.h" #define TEST_LUT_SIZE 16 @@ -36,7 +37,6 @@ static const struct vkms_color_lut test_linear_lut = { .channel_value2index_ratio = 0xf000fll }; - static void vkms_color_test_get_lut_index(struct kunit *test) { s64 lut_index; @@ -49,6 +49,19 @@ static void vkms_color_test_get_lut_index(struct kunit *test) lut_index = get_lut_index(&test_linear_lut, test_linear_array[i].red); KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(lut_index), i); } + + KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&srgb_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x101)), 0x1); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x202)), 0x2); + + KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&srgb_inv_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x101)), 0x1); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x202)), 0x2); + + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0xfefe)), 0xfe); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x)), 0xff); } static void vkms_color_test_lerp(struct kunit *test) @@ -155,9 +168,33 @@ static void vkms_color_test_lerp(struct kunit *test) KUNIT_EXPECT_EQ(test, lerp_u16(0x0, 0x1, 0x8000), 0x1); } +static void vkms_color_test_linear(struct kunit *test) +{ + for (int i = 0; i < LUT_SIZE; i++) { + int linear = apply_lut_to_channel_value(&linear_eotf, i * 0x101, LUT_RED); + + KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(linear, 0x101), i); + } +} + +static void vkms_color_srgb_inv_srgb(struct kunit *test) +{ + u16 srgb, final; + + for (int i = 0; i < LUT_SIZE; i++) { + srgb = apply_lut_to_channel_value(&srgb_eotf, i * 0x101, LUT_RED); + final = apply_lut_to_channel_value(&srgb_inv_eotf, srgb, LUT_RED); + + KUNIT_EXPECT_GE(test, final / 0x101, i - 1); + KUNIT_EXPECT_LE(test, final / 0x101, i + 1); + } +} + static struct kunit_case vkms_color_test_cases[] = { KUNIT_CASE(vkms_color_test_get_lut_index), KUNIT_CASE(vkms_color_test_lerp), + KUNIT_CASE(vkms_color_test_linear), + KUNIT_CASE(vkms_color_srgb_inv_srgb), {} }; diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 983654540ee5..ee3cfe153d8f 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -113,19 +113,8 @@ VISIBLE_IF_KUNIT s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel } EXPORT_SYMBOL_IF_KUNIT(get_lut_index); -/* - * This enum is related to the positions of the variables inside - * `struct drm_color_lut`, so the order of both needs to be the same. - */ -enum lut_channel { - LUT_RED = 0, - LUT_GREEN, - LUT_BLUE, - LUT_RESERVED -}; - -static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value, - enum lut_channel channel) +VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value, + enum lut_channel channel) { s64 lut_index = get_lut_index(lut, channel_value); u16 *floor_lut_value, *ceil_lut_value; @@ -150,6 +139,8 @@ static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 chan return lerp_u16(floor_channel_value, ceil_channel_value, lut_index & DRM_FIXED_DECIMAL_MASK); } +EXPORT_SYMBOL_IF_KUNIT(apply_lut_to_channel_value); + static void apply_lut(const
[V7 13/45] drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE
From: Harry Wentland With the introduction of the pre-blending color pipeline we can no longer have color operations that don't have a clear position in the color pipeline. We deprecate all existing plane properties. For upstream drivers those are: - COLOR_ENCODING - COLOR_RANGE Drivers are expected to ignore these properties when programming the HW. Setting of the COLOR_PIPELINE plane property or drm_colorop properties is only allowed for userspace that sets this client cap. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v5: - Fix kernel docs v4: - Don't block setting of COLOR_RANGE and COLOR_ENCODING when client cap is set drivers/gpu/drm/drm_atomic_uapi.c | 13 - drivers/gpu/drm/drm_ioctl.c | 7 +++ include/drm/drm_file.h| 7 +++ include/uapi/drm/drm.h| 16 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index f2503f56fc82..f0c22abcc28f 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -570,6 +570,12 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, } else if (property == plane->color_range_property) { state->color_range = val; } else if (property == plane->color_pipeline_property) { + if (!file_priv->plane_color_pipeline) { + drm_dbg_atomic(dev, + "Setting COLOR_PIPELINE plane property not permitted unless DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set\n"); + return -EINVAL; + } + /* find DRM colorop object */ struct drm_colorop *colorop = NULL; @@ -1180,6 +1186,12 @@ int drm_atomic_set_property(struct drm_atomic_state *state, break; } case DRM_MODE_OBJECT_COLOROP: { + if (!file_priv->plane_color_pipeline) { + drm_dbg_atomic(prop->dev, + "[OBJECT:%d] is a colorop but DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE not set\n", + obj->id); + ret = -EINVAL; + } struct drm_colorop *colorop = obj_to_colorop(obj); struct drm_colorop_state *colorop_state; @@ -1192,7 +1204,6 @@ int drm_atomic_set_property(struct drm_atomic_state *state, ret = drm_atomic_colorop_set_property(colorop, colorop_state, file_priv, prop, prop_value); - break; } default: diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 51f39912866f..6b5021b68e35 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -373,6 +373,13 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) return -EINVAL; file_priv->supports_virtualized_cursor_plane = req->value; break; + case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE: + if (!file_priv->atomic) + return -EINVAL; + if (req->value > 1) + return -EINVAL; + file_priv->plane_color_pipeline = req->value; + break; default: return -EINVAL; } diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 8c0030c77308..6d9a1578c7dc 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -206,6 +206,13 @@ struct drm_file { */ bool writeback_connectors; + /** +* @plane_color_pipeline: +* +* True if client understands plane color pipelines +*/ + bool plane_color_pipeline; + /** * @was_master: * diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 16122819edfe..a2fc85ecdea1 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -875,6 +875,22 @@ struct drm_get_cap { */ #define DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT6 +/** + * DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE + * + * If set to 1 the DRM core will allow setting the COLOR_PIPELINE + * property on a &drm_plane, as well as drm_colorop properties. + * + * Drivers will ignore these drm_plane properties: + * - COLOR_ENCODING + * - COLOR_RANGE + * + * The client must enable &DRM_CLIENT_CAP_ATOMIC first. + * + * This capability is currently in development. + */ +#define DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE7 + /* DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */ struct drm_set_client_cap { __u64 capability; -- 2.43.0
[V7 11/45] drm/colorop: Add atomic state print for drm_colorop
From: Harry Wentland Print atomic state for drm_colorop in debugfs Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Add a commit messages - Squash "drm/colorop: Add NEXT to colorop state print" (Simon Ser) drivers/gpu/drm/drm_atomic.c | 26 +- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 44c5b5dd8b33..736fa6a11c08 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -777,6 +777,20 @@ static int drm_atomic_plane_check(const struct drm_plane_state *old_plane_state, return 0; } + + +static void drm_atomic_colorop_print_state(struct drm_printer *p, + const struct drm_colorop_state *state) +{ + struct drm_colorop *colorop = state->colorop; + + drm_printf(p, "colorop[%u]:\n", colorop->base.id); + drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type)); + drm_printf(p, "\tbypass=%u\n", state->bypass); + drm_printf(p, "\tcurve_1d_type=%s\n", drm_get_colorop_curve_1d_type_name(state->curve_1d_type)); + drm_printf(p, "\tnext=%d\n", colorop->next ? colorop->next->base.id : 0); +} + static void drm_atomic_plane_print_state(struct drm_printer *p, const struct drm_plane_state *state) { @@ -798,7 +812,8 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, drm_printf(p, "\tcolor-range=%s\n", drm_get_color_range_name(state->color_range)); drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed); - + drm_printf(p, "\tcolor-pipeline=%d\n", + state->color_pipeline ? state->color_pipeline->base.id : 0); if (plane->funcs->atomic_print_state) plane->funcs->atomic_print_state(p, state); } @@ -1842,6 +1857,7 @@ static void __drm_state_dump(struct drm_device *dev, struct drm_printer *p, bool take_locks) { struct drm_mode_config *config = &dev->mode_config; + struct drm_colorop *colorop; struct drm_plane *plane; struct drm_crtc *crtc; struct drm_connector *connector; @@ -1851,6 +1867,14 @@ static void __drm_state_dump(struct drm_device *dev, struct drm_printer *p, if (!drm_drv_uses_atomic_modeset(dev)) return; + list_for_each_entry(colorop, &config->colorop_list, head) { + if (take_locks) + drm_modeset_lock(&colorop->plane->mutex, NULL); + drm_atomic_colorop_print_state(p, colorop->state); + if (take_locks) + drm_modeset_unlock(&colorop->plane->mutex); + } + list_for_each_entry(plane, &config->plane_list, head) { if (take_locks) drm_modeset_lock(&plane->mutex, NULL); -- 2.43.0
[V7 16/45] drm/colorop: Add 3x4 CTM type
From: Harry Wentland This type is used to support a 3x4 matrix in colorops. A 3x4 matrix uses the last column as a "bias" column. Some HW exposes support for 3x4. The calculation looks like: out matrixin |R| |0 1 2 3 | | R | |G| = |4 5 6 7 | x | G | |B| |8 9 10 11| | B | |1.0| This is also the first colorop where we need a blob property to program the property. For that we'll introduce a new DATA property that can be used by all colorop TYPEs requiring a blob. The way a DATA blob is read depends on the TYPE of the colorop. We only create the DATA property for property types that need it. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v6: - take ref for DATA blob in duplicate_state func (Xaver Hugl) v5: - Add function signature for init (Sebastian) - Fix kernel-doc v4: - Create helper function for creating 3x4 CTM colorop - Fix CTM indexes in comment (Pekka) drivers/gpu/drm/drm_atomic.c | 14 ++- drivers/gpu/drm/drm_atomic_uapi.c | 29 + drivers/gpu/drm/drm_colorop.c | 42 +++ include/drm/drm_colorop.h | 21 include/uapi/drm/amdgpu_drm.h | 9 --- include/uapi/drm/drm_mode.h | 24 +- 6 files changed, 128 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 35136987d5e8..c58663327e6b 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -787,7 +787,19 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, drm_printf(p, "colorop[%u]:\n", colorop->base.id); drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type)); drm_printf(p, "\tbypass=%u\n", state->bypass); - drm_printf(p, "\tcurve_1d_type=%s\n", drm_get_colorop_curve_1d_type_name(state->curve_1d_type)); + + switch (colorop->type) { + case DRM_COLOROP_1D_CURVE: + drm_printf(p, "\tcurve_1d_type=%s\n", + drm_get_colorop_curve_1d_type_name(state->curve_1d_type)); + break; + case DRM_COLOROP_CTM_3X4: + drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); + break; + default: + break; + } + drm_printf(p, "\tnext=%d\n", colorop->next ? colorop->next->base.id : 0); } diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index f0c22abcc28f..7bc4978e5441 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -692,6 +692,30 @@ drm_atomic_plane_get_property(struct drm_plane *plane, return 0; } +static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, + struct drm_colorop_state *state, + struct drm_property *property, uint64_t val) +{ + ssize_t elem_size = -1; + ssize_t size = -1; + bool replaced = false; + + switch (colorop->type) { + case DRM_COLOROP_CTM_3X4: + size = sizeof(struct drm_color_ctm_3x4); + break; + default: + /* should never get here */ + return -EINVAL; + } + + return drm_property_replace_blob_from_id(colorop->dev, + &state->data, + val, + size, + elem_size, + &replaced); +} static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, struct drm_colorop_state *state, struct drm_file *file_priv, @@ -701,6 +725,9 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, state->bypass = val; } else if (property == colorop->curve_1d_type_property) { state->curve_1d_type = val; + } else if (property == colorop->data_property) { + return drm_atomic_color_set_data_property(colorop, + state, property, val); } else { drm_dbg_atomic(colorop->dev, "[COLOROP:%d:%d] unknown property [PROP:%d:%s]]\n", @@ -723,6 +750,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->bypass; } else if (property == colorop->curve_1d_type_property) { *val = state->curve_1d_type; + } else if (property == colorop->data_property) { + *val = (state->data) ? state->data->base.id : 0; } else { return -EINVAL; } diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/d
[V7 20/45] drm/vkms: Add tests for CTM handling
From: Harry Wentland A whole slew of tests for CTM handling that greatly helped in debugging the CTM code. The extent of tests might seem a bit silly but they're fast and might someday help save someone else's day when debugging this. Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix a checkpatch a long-line warning (Louis Chauvet) v6: - update reference values since we're now rounding v5: - Make apply_3x4_matrix static v4: - Comment on origin of bt709_enc matrix (Pekka) - Use full opaque alpha (Pekka) - Add additional check for Y < 0x (Pekka) - Remove unused code (Pekka) drivers/gpu/drm/vkms/tests/vkms_color_test.c | 250 +++ drivers/gpu/drm/vkms/vkms_composer.c | 4 +- drivers/gpu/drm/vkms/vkms_composer.h | 1 + 3 files changed, 254 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c index d765c5eb5d88..e9bc4380fb88 100644 --- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c +++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c @@ -190,11 +190,261 @@ static void vkms_color_srgb_inv_srgb(struct kunit *test) } } +#define FIXPT_HALF(DRM_FIXED_ONE >> 1) +#define FIXPT_QUARTER (DRM_FIXED_ONE >> 2) + +static const struct drm_color_ctm_3x4 test_matrix_3x4_50_desat = { { + FIXPT_HALF, FIXPT_QUARTER, FIXPT_QUARTER, 0, + FIXPT_QUARTER, FIXPT_HALF, FIXPT_QUARTER, 0, + FIXPT_QUARTER, FIXPT_QUARTER, FIXPT_HALF, 0 +} }; + +static void vkms_color_ctm_3x4_50_desat(struct kunit *test) +{ + struct pixel_argb_s32 ref, out; + + /* full white */ + ref.a = 0x; + ref.r = 0x; + ref.g = 0x; + ref.b = 0x; + + memcpy(&out, &ref, sizeof(out)); + apply_3x4_matrix(&out, &test_matrix_3x4_50_desat); + + KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out)); + + /* full black */ + ref.a = 0x; + ref.r = 0x0; + ref.g = 0x0; + ref.b = 0x0; + + memcpy(&out, &ref, sizeof(out)); + apply_3x4_matrix(&out, &test_matrix_3x4_50_desat); + + KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out)); + + /* 50% grey */ + ref.a = 0x; + ref.r = 0x8000; + ref.g = 0x8000; + ref.b = 0x8000; + + memcpy(&out, &ref, sizeof(out)); + apply_3x4_matrix(&out, &test_matrix_3x4_50_desat); + + KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out)); + + /* full red to 50% desat */ + ref.a = 0x; + ref.r = 0x8000; + ref.g = 0x4000; + ref.b = 0x4000; + + out.a = 0x; + out.r = 0x; + out.g = 0x0; + out.b = 0x0; + + apply_3x4_matrix(&out, &test_matrix_3x4_50_desat); + + KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out)); +} + +/* + * BT.709 encoding matrix + * + * Values printed from within IGT when converting + * igt_matrix_3x4_bt709_enc to the fixed-point format expected + * by DRM/KMS. + */ +static const struct drm_color_ctm_3x4 test_matrix_3x4_bt709_enc = { { + 0x366cf400ull, 0xb7175900ull, 0x000127bb300ull, 0, + 0x80001993b3a0ull, 0x80005609fe80ull, 0x6f9db200ull, 0, + 0x9d70a400ull, 0x80008f011100ull, 0x8e6f9330ull, 0 +} }; + +static void vkms_color_ctm_3x4_bt709(struct kunit *test) +{ + struct pixel_argb_s32 out; + + /* full white to bt709 */ + out.a = 0x; + out.r = 0x; + out.g = 0x; + out.b = 0x; + + apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc); + + /* Y 255 */ + KUNIT_EXPECT_GT(test, out.r, 0xfe00); + KUNIT_EXPECT_LT(test, out.r, 0x1); + + /* U 0 */ + KUNIT_EXPECT_LT(test, out.g, 0x0100); + + /* V 0 */ + KUNIT_EXPECT_LT(test, out.b, 0x0100); + + /* full black to bt709 */ + out.a = 0x; + out.r = 0x0; + out.g = 0x0; + out.b = 0x0; + + apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc); + + /* Y 0 */ + KUNIT_EXPECT_LT(test, out.r, 0x100); + + /* U 0 */ + KUNIT_EXPECT_LT(test, out.g, 0x0100); + + /* V 0 */ + KUNIT_EXPECT_LT(test, out.b, 0x0100); + + /* gray to bt709 */ + out.a = 0x; + out.r = 0x7fff; + out.g = 0x7fff; + out.b = 0x7fff; + + apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc); + + /* Y 127 */ + KUNIT_EXPECT_GT(test, out.r, 0x7e00); + KUNIT_EXPECT_LT(test, out.r, 0x8000); + + /* U 0 */ + KUNIT_EXPECT_LT(test, out.g, 0x0100); + + /* V 0 */ + KUNIT_EXPECT_LT(test, out.b, 0x0100); + + /* == red 255 - bt709 enc == */ + out.a = 0x; + out.r = 0x; + out.g = 0x0; + out.b = 0x0; + + apply_3x4_ma
[V7 18/45] drm/vkms: add 3x4 matrix in color pipeline
From: Harry Wentland We add two 3x4 matrices into the VKMS color pipeline. The reason we're adding matrices is so that we can test that application of a matrix and its inverse yields an output equal to the input image. One complication with the matrix implementation has to do with the fact that the matrix entries are in signed-magnitude fixed point, whereas the drm_fixed.h implementation uses 2s-complement. The latter one is the one that we want for easy addition and subtraction, so we convert all entries to 2s-complement. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix checkpatch warnings - Change kzalloc(sizeof(struct drm_colorop) ...) to kzalloc(sizeof(*ops[i]) ...) - Change i-1to i - 1 - Add a new line at EOF v6: - pre-compute colors (Louis Chauvet) - round matrix output (Louis Chauvet) drivers/gpu/drm/vkms/vkms_colorop.c | 34 +++- drivers/gpu/drm/vkms/vkms_composer.c | 33 +++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c index af319cd3de23..b407f8ae758d 100644 --- a/drivers/gpu/drm/vkms/vkms_colorop.c +++ b/drivers/gpu/drm/vkms/vkms_colorop.c @@ -12,7 +12,7 @@ static const u64 supported_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF); -#define MAX_COLOR_PIPELINE_OPS 2 +#define MAX_COLOR_PIPELINE_OPS 4 static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list) { @@ -48,6 +48,38 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i - 1], ops[i]); + + i++; + + /* 3rd op: 3x4 matrix */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + DRM_ERROR("KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i - 1], ops[i]); + + i++; + + /* 4th op: 1d curve */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + DRM_ERROR("KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs); if (ret) goto cleanup; diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index b009e607a310..cdcaaf8cdb68 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -159,6 +159,35 @@ static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buff } } +static void apply_3x4_matrix(struct pixel_argb_s32 *pixel, const struct drm_color_ctm_3x4 *matrix) +{ + s64 rf, gf, bf; + s64 r, g, b; + + r = drm_int2fixp(pixel->r); + g = drm_int2fixp(pixel->g); + b = drm_int2fixp(pixel->b); + + rf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[0]), r) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[1]), g) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[2]), b) + +drm_sm2fixp(matrix->matrix[3]); + + gf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[4]), r) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[5]), g) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[6]), b) + +drm_sm2fixp(matrix->matrix[7]); + + bf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[8]), r) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[9]), g) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[10]), b) + +drm_sm2fixp(matrix->matrix[11]); + + pixel->r = drm_fixp2int_round(rf); + pixel->g = drm_fixp2int_round(gf); + pixel->b = drm_fixp2int_round(bf); +} + static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colorop) { struct drm_colorop_state *colorop_state = colorop->state; @@ -180,6 +209,10 @@ static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colo colorop_state->curve_1d_type); break; } + } else if (colorop->type == DRM_COLOROP_CTM_3X4) { + if (colorop_state->data) + apply_3x4_matrix(pixel, +(struct drm_color_ctm_3x4 *) colorop_state->data->data); } } -- 2.43.0
[V7 19/45] drm/tests: Add a few tests around drm_fixed.h
From: Harry Wentland While working on the CTM implementation of VKMS I had to ascertain myself of a few assumptions. One of those is whether drm_fixed.h treats its numbers using signed-magnitude or twos-complement. It is twos-complement. In order to make someone else's day easier I am adding the drm_test_int2fixp test that validates the above assumption. I am also adding a test for the new sm2fixp function that converts from a signed-magnitude fixed point to the twos-complement fixed point. Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix checkpatch warnings (Louis Chauvet) v6: - add missing MODULE_DESCRIPTION (Jeff Johnson) - fix buffer overflow drivers/gpu/drm/tests/Makefile| 3 +- drivers/gpu/drm/tests/drm_fixp_test.c | 71 +++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/tests/drm_fixp_test.c diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 56dab563abd7..bd69df0eee33 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_modes_test.o \ drm_plane_helper_test.o \ drm_probe_helper_test.o \ - drm_rect_test.o + drm_rect_test.o \ + drm_fixp_test.o CFLAGS_drm_mm_test.o := $(DISABLE_STRUCTLEAK_PLUGIN) diff --git a/drivers/gpu/drm/tests/drm_fixp_test.c b/drivers/gpu/drm/tests/drm_fixp_test.c new file mode 100644 index ..de91177af213 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_fixp_test.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2022 Advanced Micro Devices, Inc. + */ + +#include +#include + +static void drm_test_sm2fixp(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, 0x7fffll, ((1ull << 63) - 1)); + + /* 1 */ + KUNIT_EXPECT_EQ(test, drm_int2fixp(1), drm_sm2fixp(1ull << DRM_FIXED_POINT)); + + /* -1 */ + KUNIT_EXPECT_EQ(test, drm_int2fixp(-1), + drm_sm2fixp((1ull << 63) | (1ull << DRM_FIXED_POINT))); + + /* 0.5 */ + KUNIT_EXPECT_EQ(test, drm_fixp_from_fraction(1, 2), + drm_sm2fixp(1ull << (DRM_FIXED_POINT - 1))); + + /* -0.5 */ + KUNIT_EXPECT_EQ(test, drm_fixp_from_fraction(-1, 2), + drm_sm2fixp((1ull << 63) | (1ull << (DRM_FIXED_POINT - 1; +} + +static void drm_test_int2fixp(struct kunit *test) +{ + /* 1 */ + KUNIT_EXPECT_EQ(test, 1ll << 32, drm_int2fixp(1)); + + /* -1 */ + KUNIT_EXPECT_EQ(test, -(1ll << 32), drm_int2fixp(-1)); + + /* 1 + (-1) = 0 */ + KUNIT_EXPECT_EQ(test, 0, drm_int2fixp(1) + drm_int2fixp(-1)); + + /* 1 / 2 */ + KUNIT_EXPECT_EQ(test, 1ll << 31, drm_fixp_from_fraction(1, 2)); + + /* -0.5 */ + KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(-1, 2)); + + /* (1 / 2) + (-1) = 0.5 */ + KUNIT_EXPECT_EQ(test, 1ll << 31, drm_fixp_from_fraction(-1, 2) + drm_int2fixp(1)); + + /* (1 / 2) - 1) = 0.5 */ + KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(1, 2) + drm_int2fixp(-1)); + + /* (1 / 2) - 1) = 0.5 */ + KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(1, 2) - drm_int2fixp(1)); +} + +static struct kunit_case drm_fixp_tests[] = { + KUNIT_CASE(drm_test_int2fixp), + KUNIT_CASE(drm_test_sm2fixp), + { } +}; + +static struct kunit_suite drm_rect_test_suite = { + .name = "drm_fixp", + .test_cases = drm_fixp_tests, +}; + +kunit_test_suite(drm_rect_test_suite); + +MODULE_AUTHOR("AMD"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_DESCRIPTION("Unit tests for drm_fixed.h"); -- 2.43.0
[V7 17/45] drm/vkms: Use s32 for internal color pipeline precision
From: Harry Wentland Certain operations require us to preserve values below 0.0 and above 1.0 (0x0 and 0x respectively in 16 bpc unorm). One such operation is a BT709 encoding operation followed by its decoding operation, or the reverse. We'll use s32 values as intermediate in and outputs of our color operations, for the operations where it matters. For now this won't apply to LUT operations. We might want to update those to work on s32 as well, but it's unclear how that should work for unorm LUT definitions. We'll revisit that once we add LUT + CTM tests. In order to allow for this we'll also invert the nesting of our colorop processing loops. We now use the pixel iteration loop on the outside and the colorop iteration on the inside. Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix checkpatch warnings - Add a commit messages - Fix code styles by adding and removing spaces (new lines, tabs and so on) v6: - use clamp_val instead of manual clamping (Louis Chauvet) v4: - Clarify that we're packing 16-bit UNORM into s32, not converting values to a different representation (Pekka) v3: - Use new colorop->next pointer drivers/gpu/drm/vkms/vkms_composer.c | 27 +-- drivers/gpu/drm/vkms/vkms_drv.h | 4 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index ee3cfe153d8f..b009e607a310 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -159,7 +159,7 @@ static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buff } } -static void apply_colorop(struct pixel_argb_u16 *pixel, struct drm_colorop *colorop) +static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colorop) { struct drm_colorop_state *colorop_state = colorop->state; @@ -186,9 +186,26 @@ static void apply_colorop(struct pixel_argb_u16 *pixel, struct drm_colorop *colo static void pre_blend_color_transform(const struct vkms_plane_state *plane_state, struct line_buffer *output_buffer) { + struct pixel_argb_s32 pixel; + for (size_t x = 0; x < output_buffer->n_pixels; x++) { struct drm_colorop *colorop = plane_state->base.base.color_pipeline; + /* +* Some operations, such as applying a BT709 encoding matrix, +* followed by a decoding matrix, require that we preserve +* values above 1.0 and below 0.0 until the end of the pipeline. +* +* Pack the 16-bit UNORM values into s32 to give us head-room to +* avoid clipping until we're at the end of the pipeline. Clip +* intentionally at the end of the pipeline before packing +* UNORM values back into u16. +*/ + pixel.a = output_buffer->pixels[x].a; + pixel.r = output_buffer->pixels[x].r; + pixel.g = output_buffer->pixels[x].g; + pixel.b = output_buffer->pixels[x].b; + while (colorop) { struct drm_colorop_state *colorop_state; @@ -198,10 +215,16 @@ static void pre_blend_color_transform(const struct vkms_plane_state *plane_state return; if (!colorop_state->bypass) - apply_colorop(&output_buffer->pixels[x], colorop); + apply_colorop(&pixel, colorop); colorop = colorop->next; } + + /* clamp values */ + output_buffer->pixels[x].a = clamp_val(pixel.a, 0, 0x); + output_buffer->pixels[x].r = clamp_val(pixel.r, 0, 0x); + output_buffer->pixels[x].g = clamp_val(pixel.g, 0, 0x); + output_buffer->pixels[x].b = clamp_val(pixel.b, 0, 0x); } } diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index c3c8f5ac81b9..5e24c02d5294 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -36,6 +36,10 @@ struct vkms_frame_info { unsigned int cpp; }; +struct pixel_argb_s32 { + s32 a, r, g, b; +}; + struct pixel_argb_u16 { u16 a, r, g, b; }; -- 2.43.0
[V7 23/45] drm/amd/display: Ignore deprecated props when plane_color_pipeline set
From: Harry Wentland When the plane_color_pipeline bit is set we should ignore deprecated properties, such as COLOR_RANGE and COLOR_ENCODING. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 4 1 file changed, 4 insertions(+) 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 6e79028c5d78..575eacfc12e6 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -5420,6 +5420,10 @@ fill_plane_color_attributes(const struct drm_plane_state *plane_state, *color_space = COLOR_SPACE_SRGB; + /* Ignore properties when DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set */ + if (plane_state->state && plane_state->state->plane_color_pipeline) + return 0; + /* DRM color properties only affect non-RGB formats. */ if (format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) return 0; -- 2.43.0
[V7 00/45] Color Pipeline API w/ VKMS
ount issues (Xaver Hugl) v5: - amdgpu 3D LUT - Don't require BYPASS - update RFC docs and add to TOC tree - add drm_colorop and COLOR_PIPELINE kernel docs (non-RFC) - add amdgpu color pipeline doc - define SIZE property similar to drm_crtc's GAMMA_SIZE - various minor fixes and cleanups v4: - Add amdgpu color pipeline (WIP) - Don't block setting of deprecated properties, instead pass client cap to atomic check so drivers can ignore these props - Drop IOCTL definitions (Pekka) - Use enum property for colorop TYPE (Pekka) - A few cleanups to the docs (Pekka) - Rework the TYPE enum to name relation to avoid code duplication (Pekka) - Add missing function declarations (Chaitanya Kumar Borah) - Allow setting of NEXT property to NULL in _set_ function (Chaitanya Kumar Borah) - Add helper for creation of pipeline drm_plane property (Pekka) - Always create Bypass pipeline (Pekka) - A bunch of changes to VKMS kunit tests (Pekka) - Fix index in CTM doc (Pekka) v3: - Abandon IOCTLs and discover colorops as clients iterate the pipeline - Remove need for libdrm - Add color_pipeline client cap and make mutually exclusive with COLOR_RANGE and COLOR_ENCODING properties - add CTM colorop to VKMS - Use include way for kunit testing static functions (Arthur) - Make TYPE a range property - Move enum drm_colorop_type to uapi header - and a bunch of smaller bits that are highlighted in the relevant commit description v2: - Rebased on drm-misc-next - Introduce a VKMS Kunit so we can test LUT functionality in vkms_composer - Incorporate feedback in color_pipeline.rst doc - Add support for sRGB inverse EOTF - Add 2nd enumerated TF colorop to VKMS - Fix LUTs and some issues with applying LUTs in VKMS Cc: Ville Syrjala Cc: Pekka Paalanen Cc: Simon Ser Cc: Harry Wentland Cc: Melissa Wen Cc: Jonas Ådahl Cc: Sebastian Wick Cc: Shashank Sharma Cc: Alexander Goins Cc: Joshua Ashton Cc: Michel Dänzer Cc: Aleix Pol Cc: Xaver Hugl Cc: Victoria Brekenfeld Cc: Sima Cc: Uma Shankar Cc: Naseer Ahmed Cc: Christopher Braga Cc: Abhinav Kumar Cc: Arthur Grillo Cc: Hector Martin Cc: Liviu Dudau Cc: Sasha McIntosh Cc: Chaitanya Kumar Borah Alex Hung (15): drm/colorop: define a new macro for_each_new_colorop_in_state drm/amd/display: Skip color pipeline initialization for cursor plane drm/amd/display: Add support for sRGB EOTF in DEGAM block drm/amd/display: Add support for sRGB Inverse EOTF in SHAPER block drm/amd/display: Add support for sRGB EOTF in BLND block drm/colorop: Add 1D Curve Custom LUT type drm/amd/display: add shaper and blend colorops for 1D Curve Custom LUT drm/amd/display: add 3x4 matrix colorop drm/colorop: Add mutliplier type drm/amd/display: add multiplier colorop drm/amd/display: Swap matrix and multiplier drm/colorop: Add 3D LUT supports to color pipeline drm/amd/display: add 3D LUT colorop drm/colorop: Add kernel doc for data blob drm/colorop: Add destroy functions for color pipeline Harry Wentland (30): drm: Add helper for conversion from signed-magnitude drm/vkms: Round fixp2int conversion in lerp_u16 drm/vkms: Add kunit tests for VKMS LUT handling drm/doc/rfc: Describe why prescriptive color pipeline is needed drm/colorop: Introduce new drm_colorop mode object drm/colorop: Add TYPE property drm/colorop: Add 1D Curve subtype Documentation/gpu: document drm_colorop drm/colorop: Add BYPASS property drm/colorop: Add NEXT property drm/colorop: Add atomic state print for drm_colorop drm/plane: Add COLOR PIPELINE property drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE drm/vkms: Add enumerated 1D curve colorop drm/vkms: Add kunit tests for linear and sRGB LUTs drm/colorop: Add 3x4 CTM type drm/vkms: Use s32 for internal color pipeline precision drm/vkms: add 3x4 matrix in color pipeline drm/tests: Add a few tests around drm_fixed.h drm/vkms: Add tests for CTM handling drm/colorop: pass plane_color_pipeline client cap to atomic check drm/amd/display: Ignore deprecated props when plane_color_pipeline set drm/amd/display: Add bypass COLOR PIPELINE drm/colorop: Add PQ 125 EOTF and its inverse drm/amd/display: Enable support for PQ 125 EOTF and Inverse drm/colorop: add BT2020/BT709 OETF and Inverse OETF drm/amd/display: Add support for BT.709 and BT.2020 TFs drm/colorop: Define LUT_1D interpolation drm/colorop: allow non-bypass colorops drm/amd/display: Add AMD color pipeline doc Documentation/gpu/drm-kms.rst | 15 + Documentation/gpu/rfc/color_pipeline.rst | 378 + Documentation/gpu/rfc/index.rst | 3 + drivers/gpu/drm/Makefile | 1 + .../gpu/drm/amd/display/amdgpu_dm/Makefile| 3 +- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 4 + .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 566 - .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 206 + .../am
[V7 02/45] drm/vkms: Round fixp2int conversion in lerp_u16
From: Harry Wentland fixp2int always rounds down, fixp2int_ceil rounds up. We need the new fixp2int_round. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland Reviewed-by: Louis Chauvet --- drivers/gpu/drm/vkms/vkms_composer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index e7441b227b3c..3d6785d081f2 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -98,7 +98,7 @@ static u16 lerp_u16(u16 a, u16 b, s64 t) s64 delta = drm_fixp_mul(b_fp - a_fp, t); - return drm_fixp2int(a_fp + delta); + return drm_fixp2int_round(a_fp + delta); } static s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value) -- 2.43.0
[V7 01/45] drm: Add helper for conversion from signed-magnitude
From: Harry Wentland CTM values are defined as signed-magnitude values. Add a helper that converts from CTM signed-magnitude fixed point value to the twos-complement value used by drm_fixed. Signed-off-by: Harry Wentland --- include/drm/drm_fixed.h | 18 ++ 1 file changed, 18 insertions(+) diff --git a/include/drm/drm_fixed.h b/include/drm/drm_fixed.h index 1922188f00e8..0b44f2f294ce 100644 --- a/include/drm/drm_fixed.h +++ b/include/drm/drm_fixed.h @@ -78,6 +78,24 @@ static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B) #define DRM_FIXED_EPSILON 1LL #define DRM_FIXED_ALMOST_ONE (DRM_FIXED_ONE - DRM_FIXED_EPSILON) +/** + * @drm_sm2fixp + * + * Convert a 1.31.32 signed-magnitude fixed point to 32.32 + * 2s-complement fixed point + * + * @return s64 2s-complement fixed point + */ +static inline s64 drm_sm2fixp(__u64 a) +{ + if ((a & (1LL << 63))) { + return -(a & 0x7fffll); + } else { + return a; + } + +} + static inline s64 drm_int2fixp(int a) { return ((s64)a) << DRM_FIXED_POINT; -- 2.43.0
[V7 04/45] drm/doc/rfc: Describe why prescriptive color pipeline is needed
From: Harry Wentland Add documentation for color pipeline API. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Add a commit messages v5: - Don't require BYPASS to succeed (Sebastian) - use DATA for 1D and 3D LUT types (Sebastian) - update 3DLUT ops to use 3DLUT_MODES and 3DLUT_MODE_INDEX - Add section on drm_colorop extensibility - Add color_pipeline.rst to RFC toc tree v4: - Drop IOCTL docs since we dropped the IOCTLs (Pekka) - Clarify reading and setting of COLOR_PIPELINE prop (Pekka) - Add blurb about not requiring to reject a pipeline due to incompatible ops, as long as op can be bypassed (Pekka) - Dropped informational strings (such as input CSC) as they're not actually intended to be advertised (Pekka) v3: - Describe DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE (Sebastian) - Ask for clear documentation of colorop behavior (Sebastian) v2: - Update colorop visualizations to match reality (Sebastian, Alex Hung) - Updated wording (Pekka) - Change BYPASS wording to make it non-mandatory (Sebastian) - Drop cover-letter-like paragraph from COLOR_PIPELINE Plane Property section (Pekka) - Use PQ EOTF instead of its inverse in Pipeline Programming example (Melissa) - Add "Driver Implementer's Guide" section (Pekka) - Add "Driver Forward/Backward Compatibility" section (Sebastian, Pekka) Documentation/gpu/rfc/color_pipeline.rst | 378 +++ Documentation/gpu/rfc/index.rst | 3 + 2 files changed, 381 insertions(+) create mode 100644 Documentation/gpu/rfc/color_pipeline.rst diff --git a/Documentation/gpu/rfc/color_pipeline.rst b/Documentation/gpu/rfc/color_pipeline.rst new file mode 100644 index ..0470c4bc1148 --- /dev/null +++ b/Documentation/gpu/rfc/color_pipeline.rst @@ -0,0 +1,378 @@ +.. SPDX-License-Identifier: GPL-2.0 + + +Linux Color Pipeline API + + +What problem are we solving? + + +We would like to support pre-, and post-blending complex color +transformations in display controller hardware in order to allow for +HW-supported HDR use-cases, as well as to provide support to +color-managed applications, such as video or image editors. + +It is possible to support an HDR output on HW supporting the Colorspace +and HDR Metadata drm_connector properties, but that requires the +compositor or application to render and compose the content into one +final buffer intended for display. Doing so is costly. + +Most modern display HW offers various 1D LUTs, 3D LUTs, matrices, and other +operations to support color transformations. These operations are often +implemented in fixed-function HW and therefore much more power efficient than +performing similar operations via shaders or CPU. + +We would like to make use of this HW functionality to support complex color +transformations with no, or minimal CPU or shader load. + + +How are other OSes solving this problem? + + +The most widely supported use-cases regard HDR content, whether video or +gaming. + +Most OSes will specify the source content format (color gamut, encoding transfer +function, and other metadata, such as max and average light levels) to a driver. +Drivers will then program their fixed-function HW accordingly to map from a +source content buffer's space to a display's space. + +When fixed-function HW is not available the compositor will assemble a shader to +ask the GPU to perform the transformation from the source content format to the +display's format. + +A compositor's mapping function and a driver's mapping function are usually +entirely separate concepts. On OSes where a HW vendor has no insight into +closed-source compositor code such a vendor will tune their color management +code to visually match the compositor's. On other OSes, where both mapping +functions are open to an implementer they will ensure both mappings match. + +This results in mapping algorithm lock-in, meaning that no-one alone can +experiment with or introduce new mapping algorithms and achieve +consistent results regardless of which implementation path is taken. + +Why is Linux different? +=== + +Unlike other OSes, where there is one compositor for one or more drivers, on +Linux we have a many-to-many relationship. Many compositors; many drivers. +In addition each compositor vendor or community has their own view of how +color management should be done. This is what makes Linux so beautiful. + +This means that a HW vendor can now no longer tune their driver to one +compositor, as tuning it to one could make it look fairly different from +another compositor's color mapping. + +We need a better solution. + + +Descriptive API +=== + +An API that describes the source and destination colorspaces is a descriptive +API. It describes the input and output color spaces but
[V7 06/45] drm/colorop: Add TYPE property
From: Harry Wentland Add a read-only TYPE property. The TYPE specifies the colorop type, such as enumerated curve, 1D LUT, CTM, 3D LUT, PWL LUT, etc. For now we're only introducing an enumerated 1D LUT type to illustrate the concept. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v6: - fix doc typo (Alex Hung) v5: - Add drm_get_colorop_type_name in header - Add kernel docs v4: - Use enum property for TYPE (Pekka) v3: - Make TYPE a range property - Move enum drm_colorop_type to uapi header - Fix drm_get_colorop_type_name description drivers/gpu/drm/drm_atomic.c | 4 ++-- drivers/gpu/drm/drm_atomic_uapi.c | 8 +++- drivers/gpu/drm/drm_colorop.c | 12 include/drm/drm_colorop.h | 23 +++ include/uapi/drm/drm_mode.h | 17 + 5 files changed, 61 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 327d906c48c5..44c5b5dd8b33 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -631,8 +631,8 @@ drm_atomic_get_colorop_state(struct drm_atomic_state *state, state->colorops[index].new_state = colorop_state; colorop_state->state = state; - drm_dbg_atomic(colorop->dev, "Added [COLOROP:%d] %p state to %p\n", - colorop->base.id, colorop_state, state); + drm_dbg_atomic(colorop->dev, "Added [COLOROP:%d:%d] %p state to %p\n", + colorop->base.id, colorop->type, colorop_state, state); return colorop_state; } diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index cfc1485b592e..59fc25b59100 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -660,7 +660,13 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, const struct drm_colorop_state *state, struct drm_property *property, uint64_t *val) { - return -EINVAL; + if (property == colorop->type_property) { + *val = colorop->type; + } else { + return -EINVAL; + } + + return 0; } static int drm_atomic_set_writeback_fb_for_connector( diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index d215e22c9d20..1459a28c7e7b 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -102,3 +102,15 @@ void drm_colorop_reset(struct drm_colorop *colorop) if (colorop->state) __drm_colorop_reset(colorop, colorop->state); } + +static const char * const colorop_type_name[] = { + [DRM_COLOROP_1D_CURVE] = "1D Curve", +}; + +const char *drm_get_colorop_type_name(enum drm_colorop_type type) +{ + if (WARN_ON(type >= ARRAY_SIZE(colorop_type_name))) + return "unknown"; + + return colorop_type_name[type]; +} diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index cc3e94dcf085..579e72e0b6b5 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -107,6 +107,21 @@ struct drm_colorop { /** @properties: property tracking for this colorop */ struct drm_object_properties properties; + /** +* @type: +* +* Read-only +* Type of color operation +*/ + enum drm_colorop_type type; + + /** +* @type_property: +* +* Read-only "TYPE" property for specifying the type of +* this color operation. The type is enum drm_colorop_type. +*/ + struct drm_property *type_property; }; #define obj_to_colorop(x) container_of(x, struct drm_colorop, base) @@ -162,5 +177,13 @@ static inline unsigned int drm_colorop_index(const struct drm_colorop *colorop) #define drm_for_each_colorop(colorop, dev) \ list_for_each_entry(colorop, &(dev)->mode_config.colorop_list, head) +/** + * drm_get_colorop_type_name - return a string for colorop type + * @type: colorop type to compute name of + * + * In contrast to the other drm_get_*_name functions this one here returns a + * const pointer and hence is threadsafe. + */ +const char *drm_get_colorop_type_name(enum drm_colorop_type type); #endif /* __DRM_COLOROP_H__ */ diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index d49cded6c568..53985d2b7eea 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -858,6 +858,23 @@ struct drm_color_lut { __u16 reserved; }; +/** + * enum drm_colorop_type - Type of color operation + * + * drm_colorops can be of many different types. Each type behaves differently + * and defines a different set of properties. This enum defines all types and + * gives a high-level description. + */ +enum drm_colorop_type { + /** +* @DRM_COLOROP_1D_CURVE: +* +* A 1D curve that is be
[V7 05/45] drm/colorop: Introduce new drm_colorop mode object
From: Harry Wentland This patches introduces a new drm_colorop mode object. This object represents color transformations and can be used to define color pipelines. We also introduce the drm_colorop_state here, as well as various helpers and state tracking bits. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix checkpatch warnings and errors - Add a tab to for_each_oldnew_colorop_in_state definition - Change unsigned index to unsigned int index - Fix a checkpatch warning - a new line after variable declaration v6: - Comment that properties validity depends on type (Louis Chauvet) v5: - Add comment to drm_atomic_state.colorops - Replace a misplaced 'plane' with 'colorop' in comment - Fix colorop_list kernel doc - Add kernel doc for color_pipeline - drop unused drm_colorop_destroy_state - drop drm_colorop_init, to be introduced in later patch when used - Add kernel docs - Drop TODOs v4: - Drop IOCTL definitions (Pekka) - add missing declaration (Chaitanya Kumar Borah) v3: - Drop TODO for lock (it's handled in drm_modeset_drop_locks) (Melissa) - Don't get plane state when getting colorop state - Make some functions static (kernel test robot) drivers/gpu/drm/Makefile| 1 + drivers/gpu/drm/drm_atomic.c| 70 drivers/gpu/drm/drm_atomic_helper.c | 12 ++ drivers/gpu/drm/drm_atomic_uapi.c | 48 drivers/gpu/drm/drm_colorop.c | 104 + drivers/gpu/drm/drm_mode_config.c | 7 ++ include/drm/drm_atomic.h| 89 +++ include/drm/drm_atomic_uapi.h | 1 + include/drm/drm_colorop.h | 166 include/drm/drm_mode_config.h | 18 +++ include/drm/drm_plane.h | 8 ++ include/uapi/drm/drm_mode.h | 1 + 12 files changed, 525 insertions(+) create mode 100644 drivers/gpu/drm/drm_colorop.c create mode 100644 include/drm/drm_colorop.h diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 784229d4504d..055f3e535d15 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -44,6 +44,7 @@ drm-y := \ drm_client.o \ drm_client_modeset.o \ drm_color_mgmt.o \ + drm_colorop.o \ drm_connector.o \ drm_crtc.o \ drm_displayid.o \ diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 0fc99da93afe..327d906c48c5 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -107,6 +108,7 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) kfree(state->connectors); kfree(state->crtcs); kfree(state->planes); + kfree(state->colorops); kfree(state->private_objs); } EXPORT_SYMBOL(drm_atomic_state_default_release); @@ -138,6 +140,10 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) sizeof(*state->planes), GFP_KERNEL); if (!state->planes) goto fail; + state->colorops = kcalloc(dev->mode_config.num_colorop, + sizeof(*state->colorops), GFP_KERNEL); + if (!state->colorops) + goto fail; /* * Because drm_atomic_state can be committed asynchronously we need our @@ -249,6 +255,20 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) state->planes[i].new_state = NULL; } + for (i = 0; i < config->num_colorop; i++) { + struct drm_colorop *colorop = state->colorops[i].ptr; + + if (!colorop) + continue; + + drm_colorop_atomic_destroy_state(colorop, +state->colorops[i].state); + state->colorops[i].ptr = NULL; + state->colorops[i].state = NULL; + state->colorops[i].old_state = NULL; + state->colorops[i].new_state = NULL; + } + for (i = 0; i < state->num_private_objs; i++) { struct drm_private_obj *obj = state->private_objs[i].ptr; @@ -568,6 +588,56 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_get_plane_state); + +/** + * drm_atomic_get_colorop_state - get colorop state + * @state: global atomic state object + * @colorop: colorop to get state object for + * + * This function returns the colorop state for the given colorop, allocating it + * if needed. It will also grab the relevant plane lock to make sure that the + * state is consistent. + * + * Returns: + * + * Either the allocated state or the error code encoded into the pointer. When + * the error is EDEADLK the
[V7 07/45] drm/colorop: Add 1D Curve subtype
From: Harry Wentland Add a new drm_colorop with DRM_COLOROP_1D_CURVE with two subtypes: DRM_COLOROP_1D_CURVE_SRGB_EOTF and DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF. Signed-off-by: Harry Wentland Co-developed-by: Alex Hung Signed-off-by: Alex Hung --- v5: - Add drm_get_colorop_curve_1d_type_name in header - Add drm_colorop_init - Set default curve - Add kernel docs v4: - Use drm_colorop_curve_1d_type_enum_list to get name (Pekka) - Create separate init function for 1D curve - Pass supported TFs into 1D curve init function drivers/gpu/drm/drm_atomic_uapi.c | 18 ++-- drivers/gpu/drm/drm_colorop.c | 134 ++ include/drm/drm_colorop.h | 60 + 3 files changed, 207 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 59fc25b59100..9a5dbf0a1306 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -648,11 +648,17 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, struct drm_colorop_state *state, struct drm_file *file_priv, struct drm_property *property, uint64_t val) { - drm_dbg_atomic(colorop->dev, - "[COLOROP:%d] unknown property [PROP:%d:%s]]\n", - colorop->base.id, - property->base.id, property->name); - return -EINVAL; + if (property == colorop->curve_1d_type_property) { + state->curve_1d_type = val; + } else { + drm_dbg_atomic(colorop->dev, + "[COLOROP:%d:%d] unknown property [PROP:%d:%s]]\n", + colorop->base.id, colorop->type, + property->base.id, property->name); + return -EINVAL; + } + + return 0; } static int @@ -662,6 +668,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, { if (property == colorop->type_property) { *val = colorop->type; + } else if (property == colorop->curve_1d_type_property) { + *val = state->curve_1d_type; } else { return -EINVAL; } diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 1459a28c7e7b..a42de0aa48e1 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -31,6 +31,123 @@ #include "drm_crtc_internal.h" +static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { + { DRM_COLOROP_1D_CURVE, "1D Curve" }, +}; + +static const char * const colorop_curve_1d_type_names[] = { + [DRM_COLOROP_1D_CURVE_SRGB_EOTF] = "sRGB EOTF", + [DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF] = "sRGB Inverse EOTF", +}; + + +/* Init Helpers */ + +static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, enum drm_colorop_type type) +{ + struct drm_mode_config *config = &dev->mode_config; + struct drm_property *prop; + int ret = 0; + + ret = drm_mode_object_add(dev, &colorop->base, DRM_MODE_OBJECT_COLOROP); + if (ret) + return ret; + + colorop->base.properties = &colorop->properties; + colorop->dev = dev; + colorop->type = type; + colorop->plane = plane; + + list_add_tail(&colorop->head, &config->colorop_list); + colorop->index = config->num_colorop++; + + /* add properties */ + + /* type */ + prop = drm_property_create_enum(dev, + DRM_MODE_PROP_IMMUTABLE, + "TYPE", drm_colorop_type_enum_list, + ARRAY_SIZE(drm_colorop_type_enum_list)); + + if (!prop) + return -ENOMEM; + + colorop->type_property = prop; + + drm_object_attach_property(&colorop->base, + colorop->type_property, + colorop->type); + + return ret; +} + +/** + * drm_colorop_curve_1d_init - Initialize a DRM_COLOROP_1D_CURVE + * + * @dev: DRM device + * @colorop: The drm_colorop object to initialize + * @plane: The associated drm_plane + * @supported_tfs: A bitfield of supported drm_colorop_curve_1d_init enum values, + * created using BIT(curve_type) and combined with the OR '|' + * operator. + * @return zero on success, -E value on failure + */ +int drm_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, u64 supported_tfs) +{ + struct drm_prop_enum_list enum_list[DRM_COLOROP_1D_CURVE_COUNT]; + int i, len; + +
[V7 08/45] Documentation/gpu: document drm_colorop
From: Harry Wentland Add kernel doc for drm_colorop objects. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Add a commit messages v5: - Drop TODO Documentation/gpu/drm-kms.rst | 15 +++ drivers/gpu/drm/drm_colorop.c | 31 +++ 2 files changed, 46 insertions(+) diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index abfe220764e1..2292e65f044c 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -413,6 +413,21 @@ Plane Panic Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_panic.c :export: +Colorop Abstraction +=== + +.. kernel-doc:: drivers/gpu/drm/drm_colorop.c + :doc: overview + +Colorop Functions Reference +--- + +.. kernel-doc:: include/drm/drm_colorop.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_colorop.c + :export: + Display Modes Function Reference diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index a42de0aa48e1..aaaf96d729b9 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -31,6 +31,37 @@ #include "drm_crtc_internal.h" +/** + * DOC: overview + * + * A colorop represents a single color operation. Colorops are chained + * via the NEXT property and make up color pipelines. Color pipelines + * are advertised and selected via the COLOR_PIPELINE &drm_plane + * property. + * + * A colorop will be of a certain type, advertised by the read-only TYPE + * property. Each type of colorop will advertise a different set of + * properties and is programmed in a different manner. Types can be + * enumerated 1D curves, 1D LUTs, 3D LUTs, matrices, etc. See the + * &drm_colorop_type documentation for information on each type. + * + * If a colorop advertises the BYPASS property it can be bypassed. + * + * Since colorops cannot stand-alone and are used to describe colorop + * operations on a plane they don't have their own locking mechanism but + * are locked and programmed along with their associated &drm_plane. + * + * Colorops are only advertised and valid for atomic drivers and atomic + * userspace that signals the DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE client + * cap. When a driver advertises the COLOR_PIPELINE property on a + * &drm_plane and userspace signals the + * DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE the driver shall ignore all other + * plane color properties, such as COLOR_ENCODING and COLOR_RANGE. + * + * More information about colorops and color pipelines can be found at + * rfc/color_pipeline.rst. + */ + static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_CURVE, "1D Curve" }, }; -- 2.43.0
[V7 09/45] drm/colorop: Add BYPASS property
From: Harry Wentland We want to be able to bypass each colorop at all times. Introduce a new BYPASS boolean property for this. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v6: - clarify that bypass is only valid if BYPASS prop exists (Louis Chauvet) drivers/gpu/drm/drm_atomic_uapi.c | 6 +- drivers/gpu/drm/drm_colorop.c | 15 +++ include/drm/drm_colorop.h | 22 ++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 9a5dbf0a1306..8a038e04be58 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -648,7 +648,9 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, struct drm_colorop_state *state, struct drm_file *file_priv, struct drm_property *property, uint64_t val) { - if (property == colorop->curve_1d_type_property) { + if (property == colorop->bypass_property) { + state->bypass = val; + } else if (property == colorop->curve_1d_type_property) { state->curve_1d_type = val; } else { drm_dbg_atomic(colorop->dev, @@ -668,6 +670,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, { if (property == colorop->type_property) { *val = colorop->type; + } else if (property == colorop->bypass_property) { + *val = state->bypass; } else if (property == colorop->curve_1d_type_property) { *val = state->curve_1d_type; } else { diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index aaaf96d729b9..01cbe90635e8 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -110,6 +110,17 @@ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, colorop->type_property, colorop->type); + /* bypass */ + prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC, + "BYPASS"); + if (!prop) + return -ENOMEM; + + colorop->bypass_property = prop; + drm_object_attach_property(&colorop->base, + colorop->bypass_property, + 1); + return ret; } @@ -167,6 +178,7 @@ int drm_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop *coloro /* initialize 1D curve only attribute */ prop = drm_property_create_enum(dev, DRM_MODE_PROP_ATOMIC, "CURVE_1D_TYPE", enum_list, len); + if (!prop) return -ENOMEM; @@ -183,6 +195,8 @@ static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colo struct drm_colorop_state *state) { memcpy(state, colorop->state, sizeof(*state)); + + state->bypass = true; } struct drm_colorop_state * @@ -221,6 +235,7 @@ static void __drm_colorop_state_reset(struct drm_colorop_state *colorop_state, u64 val; colorop_state->colorop = colorop; + colorop_state->bypass = true; if (colorop->curve_1d_type_property) { drm_object_property_get_default_value(&colorop->base, diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index cd5a754e5d10..5ed24d60a99e 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -79,6 +79,16 @@ struct drm_colorop_state { * information. */ + + /** +* @bypass: +* +* When the property BYPASS exists on this colorop, this stores +* the requested bypass state: true if colorop shall be bypassed, +* false if colorop is enabled. +*/ + bool bypass; + /** * @curve_1d_type: * @@ -163,6 +173,18 @@ struct drm_colorop { */ struct drm_property *type_property; + /** +* @bypass_property: +* +* Boolean property to control enablement of the color +* operation. Setting bypass to "true" shall always be supported +* in order to allow compositors to quickly fall back to +* alternate methods of color processing. This is important +* since setting color operations can fail due to unique +* HW constraints. +*/ + struct drm_property *bypass_property; + /** * @curve_1d_type_property: * -- 2.43.0
[V7 10/45] drm/colorop: Add NEXT property
From: Harry Wentland We'll construct color pipelines out of drm_colorop by chaining them via the NEXT pointer. NEXT will point to the next drm_colorop in the pipeline, or by 0 if we're at the end of the pipeline. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v5: - move next comment here from Add 3x4 CTM patch (Sebastian) - Add kernel doc v4: - Allow setting of NEXT property to NULL (Chaitanya Kumar Borah) v3: - Add next pointer to colorop to be used by drivers and in DRM core drivers/gpu/drm/drm_colorop.c | 30 ++ include/drm/drm_colorop.h | 20 2 files changed, 50 insertions(+) diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 01cbe90635e8..954acd09673a 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -89,6 +89,7 @@ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, colorop->dev = dev; colorop->type = type; colorop->plane = plane; + colorop->next = NULL; list_add_tail(&colorop->head, &config->colorop_list); colorop->index = config->num_colorop++; @@ -121,6 +122,16 @@ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, colorop->bypass_property, 1); + /* next */ + prop = drm_property_create_object(dev, DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_ATOMIC, + "NEXT", DRM_MODE_OBJECT_COLOROP); + if (!prop) + return -ENOMEM; + colorop->next_property = prop; + drm_object_attach_property(&colorop->base, + colorop->next_property, + 0); + return ret; } @@ -294,3 +305,22 @@ const char *drm_get_colorop_curve_1d_type_name(enum drm_colorop_curve_1d_type ty return colorop_curve_1d_type_names[type]; } + +/** + * drm_colorop_set_next_property - sets the next pointer + * @colorop: drm colorop + * @next: next colorop + * + * Should be used when constructing the color pipeline + */ +void drm_colorop_set_next_property(struct drm_colorop *colorop, struct drm_colorop *next) +{ + if (!colorop->next_property) + return; + + drm_object_property_set_value(&colorop->base, + colorop->next_property, + next ? next->base.id : 0); + colorop->next = next; +} +EXPORT_SYMBOL(drm_colorop_set_next_property); diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index 5ed24d60a99e..2f0572be37bb 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -165,6 +165,14 @@ struct drm_colorop { */ enum drm_colorop_type type; + /** +* @next: +* +* Read-only +* Pointer to next drm_colorop in pipeline +*/ + struct drm_colorop *next; + /** * @type_property: * @@ -192,10 +200,20 @@ struct drm_colorop { */ struct drm_property *curve_1d_type_property; + /** +* @next_property: +* +* Read-only property to next colorop in the pipeline +*/ + struct drm_property *next_property; + }; #define obj_to_colorop(x) container_of(x, struct drm_colorop, base) + + + /** * drm_colorop_find - look up a Colorop object from its ID * @dev: DRM device @@ -268,4 +286,6 @@ const char *drm_get_colorop_type_name(enum drm_colorop_type type); */ const char *drm_get_colorop_curve_1d_type_name(enum drm_colorop_curve_1d_type type); +void drm_colorop_set_next_property(struct drm_colorop *colorop, struct drm_colorop *next); + #endif /* __DRM_COLOROP_H__ */ -- 2.43.0
Re: [V7 33/45] drm/colorop: Add 1D Curve Custom LUT type
On 1/15/25 01:14, Simon Ser wrote: diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index a3e1fcad47ad..4744c12e429d 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -701,6 +701,9 @@ static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, bool replaced = false; switch (colorop->type) { + case DRM_COLOROP_1D_LUT: + size = colorop->lut_size * sizeof(struct drm_color_lut); Should we set the element size and the number of elements instead of multiplying? Or is that only useful when either of these are controlled by user-space to avoid integer overflows? This multiplication here is to calculate the total size for the data blob. The user-space communicates the lut_size (which is read-only) without multiplying sizeof(drm_color_lut) in drm_atomic_colorop_get_property, i.e., + } else if (property == colorop->lut_size_property) { + *val = colorop->lut_size; Is this what you meant? + break; case DRM_COLOROP_CTM_3X4: size = sizeof(struct drm_color_ctm_3x4); break; @@ -750,6 +753,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->bypass; } else if (property == colorop->curve_1d_type_property) { *val = state->curve_1d_type; + } else if (property == colorop->lut_size_property) { + *val = colorop->lut_size; } else if (property == colorop->data_property) { *val = (state->data) ? state->data->base.id : 0; } else { diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 665b23900cc0..e6dea2713490 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -64,6 +64,7 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_CURVE, "1D Curve" }, + { DRM_COLOROP_1D_LUT, "1D Curve Custom LUT" }, Since we now have both a "normal" 1D curve, and a "special" one… Would it make sense to change our minds regarding the naming of the former? For instance, we could rename it to DRM_COLOROP_FIXED_1D_CURVE. Or is the current name clear enough (and only the human-readable name can be switched to "1D Fixed Curve")? How about keeping "1D Curve" and simplifying "1D Curve Custom LUT" to "1D LUT" such as the following? { DRM_COLOROP_1D_CURVE, "1D Curve" }, + { DRM_COLOROP_1D_LUT, "1D LUT" },
[PATCH V8 09/43] drm/colorop: Add atomic state print for drm_colorop
From: Harry Wentland Print atomic state for drm_colorop in debugfs Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Add switch statement to print colorop type (Simon Ser) v7: - Add a commit messages - Squash "drm/colorop: Add NEXT to colorop state print" (Simon Ser) drivers/gpu/drm/drm_atomic.c | 35 ++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 12c313d43e56..97a30d6a68a1 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -777,6 +777,29 @@ static int drm_atomic_plane_check(const struct drm_plane_state *old_plane_state, return 0; } + + +static void drm_atomic_colorop_print_state(struct drm_printer *p, + const struct drm_colorop_state *state) +{ + struct drm_colorop *colorop = state->colorop; + + drm_printf(p, "colorop[%u]:\n", colorop->base.id); + drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type)); + drm_printf(p, "\tbypass=%u\n", state->bypass); + + switch (colorop->type) { + case DRM_COLOROP_1D_CURVE: + drm_printf(p, "\tcurve_1d_type=%s\n", + drm_get_colorop_curve_1d_type_name(state->curve_1d_type)); + break; + default: + break; + } + + drm_printf(p, "\tnext=%d\n", colorop->next ? colorop->next->base.id : 0); +} + static void drm_atomic_plane_print_state(struct drm_printer *p, const struct drm_plane_state *state) { @@ -798,7 +821,8 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, drm_printf(p, "\tcolor-range=%s\n", drm_get_color_range_name(state->color_range)); drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed); - + drm_printf(p, "\tcolor-pipeline=%d\n", + state->color_pipeline ? state->color_pipeline->base.id : 0); if (plane->funcs->atomic_print_state) plane->funcs->atomic_print_state(p, state); } @@ -1844,6 +1868,7 @@ static void __drm_state_dump(struct drm_device *dev, struct drm_printer *p, bool take_locks) { struct drm_mode_config *config = &dev->mode_config; + struct drm_colorop *colorop; struct drm_plane *plane; struct drm_crtc *crtc; struct drm_connector *connector; @@ -1853,6 +1878,14 @@ static void __drm_state_dump(struct drm_device *dev, struct drm_printer *p, if (!drm_drv_uses_atomic_modeset(dev)) return; + list_for_each_entry(colorop, &config->colorop_list, head) { + if (take_locks) + drm_modeset_lock(&colorop->plane->mutex, NULL); + drm_atomic_colorop_print_state(p, colorop->state); + if (take_locks) + drm_modeset_unlock(&colorop->plane->mutex); + } + list_for_each_entry(plane, &config->plane_list, head) { if (take_locks) drm_modeset_lock(&plane->mutex, NULL); -- 2.43.0
[PATCH V8 31/43] drm/amd/display: Add support for BT.709 and BT.2020 TFs
From: Harry Wentland This adds support for the BT.709/BT.2020 transfer functions on all current 1D curve plane colorops, i.e., on DEGAM, SHAPER, and BLND blocks. With this change the following IGT subtests pass: kms_colorop --run plane-XR30-XR30-bt2020_inv_oetf kms_colorop --run plane-XR30-XR30-bt2020_oetf Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Move BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) in amdgpu_dm_supported_blnd_tfs to "drm/amd/display: Enable support for PQ 125 EOTF and Inverse" (Leo Li) .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c | 11 --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 9 ++--- 2 files changed, 14 insertions(+), 6 deletions(-) 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 63044e0296cb..1765402bc122 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 @@ -676,6 +676,9 @@ amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf) case DRM_COLOROP_1D_CURVE_SRGB_EOTF: case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF: return TRANSFER_FUNCTION_SRGB; + case DRM_COLOROP_1D_CURVE_BT2020_INV_OETF: + case DRM_COLOROP_1D_CURVE_BT2020_OETF: + return TRANSFER_FUNCTION_BT709; case DRM_COLOROP_1D_CURVE_PQ_125_EOTF: case DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF: return TRANSFER_FUNCTION_PQ; @@ -1284,8 +1287,10 @@ __set_colorop_1d_curve_blend_tf_lut(struct dc_plane_state *dc_plane_state, const struct drm_color_lut *blend_lut; uint32_t blend_size = 0; - if (colorop->type != DRM_COLOROP_1D_CURVE && - colorop_state->curve_1d_type != DRM_COLOROP_1D_CURVE_SRGB_EOTF) + if (colorop->type != DRM_COLOROP_1D_CURVE) + return -EINVAL; + + if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) return -EINVAL; if (colorop_state->bypass) { @@ -1321,7 +1326,7 @@ __set_dm_plane_colorop_blend(struct drm_plane_state *plane_state, /* 3nd op: 1d curve - blend */ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == old_colorop && - new_colorop_state->curve_1d_type == DRM_COLOROP_1D_CURVE_SRGB_EOTF) { + (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) { colorop_state = new_colorop_state; break; } 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 581f41907c79..ff5828a1e8cd 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 @@ -33,15 +33,18 @@ const u64 amdgpu_dm_supported_degam_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | - BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF); + BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF); const u64 amdgpu_dm_supported_shaper_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) | - BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF); + BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF); const u64 amdgpu_dm_supported_blnd_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | - BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF); + BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF); #define MAX_COLOR_PIPELINE_OPS 10 -- 2.43.0
[PATCH V8 34/43] drm/amd/display: add 3x4 matrix colorop
This adds support for a 3x4 color transformation matrix. With this change the following IGT tests pass: kms_colorop --run plane-XR30-XR30-ctm_3x4_50_desat kms_colorop --run plane-XR30-XR30-ctm_3x4_overdrive kms_colorop --run plane-XR30-XR30-ctm_3x4_oversaturate kms_colorop --run plane-XR30-XR30-ctm_3x4_bt709_enc kms_colorop --run plane-XR30-XR30-ctm_3x4_bt709_dec The color pipeline now consists of the following colorops: 1. 1D curve colorop 2. 3x4 CTM 3. 1D curve colorop 4. 1D LUT 5. 1D curve colorop 6. 1D LUT Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Return -EINVAL when drm_color_ctm_3x4's size mismatches (Leo Li) v7: - Change %lu to %zu for sizeof() when In drm_warn v6: - fix warnings in dbg prints .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 52 +++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 16 ++ 2 files changed, 68 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 7e81088804fe..8a9404339da0 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 @@ -1211,6 +1211,47 @@ __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state, return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state); } +static int +__set_dm_plane_colorop_3x4_matrix(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 drm_atomic_state *state = plane_state->state; + const struct drm_device *dev = colorop->dev; + const struct drm_property_blob *blob; + struct drm_color_ctm_3x4 *ctm = NULL; + int i = 0; + + /* 3x4 matrix */ + 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_CTM_3X4) { + colorop_state = new_colorop_state; + break; + } + } + + if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_CTM_3X4) { + drm_dbg(dev, "3x4 matrix colorop with ID: %d\n", colorop->base.id); + blob = colorop_state->data; + if (blob->length == sizeof(struct drm_color_ctm_3x4)) { + ctm = blob ? (struct drm_color_ctm_3x4 *) blob->data : NULL; + __drm_ctm_3x4_to_dc_matrix(ctm, dc_plane_state->gamut_remap_matrix.matrix); + dc_plane_state->gamut_remap_matrix.enable_remap = true; + dc_plane_state->input_csc_color_matrix.enable_adjustment = false; + } else { + drm_warn(dev, "blob->length (%lu) isn't equal to drm_color_ctm_3x4 (%zu)\n", +blob->length, sizeof(struct drm_color_ctm_3x4)); + return -EINVAL; + } + } + + return 0; +} + static int __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, @@ -1414,6 +1455,17 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state, if (ret) return ret; + /* 3x4 matrix */ + colorop = colorop->next; + if (!colorop) { + drm_dbg(dev, "no 3x4 matrix colorop found\n"); + return -EINVAL; + } + + ret = __set_dm_plane_colorop_3x4_matrix(plane_state, dc_plane_state, colorop); + if (ret) + return ret; + /* 1D Curve & LUT - SHAPER 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 8a5e15083f11..9a9386bf85ec 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 @@ -75,6 +75,22 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr i++; + /* 3x4 matrix */ + ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); + if (!ops[i]) { + DRM_ERROR("KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i-1], ops[i]);
[PATCH V8 38/43] drm/colorop: Define LUT_1D interpolation
From: Harry Wentland We want to make sure userspace is aware of the 1D LUT interpolation. While linear interpolation is common it might not be supported on all HW. Give driver implementers a way to specify their interpolation. Reviewed-by: Simon Ser Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix a checkpatch long-line warning - Modify state->size to colorop->lut_size .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 6 ++-- drivers/gpu/drm/drm_atomic.c | 2 ++ drivers/gpu/drm/drm_colorop.c | 36 ++- include/drm/drm_colorop.h | 19 +- include/uapi/drm/drm_mode.h | 13 +++ 5 files changed, 72 insertions(+), 4 deletions(-) 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 d0777691862b..92c9f3fb0254 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 @@ -131,7 +131,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES); + ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES, + DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR); if (ret) goto cleanup; @@ -163,7 +164,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES); + ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES, + DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR); if (ret) goto cleanup; diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index a04f1c98fef9..9420fb9b8a46 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -795,6 +795,8 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, break; case DRM_COLOROP_1D_LUT: drm_printf(p, "\tsize=%d\n", colorop->lut_size); + drm_printf(p, "\tinterpolation=%s\n", + drm_get_colorop_lut1d_interpolation_name(colorop->lut1d_interpolation)); drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); break; case DRM_COLOROP_CTM_3X4: diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 45aa7b059e35..7360ca4d2325 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -78,6 +78,9 @@ static const char * const colorop_curve_1d_type_names[] = { [DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF] = "PQ 125 Inverse EOTF", }; +static const struct drm_prop_enum_list drm_colorop_lut1d_interpolation_list[] = { + { DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, "Linear" }, +}; /* Init Helpers */ @@ -234,10 +237,12 @@ static int drm_colorop_create_data_prop(struct drm_device *dev, struct drm_color * @colorop: The drm_colorop object to initialize * @plane: The associated drm_plane * @lut_size: LUT size supported by driver + * @lut1d_interpolation: 1D LUT interpolation type * @return zero on success, -E value on failure */ int drm_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_colorop *colorop, - struct drm_plane *plane, uint32_t lut_size) + struct drm_plane *plane, uint32_t lut_size, + enum drm_colorop_lut1d_interpolation_type lut1d_interpolation) { struct drm_property *prop; int ret; @@ -257,6 +262,17 @@ int drm_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_colorop *co drm_object_attach_property(&colorop->base, colorop->lut_size_property, lut_size); colorop->lut_size = lut_size; + /* Interpolation */ + prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, "LUT1D_INTERPOLATION", + drm_colorop_lut1d_interpolation_list, + ARRAY_SIZE(drm_colorop_lut1d_interpolation_list)); + if (!prop) + return -ENOMEM; + + colorop->lut1d_interpolation_property = prop; + drm_object_attach_property(&colorop->base, prop, lut1d_interpolation); + colorop->lut1d_interpolation = lut1d_interpolation; + /* data */ ret = drm_colorop_create_data_prop(dev, colorop); if (ret) @@ -411,6 +427,9 @@ static const char * const colorop_type_name[] = { [DRM_COLOR
[PATCH V8 39/43] drm/colorop: allow non-bypass colorops
From: Harry Wentland Not all HW will be able to do bypass on all color operations. Introduce an 'allow_bypass' boolean for all colorop init functions and only create the BYPASS property when it's true. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 22 +--- drivers/gpu/drm/drm_atomic.c | 3 +- drivers/gpu/drm/drm_colorop.c | 53 --- drivers/gpu/drm/vkms/vkms_colorop.c | 8 +-- include/drm/drm_colorop.h | 10 ++-- 5 files changed, 61 insertions(+), 35 deletions(-) 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 92c9f3fb0254..ec94ff887886 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 @@ -66,7 +66,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_init(dev, ops[i], plane, amdgpu_dm_supported_degam_tfs); + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, + amdgpu_dm_supported_degam_tfs, + true); if (ret) goto cleanup; @@ -83,7 +85,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_mult_init(dev, ops[i], plane); + ret = drm_colorop_mult_init(dev, ops[i], plane, true); if (ret) goto cleanup; @@ -99,7 +101,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane, true); if (ret) goto cleanup; @@ -115,7 +117,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_init(dev, ops[i], plane, amdgpu_dm_supported_shaper_tfs); + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, + amdgpu_dm_supported_shaper_tfs, + true); if (ret) goto cleanup; @@ -132,7 +136,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr } ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES, - DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR); + DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, + true); if (ret) goto cleanup; @@ -148,7 +153,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_curve_1d_init(dev, ops[i], plane, amdgpu_dm_supported_blnd_tfs); + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, + amdgpu_dm_supported_blnd_tfs, + true); if (ret) goto cleanup; @@ -165,7 +172,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr } ret = drm_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES, - DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR); + DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, + true); if (ret) goto cleanup; diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 9420fb9b8a46..0efb0ead204a 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -786,7 +786,8 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, drm_printf(p, "colorop[%u]:\n", colorop->base.id); drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type)); - drm_printf(p, "\tbypass=%u\n", state->bypass); + if (colorop->bypass_property) + drm_printf(p, "\tbypass=%u\n", state->bypass); switch (colorop->type) { case DRM_COLOROP_1D_CURVE: diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 7360ca4d2325..e03706e7179b 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -85,7 +85,8 @@ static const struct drm_prop_enum_list drm_colorop_lut1d_interpolation_list[] = /* Init Helpers */ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, -
[PATCH V8 40/43] drm/colorop: Add 3D LUT support to color pipeline
It is to be used to enable HDR by allowing userpace to create and pass 3D LUTs to kernel and hardware. new drm_colorop_type: DRM_COLOROP_3D_LUT. Signed-off-by: Alex Hung --- v8: - Fix typo in subject (Simon Ser) - Update documentation for DRM_COLOROP_3D_LUT (Simon Ser) - Delete empty lines (Simon Ser) v7: - Simplify 3D LUT by removing lut_3d_modes and related functions (Simon Ser) drivers/gpu/drm/drm_atomic.c | 6 +++ drivers/gpu/drm/drm_atomic_uapi.c | 6 +++ drivers/gpu/drm/drm_colorop.c | 72 +++ include/drm/drm_colorop.h | 21 + include/uapi/drm/drm_mode.h | 33 ++ 5 files changed, 138 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 0efb0ead204a..ef47a06344f3 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -806,6 +806,12 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, case DRM_COLOROP_MULTIPLIER: drm_printf(p, "\tmultiplier=%llu\n", state->multiplier); break; + case DRM_COLOROP_3D_LUT: + drm_printf(p, "\tsize=%d\n", colorop->lut_size); + drm_printf(p, "\tinterpolation=%s\n", + drm_get_colorop_lut3d_interpolation_name(colorop->lut3d_interpolation)); + drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); + break; default: break; } diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 947c18e8bf9b..d5d464b4d0f6 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -719,6 +719,10 @@ static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, case DRM_COLOROP_CTM_3X4: size = sizeof(struct drm_color_ctm_3x4); break; + case DRM_COLOROP_3D_LUT: + size = colorop->lut_size * colorop->lut_size * colorop->lut_size * + sizeof(struct drm_color_lut); + break; default: /* should never get here */ return -EINVAL; @@ -771,6 +775,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->multiplier; } else if (property == colorop->lut_size_property) { *val = colorop->lut_size; + } else if (property == colorop->lut3d_interpolation_property) { + *val = colorop->lut3d_interpolation; } else if (property == colorop->data_property) { *val = (state->data) ? state->data->base.id : 0; } else { diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index e03706e7179b..224c6be237d2 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -67,6 +67,7 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_LUT, "1D LUT" }, { DRM_COLOROP_CTM_3X4, "3x4 Matrix"}, { DRM_COLOROP_MULTIPLIER, "Multiplier"}, + { DRM_COLOROP_3D_LUT, "3D LUT"}, }; static const char * const colorop_curve_1d_type_names[] = { @@ -82,6 +83,11 @@ static const struct drm_prop_enum_list drm_colorop_lut1d_interpolation_list[] = { DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, "Linear" }, }; + +static const struct drm_prop_enum_list drm_colorop_lut3d_interpolation_list[] = { + { DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL, "Tetrahedral" }, +}; + /* Init Helpers */ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, @@ -349,6 +355,51 @@ int drm_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop, } EXPORT_SYMBOL(drm_colorop_mult_init); +int drm_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, + uint32_t lut_size, + enum drm_colorop_lut3d_interpolation_type interpolation, + bool allow_bypass) +{ + struct drm_property *prop; + int ret; + + ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_3D_LUT, allow_bypass); + if (ret) + return ret; + + /* LUT size */ + prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_ATOMIC, +"SIZE", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + + colorop->lut_size_property = prop; + drm_object_attach_property(&colorop->base, colorop->lut_size_property, lut_size); + colorop->lut_size = lut_size; + + /* interpolation */ + prop = drm_property_create_enum(dev
[PATCH V8 43/43] drm/colorop: Add destroy functions for color pipeline
The functions are to clean up color pipeline when a device driver fails to create its color pipeline. Signed-off-by: Alex Hung --- .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 3 +- drivers/gpu/drm/drm_colorop.c | 41 +++ drivers/gpu/drm/vkms/vkms_colorop.c | 3 +- include/drm/drm_colorop.h | 2 + 4 files changed, 45 insertions(+), 4 deletions(-) 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 e03e6044f937..80173f00dfd0 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 @@ -200,8 +200,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr return 0; cleanup: - for (; i >= 0; i--) - kfree(ops[i]); + drm_colorop_pipeline_destroy(plane); return ret; } diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 224c6be237d2..5845527201d2 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -154,6 +154,47 @@ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, return ret; } +/** + * drm_colorop_cleanup - Cleanup a drm_colorop object in color_pipeline + * + * @colorop: The drm_colorop object to be cleaned + */ +static void drm_colorop_cleanup(struct drm_colorop *colorop) +{ + struct drm_device *dev = colorop->dev; + struct drm_mode_config *config = &dev->mode_config; + + list_del(&colorop->head); + config->num_colorop--; + + if (colorop->state && colorop->state->data) { + drm_property_blob_put(colorop->state->data); + colorop->state->data = NULL; + } + + kfree(colorop->state); + kfree(colorop); +} + +/** + * drm_colorop_pipeline_destroy - Helper for color pipeline destruction + * + * @plane: - The drm_plane structure containing the color_pipeline + * + * Provides a default color pipeline destroy handler for a planes. + */ +void drm_colorop_pipeline_destroy(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + struct drm_colorop *colorop, *next; + + list_for_each_entry_safe(colorop, next, &config->colorop_list, head) { + drm_colorop_cleanup(colorop); + } +} +EXPORT_SYMBOL(drm_colorop_pipeline_destroy); + /** * drm_colorop_curve_1d_init - Initialize a DRM_COLOROP_1D_CURVE * diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c index a2f548a4b84d..801e7ad6ad79 100644 --- a/drivers/gpu/drm/vkms/vkms_colorop.c +++ b/drivers/gpu/drm/vkms/vkms_colorop.c @@ -89,8 +89,7 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr return 0; cleanup: - for (; i >= 0; i--) - kfree(ops[i]); + drm_colorop_pipeline_destroy(plane); return ret; } diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index e999d5ceb8a5..e1f7c91e8db1 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -366,6 +366,8 @@ static inline struct drm_colorop *drm_colorop_find(struct drm_device *dev, return mo ? obj_to_colorop(mo) : NULL; } +void drm_colorop_pipeline_destroy(struct drm_plane *plane); + int drm_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop *colorop, struct drm_plane *plane, u64 supported_tfs, bool allow_bypass); -- 2.43.0
[PATCH V8 42/43] drm/amd/display: Add AMD color pipeline doc
From: Harry Wentland Add kernel doc for AMD color pipeline. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 122 +++--- 1 file changed, 102 insertions(+), 20 deletions(-) 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 a4b6bb7b409f..6caec63f001b 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 @@ -33,6 +33,32 @@ /** * DOC: overview * + * We have three types of color management in the AMD display driver. + * 1. the legacy &drm_crtc DEGAMMA, CTM, and GAMMA properties + * 2. AMD driver private color management on &drm_plane and &drm_crtc + * 3. AMD plane color pipeline + * + * The CRTC properties are the original color management. When they were + * implemented per-plane color management was not a thing yet. Because + * of that we could get away with plumbing the DEGAMMA and CTM + * properties to pre-blending HW functions. This is incompatible with + * per-plane color management, such as via the AMD private properties or + * the new drm_plane color pipeline. The only compatible CRTC property + * with per-plane color management is the GAMMA property as it is + * applied post-blending. + * + * The AMD driver private color management properties are only exposed + * when the kernel is built explicitly with -DAMD_PRIVATE_COLOR. They + * are temporary building blocks on the path to full-fledged &drm_plane + * and &drm_crtc color pipelines and lay the driver's groundwork for the + * color pipelines. + * + * The AMD plane color pipeline describes AMD's &drm_colorops via the + * &drm_plane's COLOR_PIPELINE property. + * + * drm_crtc Properties + * --- + * * The DC interface to HW gives us the following color management blocks * per pipe (surface): * @@ -43,33 +69,89 @@ * - Surface regamma LUT (normalized) * - Output CSC (normalized) * - * But these aren't a direct mapping to DRM color properties. The current DRM - * interface exposes CRTC degamma, CRTC CTM and CRTC regamma while our hardware - * is essentially giving: + * But these aren't a direct mapping to DRM color properties. The + * current DRM interface exposes CRTC degamma, CRTC CTM and CRTC regamma + * while our hardware is essentially giving: * * Plane CTM -> Plane degamma -> Plane CTM -> Plane regamma -> Plane CTM * - * The input gamma LUT block isn't really applicable here since it operates - * on the actual input data itself rather than the HW fp representation. The - * input and output CSC blocks are technically available to use as part of - * the DC interface but are typically used internally by DC for conversions - * between color spaces. These could be blended together with user - * adjustments in the future but for now these should remain untouched. + * The input gamma LUT block isn't really applicable here since it + * operates on the actual input data itself rather than the HW fp + * representation. The input and output CSC blocks are technically + * available to use as part of the DC interface but are typically used + * internally by DC for conversions between color spaces. These could be + * blended together with user adjustments in the future but for now + * these should remain untouched. + * + * The pipe blending also happens after these blocks so we don't + * actually support any CRTC props with correct blending with multiple + * planes - but we can still support CRTC color management properties in + * DM in most single plane cases correctly with clever management of the + * DC interface in DM. + * + * As per DRM documentation, blocks should be in hardware bypass when + * their respective property is set to NULL. A linear DGM/RGM LUT should + * also considered as putting the respective block into bypass mode. + * + * This means that the following configuration is assumed to be the + * default: + * + * Plane DGM Bypass -> Plane CTM Bypass -> Plane RGM Bypass -> ... CRTC + * DGM Bypass -> CRTC CTM Bypass -> CRTC RGM Bypass + * + * AMD Private Color Management on drm_plane + * - + * + * The AMD private color management properties on a &drm_plane are: + * + * - AMD_PLANE_DEGAMMA_LUT + * - AMD_PLANE_DEGAMMA_LUT_SIZE + * - AMD_PLANE_DEGAMMA_TF + * - AMD_PLANE_HDR_MULT + * - AMD_PLANE_CTM + * - AMD_PLANE_SHAPER_LUT + * - AMD_PLANE_SHAPER_LUT_SIZE + * - AMD_PLANE_SHAPER_TF + * - AMD_PLANE_LUT3D + * - AMD_PLANE_LUT3D_SIZE + * - AMD_PLANE_BLEND_LUT + * - AMD_PLANE_BLEND_LUT_SIZE + * - AMD_PLANE_BLEND_TF + * + * The AMD private color management property on a &drm_crtc is: + * + * - AMD_CRTC_REGAMMA_TF + * + * Use of these properties is discouraged. + * + * AMD plane color pipeline + * + * + * The AMD &drm_
[PATCH V8 36/43] drm/amd/display: add multiplier colorop
This adds support for a multiplier. This multiplier is programmed via the HDR Multiplier in DCN. With this change the following IGT tests pass: kms_colorop --run plane-XR30-XR30-multiply_125 kms_colorop --run plane-XR30-XR30-multiply_inv_125 The color pipeline now consists of the following colorops: 1. 1D curve colorop 2. 3x4 CTM 3. Multiplier 4. 1D curve colorop 5. 1D LUT 6. 1D curve colorop 7. 1D LUT Signed-off-by: Alex Hung --- .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 40 +++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 16 2 files changed, 56 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 8a9404339da0..e3ae0c32da2b 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 @@ -1252,6 +1252,35 @@ __set_dm_plane_colorop_3x4_matrix(struct drm_plane_state *plane_state, return 0; } +static int +__set_dm_plane_colorop_multiplier(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 drm_atomic_state *state = plane_state->state; + const struct drm_device *dev = colorop->dev; + int i = 0; + + /* Multiplier */ + 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_MULTIPLIER) { + colorop_state = new_colorop_state; + break; + } + } + + if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_MULTIPLIER) { + drm_dbg(dev, "Multiplier colorop with ID: %d\n", colorop->base.id); + dc_plane_state->hdr_mult = amdgpu_dm_fixpt_from_s3132(colorop_state->multiplier); + } + + return 0; +} + static int __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, @@ -1466,6 +1495,17 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state, if (ret) return ret; + /* Multiplier */ + colorop = colorop->next; + if (!colorop) { + drm_dbg(dev, "no multiplier colorop found\n"); + return -EINVAL; + } + + ret = __set_dm_plane_colorop_multiplier(plane_state, dc_plane_state, colorop); + if (ret) + return ret; + /* 1D Curve & LUT - SHAPER 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 9a9386bf85ec..33156531d751 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 @@ -91,6 +91,22 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr i++; + /* Multiplier */ + ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); + if (!ops[i]) { + DRM_ERROR("KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_colorop_mult_init(dev, ops[i], plane); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i-1], ops[i]); + + i++; + /* 1D curve - SHAPER TF */ ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); if (!ops[i]) { -- 2.43.0
[PATCH V8 01/43] drm: Add helper for conversion from signed-magnitude
From: Harry Wentland CTM values are defined as signed-magnitude values. Add a helper that converts from CTM signed-magnitude fixed point value to the twos-complement value used by drm_fixed. Reviewed-by: Louis Chauvet Signed-off-by: Harry Wentland --- include/drm/drm_fixed.h | 18 ++ 1 file changed, 18 insertions(+) diff --git a/include/drm/drm_fixed.h b/include/drm/drm_fixed.h index 1922188f00e8..0b44f2f294ce 100644 --- a/include/drm/drm_fixed.h +++ b/include/drm/drm_fixed.h @@ -78,6 +78,24 @@ static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B) #define DRM_FIXED_EPSILON 1LL #define DRM_FIXED_ALMOST_ONE (DRM_FIXED_ONE - DRM_FIXED_EPSILON) +/** + * @drm_sm2fixp + * + * Convert a 1.31.32 signed-magnitude fixed point to 32.32 + * 2s-complement fixed point + * + * @return s64 2s-complement fixed point + */ +static inline s64 drm_sm2fixp(__u64 a) +{ + if ((a & (1LL << 63))) { + return -(a & 0x7fffll); + } else { + return a; + } + +} + static inline s64 drm_int2fixp(int a) { return ((s64)a) << DRM_FIXED_POINT; -- 2.43.0
[PATCH V8 03/43] drm/doc/rfc: Describe why prescriptive color pipeline is needed
From: Harry Wentland Add documentation for color pipeline API. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Fix typo "definint" -> "defining" v7: - Add a commit messages v5: - Don't require BYPASS to succeed (Sebastian) - use DATA for 1D and 3D LUT types (Sebastian) - update 3DLUT ops to use 3DLUT_MODES and 3DLUT_MODE_INDEX - Add section on drm_colorop extensibility - Add color_pipeline.rst to RFC toc tree v4: - Drop IOCTL docs since we dropped the IOCTLs (Pekka) - Clarify reading and setting of COLOR_PIPELINE prop (Pekka) - Add blurb about not requiring to reject a pipeline due to incompatible ops, as long as op can be bypassed (Pekka) - Dropped informational strings (such as input CSC) as they're not actually intended to be advertised (Pekka) v3: - Describe DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE (Sebastian) - Ask for clear documentation of colorop behavior (Sebastian) v2: - Update colorop visualizations to match reality (Sebastian, Alex Hung) - Updated wording (Pekka) - Change BYPASS wording to make it non-mandatory (Sebastian) - Drop cover-letter-like paragraph from COLOR_PIPELINE Plane Property section (Pekka) - Use PQ EOTF instead of its inverse in Pipeline Programming example (Melissa) - Add "Driver Implementer's Guide" section (Pekka) - Add "Driver Forward/Backward Compatibility" section (Sebastian, Pekka) Documentation/gpu/rfc/color_pipeline.rst | 378 +++ Documentation/gpu/rfc/index.rst | 3 + 2 files changed, 381 insertions(+) create mode 100644 Documentation/gpu/rfc/color_pipeline.rst diff --git a/Documentation/gpu/rfc/color_pipeline.rst b/Documentation/gpu/rfc/color_pipeline.rst new file mode 100644 index ..58bcc2a5ffd8 --- /dev/null +++ b/Documentation/gpu/rfc/color_pipeline.rst @@ -0,0 +1,378 @@ +.. SPDX-License-Identifier: GPL-2.0 + + +Linux Color Pipeline API + + +What problem are we solving? + + +We would like to support pre-, and post-blending complex color +transformations in display controller hardware in order to allow for +HW-supported HDR use-cases, as well as to provide support to +color-managed applications, such as video or image editors. + +It is possible to support an HDR output on HW supporting the Colorspace +and HDR Metadata drm_connector properties, but that requires the +compositor or application to render and compose the content into one +final buffer intended for display. Doing so is costly. + +Most modern display HW offers various 1D LUTs, 3D LUTs, matrices, and other +operations to support color transformations. These operations are often +implemented in fixed-function HW and therefore much more power efficient than +performing similar operations via shaders or CPU. + +We would like to make use of this HW functionality to support complex color +transformations with no, or minimal CPU or shader load. + + +How are other OSes solving this problem? + + +The most widely supported use-cases regard HDR content, whether video or +gaming. + +Most OSes will specify the source content format (color gamut, encoding transfer +function, and other metadata, such as max and average light levels) to a driver. +Drivers will then program their fixed-function HW accordingly to map from a +source content buffer's space to a display's space. + +When fixed-function HW is not available the compositor will assemble a shader to +ask the GPU to perform the transformation from the source content format to the +display's format. + +A compositor's mapping function and a driver's mapping function are usually +entirely separate concepts. On OSes where a HW vendor has no insight into +closed-source compositor code such a vendor will tune their color management +code to visually match the compositor's. On other OSes, where both mapping +functions are open to an implementer they will ensure both mappings match. + +This results in mapping algorithm lock-in, meaning that no-one alone can +experiment with or introduce new mapping algorithms and achieve +consistent results regardless of which implementation path is taken. + +Why is Linux different? +=== + +Unlike other OSes, where there is one compositor for one or more drivers, on +Linux we have a many-to-many relationship. Many compositors; many drivers. +In addition each compositor vendor or community has their own view of how +color management should be done. This is what makes Linux so beautiful. + +This means that a HW vendor can now no longer tune their driver to one +compositor, as tuning it to one could make it look fairly different from +another compositor's color mapping. + +We need a better solution. + + +Descriptive API +=== + +An API that describes the source and destination colorspaces is a descript
[PATCH V8 02/43] drm/vkms: Add kunit tests for VKMS LUT handling
From: Harry Wentland Debugging LUT math is much easier when we can unit test it. Add kunit functionality to VKMS and add tests for - get_lut_index - lerp_u16 Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland Cc: Arthur Grillo --- v8: - Update config names (Louis Chauvet) v7: - Fix checkpatch warnings and errors (Louis Chauvet) - Change SPDX-License-Identifier: GPL-2.0+ from /* */ to // - Fix checkpatch errors and warnings (new line at EOF, redundant spaces, and long lines) - Add static to const struct vkms_color_lut test_linear_lut - Add "MODULE_DESCRIPTION" (Jeff Johnson) v6: - Eliminate need to include test as .c file (Louis Chauvet) v5: - Bring back static for lerp_u16 and get_lut_index (Arthur) v4: - Test the critical points of the lerp function (Pekka) v3: - Use include way of testing static functions (Arthur) drivers/gpu/drm/vkms/Kconfig | 15 ++ drivers/gpu/drm/vkms/Makefile| 1 + drivers/gpu/drm/vkms/tests/.kunitconfig | 4 + drivers/gpu/drm/vkms/tests/Makefile | 3 + drivers/gpu/drm/vkms/tests/vkms_color_test.c | 172 +++ drivers/gpu/drm/vkms/vkms_composer.c | 8 +- drivers/gpu/drm/vkms/vkms_composer.h | 13 ++ 7 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/vkms/tests/.kunitconfig create mode 100644 drivers/gpu/drm/vkms/tests/Makefile create mode 100644 drivers/gpu/drm/vkms/tests/vkms_color_test.c create mode 100644 drivers/gpu/drm/vkms/vkms_composer.h diff --git a/drivers/gpu/drm/vkms/Kconfig b/drivers/gpu/drm/vkms/Kconfig index 9def079f685b..3c02f928ffe6 100644 --- a/drivers/gpu/drm/vkms/Kconfig +++ b/drivers/gpu/drm/vkms/Kconfig @@ -14,3 +14,18 @@ config DRM_VKMS a VKMS. If M is selected the module will be called vkms. + +config DRM_VKMS_KUNIT_TEST + tristate "KUnit tests for VKMS" if !KUNIT_ALL_TESTS + depends on DRM_VKMS && KUNIT + default KUNIT_ALL_TESTS + help + This builds unit tests for VKMS. This option is not useful for + distributions or general kernels, but only for kernel + developers working on VKMS. + + For more information on KUnit and unit tests in general, + please refer to the KUnit documentation in + Documentation/dev-tools/kunit/. + + If in doubt, say "N". diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile index 1b28a6a32948..490b26d2e69d 100644 --- a/drivers/gpu/drm/vkms/Makefile +++ b/drivers/gpu/drm/vkms/Makefile @@ -9,3 +9,4 @@ vkms-y := \ vkms_writeback.o obj-$(CONFIG_DRM_VKMS) += vkms.o +obj-$(CONFIG_DRM_VKMS_KUNIT_TEST) += tests/ diff --git a/drivers/gpu/drm/vkms/tests/.kunitconfig b/drivers/gpu/drm/vkms/tests/.kunitconfig new file mode 100644 index ..6a2d87068edc --- /dev/null +++ b/drivers/gpu/drm/vkms/tests/.kunitconfig @@ -0,0 +1,4 @@ +CONFIG_KUNIT=y +CONFIG_DRM=y +CONFIG_DRM_VKMS=y +CONFIG_DRM_VKMS_KUNIT_TEST=y diff --git a/drivers/gpu/drm/vkms/tests/Makefile b/drivers/gpu/drm/vkms/tests/Makefile new file mode 100644 index ..de1af87cbb11 --- /dev/null +++ b/drivers/gpu/drm/vkms/tests/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_DRM_VKMS_KUNIT_TEST) += vkms_color_test.o \ No newline at end of file diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c new file mode 100644 index ..affca56cac7b --- /dev/null +++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include + +#include +#include +#include "../vkms_drv.h" +#include "../vkms_composer.h" + +#define TEST_LUT_SIZE 16 + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +static struct drm_color_lut test_linear_array[TEST_LUT_SIZE] = { + { 0x0, 0x0, 0x0, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, + { 0x, 0x, 0x, 0 }, +}; + +static const struct vkms_color_lut test_linear_lut = { + .base = test_linear_array, + .lut_length = TEST_LUT_SIZE, + .channel_value2index_ratio = 0xf000fll +}; + + +static void vkms_color_test_get_lut_index(struct kunit *test) +{ + s64 lut_index; + int i; + + lut_index = get_lut_index(&test_linear_lut, test_linear_array[0].red); + KUNIT_EXPECT_EQ(te
[PATCH V8 04/43] drm/colorop: Introduce new drm_colorop mode object
From: Harry Wentland This patches introduces a new drm_colorop mode object. This object represents color transformations and can be used to define color pipelines. We also introduce the drm_colorop_state here, as well as various helpers and state tracking bits. Reviewed-by: Simon Ser Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Remove drm_atomic_get_existing_colorop_state (Louis Chauvet) v7: - Fix checkpatch warnings and errors - Add a tab to for_each_oldnew_colorop_in_state definition - Change unsigned index to unsigned int index - Fix a checkpatch warning - a new line after variable declaration v6: - Comment that properties validity depends on type (Louis Chauvet) v5: - Add comment to drm_atomic_state.colorops - Replace a misplaced 'plane' with 'colorop' in comment - Fix colorop_list kernel doc - Add kernel doc for color_pipeline - drop unused drm_colorop_destroy_state - drop drm_colorop_init, to be introduced in later patch when used - Add kernel docs - Drop TODOs v4: - Drop IOCTL definitions (Pekka) - add missing declaration (Chaitanya Kumar Borah) v3: - Drop TODO for lock (it's handled in drm_modeset_drop_locks) (Melissa) - Don't get plane state when getting colorop state - Make some functions static (kernel test robot) drivers/gpu/drm/Makefile| 1 + drivers/gpu/drm/drm_atomic.c| 70 drivers/gpu/drm/drm_atomic_helper.c | 12 ++ drivers/gpu/drm/drm_atomic_uapi.c | 48 drivers/gpu/drm/drm_colorop.c | 104 + drivers/gpu/drm/drm_mode_config.c | 7 ++ include/drm/drm_atomic.h| 70 include/drm/drm_atomic_uapi.h | 1 + include/drm/drm_colorop.h | 171 include/drm/drm_mode_config.h | 18 +++ include/drm/drm_plane.h | 8 ++ include/uapi/drm/drm_mode.h | 1 + 12 files changed, 511 insertions(+) create mode 100644 drivers/gpu/drm/drm_colorop.c create mode 100644 include/drm/drm_colorop.h diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index ed54a546bbe2..50bb6be2cad1 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -41,6 +41,7 @@ drm-y := \ drm_bridge.o \ drm_cache.o \ drm_color_mgmt.o \ + drm_colorop.o \ drm_connector.o \ drm_crtc.o \ drm_displayid.o \ diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 9ea2611770f4..85d492a1f3d4 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -107,6 +108,7 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) kfree(state->connectors); kfree(state->crtcs); kfree(state->planes); + kfree(state->colorops); kfree(state->private_objs); } EXPORT_SYMBOL(drm_atomic_state_default_release); @@ -138,6 +140,10 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) sizeof(*state->planes), GFP_KERNEL); if (!state->planes) goto fail; + state->colorops = kcalloc(dev->mode_config.num_colorop, + sizeof(*state->colorops), GFP_KERNEL); + if (!state->colorops) + goto fail; /* * Because drm_atomic_state can be committed asynchronously we need our @@ -249,6 +255,20 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) state->planes[i].new_state = NULL; } + for (i = 0; i < config->num_colorop; i++) { + struct drm_colorop *colorop = state->colorops[i].ptr; + + if (!colorop) + continue; + + drm_colorop_atomic_destroy_state(colorop, +state->colorops[i].state); + state->colorops[i].ptr = NULL; + state->colorops[i].state = NULL; + state->colorops[i].old_state = NULL; + state->colorops[i].new_state = NULL; + } + for (i = 0; i < state->num_private_objs; i++) { struct drm_private_obj *obj = state->private_objs[i].ptr; @@ -568,6 +588,56 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_get_plane_state); + +/** + * drm_atomic_get_colorop_state - get colorop state + * @state: global atomic state object + * @colorop: colorop to get state object for + * + * This function returns the colorop state for the given colorop, allocating it + * if needed. It will also grab the relevant plane lock to make sure that the + * state is consistent. + * + * Returns: + * + * Either the allocated
[PATCH V8 06/43] drm/colorop: Add 1D Curve subtype
From: Harry Wentland Add a new drm_colorop with DRM_COLOROP_1D_CURVE with two subtypes: DRM_COLOROP_1D_CURVE_SRGB_EOTF and DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF. Reviewed-by: Simon Ser Reviewed-by: Louis Chauvet Signed-off-by: Harry Wentland Co-developed-by: Alex Hung Signed-off-by: Alex Hung --- v5: - Add drm_get_colorop_curve_1d_type_name in header - Add drm_colorop_init - Set default curve - Add kernel docs v4: - Use drm_colorop_curve_1d_type_enum_list to get name (Pekka) - Create separate init function for 1D curve - Pass supported TFs into 1D curve init function drivers/gpu/drm/drm_atomic_uapi.c | 18 ++-- drivers/gpu/drm/drm_colorop.c | 134 ++ include/drm/drm_colorop.h | 63 ++ 3 files changed, 210 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 5e8ce781fcd7..f75c987f8913 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -649,11 +649,17 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, struct drm_colorop_state *state, struct drm_file *file_priv, struct drm_property *property, uint64_t val) { - drm_dbg_atomic(colorop->dev, - "[COLOROP:%d] unknown property [PROP:%d:%s]]\n", - colorop->base.id, - property->base.id, property->name); - return -EINVAL; + if (property == colorop->curve_1d_type_property) { + state->curve_1d_type = val; + } else { + drm_dbg_atomic(colorop->dev, + "[COLOROP:%d:%d] unknown property [PROP:%d:%s]]\n", + colorop->base.id, colorop->type, + property->base.id, property->name); + return -EINVAL; + } + + return 0; } static int @@ -663,6 +669,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, { if (property == colorop->type_property) { *val = colorop->type; + } else if (property == colorop->curve_1d_type_property) { + *val = state->curve_1d_type; } else { return -EINVAL; } diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 1459a28c7e7b..a42de0aa48e1 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -31,6 +31,123 @@ #include "drm_crtc_internal.h" +static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { + { DRM_COLOROP_1D_CURVE, "1D Curve" }, +}; + +static const char * const colorop_curve_1d_type_names[] = { + [DRM_COLOROP_1D_CURVE_SRGB_EOTF] = "sRGB EOTF", + [DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF] = "sRGB Inverse EOTF", +}; + + +/* Init Helpers */ + +static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, enum drm_colorop_type type) +{ + struct drm_mode_config *config = &dev->mode_config; + struct drm_property *prop; + int ret = 0; + + ret = drm_mode_object_add(dev, &colorop->base, DRM_MODE_OBJECT_COLOROP); + if (ret) + return ret; + + colorop->base.properties = &colorop->properties; + colorop->dev = dev; + colorop->type = type; + colorop->plane = plane; + + list_add_tail(&colorop->head, &config->colorop_list); + colorop->index = config->num_colorop++; + + /* add properties */ + + /* type */ + prop = drm_property_create_enum(dev, + DRM_MODE_PROP_IMMUTABLE, + "TYPE", drm_colorop_type_enum_list, + ARRAY_SIZE(drm_colorop_type_enum_list)); + + if (!prop) + return -ENOMEM; + + colorop->type_property = prop; + + drm_object_attach_property(&colorop->base, + colorop->type_property, + colorop->type); + + return ret; +} + +/** + * drm_colorop_curve_1d_init - Initialize a DRM_COLOROP_1D_CURVE + * + * @dev: DRM device + * @colorop: The drm_colorop object to initialize + * @plane: The associated drm_plane + * @supported_tfs: A bitfield of supported drm_colorop_curve_1d_init enum values, + * created using BIT(curve_type) and combined with the OR '|' + * operator. + * @return zero on success, -E value on failure + */ +int drm_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, u64 supported_tfs) +{ + struct drm_prop_enum_list enum_list[DRM_COLOROP_1D_
[PATCH V8 05/43] drm/colorop: Add TYPE property
From: Harry Wentland Add a read-only TYPE property. The TYPE specifies the colorop type, such as enumerated curve, 1D LUT, CTM, 3D LUT, PWL LUT, etc. For now we're only introducing an enumerated 1D LUT type to illustrate the concept. Reviewed-by: Simon Ser Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v6: - fix doc typo (Alex Hung) v5: - Add drm_get_colorop_type_name in header - Add kernel docs v4: - Use enum property for TYPE (Pekka) v3: - Make TYPE a range property - Move enum drm_colorop_type to uapi header - Fix drm_get_colorop_type_name description drivers/gpu/drm/drm_atomic.c | 4 ++-- drivers/gpu/drm/drm_atomic_uapi.c | 8 +++- drivers/gpu/drm/drm_colorop.c | 12 include/drm/drm_colorop.h | 23 +++ include/uapi/drm/drm_mode.h | 19 +++ 5 files changed, 63 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 85d492a1f3d4..12c313d43e56 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -631,8 +631,8 @@ drm_atomic_get_colorop_state(struct drm_atomic_state *state, state->colorops[index].new_state = colorop_state; colorop_state->state = state; - drm_dbg_atomic(colorop->dev, "Added [COLOROP:%d] %p state to %p\n", - colorop->base.id, colorop_state, state); + drm_dbg_atomic(colorop->dev, "Added [COLOROP:%d:%d] %p state to %p\n", + colorop->base.id, colorop->type, colorop_state, state); return colorop_state; } diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index cf1772118895..5e8ce781fcd7 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -661,7 +661,13 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, const struct drm_colorop_state *state, struct drm_property *property, uint64_t *val) { - return -EINVAL; + if (property == colorop->type_property) { + *val = colorop->type; + } else { + return -EINVAL; + } + + return 0; } static int drm_atomic_set_writeback_fb_for_connector( diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index d215e22c9d20..1459a28c7e7b 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -102,3 +102,15 @@ void drm_colorop_reset(struct drm_colorop *colorop) if (colorop->state) __drm_colorop_reset(colorop, colorop->state); } + +static const char * const colorop_type_name[] = { + [DRM_COLOROP_1D_CURVE] = "1D Curve", +}; + +const char *drm_get_colorop_type_name(enum drm_colorop_type type) +{ + if (WARN_ON(type >= ARRAY_SIZE(colorop_type_name))) + return "unknown"; + + return colorop_type_name[type]; +} diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index 008665d2d960..9c9698545f63 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -112,6 +112,21 @@ struct drm_colorop { /** @properties: property tracking for this colorop */ struct drm_object_properties properties; + /** +* @type: +* +* Read-only +* Type of color operation +*/ + enum drm_colorop_type type; + + /** +* @type_property: +* +* Read-only "TYPE" property for specifying the type of +* this color operation. The type is enum drm_colorop_type. +*/ + struct drm_property *type_property; }; #define obj_to_colorop(x) container_of(x, struct drm_colorop, base) @@ -167,5 +182,13 @@ static inline unsigned int drm_colorop_index(const struct drm_colorop *colorop) #define drm_for_each_colorop(colorop, dev) \ list_for_each_entry(colorop, &(dev)->mode_config.colorop_list, head) +/** + * drm_get_colorop_type_name - return a string for colorop type + * @type: colorop type to compute name of + * + * In contrast to the other drm_get_*_name functions this one here returns a + * const pointer and hence is threadsafe. + */ +const char *drm_get_colorop_type_name(enum drm_colorop_type type); #endif /* __DRM_COLOROP_H__ */ diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index d49cded6c568..ea6d88f683cd 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -858,6 +858,25 @@ struct drm_color_lut { __u16 reserved; }; +/** + * enum drm_colorop_type - Type of color operation + * + * drm_colorops can be of many different types. Each type behaves differently + * and defines a different set of properties. This enum defines all types and + * gives a high-level description. + */ +enum drm_colorop_type { + /** +* @DRM_COLOROP_1D_
[PATCH V8 07/43] drm/colorop: Add BYPASS property
From: Harry Wentland We want to be able to bypass each colorop at all times. Introduce a new BYPASS boolean property for this. Reviewed-by: Simon Ser Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v6: - clarify that bypass is only valid if BYPASS prop exists (Louis Chauvet) drivers/gpu/drm/drm_atomic_uapi.c | 6 +- drivers/gpu/drm/drm_colorop.c | 15 +++ include/drm/drm_colorop.h | 22 ++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index f75c987f8913..744eb3a8c9db 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -649,7 +649,9 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, struct drm_colorop_state *state, struct drm_file *file_priv, struct drm_property *property, uint64_t val) { - if (property == colorop->curve_1d_type_property) { + if (property == colorop->bypass_property) { + state->bypass = val; + } else if (property == colorop->curve_1d_type_property) { state->curve_1d_type = val; } else { drm_dbg_atomic(colorop->dev, @@ -669,6 +671,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, { if (property == colorop->type_property) { *val = colorop->type; + } else if (property == colorop->bypass_property) { + *val = state->bypass; } else if (property == colorop->curve_1d_type_property) { *val = state->curve_1d_type; } else { diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index a42de0aa48e1..60edf6671643 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -79,6 +79,17 @@ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, colorop->type_property, colorop->type); + /* bypass */ + prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC, + "BYPASS"); + if (!prop) + return -ENOMEM; + + colorop->bypass_property = prop; + drm_object_attach_property(&colorop->base, + colorop->bypass_property, + 1); + return ret; } @@ -136,6 +147,7 @@ int drm_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop *coloro /* initialize 1D curve only attribute */ prop = drm_property_create_enum(dev, DRM_MODE_PROP_ATOMIC, "CURVE_1D_TYPE", enum_list, len); + if (!prop) return -ENOMEM; @@ -152,6 +164,8 @@ static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colo struct drm_colorop_state *state) { memcpy(state, colorop->state, sizeof(*state)); + + state->bypass = true; } struct drm_colorop_state * @@ -190,6 +204,7 @@ static void __drm_colorop_state_reset(struct drm_colorop_state *colorop_state, u64 val; colorop_state->colorop = colorop; + colorop_state->bypass = true; if (colorop->curve_1d_type_property) { drm_object_property_get_default_value(&colorop->base, diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index fc1b2e0f33e0..315d748e212e 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -82,6 +82,16 @@ struct drm_colorop_state { * information. */ + + /** +* @bypass: +* +* When the property BYPASS exists on this colorop, this stores +* the requested bypass state: true if colorop shall be bypassed, +* false if colorop is enabled. +*/ + bool bypass; + /** * @curve_1d_type: * @@ -171,6 +181,18 @@ struct drm_colorop { */ struct drm_property *type_property; + /** +* @bypass_property: +* +* Boolean property to control enablement of the color +* operation. Setting bypass to "true" shall always be supported +* in order to allow compositors to quickly fall back to +* alternate methods of color processing. This is important +* since setting color operations can fail due to unique +* HW constraints. +*/ + struct drm_property *bypass_property; + /** * @curve_1d_type_property: * -- 2.43.0
[PATCH V8 10/43] drm/plane: Add COLOR PIPELINE property
From: Harry Wentland We're adding a new enum COLOR PIPELINE property. This property will have entries for each COLOR PIPELINE by referencing the DRM object ID of the first drm_colorop of the pipeline. 0 disables the entire COLOR PIPELINE. Userspace can use this to discover the available color pipelines, as well as set the desired one. The color pipelines are programmed via properties on the actual drm_colorop objects. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Use upper case in drm_dbg_atomic (Simon Ser) - Remove MAX_COLOR_PIPELINES (Simon Ser) - Make pipelines const in drm_plane_create_color_pipeline_property (Simon Ser) v7: - Fix a checkpatch warning - a new line after variable declaration v4: - Add pipeline property creation helper (Pekka) - Fix function comment for drm_atomic_set_colorop_for_plane (Pekka) - Always create Bypass pipeline (Pekka) - Add missing function declaration (Chaitanya Kumar Borah) drivers/gpu/drm/drm_atomic.c | 46 ++ drivers/gpu/drm/drm_atomic_state_helper.c | 5 ++ drivers/gpu/drm/drm_atomic_uapi.c | 43 + drivers/gpu/drm/drm_plane.c | 59 +++ include/drm/drm_atomic.h | 3 ++ include/drm/drm_atomic_uapi.h | 2 + include/drm/drm_plane.h | 11 + 7 files changed, 169 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 97a30d6a68a1..e67d7a68e8a8 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1472,6 +1472,52 @@ drm_atomic_add_affected_planes(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_add_affected_planes); +/** + * drm_atomic_add_affected_colorops - add colorops for plane + * @state: atomic state + * @plane: DRM plane + * + * This function walks the current configuration and adds all colorops + * currently used by @plane to the atomic configuration @state. This is useful + * when an atomic commit also needs to check all currently enabled colorop on + * @plane, e.g. when changing the mode. It's also useful when re-enabling a plane + * to avoid special code to force-enable all colorops. + * + * Since acquiring a colorop state will always also acquire the w/w mutex of the + * current plane for that colorop (if there is any) adding all the colorop states for + * a plane will not reduce parallelism of atomic updates. + * + * Returns: + * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK + * then the w/w mutex code has detected a deadlock and the entire atomic + * sequence must be restarted. All other errors are fatal. + */ +int +drm_atomic_add_affected_colorops(struct drm_atomic_state *state, +struct drm_plane *plane) +{ + struct drm_colorop *colorop; + struct drm_colorop_state *colorop_state; + + WARN_ON(!drm_atomic_get_new_plane_state(state, plane)); + + drm_dbg_atomic(plane->dev, + "Adding all current colorops for [PLANE:%d:%s] to %p\n", + plane->base.id, plane->name, state); + + drm_for_each_colorop(colorop, plane->dev) { + if (colorop->plane != plane) + continue; + + colorop_state = drm_atomic_get_colorop_state(state, colorop); + if (IS_ERR(colorop_state)) + return PTR_ERR(colorop_state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_add_affected_colorops); + /** * drm_atomic_check_only - check whether a given config would work * @state: atomic configuration to check diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 519228eb1095..d1dd082b1286 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -267,6 +267,11 @@ void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state, plane_state->color_range = val; } + if (plane->color_pipeline_property) { + /* default is always NULL, i.e., bypass */ + plane_state->color_pipeline = NULL; + } + if (plane->zpos_property) { if (!drm_object_property_get_default_value(&plane->base, plane->zpos_property, diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 744eb3a8c9db..5738b1c18755 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -257,6 +257,36 @@ drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, } EXPORT_SYMBOL(drm_atomic_set_fb_for_plane); + +/** + * drm_atomic_set_colorop_for_plane - set colorop for plane + * @plane_state: atomic state object for the plane + * @colorop: colorop to use for the plane + * +
[PATCH V8 11/43] drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE
From: Harry Wentland With the introduction of the pre-blending color pipeline we can no longer have color operations that don't have a clear position in the color pipeline. We deprecate all existing plane properties. For upstream drivers those are: - COLOR_ENCODING - COLOR_RANGE Drivers are expected to ignore these properties when programming the HW. DRM clients that don't register with DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE will not be allowed to set the COLOR_ENCODING and COLOR_RANGE properties. Setting of the COLOR_PIPELINE plane property or drm_colorop properties is only allowed for userspace that sets this client cap. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Disallow setting of COLOR_RANGE and COLOR_ENCODING when DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set v5: - Fix kernel docs v4: - Don't block setting of COLOR_RANGE and COLOR_ENCODING when client cap is set drivers/gpu/drm/drm_atomic_uapi.c | 23 ++- drivers/gpu/drm/drm_ioctl.c | 7 +++ include/drm/drm_file.h| 7 +++ include/uapi/drm/drm.h| 15 +++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 5738b1c18755..e0b4b122ef6b 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -567,10 +567,26 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, } else if (property == plane->zpos_property) { state->zpos = val; } else if (property == plane->color_encoding_property) { + if (file_priv->plane_color_pipeline) { + drm_dbg_atomic(dev, + "Setting COLOR_ENCODING plane property not permitted with DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE client cap\n"); + return -EINVAL; + } state->color_encoding = val; } else if (property == plane->color_range_property) { + if (file_priv->plane_color_pipeline) { + drm_dbg_atomic(dev, + "Setting COLOR_RANGE plane property not permitted with DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE client cap\n"); + return -EINVAL; + } state->color_range = val; } else if (property == plane->color_pipeline_property) { + if (!file_priv->plane_color_pipeline) { + drm_dbg_atomic(dev, + "Setting COLOR_PIPELINE plane property not permitted unless DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set\n"); + return -EINVAL; + } + /* find DRM colorop object */ struct drm_colorop *colorop = NULL; @@ -1197,6 +1213,12 @@ int drm_atomic_set_property(struct drm_atomic_state *state, break; } case DRM_MODE_OBJECT_COLOROP: { + if (!file_priv->plane_color_pipeline) { + drm_dbg_atomic(prop->dev, + "[OBJECT:%d] is a colorop but DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE not set\n", + obj->id); + ret = -EINVAL; + } struct drm_colorop *colorop = obj_to_colorop(obj); struct drm_colorop_state *colorop_state; @@ -1209,7 +1231,6 @@ int drm_atomic_set_property(struct drm_atomic_state *state, ret = drm_atomic_colorop_set_property(colorop, colorop_state, file_priv, prop, prop_value); - break; } default: diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index f593dc569d31..5c89c586da7c 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -373,6 +373,13 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) return -EINVAL; file_priv->supports_virtualized_cursor_plane = req->value; break; + case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE: + if (!file_priv->atomic) + return -EINVAL; + if (req->value > 1) + return -EINVAL; + file_priv->plane_color_pipeline = req->value; + break; default: return -EINVAL; } diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 94d365b22505..86929ca667aa 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -206,6 +206,13 @@ struct drm_file { */ bool writeback_connectors; + /** +* @plane_color_pipeline: +
[PATCH V8 12/43] Documentation/gpu: document drm_colorop
From: Harry Wentland Add kernel doc for drm_colorop objects. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Move this after "drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE" (Simon Ser) - Update DOC overview (Simon Ser) v7: - Add a commit messages v5: - Drop TODO Documentation/gpu/drm-kms.rst | 15 +++ drivers/gpu/drm/drm_colorop.c | 31 +++ 2 files changed, 46 insertions(+) diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index abfe220764e1..2292e65f044c 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -413,6 +413,21 @@ Plane Panic Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_panic.c :export: +Colorop Abstraction +=== + +.. kernel-doc:: drivers/gpu/drm/drm_colorop.c + :doc: overview + +Colorop Functions Reference +--- + +.. kernel-doc:: include/drm/drm_colorop.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_colorop.c + :export: + Display Modes Function Reference diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 05de9bf118ed..a554c51fa7f5 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -31,6 +31,37 @@ #include "drm_crtc_internal.h" +/** + * DOC: overview + * + * When userspace signals the &DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE it + * should use the COLOR_PIPELINE plane property and associated colorops + * for any color operation on the &drm_plane. Setting of all old color + * properties, such as COLOR_ENCODING and COLOR_RANGE, will be rejected + * and the values of the properties will be ignored. + * + * Colorops are only advertised and valid for atomic drivers and atomic + * userspace that signals the &DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE + * client cap. + * + * A colorop represents a single color operation. Colorops are chained + * via the NEXT property and make up color pipelines. Color pipelines + * are advertised and selected via the COLOR_PIPELINE &drm_plane + * property. + * + * A colorop will be of a certain type, advertised by the read-only TYPE + * property. Each type of colorop will advertise a different set of + * properties and is programmed in a different manner. Types can be + * enumerated 1D curves, 1D LUTs, 3D LUTs, matrices, etc. See the + * &drm_colorop_type documentation for information on each type. + * + * If a colorop advertises the BYPASS property it can be bypassed. + * + * Information about colorop and color pipeline design decisions can be + * found at rfc/color_pipeline.rst, but note that this document will + * grow stale over time. + */ + static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_CURVE, "1D Curve" }, }; -- 2.43.0
[PATCH V8 08/43] drm/colorop: Add NEXT property
From: Harry Wentland We'll construct color pipelines out of drm_colorop by chaining them via the NEXT pointer. NEXT will point to the next drm_colorop in the pipeline, or by 0 if we're at the end of the pipeline. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Remove null check "colorop->next_property" in drm_colorop_set_next_property v5: - move next comment here from Add 3x4 CTM patch (Sebastian) - Add kernel doc v4: - Allow setting of NEXT property to NULL (Chaitanya Kumar Borah) v3: - Add next pointer to colorop to be used by drivers and in DRM core drivers/gpu/drm/drm_colorop.c | 27 +++ include/drm/drm_colorop.h | 17 + 2 files changed, 44 insertions(+) diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 60edf6671643..05de9bf118ed 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -58,6 +58,7 @@ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, colorop->dev = dev; colorop->type = type; colorop->plane = plane; + colorop->next = NULL; list_add_tail(&colorop->head, &config->colorop_list); colorop->index = config->num_colorop++; @@ -90,6 +91,16 @@ static int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop, colorop->bypass_property, 1); + /* next */ + prop = drm_property_create_object(dev, DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_ATOMIC, + "NEXT", DRM_MODE_OBJECT_COLOROP); + if (!prop) + return -ENOMEM; + colorop->next_property = prop; + drm_object_attach_property(&colorop->base, + colorop->next_property, + 0); + return ret; } @@ -263,3 +274,19 @@ const char *drm_get_colorop_curve_1d_type_name(enum drm_colorop_curve_1d_type ty return colorop_curve_1d_type_names[type]; } + +/** + * drm_colorop_set_next_property - sets the next pointer + * @colorop: drm colorop + * @next: next colorop + * + * Should be used when constructing the color pipeline + */ +void drm_colorop_set_next_property(struct drm_colorop *colorop, struct drm_colorop *next) +{ + drm_object_property_set_value(&colorop->base, + colorop->next_property, + next ? next->base.id : 0); + colorop->next = next; +} +EXPORT_SYMBOL(drm_colorop_set_next_property); diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index 315d748e212e..7948de5c4994 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -173,6 +173,14 @@ struct drm_colorop { */ enum drm_colorop_type type; + /** +* @next: +* +* Read-only +* Pointer to next drm_colorop in pipeline +*/ + struct drm_colorop *next; + /** * @type_property: * @@ -200,6 +208,13 @@ struct drm_colorop { */ struct drm_property *curve_1d_type_property; + /** +* @next_property: +* +* Read-only property to next colorop in the pipeline +*/ + struct drm_property *next_property; + }; #define obj_to_colorop(x) container_of(x, struct drm_colorop, base) @@ -276,4 +291,6 @@ const char *drm_get_colorop_type_name(enum drm_colorop_type type); */ const char *drm_get_colorop_curve_1d_type_name(enum drm_colorop_curve_1d_type type); +void drm_colorop_set_next_property(struct drm_colorop *colorop, struct drm_colorop *next); + #endif /* __DRM_COLOROP_H__ */ -- 2.43.0
[PATCH V8 13/43] drm/vkms: Add enumerated 1D curve colorop
From: Harry Wentland This patch introduces a VKMS color pipeline that includes two drm_colorops for named transfer functions. For now the only ones supported are sRGB EOTF, sRGB Inverse EOTF, and a Linear TF. We will expand this in the future but I don't want to do so without accompanying IGT tests. We introduce a new vkms_luts.c file that hard-codes sRGB EOTF, sRGB Inverse EOTF, and a linear EOTF LUT. These have been generated with 256 entries each as IGT is currently testing only 8 bpc surfaces. We will likely need higher precision but I'm reluctant to make that change without clear indication that we need it. We'll revisit and, if necessary, regenerate the LUTs when we have IGT tests for higher precision buffers. Signed-off-by: Harry Wentland Signed-off-by: Alex Hung --- v8: - Replace DRM_ERROR by drm_err (Louis Chauvet) - Replace DRM_WARN_ONCE by drm_WARN_ONCE (Louis Chauvet) - Fix conflicts with upstream VKMS (Louis Chauvet) - Add comments for drm_color_lut linear_array (Louis Chauvet) v7: - Fix checkpatch warnings (Louis Chauvet) - Change kzalloc(sizeof(struct drm_colorop) ...) to kzalloc(sizeof(*ops[i]) ...) - Remove if (ops[i]) before kfree(ops[i]) - Fix styles by adding and removing spaces (new lines, tabs and so on) v6: - drop 'len' var (Louis Chauvet) - cleanup if colorop alloc or init fails (Louis Chauvet) - switch loop in pre_blend_transform (Louis Chauvet) - drop extraneous if (colorop) inside while (colorop) (Louis Chauvet) v5: - Squash with "Pull apply_colorop out of pre_blend_color_transform" (Sebastian) - Fix warnings - Fix include - Drop TODOs v4: - Drop _tf_ from color_pipeline init function - Pass supported TFs into colorop init - Create bypass pipeline in DRM helper (Pekka) v2: - Add commit description - Fix sRGB EOTF LUT definition - Add linear and sRGB inverse EOTF LUTs drivers/gpu/drm/vkms/Makefile| 4 +- drivers/gpu/drm/vkms/vkms_colorop.c | 82 +++ drivers/gpu/drm/vkms/vkms_composer.c | 51 +- drivers/gpu/drm/vkms/vkms_drv.h | 3 + drivers/gpu/drm/vkms/vkms_luts.c | 808 +++ drivers/gpu/drm/vkms/vkms_luts.h | 12 + drivers/gpu/drm/vkms/vkms_plane.c| 2 + 7 files changed, 960 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/vkms/vkms_colorop.c create mode 100644 drivers/gpu/drm/vkms/vkms_luts.c create mode 100644 drivers/gpu/drm/vkms/vkms_luts.h diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile index 490b26d2e69d..357583fcb841 100644 --- a/drivers/gpu/drm/vkms/Makefile +++ b/drivers/gpu/drm/vkms/Makefile @@ -6,7 +6,9 @@ vkms-y := \ vkms_formats.o \ vkms_crtc.o \ vkms_composer.o \ - vkms_writeback.o + vkms_writeback.o \ + vkms_colorop.o \ + vkms_luts.o obj-$(CONFIG_DRM_VKMS) += vkms.o obj-$(CONFIG_DRM_VKMS_KUNIT_TEST) += tests/ diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c new file mode 100644 index ..4fe22368c8e3 --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_colorop.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include +#include +#include + +#include "vkms_drv.h" + +static const u64 supported_tfs = + BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF); + +#define MAX_COLOR_PIPELINE_OPS 2 + +static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list) +{ + struct drm_colorop *ops[MAX_COLOR_PIPELINE_OPS]; + struct drm_device *dev = plane->dev; + int ret; + int i = 0; + + memset(ops, 0, sizeof(ops)); + + /* 1st op: 1d curve */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + drm_err(dev, "KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs); + if (ret) + goto cleanup; + + list->type = ops[i]->base.id; + list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", ops[i]->base.id); + + i++; + + /* 2nd op: 1d curve */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + drm_err(dev, "KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i - 1], ops[i]); + + return 0; + +cleanup: + for (; i >= 0; i--) + kfree(ops[i]); + + return ret; +} + +int vkms_initialize_colorops(struct drm_plane *plane) +{ + struct drm_prop_enum_list pipeline; + int ret; + + /* Add colo
[PATCH V8 15/43] drm/colorop: Add 3x4 CTM type
From: Harry Wentland This type is used to support a 3x4 matrix in colorops. A 3x4 matrix uses the last column as a "bias" column. Some HW exposes support for 3x4. The calculation looks like: out matrixin |R| |0 1 2 3 | | R | |G| = |4 5 6 7 | x | G | |B| |8 9 10 11| | B | |1.0| This is also the first colorop where we need a blob property to program the property. For that we'll introduce a new DATA property that can be used by all colorop TYPEs requiring a blob. The way a DATA blob is read depends on the TYPE of the colorop. We only create the DATA property for property types that need it. Reviewed-by: Simon Ser Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v6: - take ref for DATA blob in duplicate_state func (Xaver Hugl) v5: - Add function signature for init (Sebastian) - Fix kernel-doc v4: - Create helper function for creating 3x4 CTM colorop - Fix CTM indexes in comment (Pekka) drivers/gpu/drm/drm_atomic.c | 3 +++ drivers/gpu/drm/drm_atomic_uapi.c | 30 ++ drivers/gpu/drm/drm_colorop.c | 42 +++ include/drm/drm_colorop.h | 24 ++ include/uapi/drm/amdgpu_drm.h | 9 --- include/uapi/drm/drm_mode.h | 32 ++- 6 files changed, 130 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index e67d7a68e8a8..5223cf363692 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -793,6 +793,9 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, drm_printf(p, "\tcurve_1d_type=%s\n", drm_get_colorop_curve_1d_type_name(state->curve_1d_type)); break; + case DRM_COLOROP_CTM_3X4: + drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); + break; default: break; } diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index e0b4b122ef6b..52c6a4dfbc47 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -703,6 +703,31 @@ drm_atomic_plane_get_property(struct drm_plane *plane, return 0; } +static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, + struct drm_colorop_state *state, + struct drm_property *property, + uint64_t val) +{ + ssize_t elem_size = -1; + ssize_t size = -1; + bool replaced = false; + + switch (colorop->type) { + case DRM_COLOROP_CTM_3X4: + size = sizeof(struct drm_color_ctm_3x4); + break; + default: + /* should never get here */ + return -EINVAL; + } + + return drm_property_replace_blob_from_id(colorop->dev, +&state->data, +val, +size, +elem_size, +&replaced); +} static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, struct drm_colorop_state *state, struct drm_file *file_priv, @@ -712,6 +737,9 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, state->bypass = val; } else if (property == colorop->curve_1d_type_property) { state->curve_1d_type = val; + } else if (property == colorop->data_property) { + return drm_atomic_color_set_data_property(colorop, state, + property, val); } else { drm_dbg_atomic(colorop->dev, "[COLOROP:%d:%d] unknown property [PROP:%d:%s]]\n", @@ -734,6 +762,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->bypass; } else if (property == colorop->curve_1d_type_property) { *val = state->curve_1d_type; + } else if (property == colorop->data_property) { + *val = (state->data) ? state->data->base.id : 0; } else { return -EINVAL; } diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index a554c51fa7f5..8ab754f935e6 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -64,6 +64,7 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_CURVE, "1D Curve" }, + { DRM_COLOROP_CTM_3X4, "3x4 Matrix&quo
[PATCH V8 17/43] drm/vkms: add 3x4 matrix in color pipeline
From: Harry Wentland We add two 3x4 matrices into the VKMS color pipeline. The reason we're adding matrices is so that we can test that application of a matrix and its inverse yields an output equal to the input image. One complication with the matrix implementation has to do with the fact that the matrix entries are in signed-magnitude fixed point, whereas the drm_fixed.h implementation uses 2s-complement. The latter one is the one that we want for easy addition and subtraction, so we convert all entries to 2s-complement. Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Replace DRM_ERROR with drm_err (Louis Chauvet) v7: - Fix checkpatch warnings - Change kzalloc(sizeof(struct drm_colorop) ...) to kzalloc(sizeof(*ops[i]) ...) - Change i-1to i - 1 - Add a new line at EOF v6: - pre-compute colors (Louis Chauvet) - round matrix output (Louis Chauvet) drivers/gpu/drm/vkms/vkms_colorop.c | 34 +++- drivers/gpu/drm/vkms/vkms_composer.c | 33 +++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c index 4fe22368c8e3..0fbb9262bfc5 100644 --- a/drivers/gpu/drm/vkms/vkms_colorop.c +++ b/drivers/gpu/drm/vkms/vkms_colorop.c @@ -12,7 +12,7 @@ static const u64 supported_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF); -#define MAX_COLOR_PIPELINE_OPS 2 +#define MAX_COLOR_PIPELINE_OPS 4 static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list) { @@ -48,6 +48,38 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i - 1], ops[i]); + + i++; + + /* 3rd op: 3x4 matrix */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + drm_err(dev, "KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i - 1], ops[i]); + + i++; + + /* 4th op: 1d curve */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + drm_err(dev, "KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + ret = drm_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs); if (ret) goto cleanup; diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 36f0caabb67b..8fe16d26d680 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -128,6 +128,35 @@ static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buff } } +static void apply_3x4_matrix(struct pixel_argb_s32 *pixel, const struct drm_color_ctm_3x4 *matrix) +{ + s64 rf, gf, bf; + s64 r, g, b; + + r = drm_int2fixp(pixel->r); + g = drm_int2fixp(pixel->g); + b = drm_int2fixp(pixel->b); + + rf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[0]), r) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[1]), g) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[2]), b) + +drm_sm2fixp(matrix->matrix[3]); + + gf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[4]), r) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[5]), g) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[6]), b) + +drm_sm2fixp(matrix->matrix[7]); + + bf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[8]), r) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[9]), g) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[10]), b) + +drm_sm2fixp(matrix->matrix[11]); + + pixel->r = drm_fixp2int_round(rf); + pixel->g = drm_fixp2int_round(gf); + pixel->b = drm_fixp2int_round(bf); +} + static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colorop) { struct drm_colorop_state *colorop_state = colorop->state; @@ -151,6 +180,10 @@ static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colo colorop_state->curve_1d_type); break; } + } else if (colorop->type == DRM_COLOROP_CTM_3X4) { + if (colorop_state->data) + apply_3x4_matrix(pixel, +(struct drm_color_ctm_3x4 *) colorop_state->data->data); } } -- 2.43.0
[PATCH V8 20/43] drm/colorop: pass plane_color_pipeline client cap to atomic check
From: Harry Wentland Drivers will need to know whether an atomic check/commit originated from a client with DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE so they can ignore deprecated properties, like COLOR_ENCODING and COLOR_RANGE. Pass the plane_color_pipeline bit to drm_atomic_state. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v5: - Fix kernel docs drivers/gpu/drm/drm_atomic_uapi.c | 1 + include/drm/drm_atomic.h | 18 ++ 2 files changed, 19 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 52c6a4dfbc47..dcd12fc0bd8f 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -1600,6 +1600,7 @@ int drm_mode_atomic_ioctl(struct drm_device *dev, drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); state->acquire_ctx = &ctx; state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET); + state->plane_color_pipeline = file_priv->plane_color_pipeline; retry: copied_objs = 0; diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 0f76ee3caa9d..2b088cef946a 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -464,6 +464,24 @@ struct drm_atomic_state { */ bool duplicated : 1; + /** +* @plane_color_pipeline: +* +* Indicates whether this atomic state originated with a client that +* set the DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE. +* +* Drivers and helper functions should use this to ignore legacy +* properties that are incompatible with the drm_plane COLOR_PIPELINE +* behavior, such as: +* +* - COLOR_RANGE +* - COLOR_ENCODING +* +* or any other driver-specific properties that might affect pixel +* values. +*/ + bool plane_color_pipeline : 1; + /** * @colorops: * -- 2.43.0
[PATCH V8 16/43] drm/vkms: Use s32 for internal color pipeline precision
From: Harry Wentland Certain operations require us to preserve values below 0.0 and above 1.0 (0x0 and 0x respectively in 16 bpc unorm). One such operation is a BT709 encoding operation followed by its decoding operation, or the reverse. We'll use s32 values as intermediate in and outputs of our color operations, for the operations where it matters. For now this won't apply to LUT operations. We might want to update those to work on s32 as well, but it's unclear how that should work for unorm LUT definitions. We'll revisit that once we add LUT + CTM tests. In order to allow for this we'll also invert the nesting of our colorop processing loops. We now use the pixel iteration loop on the outside and the colorop iteration on the inside. Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix checkpatch warnings - Add a commit messages - Fix code styles by adding and removing spaces (new lines, tabs and so on) v6: - use clamp_val instead of manual clamping (Louis Chauvet) v4: - Clarify that we're packing 16-bit UNORM into s32, not converting values to a different representation (Pekka) v3: - Use new colorop->next pointer drivers/gpu/drm/vkms/vkms_composer.c | 27 +-- drivers/gpu/drm/vkms/vkms_drv.h | 4 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 72890ffea13a..36f0caabb67b 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -128,7 +128,7 @@ static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buff } } -static void apply_colorop(struct pixel_argb_u16 *pixel, struct drm_colorop *colorop) +static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colorop) { struct drm_colorop_state *colorop_state = colorop->state; struct drm_device *dev = colorop->dev; @@ -157,9 +157,26 @@ static void apply_colorop(struct pixel_argb_u16 *pixel, struct drm_colorop *colo static void pre_blend_color_transform(const struct vkms_plane_state *plane_state, struct line_buffer *output_buffer) { + struct pixel_argb_s32 pixel; + for (size_t x = 0; x < output_buffer->n_pixels; x++) { struct drm_colorop *colorop = plane_state->base.base.color_pipeline; + /* +* Some operations, such as applying a BT709 encoding matrix, +* followed by a decoding matrix, require that we preserve +* values above 1.0 and below 0.0 until the end of the pipeline. +* +* Pack the 16-bit UNORM values into s32 to give us head-room to +* avoid clipping until we're at the end of the pipeline. Clip +* intentionally at the end of the pipeline before packing +* UNORM values back into u16. +*/ + pixel.a = output_buffer->pixels[x].a; + pixel.r = output_buffer->pixels[x].r; + pixel.g = output_buffer->pixels[x].g; + pixel.b = output_buffer->pixels[x].b; + while (colorop) { struct drm_colorop_state *colorop_state; @@ -169,10 +186,16 @@ static void pre_blend_color_transform(const struct vkms_plane_state *plane_state return; if (!colorop_state->bypass) - apply_colorop(&output_buffer->pixels[x], colorop); + apply_colorop(&pixel, colorop); colorop = colorop->next; } + + /* clamp values */ + output_buffer->pixels[x].a = clamp_val(pixel.a, 0, 0x); + output_buffer->pixels[x].r = clamp_val(pixel.r, 0, 0x); + output_buffer->pixels[x].g = clamp_val(pixel.g, 0, 0x); + output_buffer->pixels[x].b = clamp_val(pixel.b, 0, 0x); } } diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index 999625de0050..d9196b9db025 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -43,6 +43,10 @@ struct vkms_frame_info { unsigned int rotation; }; +struct pixel_argb_s32 { + s32 a, r, g, b; +}; + struct pixel_argb_u16 { u16 a, r, g, b; }; -- 2.43.0
[PATCH V8 18/43] drm/tests: Add a few tests around drm_fixed.h
From: Harry Wentland While working on the CTM implementation of VKMS I had to ascertain myself of a few assumptions. One of those is whether drm_fixed.h treats its numbers using signed-magnitude or twos-complement. It is twos-complement. In order to make someone else's day easier I am adding the drm_test_int2fixp test that validates the above assumption. I am also adding a test for the new sm2fixp function that converts from a signed-magnitude fixed point to the twos-complement fixed point. Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix checkpatch warnings (Louis Chauvet) v6: - add missing MODULE_DESCRIPTION (Jeff Johnson) - fix buffer overflow drivers/gpu/drm/tests/Makefile| 3 +- drivers/gpu/drm/tests/drm_fixp_test.c | 71 +++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/tests/drm_fixp_test.c diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 0109bcf7faa5..1fbb8e9e2869 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_modes_test.o \ drm_plane_helper_test.o \ drm_probe_helper_test.o \ - drm_rect_test.o + drm_rect_test.o \ + drm_fixp_test.o CFLAGS_drm_mm_test.o := $(DISABLE_STRUCTLEAK_PLUGIN) diff --git a/drivers/gpu/drm/tests/drm_fixp_test.c b/drivers/gpu/drm/tests/drm_fixp_test.c new file mode 100644 index ..de91177af213 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_fixp_test.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright 2022 Advanced Micro Devices, Inc. + */ + +#include +#include + +static void drm_test_sm2fixp(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, 0x7fffll, ((1ull << 63) - 1)); + + /* 1 */ + KUNIT_EXPECT_EQ(test, drm_int2fixp(1), drm_sm2fixp(1ull << DRM_FIXED_POINT)); + + /* -1 */ + KUNIT_EXPECT_EQ(test, drm_int2fixp(-1), + drm_sm2fixp((1ull << 63) | (1ull << DRM_FIXED_POINT))); + + /* 0.5 */ + KUNIT_EXPECT_EQ(test, drm_fixp_from_fraction(1, 2), + drm_sm2fixp(1ull << (DRM_FIXED_POINT - 1))); + + /* -0.5 */ + KUNIT_EXPECT_EQ(test, drm_fixp_from_fraction(-1, 2), + drm_sm2fixp((1ull << 63) | (1ull << (DRM_FIXED_POINT - 1; +} + +static void drm_test_int2fixp(struct kunit *test) +{ + /* 1 */ + KUNIT_EXPECT_EQ(test, 1ll << 32, drm_int2fixp(1)); + + /* -1 */ + KUNIT_EXPECT_EQ(test, -(1ll << 32), drm_int2fixp(-1)); + + /* 1 + (-1) = 0 */ + KUNIT_EXPECT_EQ(test, 0, drm_int2fixp(1) + drm_int2fixp(-1)); + + /* 1 / 2 */ + KUNIT_EXPECT_EQ(test, 1ll << 31, drm_fixp_from_fraction(1, 2)); + + /* -0.5 */ + KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(-1, 2)); + + /* (1 / 2) + (-1) = 0.5 */ + KUNIT_EXPECT_EQ(test, 1ll << 31, drm_fixp_from_fraction(-1, 2) + drm_int2fixp(1)); + + /* (1 / 2) - 1) = 0.5 */ + KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(1, 2) + drm_int2fixp(-1)); + + /* (1 / 2) - 1) = 0.5 */ + KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(1, 2) - drm_int2fixp(1)); +} + +static struct kunit_case drm_fixp_tests[] = { + KUNIT_CASE(drm_test_int2fixp), + KUNIT_CASE(drm_test_sm2fixp), + { } +}; + +static struct kunit_suite drm_rect_test_suite = { + .name = "drm_fixp", + .test_cases = drm_fixp_tests, +}; + +kunit_test_suite(drm_rect_test_suite); + +MODULE_AUTHOR("AMD"); +MODULE_LICENSE("GPL and additional rights"); +MODULE_DESCRIPTION("Unit tests for drm_fixed.h"); -- 2.43.0
[PATCH V8 19/43] drm/vkms: Add tests for CTM handling
From: Harry Wentland A whole slew of tests for CTM handling that greatly helped in debugging the CTM code. The extent of tests might seem a bit silly but they're fast and might someday help save someone else's day when debugging this. Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix a checkpatch a long-line warning (Louis Chauvet) v6: - update reference values since we're now rounding v5: - Make apply_3x4_matrix static v4: - Comment on origin of bt709_enc matrix (Pekka) - Use full opaque alpha (Pekka) - Add additional check for Y < 0x (Pekka) - Remove unused code (Pekka) drivers/gpu/drm/vkms/tests/vkms_color_test.c | 250 +++ drivers/gpu/drm/vkms/vkms_composer.c | 4 +- drivers/gpu/drm/vkms/vkms_composer.h | 1 + 3 files changed, 254 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c index 02fbe19c744a..a9c3af5025b3 100644 --- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c +++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c @@ -190,11 +190,261 @@ static void vkms_color_srgb_inv_srgb(struct kunit *test) } } +#define FIXPT_HALF(DRM_FIXED_ONE >> 1) +#define FIXPT_QUARTER (DRM_FIXED_ONE >> 2) + +static const struct drm_color_ctm_3x4 test_matrix_3x4_50_desat = { { + FIXPT_HALF, FIXPT_QUARTER, FIXPT_QUARTER, 0, + FIXPT_QUARTER, FIXPT_HALF, FIXPT_QUARTER, 0, + FIXPT_QUARTER, FIXPT_QUARTER, FIXPT_HALF, 0 +} }; + +static void vkms_color_ctm_3x4_50_desat(struct kunit *test) +{ + struct pixel_argb_s32 ref, out; + + /* full white */ + ref.a = 0x; + ref.r = 0x; + ref.g = 0x; + ref.b = 0x; + + memcpy(&out, &ref, sizeof(out)); + apply_3x4_matrix(&out, &test_matrix_3x4_50_desat); + + KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out)); + + /* full black */ + ref.a = 0x; + ref.r = 0x0; + ref.g = 0x0; + ref.b = 0x0; + + memcpy(&out, &ref, sizeof(out)); + apply_3x4_matrix(&out, &test_matrix_3x4_50_desat); + + KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out)); + + /* 50% grey */ + ref.a = 0x; + ref.r = 0x8000; + ref.g = 0x8000; + ref.b = 0x8000; + + memcpy(&out, &ref, sizeof(out)); + apply_3x4_matrix(&out, &test_matrix_3x4_50_desat); + + KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out)); + + /* full red to 50% desat */ + ref.a = 0x; + ref.r = 0x8000; + ref.g = 0x4000; + ref.b = 0x4000; + + out.a = 0x; + out.r = 0x; + out.g = 0x0; + out.b = 0x0; + + apply_3x4_matrix(&out, &test_matrix_3x4_50_desat); + + KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out)); +} + +/* + * BT.709 encoding matrix + * + * Values printed from within IGT when converting + * igt_matrix_3x4_bt709_enc to the fixed-point format expected + * by DRM/KMS. + */ +static const struct drm_color_ctm_3x4 test_matrix_3x4_bt709_enc = { { + 0x366cf400ull, 0xb7175900ull, 0x000127bb300ull, 0, + 0x80001993b3a0ull, 0x80005609fe80ull, 0x6f9db200ull, 0, + 0x9d70a400ull, 0x80008f011100ull, 0x8e6f9330ull, 0 +} }; + +static void vkms_color_ctm_3x4_bt709(struct kunit *test) +{ + struct pixel_argb_s32 out; + + /* full white to bt709 */ + out.a = 0x; + out.r = 0x; + out.g = 0x; + out.b = 0x; + + apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc); + + /* Y 255 */ + KUNIT_EXPECT_GT(test, out.r, 0xfe00); + KUNIT_EXPECT_LT(test, out.r, 0x1); + + /* U 0 */ + KUNIT_EXPECT_LT(test, out.g, 0x0100); + + /* V 0 */ + KUNIT_EXPECT_LT(test, out.b, 0x0100); + + /* full black to bt709 */ + out.a = 0x; + out.r = 0x0; + out.g = 0x0; + out.b = 0x0; + + apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc); + + /* Y 0 */ + KUNIT_EXPECT_LT(test, out.r, 0x100); + + /* U 0 */ + KUNIT_EXPECT_LT(test, out.g, 0x0100); + + /* V 0 */ + KUNIT_EXPECT_LT(test, out.b, 0x0100); + + /* gray to bt709 */ + out.a = 0x; + out.r = 0x7fff; + out.g = 0x7fff; + out.b = 0x7fff; + + apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc); + + /* Y 127 */ + KUNIT_EXPECT_GT(test, out.r, 0x7e00); + KUNIT_EXPECT_LT(test, out.r, 0x8000); + + /* U 0 */ + KUNIT_EXPECT_LT(test, out.g, 0x0100); + + /* V 0 */ + KUNIT_EXPECT_LT(test, out.b, 0x0100); + + /* == red 255 - bt709 enc == */ + out.a = 0x; + out.r = 0x; + out.g = 0x0; + out.b = 0x0; + + apply_3x4_ma
[PATCH V8 22/43] drm/amd/display: Ignore deprecated props when plane_color_pipeline set
From: Harry Wentland When the plane_color_pipeline bit is set we should ignore deprecated properties, such as COLOR_RANGE and COLOR_ENCODING. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 4 1 file changed, 4 insertions(+) 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 6a54f1cfa125..b00f760afb29 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -5691,6 +5691,10 @@ fill_plane_color_attributes(const struct drm_plane_state *plane_state, *color_space = COLOR_SPACE_SRGB; + /* Ignore properties when DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set */ + if (plane_state->state && plane_state->state->plane_color_pipeline) + return 0; + /* DRM color properties only affect non-RGB formats. */ if (format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN) return 0; -- 2.43.0
[PATCH V8 21/43] drm/colorop: define a new macro for_each_new_colorop_in_state
Create a new macro for_each_new_colorop_in_state to access new drm_colorop_state updated from uapi. Reviewed-by: Simon Ser Signed-off-by: Alex Hung --- include/drm/drm_atomic.h | 20 1 file changed, 20 insertions(+) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 2b088cef946a..266b2e6d58ea 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -1066,6 +1066,26 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (new_colorop_state) = (__state)->colorops[__i].new_state, 1)) +/** + * for_each_new_colorop_in_state - iterate over all colorops in an atomic update + * @__state: &struct drm_atomic_state pointer + * @colorop: &struct drm_colorop iteration cursor + * @new_colorop_state: &struct drm_colorop_state iteration cursor for the new state + * @__i: int iteration cursor, for macro-internal use + * + * This iterates over all colorops in an atomic update, tracking new state. This is + * useful is useful in places where the state delta needs to be considered, for + * example in atomic check functions. + */ +#define for_each_new_colorop_in_state(__state, colorop, new_colorop_state, __i) \ + for ((__i) = 0; \ +(__i) < (__state)->dev->mode_config.num_colorop; \ +(__i)++) \ + for_each_if ((__state)->colorops[__i].ptr &&\ +((colorop) = (__state)->colorops[__i].ptr, \ + (void)(colorop) /* Only to avoid unused-but-set-variable warning */, \ + (new_colorop_state) = (__state)->colorops[__i].new_state, 1)) + /** * for_each_oldnew_plane_in_state - iterate over all planes in an atomic update * @__state: &struct drm_atomic_state pointer -- 2.43.0
[PATCH V8 23/43] drm/amd/display: Add bypass COLOR PIPELINE
From: Harry Wentland Add the default Bypass pipeline and ensure it passes the kms_colorop test plane-XR30-XR30-bypass. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 19 +++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index 0090e08d5057..9632b8b73e7e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -1784,6 +1784,20 @@ dm_atomic_plane_get_property(struct drm_plane *plane, } #endif +#define MAX_COLOR_PIPELINES 5 + +static int +dm_plane_init_colorops(struct drm_plane *plane) +{ + struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES]; + int len = 0; + + /* Create COLOR_PIPELINE property and attach */ + drm_plane_create_color_pipeline_property(plane, pipelines, len); + + return 0; +} + static const struct drm_plane_funcs dm_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, @@ -1890,7 +1904,12 @@ int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm, #ifdef AMD_PRIVATE_COLOR dm_atomic_plane_attach_color_mgmt_properties(dm, plane); +#else + res = dm_plane_init_colorops(plane); + if (res) + return res; #endif + /* Create (reset) the plane state */ if (plane->funcs->reset) plane->funcs->reset(plane); -- 2.43.0
[PATCH V8 24/43] drm/amd/display: Skip color pipeline initialization for cursor plane
cursor plane does not need to have color pipeline. Signed-off-by: Alex Hung --- v7: - Add a commit messages drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index 9632b8b73e7e..b5b9b0b5da63 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -1792,6 +1792,9 @@ dm_plane_init_colorops(struct drm_plane *plane) struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES]; int len = 0; + if (plane->type == DRM_PLANE_TYPE_CURSOR) + return 0; + /* Create COLOR_PIPELINE property and attach */ drm_plane_create_color_pipeline_property(plane, pipelines, len); -- 2.43.0
[PATCH V8 25/43] drm/amd/display: Add support for sRGB EOTF in DEGAM block
Expose one 1D curve colorop with support for DRM_COLOROP_1D_CURVE_SRGB_EOTF and program HW to perform the sRGB transform when the colorop is not in bypass. With this change the following IGT test passes: kms_colorop --run plane-XR30-XR30-srgb_eotf The color pipeline now consists of a single colorop: 1. 1D curve colorop w/ sRGB EOTF Signed-off-by: Alex Hung Co-developed-by: Harry Wentland Signed-off-by: Harry Wentland --- v8: - Fix incorrect && by || in __set_colorop_in_tf_1d_curve (Leo Li) v7: - Fix checkpatch warnings - Change switch "{ }" position - Delete double ";" - Delete "{ }" for single-line if-statement - Add a new line at EOF - Change SPDX-License-Identifier: GPL-2.0+ from // to /* */ v6: - cleanup if colorop alloc or init fails .../gpu/drm/amd/display/amdgpu_dm/Makefile| 3 +- .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 86 +++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 70 +++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h | 34 .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 10 +++ 5 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c create mode 100644 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile index ab2a97e354da..46158d67ab12 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile @@ -38,7 +38,8 @@ AMDGPUDM = \ amdgpu_dm_pp_smu.o \ amdgpu_dm_psr.o \ amdgpu_dm_replay.o \ - amdgpu_dm_wb.o + amdgpu_dm_wb.o \ + amdgpu_dm_colorop.o ifdef CONFIG_DRM_AMD_DC_FP AMDGPUDM += dc_fpu.o 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 ebabfe3a512f..0b513ab5050f 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 @@ -668,6 +668,18 @@ amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf) } } +static enum dc_transfer_func_predefined +amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf) +{ + switch (tf) { + case DRM_COLOROP_1D_CURVE_SRGB_EOTF: + case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF: + return TRANSFER_FUNCTION_SRGB; + default: + return TRANSFER_FUNCTION_LINEAR; + } +} + static void __to_dc_lut3d_color(struct dc_rgb *rgb, const struct drm_color_lut lut, int bit_precision) @@ -1137,6 +1149,59 @@ __set_dm_plane_degamma(struct drm_plane_state *plane_state, return 0; } +static int +__set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state, + struct drm_colorop_state *colorop_state) +{ + struct dc_transfer_func *tf = &dc_plane_state->in_transfer_func; + struct drm_colorop *colorop = colorop_state->colorop; + struct drm_device *drm = colorop->dev; + + if (colorop->type != DRM_COLOROP_1D_CURVE || + colorop_state->curve_1d_type != DRM_COLOROP_1D_CURVE_SRGB_EOTF) + return -EINVAL; + + if (colorop_state->bypass) { + tf->type = TF_TYPE_BYPASS; + tf->tf = TRANSFER_FUNCTION_LINEAR; + return 0; + } + + drm_dbg(drm, "Degamma colorop with ID: %d\n", colorop->base.id); + + tf->type = TF_TYPE_PREDEFINED; + tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); + + return 0; +} + +static int +__set_dm_plane_colorop_degamma(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 drm_atomic_state *state = plane_state->state; + int i = 0; + + old_colorop = colorop; + + /* 1st op: 1d curve - degamma */ + for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { + if (new_colorop_state->colorop == old_colorop && + new_colorop_state->curve_1d_type == DRM_COLOROP_1D_CURVE_SRGB_EOTF) { + colorop_state = new_colorop_state; + break; + } + } + + if (!colorop_state) + return -EINVAL; + + return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state); +} + static int amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state) @@ -1187,6 +1252,24 @@ amdgpu_dm_plane_set_color_properties(struct
[PATCH V8 29/43] drm/amd/display: Enable support for PQ 125 EOTF and Inverse
From: Harry Wentland This patchset enables support for the PQ_125 EOTF and its inverse on all existing plane 1D curve colorops, i.e., on DEGAM, SHAPER, and BLND blocks. With this patchset the following IGT subtests are passing: kms_colorop --run plane-XR30-XR30-pq_125_eotf kms_colorop --run plane-XR30-XR30-pq_125_inv_eotf kms_colorop --run plane-XR30-XR30-pq_125_eotf-pq_125_inv_eotf kms_colorop --run plane-XR30-XR30-pq_125_eotf-pq_125_inv_eotf-pq_125_eotf Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Move BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) in amdgpu_dm_supported_blnd_tfs from "drm/amd/display: Add support for BT.709 and BT.2020 TFs" (Leo Li) .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 20 +-- .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 9 ++--- 2 files changed, 20 insertions(+), 9 deletions(-) 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 7ce95ddf3851..63044e0296cb 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 @@ -25,6 +25,7 @@ #include "amdgpu.h" #include "amdgpu_mode.h" #include "amdgpu_dm.h" +#include "amdgpu_dm_colorop.h" #include "dc.h" #include "modules/color/color_gamma.h" #include "basics/conversion.h" @@ -675,6 +676,9 @@ amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf) case DRM_COLOROP_1D_CURVE_SRGB_EOTF: case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF: return TRANSFER_FUNCTION_SRGB; + case DRM_COLOROP_1D_CURVE_PQ_125_EOTF: + case DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF: + return TRANSFER_FUNCTION_PQ; default: return TRANSFER_FUNCTION_LINEAR; } @@ -1157,8 +1161,10 @@ __set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state, struct drm_colorop *colorop = colorop_state->colorop; struct drm_device *drm = colorop->dev; - if (colorop->type != DRM_COLOROP_1D_CURVE || - colorop_state->curve_1d_type != DRM_COLOROP_1D_CURVE_SRGB_EOTF) + if (colorop->type != DRM_COLOROP_1D_CURVE) + return -EINVAL; + + if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_degam_tfs)) return -EINVAL; if (colorop_state->bypass) { @@ -1190,7 +1196,7 @@ __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state, /* 1st op: 1d curve - degamma */ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == old_colorop && - new_colorop_state->curve_1d_type == DRM_COLOROP_1D_CURVE_SRGB_EOTF) { + (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_degam_tfs)) { colorop_state = new_colorop_state; break; } @@ -1210,8 +1216,10 @@ __set_colorop_in_shaper_1d_curve(struct dc_plane_state *dc_plane_state, struct drm_colorop *colorop = colorop_state->colorop; struct drm_device *drm = colorop->dev; - if (colorop->type != DRM_COLOROP_1D_CURVE && - colorop_state->curve_1d_type != DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) + if (colorop->type != DRM_COLOROP_1D_CURVE) + return -EINVAL; + + if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) return -EINVAL; if (colorop_state->bypass) { @@ -1247,7 +1255,7 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, /* 2nd op: 1d curve - shaper */ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == old_colorop && - new_colorop_state->curve_1d_type == DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) { + (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) { colorop_state = new_colorop_state; break; } 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 c5154e1430f5..581f41907c79 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 @@ -32,13 +32,16 @@ #include "amdgpu_dm_colorop.h" const u64 amdgpu_dm_supported_degam_tfs = - BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF); + BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF); const u64 amdgpu_dm_supported_shaper_tfs = - BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF); + BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) | +
[PATCH V8 28/43] drm/colorop: Add PQ 125 EOTF and its inverse
From: Harry Wentland The PQ function defines a mapping of code values to nits (cd/m^2). The max code value maps to 10,000 nits. Windows DWM's canonical composition color space (CCCS) defaults to composing SDR contents to 80 nits and uses a float value of 1.0 to represent this. For this reason AMD HW hard-codes a PQ function that is scaled by 125, yielding 80 nit PQ values for 1.0 and 10,000 nits at 125.0. This patch introduces this scaled PQ EOTF and its inverse as 1D curve types. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- drivers/gpu/drm/drm_colorop.c | 2 ++ include/drm/drm_colorop.h | 24 2 files changed, 26 insertions(+) diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 8ab754f935e6..914fa0fa71d1 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -70,6 +70,8 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { static const char * const colorop_curve_1d_type_names[] = { [DRM_COLOROP_1D_CURVE_SRGB_EOTF] = "sRGB EOTF", [DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF] = "sRGB Inverse EOTF", + [DRM_COLOROP_1D_CURVE_PQ_125_EOTF] = "PQ 125 EOTF", + [DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF] = "PQ 125 Inverse EOTF", }; diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index d9149c320bac..ce51ac407c70 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -59,6 +59,30 @@ enum drm_colorop_curve_1d_type { */ DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + /** +* @DRM_COLOROP_1D_CURVE_PQ_125_EOTF: +* +* enum string "PQ 125 EOTF" +* +* The PQ transfer function, scaled by 125.0f, so that 10,000 +* nits correspond to 125.0f. +* +* Transfer characteristics of the PQ function as defined by +* SMPTE ST 2084 (2014) for 10-, 12-, 14-, and 16-bit systems +* and Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system, +* represented by H.273 TransferCharacteristics code point 16. +*/ + DRM_COLOROP_1D_CURVE_PQ_125_EOTF, + + /** +* @DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF: +* +* enum string "PQ 125 Inverse EOTF" +* +* The inverse of DRM_COLOROP_1D_CURVE_PQ_125_EOTF. +*/ + DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF, + /** * @DRM_COLOROP_1D_CURVE_COUNT: * -- 2.43.0
[PATCH V8 32/43] drm/colorop: Add 1D Curve Custom LUT type
We've previously introduced DRM_COLOROP_1D_CURVE for pre-defined 1D curves. But we also have HW that supports custom curves and userspace needs the ability to pass custom curves, aka LUTs. This patch introduces a new colorop type, called DRM_COLOROP_1D_LUT that provides a SIZE property which is used by a driver to advertise the supported SIZE of the LUT, as well as a DATA property which userspace uses to set the LUT. DATA and size function in the same way as current drm_crtc GAMMA and DEGAMMA LUTs. Signed-off-by: Alex Hung Co-developed-by: Harry Wentland Signed-off-by: Harry Wentland --- v8: - Add DRM_MODE_PROP_ATOMIC to drm_property_create_range (Simon Ser) - Change "1D Curve Custom LUT" to "1D LUT" (Simon Ser) v7: - Change "size" to "lut_size" (this affects multiple following commits) - Move "lut_size" from drm_colorop_state to drm_colorop - Modify other files accordingly (i.e. from drm_colorop_state->size to drm_colorop->lut_size) v5: - Add kernel doc - Define SIZE in similar manner to GAMMA_SIZE on drm_crtc (Melissa) drivers/gpu/drm/drm_atomic.c | 4 +++ drivers/gpu/drm/drm_atomic_uapi.c | 5 drivers/gpu/drm/drm_colorop.c | 43 +++ include/drm/drm_colorop.h | 16 include/uapi/drm/drm_mode.h | 14 ++ 5 files changed, 82 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 5223cf363692..f713d177241d 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -793,6 +793,10 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, drm_printf(p, "\tcurve_1d_type=%s\n", drm_get_colorop_curve_1d_type_name(state->curve_1d_type)); break; + case DRM_COLOROP_1D_LUT: + drm_printf(p, "\tsize=%d\n", colorop->lut_size); + drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); + break; case DRM_COLOROP_CTM_3X4: drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); break; diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index dcd12fc0bd8f..dfd88a227da7 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -713,6 +713,9 @@ static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, bool replaced = false; switch (colorop->type) { + case DRM_COLOROP_1D_LUT: + size = colorop->lut_size * sizeof(struct drm_color_lut); + break; case DRM_COLOROP_CTM_3X4: size = sizeof(struct drm_color_ctm_3x4); break; @@ -762,6 +765,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->bypass; } else if (property == colorop->curve_1d_type_property) { *val = state->curve_1d_type; + } else if (property == colorop->lut_size_property) { + *val = colorop->lut_size; } else if (property == colorop->data_property) { *val = (state->data) ? state->data->base.id : 0; } else { diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index fcb4a8d0e38d..15ffbba60b3d 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -64,6 +64,7 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_CURVE, "1D Curve" }, + { DRM_COLOROP_1D_LUT, "1D LUT" }, { DRM_COLOROP_CTM_3X4, "3x4 Matrix"}, }; @@ -225,6 +226,47 @@ static int drm_colorop_create_data_prop(struct drm_device *dev, struct drm_color return 0; } +/** + * drm_colorop_curve_1d_lut_init - Initialize a DRM_COLOROP_1D_LUT + * + * @dev: DRM device + * @colorop: The drm_colorop object to initialize + * @plane: The associated drm_plane + * @lut_size: LUT size supported by driver + * @return zero on success, -E value on failure + */ +int drm_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane, uint32_t lut_size) +{ + struct drm_property *prop; + int ret; + + ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_1D_LUT); + if (ret) + return ret; + + /* initialize 1D LUT only attribute */ + /* LUT size */ + prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_ATOMIC, +"SIZE", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + + colorop->lut_size_property = prop; + drm_object_attach_property(&colorop-&g
[PATCH V8 30/43] drm/colorop: add BT2020/BT709 OETF and Inverse OETF
From: Harry Wentland The BT.709 and BT.2020 OETFs are the same, the only difference being that the BT.2020 variant is defined with more precision for 10 and 12-bit per color encodings. Both are used as encoding functions for video content, and are therefore defined as OETF (opto-electronic transfer function) instead of as EOTF (electro-optical transfer function). Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- drivers/gpu/drm/drm_colorop.c | 2 ++ include/drm/drm_colorop.h | 23 +++ 2 files changed, 25 insertions(+) diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 914fa0fa71d1..fcb4a8d0e38d 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -70,6 +70,8 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { static const char * const colorop_curve_1d_type_names[] = { [DRM_COLOROP_1D_CURVE_SRGB_EOTF] = "sRGB EOTF", [DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF] = "sRGB Inverse EOTF", + [DRM_COLOROP_1D_CURVE_BT2020_INV_OETF] = "BT.2020 Inverse OETF", + [DRM_COLOROP_1D_CURVE_BT2020_OETF] = "BT.2020 OETF", [DRM_COLOROP_1D_CURVE_PQ_125_EOTF] = "PQ 125 EOTF", [DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF] = "PQ 125 Inverse EOTF", }; diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index ce51ac407c70..d3a60c1beed1 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -59,6 +59,29 @@ enum drm_colorop_curve_1d_type { */ DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF, + /** +* @DRM_COLOROP_1D_CURVE_BT2020_INV_OETF: +* +* enum string "BT.2020 Inverse OETF" +* +* The inverse of &DRM_COLOROP_1D_CURVE_BT2020_OETF +*/ + DRM_COLOROP_1D_CURVE_BT2020_INV_OETF, + + /** +* @DRM_COLOROP_1D_CURVE_BT2020_OETF: +* +* enum string "BT.2020 OETF" +* +* The BT.2020/BT.709 transfer function. The BT.709 and BT.2020 +* transfer functions are the same, the only difference is that +* BT.2020 is defined with more precision for 10 and 12-bit +* encodings. +* +* +*/ + DRM_COLOROP_1D_CURVE_BT2020_OETF, + /** * @DRM_COLOROP_1D_CURVE_PQ_125_EOTF: * -- 2.43.0
[PATCH V8 33/43] drm/amd/display: add shaper and blend colorops for 1D Curve Custom LUT
This patch adds colorops for custom 1D LUTs in the SHAPER and BLND HW blocks. With this change the following IGT tests pass: kms_colorop --run plane-XR30-XR30-srgb_inv_eotf_lut kms_colorop --run plane-XR30-XR30-srgb_inv_eotf_lut-srgb_eotf_lut The color pipeline now consists of the following colorops: 1. 1D curve colorop 2. 1D curve colorop 3. 1D LUT 4. 1D curve colorop 5. 1D LUT The 1D curve colorops support sRGB, BT2020, and PQ scaled to 125.0. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Return error when __set_output_tf fails (Leo Li) - v7: - Initialize uint32_t blend_size = 0 by default (kernel test robot) - Modify state->size to colorop->lut_size .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 173 ++ .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 32 2 files changed, 126 insertions(+), 79 deletions(-) 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 1765402bc122..7e81088804fe 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 @@ -1211,38 +1211,6 @@ __set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state, return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state); } -static int -__set_colorop_in_shaper_1d_curve(struct dc_plane_state *dc_plane_state, - struct drm_colorop_state *colorop_state) -{ - struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func; - struct drm_colorop *colorop = colorop_state->colorop; - struct drm_device *drm = colorop->dev; - - if (colorop->type != DRM_COLOROP_1D_CURVE) - return -EINVAL; - - if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) - return -EINVAL; - - if (colorop_state->bypass) { - tf->type = TF_TYPE_BYPASS; - tf->tf = TRANSFER_FUNCTION_LINEAR; - return 0; - } - - drm_dbg(drm, "Shaper colorop with ID: %d\n", colorop->base.id); - - if (colorop->type == DRM_COLOROP_1D_CURVE) { - tf->type = TF_TYPE_DISTRIBUTED_POINTS; - tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); - tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; - return __set_output_tf(tf, 0, 0, false); - } - - return -EINVAL; -} - static int __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state, @@ -1251,64 +1219,66 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, struct drm_colorop *old_colorop; struct drm_colorop_state *colorop_state = NULL, *new_colorop_state; struct drm_atomic_state *state = plane_state->state; - int i = 0; + enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR; + struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func; + const struct drm_color_lut *shaper_lut; + struct drm_device *dev = colorop->dev; + uint32_t shaper_size; + int i = 0, ret = 0; + /* 1D Curve - SHAPER TF */ old_colorop = colorop; - - /* 2nd op: 1d curve - shaper */ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == old_colorop && (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) { colorop_state = new_colorop_state; break; } - - if (new_colorop_state->colorop == old_colorop) { - colorop_state = new_colorop_state; - break; - } } - if (!colorop_state) - return -EINVAL; - - return __set_colorop_in_shaper_1d_curve(dc_plane_state, colorop_state); -} - - -static int -__set_colorop_1d_curve_blend_tf_lut(struct dc_plane_state *dc_plane_state, - struct drm_colorop_state *colorop_state) -{ - - struct dc_transfer_func *tf = &dc_plane_state->blend_tf; - struct drm_colorop *colorop = colorop_state->colorop; - struct drm_device *drm = colorop->dev; - const struct drm_color_lut *blend_lut; - uint32_t blend_size = 0; - - if (colorop->type != DRM_COLOROP_1D_CURVE) - return -EINVAL; + if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_CURVE) { + drm_dbg(dev, "Shaper TF colorop with ID: %d\n", colorop->base.id); + tf->type = TF_TYPE_DISTRIBUTED_POINTS; + tf->tf = default_tf = amdgpu_colorop_tf_to_dc_
[PATCH V8 37/43] drm/amd/display: Swap matrix and multiplier
Swap the order of matrix and multiplier as designed in hardware. Signed-off-by: Alex Hung --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c | 12 ++-- .../drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c| 8 2 files changed, 10 insertions(+), 10 deletions(-) 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 e3ae0c32da2b..1bf40f39bd0c 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 @@ -1484,25 +1484,25 @@ amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state, if (ret) return ret; - /* 3x4 matrix */ + /* Multiplier */ colorop = colorop->next; if (!colorop) { - drm_dbg(dev, "no 3x4 matrix colorop found\n"); + drm_dbg(dev, "no multiplier colorop found\n"); return -EINVAL; } - ret = __set_dm_plane_colorop_3x4_matrix(plane_state, dc_plane_state, colorop); + ret = __set_dm_plane_colorop_multiplier(plane_state, dc_plane_state, colorop); if (ret) return ret; - /* Multiplier */ + /* 3x4 matrix */ colorop = colorop->next; if (!colorop) { - drm_dbg(dev, "no multiplier colorop found\n"); + drm_dbg(dev, "no 3x4 matrix colorop found\n"); return -EINVAL; } - ret = __set_dm_plane_colorop_multiplier(plane_state, dc_plane_state, colorop); + ret = __set_dm_plane_colorop_3x4_matrix(plane_state, dc_plane_state, colorop); if (ret) return ret; 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 33156531d751..d0777691862b 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 @@ -75,7 +75,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr i++; - /* 3x4 matrix */ + /* Multiplier */ ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); if (!ops[i]) { DRM_ERROR("KMS: Failed to allocate colorop\n"); @@ -83,7 +83,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); + ret = drm_colorop_mult_init(dev, ops[i], plane); if (ret) goto cleanup; @@ -91,7 +91,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr i++; - /* Multiplier */ + /* 3x4 matrix */ ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL); if (!ops[i]) { DRM_ERROR("KMS: Failed to allocate colorop\n"); @@ -99,7 +99,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } - ret = drm_colorop_mult_init(dev, ops[i], plane); + ret = drm_colorop_ctm_3x4_init(dev, ops[i], plane); if (ret) goto cleanup; -- 2.43.0
[PATCH V8 35/43] drm/colorop: Add mutliplier type
This introduces a new drm_colorop_type: DRM_COLOROP_MULTIPLIER. It's a simple multiplier to all pixel values. The value is specified via a S31.32 fixed point provided via the "MULTIPLIER" property. Reviewed-by: Simon Ser Signed-off-by: Alex Hung --- v7: - Modify size_property to lut_size_property drivers/gpu/drm/drm_atomic.c | 3 +++ drivers/gpu/drm/drm_atomic_uapi.c | 4 drivers/gpu/drm/drm_colorop.c | 33 +++ include/drm/drm_colorop.h | 16 +++ include/uapi/drm/drm_mode.h | 11 +++ 5 files changed, 67 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index f713d177241d..a04f1c98fef9 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -800,6 +800,9 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, case DRM_COLOROP_CTM_3X4: drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); break; + case DRM_COLOROP_MULTIPLIER: + drm_printf(p, "\tmultiplier=%llu\n", state->multiplier); + break; default: break; } diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index dfd88a227da7..947c18e8bf9b 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -740,6 +740,8 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, state->bypass = val; } else if (property == colorop->curve_1d_type_property) { state->curve_1d_type = val; + } else if (property == colorop->multiplier_property) { + state->multiplier = val; } else if (property == colorop->data_property) { return drm_atomic_color_set_data_property(colorop, state, property, val); @@ -765,6 +767,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->bypass; } else if (property == colorop->curve_1d_type_property) { *val = state->curve_1d_type; + } else if (property == colorop->multiplier_property) { + *val = state->multiplier; } else if (property == colorop->lut_size_property) { *val = colorop->lut_size; } else if (property == colorop->data_property) { diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 15ffbba60b3d..45aa7b059e35 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -66,6 +66,7 @@ static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_CURVE, "1D Curve" }, { DRM_COLOROP_1D_LUT, "1D LUT" }, { DRM_COLOROP_CTM_3X4, "3x4 Matrix"}, + { DRM_COLOROP_MULTIPLIER, "Multiplier"}, }; static const char * const colorop_curve_1d_type_names[] = { @@ -286,6 +287,37 @@ int drm_colorop_ctm_3x4_init(struct drm_device *dev, struct drm_colorop *colorop } EXPORT_SYMBOL(drm_colorop_ctm_3x4_init); +/** + * drm_colorop_mult_init - Initialize a DRM_COLOROP_MULTIPLIER + * + * @dev: DRM device + * @colorop: The drm_colorop object to initialize + * @plane: The associated drm_plane + * @return zero on success, -E value on failure + */ +int drm_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop, + struct drm_plane *plane) +{ + struct drm_property *prop; + int ret; + + ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_MULTIPLIER); + if (ret) + return ret; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, "MULTIPLIER", 0, U64_MAX); + if (!prop) + return -ENOMEM; + + colorop->multiplier_property = prop; + drm_object_attach_property(&colorop->base, colorop->multiplier_property, 0); + + drm_colorop_reset(colorop); + + return 0; +} +EXPORT_SYMBOL(drm_colorop_mult_init); + static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop, struct drm_colorop_state *state) { @@ -377,6 +409,7 @@ static const char * const colorop_type_name[] = { [DRM_COLOROP_1D_CURVE] = "1D Curve", [DRM_COLOROP_1D_LUT] = "1D LUT", [DRM_COLOROP_CTM_3X4] = "3x4 Matrix", + [DRM_COLOROP_MULTIPLIER] = "Multiplier", }; const char *drm_get_colorop_type_name(enum drm_colorop_type type) diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index d66c76033343..159e6d52e8b1 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -146,6 +146,13 @@ struct drm_colorop_state
[PATCH V8 14/43] drm/vkms: Add kunit tests for linear and sRGB LUTs
From: Harry Wentland Two tests are added to VKMS LUT handling: - linear - inv_srgb Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v7: - Fix checkpatch warnings (Louis Chauvet) - Adde a commit messages - Fix code styles by adding and removing spaces (new lines, tabs and so on) drivers/gpu/drm/vkms/tests/vkms_color_test.c | 39 +++- drivers/gpu/drm/vkms/vkms_composer.c | 17 ++--- drivers/gpu/drm/vkms/vkms_composer.h | 13 +++ 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c index affca56cac7b..02fbe19c744a 100644 --- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c +++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c @@ -6,6 +6,7 @@ #include #include "../vkms_drv.h" #include "../vkms_composer.h" +#include "../vkms_luts.h" #define TEST_LUT_SIZE 16 @@ -36,7 +37,6 @@ static const struct vkms_color_lut test_linear_lut = { .channel_value2index_ratio = 0xf000fll }; - static void vkms_color_test_get_lut_index(struct kunit *test) { s64 lut_index; @@ -49,6 +49,19 @@ static void vkms_color_test_get_lut_index(struct kunit *test) lut_index = get_lut_index(&test_linear_lut, test_linear_array[i].red); KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(lut_index), i); } + + KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&srgb_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x101)), 0x1); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x202)), 0x2); + + KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&srgb_inv_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x101)), 0x1); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x202)), 0x2); + + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0xfefe)), 0xfe); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x)), 0xff); } static void vkms_color_test_lerp(struct kunit *test) @@ -155,9 +168,33 @@ static void vkms_color_test_lerp(struct kunit *test) KUNIT_EXPECT_EQ(test, lerp_u16(0x0, 0x1, 0x8000), 0x1); } +static void vkms_color_test_linear(struct kunit *test) +{ + for (int i = 0; i < LUT_SIZE; i++) { + int linear = apply_lut_to_channel_value(&linear_eotf, i * 0x101, LUT_RED); + + KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(linear, 0x101), i); + } +} + +static void vkms_color_srgb_inv_srgb(struct kunit *test) +{ + u16 srgb, final; + + for (int i = 0; i < LUT_SIZE; i++) { + srgb = apply_lut_to_channel_value(&srgb_eotf, i * 0x101, LUT_RED); + final = apply_lut_to_channel_value(&srgb_inv_eotf, srgb, LUT_RED); + + KUNIT_EXPECT_GE(test, final / 0x101, i - 1); + KUNIT_EXPECT_LE(test, final / 0x101, i + 1); + } +} + static struct kunit_case vkms_color_test_cases[] = { KUNIT_CASE(vkms_color_test_get_lut_index), KUNIT_CASE(vkms_color_test_lerp), + KUNIT_CASE(vkms_color_test_linear), + KUNIT_CASE(vkms_color_srgb_inv_srgb), {} }; diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 3ab829556db2..72890ffea13a 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -82,19 +82,8 @@ VISIBLE_IF_KUNIT s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel } EXPORT_SYMBOL_IF_KUNIT(get_lut_index); -/* - * This enum is related to the positions of the variables inside - * `struct drm_color_lut`, so the order of both needs to be the same. - */ -enum lut_channel { - LUT_RED = 0, - LUT_GREEN, - LUT_BLUE, - LUT_RESERVED -}; - -static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value, - enum lut_channel channel) +VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value, + enum lut_channel channel) { s64 lut_index = get_lut_index(lut, channel_value); u16 *floor_lut_value, *ceil_lut_value; @@ -119,6 +108,8 @@ static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 chan return lerp_u16(floor_channel_value, ceil_channel_value, lut_index & DRM_FIXED_DECIMAL_MASK); } +EXPORT_SYMBOL_IF_KUNIT(apply_lut_to_channel_value); + static void apply_lut(const
Re: [PATCH V8 24/43] drm/amd/display: Skip color pipeline initialization for cursor plane
On 3/31/25 12:53, Xaver Hugl wrote: Cursor plane has no color pipeline and thus it has no colorop either. It inherits color processing from its parent plane. Just to be sure: That means amdgpu will reject atomic commits that try to set a color pipeline on the primary plane while showing the cursor plane on top of it? Just like with scaling? In theory that should be the case, and I will investigate and confirm it. Is this a beavhiour expected by a compositor?
Re: [PATCH V8 03/43] drm/doc/rfc: Describe why prescriptive color pipeline is needed
On 3/31/25 10:24, Shengyu Qu wrote: 在 2025/3/27 7:46, Alex Hung 写道: From: Harry Wentland Add documentation for color pipeline API. Signed-off-by: Alex Hung Signed-off-by: Harry Wentland --- v8: - Fix typo "definint" -> "defining" v7: - Add a commit messages v5: - Don't require BYPASS to succeed (Sebastian) - use DATA for 1D and 3D LUT types (Sebastian) - update 3DLUT ops to use 3DLUT_MODES and 3DLUT_MODE_INDEX - Add section on drm_colorop extensibility - Add color_pipeline.rst to RFC toc tree v4: - Drop IOCTL docs since we dropped the IOCTLs (Pekka) - Clarify reading and setting of COLOR_PIPELINE prop (Pekka) - Add blurb about not requiring to reject a pipeline due to incompatible ops, as long as op can be bypassed (Pekka) - Dropped informational strings (such as input CSC) as they're not actually intended to be advertised (Pekka) v3: - Describe DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE (Sebastian) - Ask for clear documentation of colorop behavior (Sebastian) v2: - Update colorop visualizations to match reality (Sebastian, Alex Hung) - Updated wording (Pekka) - Change BYPASS wording to make it non-mandatory (Sebastian) - Drop cover-letter-like paragraph from COLOR_PIPELINE Plane Property section (Pekka) - Use PQ EOTF instead of its inverse in Pipeline Programming example (Melissa) - Add "Driver Implementer's Guide" section (Pekka) - Add "Driver Forward/Backward Compatibility" section (Sebastian, Pekka) Documentation/gpu/rfc/color_pipeline.rst | 378 +++ Documentation/gpu/rfc/index.rst | 3 + 2 files changed, 381 insertions(+) create mode 100644 Documentation/gpu/rfc/color_pipeline.rst diff --git a/Documentation/gpu/rfc/color_pipeline.rst b/Documentation/ gpu/rfc/color_pipeline.rst new file mode 100644 index ..58bcc2a5ffd8 --- /dev/null +++ b/Documentation/gpu/rfc/color_pipeline.rst @@ -0,0 +1,378 @@ +.. SPDX-License-Identifier: GPL-2.0 + + +Linux Color Pipeline API + + +What problem are we solving? + + +We would like to support pre-, and post-blending complex color +transformations in display controller hardware in order to allow for +HW-supported HDR use-cases, as well as to provide support to +color-managed applications, such as video or image editors. + +It is possible to support an HDR output on HW supporting the Colorspace +and HDR Metadata drm_connector properties, but that requires the +compositor or application to render and compose the content into one +final buffer intended for display. Doing so is costly. + +Most modern display HW offers various 1D LUTs, 3D LUTs, matrices, and other +operations to support color transformations. These operations are often +implemented in fixed-function HW and therefore much more power efficient than +performing similar operations via shaders or CPU. + +We would like to make use of this HW functionality to support complex color +transformations with no, or minimal CPU or shader load. + + +How are other OSes solving this problem? + + +The most widely supported use-cases regard HDR content, whether video or +gaming. + +Most OSes will specify the source content format (color gamut, encoding transfer +function, and other metadata, such as max and average light levels) to a driver. +Drivers will then program their fixed-function HW accordingly to map from a +source content buffer's space to a display's space. + +When fixed-function HW is not available the compositor will assemble a shader to +ask the GPU to perform the transformation from the source content format to the +display's format. + +A compositor's mapping function and a driver's mapping function are usually +entirely separate concepts. On OSes where a HW vendor has no insight into +closed-source compositor code such a vendor will tune their color management +code to visually match the compositor's. On other OSes, where both mapping +functions are open to an implementer they will ensure both mappings match. + +This results in mapping algorithm lock-in, meaning that no-one alone can +experiment with or introduce new mapping algorithms and achieve +consistent results regardless of which implementation path is taken. + +Why is Linux different? +=== + +Unlike other OSes, where there is one compositor for one or more drivers, on +Linux we have a many-to-many relationship. Many compositors; many drivers. +In addition each compositor vendor or community has their own view of how +color management should be done. This is what makes Linux so beautiful. + +This means that a HW vendor can now no longer tune their driver to one +compositor, as tuning it to one could make it look fairly different from +another compositor's color mapping. + +We need a b
[PATCH V9 13/43] drm/colorop: Add destroy functions for color pipeline
The functions are to clean up color pipeline when a device driver fails to create its color pipeline. Signed-off-by: Alex Hung Reviewed-by: Daniel Stone --- v9: - Move from from latest commit to here, and drm_colorop_pipeline_destroy is called in respective commits. drivers/gpu/drm/drm_colorop.c | 36 +++ include/drm/drm_colorop.h | 2 ++ 2 files changed, 38 insertions(+) diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 7b3ecf7ddd11..57a8c1063fdd 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -135,6 +135,42 @@ static int drm_plane_colorop_init(struct drm_device *dev, struct drm_colorop *co return ret; } +/** + * drm_colorop_cleanup - Cleanup a drm_colorop object in color_pipeline + * + * @colorop: The drm_colorop object to be cleaned + */ +static void drm_colorop_cleanup(struct drm_colorop *colorop) +{ + struct drm_device *dev = colorop->dev; + struct drm_mode_config *config = &dev->mode_config; + + list_del(&colorop->head); + config->num_colorop--; + + kfree(colorop->state); +} + +/** + * drm_colorop_pipeline_destroy - Helper for color pipeline destruction + * + * @plane: - The drm_plane structure containing the color_pipeline + * + * Provides a default color pipeline destroy handler for a planes. + */ +void drm_colorop_pipeline_destroy(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + struct drm_colorop *colorop, *next; + + list_for_each_entry_safe(colorop, next, &config->colorop_list, head) { + drm_colorop_cleanup(colorop); + kfree(colorop); + } +} +EXPORT_SYMBOL(drm_colorop_pipeline_destroy); + /** * drm_plane_colorop_curve_1d_init - Initialize a DRM_COLOROP_1D_CURVE * diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index 791ab940c158..8e4d79ba1eff 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -239,6 +239,8 @@ static inline struct drm_colorop *drm_colorop_find(struct drm_device *dev, return mo ? obj_to_colorop(mo) : NULL; } +void drm_colorop_pipeline_destroy(struct drm_plane *plane); + int drm_plane_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop *colorop, struct drm_plane *plane, u64 supported_tfs); -- 2.43.0
[PATCH V9 10/43] drm/plane: Add COLOR PIPELINE property
From: Harry Wentland We're adding a new enum COLOR PIPELINE property. This property will have entries for each COLOR PIPELINE by referencing the DRM object ID of the first drm_colorop of the pipeline. 0 disables the entire COLOR PIPELINE. Userspace can use this to discover the available color pipelines, as well as set the desired one. The color pipelines are programmed via properties on the actual drm_colorop objects. Reviewed-by: Simon Ser Signed-off-by: Alex Hung Signed-off-by: Harry Wentland Reviewed-by: Daniel Stone --- V9: - Remove redundant comments (Simon Ser) v8: - Use upper case in drm_dbg_atomic (Simon Ser) - Remove MAX_COLOR_PIPELINES (Simon Ser) - Make pipelines const in drm_plane_create_color_pipeline_property (Simon Ser) v7: - Fix a checkpatch warning - a new line after variable declaration v4: - Add pipeline property creation helper (Pekka) - Fix function comment for drm_atomic_set_colorop_for_plane (Pekka) - Always create Bypass pipeline (Pekka) - Add missing function declaration (Chaitanya Kumar Borah) drivers/gpu/drm/drm_atomic.c | 46 ++ drivers/gpu/drm/drm_atomic_state_helper.c | 5 ++ drivers/gpu/drm/drm_atomic_uapi.c | 42 drivers/gpu/drm/drm_plane.c | 59 +++ include/drm/drm_atomic.h | 3 ++ include/drm/drm_atomic_uapi.h | 2 + include/drm/drm_plane.h | 11 + 7 files changed, 168 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index dc679c8b904e..7b042c38d50d 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1531,6 +1531,52 @@ drm_atomic_add_affected_planes(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_add_affected_planes); +/** + * drm_atomic_add_affected_colorops - add colorops for plane + * @state: atomic state + * @plane: DRM plane + * + * This function walks the current configuration and adds all colorops + * currently used by @plane to the atomic configuration @state. This is useful + * when an atomic commit also needs to check all currently enabled colorop on + * @plane, e.g. when changing the mode. It's also useful when re-enabling a plane + * to avoid special code to force-enable all colorops. + * + * Since acquiring a colorop state will always also acquire the w/w mutex of the + * current plane for that colorop (if there is any) adding all the colorop states for + * a plane will not reduce parallelism of atomic updates. + * + * Returns: + * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK + * then the w/w mutex code has detected a deadlock and the entire atomic + * sequence must be restarted. All other errors are fatal. + */ +int +drm_atomic_add_affected_colorops(struct drm_atomic_state *state, +struct drm_plane *plane) +{ + struct drm_colorop *colorop; + struct drm_colorop_state *colorop_state; + + WARN_ON(!drm_atomic_get_new_plane_state(state, plane)); + + drm_dbg_atomic(plane->dev, + "Adding all current colorops for [PLANE:%d:%s] to %p\n", + plane->base.id, plane->name, state); + + drm_for_each_colorop(colorop, plane->dev) { + if (colorop->plane != plane) + continue; + + colorop_state = drm_atomic_get_colorop_state(state, colorop); + if (IS_ERR(colorop_state)) + return PTR_ERR(colorop_state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_add_affected_colorops); + /** * drm_atomic_check_only - check whether a given config would work * @state: atomic configuration to check diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 519228eb1095..d1dd082b1286 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -267,6 +267,11 @@ void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state, plane_state->color_range = val; } + if (plane->color_pipeline_property) { + /* default is always NULL, i.e., bypass */ + plane_state->color_pipeline = NULL; + } + if (plane->zpos_property) { if (!drm_object_property_get_default_value(&plane->base, plane->zpos_property, diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index e5da00fdbca0..0b2b3242c976 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -257,6 +257,36 @@ drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, } EXPORT_SYMBOL(drm_atomic_set_fb_for_plane); + +/** + * drm_atomic_set_colorop_for_plane - set colorop for
[PATCH V9 11/43] drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE
From: Harry Wentland With the introduction of the pre-blending color pipeline we can no longer have color operations that don't have a clear position in the color pipeline. We deprecate all existing plane properties. For upstream drivers those are: - COLOR_ENCODING - COLOR_RANGE Drivers are expected to ignore these properties when programming the HW. DRM clients that register with DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE will not be allowed to set the COLOR_ENCODING and COLOR_RANGE properties. Setting of the COLOR_PIPELINE plane property or drm_colorop properties is only allowed for userspace that sets this client cap. Reviewed-by: Simon Ser Signed-off-by: Alex Hung Signed-off-by: Harry Wentland Reviewed-by: Daniel Stone --- V9: - Fix typo in commit description (Shengyu Qu) v8: - Disallow setting of COLOR_RANGE and COLOR_ENCODING when DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set v5: - Fix kernel docs v4: - Don't block setting of COLOR_RANGE and COLOR_ENCODING when client cap is set drivers/gpu/drm/drm_atomic_uapi.c | 23 ++- drivers/gpu/drm/drm_ioctl.c | 7 +++ include/drm/drm_file.h| 7 +++ include/uapi/drm/drm.h| 15 +++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 0b2b3242c976..c0bcaec249de 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -567,10 +567,26 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, } else if (property == plane->zpos_property) { state->zpos = val; } else if (property == plane->color_encoding_property) { + if (file_priv->plane_color_pipeline) { + drm_dbg_atomic(dev, + "Setting COLOR_ENCODING plane property not permitted with DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE client cap\n"); + return -EINVAL; + } state->color_encoding = val; } else if (property == plane->color_range_property) { + if (file_priv->plane_color_pipeline) { + drm_dbg_atomic(dev, + "Setting COLOR_RANGE plane property not permitted with DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE client cap\n"); + return -EINVAL; + } state->color_range = val; } else if (property == plane->color_pipeline_property) { + if (!file_priv->plane_color_pipeline) { + drm_dbg_atomic(dev, + "Setting COLOR_PIPELINE plane property not permitted unless DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set\n"); + return -EINVAL; + } + /* find DRM colorop object */ struct drm_colorop *colorop = NULL; @@ -1200,6 +1216,12 @@ int drm_atomic_set_property(struct drm_atomic_state *state, break; } case DRM_MODE_OBJECT_COLOROP: { + if (!file_priv->plane_color_pipeline) { + drm_dbg_atomic(prop->dev, + "[OBJECT:%d] is a colorop but DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE not set\n", + obj->id); + ret = -EINVAL; + } struct drm_colorop *colorop = obj_to_colorop(obj); struct drm_colorop_state *colorop_state; @@ -1212,7 +1234,6 @@ int drm_atomic_set_property(struct drm_atomic_state *state, ret = drm_atomic_colorop_set_property(colorop, colorop_state, file_priv, prop, prop_value); - break; } default: diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index f593dc569d31..5c89c586da7c 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -373,6 +373,13 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) return -EINVAL; file_priv->supports_virtualized_cursor_plane = req->value; break; + case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE: + if (!file_priv->atomic) + return -EINVAL; + if (req->value > 1) + return -EINVAL; + file_priv->plane_color_pipeline = req->value; + break; default: return -EINVAL; } diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 94d365b22505..86929ca667aa 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -206,6 +206,13 @@ struct drm_file { */ bool writebac
[PATCH V9 12/43] Documentation/gpu: document drm_colorop
From: Harry Wentland Add kernel doc for drm_colorop objects. Reviewed-by: Simon Ser Signed-off-by: Alex Hung Signed-off-by: Harry Wentland Reviewed-by: Daniel Stone --- v8: - Move this after "drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE" (Simon Ser) - Update DOC overview (Simon Ser) v7: - Add a commit messages v5: - Drop TODO Documentation/gpu/drm-kms.rst | 15 +++ drivers/gpu/drm/drm_colorop.c | 31 +++ 2 files changed, 46 insertions(+) diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index abfe220764e1..2292e65f044c 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -413,6 +413,21 @@ Plane Panic Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_panic.c :export: +Colorop Abstraction +=== + +.. kernel-doc:: drivers/gpu/drm/drm_colorop.c + :doc: overview + +Colorop Functions Reference +--- + +.. kernel-doc:: include/drm/drm_colorop.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_colorop.c + :export: + Display Modes Function Reference diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 43cdd68a69a2..7b3ecf7ddd11 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -31,6 +31,37 @@ #include "drm_crtc_internal.h" +/** + * DOC: overview + * + * When userspace signals the &DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE it + * should use the COLOR_PIPELINE plane property and associated colorops + * for any color operation on the &drm_plane. Setting of all old color + * properties, such as COLOR_ENCODING and COLOR_RANGE, will be rejected + * and the values of the properties will be ignored. + * + * Colorops are only advertised and valid for atomic drivers and atomic + * userspace that signals the &DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE + * client cap. + * + * A colorop represents a single color operation. Colorops are chained + * via the NEXT property and make up color pipelines. Color pipelines + * are advertised and selected via the COLOR_PIPELINE &drm_plane + * property. + * + * A colorop will be of a certain type, advertised by the read-only TYPE + * property. Each type of colorop will advertise a different set of + * properties and is programmed in a different manner. Types can be + * enumerated 1D curves, 1D LUTs, 3D LUTs, matrices, etc. See the + * &drm_colorop_type documentation for information on each type. + * + * If a colorop advertises the BYPASS property it can be bypassed. + * + * Information about colorop and color pipeline design decisions can be + * found at rfc/color_pipeline.rst, but note that this document will + * grow stale over time. + */ + static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = { { DRM_COLOROP_1D_CURVE, "1D Curve" }, }; -- 2.43.0
[PATCH V9 14/43] drm/vkms: Add enumerated 1D curve colorop
From: Harry Wentland This patch introduces a VKMS color pipeline that includes two drm_colorops for named transfer functions. For now the only ones supported are sRGB EOTF, sRGB Inverse EOTF, and a Linear TF. We will expand this in the future but I don't want to do so without accompanying IGT tests. We introduce a new vkms_luts.c file that hard-codes sRGB EOTF, sRGB Inverse EOTF, and a linear EOTF LUT. These have been generated with 256 entries each as IGT is currently testing only 8 bpc surfaces. We will likely need higher precision but I'm reluctant to make that change without clear indication that we need it. We'll revisit and, if necessary, regenerate the LUTs when we have IGT tests for higher precision buffers. Signed-off-by: Harry Wentland Signed-off-by: Alex Hung Reviewed-by: Daniel Stone --- V9: - Replace cleanup code by drm_colorop_pipeline_destroy (Simon Ser) - Update function names by _plane_ (Chaitanya Kumar Borah) v8: - Replace DRM_ERROR by drm_err (Louis Chauvet) - Replace DRM_WARN_ONCE by drm_WARN_ONCE (Louis Chauvet) - Fix conflicts with upstream VKMS (Louis Chauvet) - Add comments for drm_color_lut linear_array (Louis Chauvet) v7: - Fix checkpatch warnings (Louis Chauvet) - Change kzalloc(sizeof(struct drm_colorop) ...) to kzalloc(sizeof(*ops[i]) ...) - Remove if (ops[i]) before kfree(ops[i]) - Fix styles by adding and removing spaces (new lines, tabs and so on) v6: - drop 'len' var (Louis Chauvet) - cleanup if colorop alloc or init fails (Louis Chauvet) - switch loop in pre_blend_transform (Louis Chauvet) - drop extraneous if (colorop) inside while (colorop) (Louis Chauvet) v5: - Squash with "Pull apply_colorop out of pre_blend_color_transform" (Sebastian) - Fix warnings - Fix include - Drop TODOs v4: - Drop _tf_ from color_pipeline init function - Pass supported TFs into colorop init - Create bypass pipeline in DRM helper (Pekka) v2: - Add commit description - Fix sRGB EOTF LUT definition - Add linear and sRGB inverse EOTF LUTs drivers/gpu/drm/vkms/Makefile| 4 +- drivers/gpu/drm/vkms/vkms_colorop.c | 81 +++ drivers/gpu/drm/vkms/vkms_composer.c | 51 +- drivers/gpu/drm/vkms/vkms_drv.h | 3 + drivers/gpu/drm/vkms/vkms_luts.c | 808 +++ drivers/gpu/drm/vkms/vkms_luts.h | 12 + drivers/gpu/drm/vkms/vkms_plane.c| 2 + 7 files changed, 959 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/vkms/vkms_colorop.c create mode 100644 drivers/gpu/drm/vkms/vkms_luts.c create mode 100644 drivers/gpu/drm/vkms/vkms_luts.h diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile index d657865e573f..0b8936674f69 100644 --- a/drivers/gpu/drm/vkms/Makefile +++ b/drivers/gpu/drm/vkms/Makefile @@ -8,7 +8,9 @@ vkms-y := \ vkms_composer.o \ vkms_writeback.o \ vkms_connector.o \ - vkms_config.o + vkms_config.o \ + vkms_colorop.o \ + vkms_luts.o obj-$(CONFIG_DRM_VKMS) += vkms.o obj-$(CONFIG_DRM_VKMS_KUNIT_TEST) += tests/ diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c new file mode 100644 index ..2fb74b2bd001 --- /dev/null +++ b/drivers/gpu/drm/vkms/vkms_colorop.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include +#include +#include + +#include "vkms_drv.h" + +static const u64 supported_tfs = + BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF); + +#define MAX_COLOR_PIPELINE_OPS 2 + +static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list) +{ + struct drm_colorop *ops[MAX_COLOR_PIPELINE_OPS]; + struct drm_device *dev = plane->dev; + int ret; + int i = 0; + + memset(ops, 0, sizeof(ops)); + + /* 1st op: 1d curve */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + drm_err(dev, "KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs); + if (ret) + goto cleanup; + + list->type = ops[i]->base.id; + list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", ops[i]->base.id); + + i++; + + /* 2nd op: 1d curve */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + drm_err(dev, "KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i - 1], ops[i]); + + return 0; + +cleanup: + drm_colorop_pipeline_destroy(plane); + +
[PATCH V9 17/43] drm/vkms: Use s32 for internal color pipeline precision
From: Harry Wentland Certain operations require us to preserve values below 0.0 and above 1.0 (0x0 and 0x respectively in 16 bpc unorm). One such operation is a BT709 encoding operation followed by its decoding operation, or the reverse. We'll use s32 values as intermediate in and outputs of our color operations, for the operations where it matters. For now this won't apply to LUT operations. We might want to update those to work on s32 as well, but it's unclear how that should work for unorm LUT definitions. We'll revisit that once we add LUT + CTM tests. In order to allow for this we'll also invert the nesting of our colorop processing loops. We now use the pixel iteration loop on the outside and the colorop iteration on the inside. Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland Reviewed-by: Daniel Stone --- v7: - Fix checkpatch warnings - Add a commit messages - Fix code styles by adding and removing spaces (new lines, tabs and so on) v6: - use clamp_val instead of manual clamping (Louis Chauvet) v4: - Clarify that we're packing 16-bit UNORM into s32, not converting values to a different representation (Pekka) v3: - Use new colorop->next pointer drivers/gpu/drm/vkms/vkms_composer.c | 27 +-- drivers/gpu/drm/vkms/vkms_drv.h | 4 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 72890ffea13a..36f0caabb67b 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -128,7 +128,7 @@ static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buff } } -static void apply_colorop(struct pixel_argb_u16 *pixel, struct drm_colorop *colorop) +static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colorop) { struct drm_colorop_state *colorop_state = colorop->state; struct drm_device *dev = colorop->dev; @@ -157,9 +157,26 @@ static void apply_colorop(struct pixel_argb_u16 *pixel, struct drm_colorop *colo static void pre_blend_color_transform(const struct vkms_plane_state *plane_state, struct line_buffer *output_buffer) { + struct pixel_argb_s32 pixel; + for (size_t x = 0; x < output_buffer->n_pixels; x++) { struct drm_colorop *colorop = plane_state->base.base.color_pipeline; + /* +* Some operations, such as applying a BT709 encoding matrix, +* followed by a decoding matrix, require that we preserve +* values above 1.0 and below 0.0 until the end of the pipeline. +* +* Pack the 16-bit UNORM values into s32 to give us head-room to +* avoid clipping until we're at the end of the pipeline. Clip +* intentionally at the end of the pipeline before packing +* UNORM values back into u16. +*/ + pixel.a = output_buffer->pixels[x].a; + pixel.r = output_buffer->pixels[x].r; + pixel.g = output_buffer->pixels[x].g; + pixel.b = output_buffer->pixels[x].b; + while (colorop) { struct drm_colorop_state *colorop_state; @@ -169,10 +186,16 @@ static void pre_blend_color_transform(const struct vkms_plane_state *plane_state return; if (!colorop_state->bypass) - apply_colorop(&output_buffer->pixels[x], colorop); + apply_colorop(&pixel, colorop); colorop = colorop->next; } + + /* clamp values */ + output_buffer->pixels[x].a = clamp_val(pixel.a, 0, 0x); + output_buffer->pixels[x].r = clamp_val(pixel.r, 0, 0x); + output_buffer->pixels[x].g = clamp_val(pixel.g, 0, 0x); + output_buffer->pixels[x].b = clamp_val(pixel.b, 0, 0x); } } diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index 2e192b1e5be8..5b340ffbf7d0 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -45,6 +45,10 @@ struct vkms_frame_info { unsigned int rotation; }; +struct pixel_argb_s32 { + s32 a, r, g, b; +}; + struct pixel_argb_u16 { u16 a, r, g, b; }; -- 2.43.0
[PATCH V9 16/43] drm/colorop: Add 3x4 CTM type
From: Harry Wentland This type is used to support a 3x4 matrix in colorops. A 3x4 matrix uses the last column as a "bias" column. Some HW exposes support for 3x4. The calculation looks like: out matrixin |R| |0 1 2 3 | | R | |G| = |4 5 6 7 | x | G | |B| |8 9 10 11| | B | |1.0| This is also the first colorop where we need a blob property to program the property. For that we'll introduce a new DATA property that can be used by all colorop TYPEs requiring a blob. The way a DATA blob is read depends on the TYPE of the colorop. We only create the DATA property for property types that need it. Reviewed-by: Simon Ser Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland Reviewed-by: Daniel Stone --- v9: - Merge cleanup code for colorop->state->data in drm_colorop_cleanup (Simon Ser) - Update function names by _plane_ (Chaitanya Kumar Borah) v6: - take ref for DATA blob in duplicate_state func (Xaver Hugl) v5: - Add function signature for init (Sebastian) - Fix kernel-doc v4: - Create helper function for creating 3x4 CTM colorop - Fix CTM indexes in comment (Pekka) drivers/gpu/drm/drm_atomic.c | 3 ++ drivers/gpu/drm/drm_atomic_uapi.c | 30 drivers/gpu/drm/drm_colorop.c | 47 +++ include/drm/drm_colorop.h | 24 include/uapi/drm/amdgpu_drm.h | 9 -- include/uapi/drm/drm_mode.h | 32 - 6 files changed, 135 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 7b042c38d50d..809bd856d378 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -793,6 +793,9 @@ static void drm_atomic_colorop_print_state(struct drm_printer *p, drm_printf(p, "\tcurve_1d_type=%s\n", drm_get_colorop_curve_1d_type_name(state->curve_1d_type)); break; + case DRM_COLOROP_CTM_3X4: + drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0); + break; default: break; } diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index c0bcaec249de..be73cb9f502e 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -702,6 +702,31 @@ drm_atomic_plane_get_property(struct drm_plane *plane, return 0; } +static int drm_atomic_color_set_data_property(struct drm_colorop *colorop, + struct drm_colorop_state *state, + struct drm_property *property, + uint64_t val) +{ + ssize_t elem_size = -1; + ssize_t size = -1; + bool replaced = false; + + switch (colorop->type) { + case DRM_COLOROP_CTM_3X4: + size = sizeof(struct drm_color_ctm_3x4); + break; + default: + /* should never get here */ + return -EINVAL; + } + + return drm_property_replace_blob_from_id(colorop->dev, +&state->data, +val, +size, +elem_size, +&replaced); +} static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, struct drm_colorop_state *state, struct drm_file *file_priv, @@ -711,6 +736,9 @@ static int drm_atomic_colorop_set_property(struct drm_colorop *colorop, state->bypass = val; } else if (property == colorop->curve_1d_type_property) { state->curve_1d_type = val; + } else if (property == colorop->data_property) { + return drm_atomic_color_set_data_property(colorop, state, + property, val); } else { drm_dbg_atomic(colorop->dev, "[COLOROP:%d:%d] unknown property [PROP:%d:%s]]\n", @@ -733,6 +761,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop, *val = state->bypass; } else if (property == colorop->curve_1d_type_property) { *val = state->curve_1d_type; + } else if (property == colorop->data_property) { + *val = (state->data) ? state->data->base.id : 0; } else { return -EINVAL; } diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 57a8c1063fdd..65351aaa7771 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -64,6 +64,7 @@
[PATCH V9 15/43] drm/vkms: Add kunit tests for linear and sRGB LUTs
From: Harry Wentland Two tests are added to VKMS LUT handling: - linear - inv_srgb Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland Reviewed-by: Daniel Stone --- v7: - Fix checkpatch warnings (Louis Chauvet) - Adde a commit messages - Fix code styles by adding and removing spaces (new lines, tabs and so on) drivers/gpu/drm/vkms/tests/vkms_color_test.c | 39 +++- drivers/gpu/drm/vkms/vkms_composer.c | 17 ++--- drivers/gpu/drm/vkms/vkms_composer.h | 13 +++ 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c index affca56cac7b..02fbe19c744a 100644 --- a/drivers/gpu/drm/vkms/tests/vkms_color_test.c +++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c @@ -6,6 +6,7 @@ #include #include "../vkms_drv.h" #include "../vkms_composer.h" +#include "../vkms_luts.h" #define TEST_LUT_SIZE 16 @@ -36,7 +37,6 @@ static const struct vkms_color_lut test_linear_lut = { .channel_value2index_ratio = 0xf000fll }; - static void vkms_color_test_get_lut_index(struct kunit *test) { s64 lut_index; @@ -49,6 +49,19 @@ static void vkms_color_test_get_lut_index(struct kunit *test) lut_index = get_lut_index(&test_linear_lut, test_linear_array[i].red); KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(lut_index), i); } + + KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&srgb_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x101)), 0x1); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x202)), 0x2); + + KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&srgb_inv_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x0)), 0x0); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x101)), 0x1); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x202)), 0x2); + + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0xfefe)), 0xfe); + KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x)), 0xff); } static void vkms_color_test_lerp(struct kunit *test) @@ -155,9 +168,33 @@ static void vkms_color_test_lerp(struct kunit *test) KUNIT_EXPECT_EQ(test, lerp_u16(0x0, 0x1, 0x8000), 0x1); } +static void vkms_color_test_linear(struct kunit *test) +{ + for (int i = 0; i < LUT_SIZE; i++) { + int linear = apply_lut_to_channel_value(&linear_eotf, i * 0x101, LUT_RED); + + KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(linear, 0x101), i); + } +} + +static void vkms_color_srgb_inv_srgb(struct kunit *test) +{ + u16 srgb, final; + + for (int i = 0; i < LUT_SIZE; i++) { + srgb = apply_lut_to_channel_value(&srgb_eotf, i * 0x101, LUT_RED); + final = apply_lut_to_channel_value(&srgb_inv_eotf, srgb, LUT_RED); + + KUNIT_EXPECT_GE(test, final / 0x101, i - 1); + KUNIT_EXPECT_LE(test, final / 0x101, i + 1); + } +} + static struct kunit_case vkms_color_test_cases[] = { KUNIT_CASE(vkms_color_test_get_lut_index), KUNIT_CASE(vkms_color_test_lerp), + KUNIT_CASE(vkms_color_test_linear), + KUNIT_CASE(vkms_color_srgb_inv_srgb), {} }; diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 3ab829556db2..72890ffea13a 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -82,19 +82,8 @@ VISIBLE_IF_KUNIT s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel } EXPORT_SYMBOL_IF_KUNIT(get_lut_index); -/* - * This enum is related to the positions of the variables inside - * `struct drm_color_lut`, so the order of both needs to be the same. - */ -enum lut_channel { - LUT_RED = 0, - LUT_GREEN, - LUT_BLUE, - LUT_RESERVED -}; - -static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value, - enum lut_channel channel) +VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value, + enum lut_channel channel) { s64 lut_index = get_lut_index(lut, channel_value); u16 *floor_lut_value, *ceil_lut_value; @@ -119,6 +108,8 @@ static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 chan return lerp_u16(floor_channel_value, ceil_channel_value, lut_index & DRM_FIXED_DECIMAL_MASK); } +EXPORT_SYMBOL_IF_KUNIT(apply_lut_to_channel_value); + st
[PATCH V9 18/43] drm/vkms: add 3x4 matrix in color pipeline
From: Harry Wentland We add two 3x4 matrices into the VKMS color pipeline. The reason we're adding matrices is so that we can test that application of a matrix and its inverse yields an output equal to the input image. One complication with the matrix implementation has to do with the fact that the matrix entries are in signed-magnitude fixed point, whereas the drm_fixed.h implementation uses 2s-complement. The latter one is the one that we want for easy addition and subtraction, so we convert all entries to 2s-complement. Reviewed-by: Louis Chauvet Signed-off-by: Alex Hung Signed-off-by: Harry Wentland Reviewed-by: Daniel Stone --- V9: - Update function names by _plane_ (Chaitanya Kumar Borah) v8: - Replace DRM_ERROR with drm_err (Louis Chauvet) v7: - Fix checkpatch warnings - Change kzalloc(sizeof(struct drm_colorop) ...) to kzalloc(sizeof(*ops[i]) ...) - Change i-1to i - 1 - Add a new line at EOF v6: - pre-compute colors (Louis Chauvet) - round matrix output (Louis Chauvet) drivers/gpu/drm/vkms/vkms_colorop.c | 34 +++- drivers/gpu/drm/vkms/vkms_composer.c | 33 +++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c index 2fb74b2bd001..70be35804ea9 100644 --- a/drivers/gpu/drm/vkms/vkms_colorop.c +++ b/drivers/gpu/drm/vkms/vkms_colorop.c @@ -12,7 +12,7 @@ static const u64 supported_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF); -#define MAX_COLOR_PIPELINE_OPS 2 +#define MAX_COLOR_PIPELINE_OPS 4 static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list) { @@ -48,6 +48,38 @@ static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_pr goto cleanup; } + ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i - 1], ops[i]); + + i++; + + /* 3rd op: 3x4 matrix */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + drm_err(dev, "KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + + ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane); + if (ret) + goto cleanup; + + drm_colorop_set_next_property(ops[i - 1], ops[i]); + + i++; + + /* 4th op: 1d curve */ + ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL); + if (!ops[i]) { + drm_err(dev, "KMS: Failed to allocate colorop\n"); + ret = -ENOMEM; + goto cleanup; + } + ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs); if (ret) goto cleanup; diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index 36f0caabb67b..8fe16d26d680 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -128,6 +128,35 @@ static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buff } } +static void apply_3x4_matrix(struct pixel_argb_s32 *pixel, const struct drm_color_ctm_3x4 *matrix) +{ + s64 rf, gf, bf; + s64 r, g, b; + + r = drm_int2fixp(pixel->r); + g = drm_int2fixp(pixel->g); + b = drm_int2fixp(pixel->b); + + rf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[0]), r) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[1]), g) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[2]), b) + +drm_sm2fixp(matrix->matrix[3]); + + gf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[4]), r) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[5]), g) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[6]), b) + +drm_sm2fixp(matrix->matrix[7]); + + bf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[8]), r) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[9]), g) + +drm_fixp_mul(drm_sm2fixp(matrix->matrix[10]), b) + +drm_sm2fixp(matrix->matrix[11]); + + pixel->r = drm_fixp2int_round(rf); + pixel->g = drm_fixp2int_round(gf); + pixel->b = drm_fixp2int_round(bf); +} + static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colorop) { struct drm_colorop_state *colorop_state = colorop->state; @@ -151,6 +180,10 @@ static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colo colorop_state->curve_1d_type); break; } + } else if (colorop->type == DRM_COLOROP_CTM_3X4) { + if (colorop_state->data) + apply_3x4_matrix(pixel, +(struct drm_color_ctm_3x4 *) colorop_state->data->data); } } -- 2.43.0