RE: [PATCH V9 16/43] drm/colorop: Add 3x4 CTM type

2025-06-16 Thread Borah, Chaitanya Kumar



> -Original Message-
> From: Alex Hung 
> Sent: Wednesday, April 30, 2025 6:41 AM
> To: dri-de...@lists.freedesktop.org; amd-...@lists.freedesktop.org
> Cc: wayland-devel@lists.freedesktop.org; harry.wentl...@amd.com;
> alex.h...@amd.com; leo@amd.com; ville.syrj...@linux.intel.com;
> pekka.paala...@collabora.com; cont...@emersion.fr; m...@igalia.com;
> jad...@redhat.com; sebastian.w...@redhat.com;
> shashank.sha...@amd.com; ago...@nvidia.com; jos...@froggi.es;
> mdaen...@redhat.com; aleix...@kde.org; xaver.h...@gmail.com;
> victo...@system76.com; dan...@ffwll.ch; Shankar, Uma
> ; quic_nas...@quicinc.com;
> quic_cbr...@quicinc.com; quic_abhin...@quicinc.com; mar...@marcan.st;
> liviu.du...@arm.com; sashamcint...@google.com; Borah, Chaitanya
> Kumar ; louis.chau...@bootlin.com;
> Daniel Stone 
> Subject: [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.

Is there any value to adding pre-offsets [1] in the uapi? 

 |R/Cr|| c0 c1 c2 |   ( |R/Cr|   |preoff0| )   |postoff0|
 |G/Y | = | c3 c4 c5 | x ( |G/Y | + |preoff1| ) + |postoff1|
 |B/Cb|   | c6 c7 c8 |   ( |B/Cb|   |preoff2| )   |postoff2|

Handling limited range values is one use case that I can think of.  

[1] 
https://cgit.freedesktop.org/drm-tip/tree/drivers/gpu/drm/i915/display/intel_color.c#n112

Regards

Chaitanya

> 
> 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_p

[PATCH V10 08/46] drm/colorop: Add NEXT property

2025-06-16 Thread Alex Hung
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.

Reviewed-by: Simon Ser 
Signed-off-by: Alex Hung 
Signed-off-by: Harry Wentland 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
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 11c37916c758..43cdd68a69a2 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -58,6 +58,7 @@ static int drm_plane_colorop_init(struct drm_device *dev, 
struct drm_colorop *co
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_plane_colorop_init(struct drm_device *dev, 
struct drm_colorop *co
   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 49e6564c17ba..791ab940c158 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 V10 44/46] drm/amd/display: Add AMD color pipeline doc

2025-06-16 Thread Alex Hung
From: Harry Wentland 

Add kernel doc for AMD color pipeline.

Signed-off-by: Alex Hung 
Signed-off-by: Harry Wentland 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
 .../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 a2e76df039d6..b53aecd1bebc 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_plane color pipeline is advertised for DCN generations

[PATCH V10 13/46] drm/colorop: Add destroy functions for color pipeline

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Simon Ser 
Reviewed-by: Melissa Wen 
---
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 V10 41/46] drm/colorop: allow non-bypass colorops

2025-06-16 Thread Alex Hung
From: Harry Wentland 

Not all HW will be able to do bypass on all color
operations. Introduce an 32 bits 'flags' for all colorop
init functions and DRM_COLOROP_FLAG_ALLOW_BYPASS for creating
the BYPASS property when it's true.

Signed-off-by: Alex Hung 
Signed-off-by: Harry Wentland 
Reviewed-by: Daniel Stone 
Reviewed-by: Simon Ser 
Reviewed-by: Melissa Wen 
---
V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)
 - Chagne "bool allow_bypass" to "uint32_t flags" for better extensibility 
(Simon Ser)

 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 22 +++---
 drivers/gpu/drm/drm_atomic.c  |  3 +-
 drivers/gpu/drm/drm_colorop.c | 44 +++
 drivers/gpu/drm/vkms/vkms_colorop.c   | 10 +++--
 include/drm/drm_colorop.h | 11 +++--
 5 files changed, 55 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 60a5bfdc2578..868d6bd0a5db 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
@@ -65,7 +65,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
goto cleanup;
}
 
-   ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, 
amdgpu_dm_supported_degam_tfs);
+   ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane,
+ amdgpu_dm_supported_degam_tfs,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
if (ret)
goto cleanup;
 
@@ -81,7 +83,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
goto cleanup;
}
 
-   ret = drm_plane_colorop_mult_init(dev, ops[i], plane);
+   ret = drm_plane_colorop_mult_init(dev, ops[i], plane, 
DRM_COLOROP_FLAG_ALLOW_BYPASS);
if (ret)
goto cleanup;
 
@@ -96,7 +98,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
goto cleanup;
}
 
-   ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane);
+   ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane, 
DRM_COLOROP_FLAG_ALLOW_BYPASS);
if (ret)
goto cleanup;
 
@@ -111,7 +113,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
goto cleanup;
}
 
-   ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, 
amdgpu_dm_supported_shaper_tfs);
+   ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane,
+ amdgpu_dm_supported_shaper_tfs,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
if (ret)
goto cleanup;
 
@@ -127,7 +131,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
}
 
ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, 
MAX_COLOR_LUT_ENTRIES,
- 
DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR);
+ 
DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
+ 
DRM_COLOROP_FLAG_ALLOW_BYPASS);
if (ret)
goto cleanup;
 
@@ -142,7 +147,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
goto cleanup;
}
 
-   ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, 
amdgpu_dm_supported_blnd_tfs);
+   ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane,
+ amdgpu_dm_supported_blnd_tfs,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
if (ret)
goto cleanup;
 
@@ -158,7 +165,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
}
 
ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, 
MAX_COLOR_LUT_ENTRIES,
- 
DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR);
+ 
DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
+ 
DRM_COLOROP_FLAG_ALLOW_BYPASS);
if (ret)
goto cleanup;
 
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 90df638f25d5..41dd6a90e13b 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,

[PATCH V10 20/46] drm/vkms: Add tests for CTM handling

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
---
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_matrix(&out, &test_matrix_3x4_bt709_enc);
+
+   /* Y 54 */
+   KUNIT_EXPECT_GT(test, out.r, 0x3500);
+   KU

[PATCH V10 23/46] drm/amd/display: Ignore deprecated props when plane_color_pipeline set

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
 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 d3100f641ac6..352306164387 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -5658,6 +5658,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 V10 17/46] drm/vkms: Use s32 for internal color pipeline precision

2025-06-16 Thread Alex Hung
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



Re: [PATCH V9 16/43] drm/colorop: Add 3x4 CTM type

2025-06-16 Thread Pekka Paalanen
On Mon, 16 Jun 2025 11:30:23 +
"Borah, Chaitanya Kumar"  wrote:

