diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index a54eefea2513..b19e954a223f 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -172,6 +172,24 @@ static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
 
 	state = to_tegra_plane_state(plane->base.state);
 
+	/*
+	 * Assuming default zPos window order, enable color keying for cases
+	 * of overlapping with topping windows, excluding overlap with
+	 * window B. Due to limited HW capabilities, this allows to draw
+	 * primary plane on top of video overlay in areas where key isn't
+	 * matching. Though window C will be completely transparent in a
+	 * region of three windows intersection + key mismatch.
+	 */
+	if (state->ckey0_enabled) {
+		background[0] |= BLEND_COLOR_KEY_0;
+		background[2] |= BLEND_COLOR_KEY_0;
+	}
+
+	if (state->ckey1_enabled) {
+		background[0] |= BLEND_COLOR_KEY_1;
+		background[2] |= BLEND_COLOR_KEY_1;
+	}
+
 	if (state->opaque) {
 		/*
 		 * Since custom fix-weight blending isn't utilized and weight
@@ -729,6 +747,35 @@ static unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm)
 	return 1 << drm->mode_config.num_crtc;
 }
 
+static void tegra_plane_create_legacy_properties(struct tegra_plane *plane,
+						 struct drm_device *drm)
+{
+	plane->props.color_key0 = drm_property_create_bool(
+						drm, 0, "color key 0");
+	plane->props.color_key1 = drm_property_create_bool(
+						drm, 0, "color key 1");
+
+	if (!plane->props.color_key0 ||
+	    !plane->props.color_key1)
+		goto err_cleanup;
+
+	drm_object_attach_property(&plane->base.base, plane->props.color_key0,
+				   false);
+	drm_object_attach_property(&plane->base.base, plane->props.color_key1,
+				   false);
+
+	return;
+
+err_cleanup:
+	if (plane->props.color_key0)
+		drm_property_destroy(drm, plane->props.color_key0);
+
+	if (plane->props.color_key1)
+		drm_property_destroy(drm, plane->props.color_key1);
+
+	dev_err(plane->dc->dev, "failed to create legacy plane properties\n");
+}
+
 static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
 						    struct tegra_dc *dc)
 {
@@ -764,6 +811,9 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
 	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
 	drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
 
+	if (dc->soc->legacy_blending)
+		tegra_plane_create_legacy_properties(plane, drm);
+
 	return &plane->base;
 }
 
@@ -1153,6 +1203,8 @@ tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
 	copy->pclk = state->pclk;
 	copy->div = state->div;
 	copy->planes = state->planes;
+	copy->ckey0 = state->ckey0;
+	copy->ckey1 = state->ckey1;
 
 	return &copy->base;
 }
@@ -1537,6 +1589,50 @@ static void tegra_dc_disable_vblank(struct drm_crtc *crtc)
 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
 }
 
+static int tegra_crtc_atomic_set_property(struct drm_crtc *crtc,
+					  struct drm_crtc_state *state,
+					  struct drm_property *property,
+					  uint64_t value)
+{
+	struct tegra_dc_state *tegra_state = to_dc_state(state);
+	struct tegra_dc *dc = to_tegra_dc(crtc);
+
+	if (property == dc->props.ckey0_lower)
+		tegra_state->ckey0.lower = value;
+	else if (property == dc->props.ckey0_upper)
+		tegra_state->ckey0.upper = value;
+	else if (property == dc->props.ckey1_lower)
+		tegra_state->ckey1.lower = value;
+	else if (property == dc->props.ckey1_upper)
+		tegra_state->ckey1.upper = value;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int tegra_crtc_atomic_get_property(struct drm_crtc *crtc,
+					  const struct drm_crtc_state *state,
+					  struct drm_property *property,
+					  uint64_t *value)
+{
+	struct tegra_dc_state *tegra_state = to_dc_state(state);
+	struct tegra_dc *dc = to_tegra_dc(crtc);
+
+	if (property == dc->props.ckey0_lower)
+		*value = tegra_state->ckey0.lower;
+	else if (property == dc->props.ckey0_upper)
+		*value = tegra_state->ckey0.upper;
+	else if (property == dc->props.ckey1_lower)
+		*value = tegra_state->ckey1.lower;
+	else if (property == dc->props.ckey1_upper)
+		*value = tegra_state->ckey1.upper;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
 static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.page_flip = drm_atomic_helper_page_flip,
 	.set_config = drm_atomic_helper_set_config,
@@ -1549,6 +1645,8 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = {
 	.get_vblank_counter = tegra_dc_get_vblank_counter,
 	.enable_vblank = tegra_dc_enable_vblank,
 	.disable_vblank = tegra_dc_disable_vblank,
+	.atomic_set_property = tegra_crtc_atomic_set_property,
+	.atomic_get_property = tegra_crtc_atomic_get_property,
 };
 
 static int tegra_dc_set_timings(struct tegra_dc *dc,
@@ -1883,6 +1981,18 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
 	struct tegra_dc *dc = to_tegra_dc(crtc);
 	u32 value;
 
+	if (dc->soc->legacy_blending) {
+		tegra_dc_writel(dc, state->ckey0.lower,
+				DC_DISP_COLOR_KEY0_LOWER);
+		tegra_dc_writel(dc, state->ckey0.upper,
+				DC_DISP_COLOR_KEY0_UPPER);
+
+		tegra_dc_writel(dc, state->ckey1.lower,
+				DC_DISP_COLOR_KEY1_LOWER);
+		tegra_dc_writel(dc, state->ckey1.upper,
+				DC_DISP_COLOR_KEY1_UPPER);
+	}
+
 	value = state->planes << 8 | GENERAL_UPDATE;
 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
@@ -1944,6 +2054,56 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+static int tegra_dc_create_legacy_properties(struct tegra_dc *dc,
+					     struct drm_device *drm)
+{
+	/*
+	 * Each color key value is represented in RGB888 format.
+	 * All planes share the same color key values and free to choose
+	 * among the ckey0 and ckey1.
+	 */
+	dc->props.ckey0_lower = drm_property_create_range(
+			drm, 0, "color key 0 lower margin", 0, 0xffffff);
+	dc->props.ckey0_upper = drm_property_create_range(
+			drm, 0, "color key 0 upper margin", 0, 0xffffff);
+	dc->props.ckey1_lower = drm_property_create_range(
+			drm, 0, "color key 1 lower margin", 0, 0xffffff);
+	dc->props.ckey1_upper = drm_property_create_range(
+			drm, 0, "color key 1 upper margin", 0, 0xffffff);
+
+	if (!dc->props.ckey0_lower ||
+	    !dc->props.ckey0_upper ||
+	    !dc->props.ckey1_lower ||
+	    !dc->props.ckey1_upper)
+		goto err_cleanup;
+
+	drm_object_attach_property(&dc->base.base, dc->props.ckey0_lower,
+				   0x000000);
+	drm_object_attach_property(&dc->base.base, dc->props.ckey0_upper,
+				   0x000000);
+	drm_object_attach_property(&dc->base.base, dc->props.ckey1_lower,
+				   0x000000);
+	drm_object_attach_property(&dc->base.base, dc->props.ckey1_upper,
+				   0x000000);
+
+	return 0;
+
+err_cleanup:
+	if (dc->props.ckey0_lower)
+		drm_property_destroy(drm, dc->props.ckey0_lower);
+
+	if (dc->props.ckey0_upper)
+		drm_property_destroy(drm, dc->props.ckey0_upper);
+
+	if (dc->props.ckey1_lower)
+		drm_property_destroy(drm, dc->props.ckey1_lower);
+
+	if (dc->props.ckey1_upper)
+		drm_property_destroy(drm, dc->props.ckey1_upper);
+
+	return -ENOMEM;
+}
+
 static int tegra_dc_init(struct host1x_client *client)
 {
 	struct drm_device *drm = dev_get_drvdata(client->parent);
@@ -2031,6 +2191,12 @@ static int tegra_dc_init(struct host1x_client *client)
 		goto cleanup;
 	}
 
