Re: [PATCH 1/5] drm/amd: Introduce additional IPS debug flags

2024-08-19 Thread Harry Wentland



On 2024-08-16 18:57, sunpeng...@amd.com wrote:
> From: Leo Li 
> 
> [Why]
> 
> Idle power states (IPS) describe levels of power-gating within DCN. DM
> and DC is responsible for ensuring that we are out of IPS before any DCN
> programming happens. Any DCN programming while we're in IPS leads to
> undefined behavior (mostly hangs).
> 
> Because IPS intersects with all display features, the ability to disable
> IPS by default while ironing out the known issues is desired. However,
> disabing it completely will cause important features such as s0ix entry
> to fail.
> 
> Therefore, more granular IPS debug flags are desired.
> 
> [How]
> 
> Extend the dc debug mask bits to include the available list of IPS
> debug flags.
> 
> All the flags should work as documented, with the exception of
> IPS_DISABLE_DYNAMIC. It requires dm changes which will be done in
> later changes.
> 
> Signed-off-by: Leo Li 
> ---
>  .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |  6 ++
>  drivers/gpu/drm/amd/include/amd_shared.h  | 59 ++-
>  2 files changed, 64 insertions(+), 1 deletion(-)
> 
> 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 dd8353283bda3..a18ecf8607232 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -1864,6 +1864,12 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
>  
>   if (amdgpu_dc_debug_mask & DC_DISABLE_IPS)
>   init_data.flags.disable_ips = DMUB_IPS_DISABLE_ALL;
> + else if (amdgpu_dc_debug_mask & DC_DISABLE_IPS_DYNAMIC)
> + init_data.flags.disable_ips = DMUB_IPS_DISABLE_DYNAMIC;
> + else if (amdgpu_dc_debug_mask & DC_DISABLE_IPS2_DYNAMIC)
> + init_data.flags.disable_ips = 
> DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF;
> + else if (amdgpu_dc_debug_mask & DC_FORCE_IPS_ENABLE)
> + init_data.flags.disable_ips = DMUB_IPS_ENABLE;
>   else
>   init_data.flags.disable_ips = DMUB_IPS_ENABLE;
>  
> diff --git a/drivers/gpu/drm/amd/include/amd_shared.h 
> b/drivers/gpu/drm/amd/include/amd_shared.h
> index f5b725f10a7ce..6c12ca954a53e 100644
> --- a/drivers/gpu/drm/amd/include/amd_shared.h
> +++ b/drivers/gpu/drm/amd/include/amd_shared.h
> @@ -61,7 +61,7 @@ enum amd_apu_flags {
>  * acquires the list of IP blocks for the GPU in use on initialization.
>  * It can then operate on this list to perform standard driver operations
>  * such as: init, fini, suspend, resume, etc.
> -* 
> +*
>  *
>  * IP block implementations are named using the following convention:
>  * _v (E.g.: gfx_v6_0).
> @@ -251,19 +251,76 @@ enum DC_FEATURE_MASK {
>   DC_REPLAY_MASK = (1 << 9), //0x200, disabled by default for dcn < 3.1.4
>  };
>  
> +/**
> + * enum DC_DEBUG_MASK - Bits that are useful for debugging the display 
> driver.
> + */
>  enum DC_DEBUG_MASK {
> + /* @DC_DISABLE_PIPE_SPLIT: If set, disable pipe-splitting */

This function has a kernel doc but these enum docs are not
using /**. Would be nice to fix that. Not sure if we're generating
kernel docs for this enum.

Harry

>   DC_DISABLE_PIPE_SPLIT = 0x1,
> +
> + /* @DC_DISABLE_STUTTER: If set, disable memory stutter mode */
>   DC_DISABLE_STUTTER = 0x2,
> +
> + /* @DC_DISABLE_DSC: If set, disable display stream compression */
>   DC_DISABLE_DSC = 0x4,
> +
> + /*
> +  * @DC_DISABLE_CLOCK_GATING: If set, disable clock gating optimizations
> +  */
>   DC_DISABLE_CLOCK_GATING = 0x8,
> +
> + /* @DC_DISABLE_PSR: If set, disable Panel self refresh v1 and PSR-SU */
>   DC_DISABLE_PSR = 0x10,
> +
> + /*
> +  * @DC_FORCE_SUBVP_MCLK_SWITCH: If set, force mclk switch in subvp, even
> +  * if mclk switch in vblank is possible
> +  */
>   DC_FORCE_SUBVP_MCLK_SWITCH = 0x20,
> +
> + /* @DC_DISABLE_MPO: If set, disable multi-plane offloading */
>   DC_DISABLE_MPO = 0x40,
> +
> + /* @DC_ENABLE_DPIA_TRACE: If set, enable trace logging for DPIA */
>   DC_ENABLE_DPIA_TRACE = 0x80,
> +
> + /*
> +  * @DC_ENABLE_DML2: If set, force usage of DML2, even if the DCN version
> +  * does not default to it.
> +  */
>   DC_ENABLE_DML2 = 0x100,
> +
> + /* @DC_DISABLE_PSR_SU: If set, disable PSR SU */
>   DC_DISABLE_PSR_SU = 0x200,
> +
> + /* @DC_DISABLE_REPLAY: If set, disable Panel Replay */
>   DC_DISABLE_REPLAY = 0x400,
> +
> + /*
> +  * @DC_DISABLE_IPS: If set, disable all Idle Power States, all the time.
> +  * If more than one IPS debug bit is set, the lowest bit takes
> +  * precedence. For example, if DC_FORCE_IPS_ENABLE and
> +  * DC_DISABLE_IPS_DYNAMIC are set, then DC_DISABLE_IPS_DYNAMIC takes
> +  * precedence.
> +  */
>   DC_DISABLE_IPS = 0x800,
> +
> + /*
> +  * @DC_DISABLE_IPS_DYNAMIC: If set, disable all IPS, all the time,
> +  * *except* when driver goes into suspend.
> +  */
> 

[PATCH v5 01/44] drm: Add helper for conversion from signed-magnitude

2024-08-19 Thread Harry Wentland
CTM values are defined as signed-magnitude values. Add
a helper that converts from CTM signed-magnitude fixed
point value to the twos-complement value used by
drm_fixed.

Signed-off-by: Harry Wentland 
---
 include/drm/drm_fixed.h | 18 ++
 1 file changed, 18 insertions(+)

diff --git a/include/drm/drm_fixed.h b/include/drm/drm_fixed.h
index ef8bc8d72039..793768c1e776 100644
--- a/include/drm/drm_fixed.h
+++ b/include/drm/drm_fixed.h
@@ -77,6 +77,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.46.0



[PATCH v5 02/44] drm/vkms: Round fixp2int conversion in lerp_u16

2024-08-19 Thread Harry Wentland
fixp2int always rounds down, fixp2int_ceil rounds up. We need
the new fixp2int_round.

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/vkms/vkms_composer.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
b/drivers/gpu/drm/vkms/vkms_composer.c
index e7441b227b3c..3d6785d081f2 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -98,7 +98,7 @@ static u16 lerp_u16(u16 a, u16 b, s64 t)
 
s64 delta = drm_fixp_mul(b_fp - a_fp,  t);
 
-   return drm_fixp2int(a_fp + delta);
+   return drm_fixp2int_round(a_fp + delta);
 }
 
 static s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value)
-- 
2.46.0



[PATCH v5 00/44] Color Pipeline API w/ VKMS

2024-08-19 Thread Harry Wentland
ING properties
 - add CTM colorop to VKMS
 - Use include way for kunit testing static functions (Arthur)
 - Make TYPE a range property
 - Move enum drm_colorop_type to uapi header
 - and a bunch of smaller bits that are highlighted in the relevant commit
   description

v2:
 - Rebased on drm-misc-next
 - Introduce a VKMS Kunit so we can test LUT functionality in vkms_composer
 - Incorporate feedback in color_pipeline.rst doc
 - Add support for sRGB inverse EOTF
 - Add 2nd enumerated TF colorop to VKMS
 - Fix LUTs and some issues with applying LUTs in VKMS

Cc: Ville Syrjala 
Cc: Pekka Paalanen 
Cc: Simon Ser 
Cc: Harry Wentland 
Cc: Melissa Wen 
Cc: Jonas Ådahl 
Cc: Sebastian Wick 
Cc: Shashank Sharma 
Cc: Alexander Goins 
Cc: Joshua Ashton 
Cc: Michel Dänzer 
Cc: Aleix Pol 
Cc: Xaver Hugl 
Cc: Victoria Brekenfeld 
Cc: Sima 
Cc: Uma Shankar 
Cc: Naseer Ahmed 
Cc: Christopher Braga 
Cc: Abhinav Kumar 
Cc: Arthur Grillo 
Cc: Hector Martin 
Cc: Liviu Dudau 
Cc: Sasha McIntosh 
Cc: Chaitanya Kumar Borah 

Alex Hung (13):
  drm/colorop: define a new macro for_each_new_colorop_in_state
  drm/amd/display: Skip color pipeline initialization for cursor plane
  drm/amd/display: Add support for sRGB EOTF in DEGAM block
  drm/amd/display: Add support for sRGB Inverse EOTF in SHAPER block
  drm/amd/display: Add support for sRGB EOTF in BLND block
  drm/colorop: Add 1D Curve Custom LUT type
  drm/amd/display: add shaper and blend colorops for 1D Curve Custom LUT
  drm/amd/display: add 3x4 matrix colorop
  drm/colorop: Add mutliplier type
  drm/amd/display: add multiplier colorop
  drm/amd/display: Swap matrix and multiplier
  drm/colorop: Add 3D LUT supports to color pipeline
  drm/amd/display: add 3D LUT colorop

Harry Wentland (31):
  drm: Add helper for conversion from signed-magnitude
  drm/vkms: Round fixp2int conversion in lerp_u16
  drm/vkms: Add kunit tests for VKMS LUT handling
  drm/doc/rfc: Describe why prescriptive color pipeline is needed
  drm/colorop: Introduce new drm_colorop mode object
  drm/colorop: Add TYPE property
  drm/colorop: Add 1D Curve subtype
  Documentation/gpu: document drm_colorop
  drm/colorop: Add BYPASS property
  drm/colorop: Add NEXT property
  drm/colorop: Add atomic state print for drm_colorop
  drm/plane: Add COLOR PIPELINE property
  drm/colorop: Add NEXT to colorop state print
  drm/vkms: Add enumerated 1D curve colorop
  drm/vkms: Add kunit tests for linear and sRGB LUTs
  drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE
  drm/colorop: Add 3x4 CTM type
  drm/vkms: Use s32 for internal color pipeline precision
  drm/vkms: add 3x4 matrix in color pipeline
  drm/tests: Add a few tests around drm_fixed.h
  drm/vkms: Add tests for CTM handling
  drm/colorop: pass plane_color_pipeline client cap to atomic check
  drm/amd/display: Ignore deprecated props when plane_color_pipeline set
  drm/amd/display: Add bypass COLOR PIPELINE
  drm/colorop: Add PQ 125 EOTF and its inverse
  drm/amd/display: Enable support for PQ 125 EOTF and Inverse
  drm/colorop: add BT2020/BT709 OETF and Inverse OETF
  drm/amd/display: Add support for BT.709 and BT.2020 TFs
  drm/colorop: Define LUT_1D interpolation
  drm/colorop: allow non-bypass colorops
  drm/amd/display: Add AMD color pipeline doc

 Documentation/gpu/drm-kms.rst |  15 +
 Documentation/gpu/rfc/color_pipeline.rst  | 376 
 Documentation/gpu/rfc/index.rst   |   3 +
 drivers/gpu/drm/Makefile  |   1 +
 .../gpu/drm/amd/display/amdgpu_dm/Makefile|   3 +-
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |   4 +
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 581 -
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 196 +
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h |  36 +
 .../amd/display/amdgpu_dm/amdgpu_dm_plane.c   |  32 +
 drivers/gpu/drm/drm_atomic.c  | 184 +++-
 drivers/gpu/drm/drm_atomic_helper.c   |  12 +
 drivers/gpu/drm/drm_atomic_state_helper.c |   5 +
 drivers/gpu/drm/drm_atomic_uapi.c | 175 
 drivers/gpu/drm/drm_colorop.c | 562 
 drivers/gpu/drm/drm_ioctl.c   |   7 +
 drivers/gpu/drm/drm_mode_config.c |   7 +
 drivers/gpu/drm/drm_plane.c   |  52 ++
 drivers/gpu/drm/tests/Makefile|   3 +-
 drivers/gpu/drm/tests/drm_fixp_test.c |  69 ++
 drivers/gpu/drm/vkms/Kconfig  |   5 +
 drivers/gpu/drm/vkms/Makefile |   4 +-
 drivers/gpu/drm/vkms/tests/.kunitconfig   |   4 +
 drivers/gpu/drm/vkms/tests/vkms_color_tests.c | 449 ++
 drivers/gpu/drm/vkms/vkms_colorop.c   | 102 +++
 drivers/gpu/drm/vkms/vkms_composer.c  | 115 ++-
 drivers/gpu/drm/vkms/vkms_drv.h   |   8 +
 drivers/gpu/drm/vkms/vkms_luts.c  | 802 ++
 drivers/gpu/drm/vkms/vkms_luts.h  |  12 +
 drivers/gpu/drm/vkms/vkms_plane.c |   2 +

[PATCH v5 08/44] Documentation/gpu: document drm_colorop

2024-08-19 Thread Harry Wentland
Signed-off-by: Harry Wentland 
---
 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 98d22ac163e8..5b844daabd44 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -30,6 +30,37 @@
 
 #include "drm_crtc_internal.h"
 
+/**
+ * DOC: overview
+ *
+ * A colorop represents a single color operation. Colorops are chained
+ * via the NEXT property and make up color pipelines. Color pipelines
+ * are advertised and selected via the COLOR_PIPELINE &drm_plane
+ * property.
+ *
+ * A colorop will be of a certain type, advertised by the read-only TYPE
+ * property. Each type of colorop will advertise a different set of
+ * properties and is programmed in a different manner. Types can be
+ * enumerated 1D curves, 1D LUTs, 3D LUTs, matrices, etc. See the
+ * &drm_colorop_type documentation for information on each type.
+ *
+ * If a colorop advertises the BYPASS property it can be bypassed.
+ *
+ * Since colorops cannot stand-alone and are used to describe colorop
+ * operations on a plane they don't have their own locking mechanism but
+ * are locked and programmed along with their associated &drm_plane.
+ *
+ * Colorops are only advertised and valid for atomic drivers and atomic
+ * userspace that signals the DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE client
+ * cap. When a driver advertises the COLOR_PIPELINE property on a
+ * &drm_plane and userspace signals the
+ * DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE the driver shall ignore all other
+ * plane color properties, such as COLOR_ENCODING and COLOR_RANGE.
+ *
+ * More information about colorops and color pipelines can be found at
+ * rfc/color_pipeline.rst.
+ */
+
 static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = {
{ DRM_COLOROP_1D_CURVE, "1D Curve" },
 };
-- 
2.46.0



[PATCH v5 13/44] drm/colorop: Add NEXT to colorop state print

2024-08-19 Thread Harry Wentland
v5:
 - Drop unused header definitions

v3:
 - Read NEXT ID from drm_colorop's next pointer

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_atomic.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index d333433319f6..9919a6f1092f 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -791,6 +791,7 @@ static void drm_atomic_colorop_print_state(struct 
drm_printer *p,
drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type));
drm_printf(p, "\tbypass=%u\n", state->bypass);
drm_printf(p, "\tcurve_1d_type=%s\n", 
drm_get_colorop_curve_1d_type_name(state->curve_1d_type));
+   drm_printf(p, "\tnext=%d\n", colorop->next ? colorop->next->base.id : 
0);
 }
 
 static void drm_atomic_plane_print_state(struct drm_printer *p,
-- 
2.46.0



[PATCH v5 04/44] drm/doc/rfc: Describe why prescriptive color pipeline is needed

2024-08-19 Thread Harry Wentland
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)

Signed-off-by: Harry Wentland 
---
 Documentation/gpu/rfc/color_pipeline.rst | 376 +++
 Documentation/gpu/rfc/index.rst  |   3 +
 2 files changed, 379 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 ..4135f07b85a7
--- /dev/null
+++ b/Documentation/gpu/rfc/color_pipeline.rst
@@ -0,0 +1,376 @@
+
+Linux Color Pipeline API
+
+
+What problem are we solving?
+
+
+We would like to support pre-, and post-blending complex color
+transformations in display controller hardware in order to allow for
+HW-supported HDR use-cases, as well as to provide support to
+color-managed applications, such as video or image editors.
+
+It is possible to support an HDR output on HW supporting the Colorspace
+and HDR Metadata drm_connector properties, but that requires the
+compositor or application to render and compose the content into one
+final buffer intended for display. Doing so is costly.
+
+Most modern display HW offers various 1D LUTs, 3D LUTs, matrices, and other
+operations to support color transformations. These operations are often
+implemented in fixed-function HW and therefore much more power efficient than
+performing similar operations via shaders or CPU.
+
+We would like to make use of this HW functionality to support complex color
+transformations with no, or minimal CPU or shader load.
+
+
+How are other OSes solving this problem?
+
+
+The most widely supported use-cases regard HDR content, whether video or
+gaming.
+
+Most OSes will specify the source content format (color gamut, encoding 
transfer
+function, and other metadata, such as max and average light levels) to a 
driver.
+Drivers will then program their fixed-function HW accordingly to map from a
+source content buffer's space to a display's space.
+
+When fixed-function HW is not available the compositor will assemble a shader 
to
+ask the GPU to perform the transformation from the source content format to the
+display's format.
+
+A compositor's mapping function and a driver's mapping function are usually
+entirely separate concepts. On OSes where a HW vendor has no insight into
+closed-source compositor code such a vendor will tune their color management
+code to visually match the compositor's. On other OSes, where both mapping
+functions are open to an implementer they will ensure both mappings match.
+
+This results in mapping algorithm lock-in, meaning that no-one alone can
+experiment with or introduce new mapping algorithms and achieve
+consistent results regardless of which implementation path is taken.
+
+Why is Linux different?
+===
+
+Unlike other OSes, where there is one compositor for one or more drivers, on
+Linux we have a many-to-many relationship. Many compositors; many drivers.
+In addition each compositor vendor or community has their own view of how
+color management should be done. This is what makes Linux so beautiful.
+
+This means that a HW vendor can now no longer tune their driver to one
+compositor, as tuning it to one could make it look fairly different from
+another compositor's color mapping.
+
+We need a better solution.
+
+
+Descriptive API
+===
+
+An API that describes the source and destination colorspaces is a descriptive
+API. It describes the input and output color spaces but does not describe
+how precisely they should be mapped. Such a mapping includes many minute
+design decision that can greatly affect the look of the final result

[PATCH v5 11/44] drm/colorop: Add atomic state print for drm_colorop

2024-08-19 Thread Harry Wentland
Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_atomic.c | 25 -
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index b09db9835e1f..096460d38545 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -780,6 +780,19 @@ static int drm_atomic_plane_check(const struct 
drm_plane_state *old_plane_state,
return 0;
 }
 
+
+
+static void drm_atomic_colorop_print_state(struct drm_printer *p,
+   const struct drm_colorop_state *state)
+{
+   struct drm_colorop *colorop = state->colorop;
+
+   drm_printf(p, "colorop[%u]:\n", colorop->base.id);
+   drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type));
+   drm_printf(p, "\tbypass=%u\n", state->bypass);
+   drm_printf(p, "\tcurve_1d_type=%s\n", 
drm_get_colorop_curve_1d_type_name(state->curve_1d_type));
+}
+
 static void drm_atomic_plane_print_state(struct drm_printer *p,
const struct drm_plane_state *state)
 {
@@ -801,7 +814,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);
 }
@@ -1848,6 +1862,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;
@@ -1857,6 +1872,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.46.0



[PATCH v5 05/44] drm/colorop: Introduce new drm_colorop mode object

2024-08-19 Thread 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.

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)

Signed-off-by: Harry Wentland 
---
 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   | 103 +++
 drivers/gpu/drm/drm_mode_config.c   |   7 ++
 include/drm/drm_atomic.h|  89 
 include/drm/drm_atomic_uapi.h   |   1 +
 include/drm/drm_colorop.h   | 152 
 include/drm/drm_mode_config.h   |  18 
 include/drm/drm_plane.h |   8 ++
 include/uapi/drm/drm_mode.h |   1 +
 12 files changed, 510 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 68cc9258ffc4..45b687fadabe 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -44,6 +44,7 @@ drm-y := \
drm_client.o \
drm_client_modeset.o \
drm_color_mgmt.o \
+   drm_colorop.o \
drm_connector.o \
drm_crtc.o \
drm_displayid.o \
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 6e516c39a372..f82240eccc69 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"
@@ -108,6 +109,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);
@@ -139,6 +141,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
@@ -250,6 +256,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;
 
@@ -571,6 +591,56 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
 }
 EXPORT_SYMBOL(drm_atomic_get_plane_state);
 
+
+/**
+ * drm_atomic_get_colorop_state - get colorop state
+ * @state: global atomic state object
+ * @colorop: colorop to get state object for
+ *
+ * This function returns the colorop state for the given colorop, allocating it
+ * if needed. It will also grab the relevant plane lock to make sure that the
+ * state is consistent.
+ *
+ * Returns:
+ *
+ * Either the allocated state or the error code encoded into the pointer. When
+ * the error is EDEADLK then the w/w mutex code has detected a deadlock and the
+ * entire atomic sequence must be restarted. All other errors are fatal.
+ */
+struct drm_colorop_state *
+drm_atomic_get_colorop_state(struct drm_atomic_state *state,
+struct drm_colorop *colorop)
+{
+   int ret, index = drm_colorop_index(col

[PATCH v5 07/44] drm/colorop: Add 1D Curve subtype

2024-08-19 Thread Harry Wentland
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

Signed-off-by: Harry Wentland 
Signed-off-by: Alex Hung 
Co-developed-by: Alex Hung 
---
 drivers/gpu/drm/drm_atomic_uapi.c |  18 ++--
 drivers/gpu/drm/drm_colorop.c | 134 ++
 include/drm/drm_colorop.h |  60 +
 3 files changed, 207 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_uapi.c 
b/drivers/gpu/drm/drm_atomic_uapi.c
index f76a83d79993..b867bff49f67 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -648,11 +648,17 @@ static int drm_atomic_colorop_set_property(struct 
drm_colorop *colorop,
struct drm_colorop_state *state, struct drm_file *file_priv,
struct drm_property *property, uint64_t val)
 {
-   drm_dbg_atomic(colorop->dev,
-   "[COLOROP:%d] unknown property [PROP:%d:%s]]\n",
-   colorop->base.id,
-   property->base.id, property->name);
-   return -EINVAL;
+   if (property == colorop->curve_1d_type_property) {
+   state->curve_1d_type = val;
+   } else {
+   drm_dbg_atomic(colorop->dev,
+  "[COLOROP:%d:%d] unknown property 
[PROP:%d:%s]]\n",
+  colorop->base.id, colorop->type,
+  property->base.id, property->name);
+   return -EINVAL;
+   }
+
+   return 0;
 }
 
 static int
@@ -662,6 +668,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop,
 {
if (property == colorop->type_property) {
*val = colorop->type;
+   } else if (property == colorop->curve_1d_type_property) {
+   *val = state->curve_1d_type;
} else {
return -EINVAL;
}
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 79af42b349a0..98d22ac163e8 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -30,6 +30,123 @@
 
 #include "drm_crtc_internal.h"
 
+static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = {
+   { DRM_COLOROP_1D_CURVE, "1D Curve" },
+};
+
+static const char * const colorop_curve_1d_type_names[] = {
+   [DRM_COLOROP_1D_CURVE_SRGB_EOTF] = "sRGB EOTF",
+   [DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF] = "sRGB Inverse EOTF",
+};
+
+
+/* Init Helpers */
+
+static int drm_colorop_init(struct drm_device *dev, struct drm_colorop 
*colorop,
+   struct drm_plane *plane, enum drm_colorop_type type)
+{
+   struct drm_mode_config *config = &dev->mode_config;
+   struct drm_property *prop;
+   int ret = 0;
+
+   ret = drm_mode_object_add(dev, &colorop->base, DRM_MODE_OBJECT_COLOROP);
+   if (ret)
+   return ret;
+
+   colorop->base.properties = &colorop->properties;
+   colorop->dev = dev;
+   colorop->type = type;
+   colorop->plane = plane;
+
+   list_add_tail(&colorop->head, &config->colorop_list);
+   colorop->index = config->num_colorop++;
+
+   /* add properties */
+
+   /* type */
+   prop = drm_property_create_enum(dev,
+   DRM_MODE_PROP_IMMUTABLE,
+   "TYPE", drm_colorop_type_enum_list,
+   ARRAY_SIZE(drm_colorop_type_enum_list));
+
+   if (!prop)
+   return -ENOMEM;
+
+   colorop->type_property = prop;
+
+   drm_object_attach_property(&colorop->base,
+  colorop->type_property,
+  colorop->type);
+
+   return ret;
+}
+
+/**
+ * drm_colorop_curve_1d_init - Initialize a DRM_COLOROP_1D_CURVE
+ *
+ * @dev: DRM device
+ * @colorop: The drm_colorop object to initialize
+ * @plane: The associated drm_plane
+ * @supported_tfs: A bitfield of supported drm_colorop_curve_1d_init enum 
values,
+ * created using BIT(curve_type) and combined with the OR '|'
+ * operator.
+ * @return zero on success, -E value on failure
+ */
+int drm_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop 
*colorop,
+ struct drm_plane *plane, u64 supported_tfs)
+{
+   struct drm_prop_enum_list enum_list[DRM_COLOROP_1D_CURVE_COUNT];
+   int i, len;
+
+   struct drm_property *prop;
+   int ret;
+
+   if (!supported_tfs) {
+   drm_err(dev,
+   "No supported TFs for new 1D cu

[PATCH v5 09/44] drm/colorop: Add BYPASS property

2024-08-19 Thread Harry Wentland
We want to be able to bypass each colorop at all times.
Introduce a new BYPASS boolean property for this.

v5:
 - Drop TODO

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_atomic_uapi.c |  6 +-
 drivers/gpu/drm/drm_colorop.c | 15 +++
 include/drm/drm_colorop.h | 20 
 3 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_atomic_uapi.c 
b/drivers/gpu/drm/drm_atomic_uapi.c
index b867bff49f67..419813be21b0 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -648,7 +648,9 @@ static int drm_atomic_colorop_set_property(struct 
drm_colorop *colorop,
struct drm_colorop_state *state, struct drm_file *file_priv,
struct drm_property *property, uint64_t val)
 {
-   if (property == colorop->curve_1d_type_property) {
+   if (property == colorop->bypass_property) {
+   state->bypass = val;
+   } else if (property == colorop->curve_1d_type_property) {
state->curve_1d_type = val;
} else {
drm_dbg_atomic(colorop->dev,
@@ -668,6 +670,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop,
 {
if (property == colorop->type_property) {
*val = colorop->type;
+   } else if (property == colorop->bypass_property) {
+   *val = state->bypass;
} else if (property == colorop->curve_1d_type_property) {
*val = state->curve_1d_type;
} else {
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 5b844daabd44..054e7f01151b 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -109,6 +109,17 @@ static int drm_colorop_init(struct drm_device *dev, struct 
drm_colorop *colorop,
   colorop->type_property,
   colorop->type);
 
+   /* bypass */
+   prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC,
+   "BYPASS");
+   if (!prop)
+   return -ENOMEM;
+
+   colorop->bypass_property = prop;
+   drm_object_attach_property(&colorop->base,
+  colorop->bypass_property,
+  1);
+
return ret;
 }
 
@@ -166,6 +177,7 @@ int drm_colorop_curve_1d_init(struct drm_device *dev, 
struct drm_colorop *coloro
/* initialize 1D curve only attribute */
prop = drm_property_create_enum(dev, DRM_MODE_PROP_ATOMIC, 
"CURVE_1D_TYPE",
enum_list, len);
+
if (!prop)
return -ENOMEM;
 
@@ -182,6 +194,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 *
@@ -220,6 +234,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 dd6943117461..7ecf4e3dca4f 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -72,6 +72,14 @@ struct drm_colorop_state {
 
/* colorop properties */
 
+   /**
+* @bypass:
+*
+* True if colorop shall be bypassed. False if colorop is
+* enabled.
+*/
+   bool bypass;
+
/**
 * @curve_1d_type:
 *
@@ -150,6 +158,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.46.0



[PATCH v5 06/44] drm/colorop: Add TYPE property

2024-08-19 Thread 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.

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

For now we're only introducing an enumerated 1D LUT type to
illustrate the concept.

Signed-off-by: Harry Wentland 
---
 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   | 15 +++
 5 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index f82240eccc69..b09db9835e1f 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -634,8 +634,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 fce7683e4178..f76a83d79993 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -660,7 +660,13 @@ drm_atomic_colorop_get_property(struct drm_colorop 
*colorop,
const struct drm_colorop_state *state,
struct drm_property *property, uint64_t *val)
 {
-   return -EINVAL;
+   if (property == colorop->type_property) {
+   *val = colorop->type;
+   } else {
+   return -EINVAL;
+   }
+
+   return 0;
 }
 
 static int drm_atomic_set_writeback_fb_for_connector(
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index e005aba7d28a..79af42b349a0 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -101,3 +101,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 1bd658617840..75ec3ec827ac 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -94,6 +94,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)
@@ -148,5 +163,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 af71a5c84aa0..3dd89ae6515a 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -858,6 +858,21 @@ struct drm_color_lut {
__u16 reserved;
 };
 
+/**
+ * enum drm_colorop_type - Type of color operation
+ *
+ * @DRM_COLOROP_1D_CURVE: A 1D curve that is being applied to all color 
channels.
+ *The curve is specified via tha CURVE_1D_TYPE colorop
+ *property.
+ *
+ * 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.
+ 

[PATCH v5 03/44] drm/vkms: Add kunit tests for VKMS LUT handling

2024-08-19 Thread 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

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)

Signed-off-by: Harry Wentland 
Cc: Arthur Grillo 
---
 drivers/gpu/drm/vkms/Kconfig  |   5 +
 drivers/gpu/drm/vkms/tests/.kunitconfig   |   4 +
 drivers/gpu/drm/vkms/tests/vkms_color_tests.c | 163 ++
 drivers/gpu/drm/vkms/vkms_composer.c  |   4 +
 4 files changed, 176 insertions(+)
 create mode 100644 drivers/gpu/drm/vkms/tests/.kunitconfig
 create mode 100644 drivers/gpu/drm/vkms/tests/vkms_color_tests.c

diff --git a/drivers/gpu/drm/vkms/Kconfig b/drivers/gpu/drm/vkms/Kconfig
index b9ecdebecb0b..c1f8b343ff0e 100644
--- a/drivers/gpu/drm/vkms/Kconfig
+++ b/drivers/gpu/drm/vkms/Kconfig
@@ -13,3 +13,8 @@ config DRM_VKMS
  a VKMS.
 
  If M is selected the module will be called vkms.
+
+config DRM_VKMS_KUNIT_TESTS
+   tristate "Tests for VKMS" if !KUNIT_ALL_TESTS
+   depends on DRM_VKMS && KUNIT
+   default KUNIT_ALL_TESTS
diff --git a/drivers/gpu/drm/vkms/tests/.kunitconfig 
b/drivers/gpu/drm/vkms/tests/.kunitconfig
new file mode 100644
index ..70e378228cbd
--- /dev/null
+++ b/drivers/gpu/drm/vkms/tests/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_DRM=y
+CONFIG_DRM_VKMS=y
+CONFIG_DRM_VKMS_KUNIT_TESTS=y
diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_tests.c 
b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
new file mode 100644
index ..fc73e48aa57c
--- /dev/null
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#include 
+
+#include 
+
+#define TEST_LUT_SIZE 16
+
+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 },
+};
+
+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)
+{
+   int i;
+
+   KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&test_linear_lut, 
test_linear_array[0].red)), 0);
+
+   for (i = 0; i < TEST_LUT_SIZE; i++)
+   KUNIT_EXPECT_EQ(test, 
drm_fixp2int_ceil(get_lut_index(&test_linear_lut, test_linear_array[i].red)), 
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;
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x0, 0x10, t), 0x10);
+
+   /* odd a */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x1, 0x10, t), 0x10);
+
+   /* odd b */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x1, 0xf, t), 0xf);
+
+   /* b = a */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x10, 0x10, t), 0x10);
+
+   /* b = a + 1 */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x10, 0x11, 

[PATCH v5 10/44] drm/colorop: Add NEXT property

2024-08-19 Thread 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.

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

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_colorop.c | 30 ++
 include/drm/drm_colorop.h | 20 
 2 files changed, 50 insertions(+)

diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 054e7f01151b..685dd18126aa 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -88,6 +88,7 @@ static int drm_colorop_init(struct drm_device *dev, struct 
drm_colorop *colorop,
colorop->dev = dev;
colorop->type = type;
colorop->plane = plane;
+   colorop->next = NULL;
 
list_add_tail(&colorop->head, &config->colorop_list);
colorop->index = config->num_colorop++;
@@ -120,6 +121,16 @@ static int drm_colorop_init(struct drm_device *dev, struct 
drm_colorop *colorop,
   colorop->bypass_property,
   1);
 
+   /* next */
+   prop = drm_property_create_object(dev, DRM_MODE_PROP_IMMUTABLE | 
DRM_MODE_PROP_ATOMIC,
+ "NEXT", DRM_MODE_OBJECT_COLOROP);
+   if (!prop)
+   return -ENOMEM;
+   colorop->next_property = prop;
+   drm_object_attach_property(&colorop->base,
+  colorop->next_property,
+  0);
+
return ret;
 }
 
@@ -293,3 +304,22 @@ const char *drm_get_colorop_curve_1d_type_name(enum 
drm_colorop_curve_1d_type ty
 
return colorop_curve_1d_type_names[type];
 }
+
+/**
+ * drm_colorop_set_next_property - sets the next pointer
+ * @colorop: drm colorop
+ * @next: next colorop
+ *
+ * Should be used when constructing the color pipeline
+ */
+void drm_colorop_set_next_property(struct drm_colorop *colorop, struct 
drm_colorop *next)
+{
+   if (!colorop->next_property)
+   return;
+
+   drm_object_property_set_value(&colorop->base,
+ colorop->next_property,
+ next ? next->base.id : 0);
+   colorop->next = next;
+}
+EXPORT_SYMBOL(drm_colorop_set_next_property);
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index 7ecf4e3dca4f..5255cf44f4b1 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -150,6 +150,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:
 *
@@ -177,10 +185,20 @@ struct drm_colorop {
 */
struct drm_property *curve_1d_type_property;
 
+   /**
+* @next_property:
+*
+* Read-only property to next colorop in the pipeline
+*/
+   struct drm_property *next_property;
+
 };
 
 #define obj_to_colorop(x) container_of(x, struct drm_colorop, base)
 
+
+
+
 /**
  * drm_colorop_find - look up a Colorop object from its ID
  * @dev: DRM device
@@ -252,4 +270,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.46.0



[PATCH v5 19/44] drm/vkms: add 3x4 matrix in color pipeline

2024-08-19 Thread Harry Wentland
We add two 3x4 matrices into the VKMS color pipeline. The reason
we're adding matrices is so that we can test that application
of a matrix and its inverse yields an output equal to the input
image.

One complication with the matrix implementation has to do with
the fact that the matrix entries are in signed-magnitude fixed
point, whereas the drm_fixed.h implementation uses 2s-complement.
The latter one is the one that we want for easy addition and
subtraction, so we convert all entries to 2s-complement.

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/vkms/vkms_colorop.c  | 32 +++-
 drivers/gpu/drm/vkms/vkms_composer.c | 27 +++
 2 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c 
b/drivers/gpu/drm/vkms/vkms_colorop.c
index f61dfde47156..adcb08153a09 100644
--- a/drivers/gpu/drm/vkms/vkms_colorop.c
+++ b/drivers/gpu/drm/vkms/vkms_colorop.c
@@ -37,7 +37,37 @@ static int vkms_initialize_color_pipeline(struct drm_plane 
*plane, struct drm_pr
 
prev_op = op;
 
-   /* 2nd op: 1d curve */
+   /* 2nd op: 3x4 matrix */
+   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!op) {
+   DRM_ERROR("KMS: Failed to allocate colorop\n");
+   return -ENOMEM;
+   }
+
+   ret = drm_colorop_ctm_3x4_init(dev, op, plane);
+   if (ret)
+   return ret;
+
+   drm_colorop_set_next_property(prev_op, op);
+
+   prev_op = op;
+
+   /* 3rd op: 3x4 matrix */
+   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!op) {
+   DRM_ERROR("KMS: Failed to allocate colorop\n");
+   return -ENOMEM;
+   }
+
+   ret = drm_colorop_ctm_3x4_init(dev, op, plane);
+   if (ret)
+   return ret;
+
+   drm_colorop_set_next_property(prev_op, op);
+
+   prev_op = op;
+
+   /* 4th op: 1d curve */
op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
if (!op) {
DRM_ERROR("KMS: Failed to allocate colorop\n");
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
b/drivers/gpu/drm/vkms/vkms_composer.c
index 6e939d3a6d5c..bd1df06ced85 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -164,6 +164,30 @@ 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;
+
+   rf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[0]), 
drm_int2fixp(pixel->r)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[1]), 
drm_int2fixp(pixel->g)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[2]), 
drm_int2fixp(pixel->b)) +
+drm_sm2fixp(matrix->matrix[3]);
+
+   gf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[4]), 
drm_int2fixp(pixel->r)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[5]), 
drm_int2fixp(pixel->g)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[6]), 
drm_int2fixp(pixel->b)) +
+drm_sm2fixp(matrix->matrix[7]);
+
+   bf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[8]), 
drm_int2fixp(pixel->r)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[9]), 
drm_int2fixp(pixel->g)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[10]), 
drm_int2fixp(pixel->b)) +
+drm_sm2fixp(matrix->matrix[11]);
+
+   pixel->r = drm_fixp2int(rf);
+   pixel->g = drm_fixp2int(gf);
+   pixel->b = drm_fixp2int(bf);
+}
+
 static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop 