> > -Original Message-
> > From: Alex Hung 
> > Sent: Wednesday, April 30, 2025 6:41 AM
> > To: dri-de...@lists.freedesktop.org; amd-...@lists.freedesktop.org
> > Cc: wayland-devel@lists.freedesktop.org; harry.wentl...@amd.com;
> > alex.h...@amd.com; leo@amd.com; ville.syrj...@linux.intel.com;
> > pekka.paala...@collabora.com; cont...@emersion.fr; m...@igalia.com;
> > jad...@redhat.com; sebastian.w...@redhat.com;
> > shashank.sha...@amd.com; ago...@nvidia.com; jos...@froggi.es;
> > mdaen...@redhat.com; aleix...@kde.org; xaver.h...@gmail.com;
> > victo...@system76.com; dan...@ffwll.ch; Shankar, Uma
> > ; quic_nas...@quicinc.com;
> > quic_cbr...@quicinc.com; quic_abhin...@quicinc.com; mar...@marcan.st;
> > liviu.du...@arm.com; sashamcint...@google.com; Borah, Chaitanya
> > Kumar ; louis.chau...@bootlin.com;
> > Daniel Stone 
> > Subject: [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.  
> 
> Is there any value to adding pre-offsets [1] in the uapi? 
> 
>  |R/Cr|| c0 c1 c2 |   ( |R/Cr|   |preoff0| )   |postoff0|
>  |G/Y | = | c3 c4 c5 | x ( |G/Y | + |preoff1| ) + |postoff1|
>  |B/Cb|   | c6 c7 c8 |   ( |B/Cb|   |preoff2| )   |postoff2|
> 
> Handling limited range values is one use case that I can think of.  

Hi,

in the mathematical sense, no. A pre-offset can always be converted
into a post-offset by multiplying it with the 3x3 matrix (and adding to
the existing post-offset). This can be pre-computed, no need to do
it separately for every pixel.

For hardware reasons, I have no idea.

> [1] 
> https://cgit.freedesktop.org/drm-tip/tree/drivers/gpu/drm/i915/display/intel_color.c#n112
> 

Thanks,
pq


pgpAUIBGcVjvD.pgp
Description: OpenPGP digital signature


[PATCH V10 01/46] drm: Add helper for conversion from signed-magnitude

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
 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 V10 00/46] Color Pipeline API w/ VKMS

2025-06-16 Thread Alex Hung
This is an RFC set for a color pipeline API, along with implementations
in VKMS and amdgpu. It is tested with a set of IGT tests that can be
found at [1]. The IGT tests run a pixel-by-pixel comparison with an
allowable delta variation as the goal for these transformations is
perceptual correctness, not complete pixel accuracy.

v5 of this patchset fleshed out documentation for colorops and the
various defines that are being introduced.

v6 addresses a few comments from various reviewers.

v7 simplifies 3D LUT and addresses more comments from various reviewers.

v8 fixes typo and errors and address comments from reviewers.

v9 refactors cleanup functions, fixes typo and errors, and addresses
   comments from reviewers.

v10 add 32BIT RGB (drm_color_lut_32) to 1D & 3D LUTs, addresses comments
from reviewers, and fixes typo and errors.

VKMS supports two named transfer function colorops and two matrix
colorops.

Amdgpu advertises the following pipeline for GPUs with DCN 3 or newer:

1. 1D Curve EOTF
2. 3x4 CTM
3. Multiplier
4. 1D Curve Inverse EOTF
5. 1D LUT
6. 3D LUT
7. 1D Curve EOTF
8. 1D LUT

The supported curves for the 1D Curve type are:
- sRGB EOTF and its inverse
- PQ EOTF, scaled to [0.0, 125.0] and its inverse
- BT.2020/BT.709 OETF and its inverse

Note that the 1st and 5th colorops take the EOTF or Inverse
OETF while the 3rd colorop takes the Inverse EOTF or OETF.

The 3D LUT is a 17^3 tetrahedrally interpolated LUT but the mechanism
exists for other drivers to describe their own 3D LUT capability.

This mirrors the color pipeline used by gamescope and presented by
Melissa Wen, with the exception of the DEGAM LUT, which is not currently
used. See [1]
https://indico.freedesktop.org/event/4/contributions/186/attachments/138/218/xdc2023-TheRainbowTreasureMap-MelissaWen.pdf

At this point we're hoping to see gamescope, kwin and weston implementations
take shape. The existing pipeline should be enough to satisfy the
gamescope use-cases on the drm_plane.

In order to support YUV we'll need to add COLOR_ENCODING and COLOR_RANGE
support to the color pipeline. I have sketched these out already but
don't have it all hooked up yet. This should not hinder adoption of this
API for gaming use-cases.

We'll also want to advertise IN_FORMATS on a color pipeline as some
color pipelines won't be able to work for all IN_FORMATS on a plane.
Again, I have a sketch but no full implementation yet. This is not
currently required by the AMD color pipeline and could be added after
the merge of this set.

VKMS patches could still be improved in a few ways, though the
payoff might be limited and I would rather focus on other work
at the moment. The most obvious thing to improve would be to
eliminate the hard-coded LUTs for identity, and sRGB, and replace
them with fixed-point math instead.

There are plenty of things that I would like to see, but they could
be added after the merge of this patchset:
 - COLOR_ENCODING and COLOR_RANGE
 - IN_FORMATS for a color pipeline
 - Is it possible to support HW which can't bypass entire pipeline?
 - Can we do a LOAD / COMMIT model for LUTs (and other properties)?
 - read-only scaling colorop which defines scaling taps and position
 - named matrices, for things like converting YUV to RGB
 - Add custom LUT colorops to VKMS

IGT tests can be found at [1] or on the igt-dev mailing list. There
have been no updates since v5 and rebase on latest main is straight-
forward.

A kernel branch can be found at [2].

[1] 
https://gitlab.freedesktop.org/alex.hung/igt-gpu-tools/-/tree/amd-color-pipeline-v9
[2] https://gitlab.freedesktop.org/alex.hung/linux/-/tree/amd-color-pipeline-v10

