diff mbox series

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

Message ID 20241003200129.1732122-43-harry.wentland@amd.com (mailing list archive)
State New, archived
Headers show
Series Color Pipeline API w/ VKMS | expand

Commit Message

Harry Wentland Oct. 3, 2024, 8:01 p.m. UTC
From: Alex Hung <alex.hung@amd.com>

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

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

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

Comments

Simon Ser Oct. 13, 2024, 3:58 p.m. UTC | #1
On Thursday, October 3rd, 2024 at 22:01, Harry Wentland <harry.wentland@amd.com> wrote:

> From: Alex Hung <alex.hung@amd.com>
> 
> It is to be used to enable HDR by allowing userpace to create and pass
> 3D LUTs to kernel and hardware.
> 
> 1. new drm_colorop_type: DRM_COLOROP_3D_LUT.
> 2. 3D LUT modes define hardware capabilities to userspace applications.
> 3. mode index points to current 3D LUT mode in lut_3d_modes.

Do we really need all of this complexity here?

User-space needs to copy over its 3D LUT to the KMS blob. Kernel needs to
copy from the KMS blob when programming hardware. Why do we need a list of
different modes with negotiation?

I've heard that some of this complexity has been introduced to add in the
future BO-backed LUTs. That would be a nice addition, but it's not here
right now, so how can we design for that case when we haven't actually tried
implementing it and made sure it actually works in practice?

It would be easy to introduce "modes" (or something different) when the
BO-based 3D LUT uAPI is introduced. There are many ways to handle backwards
compatibility: new properties can have their defaults set to the previously
fixed format/swizzle/etc, a new colorop can be introduced if there are
too many conflicts, and worst case new functionality can be gated behind a
DRM cap (although I don't think we'd need to resort to this here).

I'd recommend just having one fixed supported format, like we have for
1D LUTs. We can have a read-only props for the size and the color depth,
as well as a read-write prop for the data blob.

> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> index 5ef87cb5b242..290c2e32f692 100644
> --- a/include/uapi/drm/drm_mode.h
> +++ b/include/uapi/drm/drm_mode.h
> @@ -913,6 +913,90 @@ enum drm_colorop_type {
>  	 * property.
>  	 */
>  	DRM_COLOROP_MULTIPLIER,
> +	/**
> +	 * @DRM_COLOROP_3D_LUT:
> +	 *
> +	 * A 3D LUT of &drm_color_lut entries,
> +	 * packed into a blob via the DATA property. The driver's expected
> +	 * LUT size is advertised via the SIZE property.
> +	 */
> +	DRM_COLOROP_3D_LUT,

User-space docs are missing many details I believe.

> +};
> +
> +/**
> + * enum drm_colorop_lut3d_interpolation_type - type of 3DLUT interpolation
> + *
> + */
> +enum drm_colorop_lut3d_interpolation_type {
> +	/**
> +	 * @DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL:
> +	 *
> +	 * Tetrahedral 3DLUT interpolation
> +	 */
> +	DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL,
> +};
> +
> +/**
> + * enum drm_colorop_lut3d_traversal_order - traversal order of the 3D LUT
> + *
> + * This enum describes the order of traversal of 3DLUT elements.
> + */
> +enum drm_colorop_lut3d_traversal_order {
> +	/**
> +	 * @DRM_COLOROP_LUT3D_TRAVERSAL_RGB:
> +	 *
> +	 * the LUT elements are traversed like so:
> +	 *   for R in range 0..n
> +	 *     for G in range 0..n
> +	 *       for B in range 0..n
> +	 *         color = lut3d[R][G][B]
> +	 */
> +	DRM_COLOROP_LUT3D_TRAVERSAL_RGB,
> +	/**
> +	 * @DRM_COLOROP_LUT3D_TRAVERSAL_BGR:
> +	 *
> +	 * the LUT elements are traversed like so:
> +	 *   for R in range 0..n
> +	 *     for G in range 0..n
> +	 *       for B in range 0..n
> +	 *         color = lut3d[B][G][R]
> +	 */
> +	DRM_COLOROP_LUT3D_TRAVERSAL_BGR,
> +};
> +
> +/**
> + * struct drm_mode_3dlut_mode - 3D LUT mode
> + *
> + * The mode describes the supported and selected format of a 3DLUT.
> + */
> +struct drm_mode_3dlut_mode {
> +	/**
> +	 * @lut_size: 3D LUT size - can be 9, 17 or 33
> +	 */
> +	__u16 lut_size;

Are "9, 17 or 33" just example values? Or does the kernel actually guarantee
that the advertised size can never be something else? It doesn't seem like
there is a check, and enforcing it would hinder extensibility (adding new
values would be a breaking uAPI change).

> +	/**
> +	 * @lut_stride: dimensions of 3D LUT. Must be larger than lut_size
> +	 */
> +	__u16 lut_stride[3];

It sounds a bit weird to have the driver dictate the stride which must be used.
Usually user-space picks and sends the stride to the driver.

> +	/**
> +	 * @interpolation: interpolation algorithm for 3D LUT. See drm_colorop_lut3d_interpolation_type
> +	 */
> +	__u16 interpolation;
> +	/**
> +	 * @color_depth: color depth - can be 8, 10 or 12
> +	 */
> +	__u16 color_depth;

Ditto: reading these docs, user-space might not handle any other value.

It would be nice to better explain what this means exactly. Are the output
color values truncated at this depth? Or are the LUT values truncated? Or
something else?

> +	/**
> +	 * @color_format: color format specified by fourcc values
> +	 * ex. DRM_FORMAT_XRGB16161616 - color in order of RGB, each is 16bit.
> +	 */
> +	__u32 color_format;

Do we really need to support many different formats?

User-space needs to perform a copy to the KMS blob anyways, so can easily
convert to an arbitrary format while at it.

Is there a use-case that I'm missing?

> +	/**
> +	 * @traversal_order:
> +	 *
> +	 * Traversal order when parsing/writing the 3D LUT. See enum drm_colorop_lut3d_traversal_order
> +	 */
> +	 __u16 traversal_order;

DRM formats usually have variants for all of the supported/desirable swizzles.
For instance we have DRM_FORMAT_XRGB16161616F and DRM_FORMAT_XBGR16161616F.
Can't see why we couldn't add more if we need to.
Alex Hung Oct. 18, 2024, 8:23 p.m. UTC | #2
On 10/13/24 09:58, Simon Ser wrote:
> On Thursday, October 3rd, 2024 at 22:01, Harry Wentland <harry.wentland@amd.com> wrote:
> 
>> From: Alex Hung <alex.hung@amd.com>
>>
>> It is to be used to enable HDR by allowing userpace to create and pass
>> 3D LUTs to kernel and hardware.
>>
>> 1. new drm_colorop_type: DRM_COLOROP_3D_LUT.
>> 2. 3D LUT modes define hardware capabilities to userspace applications.
>> 3. mode index points to current 3D LUT mode in lut_3d_modes.
> 
> Do we really need all of this complexity here?
> 
> User-space needs to copy over its 3D LUT to the KMS blob. Kernel needs to
> copy from the KMS blob when programming hardware. Why do we need a list of
> different modes with negotiation?
> 
> I've heard that some of this complexity has been introduced to add in the
> future BO-backed LUTs. That would be a nice addition, but it's not here
> right now, so how can we design for that case when we haven't actually tried
> implementing it and made sure it actually works in practice?
> 
> It would be easy to introduce "modes" (or something different) when the
> BO-based 3D LUT uAPI is introduced. There are many ways to handle backwards
> compatibility: new properties can have their defaults set to the previously
> fixed format/swizzle/etc, a new colorop can be introduced if there are
> too many conflicts, and worst case new functionality can be gated behind a
> DRM cap (although I don't think we'd need to resort to this here).
> 
> I'd recommend just having one fixed supported format, like we have for
> 1D LUTs. We can have a read-only props for the size and the color depth,
> as well as a read-write prop for the data blob.
> 

That's a good point. I will simplify DRM_COLOROP_3D_LUT implementation 
and remove 3D LUT mode. It can be used for future BO-based 3D LUT if 
necessary

In summary:

The attributes to be kept now: lut_size, interpolation and color_depth. 
The rests can be fixed and documented for userspace.

>> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
>> index 5ef87cb5b242..290c2e32f692 100644
>> --- a/include/uapi/drm/drm_mode.h
>> +++ b/include/uapi/drm/drm_mode.h
>> @@ -913,6 +913,90 @@ enum drm_colorop_type {
>>   	 * property.
>>   	 */
>>   	DRM_COLOROP_MULTIPLIER,
>> +	/**
>> +	 * @DRM_COLOROP_3D_LUT:
>> +	 *
>> +	 * A 3D LUT of &drm_color_lut entries,
>> +	 * packed into a blob via the DATA property. The driver's expected
>> +	 * LUT size is advertised via the SIZE property.
>> +	 */
>> +	DRM_COLOROP_3D_LUT,
> 
> User-space docs are missing many details I believe.
> 
>> +};
>> +
>> +/**
>> + * enum drm_colorop_lut3d_interpolation_type - type of 3DLUT interpolation
>> + *
>> + */
>> +enum drm_colorop_lut3d_interpolation_type {
>> +	/**
>> +	 * @DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL:
>> +	 *
>> +	 * Tetrahedral 3DLUT interpolation
>> +	 */
>> +	DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL,
>> +};
>> +
>> +/**
>> + * enum drm_colorop_lut3d_traversal_order - traversal order of the 3D LUT
>> + *
>> + * This enum describes the order of traversal of 3DLUT elements.
>> + */
>> +enum drm_colorop_lut3d_traversal_order {
>> +	/**
>> +	 * @DRM_COLOROP_LUT3D_TRAVERSAL_RGB:
>> +	 *
>> +	 * the LUT elements are traversed like so:
>> +	 *   for R in range 0..n
>> +	 *     for G in range 0..n
>> +	 *       for B in range 0..n
>> +	 *         color = lut3d[R][G][B]
>> +	 */
>> +	DRM_COLOROP_LUT3D_TRAVERSAL_RGB,
>> +	/**
>> +	 * @DRM_COLOROP_LUT3D_TRAVERSAL_BGR:
>> +	 *
>> +	 * the LUT elements are traversed like so:
>> +	 *   for R in range 0..n
>> +	 *     for G in range 0..n
>> +	 *       for B in range 0..n
>> +	 *         color = lut3d[B][G][R]
>> +	 */
>> +	DRM_COLOROP_LUT3D_TRAVERSAL_BGR,
>> +};
>> +
>> +/**
>> + * struct drm_mode_3dlut_mode - 3D LUT mode
>> + *
>> + * The mode describes the supported and selected format of a 3DLUT.
>> + */
>> +struct drm_mode_3dlut_mode {
>> +	/**
>> +	 * @lut_size: 3D LUT size - can be 9, 17 or 33
>> +	 */
>> +	__u16 lut_size;
> 
> Are "9, 17 or 33" just example values? Or does the kernel actually guarantee
> that the advertised size can never be something else? It doesn't seem like
> there is a check, and enforcing it would hinder extensibility (adding new
> values would be a breaking uAPI change).
> 
>> +	/**
>> +	 * @lut_stride: dimensions of 3D LUT. Must be larger than lut_size
>> +	 */
>> +	__u16 lut_stride[3];
> 
> It sounds a bit weird to have the driver dictate the stride which must be used.
> Usually user-space picks and sends the stride to the driver.
> 
>> +	/**
>> +	 * @interpolation: interpolation algorithm for 3D LUT. See drm_colorop_lut3d_interpolation_type
>> +	 */
>> +	__u16 interpolation;
>> +	/**
>> +	 * @color_depth: color depth - can be 8, 10 or 12
>> +	 */
>> +	__u16 color_depth;
> 
> Ditto: reading these docs, user-space might not handle any other value.
> 
> It would be nice to better explain what this means exactly. Are the output
> color values truncated at this depth? Or are the LUT values truncated? Or
> something else?
> 
>> +	/**
>> +	 * @color_format: color format specified by fourcc values
>> +	 * ex. DRM_FORMAT_XRGB16161616 - color in order of RGB, each is 16bit.
>> +	 */
>> +	__u32 color_format;
> 
> Do we really need to support many different formats?
> 
> User-space needs to perform a copy to the KMS blob anyways, so can easily
> convert to an arbitrary format while at it.
> 
> Is there a use-case that I'm missing?
> 
>> +	/**
>> +	 * @traversal_order:
>> +	 *
>> +	 * Traversal order when parsing/writing the 3D LUT. See enum drm_colorop_lut3d_traversal_order
>> +	 */
>> +	 __u16 traversal_order;
> 
> DRM formats usually have variants for all of the supported/desirable swizzles.
> For instance we have DRM_FORMAT_XRGB16161616F and DRM_FORMAT_XBGR16161616F.
> Can't see why we couldn't add more if we need to.
diff mbox series

Patch

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 29f8a8f402f2..58f0ef8b2d1d 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -783,6 +783,9 @@  static void drm_atomic_colorop_print_state(struct drm_printer *p,
 		const struct drm_colorop_state *state)
 {
 	struct drm_colorop *colorop = state->colorop;
+	struct drm_property_blob *modes = state->lut_3d_modes;
+	struct drm_mode_3dlut_mode *mode_3dlut;
+	int i;
 
 	drm_printf(p, "colorop[%u]:\n", colorop->base.id);
 	drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type));
@@ -805,6 +808,24 @@  static void drm_atomic_colorop_print_state(struct drm_printer *p,
 	case DRM_COLOROP_MULTIPLIER:
 		drm_printf(p, "\tmultiplier=%llu\n", state->multiplier);
 		break;
+	case DRM_COLOROP_3D_LUT:
+		mode_3dlut = (struct drm_mode_3dlut_mode *) modes->data;
+
+		drm_printf(p, "\tlut_3d_modes blob id=%d\n", modes ? modes->base.id : 0);
+		for (i = 0; i < modes->length / sizeof(struct drm_mode_3dlut_mode); i++) {
+			drm_printf(p, "\t  lut_size=%d\n", mode_3dlut[i].lut_size);
+			drm_printf(p, "\t  lut_strides=%d %d %d\n", mode_3dlut[i].lut_stride[0],
+								    mode_3dlut[i].lut_stride[1],
+								    mode_3dlut[i].lut_stride[2]);
+			drm_printf(p, "\t  interpolation=%s\n",
+				   drm_get_colorop_lut3d_interpolation_name(mode_3dlut[i].interpolation));
+			drm_printf(p, "\t  color_depth=%d\n", mode_3dlut[i].color_depth);
+			drm_printf(p, "\t  color_format=%X\n", mode_3dlut[i].color_format);
+			drm_printf(p, "\t  traversal_order=%X\n", mode_3dlut[i].traversal_order);
+		}
+		drm_printf(p, "\tlut_3d_mode_index=%d\n", state->lut_3d_mode_index);
+		drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0);
+		break;
 	default:
 		break;
 	}
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index 57029e5938f6..e978b1bf021f 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -695,9 +695,11 @@  static int drm_atomic_color_set_data_property(struct drm_colorop *colorop,
 		struct drm_colorop_state *state,
 		struct drm_property *property, uint64_t val)
 {
+	struct drm_mode_3dlut_mode *modes;
 	ssize_t elem_size = -1;
 	ssize_t size = -1;
 	bool replaced = false;
+	uint32_t index;
 
 	switch (colorop->type) {
 	case DRM_COLOROP_1D_LUT:
@@ -706,6 +708,15 @@  static int drm_atomic_color_set_data_property(struct drm_colorop *colorop,
 	case DRM_COLOROP_CTM_3X4:
 		size = sizeof(struct drm_color_ctm_3x4);
 		break;
+	case DRM_COLOROP_3D_LUT:
+		index = state->lut_3d_mode_index;
+		if (index >= (state->lut_3d_modes->length / sizeof(struct drm_mode_3dlut_mode)))
+			return -EINVAL;
+
+		modes = (struct drm_mode_3dlut_mode *) state->lut_3d_modes->data;
+		size = modes[index].lut_stride[0] * modes[index].lut_stride[1] * modes[index].lut_stride[2] *
+		       sizeof(struct drm_color_lut);
+		break;
 	default:
 		/* should never get here */
 		return -EINVAL;
@@ -729,6 +740,8 @@  static int drm_atomic_colorop_set_property(struct drm_colorop *colorop,
 		state->curve_1d_type = val;
 	} else if (property == colorop->multiplier_property) {
 		state->multiplier = val;
+	} else if (property == colorop->lut_3d_mode_index_property) {
+		state->lut_3d_mode_index = val;
 	} else if (property == colorop->data_property) {
 		return drm_atomic_color_set_data_property(colorop,
 					state, property, val);
@@ -756,6 +769,10 @@  drm_atomic_colorop_get_property(struct drm_colorop *colorop,
 		*val = state->curve_1d_type;
 	} else if (property == colorop->multiplier_property) {
 		*val = state->multiplier;
+	} else if (property == colorop->lut_3d_modes_property) {
+		*val = (state->lut_3d_modes) ? state->lut_3d_modes->base.id : 0;
+	} else if (property == colorop->lut_3d_mode_index_property) {
+		*val = state->lut_3d_mode_index;
 	} else if (property == colorop->size_property) {
 		*val = state->size;
 	} else if (property == colorop->data_property) {
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index df0266734639..fd1cd934df48 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -66,6 +66,7 @@  static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = {
 	{ DRM_COLOROP_1D_LUT, "1D Curve Custom LUT" },
 	{ DRM_COLOROP_CTM_3X4, "3x4 Matrix"},
 	{ DRM_COLOROP_MULTIPLIER, "Multiplier"},
+	{ DRM_COLOROP_3D_LUT, "3D LUT"},
 };
 
 static const char * const colorop_curve_1d_type_names[] = {
@@ -348,6 +349,53 @@  int drm_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop,
 }
 EXPORT_SYMBOL(drm_colorop_mult_init);
 
+int drm_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *colorop,
+			   struct drm_plane *plane, struct drm_mode_3dlut_mode *mode_3dlut,
+			   size_t num, bool allow_bypass)
+{
+	struct drm_property_blob *blob;
+	struct drm_property *prop;
+	int ret;
+
+	ret = drm_colorop_init(dev, colorop, plane, DRM_COLOROP_3D_LUT, allow_bypass);
+	if (ret)
+		return ret;
+
+	/* lut_3d_modes */
+	prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, "3DLUT_MODES", 0);
+	if (!prop)
+		return -ENOMEM;
+
+	colorop->lut_3d_modes_property = prop;
+
+
+	blob = drm_property_create_blob(colorop->dev, num * sizeof(struct drm_mode_3dlut_mode),
+					mode_3dlut);
+	if (IS_ERR(blob))
+		return PTR_ERR(blob);
+
+	drm_object_attach_property(&colorop->base, colorop->lut_3d_modes_property, blob ? blob->base.id : 0);
+	drm_colorop_reset(colorop);
+
+	drm_property_replace_blob(&colorop->state->lut_3d_modes, blob);
+
+	/* lut_3d_modes index */
+	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, "3DLUT_MODE_INDEX", 0, num - 1);
+	if (!prop)
+		return -ENOMEM;
+
+	colorop->lut_3d_mode_index_property = prop;
+	drm_object_attach_property(&colorop->base, colorop->lut_3d_mode_index_property, 0);
+
+	/* data */
+	ret = drm_colorop_create_data_prop(dev, colorop);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_colorop_3dlut_init);
+
 static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop,
 							struct drm_colorop_state *state)
 {
@@ -440,7 +488,13 @@  static const char * const colorop_type_name[] = {
 	[DRM_COLOROP_1D_LUT] = "1D Curve Custom LUT",
 	[DRM_COLOROP_CTM_3X4] = "3x4 Matrix",
 	[DRM_COLOROP_MULTIPLIER] = "Multiplier",
+	[DRM_COLOROP_3D_LUT] = "3D LUT",
 };
+
+static const char * const colorop_lu3d_interpolation_name[] = {
+	[DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL] = "Tetrahedral",
+};
+
 static const char * const colorop_lut1d_interpolation_name[] = {
 	[DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR] = "Linear",
 };
@@ -476,6 +530,21 @@  const char *drm_get_colorop_lut1d_interpolation_name(enum drm_colorop_lut1d_inte
 	return colorop_lut1d_interpolation_name[type];
 }
 
+/**
+ * drm_get_colorop_lut3d_interpolation_name - return a string for interpolation type
+ * @type: interpolation type to compute name of
+ *
+ * In contrast to the other drm_get_*_name functions this one here returns a
+ * const pointer and hence is threadsafe.
+ */
+const char *drm_get_colorop_lut3d_interpolation_name(enum drm_colorop_lut3d_interpolation_type type)
+{
+	if (WARN_ON(type >= ARRAY_SIZE(colorop_lu3d_interpolation_name)))
+		return "unknown";
+
+	return colorop_lu3d_interpolation_name[type];
+}
+
 /**
  * drm_colorop_set_next_property - sets the next pointer
  * @colorop: drm colorop
diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h
index b8c1c4da3444..bf5117f30c80 100644
--- a/include/drm/drm_colorop.h
+++ b/include/drm/drm_colorop.h
@@ -147,6 +147,29 @@  struct drm_colorop_state {
 	 */
 	uint32_t size;
 
+	/**
+	 * @lut_3d_modes:
+	 *
+	 * Mode blob for displaying a list of supported 3dlut modes.
+	 *
+	 * To setup a 3D LUT, lut_3d_modes, lut_3d_modes and data are expected
+	 * to be used in the following sequence:
+	 *
+	 *  1. device driver sets a list of supported drm_mode_3dlut_mode in "lut_3d_modes".
+	 *  2. userspace reads "lut_3d_modes" to determines an appropriate mode.
+	 *  3. userspace sets "lut_3d_mode_index" pointing the selected mode.
+	 *  4. userspace passes a 3D LUT via "data"
+	 *  5. usersapce commits to device driver
+	 */
+	struct drm_property_blob *lut_3d_modes;
+
+	/**
+	 * @lut_3d_mode_index:
+	 *
+	 * A zero-based index pointing to current lut_3d_mode.
+	 */
+	uint16_t lut_3d_mode_index;
+
 	/**
 	 * @data:
 	 *
@@ -288,6 +311,21 @@  struct drm_colorop {
 	 */
 	struct drm_property *size_property;
 
+	/**
+	 * @lut_3d_modes_property:
+	 *
+	 * 3DLUT mode property used to convert the framebuffer's colors
+	 * to non-linear gamma.
+	 */
+	struct drm_property *lut_3d_modes_property;
+
+	/**
+	 * @lut_3d_mode_index_property:
+	 *
+	 * 3DLUT mode index property for choosing 3D LUT mode.
+	 */
+	struct drm_property *lut_3d_mode_index_property;
+
 	/**
 	 * @data_property:
 	 *
@@ -343,6 +381,9 @@  int drm_colorop_ctm_3x4_init(struct drm_device *dev, struct drm_colorop *colorop
 			     struct drm_plane *plane, bool allow_bypass);
 int drm_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop,
 			      struct drm_plane *plane, bool allow_bypass);
+int drm_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *colorop,
+			   struct drm_plane *plane, struct drm_mode_3dlut_mode *mode_3dlut,
+			   size_t num, bool allow_bypass);
 
 struct drm_colorop_state *
 drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop);
@@ -393,6 +434,7 @@  const char *drm_get_colorop_type_name(enum drm_colorop_type type);
  */
 const char *drm_get_colorop_curve_1d_type_name(enum drm_colorop_curve_1d_type type);
 const char *drm_get_colorop_lut1d_interpolation_name(enum drm_colorop_lut1d_interpolation_type type);
+const char *drm_get_colorop_lut3d_interpolation_name(enum drm_colorop_lut3d_interpolation_type type);
 
 void drm_colorop_set_next_property(struct drm_colorop *colorop, struct drm_colorop *next);
 
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 5ef87cb5b242..290c2e32f692 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -913,6 +913,90 @@  enum drm_colorop_type {
 	 * property.
 	 */
 	DRM_COLOROP_MULTIPLIER,
+	/**
+	 * @DRM_COLOROP_3D_LUT:
+	 *
+	 * A 3D LUT of &drm_color_lut entries,
+	 * packed into a blob via the DATA property. The driver's expected
+	 * LUT size is advertised via the SIZE property.
+	 */
+	DRM_COLOROP_3D_LUT,
+};
+
+/**
+ * enum drm_colorop_lut3d_interpolation_type - type of 3DLUT interpolation
+ *
+ */
+enum drm_colorop_lut3d_interpolation_type {
+	/**
+	 * @DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL:
+	 *
+	 * Tetrahedral 3DLUT interpolation
+	 */
+	DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL,
+};
+
+/**
+ * enum drm_colorop_lut3d_traversal_order - traversal order of the 3D LUT
+ *
+ * This enum describes the order of traversal of 3DLUT elements.
+ */
+enum drm_colorop_lut3d_traversal_order {
+	/**
+	 * @DRM_COLOROP_LUT3D_TRAVERSAL_RGB:
+	 *
+	 * the LUT elements are traversed like so:
+	 *   for R in range 0..n
+	 *     for G in range 0..n
+	 *       for B in range 0..n
+	 *         color = lut3d[R][G][B]
+	 */
+	DRM_COLOROP_LUT3D_TRAVERSAL_RGB,
+	/**
+	 * @DRM_COLOROP_LUT3D_TRAVERSAL_BGR:
+	 *
+	 * the LUT elements are traversed like so:
+	 *   for R in range 0..n
+	 *     for G in range 0..n
+	 *       for B in range 0..n
+	 *         color = lut3d[B][G][R]
+	 */
+	DRM_COLOROP_LUT3D_TRAVERSAL_BGR,
+};
+
+/**
+ * struct drm_mode_3dlut_mode - 3D LUT mode
+ *
+ * The mode describes the supported and selected format of a 3DLUT.
+ */
+struct drm_mode_3dlut_mode {
+	/**
+	 * @lut_size: 3D LUT size - can be 9, 17 or 33
+	 */
+	__u16 lut_size;
+	/**
+	 * @lut_stride: dimensions of 3D LUT. Must be larger than lut_size
+	 */
+	__u16 lut_stride[3];
+	/**
+	 * @interpolation: interpolation algorithm for 3D LUT. See drm_colorop_lut3d_interpolation_type
+	 */
+	__u16 interpolation;
+	/**
+	 * @color_depth: color depth - can be 8, 10 or 12
+	 */
+	__u16 color_depth;
+	/**
+	 * @color_format: color format specified by fourcc values
+	 * ex. DRM_FORMAT_XRGB16161616 - color in order of RGB, each is 16bit.
+	 */
+	__u32 color_format;
+	/**
+	 * @traversal_order:
+	 *
+	 * Traversal order when parsing/writing the 3D LUT. See enum drm_colorop_lut3d_traversal_order
+	 */
+	 __u16 traversal_order;
 };
 
 /**