*colorop)
 {
struct drm_colorop_state *colorop_state = colorop->state;
@@ -184,6 +208,9 @@ static void apply_colorop(struct pixel_argb_s32 *pixel, 
struct drm_colorop *colo
DRM_DEBUG_DRIVER("unkown colorop 1D curve type 
%d\n", 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.46.0



[PATCH v5 16/44] drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE

2024-08-19 Thread Harry Wentland
With the introduction of the pre-blending color pipeline we
can no longer have color operations that don't have a clear
position in the color pipeline. We deprecate all existing
plane properties. For upstream drivers those are:
 - COLOR_ENCODING
 - COLOR_RANGE

Drivers are expected to ignore these properties when
programming the HW.

Setting of the COLOR_PIPELINE plane property or drm_colorop
properties is only allowed for userspace that sets this
client cap.

v5:
 - Fix kernel docs

v4:
 - Don't block setting of COLOR_RANGE and COLOR_ENCODING
   when client cap is set

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_atomic_uapi.c | 13 -
 drivers/gpu/drm/drm_ioctl.c   |  7 +++
 include/drm/drm_file.h|  7 +++
 include/uapi/drm/drm.h| 16 
 4 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_atomic_uapi.c 
b/drivers/gpu/drm/drm_atomic_uapi.c
index d8d5be358b3e..5dd834e3979b 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -570,6 +570,12 @@ static int drm_atomic_plane_set_property(struct drm_plane 
*plane,
} else if (property == plane->color_range_property) {
state->color_range = val;
} else if (property == plane->color_pipeline_property) {
+   if (!file_priv->plane_color_pipeline) {
+   drm_dbg_atomic(dev,
+  "Setting COLOR_PIPELINE plane property 
not permitted unless DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set\n");
+   return -EINVAL;
+   }
+
/* find DRM colorop object */
struct drm_colorop *colorop = NULL;
colorop = drm_colorop_find(dev, file_priv, val);
@@ -1186,6 +1192,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;
 
@@ -1198,7 +1210,6 @@ int drm_atomic_set_property(struct drm_atomic_state 
*state,
ret = drm_atomic_colorop_set_property(colorop,
colorop_state, file_priv,
prop, prop_value);
-
break;
}
default:
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 51f39912866f..6b5021b68e35 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -373,6 +373,13 @@ drm_setclientcap(struct drm_device *dev, void *data, 
struct drm_file *file_priv)
return -EINVAL;
file_priv->supports_virtualized_cursor_plane = req->value;
break;
+   case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE:
+   if (!file_priv->atomic)
+   return -EINVAL;
+   if (req->value > 1)
+   return -EINVAL;
+   file_priv->plane_color_pipeline = req->value;
+   break;
default:
return -EINVAL;
}
diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h
index ab230d3af138..63c1d29b8520 100644
--- a/include/drm/drm_file.h
+++ b/include/drm/drm_file.h
@@ -204,6 +204,13 @@ struct drm_file {
 */
bool writeback_connectors;
 
+   /**
+* @plane_color_pipeline:
+*
+* True if client understands plane color pipelines
+*/
+   bool plane_color_pipeline;
+
/**
 * @was_master:
 *
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 16122819edfe..a2fc85ecdea1 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -875,6 +875,22 @@ struct drm_get_cap {
  */
 #define DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT6
 
+/**
+ * DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE
+ *
+ * If set to 1 the DRM core will allow setting the COLOR_PIPELINE
+ * property on a &drm_plane, as well as drm_colorop properties.
+ *
+ * Drivers will ignore these drm_plane properties:
+ *  - COLOR_ENCODING
+ *  - COLOR_RANGE
+ *
+ * The client must enable &DRM_CLIENT_CAP_ATOMIC first.
+ *
+ * This capability is currently in development.
+ */
+#define DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE7
+
 /* DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
 struct drm_set_client_cap {
__u64 capability;
-- 
2.46.0



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

2024-08-19 Thread Harry Wentland
Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/vkms/tests/vkms_color_tests.c | 37 ++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_tests.c 
b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
index fc73e48aa57c..e6ac01dee830 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
@@ -31,7 +31,6 @@ const struct vkms_color_lut test_linear_lut = {
.channel_value2index_ratio = 0xf000fll
 };
 
-
 static void vkms_color_test_get_lut_index(struct kunit *test)
 {
int i;
@@ -40,6 +39,19 @@ static void vkms_color_test_get_lut_index(struct kunit *test)
 
for (i = 0; i < TEST_LUT_SIZE; i++)
KUNIT_EXPECT_EQ(test, 
drm_fixp2int_ceil(get_lut_index(&test_linear_lut, test_linear_array[i].red)), 
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)
@@ -148,9 +160,32 @@ 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),
{}
 };
 
-- 
2.46.0



[PATCH v5 26/44] drm/amd/display: Skip color pipeline initialization for cursor plane

2024-08-19 Thread Harry Wentland
From: Alex Hung 

Signed-off-by: Alex Hung 
---
 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 d260db42d407..8fc0f73fce33 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
@@ -1754,6 +1754,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.46.0



[PATCH v5 17/44] drm/colorop: Add 3x4 CTM type

2024-08-19 Thread 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.

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)

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_atomic.c  | 14 ++-
 drivers/gpu/drm/drm_atomic_uapi.c | 29 +++
 drivers/gpu/drm/drm_colorop.c | 39 +++
 include/drm/drm_colorop.h | 21 +
 include/uapi/drm/amdgpu_drm.h |  9 ---
 include/uapi/drm/drm_mode.h   | 33 ++
 6 files changed, 131 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 9919a6f1092f..c2fcde96e218 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -790,7 +790,19 @@ static void drm_atomic_colorop_print_state(struct 
drm_printer *p,
drm_printf(p, "colorop[%u]:\n", colorop->base.id);
drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type));
drm_printf(p, "\tbypass=%u\n", state->bypass);
-   drm_printf(p, "\tcurve_1d_type=%s\n", 
drm_get_colorop_curve_1d_type_name(state->curve_1d_type));
+
+   switch (colorop->type) {
+   case DRM_COLOROP_1D_CURVE:
+   drm_printf(p, "\tcurve_1d_type=%s\n",
+  
drm_get_colorop_curve_1d_type_name(state->curve_1d_type));
+   break;
+   case DRM_COLOROP_CTM_3X4:
+   drm_printf(p, "\tdata blob id=%d\n", state->data ? 
state->data->base.id : 0);
+   break;
+   default:
+   break;
+   }
+
drm_printf(p, "\tnext=%d\n", colorop->next ? colorop->next->base.id : 
0);
 }
 
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c 
b/drivers/gpu/drm/drm_atomic_uapi.c
index 5dd834e3979b..4a9cb1f7dbf7 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -691,6 +691,30 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
return 0;
 }
 
+static int drm_atomic_color_set_data_property(struct drm_colorop *colorop,
+   struct drm_colorop_state *state,
+   struct drm_property *property, uint64_t val)
+{
+   ssize_t elem_size = -1;
+   ssize_t size = -1;
+   bool replaced = false;
+
+   switch (colorop->type) {
+   case DRM_COLOROP_CTM_3X4:
+   size = sizeof(struct drm_color_ctm_3x4);
+   break;
+   default:
+   /* should never get here */
+   return -EINVAL;
+   }
+
+   return drm_property_replace_blob_from_id(colorop->dev,
+   &state->data,
+   val,
+   size,
+   elem_size,
+   &replaced);
+}
 
 static int drm_atomic_colorop_set_property(struct drm_colorop *colorop,
struct drm_colorop_state *state, struct drm_file *file_priv,
@@ -700,6 +724,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",
@@ -722,6 +749,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 685dd18126aa..73fd4b7a4ce3 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop

[PATCH v5 14/44] drm/vkms: Add enumerated 1D curve colorop

2024-08-19 Thread 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.

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

Signed-off-by: Harry Wentland 
Signed-off-by: Alex Hung 
---
 drivers/gpu/drm/vkms/Makefile|   4 +-
 drivers/gpu/drm/vkms/vkms_colorop.c  |  72 +++
 drivers/gpu/drm/vkms/vkms_composer.c |  51 ++
 drivers/gpu/drm/vkms/vkms_drv.h  |   4 +
 drivers/gpu/drm/vkms/vkms_luts.c | 802 +++
 drivers/gpu/drm/vkms/vkms_luts.h |  12 +
 drivers/gpu/drm/vkms/vkms_plane.c|   2 +
 7 files changed, 946 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/vkms/vkms_colorop.c
 create mode 100644 drivers/gpu/drm/vkms/vkms_luts.c
 create mode 100644 drivers/gpu/drm/vkms/vkms_luts.h

diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile
index 1b28a6a32948..c38455c46be4 100644
--- a/drivers/gpu/drm/vkms/Makefile
+++ b/drivers/gpu/drm/vkms/Makefile
@@ -6,6 +6,8 @@ vkms-y := \
vkms_formats.o \
vkms_crtc.o \
vkms_composer.o \
-   vkms_writeback.o
+   vkms_writeback.o \
+   vkms_colorop.o \
+   vkms_luts.o
 
 obj-$(CONFIG_DRM_VKMS) += vkms.o
diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c 
b/drivers/gpu/drm/vkms/vkms_colorop.c
new file mode 100644
index ..f61dfde47156
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_colorop.c
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "vkms_drv.h"
+
+#define MAX_COLOR_PIPELINES 5
+
+static const u64 supported_tfs =
+   BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
+   BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF);
+
+static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct 
drm_prop_enum_list *list)
+{
+
+   struct drm_colorop *op, *prev_op;
+   struct drm_device *dev = plane->dev;
+   int ret;
+
+   /* 1st op: 1d curve */
+   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!op) {
+   DRM_ERROR("KMS: Failed to allocate colorop\n");
+   return -ENOMEM;
+   }
+
+   ret = drm_colorop_curve_1d_init(dev, op, plane, supported_tfs);
+   if (ret)
+   return ret;
+
+   list->type = op->base.id;
+   list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", op->base.id);
+
+   prev_op = op;
+
+   /* 2nd op: 1d curve */
+   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!op) {
+   DRM_ERROR("KMS: Failed to allocate colorop\n");
+   return -ENOMEM;
+   }
+
+   ret = drm_colorop_curve_1d_init(dev, op, plane, supported_tfs);
+   if (ret)
+   return ret;
+
+   drm_colorop_set_next_property(prev_op, op);
+
+   return 0;
+}
+
+int vkms_initialize_colorops(struct drm_plane *plane)
+{
+   struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES];
+   int len = 0;
+   int ret;
+
+   /* Add color pipeline */
+   ret = vkms_initialize_color_pipeline(plane, &(pipelines[len]));
+   if (ret)
+   return ret;
+   len++;
+
+   /* Create COLOR_PIPELINE property and attach */
+   drm_plane_create_color_pipeline_property(plane, pipelines, len);
+
+   return 0;
+}
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
b/drivers/gpu/drm/vkms/vkms_composer.c
index 3ecda70c2b55..bc116d16e378 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -12,6 +12,7 @@
 #include 
 
 #include "vkms_drv.h"
+#include "vkms_luts.h"
 
 static u16 pre_mul_blend_channel(u16 src, u16 dst, u16 alpha)
 {
@@ -163,6 +164,53 @@ 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)
+{
+   struct drm_colorop_state *colorop_state = colorop->state;

[PATCH v5 18/44] drm/vkms: Use s32 for internal color pipeline precision

2024-08-19 Thread 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.

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

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/vkms/vkms_composer.c | 55 ++--
 drivers/gpu/drm/vkms/vkms_drv.h  |  4 ++
 2 files changed, 47 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
b/drivers/gpu/drm/vkms/vkms_composer.c
index bc116d16e378..6e939d3a6d5c 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -164,7 +164,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;
 
@@ -190,24 +190,55 @@ 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 drm_colorop *colorop = plane_state->base.base.color_pipeline;
+   struct drm_colorop *colorop;
+   struct pixel_argb_s32 pixel;
 
-   while (colorop) {
-   struct drm_colorop_state *colorop_state;
+   for (size_t x = 0; x < output_buffer->n_pixels; x++) {
 
-   if (!colorop)
-   return;
+   /*
+* 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;
 
-   colorop_state = colorop->state;
+   colorop = plane_state->base.base.color_pipeline;
+   while (colorop) {
+   struct drm_colorop_state *colorop_state;
 
-   if (!colorop_state)
-   return;
+   if (!colorop)
+   return;
+
+   colorop_state = colorop->state;
+
+   if (!colorop_state)
+   return;
 
-   for (size_t x = 0; x < output_buffer->n_pixels; x++)
if (!colorop_state->bypass)
-   apply_colorop(&output_buffer->pixels[x], 
colorop);
+   apply_colorop(&pixel, colorop);
 
-   colorop = colorop->next;
+   colorop = colorop->next;
+   }
+
+   /* clamp pixel */
+   pixel.a = max(min(pixel.a, 0x), 0x0);
+   pixel.r = max(min(pixel.r, 0x), 0x0);
+   pixel.g = max(min(pixel.g, 0x), 0x0);
+   pixel.b = max(min(pixel.b, 0x), 0x0);
+
+   /* put back to output_buffer */
+   output_buffer->pixels[x].a = pixel.a;
+   output_buffer->pixels[x].r = pixel.r;
+   output_buffer->pixels[x].g = pixel.g;
+   output_buffer->pixels[x].b = pixel.b;
}
 }
 
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index 278cf3533f58..b78bc2611746 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -36,6 +36,10 @@ struct vkms_frame_info {
unsigned int cpp;
 };
 
+struct pixel_argb_s32 {
+   s32 a, r, g, b;
+};
+
 struct pixel_argb_u16 {
u16 a, r, g, b;
 };
-- 
2.46.0



[PATCH v5 22/44] drm/colorop: pass plane_color_pipeline client cap to atomic check

2024-08-19 Thread 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.

v5:
 - Fix kernel docs

Signed-off-by: Harry Wentland 
---
 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 4a9cb1f7dbf7..39805f822d44 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -1578,6 +1578,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 d8232fbb002b..c908ad75b95a 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -414,6 +414,24 @@ struct drm_atomic_state {
 */
bool duplicated : 1;
 
+   /**
+* @plane_color_pipeline:
+*
+* Indicates whether this atomic state originated with a client that
+* set the DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE.
+*
+* Drivers and helper functions should use this to ignore legacy
+* properties that are incompatible with the drm_plane COLOR_PIPELINE
+* behavior, such as:
+*
+*  - COLOR_RANGE
+*  - COLOR_ENCODING
+*
+* or any other driver-specific properties that might affect pixel
+* values.
+*/
+   bool plane_color_pipeline : 1;
+
/**
 * @colorops:
 *
-- 
2.46.0



[PATCH v5 23/44] drm/colorop: define a new macro for_each_new_colorop_in_state

2024-08-19 Thread Harry Wentland
From: Alex Hung 

Create a new macro for_each_new_colorop_in_state to access new
drm_colorop_state updated from uapi.

Signed-off-by: Alex Hung 
---
 include/drm/drm_atomic.h | 20 
 1 file changed, 20 insertions(+)

diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index c908ad75b95a..ed767cb7a55b 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -1035,6 +1035,26 @@ void drm_state_dump(struct drm_device *dev, struct 
drm_printer *p);
  (new_colorop_state) = 
(__state)->colorops[__i].new_state, 1))
 
 
+/**
+ * for_each_new_colorop_in_state - iterate over all colorops in an atomic 
update
+ * @__state: &struct drm_atomic_state pointer
+ * @colorop: &struct drm_colorop iteration cursor
+ * @new_colorop_state: &struct drm_colorop_state iteration cursor for the new 
state
+ * @__i: int iteration cursor, for macro-internal use
+ *
+ * This iterates over all colorops in an atomic update, tracking new state. 
This is
+ * useful is useful in places where the state delta needs to be considered, for
+ * example in atomic check functions.
+ */
+#define for_each_new_colorop_in_state(__state, colorop, new_colorop_state, 
__i) \
+   for ((__i) = 0; \
+(__i) < (__state)->dev->mode_config.num_colorop;   \
+(__i)++)   \
+   for_each_if ((__state)->colorops[__i].ptr &&\
+((colorop) = (__state)->colorops[__i].ptr, \
+ (void)(colorop) /* Only to avoid 
unused-but-set-variable warning */, \
+ (new_colorop_state) = 
(__state)->colorops[__i].new_state, 1))
+
 /**
  * for_each_oldnew_plane_in_state - iterate over all planes in an atomic update
  * @__state: &struct drm_atomic_state pointer
-- 
2.46.0



[PATCH v5 20/44] drm/tests: Add a few tests around drm_fixed.h

2024-08-19 Thread 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.

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/tests/Makefile|  3 +-
 drivers/gpu/drm/tests/drm_fixp_test.c | 69 +++
 2 files changed, 71 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/tests/drm_fixp_test.c

diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile
index 56dab563abd7..bd69df0eee33 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \
drm_modes_test.o \
drm_plane_helper_test.o \
drm_probe_helper_test.o \
-   drm_rect_test.o
+   drm_rect_test.o \
+   drm_fixp_test.o
 
 CFLAGS_drm_mm_test.o := $(DISABLE_STRUCTLEAK_PLUGIN)
diff --git a/drivers/gpu/drm/tests/drm_fixp_test.c 
b/drivers/gpu/drm/tests/drm_fixp_test.c
new file mode 100644
index ..f420f173ff66
--- /dev/null
+++ b/drivers/gpu/drm/tests/drm_fixp_test.c
@@ -0,0 +1,69 @@
+// 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, ((1LL << 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");
\ No newline at end of file
-- 
2.46.0



[PATCH v5 12/44] drm/plane: Add COLOR PIPELINE property

2024-08-19 Thread 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.

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)

Signed-off-by: Harry Wentland 
---
 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   | 52 +++
 include/drm/drm_atomic.h  |  3 ++
 include/drm/drm_atomic_uapi.h |  2 +
 include/drm/drm_plane.h   | 11 +
 7 files changed, 161 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 096460d38545..d333433319f6 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1466,6 +1466,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 419813be21b0..d8d5be358b3e 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -256,6 +256,36 @@ drm_atomic_set_fb_for_plane(struct drm_plane_state 
*plane_state,
 }
 EXPORT_SYMBOL(drm_atomic_set_fb_for_plane);
 
+
+/**
+ * drm_atomic_set_colorop_for_plane - set colorop for plane
+ * @plane_state: atomic state object for the plane
+ * @colorop: colorop to use for the plane
+ *
+ * Helper function to select the color pipeline on a plane by setting
+ * it to the first drm_colorop element of the pipeline.
+ */
+void
+drm_atomic_set_colorop_for_plane(struct drm_plane_state *plane_state,
+struct drm_colorop *colorop)
+{
+   struct drm

[PATCH v5 21/44] drm/vkms: Add tests for CTM handling

2024-08-19 Thread 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.

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)
 - Rename red, green, blue to Y, U, V where applicable

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/vkms/tests/vkms_color_tests.c | 251 ++
 1 file changed, 251 insertions(+)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_tests.c 
b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
index e6ac01dee830..83d07f7bae37 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
@@ -3,6 +3,7 @@
 #include 
 
 #include 
+#include 
 
 #define TEST_LUT_SIZE 16
 
@@ -181,11 +182,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)
+
+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 = 0x7fff;
+   ref.g = 0x3fff;
+   ref.b = 0x3fff;
+
+   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.
+ */
+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);
+   KUNIT_EXPECT_LT(test, out.r, 0x3700);
+
+   /* U 0 */
+   KUNI

[PATCH v5 31/44] drm/amd/display: Enable support for PQ 125 EOTF and Inverse

2024-08-19 Thread 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: Harry Wentland 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 20 +--
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c |  8 +---
 2 files changed, 19 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 4009895c3b6d..a1299ca83bac 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"
@@ -676,6 +677,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;;
}
@@ -1158,8 +1162,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) {
@@ -1191,7 +1197,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;
}
@@ -1213,8 +1219,10 @@ __set_colorop_in_shaper_1d_curve(struct dc_plane_state 
*dc_plane_state,
const struct drm_color_lut *shaper_lut;
uint32_t shaper_size;
 
-   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) {
@@ -1250,7 +1258,7 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state 
*plane_state,
/* 2nd op: 1d curve - shaper */
for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
if (new_colorop_state->colorop == old_colorop &&
-   new_colorop_state->curve_1d_type == 
DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) {
+   (BIT(new_colorop_state->curve_1d_type) & 
amdgpu_dm_supported_shaper_tfs)) {
colorop_state = new_colorop_state;
break;
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
index 449a2ad6a184..f99d8e09d89b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
@@ -32,10 +32,12 @@
 #include "amdgpu_dm_colorop.h"
 
 const u64 amdgpu_dm_supported_degam_tfs =
-   BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF);
+   BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
+   BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF);
 
- const u64 amdgpu_dm_supported_shaper_tfs =
-   BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF);
+const u64 amdgpu_dm_supported_shaper_tfs =
+   BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) |
+   BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF);
 
 const u64 amdgpu_dm_supported_blnd_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF);
-- 
2.46.0



[PATCH v5 28/44] drm/amd/display: Add support for sRGB Inverse EOTF in SHAPER block

2024-08-19 Thread Harry Wentland
From: 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 
Signed-off-by: Harry Wentland 
Co-developed-by: Harry Wentland 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 76 +++
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 20 -
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h |  1 +
 3 files changed, 96 insertions(+), 1 deletion(-)

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 adb1e4b3522d..58a996090509 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
@@ -1203,6 +1203,70 @@ __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;
+   const struct drm_color_lut *shaper_lut;
+   uint32_t shaper_size;
+
+   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, shaper_lut, shaper_size, 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)
@@ -1258,6 +1322,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 */
@@ -1269,6 +1334,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 e8b7fc8bb0f1..0d1626abf577 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_

[PATCH v5 33/44] drm/amd/display: Add support for BT.709 and BT.2020 TFs