v10:
 - remove duplicated "is useful" in comments (Melissa Wen)
 - guard "dm_plane_init_colorops" function when !AMD_PRIVATE_COLOR (Melissa Wen)
 - Replace DRM_ERROR by drm_err
 - Creaet color pipeline when >= DCN_VERSION_3_0 (Melissa Wen)
 - Relocate amdgpu_dm_supported_*_tfs check (Melissa Wen)
 - Support 32BIT RGB for 1D LUTs (Uma Shankar)
 - Support 32BIT RGB for 3D LUTs (Harry Wentland)
 - Fix typo mutliplier to multiplier in subject (Melissa Wen)
 - 1D & 3D LUTs are no longer immutable ((Xaver Hugl)
 - Fix 3D LUT kernel doc (Leandro Ribeiro)
 - Check dpp.hw_3d_lut before creating shaper tf/lut and 3dlut colorops 
(Melissa Wen)
 - Disable CRTC degamma when color pipeline is enabled (Melissa Wen)

v9:
 - Update RFC documents for 3DLUT and fallback behaviours (Simon Ser)
 - Specify colorop function names by _plane_ (Chaitanya Kumar Borah)
 - Remove redundant comments (Simon Ser)
 - Fix typo in commit description (Shengyu Qu)
 - Move destroy and cleanup functions earlier (Simon Ser)
 - Move DRM_COLOROP_1D_CURVE_BT2020_* from middle to end (Simon Ser)
 - Chagne "bool allow_bypass" to "uint32_t flags" for better extensibility 
(Simon Ser)
 - Return a value in __set_dm_plane_colorop_3dlut

v8:
 - Change VKMS config names (Louis Chauvet)
 - Remove deprecated function "drm_atomic_get_existing_colorop_state" (Louis 
Chauve

[PATCH V10 04/46] drm/colorop: Introduce new drm_colorop mode object

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
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 5050ac32bba2..915881d0a3d9 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 0138cf0b8b63..20d6393ec72c 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 i

[PATCH V10 05/46] drm/colorop: Add TYPE property

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
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 20d6393ec72c..886dc8b28abc 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 10e625c17f90..358a95065459 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_CURVE:
+*
+* enum string "1D Curv

[PATCH V10 06/46] drm/colorop: Add 1D Curve subtype

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
V9: Specify function names by _plane_ (Chaitanya Kumar Borah)

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 358a95065459..995f5ce87efc 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..6fbc3c284d33 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_plane_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_plane_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_plane_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_plane_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;
+
+   struct 

[PATCH V10 02/46] drm/vkms: Add kunit tests for VKMS LUT handling

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
---
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/tests/Makefile  |   2 +-
 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 ++
 4 files changed, 192 insertions(+), 3 deletions(-)
 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/tests/Makefile 
b/drivers/gpu/drm/vkms/tests/Makefile
index 9ded37b67a46..e92f7143e540 100644
--- a/drivers/gpu/drm/vkms/tests/Makefile
+++ b/drivers/gpu/drm/vkms/tests/Makefile
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-obj-$(CONFIG_DRM_VKMS_KUNIT_TEST) += vkms_config_test.o
+obj-$(CONFIG_DRM_VKMS_KUNIT_TEST) += vkms_config_test.o vkms_color_test.o
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(test, drm_fixp2int(lut_index), 0);
+
+   for (i = 0; i < TEST_LUT_SIZE; i++) {
+   lut_index = get_lut_index(&test_linear_lut, 
test_linear_array[i].red);
+   KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(lut_index), i);
+   }
+}
+
+static void vkms_color_test_lerp(struct kunit *test)
+{
+   /*** half-way round down ***/
+   s64 t = 0x8000 - 1;
+
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x0, 0x10, t), 0x8);
+
+   /* odd a */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x1, 0x10, t), 0x8);
+
+   /* odd b */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x1, 0xf, t), 0x8);
+
+   /* b = a */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x10, 0x10, t), 0x10);
+
+   /* b = a + 1 */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x10, 0x11, t), 0x10);
+
+   /*** half-way round up ***/
+   t = 0x8000;
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x0, 0x10, t), 0x8);
+
+   /* odd a */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x1, 0x10, t), 0x9);
+
+   /* odd b */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x1, 0xf, t), 0x8);
+
+   /* b = a */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x10, 0x10, t), 0x10);
+
+   /* b = a + 1 */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x10, 0x11, t), 0x11);
+
+   /*** t = 0.0 ***/
+   t = 0x0;
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x0, 0x10, t), 0x0);
+
+   /* odd a */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x1, 0x10, t), 0x1);
+
+   /* odd b */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x1, 0xf, t), 0x1);
+
+   /* b = a */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x10, 0x10, t), 0x10);
+
+   /* b = a + 1 */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x10, 0x11, t), 0x10);
+
+   /*** t = 1.0 ***/
+   t = 0x1;
+

[PATCH V10 10/46] drm/plane: Add COLOR PIPELINE property

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Melissa Wen 
---
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 plane
+ * @plane_state: atomic state object 

[PATCH V10 09/46] drm/colorop: Add atomic state print for drm_colorop

2025-06-16 Thread Alex Hung
From: Harry Wentland 

Print atomic state for drm_colorop in debugfs

Reviewed-by: Simon Ser 
Signed-off-by: Alex Hung 
Signed-off-by: Harry Wentland 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
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 886dc8b28abc..dc679c8b904e 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);
 }
