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 7094578a683f..10e6dc5c8552 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -5656,6 +5656,19 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev,
 		dc_plane_state->in_transfer_func->type = TF_TYPE_HWPWL;
 	}
 
+	/* 3D LUT from userspace */
+	if (plane_state->color_mgmt_changed) {
+		if (plane_state->lut_3d && dc_plane_state->lut3d_func) {
+			ret = amdgpu_dm_fill_3dlut_data(plane_state, &dc_plane_state->lut3d_func->lut_3d);
+			if (!ret)
+				dc_plane_state->lut3d_func->state.bits.initialized = 1;
+			else
+				return ret;
+		} else {
+			/* TODO disable 3D LUT */
+		}
+	}
+
 	return 0;
 }
 
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 667957087ccf..644c5ff6ee9a 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -726,6 +726,7 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
 				      struct dc_plane_state *dc_plane_state);
 
 void amdgpu_dm_fill_pwl_data(struct drm_property_blob *lut_blob, struct pwl_params *lut_params, struct drm_color_lut_range *pwl_definition, int pwl_size);
+int amdgpu_dm_fill_3dlut_data(const struct drm_plane_state *plane_state, struct tetrahedral_params *param);
 void amdgpu_dm_update_connector_after_detect(
 		struct amdgpu_dm_connector *aconnector);
 
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 ae633fe52525..705852bf63e7 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
@@ -22,6 +22,7 @@
  * Authors: AMD
  *
  */
+ #include <linux/videodev2.h>
 #include "amdgpu.h"
 #include "amdgpu_mode.h"
 #include "amdgpu_dm.h"
@@ -469,6 +470,186 @@ int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state)
 	return 0;
 }
 