2024-08-19 Thread 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: Harry Wentland 
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c   | 11 ---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 10 +++---
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index a1299ca83bac..8f1721bdc243 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
@@ -677,6 +677,9 @@ amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type 
tf)
case DRM_COLOROP_1D_CURVE_SRGB_EOTF:
case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF:
return TRANSFER_FUNCTION_SRGB;
+   case DRM_COLOROP_1D_CURVE_BT2020_INV_OETF:
+   case DRM_COLOROP_1D_CURVE_BT2020_OETF:
+   return TRANSFER_FUNCTION_BT709;
case DRM_COLOROP_1D_CURVE_PQ_125_EOTF:
case DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF:
return TRANSFER_FUNCTION_PQ;
@@ -1287,8 +1290,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;
 
-   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) {
@@ -1324,7 +1329,7 @@ __set_dm_plane_colorop_blend(struct drm_plane_state 
*plane_state,
/* 3nd op: 1d curve - blend */
for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
if (new_colorop_state->colorop == old_colorop &&
-   new_colorop_state->curve_1d_type == 
DRM_COLOROP_1D_CURVE_SRGB_EOTF) {
+   (BIT(new_colorop_state->curve_1d_type) & 
amdgpu_dm_supported_blnd_tfs)) {
colorop_state = new_colorop_state;
break;
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
index f99d8e09d89b..bc66bd4f9fdd 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
@@ -33,14 +33,18 @@
 
 const u64 amdgpu_dm_supported_degam_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
-   BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF);
+   BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
+   BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF);
 
 const u64 amdgpu_dm_supported_shaper_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) |
-   BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF);
+   BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF) |
+   BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF);
 
 const u64 amdgpu_dm_supported_blnd_tfs =
-   BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF);
+   BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
+   BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
+   BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF);
 
 int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct 
drm_prop_enum_list *list)
 {
-- 
2.46.0



[PATCH v5 30/44] drm/colorop: Add PQ 125 EOTF and its inverse

2024-08-19 Thread Harry Wentland
The PQ function defines a mapping of code values to nits (cd/m^2).
The max code value maps to 10,000 nits.

Windows DWM's canonical composition color space (CCCS)  defaults
to composing SDR contents to 80 nits and uses a float value of
1.0 to represent this. For this reason AMD HW hard-codes a PQ
function that is scaled by 125, yielding 80 nit PQ values for
1.0 and 10,000 nits at 125.0.

This patch introduces this scaled PQ EOTF and its inverse as
1D curve types.

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_colorop.c |  2 ++
 include/drm/drm_colorop.h | 21 -
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 73fd4b7a4ce3..f9dcc522c94d 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -69,6 +69,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 1566d88fd018..d953d16c32f7 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -35,7 +35,6 @@
  * enum drm_colorop_curve_1d_type - type of 1D curve
  *
  * Describes a 1D curve to be applied by the DRM_COLOROP_1D_CURVE colorop.
- *
  */
 enum drm_colorop_curve_1d_type {
/**
@@ -55,6 +54,26 @@ enum drm_colorop_curve_1d_type {
 */
DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF,
 
+   /**
+* @DRM_COLOROP_1D_CURVE_PQ_125_EOTF:
+*
+* The PQ transfer function, scaled by 125.0f, so that 10,000
+* nits correspond to 125.0f.
+*
+* Transfer characteristics of the PQ function as defined by
+* SMPTE ST 2084 (2014) for 10-, 12-, 14-, and 16-bit systems
+* and Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system.
+* Equivalent to H.273 TransferCharacteristics code point 16.
+*/
+   DRM_COLOROP_1D_CURVE_PQ_125_EOTF,
+
+   /**
+* @DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF:
+*
+* The inverse of DRM_COLOROP_1D_CURVE_PQ_125_EOTF.
+*/
+   DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF,
+
/**
 * @DRM_COLOROP_1D_CURVE_COUNT:
 *
-- 
2.46.0



[PATCH v5 29/44] drm/amd/display: Add support for sRGB EOTF in BLND block

2024-08-19 Thread Harry Wentland
From: 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 
Signed-off-by: Harry Wentland 
Co-developed-by: Harry Wentland 
---
 .../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 58a996090509..4009895c3b6d 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
@@ -1267,6 +1267,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;
+
+   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)
@@ -1345,6 +1411,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 0d1626abf577..449a2ad6a184 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
@@ -37,6 +37,9 @@ const u64 amdgpu_dm_supported_degam_tfs =
  const u64 amdgpu_dm_supported_shaper_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF);
 
+const u64 amdgpu_dm_supported_blnd_tfs =
+   BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF);
+
 int amdgpu_dm_initialize_default_pipeline(struct drm_plan

[PATCH v5 32/44] drm/colorop: add BT2020/BT709 OETF and Inverse OETF

2024-08-19 Thread 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).

v5:
 - Add kernel docs

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_colorop.c |  2 ++
 include/drm/drm_colorop.h | 19 +++
 2 files changed, 21 insertions(+)

diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index f9dcc522c94d..032ca65299e5 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -69,6 +69,8 @@ static const struct drm_prop_enum_list 
drm_colorop_type_enum_list[] = {
 static const char * const colorop_curve_1d_type_names[] = {
[DRM_COLOROP_1D_CURVE_SRGB_EOTF] = "sRGB EOTF",
[DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF] = "sRGB Inverse EOTF",
+   [DRM_COLOROP_1D_CURVE_BT2020_INV_OETF] = "BT.2020 Inverse OETF",
+   [DRM_COLOROP_1D_CURVE_BT2020_OETF] = "BT.2020 OETF",
[DRM_COLOROP_1D_CURVE_PQ_125_EOTF] = "PQ 125 EOTF",
[DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF] = "PQ 125 Inverse EOTF",
 };
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index d953d16c32f7..d8d0e4b571ca 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -54,6 +54,25 @@ enum drm_colorop_curve_1d_type {
 */
DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF,
 
+   /**
+* @DRM_COLOROP_1D_CURVE_BT2020_INV_OETF:
+*
+* The inverse of &DRM_COLOROP_1D_CURVE_BT2020_OETF
+*/
+   DRM_COLOROP_1D_CURVE_BT2020_INV_OETF,
+
+   /**
+* @DRM_COLOROP_1D_CURVE_BT2020_OETF:
+*
+* The BT.2020/BT.709 transfer function. The BT.709 and BT.2020
+* transfer functions are the same, the only difference is that
+* BT.2020 is defined with more precision for 10 and 12-bit
+* encodings.
+*
+*
+*/
+   DRM_COLOROP_1D_CURVE_BT2020_OETF,
+
/**
 * @DRM_COLOROP_1D_CURVE_PQ_125_EOTF:
 *
-- 
2.46.0



[PATCH v5 27/44] drm/amd/display: Add support for sRGB EOTF in DEGAM block

2024-08-19 Thread Harry Wentland
From: 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 
Signed-off-by: Harry Wentland 
Co-developed-by: Harry Wentland 
---
 .../gpu/drm/amd/display/amdgpu_dm/Makefile|  3 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 88 +++
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 58 
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.h | 34 +++
 .../amd/display/amdgpu_dm/amdgpu_dm_plane.c   | 10 +++
 5 files changed, 192 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
 create mode 100644 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile 
b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
index ab2a97e354da..46158d67ab12 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
@@ -38,7 +38,8 @@ AMDGPUDM = \
amdgpu_dm_pp_smu.o \
amdgpu_dm_psr.o \
amdgpu_dm_replay.o \
-   amdgpu_dm_wb.o
+   amdgpu_dm_wb.o \
+   amdgpu_dm_colorop.o
 
 ifdef CONFIG_DRM_AMD_DC_FP
 AMDGPUDM += dc_fpu.o
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index ebabfe3a512f..adb1e4b3522d 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,19 @@ 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 +1150,59 @@ __set_dm_plane_degamma(struct drm_plane_state 
*plane_state,
return 0;
 }
 
+static int
+__set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state,
+  struct drm_colorop_state *colorop_state)
+{
+   struct dc_transfer_func *tf = &dc_plane_state->in_transfer_func;
+   struct drm_colorop *colorop = colorop_state->colorop;
+   struct drm_device *drm = colorop->dev;
+
+   if (colorop->type != DRM_COLOROP_1D_CURVE &&
+   colorop_state->curve_1d_type != DRM_COLOROP_1D_CURVE_SRGB_EOTF)
+   return -EINVAL;
+
+   if (colorop_state->bypass) {
+   tf->type = TF_TYPE_BYPASS;
+   tf->tf = TRANSFER_FUNCTION_LINEAR;
+   return 0;
+   }
+
+   drm_dbg(drm, "Degamma colorop with ID: %d\n", colorop->base.id);
+
+   tf->type = TF_TYPE_PREDEFINED;
+   tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
+
+   return 0;
+}
+
+static int
+__set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state,
+  struct dc_plane_state *dc_plane_state,
+  struct drm_colorop *colorop)
+{
+   struct drm_colorop *old_colorop;
+   struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+   struct drm_atomic_state *state = plane_state->state;
+   int i = 0;
+
+   old_colorop = colorop;
+
+   /* 1st op: 1d curve - degamma */
+   for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+   if (new_colorop_state->colorop == old_colorop &&
+   new_colorop_state->curve_1d_type == 
DRM_COLOROP_1D_CURVE_SRGB_EOTF) {
+   colorop_state = new_colorop_state;
+   break;
+   }
+   }
+
+   if (!colorop_state)
+   return -EINVAL;
+
+   return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state);
+}
+
 static int
 amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state,
 struct dc_plane_state *dc_plane_state)
@@ -1187,6 +1253,25 @@ amdgpu_dm_plane_set_color_properties(struct 
drm_plane_state *plane_state,
return 0;
 }
 
+static int
+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;
+   int ret;
+
+   /* 1D Curve - DEGAM TF */
+

[PATCH v5 25/44] drm/amd/display: Add bypass COLOR PIPELINE

2024-08-19 Thread Harry Wentland
Add the default Bypass pipeline and ensure it passes the
kms_colorop test plane-XR30-XR30-bypass.

Signed-off-by: Harry Wentland 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_plane.c   | 19 +++
 1 file changed, 19 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
index a83bd0331c3b..d260db42d407 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
@@ -1746,6 +1746,20 @@ dm_atomic_plane_get_property(struct drm_plane *plane,
 }
 #endif
 
+#define MAX_COLOR_PIPELINES 5
+
+static int
+dm_plane_init_colorops(struct drm_plane *plane)
+{
+   struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES];
+   int len = 0;
+
+   /* Create COLOR_PIPELINE property and attach */
+   drm_plane_create_color_pipeline_property(plane, pipelines, len);
+
+   return 0;
+}
+
 static const struct drm_plane_funcs dm_plane_funcs = {
.update_plane   = drm_atomic_helper_update_plane,
.disable_plane  = drm_atomic_helper_disable_plane,
@@ -1849,7 +1863,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.46.0



[PATCH v5 24/44] drm/amd/display: Ignore deprecated props when plane_color_pipeline set

2024-08-19 Thread 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: Harry Wentland 
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 7e7929f24ae4..3257bd22bf6b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -5319,6 +5319,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.46.0



[PATCH v5 40/44] drm/colorop: Define LUT_1D interpolation

2024-08-19 Thread Harry Wentland
We want to make sure userspace is aware of the 1D LUT
interpolation. While linear interpolation is common it
might not be supported on all HW. Give driver implementers
a way to specify their interpolation.

Signed-off-by: Harry Wentland 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c |  6 ++--
 drivers/gpu/drm/drm_atomic.c  |  1 +
 drivers/gpu/drm/drm_colorop.c | 36 ++-
 include/drm/drm_colorop.h | 19 +-
 include/uapi/drm/drm_mode.h   | 13 +++
 5 files changed, 71 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 8a86849e1529..c85e344eaccb 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
@@ -121,7 +121,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
return -ENOMEM;
}
 
-   ret = drm_colorop_curve_1d_lut_init(dev, op, plane, 
MAX_COLOR_LUT_ENTRIES);
+   ret = drm_colorop_curve_1d_lut_init(dev, op, plane, 
MAX_COLOR_LUT_ENTRIES,
+   
DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR);
if (ret)
return ret;
 
@@ -151,7 +152,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
return -ENOMEM;
}
 
-   ret = drm_colorop_curve_1d_lut_init(dev, op, plane, 
MAX_COLOR_LUT_ENTRIES);
+   ret = drm_colorop_curve_1d_lut_init(dev, op, plane, 
MAX_COLOR_LUT_ENTRIES,
+   
DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR);
if (ret)
return ret;
 
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 2ab797e9d946..dbb8776ae433 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -798,6 +798,7 @@ static void drm_atomic_colorop_print_state(struct 
drm_printer *p,
break;
case DRM_COLOROP_1D_LUT:
drm_printf(p, "\tsize=%d\n", state->size);
+   drm_printf(p, "\tinterpolation=%s\n", 
drm_get_colorop_lut1d_interpolation_name(colorop->lut1d_interpolation));
drm_printf(p, "\tdata blob id=%d\n", state->data ? 
state->data->base.id : 0);
break;
case DRM_COLOROP_CTM_3X4:
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 9634effab73a..47a9877099c3 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -77,6 +77,9 @@ static const char * const colorop_curve_1d_type_names[] = {
[DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF] = "PQ 125 Inverse EOTF",
 };
 
+static const struct drm_prop_enum_list drm_colorop_lut1d_interpolation_list[] 
= {
+   { DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, "Linear" },
+};
 
 /* Init Helpers */
 
@@ -233,10 +236,12 @@ static int drm_colorop_create_data_prop(struct drm_device 
*dev, struct drm_color
  * @colorop: The drm_colorop object to initialize
  * @plane: The associated drm_plane
  * @lut_size: LUT size supported by driver
+ * @lut1d_interpolation: 1D LUT interpolation type
  * @return zero on success, -E value on failure
  */
 int drm_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_colorop 
*colorop,
- struct drm_plane *plane, uint32_t lut_size)
+ struct drm_plane *plane, uint32_t lut_size,
+ enum drm_colorop_lut1d_interpolation_type 
lut1d_interpolation)
 {
struct drm_property *prop;
int ret;
@@ -255,6 +260,17 @@ int drm_colorop_curve_1d_lut_init(struct drm_device *dev, 
struct drm_colorop *co
colorop->size_property = prop;
drm_object_attach_property(&colorop->base, colorop->size_property, 
lut_size);
 
+   /* Interpolation */
+   prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 
"LUT1D_INTERPOLATION",
+   drm_colorop_lut1d_interpolation_list,
+   
ARRAY_SIZE(drm_colorop_lut1d_interpolation_list));
+   if (!prop)
+   return -ENOMEM;
+
+   colorop->lut1d_interpolation_property = prop;
+   drm_object_attach_property(&colorop->base, prop, lut1d_interpolation);
+   colorop->lut1d_interpolation = lut1d_interpolation;
+
/* data */
ret = drm_colorop_create_data_prop(dev, colorop);
if (ret)
@@ -407,6 +423,9 @@ static const char * const colorop_type_name[] = {
[DRM_COLOROP_CTM_3X4] = "3x4 Matrix",
[DRM_COLOROP_MULTIPLIER] = "Multiplier",
 };
+static const char * const colorop_lut1d_interpolation_name[] = {
+   [DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR

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

2024-08-19 Thread Harry Wentland
From: 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 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 170 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c |  30 
 2 files changed, 119 insertions(+), 81 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 8f1721bdc243..c66f249ae70b 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
@@ -1212,40 +1212,6 @@ __set_dm_plane_colorop_degamma(struct drm_plane_state 
*plane_state,
return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state);
 }
 
-static int
-__set_colorop_in_shaper_1d_curve(struct dc_plane_state *dc_plane_state,
-  struct drm_colorop_state *colorop_state)
-{
-   struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func;
-   struct drm_colorop *colorop = colorop_state->colorop;
-   struct drm_device *drm = colorop->dev;
-   const struct drm_color_lut *shaper_lut;
-   uint32_t shaper_size;
-
-   if (colorop->type != DRM_COLOROP_1D_CURVE)
-   return -EINVAL;
-
-   if (!(BIT(colorop_state->curve_1d_type) & 
amdgpu_dm_supported_shaper_tfs))
-   return -EINVAL;
-
-   if (colorop_state->bypass) {
-   tf->type = TF_TYPE_BYPASS;
-   tf->tf = TRANSFER_FUNCTION_LINEAR;
-   return 0;
-   }
-
-   drm_dbg(drm, "Shaper colorop with ID: %d\n", colorop->base.id);
-
-   if (colorop->type == DRM_COLOROP_1D_CURVE) {
-   tf->type = TF_TYPE_DISTRIBUTED_POINTS;
-   tf->tf = 
amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
-   tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
-   return __set_output_tf(tf, shaper_lut, shaper_size, false);
-   }
-
-   return -EINVAL;
-}
-
 static int
 __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state,
  struct dc_plane_state *dc_plane_state,
@@ -1254,64 +1220,61 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state 
*plane_state,
struct drm_colorop *old_colorop;
struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
struct drm_atomic_state *state = plane_state->state;
+   enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR;
+   struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func;
+   const struct drm_color_lut *shaper_lut;
+   struct drm_device *dev = colorop->dev;
+   uint32_t shaper_size;
int i = 0;
 
+   /* 1D Curve - SHAPER TF */
old_colorop = colorop;
-
-   /* 2nd op: 1d curve - shaper */
for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
if (new_colorop_state->colorop == old_colorop &&
(BIT(new_colorop_state->curve_1d_type) & 
amdgpu_dm_supported_shaper_tfs)) {
colorop_state = new_colorop_state;
break;
}
+   }
 
-   if (new_colorop_state->colorop == old_colorop) {
+   if (colorop_state && !colorop_state->bypass && colorop->type == 
DRM_COLOROP_1D_CURVE) {
+   drm_dbg(dev, "Shaper TF colorop with ID: %d\n", 
colorop->base.id);
+   tf->type = TF_TYPE_DISTRIBUTED_POINTS;
+   tf->tf = default_tf = 
amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
+   tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
+   __set_output_tf(tf, shaper_lut, shaper_size, false);
+   }
+
+   /* 1D LUT - SHAPER LUT */
+   colorop = old_colorop->next;
+   if (!colorop) {
+   drm_dbg(dev, "no Shaper LUT colorop found\n");
+   return -EINVAL;
+   }
+
+   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_1D_LUT) {
colorop_state = new_colorop_state;
break;
}
}
 
-   if (!colorop_state)
-   return -EINVAL;
-
- 

[PATCH v5 38/44] drm/amd/display: add multiplier colorop

2024-08-19 Thread Harry Wentland
From: 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 
---
 .../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 ea9bd287cddd..a14dc67dbbdf 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
@@ -1251,6 +1251,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,
@@ -1461,6 +1490,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 ba42f1f6b620..b739d6cb3e6b 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
@@ -84,6 +84,21 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
 
prev_op = op;
 
+   /* Multiplier */
+   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!op) {
+   DRM_ERROR("KMS: Failed to allocate colorop\n");
+   return -ENOMEM;
+   }
+
+   ret = drm_colorop_mult_init(dev, op, plane);
+   if (ret)
+   return ret;
+
+   drm_colorop_set_next_property(prev_op, op);
+
+   prev_op = op;
+
/* 1D curve - SHAPER TF */
op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
if (!op) {
-- 
2.46.0



[PATCH v5 37/44] drm/colorop: Add mutliplier type

2024-08-19 Thread Harry Wentland
From: 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.

v5:
 - Fix atomic state print
 - Add kernel doc

Signed-off-by: Alex Hung 
---
 drivers/gpu/drm/drm_atomic.c  |  3 +++
 drivers/gpu/drm/drm_atomic_uapi.c |  4 
 drivers/gpu/drm/drm_colorop.c | 37 +--
 include/drm/drm_colorop.h | 16 +
 include/uapi/drm/drm_mode.h   |  9 
 5 files changed, 67 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 10af940de8de..2ab797e9d946 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -803,6 +803,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 5bbce7360262..142824de33ca 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -727,6 +727,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);
@@ -752,6 +754,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->size_property) {
*val = state->size;
} else if (property == colorop->data_property) {
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index e760d3301d48..9634effab73a 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -64,7 +64,8 @@
 static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = {
{ DRM_COLOROP_1D_CURVE, "1D Curve" },
{ DRM_COLOROP_1D_LUT, "1D Curve Custom LUT" },
-   { DRM_COLOROP_CTM_3X4, "3x4 Matrix"}
+   { DRM_COLOROP_CTM_3X4, "3x4 Matrix"},
+   { DRM_COLOROP_MULTIPLIER, "Multiplier"},
 };
 
 static const char * const colorop_curve_1d_type_names[] = {
@@ -285,6 +286,37 @@ int drm_colorop_ctm_3x4_init(struct drm_device *dev, 
struct drm_colorop *colorop
 }
 EXPORT_SYMBOL(drm_colorop_ctm_3x4_init);
 
+/**
+ * drm_colorop_mult_init - Initialize a DRM_COLOROP_MULTIPLIER
+ *
+ * @dev: DRM device
+ * @colorop: The drm_colorop object to initialize
+ * @plane: The associated drm_plane
+ * @return zero on success, -E value on failure
+ */
+int drm_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop,
+ struct drm_plane *plane)
+{
+   struct drm_property *prop;
+   int ret;
+
+   ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_MULTIPLIER);
+   if (ret)
+   return ret;
+
+   prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, 
"MULTIPLIER", 0, U64_MAX);
+   if (!prop)
+   return -ENOMEM;
+
+   colorop->multiplier_property = prop;
+   drm_object_attach_property(&colorop->base, 
colorop->multiplier_property, 0);
+
+   drm_colorop_reset(colorop);
+
+   return 0;
+}
+EXPORT_SYMBOL(drm_colorop_mult_init);
+
 static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop 