@@ -1903,6 +1927,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;
@@ -1912,6 +1937,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 V10 07/46] drm/colorop: Add BYPASS property

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
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 995f5ce87efc..e5da00fdbca0 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 6fbc3c284d33..11c37916c758 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -79,6 +79,17 @@ static int drm_plane_colorop_init(struct drm_device *dev, 
struct drm_colorop *co
   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_plane_colorop_curve_1d_init(struct drm_device *dev, 
struct drm_colorop *
/* 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 fa167e642e0d..49e6564c17ba 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 V10 11/46] drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Melissa Wen 
---
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 5c3b2aa3e69d..5d57a17502ab 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 

[PATCH V10 12/46] Documentation/gpu: document drm_colorop

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Melissa Wen 
---
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 V10 14/46] drm/vkms: Add enumerated 1D curve colorop

2025-06-16 Thread Alex Hung
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);
+
+   return ret;
+}
+
+int vkms_initialize_colorops(struct drm_plane *plane)
+{
+   struct drm_prop_enum_l

[PATCH V10 16/46] drm/colorop: Add 3x4 CTM type

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Melissa Wen 
---
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 @@
 
 static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = {
{ DRM_COLOROP_1D_CURVE, 

[PATCH V10 15/46] drm/vkms: Add kunit tests for linear and sRGB LUTs

2025-06-16 Thread Alex Hung
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);
+
 
 static void apply_lut(const struct vkms_crtc_state *crtc_state, struct 
line_buffer *output_buffer)

[PATCH V10 22/46] drm/colorop: define a new macro for_each_new_colorop_in_state

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
v10:
 - remove duplicated "is useful" in comments

 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 4a30ed8ab1a8..678708df9cdb 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -1069,6 +1069,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 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 V10 18/46] drm/vkms: add 3x4 matrix in color pipeline

2025-06-16 Thread Alex Hung
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



[PATCH V10 19/46] drm/tests: Add a few tests around drm_fixed.h

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
---
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 3afd6587df08..91437cf34d92 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -23,6 +23,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 V10 21/46] drm/colorop: pass plane_color_pipeline client cap to atomic check

2025-06-16 Thread Alex Hung
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.

Reviewed-by: Simon Ser 
Signed-off-by: Alex Hung 
Signed-off-by: Harry Wentland 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
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 be73cb9f502e..0de0fdd8af5b 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -1603,6 +1603,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 7904a38caa77..4a30ed8ab1a8 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 V10 28/46] drm/amd/display: Add support for sRGB EOTF in BLND block

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
---
V10:
 - Remove redundant DRM_ERROR (checkpatch)

V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)

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 | 18 +
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h |  1 +
 3 files changed, 96 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 4662d5de..7ce95ddf3851 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 dec64f11840f..aa6737f2b35e 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 =
+ 

[PATCH V10 24/46] drm/amd/display: Add bypass COLOR PIPELINE

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
---
v10:
 - guard "dm_plane_init_colorops" function when !AMD_PRIVATE_COLOR (Melissa Wen)

 .../amd/display/amdgpu_dm/amdgpu_dm_plane.c   | 20 +++
 1 file changed, 20 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 b7c6e8d13435..580af6e85c81 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
@@ -1782,6 +1782,21 @@ dm_atomic_plane_get_property(struct drm_plane *plane,
 
return 0;
 }
+#else
+
+#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;
+}
 #endif
 
 static const struct drm_plane_funcs dm_plane_funcs = {
@@ -1890,7 +1905,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 V10 25/46] drm/amd/display: Skip color pipeline initialization for cursor plane

2025-06-16 Thread Alex Hung
cursor plane does not need to have color pipeline.

Signed-off-by: Alex Hung 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
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 580af6e85c81..9a5b184e594f 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 V10 03/46] drm/doc/rfc: Describe why prescriptive color pipeline is needed

2025-06-16 Thread Alex Hung
From: Harry Wentland 

Add documentation for color pipeline API.

Signed-off-by: Alex Hung 
Signed-off-by: Harry Wentland 
Reviewed-by: Daniel Stone 
Reviewed-by: Simon Ser 
Reviewed-by: Melissa Wen 
---
V9:
 - Update documents according to new 3DLUT changes (Simon Ser)
 - Spell out the behaviours when fallback to shaders/CPU (Simon Ser)

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 ..cd1cc2d0f988
--- /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. The switch between HW
+fixed-function blocks and shaders/CPU must be seamless with no visible
+difference when fallback to shaders/CPU is neceesary at any time.
+
+
+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 tu

[PATCH V10 26/46] drm/amd/display: Add support for sRGB EOTF in DEGAM block

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
---
V10:
 - Replace DRM_ERROR by drm_err
 - Creaet color pipeline when >= DCN_VERSION_3_0

V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)
 - Update replace cleanup code by drm_colorop_pipeline_destroy (Simon Ser)

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 | 71 +++
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h | 34 
 .../amd/display/amdgpu_dm/amdgpu_dm_plane.c   | 16 
 5 files changed, 209 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 7329b8cc2576..8e949fe77312 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
@@ -39,7 +39,8 @@ AMDGPUDM = \
amdgpu_dm_psr.o \
amdgpu_dm_replay.o \
amdgpu_dm_quirks.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

[PATCH V10 30/46] drm/amd/display: Enable support for PQ 125 EOTF and Inverse

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
---
V10:
 - Move amdgpu_dm_supported_*_tfs check here from Patch 32 (Melissa Wen)

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..315a1028cd45 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;
}
@@ -1276,8 +1284,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) {
@@ -1313,7 +1323,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

[PATCH V10 27/46] drm/amd/display: Add support for sRGB Inverse EOTF in SHAPER block

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
---
V10:
 - Remove redundant DRM_ERROR (checkpatch)

V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)

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 | 18 +
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h |  1 +
 3 files changed, 93 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 0b513ab5050f..4662d5de 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 9d371728b5fd..dec64f11840f 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
+++ b/drivers/gpu/drm/amd

[PATCH V10 29/46] drm/colorop: Add PQ 125 EOTF and its inverse

2025-06-16 Thread Alex Hung
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.

Reviewed-by: Simon Ser 
Signed-off-by: Alex Hung 
Signed-off-by: Harry Wentland 
Reviewed-by: Daniel Stone 
---
 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 65351aaa7771..9ddb3dccaf72 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 5f0cddc3922f..40d70d4a34a0 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 V10 33/46] drm: Add Enhanced LUT precision structure

2025-06-16 Thread Alex Hung
From: Uma Shankar 

Existing LUT precision structure drm_color_lut has only 16 bit
precision. This is not enough for upcoming enhanced hardwares
and advance usecases like HDR processing. Hence added a new
structure with 32 bit precision values.

Signed-off-by: Alex Hung 
Signed-off-by: Uma Shankar 
Signed-off-by: Chaitanya Kumar Borah 
---
V10:
 - Include drm_color_lut_32 from Intel to support 32BIT RGB in 1D & 3D
   LUTs (Uma Shankar)

 drivers/gpu/drm/drm_color_mgmt.c | 43 
 include/drm/drm_color_mgmt.h | 13 ++
 include/uapi/drm/drm_mode.h  | 11 
 3 files changed, 67 insertions(+)

diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c
index 3969dc548cff..83dc850d3b54 100644
--- a/drivers/gpu/drm/drm_color_mgmt.c
+++ b/drivers/gpu/drm/drm_color_mgmt.c
@@ -630,3 +630,46 @@ int drm_color_lut_check(const struct drm_property_blob 
*lut, u32 tests)
return 0;
 }
 EXPORT_SYMBOL(drm_color_lut_check);
+
+/**
+ * drm_color_lut_32_check - check validity of extended lookup table
+ * @lut: property blob containing extended LUT to check
+ * @tests: bitmask of tests to run
+ *
+ * Helper to check whether a userspace-provided extended lookup table is valid 
and
+ * satisfies hardware requirements.  Drivers pass a bitmask indicating which of
+ * the tests in &drm_color_lut_tests should be performed.
+ *
+ * Returns 0 on success, -EINVAL on failure.
+ */
+int drm_color_lut_32_check(const struct drm_property_blob *lut, u32 tests)
+{
+   const struct drm_color_lut_32 *entry;
+   int i;
+
+   if (!lut || !tests)
+   return 0;
+
+   entry = lut->data;
+   for (i = 0; i < drm_color_lut_32_size(lut); i++) {
+   if (tests & DRM_COLOR_LUT_EQUAL_CHANNELS) {
+   if (entry[i].red != entry[i].blue ||
+   entry[i].red != entry[i].green) {
+   DRM_DEBUG_KMS("All LUT entries must have equal 
r/g/b\n");
+   return -EINVAL;
+   }
+   }
+
+   if (i > 0 && tests & DRM_COLOR_LUT_NON_DECREASING) {
+   if (entry[i].red < entry[i - 1].red ||
+   entry[i].green < entry[i - 1].green ||
+   entry[i].blue < entry[i - 1].blue) {
+   DRM_DEBUG_KMS("LUT entries must never 
decrease.\n");
+   return -EINVAL;
+   }
+   }
+   }
+
+   return 0;
+}
+EXPORT_SYMBOL(drm_color_lut_32_check);
diff --git a/include/drm/drm_color_mgmt.h b/include/drm/drm_color_mgmt.h
index ed81741036d7..882253a82bf1 100644
--- a/include/drm/drm_color_mgmt.h
+++ b/include/drm/drm_color_mgmt.h
@@ -72,6 +72,18 @@ static inline int drm_color_lut_size(const struct 
drm_property_blob *blob)
return blob->length / sizeof(struct drm_color_lut);
 }
 
+/**
+ * drm_color_lut_32_size - calculate the number of entries in the extended LUT
+ * @blob: blob containing the LUT
+ *
+ * Returns:
+ * The number of entries in the color LUT stored in @blob.
+ */
+static inline int drm_color_lut_32_size(const struct drm_property_blob *blob)
+{
+   return blob->length / sizeof(struct drm_color_lut_32);
+}
+
 enum drm_color_encoding {
DRM_COLOR_YCBCR_BT601,
DRM_COLOR_YCBCR_BT709,
@@ -118,4 +130,5 @@ enum drm_color_lut_tests {
 };
 
 int drm_color_lut_check(const struct drm_property_blob *lut, u32 tests);
+int drm_color_lut_32_check(const struct drm_property_blob *lut, u32 tests);
 #endif
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 651bdf48b766..21bd96f437e0 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -872,6 +872,16 @@ struct drm_color_lut {
__u16 reserved;
 };
 
+struct drm_color_lut_32 {
+   /*
+* Similar to drm_color_lut but for high precision LUTs
+*/
+   __u32 red;
+   __u32 green;
+   __u32 blue;
+   __u32 reserved;
+};
+
 /**
  * enum drm_colorop_type - Type of color operation
  *
@@ -879,6 +889,7 @@ struct drm_color_lut {
  * 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:
-- 
2.43.0



[PATCH V10 35/46] drm/amd/display: add shaper and blend colorops for 1D Curve Custom LUT

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
---
V10:
 - Support 32BIT RGB in 1D LUT with drm_color_lut_32 (Uma Shankar)
 - Remove redundant DRM_ERROR(...)

V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)

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   | 306 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c |  30 ++
 2 files changed, 257 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 f645f9ded95f..2b97a262b1c0 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
@@ -73,6 +73,7 @@
  */
 
 #define MAX_DRM_LUT_VALUE 0x
+#define MAX_DRM_LUT_32_VALUE 0x
 #define SDR_WHITE_LEVEL_INIT_VALUE 80
 
 /**
@@ -342,6 +343,21 @@ __extract_blob_lut(const struct drm_property_blob *blob, 
uint32_t *size)
return blob ? (struct drm_color_lut *)blob->data : NULL;
 }
 
+/**
+ * __extract_blob_lut_32 - Extracts the DRM lut and lut size from a blob.
+ * @blob: DRM color mgmt property blob
+ * @size: lut size
+ *
+ * Returns:
+ * DRM LUT or NULL
+ */
+static const struct drm_color_lut_32 *
+__extract_blob_lut_32(const struct drm_property_blob *blob, uint32_t *size)
+{
+   *size = blob ? drm_color_lut_32_size(blob) : 0;
+   return blob ? (struct drm_color_lut_32 *)blob->data : NULL;
+}
+
 /**
  * __is_lut_linear - check if the given lut is a linear mapping of values
  * @lut: given lut to check values
@@ -415,6 +431,30 @@ static void __drm_lut_to_dc_gamma(const struct 
drm_color_lut *lut,
}
 }
 
+/**
+ * __drm_lut_32_to_dc_gamma - convert the drm_color_lut to dc_gamma.
+ * @lut: DRM lookup table for color conversion
+ * @gamma: DC gamma to set entries
+ * @is_legacy: legacy or atomic gamma
+ *
+ * The conversion depends on the size of the lut - whether or not it's legacy.
+ */
+static void __drm_lut_32_to_dc_gamma(const struct drm_color_lut_32 *lut, 
struct dc_gamma *gamma)
+{
+   uint32_t r, g, b;
+   int i;
+
+   for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) {
+   r = drm_color_lut_extract(lut[i].red, 32);
+   g = drm_color_lut_extract(lut[i].green, 32);
+   b = drm_color_lut_extract(lut[i].blue, 32);
+
+   gamma->entries.red[i] = dc_fixpt_from_fraction(r, 
MAX_DRM_LUT_32_VALUE);
+   gamma->entries.green[i] = dc_fixpt_from_fraction(g, 
MAX_DRM_LUT_32_VALUE);
+   gamma->entries.blue[i] = dc_fixpt_from_fraction(b, 
MAX_DRM_LUT_32_VALUE);
+   }
+}
+
 /**
  * __drm_ctm_to_dc_matrix - converts a DRM CTM to a DC CSC float matrix
  * @ctm: DRM color transformation matrix
@@ -567,6 +607,63 @@ static int __set_output_tf(struct dc_transfer_func *func,
return res ? 0 : -ENOMEM;
 }
 
+/**
+ * __set_output_tf_32 - calculates the output transfer function based on 
expected input space.
+ * @func: transfer function
+ * @lut: lookup table that defines the color space
+ * @lut_size: size of respective lut
+ * @has_rom: if ROM can be used for hardcoded curve
+ *
+ * Returns:
+ * 0 in case of success. -ENOMEM if fails.
+ */
+static int __set_output_tf_32(struct dc_transfer_func *func,
+ const struct drm_color_lut_32 *lut, uint32_t 
lut_size,
+ bool has_rom)
+{
+   struct dc_gamma *gamma = NULL;
+   struct calculate_buffer cal_buffer = {0};
+   bool res;
+
+   cal_buffer.buffer_index = -1;
+
+   if (lut_size) {
+   gamma = dc_create_gamma();
+   if (!gamma)
+   return -ENOMEM;
+
+   gamma->num_entries = lut_size;
+   __drm_lut_32_to_dc_gamma(lut, gamma);
+   }
+
+   if (func->tf == TRANSFER_FUNCTION_LINEAR) {
+   /*
+* Color module doesn't like calculating regamma params
+* on top of a linear input. But degamma params can be used
+* instead to simulate this.
+*/
+   if (gamma)
+   gamma->type = GAMMA_CUSTOM;
+   res = mod_color_calculate_degamma_params(NULL, func,
+gamma, gamma != NULL);
+   } else {
+

[PATCH V10 31/46] drm/colorop: add BT2020/BT709 OETF and Inverse OETF

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Simon Ser 
---
V9:
 - Move DRM_COLOROP_1D_CURVE_BT2020_* from middle to end (Simon Ser)

 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 9ddb3dccaf72..3ae4a93f0200 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -72,6 +72,8 @@ static const char * const colorop_curve_1d_type_names[] = {
[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",
+   [DRM_COLOROP_1D_CURVE_BT2020_INV_OETF] = "BT.2020 Inverse OETF",
+   [DRM_COLOROP_1D_CURVE_BT2020_OETF] = "BT.2020 OETF",
 };
 
 
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index 40d70d4a34a0..766caedea2f3 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -83,6 +83,29 @@ enum drm_colorop_curve_1d_type {
 */
DRM_COLOROP_1D_CURVE_PQ_125_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_COUNT:
 *
-- 
2.43.0



[PATCH V10 32/46] drm/amd/display: Add support for BT.709 and BT.2020 TFs

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
---
V10:
 - Move amdgpu_dm_supported_*_tfs check to Patch 32 (Melissa Wen)

V9:
 - Move DRM_COLOROP_1D_CURVE_BT2020_* from middle to end

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 315a1028cd45..f645f9ded95f 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
@@ -679,6 +679,9 @@ amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type 
tf)
case DRM_COLOROP_1D_CURVE_PQ_125_EOTF:
case DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF:
return TRANSFER_FUNCTION_PQ;
+   case DRM_COLOROP_1D_CURVE_BT2020_INV_OETF:
+   case DRM_COLOROP_1D_CURVE_BT2020_OETF:
+   return TRANSFER_FUNCTION_BT709;
default:
return TRANSFER_FUNCTION_LINEAR;
}
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 1675913b3f83..5c84c6a5a74a 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 V10 36/46] drm/amd/display: add 3x4 matrix colorop

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
V10: 
 - Change %lu to %zu for sizeof() in drm_warn (kernel test robot)
 - Remove redundant DRM_ERROR(...)

V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)

v8:
 - Return -EINVAL when drm_color_ctm_3x4's size mismatches (Leo Li)

v7:
 - Change %lu to %zu for sizeof() 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 | 15 ++
 2 files changed, 67 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 2b97a262b1c0..b0223c6884c1 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
@@ -1344,6 +1344,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 (%zu) 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,
@@ -1547,6 +1588,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 427fe5050d0d..3b099eba67d6 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
@@ -74,6 +74,21 @@ 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]) {
+   ret = -ENOMEM;
+   goto cleanup;
+   }
+
+   ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane);
+   if (ret)
+   goto cleanup;
+
+   drm_colorop_set_next_proper

