2043 lines
70 KiB
Diff
2043 lines
70 KiB
Diff
|
From af60f9afa522f5f337d9b4e24eef1fdcd0ab6c05 Mon Sep 17 00:00:00 2001
|
||
|
From: Peter Jung <admin@ptr1337.dev>
|
||
|
Date: Mon, 11 Sep 2023 14:31:43 +0200
|
||
|
Subject: [PATCH 1/7] amd-hdr
|
||
|
|
||
|
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_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 +
|
||
|
19 files changed, 1441 insertions(+), 90 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
|
||
|
index 32fe05c810c6..84bf501b02f4 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 34f011cedd06..fb3400eff0b6 100644
|
||
|
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
||
|
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
||
|
@@ -4021,6 +4021,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);
|
||
|
@@ -5093,7 +5098,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;
|
||
|
|
||
|
@@ -8113,6 +8120,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,
|
||
|
@@ -8324,6 +8335,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;
|
||
|
@@ -9512,6 +9527,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)
|
||
|
@@ -9579,6 +9595,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;
|
||
|
@@ -9615,6 +9635,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 9e4cc5eeda76..24c87f425afb 100644
|
||
|
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
|
||
|
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
|
||
|
@@ -33,6 +33,8 @@
|
||
|
#include <drm/drm_plane.h>
|
||
|
#include "link_service_types.h"
|
||
|
|
||
|
+#define AMDGPU_HDR_MULT_DEFAULT (0x100000000LL)
|
||
|
+
|
||
|
/*
|
||
|
* This file contains the definition for amdgpu_display_manager
|
||
|
* and its API for amdgpu driver's use.
|
||
|
@@ -716,9 +718,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 {
|
||
|
@@ -743,6 +827,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)
|
||
|
@@ -804,14 +896,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 a4cb23d059bd..0442eeaa9763 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,
|
||
|
°amma_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,
|
||
|
+ °amma_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 97b7a0b8a1c2..a05c210754d4 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
|
||
|
@@ -260,6 +260,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 */
|
||
|
@@ -296,6 +297,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,
|
||
|
@@ -314,6 +379,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)
|
||
|
@@ -489,6 +558,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 cc74dd69acf2..17719e15cbe5 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
|
||
|
@@ -1333,8 +1333,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 *
|
||
|
@@ -1354,6 +1360,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;
|
||
|
}
|
||
|
|
||
|
@@ -1421,12 +1443,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,
|
||
|
@@ -1435,6 +1648,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,
|
||
|
@@ -1514,6 +1731,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 3538973bd0c6..04b2e04b68f3 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 255713ec29bb..fce9b33c0f88 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 ce19c54097f8..e557e2b98618 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 61205cdbe2d5..fdbe3d42cd7b 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 d4cf7ead1d87..84da1dd34efd 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 dc01c43f6193..d72c22dcf685 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 c277b198fa3f..c3df45f90145 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 784e63d70a42..25bb0859fda7 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_property.c b/drivers/gpu/drm/drm_property.c
|
||
|
index dfec479830e4..f72ef6493340 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 912f1e415685..08d7a7f0188f 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 79d62856defb..4f87803b3ea1 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 65bc9710a470..082f29156b3e 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 ea1b639bcb28..cea5653e4020 100644
|
||
|
--- a/include/uapi/drm/drm_mode.h
|
||
|
+++ b/include/uapi/drm/drm_mode.h
|
||
|
@@ -846,6 +846,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
|
||
|
--
|
||
|
2.43.0.rc2
|
||
|
|