*colorop,
struct 
drm_colorop_state *state)
 {
@@ -372,7 +404,8 @@ void drm_colorop_reset(struct drm_colorop *colorop)
 static const char * const colorop_type_name[] = {
[DRM_COLOROP_1D_CURVE] = "1D Curve",
[DRM_COLOROP_1D_LUT] = "1D Curve Custom LUT",
-   [DRM_COLOROP_CTM_3X4] = "3x4 Matrix"
+   [DRM_COLOROP_CTM_3X4] = "3x4 Matrix",
+   [DRM_COLOROP_MULTIPLIER] = "Multiplier",
 };
 
 const char *drm_get_colorop_type_name(enum drm_colorop_type type)
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index 6e5da057ef44..09e96b9a8eff 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -125,6 +125,13 @@ struct drm_colorop_state {
 */
enum drm_colorop_curve_1d_type curve_

[PATCH v5 42/44] drm/colorop: Add 3D LUT supports to color pipeline

2024-08-19 Thread Harry Wentland
From: Alex Hung 

It is to be used to enable HDR by allowing userpace to create and pass
3D LUTs to kernel and hardware.

1. new drm_colorop_type: DRM_COLOROP_3D_LUT.
2. 3D LUT modes define hardware capabilities to userspace applications.
3. mode index points to current 3D LUT mode in lut_3d_modes.

Signed-off-by: Alex Hung 
---
 drivers/gpu/drm/drm_atomic.c  | 21 
 drivers/gpu/drm/drm_atomic_uapi.c | 17 +++
 drivers/gpu/drm/drm_colorop.c | 69 +
 include/drm/drm_colorop.h | 42 
 include/uapi/drm/drm_mode.h   | 84 +++
 5 files changed, 233 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 02ff576aa7a9..1d080c367f4b 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -786,6 +786,9 @@ static void drm_atomic_colorop_print_state(struct 
drm_printer *p,
const struct drm_colorop_state *state)
 {
struct drm_colorop *colorop = state->colorop;
+   struct drm_property_blob *modes = state->lut_3d_modes;
+   struct drm_mode_3dlut_mode *mode_3dlut;
+   int i;
 
drm_printf(p, "colorop[%u]:\n", colorop->base.id);
drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type));
@@ -808,6 +811,24 @@ 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:
+   mode_3dlut = (struct drm_mode_3dlut_mode *) modes->data;
+
+   drm_printf(p, "\tlut_3d_modes blob id=%d\n", modes ? 
modes->base.id : 0);
+   for (i = 0; i < modes->length / sizeof(struct 
drm_mode_3dlut_mode); i++) {
+   drm_printf(p, "\t  lut_size=%d\n", 
mode_3dlut[i].lut_size);
+   drm_printf(p, "\t  lut_strides=%d %d %d\n", 
mode_3dlut[i].lut_stride[0],
+   
mode_3dlut[i].lut_stride[1],
+   
mode_3dlut[i].lut_stride[2]);
+   drm_printf(p, "\t  interpolation=%s\n",
+  
drm_get_colorop_lut3d_interpolation_name(mode_3dlut[i].interpolation));
+   drm_printf(p, "\t  color_depth=%d\n", 
mode_3dlut[i].color_depth);
+   drm_printf(p, "\t  color_format=%X\n", 
mode_3dlut[i].color_format);
+   drm_printf(p, "\t  traversal_order=%X\n", 
mode_3dlut[i].traversal_order);
+   }
+   drm_printf(p, "\tlut_3d_mode_index=%d\n", 
state->lut_3d_mode_index);
+   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 142824de33ca..50212eeba272 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -695,9 +695,11 @@ static int drm_atomic_color_set_data_property(struct 
drm_colorop *colorop,
struct drm_colorop_state *state,
struct drm_property *property, uint64_t val)
 {
+   struct drm_mode_3dlut_mode *modes;
ssize_t elem_size = -1;
ssize_t size = -1;
bool replaced = false;
+   uint32_t index;
 
switch (colorop->type) {
case DRM_COLOROP_1D_LUT:
@@ -706,6 +708,15 @@ 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:
+   index = state->lut_3d_mode_index;
+   if (index >= (state->lut_3d_modes->length / sizeof(struct 
drm_mode_3dlut_mode)))
+   return -EINVAL;
+
+   modes = (struct drm_mode_3dlut_mode *) 
state->lut_3d_modes->data;
+   size = modes[index].lut_stride[0] * modes[index].lut_stride[1] 
* modes[index].lut_stride[2] *
+  sizeof(struct drm_color_lut);
+   break;
default:
/* should never get here */
return -EINVAL;
@@ -729,6 +740,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->lut_3d_mode_index_property) {
+   state->lut_3d_mode_index = val;
} else if (property == colorop->data_property) {
return drm_atomic_color_set_data_property(colorop,
state, property, val);
@@ -756,6 +769,10 @@ drm_atomic_colorop_get_property(struct drm_colorop 
*colorop,
  

[PATCH v5 36/44] drm/amd/display: add 3x4 matrix colorop

2024-08-19 Thread Harry Wentland
From: 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 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 50 +++
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 15 ++
 2 files changed, 65 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 c66f249ae70b..ea9bd287cddd 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
@@ -1212,6 +1212,45 @@ __set_dm_plane_colorop_degamma(struct drm_plane_state 
*plane_state,
return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state);
 }
 
+static int
+__set_dm_plane_colorop_3x4_matrix(struct drm_plane_state *plane_state,
+ struct dc_plane_state *dc_plane_state,
+ struct drm_colorop *colorop)
+{
+   struct drm_colorop *old_colorop;
+   struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+   struct drm_atomic_state *state = plane_state->state;
+   const struct drm_device *dev = colorop->dev;
+   const struct drm_property_blob *blob;
+   struct drm_color_ctm_3x4 *ctm = NULL;
+   int i = 0;
+
+   /* 3x4 matrix */
+   old_colorop = colorop;
+   for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+   if (new_colorop_state->colorop == old_colorop &&
+   new_colorop_state->colorop->type == DRM_COLOROP_CTM_3X4) {
+   colorop_state = new_colorop_state;
+   break;
+   }
+   }
+
+   if (colorop_state && !colorop_state->bypass && colorop->type == 
DRM_COLOROP_CTM_3X4) {
+   drm_dbg(dev, "3x4 matrix colorop with ID: %d\n", 
colorop->base.id);
+   blob = colorop_state->data;
+   if (blob->length == sizeof(struct drm_color_ctm_3x4)) {
+   ctm = blob ? (struct drm_color_ctm_3x4 *) blob->data : 
NULL;
+   __drm_ctm_3x4_to_dc_matrix(ctm, 
dc_plane_state->gamut_remap_matrix.matrix);
+   dc_plane_state->gamut_remap_matrix.enable_remap = true;
+   
dc_plane_state->input_csc_color_matrix.enable_adjustment = false;
+   } else
+   drm_warn(dev, "blob->length (%ld) isn't equal to 
drm_color_ctm_3x4 (%ld)\n",
+blob->length, sizeof(struct 
drm_color_ctm_3x4));
+   }
+
+   return 0;
+}
+
 static int
 __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state,
  struct dc_plane_state *dc_plane_state,
@@ -1411,6 +1450,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 08480bf61dc5..ba42f1f6b620 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
@@ -69,6 +69,21 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
 
prev_op = op;
 
+   /* 3x4 matrix */
+   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!op) {
+   DRM_ERROR("KMS: Failed to allocate colorop\n");
+   return -ENOMEM;
+   }
+
+   ret = drm_colorop_ctm_3x4_init(dev, op, plane);
+   if (ret)
+   return ret;
+
+   drm_colorop_set_next_property(prev_op, op);
+
+   prev_op = op;
+
/* 1D curve - SHAPER TF */
op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
if (!op) {
-- 
2.46.0



[PATCH v5 43/44] drm/amd/display: add 3D LUT colorop

2024-08-19 Thread Harry Wentland
From: 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 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 113 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c |  26 
 2 files changed, 136 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index 2f0b06fb5f81..cc3d68d1386f 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
@@ -1283,7 +1283,8 @@ __set_dm_plane_colorop_multiplier(struct drm_plane_state 
*plane_state,
 static int
 __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state,
  struct dc_plane_state *dc_plane_state,
- struct drm_colorop *colorop)
+ struct drm_colorop *colorop,
+ bool *enabled)
 {
struct drm_colorop *old_colorop;
struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
@@ -1311,6 +1312,7 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state 
*plane_state,
tf->tf = default_tf = 
amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
__set_output_tf(tf, shaper_lut, shaper_size, false);
+   *enabled = true;
}
 
/* 1D LUT - SHAPER LUT */
@@ -1338,8 +1340,101 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state 
*plane_state,
shaper_size = shaper_lut != NULL ? shaper_size : 0;
 
/* Custom LUT size must be the same as supported size */
-   if (shaper_size == colorop_state->size)
+   if (shaper_size == colorop_state->size) {
__set_output_tf(tf, shaper_lut, shaper_size, false);
+   *enabled = true;
+   }
+   }
+
+   return 0;
+}
+
+/* __set_colorop_3dlut - set DRM 3D LUT to DC stream
+ * @drm_lut3d: user 3D LUT
+ * @drm_lut3d_size: size of 3D LUT
+ * @drm_mode_3dlut: drm_mode selected by userspace
+ * @lut3d: DC 3D LUT
+ *
+ * Map user 3D LUT data to DC 3D LUT and all necessary bits to program it
+ * on DCN accordingly.
+ */
+static void __set_colorop_3dlut(const struct drm_color_lut *drm_lut3d,
+   uint32_t drm_lut3d_size,
+   struct drm_mode_3dlut_mode *drm_mode_3dlut,
+   struct dc_3dlut *lut)
+{
+   if (!drm_lut3d_size)
+   return;
+
+   lut->state.bits.initialized = 0;
+
+   /* Only supports 17x17x17 3D LUT (12-bit) now */
+   if (drm_mode_3dlut->color_depth == 12)
+   lut->lut_3d.use_12bits = true;
+   else
+   return;
+
+   if (drm_mode_3dlut->lut_size == 17)
+   lut->lut_3d.use_tetrahedral_9 = false;
+   else
+   return;
+
+   lut->state.bits.initialized = 1;
+   __drm_3dlut_to_dc_3dlut(drm_lut3d, drm_lut3d_size, &lut->lut_3d,
+   lut->lut_3d.use_tetrahedral_9,
+   drm_mode_3dlut->color_depth);
+
+}
+
+static int
+__set_dm_plane_colorop_3dlut(struct drm_plane_state *plane_state,
+struct dc_plane_state *dc_plane_state,
+struct drm_colorop *colorop,
+bool shaper_enabled)
+{
+   struct drm_colorop *old_colorop;
+   struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+   struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func;
+   struct drm_atomic_state *state = plane_state->state;
+   const struct amdgpu_device *adev = drm_to_adev(colorop->dev);
+   const struct drm_device *dev = colorop->dev;
+   struct drm_mode_3dlut_mode *mode;
+   const struct drm_color_lut *lut3d;
+   uint32_t lut3d_size, index;
+   int i = 0;
+
+   /* 3D LUT */
+   old_colorop = colorop;
+   for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+   if (new_colorop_state->colorop == old_colorop &&
+   new_colorop_state->colorop->type == DRM_COLOROP_3D_LUT) {
+   colorop_state = new_colorop_state;
+   break;
+   }
+   }
+
+   if (colorop_state && !colorop_state->bypass && colorop->type == 
DRM_COLOROP_3D_LUT) {
+   if (!adev->dm.dc->caps.color.dpp.hw_3d_lut) {
+   drm_dbg(dev, "3D LUT is not supported by hardware\n");
+   return 0;
+   }
+
+   drm_dbg(dev, "3D LUT colorop with ID: %d\n", colorop->base.id);
+   mode = (struct drm_mode_3dlut_mo

[PATCH v5 44/44] drm/amd/display: Add AMD color pipeline doc

2024-08-19 Thread Harry Wentland
A short description about the AMD color pipeline.

Signed-off-by: Harry Wentland 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 122 +++---
 1 file changed, 102 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index cc3d68d1386f..d48dcaee9e2e 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.
+ * 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.
+ * 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:
+ * 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:
+ *
+ * - AM

[PATCH v5 39/44] drm/amd/display: Swap matrix and multiplier

2024-08-19 Thread Harry Wentland
From: Alex Hung 

Swap the order of matrix and multiplier as designed in hardware.

Signed-off-by: Alex Hung 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 22 +++---
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 30 +--
 2 files changed, 26 insertions(+), 26 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 a14dc67dbbdf..2f0b06fb5f81 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
@@ -1479,17 +1479,6 @@ 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;
-
/* Multiplier */
colorop = colorop->next;
if (!colorop) {
@@ -1501,6 +1490,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 b739d6cb3e6b..8a86849e1529 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
@@ -69,21 +69,6 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
 
prev_op = op;
 
-   /* 3x4 matrix */
-   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
-   if (!op) {
-   DRM_ERROR("KMS: Failed to allocate colorop\n");
-   return -ENOMEM;
-   }
-
-   ret = drm_colorop_ctm_3x4_init(dev, op, plane);
-   if (ret)
-   return ret;
-
-   drm_colorop_set_next_property(prev_op, op);
-
-   prev_op = op;
-
/* Multiplier */
op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
if (!op) {
@@ -99,6 +84,21 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
 
prev_op = op;
 
+   /* 3x4 matrix */
+   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!op) {
+   DRM_ERROR("KMS: Failed to allocate colorop\n");
+   return -ENOMEM;
+   }
+
+   ret = drm_colorop_ctm_3x4_init(dev, op, plane);
+   if (ret)
+   return ret;
+
+   drm_colorop_set_next_property(prev_op, op);
+
+   prev_op = op;
+
/* 1D curve - SHAPER TF */
op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
if (!op) {
-- 
2.46.0



[PATCH v5 34/44] drm/colorop: Add 1D Curve Custom LUT type

2024-08-19 Thread Harry Wentland
From: 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.

v5:
 - Add kernel doc
 - Define SIZE in similar manner to GAMMA_SIZE on drm_crtc (Melissa)

Signed-off-by: Alex Hung 
Signed-off-by: Harry Wentland 
Co-developed-by: Harry Wentland 
---
 drivers/gpu/drm/drm_atomic.c  |  4 +++
 drivers/gpu/drm/drm_atomic_uapi.c |  5 
 drivers/gpu/drm/drm_colorop.c | 47 +--
 include/drm/drm_colorop.h | 16 +++
 include/uapi/drm/drm_mode.h   |  9 ++
 5 files changed, 79 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index c2fcde96e218..10af940de8de 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -796,6 +796,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", state->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 39805f822d44..5bbce7360262 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -700,6 +700,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 = state->size * sizeof(struct drm_color_lut);
+   break;
case DRM_COLOROP_CTM_3X4:
size = sizeof(struct drm_color_ctm_3x4);
break;
@@ -749,6 +752,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->size_property) {
+   *val = state->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 032ca65299e5..e760d3301d48 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -63,6 +63,7 @@
 
 static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = {
{ DRM_COLOROP_1D_CURVE, "1D Curve" },
+   { DRM_COLOROP_1D_LUT, "1D Curve Custom LUT" },
{ DRM_COLOROP_CTM_3X4, "3x4 Matrix"}
 };
 
@@ -218,12 +219,53 @@ static int drm_colorop_create_data_prop(struct drm_device 
*dev, struct drm_color
 
colorop->data_property = prop;
drm_object_attach_property(&colorop->base,
-   colorop->data_property,
-   0);
+  colorop->data_property,
+  0);
 
return 0;
 }
 
+/**
+ * drm_colorop_curve_1d_lut_init - Initialize a DRM_COLOROP_1D_LUT
+ *
+ * @dev: DRM device
+ * @colorop: The drm_colorop object to initialize
+ * @plane: The associated drm_plane
+ * @lut_size: LUT size supported by driver
+ * @return zero on success, -E value on failure
+ */
+int drm_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_colorop 
*colorop,
+ struct drm_plane *plane, uint32_t lut_size)
+{
+   struct drm_property *prop;
+   int ret;
+
+   ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_1D_LUT);
+   if (ret)
+   return ret;
+
+   /* initialize 1D LUT only attribute */
+   /* LUT size */
+   prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "SIZE",
+0, UINT_MAX);
+   if (!prop)
+   return -ENOMEM;
+
+   colorop->size_property = prop;
+   drm_object_attach_property(&colorop->base, colorop->size_property, 
lut_size);
+
+   /* data */
+   ret = drm_colorop_create_data_prop(dev, 

[PATCH v5 41/44] drm/colorop: allow non-bypass colorops

2024-08-19 Thread Harry Wentland
Not all HW will be able to do bypass on all color
operations. Introduce an 'allow_bypass' boolean for
all colorop init functions and only create the BYPASS
property when it's true.

Signed-off-by: Harry Wentland 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c | 22 +---
 drivers/gpu/drm/drm_atomic.c  |  3 +-
 drivers/gpu/drm/drm_colorop.c | 51 ---
 drivers/gpu/drm/vkms/vkms_colorop.c   |  8 +--
 include/drm/drm_colorop.h | 10 ++--
 5 files changed, 60 insertions(+), 34 deletions(-)

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 c85e344eaccb..05fdec7c28ad 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
@@ -60,7 +60,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
return -ENOMEM;
}
 
-   ret = drm_colorop_curve_1d_init(dev, op, plane, 
amdgpu_dm_supported_degam_tfs);
+   ret = drm_colorop_curve_1d_init(dev, op, plane,
+   amdgpu_dm_supported_degam_tfs,
+   true);
if (ret)
return ret;
 
@@ -76,7 +78,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
return -ENOMEM;
}
 
-   ret = drm_colorop_mult_init(dev, op, plane);
+   ret = drm_colorop_mult_init(dev, op, plane, true);
if (ret)
return ret;
 
@@ -91,7 +93,7 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
return -ENOMEM;
}
 
-   ret = drm_colorop_ctm_3x4_init(dev, op, plane);
+   ret = drm_colorop_ctm_3x4_init(dev, op, plane, true);
if (ret)
return ret;
 
@@ -106,7 +108,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
return -ENOMEM;
}
 
-   ret = drm_colorop_curve_1d_init(dev, op, plane, 
amdgpu_dm_supported_shaper_tfs);
+   ret = drm_colorop_curve_1d_init(dev, op, plane,
+   amdgpu_dm_supported_shaper_tfs,
+   true);
if (ret)
return ret;
 
@@ -122,7 +126,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
}
 
ret = drm_colorop_curve_1d_lut_init(dev, op, plane, 
MAX_COLOR_LUT_ENTRIES,
-   
DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR);
+   
DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
+   true);
if (ret)
return ret;
 
@@ -137,7 +142,9 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
return -ENOMEM;
}
 
-   ret = drm_colorop_curve_1d_init(dev, op, plane, 
amdgpu_dm_supported_blnd_tfs);
+   ret = drm_colorop_curve_1d_init(dev, op, plane,
+   amdgpu_dm_supported_blnd_tfs,
+   true);
if (ret)
return ret;
 
@@ -153,7 +160,8 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane 
*plane, struct drm_pr
}
 
ret = drm_colorop_curve_1d_lut_init(dev, op, plane, 
MAX_COLOR_LUT_ENTRIES,
-   
DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR);
+   
DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
+   true);
if (ret)
return ret;
 
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index dbb8776ae433..02ff576aa7a9 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -789,7 +789,8 @@ static void drm_atomic_colorop_print_state(struct 
drm_printer *p,
 
drm_printf(p, "colorop[%u]:\n", colorop->base.id);
drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type));
-   drm_printf(p, "\tbypass=%u\n", state->bypass);
+   if (colorop->bypass_property)
+   drm_printf(p, "\tbypass=%u\n", state->bypass);
 
switch (colorop->type) {
case DRM_COLOROP_1D_CURVE:
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 47a9877099c3..2b7526c9608e 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -84,7 +84,8 @@ static const struct drm_prop_enum_list 
drm_colorop_lut1d_interpolation_list[] =
 /* Init Helpers */
 
 static int drm_colorop_init(struct drm_device *dev, struct drm_colorop 
*colorop,
-   struct drm_plane *plane, enum drm_colorop_type type)
+ 

Re: [PATCH v4 1/3] drm/amd/display: use drm_crtc_vblank_on_config()

2024-08-22 Thread Harry Wentland



On 2024-08-22 11:58, Hamza Mahfooz wrote:
> Hook up drm_crtc_vblank_on_config() in amdgpu_dm. So, that we can enable
> PSR and other static screen optimizations more quickly, while avoiding
> stuttering issues that are accompanied by the following dmesg error:
> 
> [drm:dc_dmub_srv_wait_idle [amdgpu]] *ERROR* Error waiting for DMUB idle: 
> status=3
> 
> This also allows us to mimic how vblanking is handled by the windows
> amdgpu driver.
> 

Please call out the policy change in the commit description and title. While
this is using drm_crtc_vblank_on_config() the important part is that this
stops vblank_disable_immediate = false bit (and associated 5s delay) and
replaces it with a 2 vblank delay, if I read it right.

Harry

> Signed-off-by: Hamza Mahfooz 
> ---
> v3: use a less conservative policy
> 
> v4: drop TODO, use simplified calculation from Michel, use
> DIV64_U64_ROUND_UP() and use a different policy.
> ---
>  .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 36 +--
>  1 file changed, 25 insertions(+), 11 deletions(-)
> 
> 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 7e7929f24ae4..00d4c97e8d26 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -4934,12 +4934,6 @@ static int amdgpu_dm_initialize_drm_device(struct 
> amdgpu_device *adev)
>  
>   if (psr_feature_enabled)
>   amdgpu_dm_set_psr_caps(link);
> -
> - /* TODO: Fix vblank control helpers to delay 
> PSR entry to allow this when
> -  * PSR is also supported.
> -  */
> - if (link->psr_settings.psr_feature_enabled)
> - 
> adev_to_drm(adev)->vblank_disable_immediate = false;
>   }
>   }
>   amdgpu_set_panel_orientation(&aconnector->base);
> @@ -8232,7 +8226,7 @@ static int amdgpu_dm_encoder_init(struct drm_device 
> *dev,
>  
>  static void manage_dm_interrupts(struct amdgpu_device *adev,
>struct amdgpu_crtc *acrtc,
> -  bool enable)
> +  struct dm_crtc_state *acrtc_state)
>  {
>   /*
>* We have no guarantee that the frontend index maps to the same
> @@ -8244,9 +8238,28 @@ static void manage_dm_interrupts(struct amdgpu_device 
> *adev,
>   amdgpu_display_crtc_idx_to_irq_type(
>   adev,
>   acrtc->crtc_id);
> + struct drm_vblank_crtc_config config = {0};
> + struct dc_crtc_timing *timing;
> + int offdelay;
> +
> + if (acrtc_state) {
> + if (amdgpu_ip_version(adev, DCE_HWIP, 0) <
> + IP_VERSION(3, 5, 0)) {
> + drm_crtc_vblank_on(&acrtc->base);
> + } else {
> + timing = &acrtc_state->stream->timing;
> +
> + /* at least 2 frames */
> + offdelay = DIV64_U64_ROUND_UP((u64)20 *
> +   timing->v_total *
> +   timing->h_total,
> +   timing->pix_clk_100hz);
> +
> + config.offdelay_ms = offdelay ?: 30;
> + drm_crtc_vblank_on_config(&acrtc->base,
> +   &config);
> + }
>  
> - if (enable) {
> - drm_crtc_vblank_on(&acrtc->base);
>   amdgpu_irq_get(
>   adev,
>   &adev->pageflip_irq,
> @@ -9320,7 +9333,7 @@ static void amdgpu_dm_commit_streams(struct 
> drm_atomic_state *state,
>   if (old_crtc_state->active &&
>   (!new_crtc_state->active ||
>drm_atomic_crtc_needs_modeset(new_crtc_state))) {
> - manage_dm_interrupts(adev, acrtc, false);
> + manage_dm_interrupts(adev, acrtc, NULL);
>   dc_stream_release(dm_old_crtc_state->stream);
>   }
>   }
> @@ -9835,7 +9848,8 @@ static void amdgpu_dm_atomic_commit_tail(struct 
> drm_atomic_state *state)
>drm_atomic_crtc_needs_modeset(new_crtc_state))) {
>   dc_stream_retain(dm_new_crtc_state->stream);
>   acrtc->dm_irq_params.stream = dm_new_crtc_state->stream;
> - manage_dm_interrupts(adev, acrtc, true);
> + manage_dm_interrupts(adev, acrtc,
> +  to_dm_crtc_state(new_crtc_state));
>   }
>   /* Handle vrr on->off / off->on transitions */
>   amdgpu_dm_handle_vrr_transition(dm_old_crtc_state, 
> dm_new_crtc_state);



Re: [PATCH v4 0/3] faster vblank disable

2024-08-22 Thread Harry Wentland
On 2024-08-22 11:58, Hamza Mahfooz wrote:
> Since, drm_crtc_vblank_on_config() was recently introduced, we want to
> apply laxer vblanking policies across the board, since:
> 
> 1. It will allow us to shut down interrupts faster for better power
>savings.
> 
> 2. As a payment towards tech debt, since drm_vblank_offdelay (which
>defaults to 5s) isn't necessary on AMD hardware.
> 
> Also, patches 2-3 in this series provide even laxer policies and they
> are intentionally separated from patch #1. This way, if issues with them
> are encountered in the wild, it should be easy to bisect the issue to
> those patches.
> 

With an updated description for patch 1 that calls out the new
behavior this series is
Reivewed-by: Harry Wentland 

Harry

> 
> Hamza Mahfooz (3):
>   drm/amd/display: use drm_crtc_vblank_on_config()
>   drm/amd/display: use a more lax vblank enable policy for DCN35+
>   drm/amd/display: use a more lax vblank enable policy for older ASICs
> 
>  .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 39 +--
>  1 file changed, 28 insertions(+), 11 deletions(-)
> 



Re: [PATCH] drm/amd/display: Determine IPS mode by ASIC and PMFW versions

2024-08-27 Thread Harry Wentland



On 2024-08-27 15:53, sunpeng...@amd.com wrote:
> From: Leo Li 
> 
> [Why]
> 
> DCN IPS interoperates with other system idle power features, such as
> Zstates.
> 
> On DCN35, there is a known issue where system Z8 + DCN IPS2 causes a
> hard hang. We observe this on systems where the SBIOS allows Z8.
> 
> Though there is a SBIOS fix, there's no guarantee that users will get it
> any time soon, or even install it. A workaround is needed to prevent
> this from rearing its head in the wild.
> 
> [How]
> 
> For DCN35, check the pmfw version to determine whether the SBIOS has the
> fix. If not, set IPS1+RCG as the deepest possible state in all cases
> except for s0ix and display off (DPMS). Otherwise, enable all IPS
> 
> Signed-off-by: Leo Li 

Reviewed-by: Harry Wentland 

Harry

> ---
>  .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 26 ++-
>  1 file changed, 25 insertions(+), 1 deletion(-)
> 
> 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 a18ecf8607232..a2e4973a4f6e3 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -1754,6 +1754,30 @@ static struct dml2_soc_bb 
> *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *
>   return bb;
>  }
>  
> +static enum dmub_ips_disable_type dm_get_default_ips_mode(
> + struct amdgpu_device *adev)
> +{
> + /*
> +  * On DCN35 systems with Z8 enabled, it's possible for IPS2 + Z8 to
> +  * cause a hard hang. A fix exists for newer PMFW.
> +  *
> +  * As a workaround, for non-fixed PMFW, force IPS1+RCG as the deepest
> +  * IPS state in all cases, except for s0ix and all displays off (DPMS),
> +  * where IPS2 is allowed.
> +  *
> +  * When checking pmfw version, use the major and minor only.
> +  */
> + if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(3, 5, 0) &&
> + (adev->pm.fw_version & 0x0000) < 0x005D6300)
> + return DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF;
> +
> + if (amdgpu_ip_version(adev, DCE_HWIP, 0) >= IP_VERSION(3, 5, 0))
> + return DMUB_IPS_ENABLE;
> +
> + /* ASICs older than DCN35 do not have IPSs */
> + return DMUB_IPS_DISABLE_ALL;
> +}
> +
>  static int amdgpu_dm_init(struct amdgpu_device *adev)
>  {
>   struct dc_init_data init_data;
> @@ -1871,7 +1895,7 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
>   else if (amdgpu_dc_debug_mask & DC_FORCE_IPS_ENABLE)
>   init_data.flags.disable_ips = DMUB_IPS_ENABLE;
>   else
> - init_data.flags.disable_ips = DMUB_IPS_ENABLE;
> + init_data.flags.disable_ips = dm_get_default_ips_mode(adev);
>  
>   init_data.flags.disable_ips_in_vpb = 0;
>  



Re: [PATCH v5 03/44] drm/vkms: Add kunit tests for VKMS LUT handling

2024-08-28 Thread Harry Wentland



On 2024-08-27 13:49, Louis Chauvet wrote:
> Le 19/08/24 - 16:56, Harry Wentland a écrit :
> 
> [...]
> 
>> diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
>> b/drivers/gpu/drm/vkms/vkms_composer.c
>> index 3d6785d081f2..3ecda70c2b55 100644
>> --- a/drivers/gpu/drm/vkms/vkms_composer.c
>> +++ b/drivers/gpu/drm/vkms/vkms_composer.c
>> @@ -435,3 +435,7 @@ int vkms_set_crc_source(struct drm_crtc *crtc, const 
>> char *src_name)
> diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
> b/drivers/gpu/drm/vkms/vkms_composer.c
> index 3d6785d081f2..3ecda70c2b55 100644
> --- a/drivers/gpu/drm/vkms/vkms_composer.c
> +++ b/drivers/gpu/drm/vkms/vkms_composer.c
> @@ -435,3 +435,7 @@ int vkms_set_crc_source(struct drm_crtc *crtc, const char 
> *src_name)
>  
>   return ret;
>  }
> +
> +#ifdef CONFIG_DRM_VKMS_KUNIT_TESTS
> +#include "tests/vkms_color_tests.c"
> +#endif>  
>>  return ret;
>>  }
>> +
>> +#ifdef CONFIG_DRM_VKMS_KUNIT_TESTS
>> +#include "tests/vkms_color_tests.c"
>> +#endif
> 
> This is very strange to include a .c in this file, is it something done a 
> lot in the kernel? I can find only one occurence of this pattern in the 
> kernel [1], the other tests are in their own modules.
> 
> In addition it crate many warning during compilations:
>   warning: symbol 'test_*' was not declared. Should it be static?
> 
> As other tests will be introduced (yuv [2], config [3]), it is maybe 
> interesting to introduce a new module as [2] is doing?

The VISIBLE_IF_KUNIT et al. is much nicer than including a .c file.
Thanks for pointing me to them. Will change this.

Harry

> 
> [1]: https://elixir.bootlin.com/linux/v6.11-rc5/source/fs/ext4/mballoc.c#L7047
> [2]: https://lore.kernel.org/all/20240809-yuv-v10-14-1a7c76416...@bootlin.com/
> [3]: 
> https://lore.kernel.org/all/20240814-google-remove-crtc-index-from-parameter-v1-15-6e179abf9...@bootlin.com/
> 



Re: [RESEND 3/3] drm/amd/display: switch to guid_gen() to generate valid GUIDs

2024-08-28 Thread Harry Wentland



On 2024-08-28 09:58, Alex Deucher wrote:
> On Wed, Aug 28, 2024 at 9:53 AM Jani Nikula  wrote:
>>
>> On Wed, 28 Aug 2024, Daniel Vetter  wrote:
>>> On Mon, Aug 12, 2024 at 03:23:12PM +0300, Jani Nikula wrote:
>>>> Instead of just smashing jiffies into a GUID, use guid_gen() to generate
>>>> RFC 4122 compliant GUIDs.
>>>>
>>>> Signed-off-by: Jani Nikula 
>>>>
>>>> ---
>>>>
>>>> Side note, it baffles me why amdgpu has a copy of this instead of
>>>> plumbing it into drm mst code.
>>>
>>> Yeah ec5fa9fcdeca ("drm/amd/display: Adjust the MST resume flow") promised
>>> a follow-up, but that seems to have never materialized. Really should
>>> materialize though. Patch lgtm
>>>
>>> Reviewed-by: Daniel Vetter 
>>
>> Thanks!
>>
>> Cc: AMD folks, ack for merging the series via drm-misc-next?
> 
> Unless Harry has any objections,
> Acked-by: Alex Deucher 
> 

Acked-by: Harry Wentland 

Harry