[PATCH V10 34/46] drm/colorop: Add 1D Curve Custom LUT type

2025-06-16 Thread Alex Hung
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.

Reviewed-by: Simon Ser 
Signed-off-by: Alex Hung 
Co-developed-by: Harry Wentland 
Signed-off-by: Harry Wentland 
Reviewed-by: Daniel Stone 
---
V10:
 - 1D LUT API is now using 32BIT RGB with drm_color_lut_32 (Uma Shankar)

V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)

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 809bd856d378..1dda90554e46 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 0de0fdd8af5b..6839363fa05e 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -712,6 +712,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_32);
+   break;
case DRM_COLOROP_CTM_3X4:
size = sizeof(struct drm_color_ctm_3x4);
break;
@@ -761,6 +764,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 3ae4a93f0200..6c87f5b9f7f9 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"},
 };
 
@@ -266,6 +267,47 @@ static int drm_colorop_create_data_prop(struct drm_device 
*dev, struct drm_color
return 0;
 }
 
+/**
+ * drm_plane_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_plane_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_plane_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(&c

[PATCH V10 38/46] drm/amd/display: add multiplier colorop

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
V10:
 - Remove redundant DRM_ERROR(...)

V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)

 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 40 +++
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 15 +++
 2 files changed, 55 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 b0223c6884c1..f110b5ae3a77 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
@@ -1385,6 +1385,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,
@@ -1599,6 +1628,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 3b099eba67d6..c31c201e0a83 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
@@ -89,6 +89,21 @@ 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]) {
+   ret = -ENOMEM;
+   goto cleanup;
+   }
+
+   ret = drm_plane_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 V10 37/46] drm/colorop: Add multiplier type

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
V10:
 - Fix typo of mutliplier to multiplier in subject (Melissa Wen)

V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)

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 1dda90554e46..907ca790689f 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 6839363fa05e..405f4234e641 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -739,6 +739,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);
@@ -764,6 +766,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 6c87f5b9f7f9..a3cf62c5e263 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[] = {
@@ -327,6 +328,37 @@ int drm_plane_colorop_ctm_3x4_init(struct drm_device *dev, 
struct drm_colorop *c
 }
 EXPORT_SYMBOL(drm_plane_colorop_ctm_3x4_init);
 
+/**
+ * drm_plane_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_plane_colorop_mult_init(struct drm_device *dev, struct drm_colorop 
*colorop,
+   struct drm_plane *plane)
+{
+   struct drm_property *prop;
+   int ret;
+
+   ret = drm_plane_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_plane_colorop_mult_init);
+
 static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop 
*colorop,
struct 
drm_colorop_state *state)
 {
@@ -418,6 +450,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 52e965577bd8..888184aef7a0 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -146,6 +146,13 @@ struct

[PATCH V10 40/46] drm/colorop: Define LUT_1D interpolation

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
V10
 - 1D LUT is no longer immutable (Xaver Hugl)
 - Add setting 1D LUT's property in drm_atomic_colorop_set_property

V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)

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_atomic_uapi.c |  4 +++
 drivers/gpu/drm/drm_colorop.c | 36 ++-
 include/drm/drm_colorop.h | 19 +-
 include/uapi/drm/drm_mode.h   | 13 +++
 6 files changed, 76 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 f89f6631062e..60a5bfdc2578 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
@@ -126,7 +126,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
goto cleanup;
}
 
-   ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, 
MAX_COLOR_LUT_ENTRIES);
+   ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, 
MAX_COLOR_LUT_ENTRIES,
+ 
DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR);
if (ret)
goto cleanup;
 
@@ -156,7 +157,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
goto cleanup;
}
 
-   ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, 
MAX_COLOR_LUT_ENTRIES);
+   ret = drm_plane_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 907ca790689f..90df638f25d5 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_atomic_uapi.c 
b/drivers/gpu/drm/drm_atomic_uapi.c
index 405f4234e641..40ee221b6496 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -737,6 +737,8 @@ static int drm_atomic_colorop_set_property(struct 
drm_colorop *colorop,
 {
if (property == colorop->bypass_property) {
state->bypass = val;
+   } else if (property == colorop->lut1d_interpolation_property) {
+   colorop->lut1d_interpolation = val;
} else if (property == colorop->curve_1d_type_property) {
state->curve_1d_type = val;
} else if (property == colorop->multiplier_property) {
@@ -764,6 +766,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop,
*val = colorop->type;
} else if (property == colorop->bypass_property) {
*val = state->bypass;
+   } else if (property == colorop->lut1d_interpolation_property) {
+   *val = colorop->lut1d_interpolation;
} else if (property == colorop->curve_1d_type_property) {
*val = state->curve_1d_type;
} else if (property == colorop->multiplier_property) {
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index a3cf62c5e263..1617b2742551 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_BT2020_OETF] = "BT.2020 OETF",
 };
 
+static const struct drm_prop_enum_list drm_colorop_lut1d_interpolation_list[] 
= {
+   { DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, "Linear" },
+};
 
 /* Init Helpers */
 
