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 06da4f2ed7ad..02ed3991a803 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -11221,7 +11221,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
 			dm_old_crtc_state->dsc_force_changed == false)
 			continue;
 
-		ret = amdgpu_dm_verify_lut_sizes(new_crtc_state);
+		ret = amdgpu_dm_verify_lut_sizes(new_crtc_state, adev);
 		if (ret) {
 			DRM_DEBUG_DRIVER("amdgpu_dm_verify_lut_sizes() failed\n");
 			goto fail;
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 8fd1be7f2583..205503f12d87 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -721,9 +721,10 @@ void amdgpu_dm_trigger_timing_sync(struct drm_device *dev);
 #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
+#define COLOR_3DLUT_ENTRIES 17
 
 void amdgpu_dm_init_color_mod(void);
-int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state);
+int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state, struct amdgpu_device *adev);
 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 dc_plane_state *dc_plane_state);
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 1f4a7c908587..d22396976c7a 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
@@ -210,6 +210,45 @@ static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
 	}
 }
 
+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, 12);
+	rgb->green = drm_color_lut_extract(lut.green, 12);
+	rgb->blue  = drm_color_lut_extract(lut.blue, 12);
+
+}
+
+static void __drm_lut3d_to_dc_lut3d(const struct drm_color_lut *lut,
+				    struct dc_3dlut *lut3d)
+{
+        int lut3d_size = COLOR_3DLUT_ENTRIES*COLOR_3DLUT_ENTRIES*COLOR_3DLUT_ENTRIES;
+	int lutn_size = (lut3d_size - 1)/4 + 1;
+	int i;
+
+	ASSERT(lut3d && lut3d_size == MAX_COLOR_LUT_ENTRIES);
+
+	/* So far, only supports 17x17x17 3D LUT with 12-bit*/
+	lut3d->lut_3d.use_tetrahedral_9 = false;
+	lut3d->lut_3d.use_12bits = true;
+
+	for (i = 0; i < lutn_size - 1 ; i++) {
+		/* We should consider the 3dlut RGB values are distributed
+		 * along four arrays lut0-3 where the first sizes 1229 and the
+		 * other 1228. The max bit precision supported for 3dlut
+		 * channel is 12-bit, but can be 10-bit. Still need to verify
+		 * where it is setup */
+		__to_dc_lut3d_color(&lut3d->lut_3d.tetrahedral_17.lut0[i], lut[i], 12);
+		__to_dc_lut3d_color(&lut3d->lut_3d.tetrahedral_17.lut1[i], lut[i+lutn_size], 12);
+		__to_dc_lut3d_color(&lut3d->lut_3d.tetrahedral_17.lut2[i], lut[i+lutn_size*2], 12);
+		__to_dc_lut3d_color(&lut3d->lut_3d.tetrahedral_17.lut3[i], lut[i+lutn_size*3], 12);
+	}
+
+	/* lut0 goes to lutn_size == 1229 */
+	__to_dc_lut3d_color(&lut3d->lut_3d.tetrahedral_17.lut0[i], lut[i], 12);
+};
+
 /**
  * __set_legacy_tf - Calculates the legacy transfer function
  * @func: transfer function
@@ -335,7 +374,20 @@ static int __set_input_tf(struct dc_transfer_func *func,
 }
 
 /**
- * amdgpu_dm_verify_lut_sizes - verifies if DRM luts match the hw supported sizes
+ * amdgpu_dm_3dlut_size - get expected size according to hw color caps
+ * @lut_size: default size
+ * @adev: amdgpu device
+ *
+ * Return:
+ * lut_size if 3dlut is supported, zero otherwise
+ */
+static int amdgpu_dm_3dlut_size(int lut_size, struct amdgpu_device *adev)
+{
+	return adev->dm.dc->caps.color.mpc.num_3dluts ? lut_size : 0;
+}
+
+/**
+ * amdgpu_dm_verify_lut_sizes
  * @crtc_state: the DRM CRTC state
  * @adev: amdgpu device
  *
@@ -346,10 +398,11 @@ static int __set_input_tf(struct dc_transfer_func *func,
  * 0 on success.
  * -EINVAL if any lut sizes are invalid.
  */