>>
>> BR,
>> Jani.
>>
>>
>>>
>>>
>>>> ---
>>>>  .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 23 ++-
>>>>  1 file changed, 12 insertions(+), 11 deletions(-)
>>>>
>>>> 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 72c10fc2c890..ce05e7e2a383 100644
>>>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>>>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>>>> @@ -2568,9 +2568,9 @@ static int dm_late_init(void *handle)
>>>>
>>>>  static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr)
>>>>  {
>>>> +u8 buf[UUID_SIZE];
>>>> +guid_t guid;
>>>>  int ret;
>>>> -u8 guid[16];
>>>> -u64 tmp64;
>>>>
>>>>  mutex_lock(&mgr->lock);
>>>>  if (!mgr->mst_primary)
>>>> @@ -2591,26 +2591,27 @@ static void resume_mst_branch_status(struct 
>>>> drm_dp_mst_topology_mgr *mgr)
>>>>  }
>>>>
>>>>  /* Some hubs forget their guids after they resume */
>>>> -ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
>>>> -if (ret != 16) {
>>>> +ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, buf, sizeof(buf));
>>>> +if (ret != sizeof(buf)) {
>>>>  drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during 
>>>> suspend?\n");
>>>>  goto out_fail;
>>>>  }
>>>>
>>>> -if (memchr_inv(guid, 0, 16) == NULL) {
>>>> -tmp64 = get_jiffies_64();
>>>> -memcpy(&guid[0], &tmp64, sizeof(u64));
>>>> -memcpy(&guid[8], &tmp64, sizeof(u64));
>>>> +import_guid(&guid, buf);
>>>>
>>>> -ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, guid, 16);
>>>> +if (guid_is_null(&guid)) {
>>>> +guid_gen(&guid);
>>>> +export_guid(buf, &guid);
>>>>
>>>> -if (ret != 16) {
>>>> +ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, buf, sizeof(buf));
>>>> +
>>>> +if (ret != sizeof(buf)) {
>>>>  drm_dbg_kms(mgr->dev, "check mstb guid failed - 
>>>> undocked during suspend?\n");
>>>>  goto out_fail;
>>>>  }
>>>>  }
>>>>
>>>> -import_guid(&mgr->mst_primary->guid, guid);
>>>> +guid_copy(&mgr->mst_primary->guid, &guid);
>>>>
>>>>  out_fail:
>>>>  mutex_unlock(&mgr->lock);
>>>> --
>>>> 2.39.2
>>>>
>>
>> --
>> Jani Nikula, Intel



Re: [PATCH 1/2] drm/amd/display: Avoid race between dcn10_set_drr() and dc_state_destruct()

2024-09-03 Thread Harry Wentland



On 2024-09-02 05:40, tjak...@math.uni-bielefeld.de wrote:
> From: Tobias Jakobi 
> 
> dc_state_destruct() nulls the resource context of the DC state. The pipe
> context passed to dcn10_set_drr() is a member of this resource context.
> 
> If dc_state_destruct() is called parallel to the IRQ processing (which
> calls dcn10_set_drr() at some point), we can end up using already nulled
> function callback fields of struct stream_resource.
> 
> The logic in dcn10_set_drr() already tries to avoid this, by checking tg
> against NULL. But if the nulling happens exactly after the NULL check and
> before the next access, then we get a race.
> 
> Avoid this by copying tg first to a local variable, and then use this
> variable for all the operations. This should work, as long as nobody
> frees the resource pool where the timing generators live.
> 
> Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/3142
> Fixes: 06ad7e164256 ("drm/amd/display: Destroy DC context while keeping DML 
> and DML2")
> Signed-off-by: Tobias Jakobi 

Thanks for this fix. It also makes the code more readable.

Reviewed-by: Harry Wentland 

Harry

> ---
>  .../amd/display/dc/hwss/dcn10/dcn10_hwseq.c   | 20 +++
>  1 file changed, 12 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c 
> b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c
> index 3306684e805a..da8f2cb3c5db 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c
> @@ -3223,15 +3223,19 @@ void dcn10_set_drr(struct pipe_ctx **pipe_ctx,
>* as well.
>*/
>   for (i = 0; i < num_pipes; i++) {
> - if ((pipe_ctx[i]->stream_res.tg != NULL) && 
> pipe_ctx[i]->stream_res.tg->funcs) {
> - if (pipe_ctx[i]->stream_res.tg->funcs->set_drr)
> - pipe_ctx[i]->stream_res.tg->funcs->set_drr(
> - pipe_ctx[i]->stream_res.tg, ¶ms);
> + /* dc_state_destruct() might null the stream resources, so 
> fetch tg
> +  * here first to avoid a race condition. The lifetime of the 
> pointee
> +  * itself (the timing_generator object) is not a problem here.
> +  */
> + struct timing_generator *tg = pipe_ctx[i]->stream_res.tg;
> +
> + if ((tg != NULL) && tg->funcs) {
> + if (tg->funcs->set_drr)
> + tg->funcs->set_drr(tg, ¶ms);
>   if (adjust.v_total_max != 0 && adjust.v_total_min != 0)
> - if 
> (pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control)
> - 
> pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control(
> - pipe_ctx[i]->stream_res.tg,
> - event_triggers, num_frames);
> + if (tg->funcs->set_static_screen_control)
> + tg->funcs->set_static_screen_control(
> + tg, event_triggers, num_frames);
>   }
>   }
>  }



Re: [PATCH 2/2] drm/amd/display: Avoid race between dcn35_set_drr() and dc_state_destruct()

2024-09-03 Thread Harry Wentland



On 2024-09-02 05:40, tjak...@math.uni-bielefeld.de wrote:
> From: Tobias Jakobi 
> 
> dc_state_destruct() nulls the resource context of the DC state. The pipe
> context passed to dcn35_set_drr() is a member of this resource context.
> 
> If dc_state_destruct() is called parallel to the IRQ processing (which
> calls dcn35_set_drr() at some point), we can end up using already nulled
> function callback fields of struct stream_resource.
> 
> The logic in dcn35_set_drr() already tries to avoid this, by checking tg
> against NULL. But if the nulling happens exactly after the NULL check and
> before the next access, then we get a race.
> 
> Avoid this by copying tg first to a local variable, and then use this
> variable for all the operations. This should work, as long as nobody
> frees the resource pool where the timing generators live.
> 
> Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/3142
> Fixes: 06ad7e164256 ("drm/amd/display: Destroy DC context while keeping DML 
> and DML2")
> Signed-off-by: Tobias Jakobi 

Reviewed-by: Harry Wentland 

Harry

> ---
>  .../amd/display/dc/hwss/dcn35/dcn35_hwseq.c   | 20 +++
>  1 file changed, 12 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c 
> b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
> index dcced89c07b3..4e77728dac10 100644
> --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
> +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
> @@ -1370,7 +1370,13 @@ void dcn35_set_drr(struct pipe_ctx **pipe_ctx,
>   params.vertical_total_mid_frame_num = adjust.v_total_mid_frame_num;
>  
>   for (i = 0; i < num_pipes; i++) {
> - if ((pipe_ctx[i]->stream_res.tg != NULL) && 
> pipe_ctx[i]->stream_res.tg->funcs) {
> + /* dc_state_destruct() might null the stream resources, so 
> fetch tg
> +  * here first to avoid a race condition. The lifetime of the 
> pointee
> +  * itself (the timing_generator object) is not a problem here.
> +  */
> + struct timing_generator *tg = pipe_ctx[i]->stream_res.tg;
> +
> + if ((tg != NULL) && tg->funcs) {
>   struct dc_crtc_timing *timing = 
> &pipe_ctx[i]->stream->timing;
>   struct dc *dc = pipe_ctx[i]->stream->ctx->dc;
>  
> @@ -1383,14 +1389,12 @@ void dcn35_set_drr(struct pipe_ctx **pipe_ctx,
>   num_frames = 2 * (frame_rate % 60);
>   }
>   }
> - if (pipe_ctx[i]->stream_res.tg->funcs->set_drr)
> - pipe_ctx[i]->stream_res.tg->funcs->set_drr(
> - pipe_ctx[i]->stream_res.tg, ¶ms);
> + if (tg->funcs->set_drr)
> + tg->funcs->set_drr(tg, ¶ms);
>   if (adjust.v_total_max != 0 && adjust.v_total_min != 0)
> - if 
> (pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control)
> - 
> pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control(
> - pipe_ctx[i]->stream_res.tg,
> - event_triggers, num_frames);
> + if (tg->funcs->set_static_screen_control)
> + tg->funcs->set_static_screen_control(
> + tg, event_triggers, num_frames);
>   }
>   }
>  }



Re: [PATCH v5 19/44] drm/vkms: add 3x4 matrix in color pipeline

2024-09-06 Thread Harry Wentland



On 2024-08-27 13:49, Louis Chauvet wrote:
> Le 19/08/24 - 16:56, Harry Wentland a écrit :
>> 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.
> 
> Is there a reason to use signed-magnitude and not 2s-complement here? I 
> did not read the whole amd driver, but it seems that the matrix is always 
> converted to fixed point notation (amdgpu_dm_fixpt_from_s3132 in 
> amdgpu_dm_color.c). It may reduce the complexity here and in the amd 
> driver too.
> 

It's so as to keep the 3x4 matrix the same as the 3x3 one
(drm_color_ctm_3x4 and drm_color_ctm) and the original
3x3 one was defined to use S31.32 sign-magnitude. I'd
prefer 2s-complement myself but that ship has sailed.

>>
>> Signed-off-by: Harry Wentland 
>> ---
>>  drivers/gpu/drm/vkms/vkms_colorop.c  | 32 +++-
>>  drivers/gpu/drm/vkms/vkms_composer.c | 27 +++
>>  2 files changed, 58 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c 
>> b/drivers/gpu/drm/vkms/vkms_colorop.c
>> index f61dfde47156..adcb08153a09 100644
>> --- a/drivers/gpu/drm/vkms/vkms_colorop.c
>> +++ b/drivers/gpu/drm/vkms/vkms_colorop.c
>> @@ -37,7 +37,37 @@ static int vkms_initialize_color_pipeline(struct 
>> drm_plane *plane, struct drm_pr
>>  
>>  prev_op = op;
>>  
>> -/* 2nd op: 1d curve */
>> +/* 2nd op: 3x4 matrix */
>> +op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
>> +if (!op) {
>> +DRM_ERROR("KMS: Failed to allocate colorop\n");
>> +return -ENOMEM;
>> +}
> 
> Same as before, don't we leak memory/properties here?
> 

Thanks. Fix for this will be in v6 (for both vkms and amdgpu).

>> +ret = drm_colorop_ctm_3x4_init(dev, op, plane);
>> +if (ret)
>> +return ret;
>> +
>> +drm_colorop_set_next_property(prev_op, op);
>> +
>> +prev_op = op;
>> +
>> +/* 3rd op: 3x4 matrix */
>> +op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
>> +if (!op) {
>> +DRM_ERROR("KMS: Failed to allocate colorop\n");
>> +return -ENOMEM;
>> +}
>> +
>> +ret = drm_colorop_ctm_3x4_init(dev, op, plane);
>> +if (ret)
>> +return ret;
>> +
>> +drm_colorop_set_next_property(prev_op, op);
>> +
>> +prev_op = op;
>> +
>> +/* 4th op: 1d curve */
>>  op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
>>  if (!op) {
>>  DRM_ERROR("KMS: Failed to allocate colorop\n");
>> diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
>> b/drivers/gpu/drm/vkms/vkms_composer.c
>> index 6e939d3a6d5c..bd1df06ced85 100644
>> --- a/drivers/gpu/drm/vkms/vkms_composer.c
>> +++ b/drivers/gpu/drm/vkms/vkms_composer.c
>> @@ -164,6 +164,30 @@ 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;
>> +
>> +rf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[0]), 
>> drm_int2fixp(pixel->r)) +
>> + drm_fixp_mul(drm_sm2fixp(matrix->matrix[1]), 
>> drm_int2fixp(pixel->g)) +
>> + drm_fixp_mul(drm_sm2fixp(matrix->matrix[2]), 
>> drm_int2fixp(pixel->b)) +
>> + drm_sm2fixp(matrix->matrix[3]);
>> +
>> +gf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[4]), 
>> drm_int2fixp(pixel->r)) +
>> + drm_fixp_mul(drm_sm2fixp(matrix->matrix[5]), 
>> drm_int2fixp(pixel->g)) +
>> + drm_fixp_mul(drm_sm2fixp(matrix->matrix[6]), 
>> drm_int2fixp(pixel->b)) +
>> + drm_sm2fixp(matrix->matrix[7]);
>> +
>> +bf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[8]), 
>> drm_int2fixp(pixel->r)) +
>> + drm_fixp_mul(drm_sm2fixp(matrix->matrix[9]), 
>> drm_int2fixp(pixel->g)) +
>> + drm_fixp_mul(drm_sm2

Re: [PATCH] drm/amd/display: Do not reset planes based on crtc zpos_changed

2024-09-06 Thread Harry Wentland



On 2024-09-06 17:20, sunpeng...@amd.com wrote:
> From: Leo Li 
> 
> [Why]
> 
> drm_normalize_zpos will set the crtc_state->zpos_changed to 1 if any of
> it's assigned planes changes zpos, or is removed/added from it.
> 
> To have amdgpu_dm request a plane reset on this is too broad. For
> example, if only the cursor plane was moved from one crtc to another,
> the crtc's zpos_changed will be set to true. But that does not mean that
> the underlying primary plane requires a reset.
> 
> [How]
> 
> Narrow it down so that only the plane that has a change in zpos will
> require a reset.
> 
> As a future TODO, we can further optimize this by only requiring a reset
> on z-order change. Z-order is different from z-pos, since a zpos change
> doesn't necessarily mean the z-ordering changed, and DC should only
> require a reset if the z-ordering changed.
> 
> For example, the following zpos update does not change z-ordering:
> 
> Plane A: zpos 2 -> 3
> Plane B: zpos 1 -> 2
> => Plane A is still on top of plane B: no reset needed
> 
> Whereas this one does change z-ordering:
> 
> Plane A: zpos 2 -> 1
> Plane B: zpos 1 -> 2
> => Plane A changed from on top, to below plane B: reset needed
> 
> Fixes: 38e0c3df6dbd ("drm/amd/display: Move PRIMARY plane zpos higher")
> Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/3569
> Signed-off-by: Leo Li 

Reviewed-by: Harry Wentland 

Harry

> ---
>  drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> 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 43bf8883ddc0f..a3edaf658ae00 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -10579,7 +10579,7 @@ static bool should_reset_plane(struct 
> drm_atomic_state *state,
>* TODO: We can likely skip bandwidth validation if the only thing that
>* changed about the plane was it'z z-ordering.
>*/
> - if (new_crtc_state->zpos_changed)
> + if (old_plane_state->normalized_zpos != 
> new_plane_state->normalized_zpos)
>   return true;
>  
>   if (drm_atomic_crtc_needs_modeset(new_crtc_state))



Re: [PATCH 0/2] drm/amd: fix VRR race condition during IRQ handling

2024-09-09 Thread Harry Wentland



On 2024-09-09 13:11, Alex Deucher wrote:
> On Sun, Sep 8, 2024 at 7:23 AM Tobias Jakobi
>  wrote:
>>
>> On 9/8/24 09:35, Christopher Snowhill wrote:
>>
>>> On Mon Sep 2, 2024 at 2:40 AM PDT, tjakobi wrote:
 From: Tobias Jakobi 

 Hello,

 this fixes a nasty race condition in the set_drr() callbacks for DCN10
 and DCN35 that has existed now since quite some time, see this GitLab
 issue for reference.

 https://gitlab.freedesktop.org/drm/amd/-/issues/3142

 The report just focuses von DCN10, but the same problem also exists in
 the DCN35 code.
>>> Does the problem not exist in the following references to funcs->set_drr?
>>>
>>> drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c:  if 
>>> (pipe_ctx->stream_res.tg->funcs->set_drr)
>>> drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c:  
>>> pipe_ctx->stream_res.tg->funcs->set_drr(
>>> drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c:  
>>> pipe_ctx[i]->stream_res.tg->funcs->set_drr(
>>> drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c:if 
>>> (pipe_ctx->stream_res.tg->funcs->set_drr)
>>> drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c:
>>> pipe_ctx->stream_res.tg->funcs->set_drr(
>>> drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c:if 
>>> (pipe_ctx->stream_res.tg->funcs->set_drr)
>>> drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c:
>>> pipe_ctx->stream_res.tg->funcs->set_drr(
>>> drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c:if 
>>> (pipe_ctx->stream_res.tg->funcs->set_drr)
>>> drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c:
>>> pipe_ctx->stream_res.tg->funcs->set_drr(
>>> drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c:  if 
>>> (pipe_ctx->stream_res.tg->funcs->set_drr)
>>> drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c:  
>>> pipe_ctx->stream_res.tg->funcs->set_drr(
>>
>> Maybe. But the big difference I see here, is that in this code there
>> isn't even any kind of NULL check applied to tg. Or most of the members
>> of *pipe_ctx. If there really is the same kind of problem here, then one
>> would need to rewrite a bit more code to fix stuff.
>>
>> E.g. in the case of  dcn31_hwseq.c, the questionable code is in
>> dcn31_reset_back_end_for_pipe(), which is static and only called from
>> dcn31_reset_hw_ctx_wrap(). Which is assigned to the .reset_hw_ctx_wrap
>> callback. And this specific callback, from what I can see, is only
>> called from dce110_reset_hw_ctx_wrap(). Which is then assigned to the
>> .apply_ctx_to_hw callback. The callback is only called from
>> dc_commit_state_no_check(). That one is static again, and called from
>> dc_commit_streams().
>>
>> I could trace this even further. My point is: I don't think this is
>> called from any IRQ handler code. And given the depth and complexity of
>> the callgraph, I have to admit, that, at least at this point, this is a
>> bit over my head.
>>
>> Sure, I could now sprinkle a bunch of x != NULL in the code, but that
>> would be merely voodoo. And I usually try to have a theoretical basis
>> when I apply changes to code.
>>
>> Maybe if someone from the AMD display team could give some insight if
>> there still is potentially vulnerable code in some of the instances that
>> Christopher has posted, then I would gladly take a look.
> 
> @Wentland, Harry can you confirm this?
> 

As Tobias said, without extensive analysis and trace of the code in all
possible use-case it's hard to say there's no possible way the other
set_drr calls could potentially have a similar issue.

I think Tobias' analysis is sound and this fixes a number of issues, hence
my RB.

Harry

> Alex
> 
>>
>> With best wishes,
>> Tobias
>>
>>>
 With best wishes,
 Tobias

 Tobias Jakobi (2):
drm/amd/display: Avoid race between dcn10_set_drr() and
  dc_state_destruct()
drm/amd/display: Avoid race between dcn35_set_drr() and
  dc_state_destruct()

   .../amd/display/dc/hwss/dcn10/dcn10_hwseq.c   | 20 +++
   .../amd/display/dc/hwss/dcn35/dcn35_hwseq.c   | 20 +++
   2 files changed, 24 insertions(+), 16 deletions(-)



Re: [PATCH] drm/amdgpu/atomfirmware: Silence UBSAN warning

2024-09-09 Thread Harry Wentland
On 2024-09-09 13:19, Alex Deucher wrote:
> Ping?
> 
> Alex
> 
> On Fri, Sep 6, 2024 at 12:43 PM Alex Deucher  
> wrote:
>>
>> Per the comments, these are variable sized arrays.
>>
>> Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/3613
>> Signed-off-by: Alex Deucher 

Reviewed-by: Harry Wentland 

Harry

>> ---
>>  drivers/gpu/drm/amd/include/atomfirmware.h | 4 ++--
>>  1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/amd/include/atomfirmware.h 
>> b/drivers/gpu/drm/amd/include/atomfirmware.h
>> index 09cbc3afd6d8..b0fc22383e28 100644
>> --- a/drivers/gpu/drm/amd/include/atomfirmware.h
>> +++ b/drivers/gpu/drm/amd/include/atomfirmware.h
>> @@ -1038,7 +1038,7 @@ struct display_object_info_table_v1_4
>>uint16_t  supporteddevices;
>>uint8_t   number_of_path;
>>uint8_t   reserved;
>> -  structatom_display_object_path_v2 display_path[8];   //the real 
>> number of this included in the structure is calculated by using the (whole 
>> structure size - the header size- number_of_path)/size of 
>> atom_display_object_path
>> +  structatom_display_object_path_v2 display_path[];   //the real number 
>> of this included in the structure is calculated by using the (whole 
>> structure size - the header size- number_of_path)/size of 
>> atom_display_object_path
>>  };
>>
>>  struct display_object_info_table_v1_5 {
>> @@ -1048,7 +1048,7 @@ struct display_object_info_table_v1_5 {
>> uint8_t reserved;
>> // the real number of this included in the structure is calculated 
>> by using the
>> // (whole structure size - the header size- number_of_path)/size of 
>> atom_display_object_path
>> -   struct atom_display_object_path_v3 display_path[8];
>> +   struct atom_display_object_path_v3 display_path[];
>>  };
>>
>>  /*
>> --
>> 2.46.0
>>



Re: [PATCH v5 00/44] Color Pipeline API w/ VKMS

2024-09-09 Thread Harry Wentland



On 2024-08-29 10:55, Xaver Hugl wrote:
> Hi,
> 
> I have a WIP implementation of this for KWin at
> https://invent.kde.org/plasma/kwin/-/commits/work/zamundaaa/drm-colorop.
> It maps KWin's color pipeline to the drm one to get (primary plane
> only, for now) direct scanout of HDR content on SDR displays while
> doing tone mapping (or SDR on HDR, but that's been possible before).
> It currently uses the two 1D custom luts and the 3D lut for this; once
> I add support for named 1D curves, it should also make use of that and
> the matrix. I haven't tested the Intel version yet, but if it can
> match the pipeline, it should work the same there.
> 
> In testing your amd-color-pipeline-v5 branch I get two crashes in the
> kernel though: https://invent.kde.org/-/snippets/3217
> The null pointer dereference only happens sometimes, seemingly
> randomly, but if I make KWin generate new blobs each frame, I can 100%
> reliably reproduce the list_add corruption problem by just opening
> glxgears in fullscreen while the color profile of the display is set
> to "built in" (which makes it use the 3D LUT for gamut mapping).
> Here's a branch that generates the color pipeline each frame and
> triggers this: 
> https://invent.kde.org/plasma/kwin/-/commits/work/zamundaaa/drm-colorop-list-add-corruption
> 

Thanks for the test branch. Got a repro and will investigate.

Harry

> 
> 
> Am Mo., 19. Aug. 2024 um 22:57 Uhr schrieb Harry Wentland
> :
>>
>> 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.
>>
>> 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 and kwin 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
>>  - n

[PATCH] drm/colorop: get DATA blob ref at duplicate_state

2024-09-10 Thread Harry Wentland
Signed-off-by: Harry Wentland 
---

It was a stupid mistake on my part. The duplicate_state
function needs to take a reference to the blob.

This should fix it. Please give it a try if you can.
I'll roll it into the patch that introduces the bug in
my v6.

Harry

 drivers/gpu/drm/drm_colorop.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 86f7f084a73f..fd1cd934df48 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -401,6 +401,9 @@ static void 
__drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colo
 {
memcpy(state, colorop->state, sizeof(*state));
 
+   if (state->data)
+   drm_property_blob_get(state->data);
+
state->bypass = true;
 }
 
-- 
2.46.0



Re: [PATCH] drm/amd/display: Add all planes on CRTC to state for overlay cursor

2024-09-11 Thread Harry Wentland



On 2024-09-11 09:06, sunpeng...@amd.com wrote:
> From: Leo Li 
> 
> [Why]
> 
> DC has a special commit path for native cursor, which use the built-in
> cursor pipe within DCN planes. This update path does not require all
> enabled planes to be added to the list of surface updates sent to DC.
> 
> This is not the case for overlay cursor; it uses the same path as MPO
> commits. This update path requires all enabled planes to be added to the
> list of surface updates sent to DC. Otherwise, DC will disable planes
> not inside the list.
> 
> [How]
> 
> If overlay cursor is needed, add all planes on the same CRTC as this
> cursor to the atomic state. This is already done for non-cursor planes
> (MPO), just before the added lines.
> 
> Fixes: 1b04dcca4fb1 ("drm/amd/display: Introduce overlay cursor mode")
> Closes: 
> https://lore.kernel.org/lkml/f68020a3-c413-482d-beb2-5432d98a1...@amd.com
> Signed-off-by: Leo Li 
> Tested-by: Mikhail Gavrilov 

Reviewed-by: Harry Wentland 

Harry

> ---
>  drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 11 +++
>  1 file changed, 11 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 a3edaf658ae00..6b5baa3e20c49 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -11427,6 +11427,17 @@ static int amdgpu_dm_atomic_check(struct drm_device 
> *dev,
>   drm_dbg(dev, "Failed to determine cursor mode\n");
>   goto fail;
>   }
> +
> + /*
> +  * If overlay cursor is needed, DC cannot go through the
> +  * native cursor update path. All enabled planes on the CRTC
> +  * need to be added for DC to not disable a plane by mistake
> +  */
> + if (dm_new_crtc_state->cursor_mode == DM_CURSOR_OVERLAY_MODE) {
> + ret = drm_atomic_add_affected_planes(state, crtc);
> + if (ret)
> + goto fail;
> + }
>   }
>  
>   /* Remove exiting planes if they are modified */



Re: [PATCH] drm/amd/display: Validate backlight caps are sane

2024-09-13 Thread Harry Wentland



On 2024-09-13 14:00, Mario Limonciello wrote:
> Currently amdgpu takes backlight caps provided by the ACPI tables
> on systems as is.  If the firmware sets maximums that are too low
> this means that users don't get a good experience.
> 
> To avoid having to maintain a quirk list of such systems, do a sanity
> check on the values.  Check that the spread is at least half of the
> values that amdgpu would use if no ACPI table was found and if not
> use the amdgpu defaults.
> 
> Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/3020
> Signed-off-by: Mario Limonciello 
> ---
>  .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c| 16 
>  1 file changed, 16 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 5942fc4e1c86..ad66f09cd0bb 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -4428,6 +4428,7 @@ static int amdgpu_dm_mode_config_init(struct 
> amdgpu_device *adev)
>  
>  #define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12
>  #define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255
> +#define AMDGPU_DM_MIN_SPREAD ((AMDGPU_DM_DEFAULT_MAX_BACKLIGHT - 
> AMDGPU_DM_DEFAULT_MIN_BACKLIGHT) / 2)
>  #define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50
>  
>  static void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager 
> *dm,
> @@ -4442,6 +4443,21 @@ static void amdgpu_dm_update_backlight_caps(struct 
> amdgpu_display_manager *dm,
>   return;
>  
>   amdgpu_acpi_get_backlight_caps(&caps);
> +
> + /* validate the firmware value is sane */
> + if (caps.caps_valid) {
> + int spread = caps.max_input_signal - caps.min_input_signal;
> +
> + if (caps.max_input_signal > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
> + caps.min_input_signal < AMDGPU_DM_DEFAULT_MIN_BACKLIGHT ||

Would we still want to allow signals below AMDGPU_DM_DEFAULT_MIN_BACKLIGHT
(but above 0)? The min backlight value of 12 is a bit conservative and
I wouldn't be surprised if systems set it lower via ACPI.

The rest looks like great checks that we should absolutely have.

Harry

> + spread > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
> + spread < AMDGPU_DM_MIN_SPREAD) {
> + DRM_DEBUG_KMS("DM: Invalid backlight caps: min=%d, 
> max=%d\n",
> +   caps.min_input_signal, 
> caps.max_input_signal);
> + caps.caps_valid = false;
> + }
> + }
> +
>   if (caps.caps_valid) {
>   dm->backlight_caps[bl_idx].caps_valid = true;
>   if (caps.aux_support)



Re: [PATCH] drm/amd/display: Validate backlight caps are sane

2024-09-13 Thread Harry Wentland



On 2024-09-13 14:51, Mario Limonciello wrote:
> On 9/13/2024 13:47, Harry Wentland wrote:
>>
>>
>> On 2024-09-13 14:00, Mario Limonciello wrote:
>>> Currently amdgpu takes backlight caps provided by the ACPI tables
>>> on systems as is.  If the firmware sets maximums that are too low
>>> this means that users don't get a good experience.
>>>
>>> To avoid having to maintain a quirk list of such systems, do a sanity
>>> check on the values.  Check that the spread is at least half of the
>>> values that amdgpu would use if no ACPI table was found and if not
>>> use the amdgpu defaults.
>>>
>>> Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/3020
>>> Signed-off-by: Mario Limonciello 
>>> ---
>>>   .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c    | 16 
>>>   1 file changed, 16 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 5942fc4e1c86..ad66f09cd0bb 100644
>>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>>> @@ -4428,6 +4428,7 @@ static int amdgpu_dm_mode_config_init(struct 
>>> amdgpu_device *adev)
>>>     #define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12
>>>   #define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255
>>> +#define AMDGPU_DM_MIN_SPREAD ((AMDGPU_DM_DEFAULT_MAX_BACKLIGHT - 
>>> AMDGPU_DM_DEFAULT_MIN_BACKLIGHT) / 2)
>>>   #define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50
>>>     static void amdgpu_dm_update_backlight_caps(struct 
>>> amdgpu_display_manager *dm,
>>> @@ -4442,6 +4443,21 @@ static void amdgpu_dm_update_backlight_caps(struct 
>>> amdgpu_display_manager *dm,
>>>   return;
>>>     amdgpu_acpi_get_backlight_caps(&caps);
>>> +
>>> +    /* validate the firmware value is sane */
>>> +    if (caps.caps_valid) {
>>> +    int spread = caps.max_input_signal - caps.min_input_signal;
>>> +
>>> +    if (caps.max_input_signal > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
>>> +    caps.min_input_signal < AMDGPU_DM_DEFAULT_MIN_BACKLIGHT ||
>>
>> Would we still want to allow signals below AMDGPU_DM_DEFAULT_MIN_BACKLIGHT
>> (but above 0)? The min backlight value of 12 is a bit conservative and
>> I wouldn't be surprised if systems set it lower via ACPI.
>>
>> The rest looks like great checks that we should absolutely have.
> 
> I'm waffling about that one because Thomas' testing showed that there was 
> some problems with panel power savings when he quirked the Framework panels 
> below their ACPI default (12) in his v6 series of the Framework quirks.
> 

Ah, didn't know about that one. In that case this is
Reviewed-by: Harry Wentland 

> So my thought process was we should put in an explicit check for now and then 
> when we have panel power savings working without a modeset landed then we can 
> also add code to the backlight set callback to turn off panel power savings 
> when set below AMDGPU_DM_DEFAULT_MIN_BACKLIGHT to prevent the issue he found.
> 

Sounds reasonable.

Harry

>>
>> Harry
>>
>>> +    spread > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
>>> +    spread < AMDGPU_DM_MIN_SPREAD) {
>>> +    DRM_DEBUG_KMS("DM: Invalid backlight caps: min=%d, max=%d\n",
>>> +  caps.min_input_signal, caps.max_input_signal);
>>> +    caps.caps_valid = false;
>>> +    }
>>> +    }
>>> +
>>>   if (caps.caps_valid) {
>>>   dm->backlight_caps[bl_idx].caps_valid = true;
>>>   if (caps.aux_support)
>>
> 



Re: [PATCH v6 2/4] drm/amd/display: Add support for minimum backlight quirk

2024-09-16 Thread Harry Wentland



On 2024-08-26 12:57, Mario Limonciello wrote:
> On 8/24/2024 13:33, Thomas Weißschuh wrote:
>> Not all platforms provide the full range of PWM backlight capabilities
>> supported by the hardware through ATIF.
>> Use the generic drm panel minimum backlight quirk infrastructure to
>> override the capabilities where necessary.
>>
>> Testing the backlight quirk together with the "panel_power_savings"
>> sysfs file has not shown any negative impact.
>> One quirk seems to be that 0% at panel_power_savings=0 seems to be
>> slightly darker than at panel_power_savings=4.
> 
> Thanks; This is the kind of thing I was worried about.
> 
> Harry, Leo,
> 
> Is that expected?  I wonder if we need to internally turn off panel power 
> savings in display code when brightness falls a threshold (12 IIRC was the 
> real "minimum" advertised in the table?).

How much darker? Is it bothersome?

I wonder the FW and driver have different min backlight values now.

Leo, any thoughts?

Harry

> 
>>
>> Signed-off-by: Thomas Weißschuh 
>> Tested-by: Dustin L. Howett 
>> Reviewed-by: Mario Limonciello 
>> ---
>>   drivers/gpu/drm/amd/amdgpu/Kconfig    |  1 +
>>   drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++
>>   2 files changed, 11 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig 
>> b/drivers/gpu/drm/amd/amdgpu/Kconfig
>> index 0051fb1b437f..655c10aef2e3 100644
>> --- a/drivers/gpu/drm/amd/amdgpu/Kconfig
>> +++ b/drivers/gpu/drm/amd/amdgpu/Kconfig
>> @@ -23,6 +23,7 @@ config DRM_AMDGPU
>>   select DRM_BUDDY
>>   select DRM_SUBALLOC_HELPER
>>   select DRM_EXEC
>> +    select DRM_PANEL_BACKLIGHT_QUIRKS
>>   # amdgpu depends on ACPI_VIDEO when ACPI is enabled, for select to work
>>   # ACPI_VIDEO's dependencies must also be selected.
>>   select INPUT if ACPI
>> 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 983a977632ff..056960ea335c 100644
>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>> @@ -93,6 +93,7 @@
>>   #include 
>>   #include 
>>   #include 
>> +#include 
>>   #include 
>>   #include 
>>   #include 
>> @@ -,6 +3334,8 @@ static void update_connector_ext_caps(struct 
>> amdgpu_dm_connector *aconnector)
>>   struct drm_connector *conn_base;
>>   struct amdgpu_device *adev;
>>   struct drm_luminance_range_info *luminance_range;
>> +    const struct drm_edid *drm_edid;
>> +    int min_input_signal_override;
>>     if (aconnector->bl_idx == -1 ||
>>   aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP)
>> @@ -3367,6 +3370,13 @@ static void update_connector_ext_caps(struct 
>> amdgpu_dm_connector *aconnector)
>>   caps->aux_min_input_signal = 0;
>>   caps->aux_max_input_signal = 512;
>>   }
>> +
>> +    drm_edid = drm_edid_alloc(aconnector->edid,
>> +  EDID_LENGTH * (aconnector->edid->extensions + 1));
>> +    min_input_signal_override = 
>> drm_get_panel_min_brightness_quirk(drm_edid);
>> +    drm_edid_free(drm_edid);
>> +    if (min_input_signal_override >= 0)
>> +    caps->min_input_signal = min_input_signal_override;
>>   }
>>     void amdgpu_dm_update_connector_after_detect(
>>
> 



Re: [PATCH] drm/amd/display: Allow backlight to go below `AMDGPU_DM_DEFAULT_MIN_BACKLIGHT`

2024-09-16 Thread Harry Wentland
On 2024-09-15 15:28, Mario Limonciello wrote:
> From: Mario Limonciello 
> 
> The issue with panel power savings compatibility below
> `AMDGPU_DM_DEFAULT_MIN_BACKLIGHT` happens at
> `AMDGPU_DM_DEFAULT_MIN_BACKLIGHT` as well.
> 
> That issue will be fixed separately, so don't prevent the backlight
> brightness from going that low.
> 
> Cc: Harry Wentland 
> Cc: Thomas Weißschuh 
> Link: 
> https://lore.kernel.org/amd-gfx/be04226a-a9e3-4a45-a83b-6d263c655...@t-8ch.de/T/#m400dee4e2fc61fe9470334d20a7c8c89c9aef44f
> Signed-off-by: Mario Limonciello 

Reviewed-by: Harry Wentland 

Harry

> ---
>  drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> 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 ad66f09cd0bb..80b8594fcc33 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -4449,7 +4449,7 @@ static void amdgpu_dm_update_backlight_caps(struct 
> amdgpu_display_manager *dm,
>   int spread = caps.max_input_signal - caps.min_input_signal;
>  
>   if (caps.max_input_signal > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
> - caps.min_input_signal < AMDGPU_DM_DEFAULT_MIN_BACKLIGHT ||
> + caps.min_input_signal < 0 ||
>   spread > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
>   spread < AMDGPU_DM_MIN_SPREAD) {
>   DRM_DEBUG_KMS("DM: Invalid backlight caps: min=%d, 
> max=%d\n",



Re: [PATCH] drm/amd/display: only call sysfs_remove_group() for eDP connectors

2024-02-05 Thread Harry Wentland
On 2024-02-05 10:18, Hamza Mahfooz wrote:
> Since we only register the amdgpu sysfs group for eDP connectors, we
> should only remove it from them. Otherwise, we run into a harmless
> WARN() on device detach for all of the device's non-eDP connectors.
> 
> Fixes: f97e4303da16 ("drm/amd/display: add panel_power_savings sysfs entry to 
> eDP connectors")
> Signed-off-by: Hamza Mahfooz 

Reviewed-by: Harry Wentland 

Harry

> ---
>  drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> 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 adda423615a1..b3a5e730be24 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -6508,7 +6508,9 @@ static void amdgpu_dm_connector_unregister(struct 
> drm_connector *connector)
>  {
>   struct amdgpu_dm_connector *amdgpu_dm_connector = 
> to_amdgpu_dm_connector(connector);
>  
> - sysfs_remove_group(&connector->kdev->kobj, &amdgpu_group);
> + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
> + sysfs_remove_group(&connector->kdev->kobj, &amdgpu_group);
> +
>   drm_dp_aux_unregister(&amdgpu_dm_connector->dm_dp_aux.aux);
>  }
>  



Re: [PATCH] drm/amd/display: Clear phantom stream count and plane count

2024-02-06 Thread Harry Wentland



On 2024-02-05 08:54, Deucher, Alexander wrote:
> [Public]
> 
> 
> [Public]
> 
> 
> Acked-by: Alex Deucher 
> 

Reviewed-by: Harry Wentland 

Harry

--
> *From:* amd-gfx  on behalf of Mario 
> Limonciello 
> *Sent:* Friday, February 2, 2024 7:30 PM
> *To:* amd-gfx@lists.freedesktop.org 
> *Cc:* Limonciello, Mario 
> *Subject:* [PATCH] drm/amd/display: Clear phantom stream count and plane count
>  
> When dc_state_destruct() was refactored the new phantom_stream_count
> and phantom_plane_count members weren't cleared.
> 
> Fixes: 012a04b1d6af ("drm/amd/display: Refactor phantom resource allocation")
> Signed-off-by: Mario Limonciello 
> ---
>  drivers/gpu/drm/amd/display/dc/core/dc_state.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_state.c 
> b/drivers/gpu/drm/amd/display/dc/core/dc_state.c
> index 88c6436b28b6..180ac47868c2 100644
> --- a/drivers/gpu/drm/amd/display/dc/core/dc_state.c
> +++ b/drivers/gpu/drm/amd/display/dc/core/dc_state.c
> @@ -291,11 +291,14 @@ void dc_state_destruct(struct dc_state *state)
>  dc_stream_release(state->phantom_streams[i]);
>  state->phantom_streams[i] = NULL;
>  }
> +   state->phantom_stream_count = 0;
>  
>  for (i = 0; i < state->phantom_plane_count; i++) {
>  dc_plane_state_release(state->phantom_planes[i]);
>  state->phantom_planes[i] = NULL;
>  }
> +   state->phantom_plane_count = 0;
> +
>  state->stream_mask = 0;
>  memset(&state->res_ctx, 0, sizeof(state->res_ctx));
>  memset(&state->pp_display_cfg, 0, sizeof(state->pp_display_cfg));
> -- 
> 2.34.1
> 



Re: [PATCH v2] drm/amd/display: add panel_power_savings sysfs entry to eDP connectors

2024-02-15 Thread Harry Wentland
On 2024-02-02 11:20, Mario Limonciello wrote:
> On 2/2/2024 09:28, Hamza Mahfooz wrote:
>> We want programs besides the compositor to be able to enable or disable
>> panel power saving features. However, since they are currently only
>> configurable through DRM properties, that isn't possible. So, to remedy
>> that issue introduce a new "panel_power_savings" sysfs attribute.
>>

I've been trying to avoid looking at this too closely, partly because
I want ABM enablement by default, with control for users. But the
more I think about this the more uncomfortable I get. The key for my
discomfort is that we're going around the back of DRM master to set
a DRM property. This is apt to create lots of weird behaviors,
especially if compositors also decide to implement support for the
abm_level property and then potentially fight PPD, or other users
of this sysfs.

I'm also not sure a new sysfs is a good candidate for drm-fixes.

Harry

>> Cc: Mario Limonciello 
>> Signed-off-by: Hamza Mahfooz 
> 
> Reviewed-by: Mario Limonciello 
> Tested-by: Mario Limonciello 
> 
>> ---
>> v2: hide ABM_LEVEL_IMMEDIATE_DISABLE in the read case, force an atomic
>>  commit when setting the value, call sysfs_remove_group() in
>>  amdgpu_dm_connector_unregister() and add some documentation.
>> ---
>>   .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 76 +++
>>   1 file changed, 76 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 8590c9f1dda6..3c62489d03dc 100644
>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>> @@ -6436,10 +6436,79 @@ int amdgpu_dm_connector_atomic_get_property(struct 
>> drm_connector *connector,
>>   return ret;
>>   }
>>   +/**
>> + * DOC: panel power savings
>> + *
>> + * The display manager allows you to set your desired **panel power 
>> savings**
>> + * level (between 0-4, with 0 representing off), e.g. using the following::
>> + *
>> + *   # echo 3 > /sys/class/drm/card0-eDP-1/amdgpu/panel_power_savings
>> + *
>> + * Modifying this value can have implications on color accuracy, so tread
>> + * carefully.
>> + */
>> +
>> +static ssize_t panel_power_savings_show(struct device *device,
>> +    struct device_attribute *attr,
>> +    char *buf)
>> +{
>> +    struct drm_connector *connector = dev_get_drvdata(device);
>> +    struct drm_device *dev = connector->dev;
>> +    u8 val;
>> +
>> +    drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>> +    val = to_dm_connector_state(connector->state)->abm_level ==
>> +    ABM_LEVEL_IMMEDIATE_DISABLE ? 0 :
>> +    to_dm_connector_state(connector->state)->abm_level;
>> +    drm_modeset_unlock(&dev->mode_config.connection_mutex);
>> +
>> +    return sysfs_emit(buf, "%u\n", val);
>> +}
>> +
>> +static ssize_t panel_power_savings_store(struct device *device,
>> + struct device_attribute *attr,
>> + const char *buf, size_t count)
>> +{
>> +    struct drm_connector *connector = dev_get_drvdata(device);
>> +    struct drm_device *dev = connector->dev;
>> +    long val;
>> +    int ret;
>> +
>> +    ret = kstrtol(buf, 0, &val);
>> +
>> +    if (ret)
>> +    return ret;
>> +
>> +    if (val < 0 || val > 4)
>> +    return -EINVAL;
>> +
>> +    drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>> +    to_dm_connector_state(connector->state)->abm_level = val ?:
>> +    ABM_LEVEL_IMMEDIATE_DISABLE;
>> +    drm_modeset_unlock(&dev->mode_config.connection_mutex);
>> +
>> +    drm_kms_helper_hotplug_event(dev);
>> +
>> +    return count;
>> +}
>> +
>> +static DEVICE_ATTR_RW(panel_power_savings);
>> +
>> +static struct attribute *amdgpu_attrs[] = {
>> +    &dev_attr_panel_power_savings.attr,
>> +    NULL
>> +};
>> +
>> +static const struct attribute_group amdgpu_group = {
>> +    .name = "amdgpu",
>> +    .attrs = amdgpu_attrs
>> +};
>> +
>>   static void amdgpu_dm_connector_unregister(struct drm_connector *connector)
>>   {
>>   struct amdgpu_dm_connector *amdgpu_dm_connector = 
>> to_amdgpu_dm_connector(connector);
>>   +    sysfs_remove_group(&connector->kdev->kobj, &amdgpu_group);
>>   drm_dp_aux_unregister(&amdgpu_dm_connector->dm_dp_aux.aux);
>>   }
>>   @@ -6541,6 +6610,13 @@ amdgpu_dm_connector_late_register(struct 
>> drm_connector *connector)
>>   to_amdgpu_dm_connector(connector);
>>   int r;
>>   +    if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
>> +    r = sysfs_create_group(&connector->kdev->kobj,
>> +   &amdgpu_group);
>> +    if (r)
>> +    return r;
>> +    }
>> +
>>   amdgpu_dm_register_backlight_device(amdgpu_dm_connector);
>>     if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
> 



Re: [PATCH v2] drm/amd/display: add panel_power_savings sysfs entry to eDP connectors

2024-02-16 Thread Harry Wentland



On 2024-02-16 03:19, Pekka Paalanen wrote:
> On Fri, 2 Feb 2024 10:28:35 -0500
> Hamza Mahfooz  wrote:
> 
>> We want programs besides the compositor to be able to enable or disable
>> panel power saving features.
> 
> Could you also explain why, in the commit message, please?
> 
> It is unexpected for arbitrary programs to be able to override the KMS
> client, and certainly new ways to do so should not be added without an
> excellent justification.
> 
> Maybe debugfs would be more appropriate if the purpose is only testing
> rather than production environments?
> 
>> However, since they are currently only
>> configurable through DRM properties, that isn't possible. So, to remedy
>> that issue introduce a new "panel_power_savings" sysfs attribute.
> 
> When the DRM property was added, what was used as the userspace to
> prove its workings?
> 

I don't think there ever was a userspace implementation and doubt any
exists today. Part of that is on me. In hindsight, the KMS prop should
have never gone upstream.

I suggest we drop the KMS prop entirely.

As for the color accuracy topic, I think it is important that compositors
can have full control over that if needed, while it's also important
for HW vendors to optimize for power when absolute color accuracy is not
needed. An average end-user writing code or working on their slides
would rather have a longer battery life than a perfectly color-accurate
display. We should probably think of a solution that can support both
use-cases.

Harry

> 
> Thanks,
> pq
> 
>>
>> Cc: Mario Limonciello 
>> Signed-off-by: Hamza Mahfooz 
>> ---
>> v2: hide ABM_LEVEL_IMMEDIATE_DISABLE in the read case, force an atomic
>> commit when setting the value, call sysfs_remove_group() in
>> amdgpu_dm_connector_unregister() and add some documentation.
>> ---
>>  .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 76 +++
>>  1 file changed, 76 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 8590c9f1dda6..3c62489d03dc 100644
>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>> @@ -6436,10 +6436,79 @@ int amdgpu_dm_connector_atomic_get_property(struct 
>> drm_connector *connector,
>>  return ret;
>>  }
>>  
>> +/**
>> + * DOC: panel power savings
>> + *
>> + * The display manager allows you to set your desired **panel power 
>> savings**
>> + * level (between 0-4, with 0 representing off), e.g. using the following::
>> + *
>> + *   # echo 3 > /sys/class/drm/card0-eDP-1/amdgpu/panel_power_savings
>> + *
>> + * Modifying this value can have implications on color accuracy, so tread
>> + * carefully.
>> + */
>> +
>> +static ssize_t panel_power_savings_show(struct device *device,
>> +struct device_attribute *attr,
>> +char *buf)
>> +{
>> +struct drm_connector *connector = dev_get_drvdata(device);
>> +struct drm_device *dev = connector->dev;
>> +u8 val;
>> +
>> +drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>> +val = to_dm_connector_state(connector->state)->abm_level ==
>> +ABM_LEVEL_IMMEDIATE_DISABLE ? 0 :
>> +to_dm_connector_state(connector->state)->abm_level;
>> +drm_modeset_unlock(&dev->mode_config.connection_mutex);
>> +
>> +return sysfs_emit(buf, "%u\n", val);
>> +}
>> +
>> +static ssize_t panel_power_savings_store(struct device *device,
>> + struct device_attribute *attr,
>> + const char *buf, size_t count)
>> +{
>> +struct drm_connector *connector = dev_get_drvdata(device);
>> +struct drm_device *dev = connector->dev;
>> +long val;
>> +int ret;
>> +
>> +ret = kstrtol(buf, 0, &val);
>> +
>> +if (ret)
>> +return ret;
>> +
>> +if (val < 0 || val > 4)
>> +return -EINVAL;
>> +
>> +drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
>> +to_dm_connector_state(connector->state)->abm_level = val ?:
>> +ABM_LEVEL_IMMEDIATE_DISABLE;
>> +drm_modeset_unlock(&dev->mode_config.connection_mutex);
>> +
>> +drm_kms_helper_hotplug_event(dev);
>> +
>> +return count;
>> +}
>> +
>> +static DEVICE_ATTR_RW(panel_power_savings);
>> +
>> +static struct attribute *amdgpu_attrs[] = {
>> +&dev_attr_panel_power_savings.attr,
>> +NULL
>> +};
>> +
>> +static const struct attribute_group amdgpu_group = {
>> +.name = "amdgpu",
>> +.attrs = amdgpu_attrs
>> +};
>> +
>>  static void amdgpu_dm_connector_unregister(struct drm_connector *connector)
>>  {
>>  struct amdgpu_dm_connector *amdgpu_dm_connector = 
>> to_amdgpu_dm_connector(connector);
>>  
>> +sysfs_remove_group(&connector->kdev->kobj, &amdgpu_group);
>>  drm_dp_aux_unregister(&amdgpu_dm_connector->dm_dp_aux.aux);
>>  }
>>  
>> @@ -6541,6 +6610,13 @@ a

Re: [PATCH] drm/amd: Only allow one entity to control ABM

2024-02-16 Thread Harry Wentland



On 2024-02-16 09:47, Christian König wrote:
> Am 16.02.24 um 15:42 schrieb Mario Limonciello:
>> On 2/16/2024 08:38, Christian König wrote:
>>> Am 16.02.24 um 15:07 schrieb Mario Limonciello:
>>>> By exporting ABM to sysfs it's possible that DRM master and software
>>>> controlling the sysfs file fight over the value programmed for ABM.
>>>>
>>>> Adjust the module parameter behavior to control who control ABM:
>>>> -2: DRM
>>>> -1: sysfs (IE via software like power-profiles-daemon)
>>>
>>> Well that sounds extremely awkward. Why should a power-profiles-deamon has 
>>> control over the panel power saving features?
>>>
>>> I mean we are talking about things like reducing backlight level when the 
>>> is inactivity, don't we?
>>
>> We're talking about activating the ABM algorithm when the system is in power 
>> saving mode; not from inactivity.  This allows the user to squeeze out some 
>> extra power "just" in that situation.
>>
>> But given the comments on the other patch, I tend to agree with Harry's 
>> proposal instead that we just drop the DRM property entirely as there are no 
>> consumers of it.
> 
> Yeah, but even then the design to let this be controlled by an userspace 
> deamon is questionable. Stuff like that is handled inside the kernel and not 
> exposed to userspace usually.
> 

I think we'll need a bit in our kernel docs describing ABM. Assumptions around 
what it is and does seem to lead to confusion.

The thing is that ABM has a visual impact that not all users like. It would 
make sense for users to be able to change the ABM level as desired, and/or 
configure their power profiles to select a certain ABM level.

ABM will reduce the backlight and compensate by adjusting brightness and 
contrast of the image. It has 5 levels: 0, 1, 2, 3, 4. 0 means off. 4 means 
maximum backlight reduction. IMO, 1 and 2 look okay. 3 and 4 can be quite 
impactful, both to power and visual fidelity.

Harry

> Regards,
> Christian.
> 
>>
>>>
>>> Regards,
>>> Christian.
>>>
>>>> 0-4: User via command line
>>>>
>>>> Also introduce a Kconfig option that allows distributions to choose
>>>> the default policy that is appropriate for them.
>>>>
>>>> Fixes: f97e4303da16 ("drm/amd/display: add panel_power_savings sysfs entry 
>>>> to eDP connectors")
>>>> Signed-off-by: Mario Limonciello 
>>>> ---
>>>> Cc: Hamza Mahfooz 
>>>> Cc: Harry Wentland 
>>>> Cc: Sun peng (Leo) Li 
>>>>   drivers/gpu/drm/amd/amdgpu/Kconfig    | 72 +++
>>>>   drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c   | 23 +++---
>>>>   .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |  6 +-
>>>>   3 files changed, 90 insertions(+), 11 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig 
>>>> b/drivers/gpu/drm/amd/amdgpu/Kconfig
>>>> index 22d88f8ef527..2ab57ccf6f21 100644
>>>> --- a/drivers/gpu/drm/amd/amdgpu/Kconfig
>>>> +++ b/drivers/gpu/drm/amd/amdgpu/Kconfig
>>>> @@ -80,6 +80,78 @@ config DRM_AMDGPU_WERROR
>>>>     Add -Werror to the build flags for amdgpu.ko.
>>>>     Only enable this if you are warning code for amdgpu.ko.
>>>> +choice
>>>> +    prompt "Amdgpu panel power Savings"
>>>> +    default AMDGPU_SYSFS_ABM
>>>> +    help
>>>> +    Control the default behavior for adaptive panel power savings.
>>>> +
>>>> +    Panel power savings features will sacrifice color accuracy
>>>> +    in exchange for power savings.
>>>> +
>>>> +    This can be configured for:
>>>> +    - dynamic control by the DRM master
>>>> +    - dynamic control by sysfs nodes
>>>> +    - statically by the user at kernel compile time
>>>> +
>>>> +    This value can also be overridden by the amdgpu.abmlevel
>>>> +    module parameter.
>>>> +
>>>> +config AMDGPU_DRM_ABM
>>>> +    bool "DRM Master control"
>>>> +    help
>>>> +    Export a property called 'abm_level' that can be
>>>> +    manipulated by the DRM master for supported hardware.
>>>> +
>>>> +config AMDGPU_SYSFS_ABM
>>>> +    bool "sysfs control"
>>>> +    help
>>>> +    Exp

Re: [PATCH v2] drm/amd/display: add panel_power_savings sysfs entry to eDP connectors

2024-02-16 Thread Harry Wentland



On 2024-02-16 10:42, Pekka Paalanen wrote:
> On Fri, 16 Feb 2024 09:33:47 -0500
> Harry Wentland  wrote:
> 
>> On 2024-02-16 03:19, Pekka Paalanen wrote:
>>> On Fri, 2 Feb 2024 10:28:35 -0500
>>> Hamza Mahfooz  wrote:
>>>   
>>>> We want programs besides the compositor to be able to enable or disable
>>>> panel power saving features.  
>>>
>>> Could you also explain why, in the commit message, please?
>>>
>>> It is unexpected for arbitrary programs to be able to override the KMS
>>> client, and certainly new ways to do so should not be added without an
>>> excellent justification.
>>>
>>> Maybe debugfs would be more appropriate if the purpose is only testing
>>> rather than production environments?
>>>   
>>>> However, since they are currently only
>>>> configurable through DRM properties, that isn't possible. So, to remedy
>>>> that issue introduce a new "panel_power_savings" sysfs attribute.  
>>>
>>> When the DRM property was added, what was used as the userspace to
>>> prove its workings?
>>>   
>>
>> I don't think there ever was a userspace implementation and doubt any
>> exists today. Part of that is on me. In hindsight, the KMS prop should
>> have never gone upstream.
>>
>> I suggest we drop the KMS prop entirely.
> 
> Sounds good. What about the sysfs thing? Should it be a debugfs thing
> instead, assuming the below question will be resolved?
> 


It's intended to be used by the power profiles daemon (PPD). I don't think
debugfs is the right choice. See
https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/commit/41ed5d33a82b0ceb7b6d473551eb2aa62cade6bc

>> As for the color accuracy topic, I think it is important that compositors
>> can have full control over that if needed, while it's also important
>> for HW vendors to optimize for power when absolute color accuracy is not
>> needed. An average end-user writing code or working on their slides
>> would rather have a longer battery life than a perfectly color-accurate
>> display. We should probably think of a solution that can support both
>> use-cases.
> 
> I agree. Maybe this pondering should start from "how would it work from
> end user perspective"?
> 
> "Automatically" is probably be most desirable answer. Some kind of

I agree

> desktop settings with options like "save power at the expense of image
> quality":
> - always
> - not if watching movies/gaming
> - on battery
> - on battery, unless I'm watching movies/gaming
> - never
> 

It's interesting that you split out movies/gaming, specifically. AMD's
ABM algorithm seems to have considered movies in particular when
evaluating the power/fidelity trade-off.

I wouldn't think consumer media is very particular about a specific
color fidelity (despite what HDR specs try to make you believe). Where
color fidelity would matter to me is when I'd want to edit pictures or
video.

The "abm_level" property that we expose is really just that, a setting
for the strength of the power-savings effect, with 0 being off and 4 being
maximum strength and power saving, at the expense of fidelity.

Mario's work is to let the PPD control it and set the ABM levels based on
the selected power profile:
0 - Performance
1 - Balance
3 - Power

And I believe we've looked at disabling ABM (setting it to 0) automatically
if we know we're on AC power.

> Or maybe there already is something like that, and it only needs to be
> plumbed through?
> 
> Which would point towards KMS clients needing to control it, which
> means a generic KMS prop rather than vendor specific?
> 
> Or should the desktop compositor be talking to some daemon instead of
> KMS for this? Maybe they already are?
> 

I think the intention is for the PPD to be that daemon. Mario can elaborate.

Harry

> 
> Thanks,
> pq



Re: [PATCH v2] drm/amd/display: add panel_power_savings sysfs entry to eDP connectors

2024-02-16 Thread Harry Wentland



On 2024-02-16 11:11, Harry Wentland wrote:
> 
> 
> On 2024-02-16 10:42, Pekka Paalanen wrote:
>> On Fri, 16 Feb 2024 09:33:47 -0500
>> Harry Wentland  wrote:
>>
>>> On 2024-02-16 03:19, Pekka Paalanen wrote:
>>>> On Fri, 2 Feb 2024 10:28:35 -0500
>>>> Hamza Mahfooz  wrote:
>>>>   
>>>>> We want programs besides the compositor to be able to enable or disable
>>>>> panel power saving features.  
>>>>
>>>> Could you also explain why, in the commit message, please?
>>>>
>>>> It is unexpected for arbitrary programs to be able to override the KMS
>>>> client, and certainly new ways to do so should not be added without an
>>>> excellent justification.
>>>>
>>>> Maybe debugfs would be more appropriate if the purpose is only testing
>>>> rather than production environments?
>>>>   
>>>>> However, since they are currently only
>>>>> configurable through DRM properties, that isn't possible. So, to remedy
>>>>> that issue introduce a new "panel_power_savings" sysfs attribute.  
>>>>
>>>> When the DRM property was added, what was used as the userspace to
>>>> prove its workings?
>>>>   
>>>
>>> I don't think there ever was a userspace implementation and doubt any
>>> exists today. Part of that is on me. In hindsight, the KMS prop should
>>> have never gone upstream.
>>>
>>> I suggest we drop the KMS prop entirely.
>>
>> Sounds good. What about the sysfs thing? Should it be a debugfs thing
>> instead, assuming the below question will be resolved?
>>
> 
> 
> It's intended to be used by the power profiles daemon (PPD). I don't think
> debugfs is the right choice. See
> https://gitlab.freedesktop.org/upower/power-profiles-daemon/-/commit/41ed5d33a82b0ceb7b6d473551eb2aa62cade6bc
> 
>>> As for the color accuracy topic, I think it is important that compositors
>>> can have full control over that if needed, while it's also important
>>> for HW vendors to optimize for power when absolute color accuracy is not
>>> needed. An average end-user writing code or working on their slides
>>> would rather have a longer battery life than a perfectly color-accurate
>>> display. We should probably think of a solution that can support both
>>> use-cases.
>>
>> I agree. Maybe this pondering should start from "how would it work from
>> end user perspective"?
>>
>> "Automatically" is probably be most desirable answer. Some kind of
> 
> I agree
> 
>> desktop settings with options like "save power at the expense of image
>> quality":
>> - always
>> - not if watching movies/gaming
>> - on battery
>> - on battery, unless I'm watching movies/gaming
>> - never
>>
> 
> It's interesting that you split out movies/gaming, specifically. AMD's
> ABM algorithm seems to have considered movies in particular when
> evaluating the power/fidelity trade-off.
> 
> I wouldn't think consumer media is very particular about a specific
> color fidelity (despite what HDR specs try to make you believe). Where
> color fidelity would matter to me is when I'd want to edit pictures or
> video.
> 
> The "abm_level" property that we expose is really just that, a setting
> for the strength of the power-savings effect, with 0 being off and 4 being
> maximum strength and power saving, at the expense of fidelity.
> 
> Mario's work is to let the PPD control it and set the ABM levels based on
> the selected power profile:
> 0 - Performance
> 1 - Balance
> 3 - Power
> 
> And I believe we've looked at disabling ABM (setting it to 0) automatically
> if we know we're on AC power.
> 
>> Or maybe there already is something like that, and it only needs to be
>> plumbed through?
>>
>> Which would point towards KMS clients needing to control it, which
>> means a generic KMS prop rather than vendor specific?
>>
>> Or should the desktop compositor be talking to some daemon instead of
>> KMS for this? Maybe they already are?
>>
> 
> I think the intention is for the PPD to be that daemon. Mario can elaborate.
> 

Some more details and screenshots on how the PPD is expected to work and look:
https://linuxconfig.org/how-to-manage-power-profiles-over-d-bus-with-power-profiles-daemon-on-linux

Harry

> Harry
> 
>>
>> Thanks,
>> pq
> 



Re: [PATCH] drm/amd: Drop abm_level property

2024-02-16 Thread Harry Wentland



On 2024-02-16 10:33, Mario Limonciello wrote:
> This vendor specific property has never been used by userspace
> software and conflicts with the panel_power_savings sysfs file.
> That is a compositor and user could fight over the same data.
> 
> Fixes: f97e4303da16 ("drm/amd/display: add panel_power_savings sysfs entry to 
> eDP connectors")
> Suggested-by: Harry Wentland 
> Signed-off-by: Mario Limonciello 
> --
> Cc: Hamza Mahfooz 
> Cc: Sun peng (Leo) Li 

Reviewed-by: Harry Wentland 

Harry

> ---
>  drivers/gpu/drm/amd/amdgpu/amdgpu_display.c   |  8 
>  drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h  |  2 --
>  drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 14 --
>  3 files changed, 24 deletions(-)
> 
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c 
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
> index b8fbe97efe1d..3ecc7ef95172 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
> @@ -1350,14 +1350,6 @@ int amdgpu_display_modeset_create_props(struct 
> amdgpu_device *adev)
>"dither",
>amdgpu_dither_enum_list, sz);
>  
> - if (adev->dc_enabled) {
> - adev->mode_info.abm_level_property =
> - drm_property_create_range(adev_to_drm(adev), 0,
> -   "abm level", 0, 4);
> - if (!adev->mode_info.abm_level_property)
> - return -ENOMEM;
> - }
> -
>   return 0;
>  }
>  
> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h 
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
> index 2e4911050cc5..1fe21a70ddd0 100644
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
> @@ -324,8 +324,6 @@ struct amdgpu_mode_info {
>   struct drm_property *audio_property;
>   /* FMT dithering */
>   struct drm_property *dither_property;
> - /* Adaptive Backlight Modulation (power feature) */
> - struct drm_property *abm_level_property;
>   /* hardcoded DFP edid from BIOS */
>   struct edid *bios_hardcoded_edid;
>   int bios_hardcoded_edid_size;
> 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 b9ac3d2f8029..e3b32ffba85a 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> @@ -6388,9 +6388,6 @@ int amdgpu_dm_connector_atomic_set_property(struct 
> drm_connector *connector,
>   } else if (property == adev->mode_info.underscan_property) {
>   dm_new_state->underscan_enable = val;
>   ret = 0;
> - } else if (property == adev->mode_info.abm_level_property) {
> - dm_new_state->abm_level = val ?: ABM_LEVEL_IMMEDIATE_DISABLE;
> - ret = 0;
>   }
>  
>   return ret;
> @@ -6433,10 +6430,6 @@ int amdgpu_dm_connector_atomic_get_property(struct 
> drm_connector *connector,
>   } else if (property == adev->mode_info.underscan_property) {
>   *val = dm_state->underscan_enable;
>   ret = 0;
> - } else if (property == adev->mode_info.abm_level_property) {
> - *val = (dm_state->abm_level != ABM_LEVEL_IMMEDIATE_DISABLE) ?
> - dm_state->abm_level : 0;
> - ret = 0;
>   }
>  
>   return ret;
> @@ -7652,13 +7645,6 @@ void amdgpu_dm_connector_init_helper(struct 
> amdgpu_display_manager *dm,
>   aconnector->base.state->max_bpc = 16;
>   aconnector->base.state->max_requested_bpc = 
> aconnector->base.state->max_bpc;
>  
> - if (connector_type == DRM_MODE_CONNECTOR_eDP &&
> - (dc_is_dmcu_initialized(adev->dm.dc) ||
> -  adev->dm.dc->ctx->dmub_srv) && amdgpu_dm_abm_level < 0) {
> - drm_object_attach_property(&aconnector->base.base,
> - adev->mode_info.abm_level_property, 0);
> - }
> -
>   if (connector_type == DRM_MODE_CONNECTOR_HDMIA) {
>   /* Content Type is currently only implemented for HDMI. */
>   drm_connector_attach_content_type_property(&aconnector->base);



[RFC PATCH v4 03/42] drm: Correctly round for fixp2int_round

2024-02-26 Thread Harry Wentland
A value of 0x8000 and higher should round up, and
below should round down. VKMS Kunit tests for lerp_u16
showed that this is not the case. Fix it.

1 << (DRM_FIXED_POINT_HALF - 1) =
1 << 15 = 0x8000

This is not 0.5, but 0.0762939453125.

Instead of some smart math use a simple if/else to
round up or down. This helps people like me to understand
what the function does.

Signed-off-by: Harry Wentland 
---
 include/drm/drm_fixed.h | 15 ++-
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/include/drm/drm_fixed.h b/include/drm/drm_fixed.h
index cb842ba80ddd..8ee549f68537 100644
--- a/include/drm/drm_fixed.h
+++ b/include/drm/drm_fixed.h
@@ -77,6 +77,8 @@ static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B)
 #define DRM_FIXED_DIGITS_MASK  (~DRM_FIXED_DECIMAL_MASK)
 #define DRM_FIXED_EPSILON  1LL
 #define DRM_FIXED_ALMOST_ONE   (DRM_FIXED_ONE - DRM_FIXED_EPSILON)
+#define DRM_FIXED_FRACTIONAL   0xll
+#define DRM_FIXED_HALF 0x8000ll
 
 /**
  * @drm_sm2fixp
@@ -106,11 +108,6 @@ static inline int drm_fixp2int(s64 a)
return ((s64)a) >> DRM_FIXED_POINT;
 }
 
-static inline int drm_fixp2int_round(s64 a)
-{
-   return drm_fixp2int(a + (1 << (DRM_FIXED_POINT_HALF - 1)));
-}
-
 static inline int drm_fixp2int_ceil(s64 a)
 {
if (a >= 0)
@@ -119,6 +116,14 @@ static inline int drm_fixp2int_ceil(s64 a)
return drm_fixp2int(a - DRM_FIXED_ALMOST_ONE);
 }
 
+static inline int drm_fixp2int_round(s64 a)
+{
+   if ((a & DRM_FIXED_FRACTIONAL) < DRM_FIXED_HALF)
+   return drm_fixp2int(a);
+   else
+   return drm_fixp2int_ceil(a);
+}
+
 static inline unsigned drm_fixp_msbset(s64 a)
 {
unsigned shift, sign = (a >> 63) & 1;
-- 
2.44.0



[RFC PATCH v4 00/42] Color Pipeline API w/ VKMS

2024-02-26 Thread Harry Wentland
This is an RFC set for a color pipeline API, along with a sample
implementation in VKMS. All the key API bits are here. VKMS now
supports two named transfer function colorops and two matrix
colorops. We have IGT tests that check all four of these colorops
with a pixel-by-pixel comparison that checks that these colorops
do what we expect them to do with a +/- 1 8 bpc code point margin.

The big new change with v4 is the addition of an amdgpu color
pipeline, for all AMD GPUs with DCN 3 and newer. Amdgpu now support
the following:

1. 1D Curve EOTF
2. 3x4 CTM
3. Multiplier
4. 1D Curve Inverse EOTF
5. 1D LUT
6. 1D Curve EOTF
7. 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.

We are working on two more ops for amdgpu, the HDR multiplier
and the 3DLUT, which will give us this:

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

This, essentially 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

After this we'd like to also add the following ops:
- Scaler (Informational only)
- Color Encoding, to replace drm_plane's COLOR_ENCODING
- Color Range, to replace drm_plane's COLOR_RANGE

This patchset is grouped as follows:
 - Patches 1-3: couple general patches/fixes
 - Patches 4-7: introduce kunit to VKMS
 - Patch 7: description of motivation and details behind the
Color Pipeline API. If you're reading nothing else
but are interested in the topic I highly recommend
you take a look at this.
 - Patches 7-27: DRM core and VKMS changes for color pipeline API
 - Patches 28-40: DRM core and amdgpu changes for color pipeline API

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 here but
haven't had a chance to look at. These will (hopefully) be
addressed in future iterations, either in VKMS or amdgpu:
 - Clear documentation for each drm_colorop_type
 - Add custom LUT colorops to VKMS
 - Add pre-blending 3DLUT
 - How to support HW which can't bypass entire pipeline?
 - Add ability to create colorops that don't have BYPASS
 - Can we do a LOAD / COMMIT model for LUTs (and other properties)?
 - read-only scaling colorop which defines scaling taps and position
 - read-only color format colorop to define supported color formats
   for a pipeline
 - named matrices, for things like converting YUV to RGB

IGT tests can be found at
https://gitlab.freedesktop.org/hwentland/igt-gpu-tools/-/merge_requests/1

IGT patches are also being sent to the igt-dev mailing list.

If you prefer a gitlab MR for review you can find it at
https://gitlab.freedesktop.org/hwentland/linux/-/merge_requests/5

v4:
 - Add amdgpu color pipeline (WIP)
 - Don't block setting of deprecated properties, instead pass client cap
   to atomic check so drivers can ignore these props
 - Drop IOCTL definitions (Pekka)
 - Use enum property for colorop TYPE (Pekka)
 - A few cleanups to the docs (Pekka)
 - Rework the TYPE enum to name relation to avoid code duplication (Pekka)
 - Add missing function declarations (Chaitanya Kumar Borah)
 - Allow setting of NEXT property to NULL in _set_ function (Chaitanya Kumar 
Borah)
 - Add helper for creation of pipeline drm_plane property (Pekka)
 - Always create Bypass pipeline (Pekka)
 - A bunch of changes to VKMS kunit tests (Pekka)
 - Fix index in CTM doc (Pekka)

v3:
 - Abandon IOCTLs and discover colorops as clients iterate the pipeline
 - Remove need for libdrm
 - Add color_pipeline client cap and make mutually exclusive with
   COLOR_RANGE and COLOR_ENCODING properties
 - add CTM colorop to VKMS
 - Use include way for kunit testing static functions (Arthur)
 - Make TYPE a range property
 - Move enum drm_colorop_type to uapi header
 - and a bunch of smaller bits that are highlighted in the relevant commit
   description

v2:
 - Rebased on drm-misc-next
 - Introduce a VKMS Kunit so we can test LUT functionality in vkms_composer
 - Incorporate feedback in color_pipeline.rst doc
 - Add support for sRGB inverse EOTF
 - Add 2nd enumerated TF colorop to VKMS
 - Fix LUTs and some issues with applying LUTs in VKMS

Cc: Ville Syrjala 
Cc: Pekka Paalanen 
Cc: Simon Ser 
Cc: Harry Wentland 
Cc: Melissa Wen 
Cc: Jonas Ådahl 
Cc: Sebastian Wick 
Cc: 

[RFC PATCH v4 01/42] drm: Don't treat 0 as -1 in drm_fixp2int_ceil

2024-02-26 Thread Harry Wentland
Unit testing this in VKMS shows that passing 0 into
this function returns -1, which is highly counter-
intuitive. Fix it by checking whether the input is
>= 0 instead of > 0.

Fixes: 64566b5e767f9 ("drm: Add drm_fixp_from_fraction and drm_fixp2int_ceil")
Signed-off-by: Harry Wentland 
Reviewed-by: Simon Ser 
Reviewed-by: Melissa Wen 
---
 include/drm/drm_fixed.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/drm/drm_fixed.h b/include/drm/drm_fixed.h
index 6ea339d5de08..0c9f917a4d4b 100644
--- a/include/drm/drm_fixed.h
+++ b/include/drm/drm_fixed.h
@@ -95,7 +95,7 @@ static inline int drm_fixp2int_round(s64 a)
 
 static inline int drm_fixp2int_ceil(s64 a)
 {
-   if (a > 0)
+   if (a >= 0)
return drm_fixp2int(a + DRM_FIXED_ALMOST_ONE);
else
return drm_fixp2int(a - DRM_FIXED_ALMOST_ONE);
-- 
2.44.0



[RFC PATCH v4 06/42] drm/vkms: Add kunit tests for VKMS LUT handling

2024-02-26 Thread 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

v4:
 - Test the critical points of the lerp function (Pekka)

v3:
 - Use include way of testing static functions (Arthur)

Signed-off-by: Harry Wentland 
Cc: Arthur Grillo 
---
 drivers/gpu/drm/vkms/Kconfig  |   5 +
 drivers/gpu/drm/vkms/tests/.kunitconfig   |   4 +
 drivers/gpu/drm/vkms/tests/vkms_color_tests.c | 163 ++
 drivers/gpu/drm/vkms/vkms_composer.c  |   8 +-
 4 files changed, 178 insertions(+), 2 deletions(-)
 create mode 100644 drivers/gpu/drm/vkms/tests/.kunitconfig
 create mode 100644 drivers/gpu/drm/vkms/tests/vkms_color_tests.c

diff --git a/drivers/gpu/drm/vkms/Kconfig b/drivers/gpu/drm/vkms/Kconfig
index b9ecdebecb0b..c1f8b343ff0e 100644
--- a/drivers/gpu/drm/vkms/Kconfig
+++ b/drivers/gpu/drm/vkms/Kconfig
@@ -13,3 +13,8 @@ config DRM_VKMS
  a VKMS.
 
  If M is selected the module will be called vkms.
+
+config DRM_VKMS_KUNIT_TESTS
+   tristate "Tests for VKMS" if !KUNIT_ALL_TESTS
+   depends on DRM_VKMS && KUNIT
+   default KUNIT_ALL_TESTS
diff --git a/drivers/gpu/drm/vkms/tests/.kunitconfig 
b/drivers/gpu/drm/vkms/tests/.kunitconfig
new file mode 100644
index ..70e378228cbd
--- /dev/null
+++ b/drivers/gpu/drm/vkms/tests/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_DRM=y
+CONFIG_DRM_VKMS=y
+CONFIG_DRM_VKMS_KUNIT_TESTS=y
diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_tests.c 
b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
new file mode 100644
index ..fc73e48aa57c
--- /dev/null
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#include 
+
+#include 
+
+#define TEST_LUT_SIZE 16
+
+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 },
+};
+
+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)
+{
+   int i;
+
+   KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&test_linear_lut, 
test_linear_array[0].red)), 0);
+
+   for (i = 0; i < TEST_LUT_SIZE; i++)
+   KUNIT_EXPECT_EQ(test, 
drm_fixp2int_ceil(get_lut_index(&test_linear_lut, test_linear_array[i].red)), 
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;
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x0, 0x10, t), 0x10);
+
+   /* odd a */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x1, 0x10, t), 0x10);
+
+   /* odd b */
+   KUNIT_EXPECT_EQ(test, lerp_u16(0x1, 0xf, t), 0xf);
+
+   /* 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

[RFC PATCH v4 07/42] drm/vkms: Avoid reading beyond LUT array

2024-02-26 Thread Harry Wentland
When the floor LUT index (drm_fixp2int(lut_index) is the last
index of the array the ceil LUT index will point to an entry
beyond the array. Make sure we guard against it and use the
value of the floor LUT index.

v3:
 - Drop bits from commit description that didn't contribute
   anything of value

Fixes: db1f254f2cfaf ("drm/vkms: Add support to 1D gamma LUT")
Signed-off-by: Harry Wentland 
Cc: Arthur Grillo 
Reviewed-by: Melissa Wen 
---
 drivers/gpu/drm/vkms/vkms_composer.c | 14 ++
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
b/drivers/gpu/drm/vkms/vkms_composer.c
index d178f2a400f6..b90e446d5954 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -123,6 +123,8 @@ static u16 apply_lut_to_channel_value(const struct 
vkms_color_lut *lut, u16 chan
  enum lut_channel channel)
 {
s64 lut_index = get_lut_index(lut, channel_value);
+   u16 *floor_lut_value, *ceil_lut_value;
+   u16 floor_channel_value, ceil_channel_value;
 
/*
 * This checks if `struct drm_color_lut` has any gap added by the 
compiler
@@ -130,11 +132,15 @@ static u16 apply_lut_to_channel_value(const struct 
vkms_color_lut *lut, u16 chan
 */
static_assert(sizeof(struct drm_color_lut) == sizeof(__u16) * 4);
 
-   u16 *floor_lut_value = (__u16 *)&lut->base[drm_fixp2int(lut_index)];
-   u16 *ceil_lut_value = (__u16 *)&lut->base[drm_fixp2int_ceil(lut_index)];
+   floor_lut_value = (__u16 *)&lut->base[drm_fixp2int(lut_index)];
+   if (drm_fixp2int(lut_index) == (lut->lut_length - 1))
+   /* We're at the end of the LUT array, use same value for ceil 
and floor */
+   ceil_lut_value = floor_lut_value;
+   else
+   ceil_lut_value = (__u16 
*)&lut->base[drm_fixp2int_ceil(lut_index)];
 
-   u16 floor_channel_value = floor_lut_value[channel];
-   u16 ceil_channel_value = ceil_lut_value[channel];
+   floor_channel_value = floor_lut_value[channel];
+   ceil_channel_value = ceil_lut_value[channel];
 
return lerp_u16(floor_channel_value, ceil_channel_value,
lut_index & DRM_FIXED_DECIMAL_MASK);
-- 
2.44.0



[RFC PATCH v4 05/42] drm/vkms: Create separate Kconfig file for VKMS

2024-02-26 Thread Harry Wentland
This aligns with most other DRM drivers and will allow
us to add new VKMS config options without polluting
the DRM Kconfig.

v3:
 - Change SPDX to GPL-2.0-only to match DRM KConfig
   SPDX (Simon)

Signed-off-by: Harry Wentland 
Reviewed-by: Simon Ser 
---
 drivers/gpu/drm/Kconfig  | 14 +-
 drivers/gpu/drm/vkms/Kconfig | 15 +++
 2 files changed, 16 insertions(+), 13 deletions(-)
 create mode 100644 drivers/gpu/drm/vkms/Kconfig

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 2520db0b776e..e3ea8793cb8a 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -289,19 +289,7 @@ config DRM_VGEM
  as used by Mesa's software renderer for enhanced performance.
  If M is selected the module will be called vgem.
 
-config DRM_VKMS
-   tristate "Virtual KMS (EXPERIMENTAL)"
-   depends on DRM && MMU
-   select DRM_KMS_HELPER
-   select DRM_GEM_SHMEM_HELPER
-   select CRC32
-   default n
-   help
- Virtual Kernel Mode-Setting (VKMS) is used for testing or for
- running GPU in a headless machines. Choose this option to get
- a VKMS.
-
- If M is selected the module will be called vkms.
+source "drivers/gpu/drm/vkms/Kconfig"
 
 source "drivers/gpu/drm/exynos/Kconfig"
 
diff --git a/drivers/gpu/drm/vkms/Kconfig b/drivers/gpu/drm/vkms/Kconfig
new file mode 100644
index ..b9ecdebecb0b
--- /dev/null
+++ b/drivers/gpu/drm/vkms/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config DRM_VKMS
+   tristate "Virtual KMS (EXPERIMENTAL)"
+   depends on DRM && MMU
+   select DRM_KMS_HELPER
+   select DRM_GEM_SHMEM_HELPER
+   select CRC32
+   default n
+   help
+ Virtual Kernel Mode-Setting (VKMS) is used for testing or for
+ running GPU in a headless machines. Choose this option to get
+ a VKMS.
+
+ If M is selected the module will be called vkms.
-- 
2.44.0



[RFC PATCH v4 12/42] drm/colorop: Add BYPASS property

2024-02-26 Thread Harry Wentland
We want to be able to bypass each colorop at all times.
Introduce a new BYPASS boolean property for this.

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_atomic_uapi.c |  6 +-
 drivers/gpu/drm/drm_colorop.c | 16 
 include/drm/drm_colorop.h | 20 
 3 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_atomic_uapi.c 
b/drivers/gpu/drm/drm_atomic_uapi.c
index fdd540cfe24f..87f00131be11 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -648,7 +648,9 @@ static int drm_atomic_colorop_set_property(struct 
drm_colorop *colorop,
struct drm_colorop_state *state, struct drm_file *file_priv,
struct drm_property *property, uint64_t val)
 {
-   if (property == colorop->curve_1d_type_property) {
+   if (property == colorop->bypass_property) {
+   state->bypass = val;
+   } else if (property == colorop->curve_1d_type_property) {
state->curve_1d_type = val;
} else {
drm_dbg_atomic(colorop->dev,
@@ -668,6 +670,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop,
 {
if (property == colorop->type_property) {
*val = colorop->type;
+   } else if (property == colorop->bypass_property) {
+   *val = state->bypass;
} else if (property == colorop->curve_1d_type_property) {
*val = state->curve_1d_type;
} else {
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index f4740b6115d1..29979816a2d1 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -80,6 +80,18 @@ int drm_colorop_init(struct drm_device *dev, struct 
drm_colorop *colorop,
   colorop->type_property,
   colorop->type);
 
+   /* bypass */
+   /* TODO can we reuse the mode_config->active_prop? */
+   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;
 }
 EXPORT_SYMBOL(drm_colorop_init);
@@ -123,6 +135,7 @@ int drm_colorop_curve_1d_init(struct drm_device *dev, 
struct drm_colorop *coloro
/* initialize 1D curve only attribute */
prop = drm_property_create_enum(dev, DRM_MODE_PROP_ATOMIC, 
"CURVE_1D_TYPE",
enum_list, len);
+
if (!prop)
return -ENOMEM;
 
@@ -138,6 +151,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 *
@@ -189,6 +204,7 @@ static void __drm_colorop_state_reset(struct 
drm_colorop_state *colorop_state,
  struct drm_colorop *colorop)
 {
colorop_state->colorop = colorop;
+   colorop_state->bypass = true;
 }
 
 /**
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index 539db900f16f..28aa5c1c309e 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -45,6 +45,14 @@ struct drm_colorop_state {
 
/* colorop properties */
 
+   /**
+* @bypass:
+*
+* True if colorop shall be bypassed. False if colorop is
+* enabled.
+*/
+   bool bypass;
+
/**
 * @curve_1d_type:
 *
@@ -132,6 +140,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:
 *
-- 
2.44.0



[RFC PATCH v4 18/42] drm/vkms: Add kunit tests for linear and sRGB LUTs

2024-02-26 Thread Harry Wentland
Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/vkms/tests/vkms_color_tests.c | 37 ++-
 1 file changed, 36 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_tests.c 
b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
index fc73e48aa57c..e6ac01dee830 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
@@ -31,7 +31,6 @@ const struct vkms_color_lut test_linear_lut = {
.channel_value2index_ratio = 0xf000fll
 };
 
-
 static void vkms_color_test_get_lut_index(struct kunit *test)
 {
int i;
@@ -40,6 +39,19 @@ static void vkms_color_test_get_lut_index(struct kunit *test)
 
for (i = 0; i < TEST_LUT_SIZE; i++)
KUNIT_EXPECT_EQ(test, 
drm_fixp2int_ceil(get_lut_index(&test_linear_lut, test_linear_array[i].red)), 
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)
@@ -148,9 +160,32 @@ 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),
{}
 };
 
-- 
2.44.0



[RFC PATCH v4 04/42] drm/vkms: Round fixp2int conversion in lerp_u16

2024-02-26 Thread Harry Wentland
fixp2int always rounds down, fixp2int_ceil rounds up. We need
the new fixp2int_round.

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/vkms/vkms_composer.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
b/drivers/gpu/drm/vkms/vkms_composer.c
index 3c99fb8b54e2..e70cd473e3be 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -98,7 +98,7 @@ static u16 lerp_u16(u16 a, u16 b, s64 t)
 
s64 delta = drm_fixp_mul(b_fp - a_fp,  t);
 
-   return drm_fixp2int(a_fp + delta);
+   return drm_fixp2int_round(a_fp + delta);
 }
 
 static s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value)
-- 
2.44.0



[RFC PATCH v4 16/42] drm/colorop: Add NEXT to colorop state print

2024-02-26 Thread Harry Wentland
v3:
 - Read NEXT ID from drm_colorop's next pointer

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_atomic.c | 1 +
 include/drm/drm_colorop.h| 2 ++
 2 files changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 27a8805c5fa1..d82858dabf06 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -794,6 +794,7 @@ static void drm_atomic_colorop_print_state(struct 
drm_printer *p,
drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type));
drm_printf(p, "\tbypass=%u\n", state->bypass);
drm_printf(p, "\tcurve_1d_type=%s\n", 
drm_get_colorop_curve_1d_type_name(state->curve_1d_type));
+   drm_printf(p, "\tnext=%d\n", colorop->next ? colorop->next->base.id : 
0);
 }
 
 static void drm_atomic_plane_print_state(struct drm_printer *p,
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index fc9a28d138b8..e85ed5aa68d0 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -237,6 +237,8 @@ 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);
+uint32_t drm_colorop_get_next_property(struct drm_colorop *colorop);
+struct drm_colorop *drm_colorop_get_next(struct drm_colorop *colorop);
 
 
 #endif /* __DRM_COLOROP_H__ */
-- 
2.44.0



[RFC PATCH v4 22/42] drm/vkms: Use s32 for internal color pipeline precision

2024-02-26 Thread 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.

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

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/vkms/vkms_composer.c | 57 +---
 drivers/gpu/drm/vkms/vkms_drv.h  |  4 ++
 2 files changed, 48 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
b/drivers/gpu/drm/vkms/vkms_composer.c
index 25b786b49db0..d2101fa55aa3 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -164,7 +164,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)
 {
/* TODO is this right? */
struct drm_colorop_state *colorop_state = colorop->state;
@@ -191,25 +191,56 @@ 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 drm_colorop *colorop = plane_state->base.base.color_pipeline;
+   struct drm_colorop *colorop;
+   struct pixel_argb_s32 pixel;
 
-   while (colorop) {
-   struct drm_colorop_state *colorop_state;
+   for (size_t x = 0; x < output_buffer->n_pixels; x++) {
 
-   if (!colorop)
-   return;
+   /*
+* 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;
 
-   /* TODO this is probably wrong */
-   colorop_state = colorop->state;
+   colorop = plane_state->base.base.color_pipeline;
+   while (colorop) {
+   struct drm_colorop_state *colorop_state;
 
-   if (!colorop_state)
-   return;
+   if (!colorop)
+   return;
+
+   /* TODO this is probably wrong */
+   colorop_state = colorop->state;
+
+   if (!colorop_state)
+   return;
 
-   for (size_t x = 0; x < output_buffer->n_pixels; x++)
if (!colorop_state->bypass)
-   apply_colorop(&output_buffer->pixels[x], 
colorop);
+   apply_colorop(&pixel, colorop);
 
-   colorop = colorop->next;
+   colorop = colorop->next;
+   }
+
+   /* clamp pixel */
+   pixel.a = max(min(pixel.a, 0x), 0x0);
+   pixel.r = max(min(pixel.r, 0x), 0x0);
+   pixel.g = max(min(pixel.g, 0x), 0x0);
+   pixel.b = max(min(pixel.b, 0x), 0x0);
+
+   /* put back to output_buffer */
+   output_buffer->pixels[x].a = pixel.a;
+   output_buffer->pixels[x].r = pixel.r;
+   output_buffer->pixels[x].g = pixel.g;
+   output_buffer->pixels[x].b = pixel.b;
}
 }
 
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index 2bcc24c196a2..fadb7685a360 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -36,6 +36,10 @@ struct vkms_frame_info {
unsigned

[RFC PATCH v4 17/42] drm/vkms: Add enumerated 1D curve colorop

2024-02-26 Thread 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.

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

Signed-off-by: Harry Wentland 
Signed-off-by: Alex Hung 
---
 drivers/gpu/drm/vkms/Makefile|   4 +-
 drivers/gpu/drm/vkms/vkms_colorop.c  |  70 +++
 drivers/gpu/drm/vkms/vkms_composer.c |  45 ++
 drivers/gpu/drm/vkms/vkms_drv.h  |   4 +
 drivers/gpu/drm/vkms/vkms_luts.c | 802 +++
 drivers/gpu/drm/vkms/vkms_luts.h |  12 +
 drivers/gpu/drm/vkms/vkms_plane.c|   2 +
 7 files changed, 938 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/vkms/vkms_colorop.c
 create mode 100644 drivers/gpu/drm/vkms/vkms_luts.c
 create mode 100644 drivers/gpu/drm/vkms/vkms_luts.h

diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile
index 1b28a6a32948..c38455c46be4 100644
--- a/drivers/gpu/drm/vkms/Makefile
+++ b/drivers/gpu/drm/vkms/Makefile
@@ -6,6 +6,8 @@ vkms-y := \
vkms_formats.o \
vkms_crtc.o \
vkms_composer.o \
-   vkms_writeback.o
+   vkms_writeback.o \
+   vkms_colorop.o \
+   vkms_luts.o
 
 obj-$(CONFIG_DRM_VKMS) += vkms.o
diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c 
b/drivers/gpu/drm/vkms/vkms_colorop.c
new file mode 100644
index ..d2db366da6d3
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_colorop.c
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define MAX_COLOR_PIPELINES 5
+
+static const u64 supported_tfs =
+   BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
+   BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF);
+
+const int vkms_initialize_color_pipeline(struct drm_plane *plane, struct 
drm_prop_enum_list *list)
+{
+
+   struct drm_colorop *op, *prev_op;
+   struct drm_device *dev = plane->dev;
+   int ret;
+
+   /* 1st op: 1d curve */
+   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!op) {
+   DRM_ERROR("KMS: Failed to allocate colorop\n");
+   return -ENOMEM;
+   }
+
+   ret = drm_colorop_curve_1d_init(dev, op, plane, supported_tfs);
+   if (ret)
+   return ret;
+
+   list->type = op->base.id;
+   list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", op->base.id);
+
+   prev_op = op;
+
+   /* 2nd op: 1d curve */
+   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!op) {
+   DRM_ERROR("KMS: Failed to allocate colorop\n");
+   return -ENOMEM;
+   }
+
+   ret = drm_colorop_curve_1d_init(dev, op, plane, supported_tfs);
+   if (ret)
+   return ret;
+
+   drm_colorop_set_next_property(prev_op, op);
+
+   return 0;
+}
+
+int vkms_initialize_colorops(struct drm_plane *plane)
+{
+   struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES];
+   int len = 0;
+   int ret;
+
+   /* Add color pipeline */
+   ret = vkms_initialize_color_pipeline(plane, &(pipelines[len]));
+   if (ret)
+   return ret;
+   len++;
+
+   /* Create COLOR_PIPELINE property and attach */
+   drm_plane_create_color_pipeline_property(plane, pipelines, len);
+
+   return 0;
+}
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
b/drivers/gpu/drm/vkms/vkms_composer.c
index b90e446d5954..9493bdb1ba3f 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -12,6 +12,7 @@
 #include 
 
 #include "vkms_drv.h"
+#include "vkms_luts.h"
 
 static u16 pre_mul_blend_channel(u16 src, u16 dst, u16 alpha)
 {
@@ -163,6 +164,47 @@ static void apply_lut(const struct vkms_crtc_state 
*crtc_state, struct line_buff
}
 }
 
+static void pre_blend_color_transform(const struct vkms_plane_state 
*plane_state, struct line_buffer *output_buffer)
+{
+   struct drm_colorop *colorop = plane_state->base.base.color_pipeline;
+
+   while (colorop) {
+   struct drm_colorop_state *colorop_state;
+
+   if (!colorop)
+   return;
+
+

[RFC PATCH v4 23/42] drm/vkms: add 3x4 matrix in color pipeline

2024-02-26 Thread Harry Wentland
We add two 3x4 matrices into the VKMS color pipeline. The reason
we're adding matrices is so that we can test that application
of a matrix and its inverse yields an output equal to the input
image.

One complication with the matrix implementation has to do with
the fact that the matrix entries are in signed-magnitude fixed
point, whereas the drm_fixed.h implementation uses 2s-complement.
The latter one is the one that we want for easy addition and
subtraction, so we convert all entries to 2s-complement.

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/vkms/vkms_colorop.c  | 32 +++-
 drivers/gpu/drm/vkms/vkms_composer.c | 27 +++
 include/drm/drm_colorop.h|  2 ++
 3 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c 
b/drivers/gpu/drm/vkms/vkms_colorop.c
index d2db366da6d3..a0e54b2c1f7a 100644
--- a/drivers/gpu/drm/vkms/vkms_colorop.c
+++ b/drivers/gpu/drm/vkms/vkms_colorop.c
@@ -35,7 +35,37 @@ const int vkms_initialize_color_pipeline(struct drm_plane 
*plane, struct drm_pro
 
prev_op = op;
 
-   /* 2nd op: 1d curve */
+   /* 2nd op: 3x4 matrix */
+   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!op) {
+   DRM_ERROR("KMS: Failed to allocate colorop\n");
+   return -ENOMEM;
+   }
+
+   ret = drm_colorop_ctm_3x4_init(dev, op, plane);
+   if (ret)
+   return ret;
+
+   drm_colorop_set_next_property(prev_op, op);
+
+   prev_op = op;
+
+   /* 3rd op: 3x4 matrix */
+   op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+   if (!op) {
+   DRM_ERROR("KMS: Failed to allocate colorop\n");
+   return -ENOMEM;
+   }
+
+   ret = drm_colorop_ctm_3x4_init(dev, op, plane);
+   if (ret)
+   return ret;
+
+   drm_colorop_set_next_property(prev_op, op);
+
+   prev_op = op;
+
+   /* 4th op: 1d curve */
op = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
if (!op) {
DRM_ERROR("KMS: Failed to allocate colorop\n");
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
b/drivers/gpu/drm/vkms/vkms_composer.c
index d2101fa55aa3..8bbfce651526 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -164,6 +164,30 @@ 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;
+
+   rf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[0]), 
drm_int2fixp(pixel->r)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[1]), 
drm_int2fixp(pixel->g)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[2]), 
drm_int2fixp(pixel->b)) +
+drm_sm2fixp(matrix->matrix[3]);
+
+   gf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[4]), 
drm_int2fixp(pixel->r)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[5]), 
drm_int2fixp(pixel->g)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[6]), 
drm_int2fixp(pixel->b)) +
+drm_sm2fixp(matrix->matrix[7]);
+
+   bf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[8]), 
drm_int2fixp(pixel->r)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[9]), 
drm_int2fixp(pixel->g)) +
+drm_fixp_mul(drm_sm2fixp(matrix->matrix[10]), 
drm_int2fixp(pixel->b)) +
+drm_sm2fixp(matrix->matrix[11]);
+
+   pixel->r = drm_fixp2int(rf);
+   pixel->g = drm_fixp2int(gf);
+   pixel->b = drm_fixp2int(bf);
+}
+
 static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop 
*colorop)
 {
/* TODO is this right? */
@@ -185,6 +209,9 @@ static void apply_colorop(struct pixel_argb_s32 *pixel, 
struct drm_colorop *colo
DRM_DEBUG_DRIVER("unkown colorop 1D curve type 
%d\n", 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);
}
 
 }
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index 4aee29e161d6..8710e550790c 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -224,6 +224,8 @@ int drm_colorop_init(struct drm_device *dev, struct 
drm_colorop *colorop,
 
 int drm_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop 
*colorop,
  struct drm_plane *plane, u64 supported_tfs);
+int drm_colorop_ctm_3x4_init(struct drm_device *dev, struct drm_colorop 
*colorop,
+struct drm_plane *plane);
 
 struct drm_colorop_state *
 drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop);
-- 
2.44.0



[RFC PATCH v4 28/42] drm/amd/display: Ignore deprecated props when plane_color_pipeline set

2024-02-26 Thread 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: Harry Wentland 
---
 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index f6575d7dee97..47c6fd33fd60 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -4948,6 +4948,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.44.0



[RFC PATCH v4 09/42] drm/colorop: Introduce new drm_colorop mode object

2024-02-26 Thread 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.

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)

Signed-off-by: Harry Wentland 
---
 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   | 152 ++
 drivers/gpu/drm/drm_mode_config.c   |   7 ++
 include/drm/drm_atomic.h|  82 +++
 include/drm/drm_atomic_uapi.h   |   1 +
 include/drm/drm_colorop.h   | 158 
 include/drm/drm_mode_config.h   |  18 
 include/drm/drm_plane.h |   2 +
 include/uapi/drm/drm_mode.h |   1 +
 12 files changed, 552 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 104b42df2e95..4b14dcbb6117 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -16,6 +16,7 @@ drm-y := \
drm_client.o \
drm_client_modeset.o \
drm_color_mgmt.o \
+   drm_colorop.o \
drm_connector.o \
drm_crtc.o \
drm_displayid.o \
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index a91737adf8e7..62e87e6a9653 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"
@@ -108,6 +109,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);
@@ -139,6 +141,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
@@ -250,6 +256,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;
 