@@ -275,10 +278,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
  * @

[PATCH V10 39/46] drm/amd/display: Swap matrix and multiplier

2025-06-16 Thread Alex Hung
Swap the order of matrix and multiplier as designed in hardware.

Signed-off-by: Alex Hung 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)

 .../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 f110b5ae3a77..47414f9c5757 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
@@ -1617,25 +1617,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 c31c201e0a83..f89f6631062e 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
@@ -74,14 +74,14 @@ 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]) {
ret = -ENOMEM;
goto cleanup;
}
 
-   ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane);
+   ret = drm_plane_colorop_mult_init(dev, ops[i], plane);
if (ret)
goto cleanup;
 
@@ -89,14 +89,14 @@ 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]) {
ret = -ENOMEM;
goto cleanup;
}
 
-   ret = drm_plane_colorop_mult_init(dev, ops[i], plane);
+   ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane);
if (ret)
goto cleanup;
 
-- 
2.43.0



[PATCH V10 42/46] drm/colorop: Add 3D LUT support to color pipeline

2025-06-16 Thread Alex Hung
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.

Reviewed-by: Simon Ser 
Signed-off-by: Alex Hung 
Reviewed-by: Daniel Stone 
Reviewed-by: Melissa Wen 
---
V10:
 - Add missing set lut3d_interpolation in drm_atomic_colorop_set_property
 - LUT3D_INTERPOLATION is no longer immutable (Xaver Hugl)
 - Support 32BIT RGB in 3D LUT with drm_color_lut_32 (Harry Wentland)
 - Fix 3D LUT kernel doc (Leandro Ribeiro)

V9:
 - Update function names by _plane_ (Chaitanya Kumar Borah)

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 |  8 
 drivers/gpu/drm/drm_colorop.c | 72 +++
 include/drm/drm_colorop.h | 21 +
 include/uapi/drm/drm_mode.h   | 34 +++
 5 files changed, 141 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 41dd6a90e13b..35640154c70a 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 40ee221b6496..828d53e7b460 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -718,6 +718,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_32);
