aboutsummaryrefslogtreecommitdiff
path: root/SOURCES/0001-HDR.patch
diff options
context:
space:
mode:
authorJan200101 <sentrycraft123@gmail.com>2023-11-28 09:25:22 +0100
committerJan200101 <sentrycraft123@gmail.com>2023-11-28 09:25:22 +0100
commit6229f58d19ef9e8c060cc9d9974ef6fcf1bcb528 (patch)
tree5d375a5d3b74c7b8320cd0bde47d2947a3446a9b /SOURCES/0001-HDR.patch
parent6d1c932b1ab6892c861ea92d93914a175e187b08 (diff)
downloadkernel-fsync-6229f58d19ef9e8c060cc9d9974ef6fcf1bcb528.tar.gz
kernel-fsync-6229f58d19ef9e8c060cc9d9974ef6fcf1bcb528.zip
kernel 6.6.2
Diffstat (limited to 'SOURCES/0001-HDR.patch')
-rw-r--r--SOURCES/0001-HDR.patch2310
1 files changed, 0 insertions, 2310 deletions
diff --git a/SOURCES/0001-HDR.patch b/SOURCES/0001-HDR.patch
deleted file mode 100644
index 6df422b..0000000
--- a/SOURCES/0001-HDR.patch
+++ /dev/null
@@ -1,2310 +0,0 @@
-From 03248cc1991679d1025ea5bdf30ee324cdebf622 Mon Sep 17 00:00:00 2001
-From: Peter Jung <admin@ptr1337.dev>
-Date: Wed, 23 Aug 2023 18:54:42 +0200
-Subject: [PATCH] AMD-HDR
-
-Hi all,
-
-Here is the next version of our work to enable AMD driver-specific color
-management properties [1][2]. This series is a collection of
-contributions from Joshua, Harry, and me to enhance the AMD KMS color
-pipeline for Steam Deck/SteamOS by exposing additional pre-blending and
-post-blending color capabilities from those available in the current DRM
-KMS API[3].
-
-The userspace case here is Gamescope which is the compositor for
-SteamOS. Gamescope is already using these features to implement its
-color management pipeline [4].
-
-In this version, I try to address all concerns shared in the previous
-one, i.e.:
-- Replace DRM_ by AMDGPU_ prefix for transfer function enumeration;
-- Explicitly define EOTFs and inverse EOTFs and set props accordingly;
-- Document pre-defined transfer functions;
-- Remove misleading comments;
-- Remove post-blending/MPC shaper and 3D LUT support;
-- Move driver-specific property operations from amdgpu_display.c to
- amdgpu_dm_color.c;
-- Reset planes if any color props change;
-- Nits/small fixes;
-
-Bearing in mind the complexity of color concepts, I believe there is a
-high chance of some misunderstanding from my side when defining EOTFs
-and documenting pre-defined TFs. So, reviews are very important and
-welcome (thanks in advance). FWIW, I added Harry as a co-developer of
-this TF documentation since I based on his description of EOTF/inv_EOTF
-and previous documentation work [5]. Let me know if there is a better
-way for credits.
-
-Two DC patches were already applied and, therefore, removed from the
-series. I added r-b according to previous feedback. We also add plane
-CTM to driver-specific properties. As a result, this is the updated list
-of all driver-specific color properties exposed by this series:
-
-- plane degamma LUT and pre-defined TF;
-- plane HDR multiplier;
-- plane CTM 3x4;
-- plane shaper LUT and pre-defined TF;
-- plane 3D LUT;
-- plane blend LUT and pre-defined TF;
-- CRTC gamma pre-defined TF;
-
-Remember you can find the AMD HW color capabilities documented here:
-https://dri.freedesktop.org/docs/drm/gpu/amdgpu/display/display-manager.html#color-management-properties
-
-Worth mentioning that the pre-blending degamma block can use ROM curves
-for some pre-defined TFs, but the other blocks use the AMD color module
-to calculate this curve considering pre-defined coefficients.
-
-We need changes on DC gamut remap matrix to support the plane and CRTC
-CTM on drivers that support both. I've sent a previous patch to apply
-these changes to all DCN3+ families [6]. Here I use the same changes but
-limited to DCN301. Just let me know if you prefer the previous/expanded
-version.
-
-Finally, this is the Linux/AMD color management API before and after
-blending with the driver-specific properties:
-
-+----------------------+
-| PLANE |
-| |
-| +----------------+ |
-| | AMD Degamma | |
-| | | |
-| | EOTF | 1D LUT | |
-| +--------+-------+ |
-| | |
-| +--------v-------+ |
-| | AMD HDR | |
-| | Multiply | |
-| +--------+-------+ |
-| | |
-| +--------v-------+ |
-| | AMD CTM (3x4) | |
-| +--------+-------+ |
-| | |
-| +--------v-------+ |
-| | AMD Shaper | |
-| | | |
-| | inv_EOTF | | |
-| | Custom 1D LUT | |
-| +--------+-------+ |
-| | |
-| +--------v-------+ |
-| | AMD 3D LUT | |
-| | 17^3/12-bit | |
-| +--------+-------+ |
-| | |
-| +--------v-------+ |
-| | AMD Blend | |
-| | | |
-| | EOTF | 1D LUT | |
-| +--------+-------+ |
-| | |
-++----------v---------++
-|| Blending ||
-++----------+---------++
-| CRTC | |
-| | |
-| +-------v-------+ |
-| | DRM Degamma | |
-| | | |
-| | Custom 1D LUT | |
-| +-------+-------+ |
-| | |
-| +-------v-------+ |
-| | DRM CTM (3x3) | |
-| +-------+-------+ |
-| | |
-| +-------v-------+ |
-| | DRM Gamma | |
-| | | |
-| | Custom 1D LUT | |
-| +---------------+ |
-| | *AMD Gamma | |
-| | inv_EOTF | |
-| +---------------+ |
-| |
-+----------------------+
-
-Let me know your thoughts.
-
-Best Regards,
-
-Melissa Wen
-
-[1] https://lore.kernel.org/dri-devel/20230423141051.702990-1-mwen@igalia.com
-[2] https://lore.kernel.org/dri-devel/20230523221520.3115570-1-mwen@igalia.com
-[3] https://github.com/ValveSoftware/gamescope/blob/master/src/docs/Steam%20Deck%20Display%20Pipeline.png
-[4] https://github.com/ValveSoftware/gamescope
-[5] https://lore.kernel.org/dri-devel/20210730204134.21769-1-harry.wentland@amd.com
-[6] https://lore.kernel.org/dri-devel/20230721132431.692158-1-mwen@igalia.com
-
-Harry Wentland (1):
- drm/amd/display: fix segment distribution for linear LUTs
-
-Joshua Ashton (14):
- drm/amd/display: add plane degamma TF driver-specific property
- drm/amd/display: add plane HDR multiplier driver-specific property
- drm/amd/display: add plane blend LUT and TF driver-specific properties
- drm/amd/display: add CRTC gamma TF support
- drm/amd/display: set sdr_ref_white_level to 80 for out_transfer_func
- drm/amd/display: mark plane as needing reset if color props change
- drm/amd/display: add plane degamma TF and LUT support
- drm/amd/display: add dc_fixpt_from_s3132 helper
- drm/amd/display: add HDR multiplier support
- drm/amd/display: handle empty LUTs in __set_input_tf
- drm/amd/display: add plane blend LUT and TF support
- drm/amd/display: allow newer DC hardware to use degamma ROM for PQ/HLG
- drm/amd/display: copy 3D LUT settings from crtc state to stream_update
- drm/amd/display: Use 3x4 CTM for plane CTM
-
-Melissa Wen (19):
- drm/drm_mode_object: increase max objects to accommodate new color
- props
- drm/drm_property: make replace_property_blob_from_id a DRM helper
- drm/drm_plane: track color mgmt changes per plane
- drm/amd/display: add driver-specific property for plane degamma LUT
- drm/amd/display: explicitly define EOTF and inverse EOTF
- drm/amd/display: document AMDGPU pre-defined transfer functions
- drm/amd/display: add plane 3D LUT driver-specific properties
- drm/amd/display: add plane shaper LUT and TF driver-specific
- properties
- drm/amd/display: add CRTC gamma TF driver-specific property
- drm/amd/display: add comments to describe DM crtc color mgmt behavior
- drm/amd/display: encapsulate atomic regamma operation
- drm/amd/display: decouple steps for mapping CRTC degamma to DC plane
- drm/amd/display: reject atomic commit if setting both plane and CRTC
- degamma
- drm/amd/display: add plane shaper LUT support
- drm/amd/display: add plane shaper TF support
- drm/amd/display: add plane 3D LUT support
- drm/amd/display: set stream gamut remap matrix to MPC for DCN301
- drm/amd/display: add plane CTM driver-specific property
- drm/amd/display: add plane CTM support
-
-Signed-off-by: Peter Jung <admin@ptr1337.dev>
----
- drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h | 71 ++
- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 34 +-
- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 100 +++
- .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 805 ++++++++++++++++--
- .../amd/display/amdgpu_dm/amdgpu_dm_crtc.c | 72 ++
- .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 224 ++++-
- .../amd/display/dc/dcn10/dcn10_cm_common.c | 95 ++-
- .../drm/amd/display/dc/dcn30/dcn30_hwseq.c | 37 +
- .../drm/amd/display/dc/dcn30/dcn30_hwseq.h | 3 +
- .../drm/amd/display/dc/dcn301/dcn301_init.c | 2 +-
- .../gpu/drm/amd/display/include/fixed31_32.h | 12 +
- drivers/gpu/drm/arm/malidp_crtc.c | 2 +-
- drivers/gpu/drm/drm_atomic.c | 1 +
- drivers/gpu/drm/drm_atomic_state_helper.c | 1 +
- drivers/gpu/drm/drm_atomic_uapi.c | 43 +-
- drivers/gpu/drm/drm_property.c | 49 ++
- include/drm/drm_mode_object.h | 2 +-
- include/drm/drm_plane.h | 7 +
- include/drm/drm_property.h | 6 +
- include/uapi/drm/drm_mode.h | 8 +
- 20 files changed, 1446 insertions(+), 128 deletions(-)
-
-diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
-index 32fe05c810c6fc..84bf501b02f4c2 100644
---- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
-+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
-@@ -343,6 +343,77 @@ struct amdgpu_mode_info {
- int disp_priority;
- const struct amdgpu_display_funcs *funcs;
- const enum drm_plane_type *plane_type;
-+
-+ /* Driver-private color mgmt props */
-+
-+ /* @plane_degamma_lut_property: Plane property to set a degamma LUT to
-+ * convert input space before blending.
-+ */
-+ struct drm_property *plane_degamma_lut_property;
-+ /* @plane_degamma_lut_size_property: Plane property to define the max
-+ * size of degamma LUT as supported by the driver (read-only).
-+ */
-+ struct drm_property *plane_degamma_lut_size_property;
-+ /**
-+ * @plane_degamma_tf_property: Plane pre-defined transfer function to
-+ * to go from scanout/encoded values to linear values.
-+ */
-+ struct drm_property *plane_degamma_tf_property;
-+ /**
-+ * @plane_hdr_mult_property:
-+ */
-+ struct drm_property *plane_hdr_mult_property;
-+
-+ struct drm_property *plane_ctm_property;
-+ /**
-+ * @shaper_lut_property: Plane property to set pre-blending shaper LUT
-+ * that converts color content before 3D LUT.
-+ */
-+ struct drm_property *plane_shaper_lut_property;
-+ /**
-+ * @shaper_lut_size_property: Plane property for the size of
-+ * pre-blending shaper LUT as supported by the driver (read-only).
-+ */
-+ struct drm_property *plane_shaper_lut_size_property;
-+ /**
-+ * @plane_shaper_tf_property: Plane property to set a predefined
-+ * transfer function for pre-blending shaper (before applying 3D LUT)
-+ * with or without LUT.
-+ */
-+ struct drm_property *plane_shaper_tf_property;
-+ /**
-+ * @plane_lut3d_property: Plane property for gamma correction using a
-+ * 3D LUT (pre-blending).
-+ */
-+ struct drm_property *plane_lut3d_property;
-+ /**
-+ * @plane_degamma_lut_size_property: Plane property to define the max
-+ * size of 3D LUT as supported by the driver (read-only).
-+ */
-+ struct drm_property *plane_lut3d_size_property;
-+ /**
-+ * @plane_blend_lut_property: Plane property for output gamma before
-+ * blending. Userspace set a blend LUT to convert colors after 3D LUT
-+ * conversion. It works as a post-3D LUT 1D LUT, with shaper LUT, they
-+ * are sandwiching 3D LUT with two 1D LUT.
-+ */
-+ struct drm_property *plane_blend_lut_property;
-+ /**
-+ * @plane_blend_lut_size_property: Plane property to define the max
-+ * size of blend LUT as supported by the driver (read-only).
-+ */
-+ struct drm_property *plane_blend_lut_size_property;
-+ /**
-+ * @plane_blend_tf_property: Plane property to set a predefined
-+ * transfer function for pre-blending blend (before applying 3D LUT)
-+ * with or without LUT.
-+ */
-+ struct drm_property *plane_blend_tf_property;
-+ /* @regamma_tf_property: Transfer function for CRTC regamma
-+ * (post-blending). Possible values are defined by `enum
-+ * amdgpu_transfer_function`.
-+ */
-+ struct drm_property *regamma_tf_property;
- };
-
- #define AMDGPU_MAX_BL_LEVEL 0xFF
-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 e5554a36e8c8b2..43ef0e5f97ae1a 100644
---- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
-+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
-@@ -3943,6 +3943,11 @@ static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
- return r;
- }
-
-+#ifdef AMD_PRIVATE_COLOR
-+ if (amdgpu_dm_create_color_properties(adev))
-+ return -ENOMEM;
-+#endif
-+
- r = amdgpu_dm_audio_init(adev);
- if (r) {
- dc_release_state(state->context);
-@@ -4992,7 +4997,9 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev,
- * Always set input transfer function, since plane state is refreshed
- * every time.
- */
-- ret = amdgpu_dm_update_plane_color_mgmt(dm_crtc_state, dc_plane_state);
-+ ret = amdgpu_dm_update_plane_color_mgmt(dm_crtc_state,
-+ plane_state,
-+ dc_plane_state);
- if (ret)
- return ret;
-
-@@ -8007,6 +8014,10 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
- bundle->surface_updates[planes_count].gamma = dc_plane->gamma_correction;
- bundle->surface_updates[planes_count].in_transfer_func = dc_plane->in_transfer_func;
- bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix;
-+ bundle->surface_updates[planes_count].hdr_mult = dc_plane->hdr_mult;
-+ bundle->surface_updates[planes_count].func_shaper = dc_plane->in_shaper_func;
-+ bundle->surface_updates[planes_count].lut3d_func = dc_plane->lut3d_func;
-+ bundle->surface_updates[planes_count].blend_tf = dc_plane->blend_tf;
- }
-
- amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state,
-@@ -8215,6 +8226,10 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
- &acrtc_state->stream->csc_color_matrix;
- bundle->stream_update.out_transfer_func =
- acrtc_state->stream->out_transfer_func;
-+ bundle->stream_update.lut3d_func =
-+ (struct dc_3dlut *) acrtc_state->stream->lut3d_func;
-+ bundle->stream_update.func_shaper =
-+ (struct dc_transfer_func *) acrtc_state->stream->func_shaper;
- }
-
- acrtc_state->stream->abm_level = acrtc_state->abm_level;
-@@ -9405,6 +9420,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
- * when a modeset is needed, to ensure it gets reprogrammed.
- */
- if (dm_new_crtc_state->base.color_mgmt_changed ||
-+ dm_old_crtc_state->regamma_tf != dm_new_crtc_state->regamma_tf ||
- drm_atomic_crtc_needs_modeset(new_crtc_state)) {
- ret = amdgpu_dm_update_crtc_color_mgmt(dm_new_crtc_state);
- if (ret)
-@@ -9472,6 +9488,10 @@ static bool should_reset_plane(struct drm_atomic_state *state,
- */
- for_each_oldnew_plane_in_state(state, other, old_other_state, new_other_state, i) {
- struct amdgpu_framebuffer *old_afb, *new_afb;
-+ struct dm_plane_state *dm_new_other_state, *dm_old_other_state;
-+
-+ dm_new_other_state = to_dm_plane_state(new_other_state);
-+ dm_old_other_state = to_dm_plane_state(old_other_state);
-
- if (other->type == DRM_PLANE_TYPE_CURSOR)
- continue;
-@@ -9508,6 +9528,18 @@ static bool should_reset_plane(struct drm_atomic_state *state,
- old_other_state->color_encoding != new_other_state->color_encoding)
- return true;
-
-+ /* HDR/Transfer Function changes. */
-+ if (dm_old_other_state->degamma_tf != dm_new_other_state->degamma_tf ||
-+ dm_old_other_state->degamma_lut != dm_new_other_state->degamma_lut ||
-+ dm_old_other_state->hdr_mult != dm_new_other_state->hdr_mult ||
-+ dm_old_other_state->ctm != dm_new_other_state->ctm ||
-+ dm_old_other_state->shaper_lut != dm_new_other_state->shaper_lut ||
-+ dm_old_other_state->shaper_tf != dm_new_other_state->shaper_tf ||
-+ dm_old_other_state->lut3d != dm_new_other_state->lut3d ||
-+ dm_old_other_state->blend_lut != dm_new_other_state->blend_lut ||
-+ dm_old_other_state->blend_tf != dm_new_other_state->blend_tf)
-+ return true;
-+
- /* Framebuffer checks fall at the end. */
- if (!old_other_state->fb || !new_other_state->fb)
- continue;
-diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
-index 9fb5bb3a75a777..f92bbd7ed867b0 100644
---- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
-+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
-@@ -51,6 +51,8 @@
-
- #define AMDGPU_DMUB_NOTIFICATION_MAX 5
-
-+#define AMDGPU_HDR_MULT_DEFAULT (0x100000000LL)
-+
- /*
- #include "include/amdgpu_dal_power_if.h"
- #include "amdgpu_dm_irq.h"
-@@ -702,9 +704,91 @@ static inline void amdgpu_dm_set_mst_status(uint8_t *status,
-
- extern const struct amdgpu_ip_block_version dm_ip_block;
-
-+enum amdgpu_transfer_function {
-+ AMDGPU_TRANSFER_FUNCTION_DEFAULT,
-+ AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_BT709_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_PQ_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_LINEAR,
-+ AMDGPU_TRANSFER_FUNCTION_UNITY,
-+ AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_BT709_INV_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF,
-+ AMDGPU_TRANSFER_FUNCTION_COUNT
-+};
-+
- struct dm_plane_state {
- struct drm_plane_state base;
- struct dc_plane_state *dc_state;
-+
-+ /* Plane color mgmt */
-+ /**
-+ * @degamma_lut:
-+ *
-+ * 1D LUT for mapping framebuffer/plane pixel data before sampling or
-+ * blending operations. It's usually applied to linearize input space.
-+ * The blob (if not NULL) is an array of &struct drm_color_lut.
-+ */
-+ struct drm_property_blob *degamma_lut;
-+ /**
-+ * @degamma_tf:
-+ *
-+ * Predefined transfer function to tell DC driver the input space to
-+ * linearize.
-+ */
-+ enum amdgpu_transfer_function degamma_tf;
-+ /**
-+ * @hdr_mult:
-+ *
-+ * Multiplier to 'gain' the plane. When PQ is decoded using the fixed
-+ * func transfer function to the internal FP16 fb, 1.0 -> 80 nits (on
-+ * AMD at least). When sRGB is decoded, 1.0 -> 1.0, obviously.
-+ * Therefore, 1.0 multiplier = 80 nits for SDR content. So if you
-+ * want, 203 nits for SDR content, pass in (203.0 / 80.0). Format is
-+ * S31.32 sign-magnitude.
-+ */
-+ __u64 hdr_mult;
-+ /**
-+ * @ctm:
-+ *
-+ * Color transformation matrix. See drm_crtc_enable_color_mgmt(). The
-+ * blob (if not NULL) is a &struct drm_color_ctm.
-+ */
-+ struct drm_property_blob *ctm;
-+ /**
-+ * @shaper_lut: shaper lookup table blob. The blob (if not NULL) is an
-+ * array of &struct drm_color_lut.
-+ */
-+ struct drm_property_blob *shaper_lut;
-+ /**
-+ * @shaper_tf:
-+ *
-+ * Predefined transfer function to delinearize color space.
-+ */
-+ enum amdgpu_transfer_function shaper_tf;
-+ /**
-+ * @lut3d: 3D lookup table blob. The blob (if not NULL) is an array of
-+ * &struct drm_color_lut.
-+ */
-+ struct drm_property_blob *lut3d;
-+ /**
-+ * @blend_lut: blend lut lookup table blob. The blob (if not NULL) is an
-+ * array of &struct drm_color_lut.
-+ */
-+ struct drm_property_blob *blend_lut;
-+ /**
-+ * @blend_tf:
-+ *
-+ * Pre-defined transfer function for converting plane pixel data before
-+ * applying blend LUT.
-+ */
-+ enum amdgpu_transfer_function blend_tf;
- };
-
- struct dm_crtc_state {
-@@ -729,6 +813,14 @@ struct dm_crtc_state {
- struct dc_info_packet vrr_infopacket;
-
- int abm_level;
-+
-+ /**
-+ * @regamma_tf:
-+ *
-+ * Pre-defined transfer function for converting internal FB -> wire
-+ * encoding.
-+ */
-+ enum amdgpu_transfer_function regamma_tf;
- };
-
- #define to_dm_crtc_state(x) container_of(x, struct dm_crtc_state, base)
-@@ -790,14 +882,22 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
-
- void amdgpu_dm_trigger_timing_sync(struct drm_device *dev);
-
-+/* 3D LUT max size is 17x17x17 */
-+#define MAX_COLOR_3DLUT_ENTRIES 4913
-+#define MAX_COLOR_3DLUT_BITDEPTH 12
-+int amdgpu_dm_verify_lut3d_size(struct amdgpu_device *adev,
-+ struct drm_plane_state *plane_state);
-+/* 1D LUT size */
- #define MAX_COLOR_LUT_ENTRIES 4096
- /* Legacy gamm LUT users such as X doesn't like large LUT sizes */
- #define MAX_COLOR_LEGACY_LUT_ENTRIES 256
-
- void amdgpu_dm_init_color_mod(void);
-+int amdgpu_dm_create_color_properties(struct amdgpu_device *adev);
- int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state);
- int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc);
- int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
-+ struct drm_plane_state *plane_state,
- struct dc_plane_state *dc_plane_state);
-
- void amdgpu_dm_update_connector_after_detect(
-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 a4cb23d059bd6a..0a51af44efd5f7 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
-@@ -72,6 +72,7 @@
- */
-
- #define MAX_DRM_LUT_VALUE 0xFFFF
-+#define SDR_WHITE_LEVEL_INIT_VALUE 80
-
- /**
- * amdgpu_dm_init_color_mod - Initialize the color module.
-@@ -84,6 +85,213 @@ void amdgpu_dm_init_color_mod(void)
- setup_x_points_distribution();
- }
-
-+#ifdef AMD_PRIVATE_COLOR
-+/* Pre-defined Transfer Functions (TF)
-+ *
-+ * AMD driver supports pre-defined mathematical functions for transferring
-+ * between encoded values and optical/linear space. Depending on HW color caps,
-+ * ROMs and curves built by the AMD color module support these transforms.
-+ *
-+ * The driver-specific color implementation exposes properties for pre-blending
-+ * degamma TF, shaper TF (before 3D LUT), and blend(dpp.ogam) TF and
-+ * post-blending regamma (mpc.ogam) TF. However, only pre-blending degamma
-+ * supports ROM curves. AMD color module uses pre-defined coefficients to build
-+ * curves for the other blocks. What can be done by each color block is
-+ * described by struct dpp_color_capsand struct mpc_color_caps.
-+ *
-+ * AMD driver-specific color API exposes the following pre-defined transfer
-+ * functions:
-+ *
-+ * - Linear/Unity: linear/identity relationship between pixel value and
-+ * luminance value;
-+ * - Gamma 2.2, Gamma 2.4, Gamma 2.6: pure gamma functions;
-+ * - sRGB: 2.4 gamma with small initial linear section as standardized by IEC
-+ * 61966-2-1:1999;
-+ * - BT.709 (BT.1886): 2.4 gamma with differences in the dark end of the scale.
-+ * Used in HD-TV and standardized by ITU-R BT.1886;
-+ * - PQ (Perceptual Quantizer): used for HDR display, allows luminance range
-+ * capability of 0 to 10,000 nits; standardized by SMPTE ST 2084.
-+ *
-+ * In the driver-specific API, color block names attached to TF properties
-+ * suggest the intention regarding non-linear encoding pixel's luminance
-+ * values. As some newer encodings don't use gamma curve, we make encoding and
-+ * decoding explicit by defining an enum list of transfer functions supported
-+ * in terms of EOTF and inverse EOTF, where:
-+ *
-+ * - EOTF (electro-optical transfer function): is the transfer function to go
-+ * from the encoded value to an optical (linear) value. De-gamma functions
-+ * traditionally do this.
-+ * - Inverse EOTF (simply the inverse of the EOTF): is usually intended to go
-+ * from an optical/linear space (which might have been used for blending)
-+ * back to the encoded values. Gamma functions traditionally do this.
-+ */
-+static const char * const
-+amdgpu_transfer_function_names[] = {
-+ [AMDGPU_TRANSFER_FUNCTION_DEFAULT] = "Default",
-+ [AMDGPU_TRANSFER_FUNCTION_LINEAR] = "Linear",
-+ [AMDGPU_TRANSFER_FUNCTION_UNITY] = "Unity",
-+ [AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF] = "sRGB EOTF",
-+ [AMDGPU_TRANSFER_FUNCTION_BT709_EOTF] = "BT.709 EOTF",
-+ [AMDGPU_TRANSFER_FUNCTION_PQ_EOTF] = "PQ EOTF",
-+ [AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF] = "Gamma 2.2 EOTF",
-+ [AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF] = "Gamma 2.4 EOTF",
-+ [AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF] = "Gamma 2.6 EOTF",
-+ [AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF] = "sRGB inv_EOTF",
-+ [AMDGPU_TRANSFER_FUNCTION_BT709_INV_EOTF] = "BT.709 inv_EOTF",
-+ [AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF] = "PQ inv_EOTF",
-+ [AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF] = "Gamma 2.2 inv_EOTF",
-+ [AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF] = "Gamma 2.4 inv_EOTF",
-+ [AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF] = "Gamma 2.6 inv_EOTF",
-+};
-+
-+static const u32 amdgpu_eotf =
-+ BIT(AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_BT709_EOTF) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_PQ_EOTF) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF);
-+
-+static const u32 amdgpu_inv_eotf =
-+ BIT(AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_BT709_INV_EOTF) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF);
-+
-+static struct drm_property *
-+amdgpu_create_tf_property(struct drm_device *dev,
-+ const char *name,
-+ u32 supported_tf)
-+{
-+ u32 transfer_functions = supported_tf |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_DEFAULT) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_LINEAR) |
-+ BIT(AMDGPU_TRANSFER_FUNCTION_UNITY);
-+ struct drm_prop_enum_list enum_list[AMDGPU_TRANSFER_FUNCTION_COUNT];
-+ int i, len;
-+
-+ len = 0;
-+ for (i = 0; i < AMDGPU_TRANSFER_FUNCTION_COUNT; i++) {
-+ if ((transfer_functions & BIT(i)) == 0)
-+ continue;
-+
-+ enum_list[len].type = i;
-+ enum_list[len].name = amdgpu_transfer_function_names[i];
-+ len++;
-+ }
-+
-+ return drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
-+ name, enum_list, len);
-+}
-+
-+int
-+amdgpu_dm_create_color_properties(struct amdgpu_device *adev)
-+{
-+ struct drm_property *prop;
-+
-+ prop = drm_property_create(adev_to_drm(adev),
-+ DRM_MODE_PROP_BLOB,
-+ "AMD_PLANE_DEGAMMA_LUT", 0);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_degamma_lut_property = prop;
-+
-+ prop = drm_property_create_range(adev_to_drm(adev),
-+ DRM_MODE_PROP_IMMUTABLE,
-+ "AMD_PLANE_DEGAMMA_LUT_SIZE", 0, UINT_MAX);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_degamma_lut_size_property = prop;
-+
-+ prop = amdgpu_create_tf_property(adev_to_drm(adev),
-+ "AMD_PLANE_DEGAMMA_TF",
-+ amdgpu_eotf);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_degamma_tf_property = prop;
-+
-+ prop = drm_property_create_range(adev_to_drm(adev),
-+ 0, "AMD_PLANE_HDR_MULT", 0, U64_MAX);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_hdr_mult_property = prop;
-+
-+ prop = drm_property_create(adev_to_drm(adev),
-+ DRM_MODE_PROP_BLOB,
-+ "AMD_PLANE_CTM", 0);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_ctm_property = prop;
-+
-+ prop = drm_property_create(adev_to_drm(adev),
-+ DRM_MODE_PROP_BLOB,
-+ "AMD_PLANE_SHAPER_LUT", 0);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_shaper_lut_property = prop;
-+
-+ prop = drm_property_create_range(adev_to_drm(adev),
-+ DRM_MODE_PROP_IMMUTABLE,
-+ "AMD_PLANE_SHAPER_LUT_SIZE", 0, UINT_MAX);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_shaper_lut_size_property = prop;
-+
-+ prop = amdgpu_create_tf_property(adev_to_drm(adev),
-+ "AMD_PLANE_SHAPER_TF",
-+ amdgpu_inv_eotf);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_shaper_tf_property = prop;
-+
-+ prop = drm_property_create(adev_to_drm(adev),
-+ DRM_MODE_PROP_BLOB,
-+ "AMD_PLANE_LUT3D", 0);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_lut3d_property = prop;
-+
-+ prop = drm_property_create_range(adev_to_drm(adev),
-+ DRM_MODE_PROP_IMMUTABLE,
-+ "AMD_PLANE_LUT3D_SIZE", 0, UINT_MAX);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_lut3d_size_property = prop;
-+
-+ prop = drm_property_create(adev_to_drm(adev),
-+ DRM_MODE_PROP_BLOB,
-+ "AMD_PLANE_BLEND_LUT", 0);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_blend_lut_property = prop;
-+
-+ prop = drm_property_create_range(adev_to_drm(adev),
-+ DRM_MODE_PROP_IMMUTABLE,
-+ "AMD_PLANE_BLEND_LUT_SIZE", 0, UINT_MAX);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_blend_lut_size_property = prop;
-+
-+ prop = amdgpu_create_tf_property(adev_to_drm(adev),
-+ "AMD_PLANE_BLEND_TF",
-+ amdgpu_eotf);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.plane_blend_tf_property = prop;
-+
-+ prop = amdgpu_create_tf_property(adev_to_drm(adev),
-+ "AMD_CRTC_REGAMMA_TF",
-+ amdgpu_inv_eotf);
-+ if (!prop)
-+ return -ENOMEM;
-+ adev->mode_info.regamma_tf_property = prop;
-+
-+ return 0;
-+}
-+#endif
-+
- /**
- * __extract_blob_lut - Extracts the DRM lut and lut size from a blob.
- * @blob: DRM color mgmt property blob
-@@ -182,7 +390,6 @@ static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut,
- static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
- struct fixed31_32 *matrix)
- {
-- int64_t val;
- int i;
-
- /*
-@@ -201,12 +408,33 @@ static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
- }
-
- /* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */
-- val = ctm->matrix[i - (i / 4)];
-- /* If negative, convert to 2's complement. */
-- if (val & (1ULL << 63))
-- val = -(val & ~(1ULL << 63));
-+ matrix[i] = dc_fixpt_from_s3132(ctm->matrix[i - (i / 4)]);
-+ }
-+}
-
-- matrix[i].value = val;
-+/**
-+ * __drm_ctm2_to_dc_matrix - converts a DRM CTM2 to a DC CSC float matrix
-+ * @ctm: DRM color transformation matrix
-+ * @matrix: DC CSC float matrix
-+ *
-+ * The matrix needs to be a 3x4 (12 entry) matrix.
-+ */
-+static void __drm_ctm2_to_dc_matrix(const struct drm_color_ctm2 *ctm,
-+ struct fixed31_32 *matrix)
-+{
-+ int i;
-+
-+ /*
-+ * DRM gives a 3x3 matrix, but DC wants 3x4. Assuming we're operating
-+ * with homogeneous coordinates, augment the matrix with 0's.
-+ *
-+ * The format provided is S31.32, using signed-magnitude representation.
-+ * Our fixed31_32 is also S31.32, but is using 2's complement. We have
-+ * to convert from signed-magnitude to 2's complement.
-+ */
-+ for (i = 0; i < 12; i++) {
-+ /* gamut_remap_matrix[i] = ctm[i - floor(i/4)] */
-+ matrix[i] = dc_fixpt_from_s3132(ctm->matrix[i]);
- }
- }
-
-@@ -268,16 +496,18 @@ static int __set_output_tf(struct dc_transfer_func *func,
- struct calculate_buffer cal_buffer = {0};
- bool res;
-
-- ASSERT(lut && lut_size == MAX_COLOR_LUT_ENTRIES);
--
- cal_buffer.buffer_index = -1;
-
-- gamma = dc_create_gamma();
-- if (!gamma)
-- return -ENOMEM;
-+ if (lut_size) {
-+ ASSERT(lut && lut_size == MAX_COLOR_LUT_ENTRIES);
-
-- gamma->num_entries = lut_size;
-- __drm_lut_to_dc_gamma(lut, gamma, false);
-+ gamma = dc_create_gamma();
-+ if (!gamma)
-+ return -ENOMEM;
-+
-+ gamma->num_entries = lut_size;
-+ __drm_lut_to_dc_gamma(lut, gamma, false);
-+ }
-
- if (func->tf == TRANSFER_FUNCTION_LINEAR) {
- /*
-@@ -285,27 +515,63 @@ static int __set_output_tf(struct dc_transfer_func *func,
- * on top of a linear input. But degamma params can be used
- * instead to simulate this.
- */
-- gamma->type = GAMMA_CUSTOM;
-+ if (gamma)
-+ gamma->type = GAMMA_CUSTOM;
- res = mod_color_calculate_degamma_params(NULL, func,
-- gamma, true);
-+ gamma, gamma != NULL);
- } else {
- /*
- * Assume sRGB. The actual mapping will depend on whether the
- * input was legacy or not.
- */
-- gamma->type = GAMMA_CS_TFM_1D;
-- res = mod_color_calculate_regamma_params(func, gamma, false,
-+ if (gamma)
-+ gamma->type = GAMMA_CS_TFM_1D;
-+ res = mod_color_calculate_regamma_params(func, gamma, gamma != NULL,
- has_rom, NULL, &cal_buffer);
- }
-
-- dc_gamma_release(&gamma);
-+ if (gamma)
-+ dc_gamma_release(&gamma);
-
- return res ? 0 : -ENOMEM;
- }
-
-+static int amdgpu_dm_set_atomic_regamma(struct dc_stream_state *stream,
-+ const struct drm_color_lut *regamma_lut,
-+ uint32_t regamma_size, bool has_rom,
-+ enum dc_transfer_func_predefined tf)
-+{
-+ struct dc_transfer_func *out_tf = stream->out_transfer_func;
-+ int ret = 0;
-+
-+ if (regamma_size || tf != TRANSFER_FUNCTION_LINEAR) {
-+ /* CRTC RGM goes into RGM LUT.
-+ *
-+ * Note: there is no implicit sRGB regamma here. We are using
-+ * degamma calculation from color module to calculate the curve
-+ * from a linear base.
-+ */
-+ out_tf->type = TF_TYPE_DISTRIBUTED_POINTS;
-+ out_tf->tf = tf;
-+ out_tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
-+
-+ ret = __set_output_tf(out_tf, regamma_lut, regamma_size, has_rom);
-+ } else {
-+ /*
-+ * No CRTC RGM means we can just put the block into bypass
-+ * since we don't have any plane level adjustments using it.
-+ */
-+ out_tf->type = TF_TYPE_BYPASS;
-+ out_tf->tf = TRANSFER_FUNCTION_LINEAR;
-+ }
-+
-+ return ret;
-+}
-+
- /**
- * __set_input_tf - calculates the input transfer function based on expected
- * input space.
-+ * @caps: dc color capabilities
- * @func: transfer function
- * @lut: lookup table that defines the color space
- * @lut_size: size of respective lut.
-@@ -313,27 +579,249 @@ static int __set_output_tf(struct dc_transfer_func *func,
- * Returns:
- * 0 in case of success. -ENOMEM if fails.
- */
--static int __set_input_tf(struct dc_transfer_func *func,
-+static int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *func,
- const struct drm_color_lut *lut, uint32_t lut_size)
- {
- struct dc_gamma *gamma = NULL;
- bool res;
-
-- gamma = dc_create_gamma();
-- if (!gamma)
-- return -ENOMEM;
-+ if (lut_size) {
-+ gamma = dc_create_gamma();
-+ if (!gamma)
-+ return -ENOMEM;
-
-- gamma->type = GAMMA_CUSTOM;
-- gamma->num_entries = lut_size;
-+ gamma->type = GAMMA_CUSTOM;
-+ gamma->num_entries = lut_size;
-
-- __drm_lut_to_dc_gamma(lut, gamma, false);
-+ __drm_lut_to_dc_gamma(lut, gamma, false);
-+ }
-
-- res = mod_color_calculate_degamma_params(NULL, func, gamma, true);
-- dc_gamma_release(&gamma);
-+ res = mod_color_calculate_degamma_params(caps, func, gamma, gamma != NULL);
-+
-+ if (gamma)
-+ dc_gamma_release(&gamma);
-
- return res ? 0 : -ENOMEM;
- }
-
-+static enum dc_transfer_func_predefined
-+amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf)
-+{
-+ switch (tf)
-+ {
-+ default:
-+ case AMDGPU_TRANSFER_FUNCTION_DEFAULT:
-+ case AMDGPU_TRANSFER_FUNCTION_LINEAR:
-+ return TRANSFER_FUNCTION_LINEAR;
-+ case AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF:
-+ case AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF:
-+ return TRANSFER_FUNCTION_SRGB;
-+ case AMDGPU_TRANSFER_FUNCTION_BT709_EOTF:
-+ case AMDGPU_TRANSFER_FUNCTION_BT709_INV_EOTF:
-+ return TRANSFER_FUNCTION_BT709;
-+ case AMDGPU_TRANSFER_FUNCTION_PQ_EOTF:
-+ case AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF:
-+ return TRANSFER_FUNCTION_PQ;
-+ case AMDGPU_TRANSFER_FUNCTION_UNITY:
-+ return TRANSFER_FUNCTION_UNITY;
-+ case AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF:
-+ case AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF:
-+ return TRANSFER_FUNCTION_GAMMA22;
-+ case AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF:
-+ case AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF:
-+ return TRANSFER_FUNCTION_GAMMA24;
-+ case AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF:
-+ case AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF:
-+ return TRANSFER_FUNCTION_GAMMA26;
-+ }
-+}
-+
-+static void __to_dc_lut3d_color(struct dc_rgb *rgb,
-+ const struct drm_color_lut lut,
-+ int bit_precision)
-+{
-+ rgb->red = drm_color_lut_extract(lut.red, bit_precision);
-+ rgb->green = drm_color_lut_extract(lut.green, bit_precision);
-+ rgb->blue = drm_color_lut_extract(lut.blue, bit_precision);
-+}
-+
-+static void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut,
-+ uint32_t lut3d_size,
-+ struct tetrahedral_params *params,
-+ bool use_tetrahedral_9,
-+ int bit_depth)
-+{
-+ struct dc_rgb *lut0;
-+ struct dc_rgb *lut1;
-+ struct dc_rgb *lut2;
-+ struct dc_rgb *lut3;
-+ int lut_i, i;
-+
-+
-+ if (use_tetrahedral_9) {
-+ lut0 = params->tetrahedral_9.lut0;
-+ lut1 = params->tetrahedral_9.lut1;
-+ lut2 = params->tetrahedral_9.lut2;
-+ lut3 = params->tetrahedral_9.lut3;
-+ } else {
-+ lut0 = params->tetrahedral_17.lut0;
-+ lut1 = params->tetrahedral_17.lut1;
-+ lut2 = params->tetrahedral_17.lut2;
-+ lut3 = params->tetrahedral_17.lut3;
-+ }
-+
-+ for (lut_i = 0, i = 0; i < lut3d_size - 4; lut_i++, i += 4) {
-+ /* We should consider the 3dlut RGB values are distributed
-+ * along four arrays lut0-3 where the first sizes 1229 and the
-+ * other 1228. The bit depth supported for 3dlut channel is
-+ * 12-bit, but DC also supports 10-bit.
-+ *
-+ * TODO: improve color pipeline API to enable the userspace set
-+ * bit depth and 3D LUT size/stride, as specified by VA-API.
-+ */
-+ __to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth);
-+ __to_dc_lut3d_color(&lut1[lut_i], lut[i + 1], bit_depth);
-+ __to_dc_lut3d_color(&lut2[lut_i], lut[i + 2], bit_depth);
-+ __to_dc_lut3d_color(&lut3[lut_i], lut[i + 3], bit_depth);
-+ }
-+ /* lut0 has 1229 points (lut_size/4 + 1) */
-+ __to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth);
-+}
-+
-+/* amdgpu_dm_atomic_lut3d - set DRM 3D LUT to DC stream
-+ * @drm_lut3d: DRM CRTC (user) 3D LUT
-+ * @drm_lut3d_size: size of 3D LUT
-+ * @lut3d: DC 3D LUT
-+ *
-+ * Map DRM CRTC 3D LUT to DC 3D LUT and all necessary bits to program it
-+ * on DCN MPC accordingly.
-+ */
-+static void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut,
-+ uint32_t drm_lut3d_size,
-+ struct dc_3dlut *lut)
-+{
-+ if (!drm_lut3d_size) {
-+ lut->state.bits.initialized = 0;
-+ } else {
-+ /* Stride and bit depth are not programmable by API yet.
-+ * Therefore, only supports 17x17x17 3D LUT (12-bit).
-+ */
-+ lut->lut_3d.use_tetrahedral_9 = false;
-+ lut->lut_3d.use_12bits = true;
-+ lut->state.bits.initialized = 1;
-+ __drm_3dlut_to_dc_3dlut(drm_lut, drm_lut3d_size, &lut->lut_3d,
-+ lut->lut_3d.use_tetrahedral_9,
-+ MAX_COLOR_3DLUT_BITDEPTH);
-+ }
-+}
-+
-+static int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *shaper_lut,
-+ bool has_rom,
-+ enum dc_transfer_func_predefined tf,
-+ uint32_t shaper_size,
-+ struct dc_transfer_func *func_shaper)
-+{
-+ int ret = 0;
-+
-+ if (shaper_size || tf != TRANSFER_FUNCTION_LINEAR) {
-+ /* If DRM shaper LUT is set, we assume a linear color space
-+ * (linearized by DRM degamma 1D LUT or not)
-+ */
-+ func_shaper->type = TF_TYPE_DISTRIBUTED_POINTS;
-+ func_shaper->tf = tf;
-+ func_shaper->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
-+
-+ ret = __set_output_tf(func_shaper, shaper_lut, shaper_size, has_rom);
-+ } else {
-+ func_shaper->type = TF_TYPE_BYPASS;
-+ func_shaper->tf = TRANSFER_FUNCTION_LINEAR;
-+ }
-+
-+ return ret;
-+}
-+
-+static int amdgpu_dm_atomic_blend_lut(const struct drm_color_lut *blend_lut,
-+ bool has_rom,
-+ enum dc_transfer_func_predefined tf,
-+ uint32_t blend_size,
-+ struct dc_transfer_func *func_blend)
-+{
-+ int ret = 0;
-+
-+ if (blend_size || tf != TRANSFER_FUNCTION_LINEAR) {
-+ /* DRM plane gamma LUT or TF means we are linearizing color
-+ * space before blending (similar to degamma programming). As
-+ * we don't have hardcoded curve support, or we use AMD color
-+ * module to fill the parameters that will be translated to HW
-+ * points.
-+ */
-+ func_blend->type = TF_TYPE_DISTRIBUTED_POINTS;
-+ func_blend->tf = tf;
-+ func_blend->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
-+
-+ ret = __set_input_tf(NULL, func_blend, blend_lut, blend_size);
-+ } else {
-+ func_blend->type = TF_TYPE_BYPASS;
-+ func_blend->tf = TRANSFER_FUNCTION_LINEAR;
-+ }
-+
-+ return ret;
-+}
-+
-+/* amdgpu_dm_lut3d_size - get expected size according to hw color caps
-+ * @adev: amdgpu device
-+ * @lut_size: default size
-+ *
-+ * Return:
-+ * lut_size if DC 3D LUT is supported, zero otherwise.
-+ */
-+static uint32_t amdgpu_dm_get_lut3d_size(struct amdgpu_device *adev,
-+ uint32_t lut_size)
-+{
-+ return adev->dm.dc->caps.color.dpp.hw_3d_lut ? lut_size : 0;
-+}
-+
-+/**
-+ * amdgpu_dm_verify_lut3d_size - verifies if 3D LUT is supported and if DRM 3D
-+ * LUT matches the hw supported size
-+ * @adev: amdgpu device
-+ * @crtc_state: the DRM CRTC state
-+ *
-+ * Verifies if post-blending (MPC) 3D LUT is supported by the HW (DCN 3.0 or
-+ * newer) and if the DRM 3D LUT matches the supported size.
-+ *
-+ * Returns:
-+ * 0 on success. -EINVAL if lut size are invalid.
-+ */
-+int amdgpu_dm_verify_lut3d_size(struct amdgpu_device *adev,
-+ struct drm_plane_state *plane_state)
-+{
-+ struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
-+ const struct drm_color_lut *shaper = NULL, *lut3d = NULL;
-+ uint32_t exp_size, size;
-+
-+ /* shaper LUT is only available if 3D LUT color caps*/
-+ exp_size = amdgpu_dm_get_lut3d_size(adev, MAX_COLOR_LUT_ENTRIES);
-+ shaper = __extract_blob_lut(dm_plane_state->shaper_lut, &size);
-+
-+ if (shaper && size != exp_size) {
-+ drm_dbg(&adev->ddev,
-+ "Invalid Shaper LUT size. Should be %u but got %u.\n",
-+ exp_size, size);
-+ }
-+
-+ exp_size = amdgpu_dm_get_lut3d_size(adev, MAX_COLOR_3DLUT_ENTRIES);
-+ lut3d = __extract_blob_lut(dm_plane_state->lut3d, &size);
-+
-+ if (lut3d && size != exp_size) {
-+ drm_dbg(&adev->ddev, "Invalid 3D LUT size. Should be %u but got %u.\n",
-+ exp_size, size);
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
- /**
- * amdgpu_dm_verify_lut_sizes - verifies if DRM luts match the hw supported sizes
- * @crtc_state: the DRM CRTC state
-@@ -401,9 +889,12 @@ int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc)
- const struct drm_color_lut *degamma_lut, *regamma_lut;
- uint32_t degamma_size, regamma_size;
- bool has_regamma, has_degamma;
-+ enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_LINEAR;
- bool is_legacy;
- int r;
-
-+ tf = amdgpu_tf_to_dc_tf(crtc->regamma_tf);
-+
- r = amdgpu_dm_verify_lut_sizes(&crtc->base);
- if (r)
- return r;
-@@ -440,26 +931,22 @@ int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc)
- stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
- stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
-
-+ /* Note: although we pass has_rom as parameter here, we never
-+ * actually use ROM because the color module only takes the ROM
-+ * path if transfer_func->type == PREDEFINED.
-+ *
-+ * See more in mod_color_calculate_regamma_params()
-+ */
- r = __set_legacy_tf(stream->out_transfer_func, regamma_lut,
- regamma_size, has_rom);
- if (r)
- return r;
-- } else if (has_regamma) {
-- /* If atomic regamma, CRTC RGM goes into RGM LUT. */
-- stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
-- stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
--
-- r = __set_output_tf(stream->out_transfer_func, regamma_lut,
-- regamma_size, has_rom);
-+ } else {
-+ regamma_size = has_regamma ? regamma_size : 0;
-+ r = amdgpu_dm_set_atomic_regamma(stream, regamma_lut,
-+ regamma_size, has_rom, tf);
- if (r)
- return r;
-- } else {
-- /*
-- * No CRTC RGM means we can just put the block into bypass
-- * since we don't have any plane level adjustments using it.
-- */
-- stream->out_transfer_func->type = TF_TYPE_BYPASS;
-- stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
- }
-
- /*
-@@ -495,20 +982,10 @@ int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc)
- return 0;
- }
-
--/**
-- * amdgpu_dm_update_plane_color_mgmt: Maps DRM color management to DC plane.
-- * @crtc: amdgpu_dm crtc state
-- * @dc_plane_state: target DC surface
-- *
-- * Update the underlying dc_stream_state's input transfer function (ITF) in
-- * preparation for hardware commit. The transfer function used depends on
-- * the preparation done on the stream for color management.
-- *
-- * Returns:
-- * 0 on success. -ENOMEM if mem allocation fails.
-- */
--int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
-- struct dc_plane_state *dc_plane_state)
-+static int
-+map_crtc_degamma_to_dc_plane(struct dm_crtc_state *crtc,
-+ struct dc_plane_state *dc_plane_state,
-+ struct dc_color_caps *caps)
- {
- const struct drm_color_lut *degamma_lut;
- enum dc_transfer_func_predefined tf = TRANSFER_FUNCTION_SRGB;
-@@ -531,8 +1008,7 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
- &degamma_size);
- ASSERT(degamma_size == MAX_COLOR_LUT_ENTRIES);
-
-- dc_plane_state->in_transfer_func->type =
-- TF_TYPE_DISTRIBUTED_POINTS;
-+ dc_plane_state->in_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
-
- /*
- * This case isn't fully correct, but also fairly
-@@ -564,11 +1040,11 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
- dc_plane_state->in_transfer_func->tf =
- TRANSFER_FUNCTION_LINEAR;
-
-- r = __set_input_tf(dc_plane_state->in_transfer_func,
-+ r = __set_input_tf(caps, dc_plane_state->in_transfer_func,
- degamma_lut, degamma_size);
- if (r)
- return r;
-- } else if (crtc->cm_is_degamma_srgb) {
-+ } else {
- /*
- * For legacy gamma support we need the regamma input
- * in linear space. Assume that the input is sRGB.
-@@ -577,14 +1053,213 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
- dc_plane_state->in_transfer_func->tf = tf;
-
- if (tf != TRANSFER_FUNCTION_SRGB &&
-- !mod_color_calculate_degamma_params(NULL,
-- dc_plane_state->in_transfer_func, NULL, false))
-+ !mod_color_calculate_degamma_params(caps,
-+ dc_plane_state->in_transfer_func,
-+ NULL, false))
-+ return -ENOMEM;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+__set_dm_plane_degamma(struct drm_plane_state *plane_state,
-+ struct dc_plane_state *dc_plane_state,
-+ struct dc_color_caps *color_caps)
-+{
-+ struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
-+ const struct drm_color_lut *degamma_lut;
-+ enum amdgpu_transfer_function tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
-+ uint32_t degamma_size;
-+ bool has_degamma_lut;
-+ int ret;
-+
-+ degamma_lut = __extract_blob_lut(dm_plane_state->degamma_lut,
-+ &degamma_size);
-+
-+ has_degamma_lut = degamma_lut &&
-+ !__is_lut_linear(degamma_lut, degamma_size);
-+
-+ tf = dm_plane_state->degamma_tf;
-+
-+ /* If we don't have plane degamma LUT nor TF to set on DC, we have
-+ * nothing to do here, return.
-+ */
-+ if (!has_degamma_lut && tf == AMDGPU_TRANSFER_FUNCTION_DEFAULT)
-+ return -EINVAL;
-+
-+ dc_plane_state->in_transfer_func->tf = amdgpu_tf_to_dc_tf(tf);
-+
-+ if (has_degamma_lut) {
-+ ASSERT(degamma_size == MAX_COLOR_LUT_ENTRIES);
-+
-+ dc_plane_state->in_transfer_func->type =
-+ TF_TYPE_DISTRIBUTED_POINTS;
-+
-+ ret = __set_input_tf(color_caps, dc_plane_state->in_transfer_func,
-+ degamma_lut, degamma_size);
-+ if (ret)
-+ return ret;
-+ } else {
-+ dc_plane_state->in_transfer_func->type =
-+ TF_TYPE_PREDEFINED;
-+
-+ if (!mod_color_calculate_degamma_params(color_caps,
-+ dc_plane_state->in_transfer_func, NULL, false))
- return -ENOMEM;
-- } else {
-- /* ...Otherwise we can just bypass the DGM block. */
-- dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;
-- dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
-+ }
-+ return 0;
-+}
-+
-+static int
-+amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state,
-+ struct dc_plane_state *dc_plane_state,
-+ struct dc_color_caps *color_caps)
-+{
-+ struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
-+ enum amdgpu_transfer_function shaper_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
-+ enum amdgpu_transfer_function blend_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
-+ const struct drm_color_lut *shaper_lut, *lut3d, *blend_lut;
-+ uint32_t shaper_size, lut3d_size, blend_size;
-+ int ret;
-+
-+ /* We have nothing to do here, return */
-+ if (!plane_state->color_mgmt_changed)
-+ return 0;
-+
-+ dc_plane_state->hdr_mult = dc_fixpt_from_s3132(dm_plane_state->hdr_mult);
-+
-+ shaper_lut = __extract_blob_lut(dm_plane_state->shaper_lut, &shaper_size);
-+ shaper_size = shaper_lut != NULL ? shaper_size : 0;
-+ shaper_tf = dm_plane_state->shaper_tf;
-+ lut3d = __extract_blob_lut(dm_plane_state->lut3d, &lut3d_size);
-+ lut3d_size = lut3d != NULL ? lut3d_size : 0;
-+
-+ amdgpu_dm_atomic_lut3d(lut3d, lut3d_size, dc_plane_state->lut3d_func);
-+ ret = amdgpu_dm_atomic_shaper_lut(shaper_lut, false,
-+ amdgpu_tf_to_dc_tf(shaper_tf),
-+ shaper_size,
-+ dc_plane_state->in_shaper_func);
-+ if (ret) {
-+ drm_dbg_kms(plane_state->plane->dev,
-+ "setting plane %d shaper LUT failed.\n",
-+ plane_state->plane->index);
-+
-+ return ret;
-+ }
-+
-+ blend_tf = dm_plane_state->blend_tf;
-+ blend_lut = __extract_blob_lut(dm_plane_state->blend_lut, &blend_size);
-+ blend_size = blend_lut != NULL ? blend_size : 0;
-+
-+ ret = amdgpu_dm_atomic_blend_lut(blend_lut, false,
-+ amdgpu_tf_to_dc_tf(blend_tf),
-+ blend_size, dc_plane_state->blend_tf);
-+ if (ret) {
-+ drm_dbg_kms(plane_state->plane->dev,
-+ "setting plane %d gamma lut failed.\n",
-+ plane_state->plane->index);
-+
-+ return ret;
- }
-
- return 0;
- }
-+
-+/**
-+ * amdgpu_dm_update_plane_color_mgmt: Maps DRM color management to DC plane.
-+ * @crtc: amdgpu_dm crtc state
-+ * @plane_state: DRM plane state
-+ * @dc_plane_state: target DC surface
-+ *
-+ * Update the underlying dc_stream_state's input transfer function (ITF) in
-+ * preparation for hardware commit. The transfer function used depends on
-+ * the preparation done on the stream for color management.
-+ *
-+ * Returns:
-+ * 0 on success. -ENOMEM if mem allocation fails.
-+ */
-+int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
-+ struct drm_plane_state *plane_state,
-+ struct dc_plane_state *dc_plane_state)
-+{
-+ struct amdgpu_device *adev = drm_to_adev(crtc->base.state->dev);
-+ struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane_state);
-+ struct drm_color_ctm2 *ctm = NULL;
-+ struct dc_color_caps *color_caps = NULL;
-+ bool has_crtc_cm_degamma;
-+ int ret;
-+
-+ ret = amdgpu_dm_verify_lut3d_size(adev, plane_state);
-+ if (ret) {
-+ drm_dbg_driver(&adev->ddev, "amdgpu_dm_verify_lut3d_size() failed\n");
-+ return ret;
-+ }
-+
-+ if (dc_plane_state->ctx && dc_plane_state->ctx->dc)
-+ color_caps = &dc_plane_state->ctx->dc->caps.color;
-+
-+ /* Initially, we can just bypass the DGM block. */
-+ dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;
-+ dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
-+
-+ /* After, we start to update values according to color props */
-+ has_crtc_cm_degamma = (crtc->cm_has_degamma || crtc->cm_is_degamma_srgb);
-+
-+ ret = __set_dm_plane_degamma(plane_state, dc_plane_state, color_caps);
-+ if (ret == -ENOMEM)
-+ return ret;
-+
-+ /* We only have one degamma block available (pre-blending) for the
-+ * whole color correction pipeline, so that we can't actually perform
-+ * plane and CRTC degamma at the same time. Explicitly reject atomic
-+ * updates when userspace sets both plane and CRTC degamma properties.
-+ */
-+ if (has_crtc_cm_degamma && ret != -EINVAL){
-+ drm_dbg_kms(crtc->base.crtc->dev,
-+ "doesn't support plane and CRTC degamma at the same time\n");
-+ return -EINVAL;
-+ }
-+
-+ /* If we are here, it means we don't have plane degamma settings, check
-+ * if we have CRTC degamma waiting for mapping to pre-blending degamma
-+ * block
-+ */
-+ if (has_crtc_cm_degamma) {
-+ /* AMD HW doesn't have post-blending degamma caps. When DRM
-+ * CRTC atomic degamma is set, we maps it to DPP degamma block
-+ * (pre-blending) or, on legacy gamma, we use DPP degamma to
-+ * linearize (implicit degamma) from sRGB/BT709 according to
-+ * the input space.
-+ */
-+ ret = map_crtc_degamma_to_dc_plane(crtc, dc_plane_state, color_caps);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ /* Setup CRTC CTM. */
-+ if (dm_plane_state->ctm) {
-+ ctm = (struct drm_color_ctm2 *)dm_plane_state->ctm->data;
-+
-+ /*
-+ * So far, if we have both plane and CRTC CTM, plane CTM takes
-+ * the priority and we discard data for CRTC CTM, as
-+ * implemented in dcn10_program_gamut_remap(). However, we
-+ * have MPC gamut_remap_matrix from DCN3 family, therefore we
-+ * can remap MPC programing of the matrix to MPC block and
-+ * provide support for both DPP and MPC matrix at the same
-+ * time.
-+ */
-+ __drm_ctm2_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 {
-+ /* Bypass CTM. */
-+ dc_plane_state->gamut_remap_matrix.enable_remap = false;
-+ dc_plane_state->input_csc_color_matrix.enable_adjustment = false;
-+ }
-+
-+ return amdgpu_dm_plane_set_color_properties(plane_state,
-+ dc_plane_state, color_caps);
-+}
-diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
-index 30d4c6fd95f531..e7b38cce010cc8 100644
---- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
-+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
-@@ -253,6 +253,7 @@ static struct drm_crtc_state *dm_crtc_duplicate_state(struct drm_crtc *crtc)
- state->freesync_config = cur->freesync_config;
- state->cm_has_degamma = cur->cm_has_degamma;
- state->cm_is_degamma_srgb = cur->cm_is_degamma_srgb;
-+ state->regamma_tf = cur->regamma_tf;
- state->crc_skip_count = cur->crc_skip_count;
- state->mpo_requested = cur->mpo_requested;
- /* TODO Duplicate dc_stream after objects are stream object is flattened */
-@@ -289,6 +290,70 @@ static int amdgpu_dm_crtc_late_register(struct drm_crtc *crtc)
- }
- #endif
-
-+#ifdef AMD_PRIVATE_COLOR
-+/**
-+ * drm_crtc_additional_color_mgmt - enable additional color properties
-+ * @crtc: DRM CRTC
-+ *
-+ * This function lets the driver enable post-blending CRTC regamma transfer
-+ * function property in addition to DRM CRTC gamma LUT. Default value means
-+ * linear transfer function, which is the default CRTC gamma LUT behaviour
-+ * without this property.
-+ */
-+static void
-+dm_crtc_additional_color_mgmt(struct drm_crtc *crtc)
-+{
-+ struct amdgpu_device *adev = drm_to_adev(crtc->dev);
-+
-+ if(adev->dm.dc->caps.color.mpc.ogam_ram)
-+ drm_object_attach_property(&crtc->base,
-+ adev->mode_info.regamma_tf_property,
-+ AMDGPU_TRANSFER_FUNCTION_DEFAULT);
-+}
-+
-+static int
-+amdgpu_dm_atomic_crtc_set_property(struct drm_crtc *crtc,
-+ struct drm_crtc_state *state,
-+ struct drm_property *property,
-+ uint64_t val)
-+{
-+ struct amdgpu_device *adev = drm_to_adev(crtc->dev);
-+ struct dm_crtc_state *acrtc_state = to_dm_crtc_state(state);
-+
-+ if (property == adev->mode_info.regamma_tf_property) {
-+ if (acrtc_state->regamma_tf != val) {
-+ acrtc_state->regamma_tf = val;
-+ acrtc_state->base.color_mgmt_changed |= 1;
-+ }
-+ } else {
-+ drm_dbg_atomic(crtc->dev,
-+ "[CRTC:%d:%s] unknown property [PROP:%d:%s]]\n",
-+ crtc->base.id, crtc->name,
-+ property->base.id, property->name);
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+amdgpu_dm_atomic_crtc_get_property(struct drm_crtc *crtc,
-+ const struct drm_crtc_state *state,
-+ struct drm_property *property,
-+ uint64_t *val)
-+{
-+ struct amdgpu_device *adev = drm_to_adev(crtc->dev);
-+ struct dm_crtc_state *acrtc_state = to_dm_crtc_state(state);
-+
-+ if (property == adev->mode_info.regamma_tf_property)
-+ *val = acrtc_state->regamma_tf;
-+ else
-+ return -EINVAL;
-+
-+ return 0;
-+}
-+#endif
-+
- /* Implemented only the options currently available for the driver */
- static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
- .reset = dm_crtc_reset_state,
-@@ -307,6 +372,10 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
- #if defined(CONFIG_DEBUG_FS)
- .late_register = amdgpu_dm_crtc_late_register,
- #endif
-+#ifdef AMD_PRIVATE_COLOR
-+ .atomic_set_property = amdgpu_dm_atomic_crtc_set_property,
-+ .atomic_get_property = amdgpu_dm_atomic_crtc_get_property,
-+#endif
- };
-
- static void dm_crtc_helper_disable(struct drm_crtc *crtc)
-@@ -482,6 +551,9 @@ int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm,
-
- drm_mode_crtc_set_gamma_size(&acrtc->base, MAX_COLOR_LEGACY_LUT_ENTRIES);
-
-+#ifdef AMD_PRIVATE_COLOR
-+ dm_crtc_additional_color_mgmt(&acrtc->base);
-+#endif
- return 0;
-
- fail:
-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 32266897374792..60e5ffb1863d74 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
-@@ -1317,8 +1317,14 @@ static void dm_drm_plane_reset(struct drm_plane *plane)
- amdgpu_state = kzalloc(sizeof(*amdgpu_state), GFP_KERNEL);
- WARN_ON(amdgpu_state == NULL);
-
-- if (amdgpu_state)
-- __drm_atomic_helper_plane_reset(plane, &amdgpu_state->base);
-+ if (!amdgpu_state)
-+ return;
-+
-+ __drm_atomic_helper_plane_reset(plane, &amdgpu_state->base);
-+ amdgpu_state->degamma_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
-+ amdgpu_state->hdr_mult = AMDGPU_HDR_MULT_DEFAULT;
-+ amdgpu_state->shaper_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
-+ amdgpu_state->blend_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
- }
-
- static struct drm_plane_state *
-@@ -1338,6 +1344,22 @@ dm_drm_plane_duplicate_state(struct drm_plane *plane)
- dc_plane_state_retain(dm_plane_state->dc_state);
- }
-
-+ if (dm_plane_state->degamma_lut)
-+ drm_property_blob_get(dm_plane_state->degamma_lut);
-+ if (dm_plane_state->ctm)
-+ drm_property_blob_get(dm_plane_state->ctm);
-+ if (dm_plane_state->shaper_lut)
-+ drm_property_blob_get(dm_plane_state->shaper_lut);
-+ if (dm_plane_state->lut3d)
-+ drm_property_blob_get(dm_plane_state->lut3d);
-+ if (dm_plane_state->blend_lut)
-+ drm_property_blob_get(dm_plane_state->blend_lut);
-+
-+ dm_plane_state->degamma_tf = old_dm_plane_state->degamma_tf;
-+ dm_plane_state->hdr_mult = old_dm_plane_state->hdr_mult;
-+ dm_plane_state->shaper_tf = old_dm_plane_state->shaper_tf;
-+ dm_plane_state->blend_tf = old_dm_plane_state->blend_tf;
-+
- return &dm_plane_state->base;
- }
-
-@@ -1405,12 +1427,203 @@ static void dm_drm_plane_destroy_state(struct drm_plane *plane,
- {
- struct dm_plane_state *dm_plane_state = to_dm_plane_state(state);
-
-+ if (dm_plane_state->degamma_lut)
-+ drm_property_blob_put(dm_plane_state->degamma_lut);
-+ if (dm_plane_state->ctm)
-+ drm_property_blob_put(dm_plane_state->ctm);
-+ if (dm_plane_state->lut3d)
-+ drm_property_blob_put(dm_plane_state->lut3d);
-+ if (dm_plane_state->shaper_lut)
-+ drm_property_blob_put(dm_plane_state->shaper_lut);
-+ if (dm_plane_state->blend_lut)
-+ drm_property_blob_put(dm_plane_state->blend_lut);
-+
- if (dm_plane_state->dc_state)
- dc_plane_state_release(dm_plane_state->dc_state);
-
- drm_atomic_helper_plane_destroy_state(plane, state);
- }
-
-+#ifdef AMD_PRIVATE_COLOR
-+static void
-+dm_atomic_plane_attach_color_mgmt_properties(struct amdgpu_display_manager *dm,
-+ struct drm_plane *plane)
-+{
-+ struct amdgpu_mode_info mode_info = dm->adev->mode_info;
-+ struct dpp_color_caps dpp_color_caps = dm->dc->caps.color.dpp;
-+
-+ /* Check HW color pipeline capabilities for DPP (pre-blending) before expose*/
-+ if (dpp_color_caps.dgam_ram || dpp_color_caps.gamma_corr) {
-+ drm_object_attach_property(&plane->base,
-+ mode_info.plane_degamma_lut_property, 0);
-+ drm_object_attach_property(&plane->base,
-+ mode_info.plane_degamma_lut_size_property,
-+ MAX_COLOR_LUT_ENTRIES);
-+ drm_object_attach_property(&plane->base,
-+ dm->adev->mode_info.plane_degamma_tf_property,
-+ AMDGPU_TRANSFER_FUNCTION_DEFAULT);
-+ }
-+ /* HDR MULT is always available */
-+ drm_object_attach_property(&plane->base,
-+ dm->adev->mode_info.plane_hdr_mult_property,
-+ AMDGPU_HDR_MULT_DEFAULT);
-+
-+ /* Only enable plane CTM if both DPP and MPC gamut remap is available. */
-+ if (dm->dc->caps.color.mpc.gamut_remap)
-+ drm_object_attach_property(&plane->base,
-+ dm->adev->mode_info.plane_ctm_property, 0);
-+
-+ if (dpp_color_caps.hw_3d_lut) {
-+ drm_object_attach_property(&plane->base,
-+ mode_info.plane_shaper_lut_property, 0);
-+ drm_object_attach_property(&plane->base,
-+ mode_info.plane_shaper_lut_size_property,
-+ MAX_COLOR_LUT_ENTRIES);
-+ drm_object_attach_property(&plane->base,
-+ mode_info.plane_shaper_tf_property,
-+ AMDGPU_TRANSFER_FUNCTION_DEFAULT);
-+ drm_object_attach_property(&plane->base,
-+ mode_info.plane_lut3d_property, 0);
-+ drm_object_attach_property(&plane->base,
-+ mode_info.plane_lut3d_size_property,
-+ MAX_COLOR_3DLUT_ENTRIES);
-+ }
-+
-+ if (dpp_color_caps.ogam_ram) {
-+ drm_object_attach_property(&plane->base,
-+ mode_info.plane_blend_lut_property, 0);
-+ drm_object_attach_property(&plane->base,
-+ mode_info.plane_blend_lut_size_property,
-+ MAX_COLOR_LUT_ENTRIES);
-+ drm_object_attach_property(&plane->base,
-+ mode_info.plane_blend_tf_property,
-+ AMDGPU_TRANSFER_FUNCTION_DEFAULT);
-+ }
-+}
-+
-+static int
-+dm_atomic_plane_set_property(struct drm_plane *plane,
-+ struct drm_plane_state *state,
-+ struct drm_property *property,
-+ uint64_t val)
-+{
-+ struct dm_plane_state *dm_plane_state = to_dm_plane_state(state);
-+ struct amdgpu_device *adev = drm_to_adev(plane->dev);
-+ bool replaced = false;
-+ int ret;
-+
-+ if (property == adev->mode_info.plane_degamma_lut_property) {
-+ ret = drm_property_replace_blob_from_id(plane->dev,
-+ &dm_plane_state->degamma_lut,
-+ val,
-+ -1, sizeof(struct drm_color_lut),
-+ &replaced);
-+ dm_plane_state->base.color_mgmt_changed |= replaced;
-+ return ret;
-+ } else if (property == adev->mode_info.plane_degamma_tf_property) {
-+ if (dm_plane_state->degamma_tf != val) {
-+ dm_plane_state->degamma_tf = val;
-+ dm_plane_state->base.color_mgmt_changed = 1;
-+ }
-+ } else if (property == adev->mode_info.plane_hdr_mult_property) {
-+ if (dm_plane_state->hdr_mult != val) {
-+ dm_plane_state->hdr_mult = val;
-+ dm_plane_state->base.color_mgmt_changed = 1;
-+ }
-+ } else if (property == adev->mode_info.plane_ctm_property) {
-+ ret = drm_property_replace_blob_from_id(plane->dev,
-+ &dm_plane_state->ctm,
-+ val,
-+ sizeof(struct drm_color_ctm2), -1,
-+ &replaced);
-+ dm_plane_state->base.color_mgmt_changed |= replaced;
-+ return ret;
-+ } else if (property == adev->mode_info.plane_shaper_lut_property) {
-+ ret = drm_property_replace_blob_from_id(plane->dev,
-+ &dm_plane_state->shaper_lut,
-+ val, -1,
-+ sizeof(struct drm_color_lut),
-+ &replaced);
-+ dm_plane_state->base.color_mgmt_changed |= replaced;
-+ return ret;
-+ } else if (property == adev->mode_info.plane_shaper_tf_property) {
-+ if (dm_plane_state->shaper_tf != val) {
-+ dm_plane_state->shaper_tf = val;
-+ dm_plane_state->base.color_mgmt_changed = 1;
-+ }
-+ } else if (property == adev->mode_info.plane_lut3d_property) {
-+ ret = drm_property_replace_blob_from_id(plane->dev,
-+ &dm_plane_state->lut3d,
-+ val, -1,
-+ sizeof(struct drm_color_lut),
-+ &replaced);
-+ dm_plane_state->base.color_mgmt_changed |= replaced;
-+ return ret;
-+ } else if (property == adev->mode_info.plane_blend_lut_property) {
-+ ret = drm_property_replace_blob_from_id(plane->dev,
-+ &dm_plane_state->blend_lut,
-+ val, -1,
-+ sizeof(struct drm_color_lut),
-+ &replaced);
-+ dm_plane_state->base.color_mgmt_changed |= replaced;
-+ return ret;
-+ } else if (property == adev->mode_info.plane_blend_tf_property) {
-+ if (dm_plane_state->blend_tf != val) {
-+ dm_plane_state->blend_tf = val;
-+ dm_plane_state->base.color_mgmt_changed = 1;
-+ }
-+ } else {
-+ drm_dbg_atomic(plane->dev,
-+ "[PLANE:%d:%s] unknown property [PROP:%d:%s]]\n",
-+ plane->base.id, plane->name,
-+ property->base.id, property->name);
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int
-+dm_atomic_plane_get_property(struct drm_plane *plane,
-+ const struct drm_plane_state *state,
-+ struct drm_property *property,
-+ uint64_t *val)
-+{
-+ struct dm_plane_state *dm_plane_state = to_dm_plane_state(state);
-+ struct amdgpu_device *adev = drm_to_adev(plane->dev);
-+
-+ if (property == adev->mode_info.plane_degamma_lut_property) {
-+ *val = (dm_plane_state->degamma_lut) ?
-+ dm_plane_state->degamma_lut->base.id : 0;
-+ } else if (property == adev->mode_info.plane_degamma_tf_property) {
-+ *val = dm_plane_state->degamma_tf;
-+ } else if (property == adev->mode_info.plane_hdr_mult_property) {
-+ *val = dm_plane_state->hdr_mult;
-+ } else if (property == adev->mode_info.plane_ctm_property) {
-+ *val = (dm_plane_state->ctm) ?
-+ dm_plane_state->ctm->base.id : 0;
-+ } else if (property == adev->mode_info.plane_shaper_lut_property) {
-+ *val = (dm_plane_state->shaper_lut) ?
-+ dm_plane_state->shaper_lut->base.id : 0;
-+ } else if (property == adev->mode_info.plane_shaper_tf_property) {
-+ *val = dm_plane_state->shaper_tf;
-+ } else if (property == adev->mode_info.plane_lut3d_property) {
-+ *val = (dm_plane_state->lut3d) ?
-+ dm_plane_state->lut3d->base.id : 0;
-+ } else if (property == adev->mode_info.plane_blend_lut_property) {
-+ *val = (dm_plane_state->blend_lut) ?
-+ dm_plane_state->blend_lut->base.id : 0;
-+ } else if (property == adev->mode_info.plane_blend_tf_property) {
-+ *val = dm_plane_state->blend_tf;
-+
-+ } else {
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+#endif
-+
- static const struct drm_plane_funcs dm_plane_funcs = {
- .update_plane = drm_atomic_helper_update_plane,
- .disable_plane = drm_atomic_helper_disable_plane,
-@@ -1419,6 +1632,10 @@ static const struct drm_plane_funcs dm_plane_funcs = {
- .atomic_duplicate_state = dm_drm_plane_duplicate_state,
- .atomic_destroy_state = dm_drm_plane_destroy_state,
- .format_mod_supported = dm_plane_format_mod_supported,
-+#ifdef AMD_PRIVATE_COLOR
-+ .atomic_set_property = dm_atomic_plane_set_property,
-+ .atomic_get_property = dm_atomic_plane_get_property,
-+#endif
- };
-
- int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm,
-@@ -1489,6 +1706,9 @@ int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm,
-
- drm_plane_helper_add(plane, &dm_plane_helper_funcs);
-
-+#ifdef AMD_PRIVATE_COLOR
-+ dm_atomic_plane_attach_color_mgmt_properties(dm, plane);
-+#endif
- /* Create (reset) the plane state */
- if (plane->funcs->reset)
- plane->funcs->reset(plane);
-diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c
-index 3538973bd0c6cb..04b2e04b68f33b 100644
---- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c
-+++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c
-@@ -349,20 +349,37 @@ bool cm_helper_translate_curve_to_hw_format(struct dc_context *ctx,
- * segment is from 2^-10 to 2^1
- * There are less than 256 points, for optimization
- */
-- seg_distr[0] = 3;
-- seg_distr[1] = 4;
-- seg_distr[2] = 4;
-- seg_distr[3] = 4;
-- seg_distr[4] = 4;
-- seg_distr[5] = 4;
-- seg_distr[6] = 4;
-- seg_distr[7] = 4;
-- seg_distr[8] = 4;
-- seg_distr[9] = 4;
-- seg_distr[10] = 1;
--
-- region_start = -10;
-- region_end = 1;
-+ if (output_tf->tf == TRANSFER_FUNCTION_LINEAR) {
-+ seg_distr[0] = 0; /* 2 */
-+ seg_distr[1] = 1; /* 4 */
-+ seg_distr[2] = 2; /* 4 */
-+ seg_distr[3] = 3; /* 8 */
-+ seg_distr[4] = 4; /* 16 */
-+ seg_distr[5] = 5; /* 32 */
-+ seg_distr[6] = 6; /* 64 */
-+ seg_distr[7] = 7; /* 128 */
-+
-+ region_start = -8;
-+ region_end = 1;
-+ } else {
-+ seg_distr[0] = 3; /* 8 */
-+ seg_distr[1] = 4; /* 16 */
-+ seg_distr[2] = 4;
-+ seg_distr[3] = 4;
-+ seg_distr[4] = 4;
-+ seg_distr[5] = 4;
-+ seg_distr[6] = 4;
-+ seg_distr[7] = 4;
-+ seg_distr[8] = 4;
-+ seg_distr[9] = 4;
-+ seg_distr[10] = 1; /* 2 */
-+ /* total = 8*16 + 8 + 64 + 2 = */
-+
-+ region_start = -10;
-+ region_end = 1;
-+ }
-+
-+
- }
-
- for (i = region_end - region_start; i < MAX_REGIONS_NUMBER ; i++)
-@@ -375,16 +392,56 @@ bool cm_helper_translate_curve_to_hw_format(struct dc_context *ctx,
-
- j = 0;
- for (k = 0; k < (region_end - region_start); k++) {
-- increment = NUMBER_SW_SEGMENTS / (1 << seg_distr[k]);
-+ /*
-+ * We're using an ugly-ish hack here. Our HW allows for
-+ * 256 segments per region but SW_SEGMENTS is 16.
-+ * SW_SEGMENTS has some undocumented relationship to
-+ * the number of points in the tf_pts struct, which
-+ * is 512, unlike what's suggested TRANSFER_FUNC_POINTS.
-+ *
-+ * In order to work past this dilemma we'll scale our
-+ * increment by (1 << 4) and then do the inverse (1 >> 4)
-+ * when accessing the elements in tf_pts.
-+ *
-+ * TODO: find a better way using SW_SEGMENTS and
-+ * TRANSFER_FUNC_POINTS definitions
-+ */
-+ increment = (NUMBER_SW_SEGMENTS << 4) / (1 << seg_distr[k]);
- start_index = (region_start + k + MAX_LOW_POINT) *
- NUMBER_SW_SEGMENTS;
-- for (i = start_index; i < start_index + NUMBER_SW_SEGMENTS;
-+ for (i = (start_index << 4); i < (start_index << 4) + (NUMBER_SW_SEGMENTS << 4);
- i += increment) {
-+ struct fixed31_32 in_plus_one, in;
-+ struct fixed31_32 value, red_value, green_value, blue_value;
-+ uint32_t t = i & 0xf;
-+
- if (j == hw_points - 1)
- break;
-- rgb_resulted[j].red = output_tf->tf_pts.red[i];
-- rgb_resulted[j].green = output_tf->tf_pts.green[i];
-- rgb_resulted[j].blue = output_tf->tf_pts.blue[i];
-+
-+ in_plus_one = output_tf->tf_pts.red[(i >> 4) + 1];
-+ in = output_tf->tf_pts.red[i >> 4];
-+ value = dc_fixpt_sub(in_plus_one, in);
-+ value = dc_fixpt_shr(dc_fixpt_mul_int(value, t), 4);
-+ value = dc_fixpt_add(in, value);
-+ red_value = value;
-+
-+ in_plus_one = output_tf->tf_pts.green[(i >> 4) + 1];
-+ in = output_tf->tf_pts.green[i >> 4];
-+ value = dc_fixpt_sub(in_plus_one, in);
-+ value = dc_fixpt_shr(dc_fixpt_mul_int(value, t), 4);
-+ value = dc_fixpt_add(in, value);
-+ green_value = value;
-+
-+ in_plus_one = output_tf->tf_pts.blue[(i >> 4) + 1];
-+ in = output_tf->tf_pts.blue[i >> 4];
-+ value = dc_fixpt_sub(in_plus_one, in);
-+ value = dc_fixpt_shr(dc_fixpt_mul_int(value, t), 4);
-+ value = dc_fixpt_add(in, value);
-+ blue_value = value;
-+
-+ rgb_resulted[j].red = red_value;
-+ rgb_resulted[j].green = green_value;
-+ rgb_resulted[j].blue = blue_value;
- j++;
- }
- }
-diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c
-index bf8864bc8a99ee..72558eb877dc65 100644
---- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c
-+++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c
-@@ -186,6 +186,43 @@ bool dcn30_set_input_transfer_func(struct dc *dc,
- return result;
- }
-
-+void dcn30_program_gamut_remap(struct pipe_ctx *pipe_ctx)
-+{
-+ int i = 0;
-+ struct dpp_grph_csc_adjustment dpp_adjust;
-+ struct mpc_grph_gamut_adjustment mpc_adjust;
-+ int mpcc_id = pipe_ctx->plane_res.hubp->inst;
-+ struct mpc *mpc = pipe_ctx->stream_res.opp->ctx->dc->res_pool->mpc;
-+
-+ memset(&dpp_adjust, 0, sizeof(dpp_adjust));
-+ dpp_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;
-+
-+ if (pipe_ctx->plane_state &&
-+ pipe_ctx->plane_state->gamut_remap_matrix.enable_remap == true) {
-+ dpp_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW;
-+ for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++)
-+ dpp_adjust.temperature_matrix[i] =
-+ pipe_ctx->plane_state->gamut_remap_matrix.matrix[i];
-+ }
-+
-+ pipe_ctx->plane_res.dpp->funcs->dpp_set_gamut_remap(pipe_ctx->plane_res.dpp,
-+ &dpp_adjust);
-+
-+ memset(&mpc_adjust, 0, sizeof(mpc_adjust));
-+ mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;
-+
-+ if (pipe_ctx->top_pipe == NULL) {
-+ if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) {
-+ mpc_adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW;
-+ for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++)
-+ mpc_adjust.temperature_matrix[i] =
-+ pipe_ctx->stream->gamut_remap_matrix.matrix[i];
-+ }
-+ }
-+
-+ mpc->funcs->set_gamut_remap(mpc, mpcc_id, &mpc_adjust);
-+}
-+
- bool dcn30_set_output_transfer_func(struct dc *dc,
- struct pipe_ctx *pipe_ctx,
- const struct dc_stream_state *stream)
-diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h
-index a24a8e33a3d289..cb34ca932a5ff8 100644
---- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h
-+++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h
-@@ -58,6 +58,9 @@ bool dcn30_set_blend_lut(struct pipe_ctx *pipe_ctx,
- bool dcn30_set_input_transfer_func(struct dc *dc,
- struct pipe_ctx *pipe_ctx,
- const struct dc_plane_state *plane_state);
-+
-+void dcn30_program_gamut_remap(struct pipe_ctx *pipe_ctx);
-+
- bool dcn30_set_output_transfer_func(struct dc *dc,
- struct pipe_ctx *pipe_ctx,
- const struct dc_stream_state *stream);
-diff --git a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c
-index 257df8660b4caf..81fd50ee97c307 100644
---- a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c
-+++ b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c
-@@ -33,7 +33,7 @@
- #include "dcn301_init.h"
-
- static const struct hw_sequencer_funcs dcn301_funcs = {
-- .program_gamut_remap = dcn10_program_gamut_remap,
-+ .program_gamut_remap = dcn30_program_gamut_remap,
- .init_hw = dcn10_init_hw,
- .power_down_on_boot = dcn10_power_down_on_boot,
- .apply_ctx_to_hw = dce110_apply_ctx_to_hw,
-diff --git a/drivers/gpu/drm/amd/display/include/fixed31_32.h b/drivers/gpu/drm/amd/display/include/fixed31_32.h
-index d4cf7ead1d877e..84da1dd34efd18 100644
---- a/drivers/gpu/drm/amd/display/include/fixed31_32.h
-+++ b/drivers/gpu/drm/amd/display/include/fixed31_32.h
-@@ -69,6 +69,18 @@ static const struct fixed31_32 dc_fixpt_epsilon = { 1LL };
- static const struct fixed31_32 dc_fixpt_half = { 0x80000000LL };
- static const struct fixed31_32 dc_fixpt_one = { 0x100000000LL };
-
-+static inline struct fixed31_32 dc_fixpt_from_s3132(__u64 x)
-+{
-+ struct fixed31_32 val;
-+
-+ /* If negative, convert to 2's complement. */
-+ if (x & (1ULL << 63))
-+ x = -(x & ~(1ULL << 63));
-+
-+ val.value = x;
-+ return val;
-+}
-+
- /*
- * @brief
- * Initialization routines
-diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c
-index dc01c43f61930b..d72c22dcf6855a 100644
---- a/drivers/gpu/drm/arm/malidp_crtc.c
-+++ b/drivers/gpu/drm/arm/malidp_crtc.c
-@@ -221,7 +221,7 @@ static int malidp_crtc_atomic_check_ctm(struct drm_crtc *crtc,
-
- /*
- * The size of the ctm is checked in
-- * drm_atomic_replace_property_blob_from_id.
-+ * drm_property_replace_blob_from_id.
- */
- ctm = (struct drm_color_ctm *)state->ctm->data;
- for (i = 0; i < ARRAY_SIZE(ctm->matrix); ++i) {
-diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
-index c277b198fa3fa2..c3df45f901456b 100644
---- a/drivers/gpu/drm/drm_atomic.c
-+++ b/drivers/gpu/drm/drm_atomic.c
-@@ -733,6 +733,7 @@ static void drm_atomic_plane_print_state(struct drm_printer *p,
- drm_get_color_encoding_name(state->color_encoding));
- 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);
-
- if (plane->funcs->atomic_print_state)
- plane->funcs->atomic_print_state(p, state);
-diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
-index 784e63d70a421e..25bb0859fda74d 100644
---- a/drivers/gpu/drm/drm_atomic_state_helper.c
-+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
-@@ -338,6 +338,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
- state->fence = NULL;
- state->commit = NULL;
- state->fb_damage_clips = NULL;
-+ state->color_mgmt_changed = false;
- }
- EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
-
-diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
-index d867e7f9f2cd58..a6a9ee5086ddb1 100644
---- a/drivers/gpu/drm/drm_atomic_uapi.c
-+++ b/drivers/gpu/drm/drm_atomic_uapi.c
-@@ -362,39 +362,6 @@ static s32 __user *get_out_fence_for_connector(struct drm_atomic_state *state,
- return fence_ptr;
- }
-
--static int
--drm_atomic_replace_property_blob_from_id(struct drm_device *dev,
-- struct drm_property_blob **blob,
-- uint64_t blob_id,
-- ssize_t expected_size,
-- ssize_t expected_elem_size,
-- bool *replaced)
--{
-- struct drm_property_blob *new_blob = NULL;
--
-- if (blob_id != 0) {
-- new_blob = drm_property_lookup_blob(dev, blob_id);
-- if (new_blob == NULL)
-- return -EINVAL;
--
-- if (expected_size > 0 &&
-- new_blob->length != expected_size) {
-- drm_property_blob_put(new_blob);
-- return -EINVAL;
-- }
-- if (expected_elem_size > 0 &&
-- new_blob->length % expected_elem_size != 0) {
-- drm_property_blob_put(new_blob);
-- return -EINVAL;
-- }
-- }
--
-- *replaced |= drm_property_replace_blob(blob, new_blob);
-- drm_property_blob_put(new_blob);
--
-- return 0;
--}
--
- static int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
- struct drm_crtc_state *state, struct drm_property *property,
- uint64_t val)
-@@ -415,7 +382,7 @@ static int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
- } else if (property == config->prop_vrr_enabled) {
- state->vrr_enabled = val;
- } else if (property == config->degamma_lut_property) {
-- ret = drm_atomic_replace_property_blob_from_id(dev,
-+ ret = drm_property_replace_blob_from_id(dev,
- &state->degamma_lut,
- val,
- -1, sizeof(struct drm_color_lut),
-@@ -423,7 +390,7 @@ static int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
- state->color_mgmt_changed |= replaced;
- return ret;
- } else if (property == config->ctm_property) {
-- ret = drm_atomic_replace_property_blob_from_id(dev,
-+ ret = drm_property_replace_blob_from_id(dev,
- &state->ctm,
- val,
- sizeof(struct drm_color_ctm), -1,
-@@ -431,7 +398,7 @@ static int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
- state->color_mgmt_changed |= replaced;
- return ret;
- } else if (property == config->gamma_lut_property) {
-- ret = drm_atomic_replace_property_blob_from_id(dev,
-+ ret = drm_property_replace_blob_from_id(dev,
- &state->gamma_lut,
- val,
- -1, sizeof(struct drm_color_lut),
-@@ -563,7 +530,7 @@ 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 == config->prop_fb_damage_clips) {
-- ret = drm_atomic_replace_property_blob_from_id(dev,
-+ ret = drm_property_replace_blob_from_id(dev,
- &state->fb_damage_clips,
- val,
- -1,
-@@ -729,7 +696,7 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
- if (state->link_status != DRM_LINK_STATUS_GOOD)
- state->link_status = val;
- } else if (property == config->hdr_output_metadata_property) {
-- ret = drm_atomic_replace_property_blob_from_id(dev,
-+ ret = drm_property_replace_blob_from_id(dev,
- &state->hdr_output_metadata,
- val,
- sizeof(struct hdr_output_metadata), -1,
-diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c
-index dfec479830e496..f72ef6493340a7 100644
---- a/drivers/gpu/drm/drm_property.c
-+++ b/drivers/gpu/drm/drm_property.c
-@@ -751,6 +751,55 @@ bool drm_property_replace_blob(struct drm_property_blob **blob,
- }
- EXPORT_SYMBOL(drm_property_replace_blob);
-
-+/**
-+ * drm_property_replace_blob_from_id - replace a blob property taking a reference
-+ * @dev: DRM device
-+ * @blob: a pointer to the member blob to be replaced
-+ * @blob_id: the id of the new blob to replace with
-+ * @expected_size: expected size of the blob property
-+ * @expected_elem_size: expected size of an element in the blob property
-+ * @replaced: if the blob was in fact replaced
-+ *
-+ * Look up the new blob from id, take its reference, check expected sizes of
-+ * the blob and its element and replace the old blob by the new one. Advertise
-+ * if the replacement operation was successful.
-+ *
-+ * Return: true if the blob was in fact replaced. -EINVAL if the new blob was
-+ * not found or sizes don't match.
-+ */
-+int drm_property_replace_blob_from_id(struct drm_device *dev,
-+ struct drm_property_blob **blob,
-+ uint64_t blob_id,
-+ ssize_t expected_size,
-+ ssize_t expected_elem_size,
-+ bool *replaced)
-+{
-+ struct drm_property_blob *new_blob = NULL;
-+
-+ if (blob_id != 0) {
-+ new_blob = drm_property_lookup_blob(dev, blob_id);
-+ if (new_blob == NULL)
-+ return -EINVAL;
-+
-+ if (expected_size > 0 &&
-+ new_blob->length != expected_size) {
-+ drm_property_blob_put(new_blob);
-+ return -EINVAL;
-+ }
-+ if (expected_elem_size > 0 &&
-+ new_blob->length % expected_elem_size != 0) {
-+ drm_property_blob_put(new_blob);
-+ return -EINVAL;
-+ }
-+ }
-+
-+ *replaced |= drm_property_replace_blob(blob, new_blob);
-+ drm_property_blob_put(new_blob);
-+
-+ return 0;
-+}
-+EXPORT_SYMBOL(drm_property_replace_blob_from_id);
-+
- int drm_mode_getblob_ioctl(struct drm_device *dev,
- void *data, struct drm_file *file_priv)
- {
-diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h
-index 912f1e4156853f..08d7a7f0188fea 100644
---- a/include/drm/drm_mode_object.h
-+++ b/include/drm/drm_mode_object.h
-@@ -60,7 +60,7 @@ struct drm_mode_object {
- void (*free_cb)(struct kref *kref);
- };
-
--#define DRM_OBJECT_MAX_PROPERTY 24
-+#define DRM_OBJECT_MAX_PROPERTY 64
- /**
- * struct drm_object_properties - property tracking for &drm_mode_object
- */
-diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
-index 51291983ea445d..52c3287da0daad 100644
---- a/include/drm/drm_plane.h
-+++ b/include/drm/drm_plane.h
-@@ -237,6 +237,13 @@ struct drm_plane_state {
-
- /** @state: backpointer to global drm_atomic_state */
- struct drm_atomic_state *state;
-+
-+ /**
-+ * @color_mgmt_changed: Color management properties have changed. Used
-+ * by the atomic helpers and drivers to steer the atomic commit control
-+ * flow.
-+ */
-+ bool color_mgmt_changed : 1;
- };
-
- static inline struct drm_rect
-diff --git a/include/drm/drm_property.h b/include/drm/drm_property.h
-index 65bc9710a47029..082f29156b3e3f 100644
---- a/include/drm/drm_property.h
-+++ b/include/drm/drm_property.h
-@@ -279,6 +279,12 @@ struct drm_property_blob *drm_property_create_blob(struct drm_device *dev,
- const void *data);
- struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev,
- uint32_t id);
-+int drm_property_replace_blob_from_id(struct drm_device *dev,
-+ struct drm_property_blob **blob,
-+ uint64_t blob_id,
-+ ssize_t expected_size,
-+ ssize_t expected_elem_size,
-+ bool *replaced);
- int drm_property_replace_global_blob(struct drm_device *dev,
- struct drm_property_blob **replace,
- size_t length,
-diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
-index 43691058d28fb8..23fc194009980f 100644
---- a/include/uapi/drm/drm_mode.h
-+++ b/include/uapi/drm/drm_mode.h
-@@ -843,6 +843,14 @@ struct drm_color_ctm {
- __u64 matrix[9];
- };
-
-+struct drm_color_ctm2 {
-+ /*
-+ * Conversion matrix in S31.32 sign-magnitude
-+ * (not two's complement!) format.
-+ */
-+ __u64 matrix[12];
-+};
-+
- struct drm_color_lut {
- /*
- * Values are mapped linearly to 0.0 - 1.0 range, with 0x0 == 0.0 and
-