@@ -571,6 +591,56 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
 }
 EXPORT_SYMBOL(drm_atomic_get_plane_state);
 
+
+/**
+ * drm_atomic_get_colorop_state - get colorop state
+ * @state: global atomic state object
+ * @colorop: colorop to get state object for
+ *
+ * This function returns the colorop state for the given colorop, allocating it
+ * if needed. It will also grab the relevant plane lock to make sure that the
+ * state is consistent.
+ *
+ * Returns:
+ *
+ * Either the allocated state or the error code encoded into the pointer. When
+ * the error is EDEADLK then the w/w mutex code has detected a deadlock and the
+ * entire atomic sequence must be restarted. All other errors are fatal.
+ */
+struct drm_colorop_state *
+drm_atomic_get_colorop_state(struct drm_atomic_state *state,
+struct drm_colorop *colorop)
+{
+   int ret, index = drm_colorop_index(colorop);
+   struct drm_colorop_state *colorop_state;
+
+   WARN_ON(!state->acquire_ctx);
+
+   colorop_state = drm_atomic_get_existing_colorop_state(state, colorop);
+   if (colorop_state)
+   return colorop_state;
+
+   ret = drm_modeset_lock(&colorop->plane->mutex, state->acquire_ctx);
+ 

[RFC PATCH v4 11/42] drm/colorop: Add 1D Curve subtype

2024-02-26 Thread Harry Wentland
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

Signed-off-by: Harry Wentland 
Signed-off-by: Alex Hung 
Co-developed-by: Alex Hung 
---
 drivers/gpu/drm/drm_atomic_uapi.c | 18 +---
 drivers/gpu/drm/drm_colorop.c | 71 +++
 include/drm/drm_colorop.h | 24 +++
 3 files changed, 108 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_uapi.c 
b/drivers/gpu/drm/drm_atomic_uapi.c
index e3067c095c72..fdd540cfe24f 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -648,11 +648,17 @@ static int drm_atomic_colorop_set_property(struct 
drm_colorop *colorop,
struct drm_colorop_state *state, struct drm_file *file_priv,
struct drm_property *property, uint64_t val)
 {
-   drm_dbg_atomic(colorop->dev,
-   "[COLOROP:%d] unknown property [PROP:%d:%s]]\n",
-   colorop->base.id,
-   property->base.id, property->name);
-   return -EINVAL;
+   if (property == colorop->curve_1d_type_property) {
+   state->curve_1d_type = val;
+   } else {
+   drm_dbg_atomic(colorop->dev,
+  "[COLOROP:%d:%d] unknown property 
[PROP:%d:%s]]\n",
+  colorop->base.id, colorop->type,
+  property->base.id, property->name);
+   return -EINVAL;
+   }
+
+   return 0;
 }
 
 static int