+   break;
default:
/* should never get here */
return -EINVAL;
@@ -743,6 +747,8 @@ static int drm_atomic_colorop_set_property(struct 
drm_colorop *colorop,
state->curve_1d_type = val;
} else if (property == colorop->multiplier_property) {
state->multiplier = val;
+   } else if (property == colorop->lut3d_interpolation_property) {
+   colorop->lut3d_interpolation = val;
} else if (property == colorop->data_property) {
return drm_atomic_color_set_data_property(colorop, state,
  property, val);
@@ -774,6 +780,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 19cb4ecd3033..52c08d717944 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_plane_colorop_init(struct drm_device *dev, struct drm_colorop 
*colorop,
@@ -381,6 +387,51 @@ int drm_plane_colorop_mult_init(struct drm_device *dev, 
struct drm_colorop *colo
 }
 EXPORT_SYMBOL(drm_plane_colorop_mult_init);
 
+int drm_plane_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop 
*colorop,
+struct drm_plane *plane,
+uint32_t lut_size,
+ 

[PATCH V10 46/46] drm/amd/display: Disable CRTC degamma when color pipeline is enabled

2025-06-16 Thread Alex Hung
The degamma is to be handled by Color pipeline API.

Signed-off-by: Alex Hung 
---
V10:
 - Disable CRTC degamma when color pipeline is enabled (Melissa Wen)

 .../drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c| 15 ++-
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index 87058271b00c..62bb4855306d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -685,7 +685,7 @@ int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm,
 {
struct amdgpu_crtc *acrtc = NULL;
struct drm_plane *cursor_plane;
-   bool is_dcn;
+   bool has_degamma;
int res = -ENOMEM;
 
cursor_plane = kzalloc(sizeof(*cursor_plane), GFP_KERNEL);
@@ -724,11 +724,16 @@ int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm,
 
dm->adev->mode_info.crtcs[crtc_index] = acrtc;
 
-   /* Don't enable DRM CRTC degamma property for DCE since it doesn't
-* support programmable degamma anywhere.
+   /* Don't enable DRM CRTC degamma property for
+* 1. Degamma is replaced by color pipeline.
+* 2. DCE since it doesn't support programmable degamma anywhere.
 */
-   is_dcn = dm->adev->dm.dc->caps.color.dpp.dcn_arch;
-   drm_crtc_enable_color_mgmt(&acrtc->base, is_dcn ? MAX_COLOR_LUT_ENTRIES 
: 0,
+   if (plane->color_pipeline_property)
+   has_degamma = false;
+   else
+   has_degamma = dm->adev->dm.dc->caps.color.dpp.dcn_arch;
+
+   drm_crtc_enable_color_mgmt(&acrtc->base, has_degamma ? 
MAX_COLOR_LUT_ENTRIES : 0,
   true, MAX_COLOR_LUT_ENTRIES);
 
drm_mode_crtc_set_gamma_size(&acrtc->base, 
MAX_COLOR_LEGACY_LUT_ENTRIES);
-- 
2.43.0



[PATCH V10 43/46] drm/amd/display: add 3D LUT colorop

2025-06-16 Thread Alex Hung
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 
Reviewed-by: Daniel Stone 
---
V10:
 - Support 32BIT RGB in 3D LUT with drm_color_lut_32 (Harry Wentland)
 - Remove redundant DRM_ERROR(...)

V9:
 - Return a value in __set_dm_plane_colorop_3dlut

v8:
 - Set initialized to 0 and return when drm_lut3d_size is 0 (Harry Wentland)
 - Rework tf->type = TF_TYPE_BYPASS for shaper (Harry Wentland & Leo Li)

v7:
 - Simplify 3D LUT according to drm_colorop changes (Simon Ser)

 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 147 ++
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c |  19 +++
 2 files changed, 166 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index 47414f9c5757..a2e76df039d6 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
@@ -873,6 +873,59 @@ static void __drm_3dlut_to_dc_3dlut(const struct 
drm_color_lut *lut,
__to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth);
 }
 
+static void __to_dc_lut3d_32_color(struct dc_rgb *rgb,
+  const struct drm_color_lut_32 lut,
+  int bit_precision)
+{
+   rgb->red = drm_color_lut_extract(lut.red, bit_precision);
+   rgb->green = drm_color_lut_extract(lut.green, bit_precision);
+   rgb->blue  = drm_color_lut_extract(lut.blue, bit_precision);
+}
+
+static void __drm_3dlut_32_to_dc_3dlut(const struct drm_color_lut_32 *lut,
+  uint32_t lut3d_size,
+  struct tetrahedral_params *params,
+  bool use_tetrahedral_9,
+  int bit_depth)
+{
+   struct dc_rgb *lut0;
+   struct dc_rgb *lut1;
+   struct dc_rgb *lut2;
+   struct dc_rgb *lut3;
+   int lut_i, i;
+
+
+   if (use_tetrahedral_9) {
+   lut0 = params->tetrahedral_9.lut0;
+   lut1 = params->tetrahedral_9.lut1;
+   lut2 = params->tetrahedral_9.lut2;
+   lut3 = params->tetrahedral_9.lut3;
+   } else {
+   lut0 = params->tetrahedral_17.lut0;
+   lut1 = params->tetrahedral_17.lut1;
+   lut2 = params->tetrahedral_17.lut2;
+   lut3 = params->tetrahedral_17.lut3;
+   }
+
+   for (lut_i = 0, i = 0; i < lut3d_size - 4; lut_i++, i += 4) {
+   /*
+* We should consider the 3D LUT RGB values are distributed
+* along four arrays lut0-3 where the first sizes 1229 and the
+* other 1228. The bit depth supported for 3dlut channel is
+* 12-bit, but DC also supports 10-bit.
+*
+* TODO: improve color pipeline API to enable the userspace set
+* bit depth and 3D LUT size/stride, as specified by VA-API.
+*/
+   __to_dc_lut3d_32_color(&lut0[lut_i], lut[i], bit_depth);
+   __to_dc_lut3d_32_color(&lut1[lut_i], lut[i + 1], bit_depth);
+   __to_dc_lut3d_32_color(&lut2[lut_i], lut[i + 2], bit_depth);
+   __to_dc_lut3d_32_color(&lut3[lut_i], lut[i + 3], bit_depth);
+   }
+   /* lut0 has 1229 points (lut_size/4 + 1) */
+   __to_dc_lut3d_32_color(&lut0[lut_i], lut[i], bit_depth);
+}
+
 /* amdgpu_dm_atomic_lut3d - set DRM 3D LUT to DC stream
  * @drm_lut3d: user 3D LUT
  * @drm_lut3d_size: size of 3D LUT
@@ -1426,6 +1479,7 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state 
*plane_state,
struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func;
const struct drm_color_lut_32 *shaper_lut;
struct drm_device *dev = colorop->dev;
+   bool enabled = false;
uint32_t shaper_size;
int i = 0, ret = 0;
 
@@ -1447,6 +1501,7 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state 
*plane_state,
ret = __set_output_tf(tf, 0, 0, false);
if (ret)
return ret;
+   enabled = true;
}
 
/* 1D LUT - SHAPER LUT */
@@ -1478,12 +1533,93 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state 
*plane_state,
ret = __set_output_tf_32(tf, shaper_lut, shaper_size, 
false);
if (ret)
return ret;
+   enabled = true;
}
}
 
+   if (!enabled)
+   tf->type = TF_TYPE_BYPASS;
+
return 0;
 }
 