+	if (dc->soc->legacy_blending) {
+		err = tegra_dc_create_legacy_properties(dc, drm);
+		if (err < 0)
+			dev_err(dc->dev, "failed to create CRTC properties\n");
+	}
+
 	return 0;
 
 cleanup:
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 3156006e75c6..3913d047abac 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -18,6 +18,11 @@
 
 struct tegra_output;
 
+struct tegra_dc_color_key_state {
+	u32 lower;
+	u32 upper;
+};
+
 struct tegra_dc_state {
 	struct drm_crtc_state base;
 
@@ -26,9 +31,13 @@ struct tegra_dc_state {
 	unsigned int div;
 
 	u32 planes;
+
+	struct tegra_dc_color_key_state ckey0;
+	struct tegra_dc_color_key_state ckey1;
 };
 
-static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
+static inline struct tegra_dc_state *
+to_dc_state(const struct drm_crtc_state *state)
 {
 	if (state)
 		return container_of(state, struct tegra_dc_state, base);
@@ -94,6 +103,13 @@ struct tegra_dc {
 	const struct tegra_dc_soc_info *soc;
 
 	struct iommu_domain *domain;
+
+	struct {
+		struct drm_property *ckey0_lower;
+		struct drm_property *ckey0_upper;
+		struct drm_property *ckey1_lower;
+		struct drm_property *ckey1_upper;
+	} props;
 };
 
 static inline struct tegra_dc *
diff --git a/drivers/gpu/drm/tegra/plane.c b/drivers/gpu/drm/tegra/plane.c
index 0406c2ef432c..4d794f2b44df 100644
--- a/drivers/gpu/drm/tegra/plane.c
+++ b/drivers/gpu/drm/tegra/plane.c
@@ -57,6 +57,8 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
 	copy->format = state->format;
 	copy->swap = state->swap;
 	copy->opaque = state->opaque;
+	copy->ckey0_enabled = state->ckey0_enabled;
+	copy->ckey1_enabled = state->ckey1_enabled;
 
 	for (i = 0; i < 2; i++)
 		copy->blending[i] = state->blending[i];
@@ -86,6 +88,42 @@ static bool tegra_plane_format_mod_supported(struct drm_plane *plane,
 	return false;
 }
 
+static int tegra_plane_set_property(struct drm_plane *plane,
+				    struct drm_plane_state *state,
+				    struct drm_property *property,
+				    uint64_t value)
+{
+	struct tegra_plane_state *tegra_state = to_tegra_plane_state(state);
+	struct tegra_plane *tegra = to_tegra_plane(plane);
+
+	if (property == tegra->props.color_key0)
+		tegra_state->ckey0_enabled = value;
+	else if (property == tegra->props.color_key1)
+		tegra_state->ckey1_enabled = value;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int tegra_plane_get_property(struct drm_plane *plane,
+				    const struct drm_plane_state *state,
+				    struct drm_property *property,
+				    uint64_t *value)
+{
+	struct tegra_plane_state *tegra_state = to_tegra_plane_state(state);
+	struct tegra_plane *tegra = to_tegra_plane(plane);
+
+	if (property == tegra->props.color_key0)
+		*value = tegra_state->ckey0_enabled;
+	else if (property == tegra->props.color_key1)
+		*value = tegra_state->ckey1_enabled;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
 const struct drm_plane_funcs tegra_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
@@ -94,6 +132,8 @@ const struct drm_plane_funcs tegra_plane_funcs = {
 	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
 	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
 	.format_mod_supported = tegra_plane_format_mod_supported,
+	.atomic_set_property = tegra_plane_set_property,
+	.atomic_get_property = tegra_plane_get_property,
 };
 
 int tegra_plane_state_add(struct tegra_plane *plane,
diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h
index 7360ddfafee8..dafecea73b29 100644
--- a/drivers/gpu/drm/tegra/plane.h
+++ b/drivers/gpu/drm/tegra/plane.h
@@ -19,6 +19,11 @@ struct tegra_plane {
 	struct tegra_dc *dc;
 	unsigned int offset;
 	unsigned int index;
+
+	struct {
+		struct drm_property *color_key0;
+		struct drm_property *color_key1;
+	} props;
 };
 
 struct tegra_cursor {
@@ -49,10 +54,12 @@ struct tegra_plane_state {
 	/* used for legacy blending support only */
 	struct tegra_plane_legacy_blending_state blending[2];
 	bool opaque;
+	bool ckey0_enabled;
+	bool ckey1_enabled;
 };
 
 static inline struct tegra_plane_state *
-to_tegra_plane_state(struct drm_plane_state *state)
+to_tegra_plane_state(const struct drm_plane_state *state)
 {
 	if (state)
 		return container_of(state, struct tegra_plane_state, base);