@@ -662,6 +668,8 @@ drm_atomic_colorop_get_property(struct drm_colorop *colorop,
 {
if (property == colorop->type_property) {
*val = colorop->type;
+   } else if (property == colorop->curve_1d_type_property) {
+   *val = state->curve_1d_type;
} else {
return -EINVAL;
}
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 3a07a8fe2842..f4740b6115d1 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -36,6 +36,12 @@ 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 */
 
 int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop,
@@ -78,6 +84,56 @@ int drm_colorop_init(struct drm_device *dev, struct 
drm_colorop *colorop,
 }
 EXPORT_SYMBOL(drm_colorop_init);
 
+int drm_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop 
*colorop,
+ struct drm_plane *plane, u64 supported_tfs)
+{
+   struct drm_prop_enum_list enum_list[DRM_COLOROP_1D_CURVE_COUNT];
+   int i, len;
+
+   struct drm_property *prop;
+   int ret;
+
+   if (!supported_tfs) {
+   drm_err(dev,
+   "No supported TFs for new 1D curve colorop on 
[PLANE:%d:%s]\n",
+   plane->base.id, plane->name);
+   return -EINVAL;
+   }
+
+   if ((supported_tfs & -BIT(DRM_COLOROP_1D_CURVE_COUNT)) != 0) {
+   drm_err(dev, "Unknown TF provided on [PLANE:%d:%s]\n",
+   plane->base.id, plane->name);
+   return -EINVAL;
+   }
+
+   ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_1D_CURVE);
+   if (ret)
+   return ret;
+
+   len = 0;
+   for (i = 0; i < DRM_COLOROP_1D_CURVE_COUNT; i++) {
+   if ((supported_tfs & BIT(i)) == 0)
+   continue;
+
+   enum_list[len].type = i;
+   enum_list[len].name = colorop_curve_1d_type_names[i];
+   len++;
+   }
+
+   /* 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;
+
+   colorop->curve_1d_type_property = prop;
+   drm_object_attach_property(&colorop->base, 
colorop->curve_1d_type_property, 0);
+   drm_colorop_reset(colorop);
+
+   return 0;
+}
+EXPORT_SYMBOL(drm_colorop_curve_1d_init);
+
 static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop 
*colorop,
struct 
drm_colorop_state *state)
 {
@@ -192,3 +248,18 @@ const char *drm_get_colorop_type_name(enum 
drm_colorop_type type)
 
return colorop_type_name[type];
 }
+
+/**
+ * drm_get_colorop_curve_1d_type_name - return a string for 1D curve type
+ * @ra

[RFC PATCH v4 14/42] drm/colorop: Add atomic state print for drm_colorop

2024-02-26 Thread Harry Wentland
Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_atomic.c | 25 -
 include/drm/drm_colorop.h|  5 +
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index b400e32c9d39..3645c36d63b3 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -783,6 +783,19 @@ static int drm_atomic_plane_check(const struct 
drm_plane_state *old_plane_state,
return 0;
 }
 
+
+
+static void drm_atomic_colorop_print_state(struct drm_printer *p,
+   const struct drm_colorop_state *state)
+{
+   struct drm_colorop *colorop = state->colorop;
+
+   drm_printf(p, "colorop[%u]:\n", colorop->base.id);
+   drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type));
+   drm_printf(p, "\tbypass=%u\n", state->bypass);
+   drm_printf(p, "\tcurve_1d_type=%s\n", 
drm_get_colorop_curve_1d_type_name(state->curve_1d_type));
+}
+
 static void drm_atomic_plane_print_state(struct drm_printer *p,
const struct drm_plane_state *state)
 {
@@ -804,7 +817,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);
 }
@@ -1840,6 +1854,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;
@@ -1849,6 +1864,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);
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index 8060988b5892..fc9a28d138b8 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -231,6 +231,11 @@ 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)
 
+const char *drm_get_color_pipeline_name(struct drm_colorop *colorop);
+
+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);
 
 
-- 
2.44.0



[RFC PATCH v4 10/42] drm/colorop: Add TYPE property

2024-02-26 Thread 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.

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

For now we're only introducing an enumerated 1D LUT type to
illustrate the concept.

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_atomic.c  |  4 +--
 drivers/gpu/drm/drm_atomic_uapi.c |  8 +-
 drivers/gpu/drm/drm_colorop.c | 44 ++-
 include/drm/drm_colorop.h | 17 +++-
 include/uapi/drm/drm_mode.h   |  4 +++
 5 files changed, 72 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 62e87e6a9653..b400e32c9d39 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -634,8 +634,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 1f9b6dfa8ca7..e3067c095c72 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -660,7 +660,13 @@ drm_atomic_colorop_get_property(struct drm_colorop 
*colorop,
const struct drm_colorop_state *state,
struct drm_property *property, uint64_t *val)
 {
-   return -EINVAL;
+   if (property == colorop->type_property) {
+   *val = colorop->type;
+   } else {
+   return -EINVAL;
+   }
+
+   return 0;
 }
 
 static int drm_atomic_set_writeback_fb_for_connector(
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index a295ab96aee1..3a07a8fe2842 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -32,12 +32,17 @@
 
 /* TODO big colorop doc, including properties, etc. */
 
+static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = {
+   { DRM_COLOROP_1D_CURVE, "1D Curve" },
+};
+
 /* Init Helpers */
 
 int drm_colorop_init(struct drm_device *dev, struct drm_colorop *colorop,
-struct drm_plane *plane)
+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);
@@ -46,12 +51,29 @@ int drm_colorop_init(struct drm_device *dev, struct 
drm_colorop *colorop,
 
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;
 }
 EXPORT_SYMBOL(drm_colorop_init);