+/* __set_colorop_3dlut - set DRM 3D LUT to DC stream
+ * @drm_lut3d: user 3D LUT
+ * @drm_lut3d_size: size of 3D LUT
+ * @lut3d: DC 3D LUT
+ *
+ * Map user 3D LUT data to DC 3D LUT and all necessary bits to pr

[PATCH V10 45/46] drm/amd/display: Ensure 3D LUT for color pipeline

2025-06-16 Thread Alex Hung
Check dpp.hw_3d_lut before creating shaper tf/lut and 3dlut colorops in
colorpipeline and handling these colorops.

Signed-off-by: Alex Hung 
---
V10:
 - Check dpp.hw_3d_lut before creating shaper tf/lut and 3dlut colorops
   (Melissa Wen)

 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   |  47 
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 103 +-
 2 files changed, 78 insertions(+), 72 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 b53aecd1bebc..c6d4a9365c00 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
@@ -1825,6 +1825,7 @@ amdgpu_dm_plane_set_colorop_properties(struct 
drm_plane_state *plane_state,
 {
struct drm_colorop *colorop = plane_state->color_pipeline;
struct drm_device *dev = plane_state->plane->dev;
+   struct amdgpu_device *adev = drm_to_adev(dev);
int ret;
 
/* 1D Curve - DEGAM TF */
@@ -1857,32 +1858,34 @@ amdgpu_dm_plane_set_colorop_properties(struct 
drm_plane_state *plane_state,
if (ret)
return ret;
 
-   /* 1D Curve & LUT - SHAPER TF & LUT */
-   colorop = colorop->next;
-   if (!colorop) {
-   drm_dbg(dev, "no Shaper TF colorop found\n");
-   return -EINVAL;
-   }
+   if (adev->dm.dc->caps.color.dpp.hw_3d_lut) {
+   /* 1D Curve & LUT - SHAPER TF & LUT */
+   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;
+   ret = __set_dm_plane_colorop_shaper(plane_state, 
dc_plane_state, colorop);
+   if (ret)
+   return ret;
 
-   /* Shaper LUT colorop is already handled, just skip here */
-   colorop = colorop->next;
-   if (!colorop)
-   return -EINVAL;
+   /* Shaper LUT colorop is already handled, just skip here */
+   colorop = colorop->next;
+   if (!colorop)
+   return -EINVAL;
 
-   /* 3D LUT */
-   colorop = colorop->next;
-   if (!colorop) {
-   drm_dbg(dev, "no 3D LUT colorop found\n");
-   return -EINVAL;
-   }
+   /* 3D LUT */
+   colorop = colorop->next;
+   if (!colorop) {
+   drm_dbg(dev, "no 3D LUT colorop found\n");
+   return -EINVAL;
+   }
 
-   ret = __set_dm_plane_colorop_3dlut(plane_state, dc_plane_state, 
colorop);
-   if (ret)
-   return ret;
+   ret = __set_dm_plane_colorop_3dlut(plane_state, dc_plane_state, 
colorop);
+   if (ret)
+   return ret;
+   }
 
/* 1D Curve & LUT - BLND TF & LUT */
colorop = colorop->next;
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 680b4e783959..fdb653548c9a 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
@@ -31,6 +31,7 @@
 
 #include "amdgpu.h"
 #include "amdgpu_dm_colorop.h"
+#include "dc.h"
 
 const u64 amdgpu_dm_supported_degam_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
@@ -55,6 +56,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
 {
struct drm_colorop *ops[MAX_COLOR_PIPELINE_OPS];
struct drm_device *dev = plane->dev;
+   struct amdgpu_device *adev = drm_to_adev(dev);
int ret;
int i = 0;
 
@@ -108,57 +110,58 @@ int amdgpu_dm_initialize_default_pipeline(struct 
drm_plane *plane, struct drm_pr
 
i++;
 
-   /* 1D curve - SHAPER TF */
-   ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
-   if (!ops[i]) {
-   ret = -ENOMEM;
-   goto cleanup;
-   }
-
-   ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane,
- amdgpu_dm_supported_shaper_tfs,
- DRM_COLOROP_FLAG_ALLOW_BYPASS);
-   if (ret)
-   goto cleanup;
-
-   drm_colorop_set_next_property(ops[i-1], ops[i]);
-
-   i++;
-
-   /* 1D LUT - SHAPER LUT */
-   ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
-   if (!ops[i]) {
-   ret = -ENOMEM;
-   goto cleanup;
+   if (adev->dm.dc->caps.color.dpp.hw_3d_lut) {
+   /* 1D curve - SHAPER TF */
+   ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!ops[i]) {
+   ret = -ENOMEM;
+

RE: [PATCH V9 16/43] drm/colorop: Add 3x4 CTM type

2025-06-16 Thread Borah, Chaitanya Kumar



> -Original Message-
> From: Pekka Paalanen 
> Sent: Monday, June 16, 2025 7:02 PM
> To: Borah, Chaitanya Kumar 
> Cc: Alex Hung ; dri-de...@lists.freedesktop.org; amd-
> g...@lists.freedesktop.org; wayland-devel@lists.freedesktop.org;
> harry.wentl...@amd.com; leo@amd.com; ville.syrj...@linux.intel.com;
> m...@igalia.com; jad...@redhat.com; sebastian.w...@redhat.com;
> shashank.sha...@amd.com; ago...@nvidia.com; jos...@froggi.es;
> mdaen...@redhat.com; aleix...@kde.org; xaver.h...@gmail.com;
> victo...@system76.com; dan...@ffwll.ch; Shankar, Uma
> ; quic_nas...@quicinc.com;
> quic_cbr...@quicinc.com; quic_abhin...@quicinc.com; mar...@marcan.st;
> liviu.du...@arm.com; sashamcint...@google.com;
> louis.chau...@bootlin.com; Daniel Stone 
> Subject: Re: [PATCH V9 16/43] drm/colorop: Add 3x4 CTM type
> 
> On Mon, 16 Jun 2025 11:30:23 +
> "Borah, Chaitanya Kumar"  wrote:
> 
> > > -Original Message-
> > > From: Alex Hung 
> > > Sent: Wednesday, April 30, 2025 6:41 AM
> > > To: dri-de...@lists.freedesktop.org; amd-...@lists.freedesktop.org
> > > Cc: wayland-devel@lists.freedesktop.org; harry.wentl...@amd.com;
> > > alex.h...@amd.com; leo@amd.com; ville.syrj...@linux.intel.com;
> > > pekka.paala...@collabora.com; cont...@emersion.fr; m...@igalia.com;
> > > jad...@redhat.com; sebastian.w...@redhat.com;
> > > shashank.sha...@amd.com; ago...@nvidia.com; jos...@froggi.es;
> > > mdaen...@redhat.com; aleix...@kde.org; xaver.h...@gmail.com;
> > > victo...@system76.com; dan...@ffwll.ch; Shankar, Uma
> > > ; quic_nas...@quicinc.com;
> > > quic_cbr...@quicinc.com; quic_abhin...@quicinc.com;
> > > mar...@marcan.st; liviu.du...@arm.com; sashamcint...@google.com;
> > > Borah, Chaitanya Kumar ;
> > > louis.chau...@bootlin.com; Daniel Stone 
> > > Subject: [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.
> >
> > Is there any value to adding pre-offsets [1] in the uapi?
> >
> >  |R/Cr|| c0 c1 c2 |   ( |R/Cr|   |preoff0| )   |postoff0|
> >  |G/Y | = | c3 c4 c5 | x ( |G/Y | + |preoff1| ) + |postoff1|
> >  |B/Cb|   | c6 c7 c8 |   ( |B/Cb|   |preoff2| )   |postoff2|
> >
> > Handling limited range values is one use case that I can think of.
> 
> Hi,
> 
> in the mathematical sense, no. A pre-offset can always be converted into a
> post-offset by multiplying it with the 3x3 matrix (and adding to the existing
> post-offset). This can be pre-computed, no need to do it separately for every
> pixel.
> 
> For hardware reasons, I have no idea.

Thank you for the reply, Pekka. Our hardware does allow programming Pre-offsets 
separately.
Currently I can't think of a particular advantage of that if mathematically a 
post-offset does the job but I will keep this thread posted if I find something 
out.

Regards

Chaitanya

> 
> > [1]
> > https://cgit.freedesktop.org/drm-tip/tree/drivers/gpu/drm/i915/display
> > /intel_color.c#n112
> >
> 
> Thanks,
> pq