-int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state)
+int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state,
+			       struct amdgpu_device *adev)
 {
 	const struct drm_color_lut *lut = NULL;
-	uint32_t size = 0;
+	uint32_t size = 0, exp_size;
 
 	lut = __extract_blob_lut(crtc_state->degamma_lut, &size);
 	if (lut && size != MAX_COLOR_LUT_ENTRIES) {
@@ -359,6 +412,24 @@ int amdgpu_dm_verify_lut_sizes(const struct drm_crtc_state *crtc_state)
 		return -EINVAL;
 	}
 
+	lut = __extract_blob_lut(crtc_state->shaper_lut, &size);
+	exp_size = amdgpu_dm_3dlut_size(MAX_COLOR_LUT_ENTRIES, adev);
+	if (lut && size != exp_size) {
+		DRM_DEBUG_DRIVER(
+			"Invalid Shaper LUT size. Should be %u but got %u.\n",
+			exp_size, size);
+		return -EINVAL;
+	}
+
+	lut = __extract_blob_lut(crtc_state->lut3d, &size);
+	exp_size = amdgpu_dm_3dlut_size(COLOR_3DLUT_ENTRIES, adev);
+	if (lut && size != exp_size) {
+		DRM_DEBUG_DRIVER(
+			"Invalid Gamma 3D LUT size. Should be %u but got %u.\n",
+			exp_size, size);
+		return -EINVAL;
+	}
+
 	lut = __extract_blob_lut(crtc_state->gamma_lut, &size);
 	if (lut && size != MAX_COLOR_LUT_ENTRIES &&
 	    size != MAX_COLOR_LEGACY_LUT_ENTRIES) {
@@ -401,17 +472,19 @@ int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc)
 	struct amdgpu_device *adev = drm_to_adev(crtc->base.state->dev);
 	bool has_rom = adev->asic_type <= CHIP_RAVEN;
 	struct drm_color_ctm *ctm = NULL;
-	const struct drm_color_lut *degamma_lut, *regamma_lut;
-	uint32_t degamma_size, regamma_size;
-	bool has_regamma, has_degamma;
+	const struct drm_color_lut *degamma_lut, *shaper_lut, *lut3d, *regamma_lut;
+	uint32_t degamma_size, regamma_size, shaper_size, lut3d_size;
+	bool has_regamma, has_degamma, has_lut3d, has_shaper_lut;
 	bool is_legacy;
 	int r;
 
-	r = amdgpu_dm_verify_lut_sizes(&crtc->base);
+	r = amdgpu_dm_verify_lut_sizes(&crtc->base, adev);
 	if (r)
 		return r;
 
 	degamma_lut = __extract_blob_lut(crtc->base.degamma_lut, &degamma_size);
+	shaper_lut =  __extract_blob_lut(crtc->base.shaper_lut, &shaper_size);
+	lut3d = __extract_blob_lut(crtc->base.lut3d, &lut3d_size);
 	regamma_lut = __extract_blob_lut(crtc->base.gamma_lut, &regamma_size);
 
 	has_degamma =
@@ -420,6 +493,12 @@ int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc)
 	has_regamma =
 		regamma_lut && !__is_lut_linear(regamma_lut, regamma_size);
 
+	has_shaper_lut =
+		shaper_lut && !__is_lut_linear(shaper_lut, shaper_size);
+
+	has_lut3d = shaper_lut && lut3d &&
+			    !__is_lut_linear(lut3d, lut3d_size);
+
 	is_legacy = regamma_size == MAX_COLOR_LEGACY_LUT_ENTRIES;
 
 	/* Reset all adjustments. */
@@ -447,23 +526,55 @@ int amdgpu_dm_update_crtc_color_mgmt(struct dm_crtc_state *crtc)
 				    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);
-		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.
+		/* If no 3D LUT, use regamma original settings that means
+		 * linear input space.
 		 */
-		stream->out_transfer_func->type = TF_TYPE_BYPASS;
 		stream->out_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
+
+		if (has_lut3d && has_shaper_lut) {
+			/* 3D LUT always implies a shaper 1D LUT to
+			 * de-linearize and shape the content.
+			 * Q: I see func_shaper and lut3d_func is originally
+			 * const, is it expected? If so, where is the point to
+			 * update them?
+			 */
+			stream->func_shaper->type = TF_TYPE_DISTRIBUTED_POINTS;
+			stream->func_shaper->tf = TRANSFER_FUNCTION_LINEAR;
+			r = __set_output_tf(stream->func_shaper, shaper_lut,
+					    shaper_size, has_rom);
+			if (r)
+				return r;
+
+			/* Function to convert DRM 3D LUT to DC 3D LUT*/
+			 __drm_lut3d_to_dc_lut3d(lut3d, stream->lut3d_func);
+
+			/* Set different input space to gamma lut, since gamma
+			 * 1D LUT will be applied in a non-linear space after
+			 * 3D LUT. But which predefined function to select for
+			 * gamma after 3D LUT?
+			 */
+			stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
+		}
+
+		if (has_regamma) {
+			/**
+			 * CRTC RGM goes into RGM LUT.
+			 */
+			stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
+
+			r = __set_output_tf(stream->out_transfer_func, regamma_lut,
+					    regamma_size, has_rom);
+			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;
+		}
 	}
 
 	/**
diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h
index f8f66790d09b..a7cc6ace5904 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_stream.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h
@@ -246,8 +246,8 @@ struct dc_stream_state {
 	/* writeback */
 	unsigned int num_wb_info;
 	struct dc_writeback_info writeback_info[MAX_DWB_PIPES];
-	const struct dc_transfer_func *func_shaper;
-	const struct dc_3dlut *lut3d_func;
+	struct dc_transfer_func *func_shaper;
+	struct dc_3dlut *lut3d_func;
 	/* Computed state bits */
 	bool mode_changed : 1;
 