+#define R_3DLUT	0
+#define G_3DLUT	1
+#define B_3DLUT	2
+
+static __u16 extract_rgb_value(void *lut_3d, __u32 color_format, __u8 color)
+{
+	__u64 val = *(__u64 *) lut_3d;
+
+	switch (color_format) {
+	case DRM_FORMAT_XRGB16161616:
+		if (color == R_3DLUT)
+			return val & 0xFFFF;
+		else if (color == G_3DLUT)
+			return (val >> 16) & 0xFFFF;
+		else if (color == B_3DLUT)
+			return (val >> 32) & 0xFFFF;
+		break;
+	case DRM_FORMAT_XBGR16161616:
+		if (color == B_3DLUT)
+			return val & 0xFFFF;
+		else if (color == G_3DLUT)
+			return (val >> 16) & 0xFFFF;
+		else if (color == R_3DLUT)
+			return (val >> 32) & 0xFFFF;
+		break;
+	case DRM_FORMAT_XRGB8888:
+		if (color == R_3DLUT)
+			return val & 0xFF;
+		else if (color == G_3DLUT)
+			return (val >> 8) & 0xFF;
+		else if (color == B_3DLUT)
+			return (val >> 16) & 0xFF;
+		break;
+	case DRM_FORMAT_XBGR8888:
+		if (color == B_3DLUT)
+			return val & 0xFF;
+		else if (color == G_3DLUT)
+			return (val >> 8) & 0xFF;
+		else if (color == R_3DLUT)
+			return (val >> 16) & 0xFF;
+			break;
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
+static bool extract_rgb_data(const struct drm_plane_state *plane_state, struct drm_mode_3dlut_mode *mode, __u16 *lut_data)
+{
+	__u16 i, lut_volume;
+	void *lut_3d = plane_state->lut_3d->data;
+	__u32 cfmt = mode->color_format;
+
+	/* copy RGB accordingly */
+	lut_volume = mode->lut_size * mode->lut_size * mode->lut_size;
+	for (i = 0; i < lut_volume; i += 3) {
+		lut_data[i] = extract_rgb_value(lut_3d, cfmt, R_3DLUT);
+		lut_data[i+1] = extract_rgb_value(lut_3d, cfmt, G_3DLUT);
+		lut_data[i+2] = extract_rgb_value(lut_3d, cfmt, B_3DLUT);
+
+		if (cfmt == DRM_FORMAT_XRGB16161616 || cfmt == DRM_FORMAT_XBGR16161616)
+			lut_3d += sizeof(__u64);
+		else if (cfmt == DRM_FORMAT_XRGB8888 || cfmt == DRM_FORMAT_XBGR8888)
+			lut_3d += sizeof(__u32);
+		else
+			return false;
+	}
+	return true;
+}
+
+static void convert_3dlut_to_tetrahedral_params(struct dc_rgb *rgb,
+	bool is_17x17x17, bool is_12_bits, struct tetrahedral_params *params)
+{
+	struct dc_rgb *lut0;
+	struct dc_rgb *lut1;
+	struct dc_rgb *lut2;
+	struct dc_rgb *lut3;
+	int i, lut_i;
+
+	int num_values;
+
+	if (is_17x17x17 == false) {
+		lut0 = params->tetrahedral_9.lut0;
+		lut1 = params->tetrahedral_9.lut1;
+		lut2 = params->tetrahedral_9.lut2;
+		lut3 = params->tetrahedral_9.lut3;
+		num_values = 729;
+	} else {
+		lut0 = params->tetrahedral_17.lut0;
+		lut1 = params->tetrahedral_17.lut1;
+		lut2 = params->tetrahedral_17.lut2;
+		lut3 = params->tetrahedral_17.lut3;
+		num_values = 4913;
+	}
+
+	params->use_12bits = is_12_bits;
+	params->use_tetrahedral_9 = !is_17x17x17;
+
+	for (lut_i = 0, i = 0; i < num_values - 4; lut_i++, i += 4) {
+		lut0[lut_i].red   = rgb[i].red;
+		lut0[lut_i].green = rgb[i].green;
+		lut0[lut_i].blue  = rgb[i].blue;
+
+		lut1[lut_i].red   = rgb[i + 1].red;
+		lut1[lut_i].green = rgb[i + 1].green;
+		lut1[lut_i].blue  = rgb[i + 1].blue;
+
+		lut2[lut_i].red   = rgb[i + 2].red;
+		lut2[lut_i].green = rgb[i + 2].green;
+		lut2[lut_i].blue  = rgb[i + 2].blue;
+
+		lut3[lut_i].red   = rgb[i + 3].red;
+		lut3[lut_i].green = rgb[i + 3].green;
+		lut3[lut_i].blue  = rgb[i + 3].blue;
+	}
+
+	lut0[lut_i].red      = rgb[i].red;
+	lut0[lut_i].green    = rgb[i].green;
+	lut0[lut_i].blue     = rgb[i].blue;
+}
+
+/* only use for 17x17x17 */
+bool convert_to_tetrahedral(unsigned short rgb_lib[17*17*17*3], struct tetrahedral_params *params)
+{
+	bool ret = false;
+	struct dc_rgb *rgb_area = NULL;
+	int ind = 0;
+	int ind_lut = 0;
+	int nir, nig, nib;
+
+	rgb_area = kvcalloc(17 * 17 * 17, sizeof(struct dc_rgb), GFP_KERNEL);
+	if (rgb_area == NULL)
+		goto release;
+
+	memset(rgb_area, 0, sizeof(17 * 17 * 17 * sizeof(struct dc_rgb)));
+
+	for (nib = 0; nib < 17; nib++) {
+		for (nig = 0; nig < 17; nig++) {
+			for (nir = 0; nir < 17; nir++) {
+				ind_lut = 3 * (nib + 17*nig + 289*nir);
+
+				rgb_area[ind].red = rgb_lib[ind_lut + 0];
+				rgb_area[ind].green = rgb_lib[ind_lut + 1];
+				rgb_area[ind].blue = rgb_lib[ind_lut + 2];
+				ind++;
+			}
+		}
+	}
+	convert_3dlut_to_tetrahedral_params(rgb_area, true, true, params);
+	kvfree(rgb_area);
+	ret = true;
+
+release:
+	return ret;
+}
+
+int amdgpu_dm_fill_3dlut_data(const struct drm_plane_state *plane_state, struct tetrahedral_params *param)
+{
+	const struct drm_mode_3dlut_mode *mode = &lut_3d_mode_17_12bit;
+	unsigned short *lut_data;
+
+	lut_data = kmalloc(mode->lut_size * mode->lut_size * mode->lut_size * sizeof(__u16) * 3, GFP_KERNEL);
+	if (!extract_rgb_data(plane_state, mode, lut_data))
+		return -EINVAL;
+
+	if (!convert_to_tetrahedral(lut_data, param))
+		return -EINVAL;
+
+	kfree(lut_data);
+
+	if (mode->lut_size == 17)
+		param->use_tetrahedral_9 = false;
+
+	if (mode->bit_depth == 12)
+		param->use_12bits = true;
+
+	return 0;
+}
+
 /**
  * amdgpu_dm_update_crtc_color_mgmt: Maps DRM color management to DC stream.
  * @crtc: amdgpu_dm crtc state