@@ -150,3 +172,23 @@ void drm_colorop_reset(struct drm_colorop *colorop)
__drm_colorop_reset(colorop, colorop->state);
 }
 EXPORT_SYMBOL(drm_colorop_reset);
+
+
+static const char * const colorop_type_name[] = {
+   [DRM_COLOROP_1D_CURVE] = "1D Curve",
+};
+
+/**
+ * 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)
+{
+   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 e611f830f986..cb98c55f8387 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -103,6 +103,21 @@ struct drm_colorop {
 

[RFC PATCH v4 24/42] drm/tests: Add a few tests around drm_fixed.h

2024-02-26 Thread 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.

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/tests/Makefile|  3 +-
 drivers/gpu/drm/tests/drm_fixp_test.c | 69 +++
 2 files changed, 71 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 d6183b3d7688..98468f7908dd 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -19,6 +19,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 ..f420f173ff66
--- /dev/null
+++ b/drivers/gpu/drm/tests/drm_fixp_test.c
@@ -0,0 +1,69 @@
+// 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, ((1LL << 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");
\ No newline at end of file
-- 
2.44.0



[RFC PATCH v4 25/42] drm/vkms: Add tests for CTM handling

2024-02-26 Thread 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.

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)
 - Rename red, green, blue to Y, U, V where applicable

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/vkms/tests/vkms_color_tests.c | 251 ++
 drivers/gpu/drm/vkms/vkms_composer.c  |   2 +-
 2 files changed, 252 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_tests.c 
b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
index e6ac01dee830..83d07f7bae37 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_tests.c
@@ -3,6 +3,7 @@
 #include 
 
 #include 
+#include 
 
 #define TEST_LUT_SIZE 16
 
@@ -181,11 +182,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)
+
+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 = 0x7fff;
+   ref.g = 0x3fff;
+   ref.b = 0x3fff;
+
+   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.
+ */
+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);
+   KUNIT_EXPECT_LT(test, ou

[RFC PATCH v4 15/42] drm/plane: Add COLOR PIPELINE property

2024-02-26 Thread 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.

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)

Signed-off-by: Harry Wentland 
---
 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   | 52 +++
 include/drm/drm_atomic.h  |  3 ++
 include/drm/drm_atomic_uapi.h |  2 +
 include/drm/drm_plane.h   | 11 +
 7 files changed, 161 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 3645c36d63b3..27a8805c5fa1 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1458,6 +1458,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 87f00131be11..86f77a9aa3a8 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -256,6 +256,36 @@ drm_atomic_set_fb_for_plane(struct drm_plane_state 
*plane_state,
 }
 EXPORT_SYMBOL(drm_atomic_set_fb_for_plane);
 
+
+/**
+ * drm_atomic_set_colorop_for_plane - set colorop for plane
+ * @plane_state: atomic state object for the plane
+ * @colorop: colorop to use for the plane
+ *
+ * Helper function to select the color pipeline on a plane by setting
+ * it to the first drm_colorop element of the pipeline.
+ */
+void
+drm_atomic_set_colorop_for_plane(struct drm_plane_state *plane_state,
+struct drm_colorop *colorop)
+{
+   struct drm

[RFC PATCH v4 35/42] drm/amd/display: Enable support for PQ 125 EOTF and Inverse

2024-02-26 Thread 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: Harry Wentland 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 20 +--
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c |  8 +---
 2 files changed, 19 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 3e3ae2b58b06..20b7eb47388c 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"
@@ -676,6 +677,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;;
}
@@ -1158,8 +1162,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) {
@@ -1191,7 +1197,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;
}
@@ -1213,8 +1219,10 @@ __set_colorop_in_shaper_1d_curve(struct dc_plane_state 
*dc_plane_state,
const struct drm_color_lut *shaper_lut;
uint32_t shaper_size;
 
-   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) {
@@ -1250,7 +1258,7 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state 
*plane_state,
/* 2nd op: 1d curve - shaper */
for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
if (new_colorop_state->colorop == old_colorop &&
-   new_colorop_state->curve_1d_type == 
DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) {
+   (BIT(new_colorop_state->curve_1d_type) & 
amdgpu_dm_supported_shaper_tfs)) {
colorop_state = new_colorop_state;
break;
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
index 449a2ad6a184..f99d8e09d89b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
@@ -32,10 +32,12 @@
 #include "amdgpu_dm_colorop.h"
 
 const u64 amdgpu_dm_supported_degam_tfs =
-   BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF);
+   BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
+   BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF);
 
- const u64 amdgpu_dm_supported_shaper_tfs =
-   BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF);
+const u64 amdgpu_dm_supported_shaper_tfs =
+   BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) |
+   BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF);
 
 const u64 amdgpu_dm_supported_blnd_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF);
-- 
2.44.0



[RFC PATCH v4 21/42] drm/vkms: Pull apply_colorop out of pre_blend_color_transform

2024-02-26 Thread Harry Wentland
The if/switch statement is bound to grow with more types and
subtypes. Pull this out into its own funcion to make things more
manageable and readable.

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/vkms/vkms_composer.c | 48 
 1 file changed, 28 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/vkms/vkms_composer.c 
b/drivers/gpu/drm/vkms/vkms_composer.c
index 9493bdb1ba3f..25b786b49db0 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -164,6 +164,31 @@ 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)
+{
+   /* TODO is this right? */
+   struct drm_colorop_state *colorop_state = colorop->state;
+
+   if (colorop->type == DRM_COLOROP_1D_CURVE) {
+   switch (colorop_state->curve_1d_type) {
+   case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF:
+   pixel->r = 
apply_lut_to_channel_value(&srgb_inv_eotf, pixel->r, LUT_RED);
+   pixel->g = 
apply_lut_to_channel_value(&srgb_inv_eotf, pixel->g, LUT_GREEN);
+   pixel->b = 
apply_lut_to_channel_value(&srgb_inv_eotf, pixel->b, LUT_BLUE);
+   break;
+   case DRM_COLOROP_1D_CURVE_SRGB_EOTF:
+   pixel->r = 
apply_lut_to_channel_value(&srgb_eotf, pixel->r, LUT_RED);
+   pixel->g = 
apply_lut_to_channel_value(&srgb_eotf, pixel->g, LUT_GREEN);
+   pixel->b = 
apply_lut_to_channel_value(&srgb_eotf, pixel->b, LUT_BLUE);
+   break;
+   default:
+   DRM_DEBUG_DRIVER("unkown colorop 1D curve type 
%d\n", colorop_state->curve_1d_type);
+   break;
+   }
+   }
+
+}
+
 static void pre_blend_color_transform(const struct vkms_plane_state 
*plane_state, struct line_buffer *output_buffer)
 {
struct drm_colorop *colorop = plane_state->base.base.color_pipeline;
@@ -180,26 +205,9 @@ static void pre_blend_color_transform(const struct 
vkms_plane_state *plane_state
if (!colorop_state)
return;
 
-   for (size_t x = 0; x < output_buffer->n_pixels; x++) {
-   struct pixel_argb_u16 *pixel = 
&output_buffer->pixels[x];
-
-   if (colorop->type == DRM_COLOROP_1D_CURVE &&
-   colorop_state->bypass == false) {
-   switch (colorop_state->curve_1d_type) {
-   case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF:
-   pixel->r = 
apply_lut_to_channel_value(&srgb_inv_eotf, pixel->r, LUT_RED);
-   pixel->g = 
apply_lut_to_channel_value(&srgb_inv_eotf, pixel->g, LUT_GREEN);
-   pixel->b = 
apply_lut_to_channel_value(&srgb_inv_eotf, pixel->b, LUT_BLUE);
-   break;
-   case DRM_COLOROP_1D_CURVE_SRGB_EOTF:
-   default:
-   pixel->r = 
apply_lut_to_channel_value(&srgb_eotf, pixel->r, LUT_RED);
-   pixel->g = 
apply_lut_to_channel_value(&srgb_eotf, pixel->g, LUT_GREEN);
-   pixel->b = 
apply_lut_to_channel_value(&srgb_eotf, pixel->b, LUT_BLUE);
-   break;
-   }
-   }
-   }
+   for (size_t x = 0; x < output_buffer->n_pixels; x++)
+   if (!colorop_state->bypass)
+   apply_colorop(&output_buffer->pixels[x], 
colorop);
 
colorop = colorop->next;
}
-- 
2.44.0



[RFC PATCH v4 34/42] drm/colorop: Add PQ 125 EOTF and its inverse

2024-02-26 Thread Harry Wentland
The PQ function defines a mapping of code values to nits (cd/m^2).
The max code value maps to 10,000 nits.

Windows DWM's canonical composition color space (CCCS)  defaults
to composing SDR contents to 80 nits and uses a float value of
1.0 to represent this. For this reason AMD HW hard-codes a PQ
function that is scaled by 125, yielding 80 nit PQ values for
1.0 and 10,000 nits at 125.0.

This patch introduces this scaled PQ EOTF and its inverse as
1D curve types.

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_colorop.c | 2 ++
 include/drm/drm_colorop.h | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 7baa1eba..781947e42b02 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -40,6 +40,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 8710e550790c..e06d9ea28efd 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -33,6 +33,8 @@
 enum drm_colorop_curve_1d_type {
DRM_COLOROP_1D_CURVE_SRGB_EOTF,
DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF,
+   DRM_COLOROP_1D_CURVE_PQ_125_EOTF,
+   DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF,
DRM_COLOROP_1D_CURVE_COUNT
 };
 
-- 
2.44.0



[RFC PATCH v4 19/42] drm/colorop: Introduce DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE

2024-02-26 Thread Harry Wentland
With the introduction of the pre-blending color pipeline we
can no longer have color operations that don't have a clear
position in the color pipeline. We deprecate all existing
plane properties. For upstream drivers those are:
 - COLOR_ENCODING
 - COLOR_RANGE

Drivers are expected to ignore these properties when
programming the HW.

Setting of the COLOR_PIPELINE plane property or drm_colorop
properties is only allowed for userspace that sets this
client cap.

v4:
 - Don't block setting of COLOR_RANGE and COLOR_ENCODING
   when client cap is set

Signed-off-by: Harry Wentland 
---
 drivers/gpu/drm/drm_atomic_uapi.c | 13 -
 drivers/gpu/drm/drm_ioctl.c   |  7 +++
 include/drm/drm_file.h|  7 +++
 include/uapi/drm/drm.h| 16 
 4 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_atomic_uapi.c 
b/drivers/gpu/drm/drm_atomic_uapi.c
index 86f77a9aa3a8..ff258b34544e 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -570,6 +570,12 @@ static int drm_atomic_plane_set_property(struct drm_plane 
*plane,
} else if (property == plane->color_range_property) {
state->color_range = val;
} else if (property == plane->color_pipeline_property) {
+   if (!file_priv->plane_color_pipeline) {
+   drm_dbg_atomic(dev,
+  "Setting COLOR_PIPELINE plane property 
not permitted unless DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set\n");
+   return -EINVAL;
+   }
+
/* find DRM colorop object */
struct drm_colorop *colorop = NULL;
colorop = drm_colorop_find(dev, file_priv, val);
@@ -1179,6 +1185,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;
 
@@ -1191,7 +1203,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 e368fc084c77..da59e37ae228 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 ab230d3af138..63c1d29b8520 100644
--- a/include/drm/drm_file.h
+++ b/include/drm/drm_file.h
@@ -204,6 +204,13 @@ struct drm_file {
 */
bool writeback_connectors;
 
+   /**
+* @plane_color_pipeline:
+*
+* True if client understands plane color pipelines
+*/
+   bool plane_color_pipeline;
+
/**
 * @was_master:
 *
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 16122819edfe..2d74c49274ee 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -875,6 +875,22 @@ struct drm_get_cap {
  */
 #define DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT6
 
+/**
+ * DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE
+ *
+ * If set to 1
+ * - the DRM core will allow setting of plane the COLOR_PIPELINE
+ *   property, as well as drm_colorop properties.
+ * - Drivers will ignore these properties
+ *   - COLOR_ENCODING
+ *   - COLOR_RANGE
+ *
+ * The client must enable &DRM_CLIENT_CAP_ATOMIC first.
+ *
+ * This capability is currently in development.
+ */
+#define DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE7
+
 /* DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
 struct drm_set_client_cap {
__u64 capability;
-- 
2.44.0



[RFC PATCH v4 36/42] drm/colorop: add BT2020/BT709 OETF and Inverse OETF

2024-02-26 Thread 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: Harry Wentland 
---
 drivers/gpu/drm/drm_colorop.c | 2 ++
 include/drm/drm_colorop.h | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 781947e42b02..b10cad5a7208 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -40,6 +40,8 @@ static const struct drm_prop_enum_list 
drm_colorop_type_enum_list[] = {
 static const char * const colorop_curve_1d_type_names[] = {
[DRM_COLOROP_1D_CURVE_SRGB_EOTF] = "sRGB EOTF",
[DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF] = "sRGB Inverse EOTF",
+   [DRM_COLOROP_1D_CURVE_BT2020_INV_OETF] = "BT.2020 Inverse OETF",
+   [DRM_COLOROP_1D_CURVE_BT2020_OETF] = "BT.2020 OETF",
[DRM_COLOROP_1D_CURVE_PQ_125_EOTF] = "PQ 125 EOTF",
[DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF] = "PQ 125 Inverse EOTF",
 };
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index e06d9ea28efd..28b3136dabad 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -33,6 +33,8 @@
 enum drm_colorop_curve_1d_type {
DRM_COLOROP_1D_CURVE_SRGB_EOTF,
DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF,
+   DRM_COLOROP_1D_CURVE_BT2020_INV_OETF,
+   DRM_COLOROP_1D_CURVE_BT2020_OETF,
DRM_COLOROP_1D_CURVE_PQ_125_EOTF,
DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF,
DRM_COLOROP_1D_CURVE_COUNT
-- 
2.44.0



[RFC PATCH v4 41/42] drm/colorop: Add mutliplier type

2024-02-26 Thread Harry Wentland
From: 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.

Signed-off-by: Alex Hung 
---
 drivers/gpu/drm/drm_atomic.c  |  3 +++
 drivers/gpu/drm/drm_atomic_uapi.c |  4 
 drivers/gpu/drm/drm_colorop.c | 29 +++--
 include/drm/drm_colorop.h | 16 
 include/uapi/drm/drm_mode.h   |  1 +
 5 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index f7d51839ca03..af0b6338a55c 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -806,6 +806,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=%u\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 6bfe857720cd..b4ecda563728 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -727,6 +727,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);
@@ -752,6 +754,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->size_property) {
*val = state->size;
} else if (property == colorop->data_property) {
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 4452eaeeb242..c6cdd743de51 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -35,7 +35,8 @@
 static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = {
{ DRM_COLOROP_1D_CURVE, "1D Curve" },
{ DRM_COLOROP_1D_LUT, "1D Curve Custom LUT" },
-   { DRM_COLOROP_CTM_3X4, "3x4 Matrix"}
+   { DRM_COLOROP_CTM_3X4, "3x4 Matrix"},
+   { DRM_COLOROP_MULTIPLIER, "Multiplier"},
 };
 
 static const char * const colorop_curve_1d_type_names[] = {
@@ -231,6 +232,29 @@ int drm_colorop_ctm_3x4_init(struct drm_device *dev, 
struct drm_colorop *colorop
 }
 EXPORT_SYMBOL(drm_colorop_ctm_3x4_init);
 
+int drm_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop,
+ struct drm_plane *plane)
+{
+   struct drm_property *prop;
+   int ret;
+
+   ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_MULTIPLIER);
+   if (ret)
+   return ret;
+
+   prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, 
"MULTIPLIER", 0, U64_MAX);
+   if (!prop)
+   return -ENOMEM;
+
+   colorop->multiplier_property = prop;
+   drm_object_attach_property(&colorop->base, 
colorop->multiplier_property, 0);
+
+   drm_colorop_reset(colorop);
+
+   return 0;
+}
+EXPORT_SYMBOL(drm_colorop_mult_init);
+
 static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop 
*colorop,
struct 
drm_colorop_state *state)
 {
@@ -333,7 +357,8 @@ EXPORT_SYMBOL(drm_colorop_reset);
 static const char * const colorop_type_name[] = {
[DRM_COLOROP_1D_CURVE] = "1D Curve",
[DRM_COLOROP_1D_LUT] = "1D Curve Custom LUT",
-   [DRM_COLOROP_CTM_3X4] = "3x4 Matrix"
+   [DRM_COLOROP_CTM_3X4] = "3x4 Matrix",
+   [DRM_COLOROP_MULTIPLIER] = "Multiplier",
 };
 
 /**
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index 8adc7ece3bd1..f9f83644cc9f 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -64,6 +64,13 @@ struct drm_colorop_state {
 */
enum drm_colorop_curve_1d_type curve_1d_type;
 
+   /**
+* @multiplier:
+*
+* Multiplier to 'gain' the plane. Format is S31.32 sign-magnitude.
+*/
+   uint64_t multiplier;
+
/**
 * @size:
 *
@@ -186,6 +193,13 @@ struct drm_colorop {
 */
struct drm_property *curve_1d_type_property;
 
+   /**
+* @multiplier_property:
+  

[RFC PATCH v4 39/42] drm/amd/display: add shaper and blend colorops for 1D Curve Custom LUT

2024-02-26 Thread Harry Wentland
From: 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 
---
 .../amd/display/amdgpu_dm/amdgpu_dm_color.c   | 170 +-
 .../amd/display/amdgpu_dm/amdgpu_dm_colorop.c |  30 
 2 files changed, 119 insertions(+), 81 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 d5d356cf9fc6..e7b51b29cc04 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
@@ -1212,40 +1212,6 @@ __set_dm_plane_colorop_degamma(struct drm_plane_state 
*plane_state,
return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state);
 }
 
-static int
-__set_colorop_in_shaper_1d_curve(struct dc_plane_state *dc_plane_state,
-  struct drm_colorop_state *colorop_state)
-{
-   struct dc_transfer_func *tf = dc_plane_state->in_shaper_func;
-   struct drm_colorop *colorop = colorop_state->colorop;
-   struct drm_device *drm = colorop->dev;
-   const struct drm_color_lut *shaper_lut;
-   uint32_t shaper_size;
-
-   if (colorop->type != DRM_COLOROP_1D_CURVE)
-   return -EINVAL;
-
-   if (!(BIT(colorop_state->curve_1d_type) & 
amdgpu_dm_supported_shaper_tfs))
-   return -EINVAL;
-
-   if (colorop_state->bypass) {
-   tf->type = TF_TYPE_BYPASS;
-   tf->tf = TRANSFER_FUNCTION_LINEAR;
-   return 0;
-   }
-
-   drm_dbg(drm, "Shaper colorop with ID: %d\n", colorop->base.id);
-
-   if (colorop->type == DRM_COLOROP_1D_CURVE) {
-   tf->type = TF_TYPE_DISTRIBUTED_POINTS;
-   tf->tf = 
amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
-   tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
-   return __set_output_tf(tf, shaper_lut, shaper_size, false);
-   }
-
-   return -EINVAL;
-}
-
 static int
 __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state,
  struct dc_plane_state *dc_plane_state,
@@ -1254,64 +1220,61 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state 
*plane_state,
struct drm_colorop *old_colorop;
struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
struct drm_atomic_state *state = plane_state->state;
+   enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR;
+   struct dc_transfer_func *tf = dc_plane_state->in_shaper_func;
+   const struct drm_color_lut *shaper_lut;
+   struct drm_device *dev = colorop->dev;
+   uint32_t shaper_size;
int i = 0;
 
+   /* 1D Curve - SHAPER TF */
old_colorop = colorop;
-
-   /* 2nd op: 1d curve - shaper */
for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
if (new_colorop_state->colorop == old_colorop &&
(BIT(new_colorop_state->curve_1d_type) & 
amdgpu_dm_supported_shaper_tfs)) {
colorop_state = new_colorop_state;
break;
}
+   }
 
-   if (new_colorop_state->colorop == old_colorop) {
+   if (colorop_state && !colorop_state->bypass && colorop->type == 
DRM_COLOROP_1D_CURVE) {
+   drm_dbg(dev, "Shaper TF colorop with ID: %d\n", 
colorop->base.id);
+   tf->type = TF_TYPE_DISTRIBUTED_POINTS;
+   tf->tf = default_tf = 
amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
+   tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
+   __set_output_tf(tf, shaper_lut, shaper_size, false);
+   }
+
+   /* 1D LUT - SHAPER LUT */
+   colorop = old_colorop->next;
+   if (!colorop) {
+   drm_dbg(dev, "no Shaper LUT colorop found\n");
+   return -EINVAL;
+   }
+
+   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_1D_LUT) {
colorop_state = new_colorop_state;
break;
}
}
 
-   if (!colorop_state)
-   return -EINVAL;
-
-   re

  1   2   3   4   5   6   7   8   9   10   >