diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 7d6acb4..6e14de9 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2516,6 +2516,7 @@ int i915_reg_read_ioctl(struct drm_device *dev, void *data,
 			struct drm_file *file);
 int i915_get_reset_stats_ioctl(struct drm_device *dev, void *data,
 			       struct drm_file *file);
+extern int i915_set_plane_zorder(struct drm_crtc *crtc, uint64_t zorder);
 
 /* overlay */
 extern struct intel_overlay_error_state *intel_overlay_capture_error_state(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 8f84555..8c382a5 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3881,6 +3881,9 @@ enum punit_power_well {
 #define   SPRITE_INT_GAMMA_ENABLE	(1<<13)
 #define   SPRITE_TILED			(1<<10)
 #define   SPRITE_DEST_KEY		(1<<2)
+#define   SPRITE_FORCE_BOTTOM		(1<<2)
+#define   SPRITE_ZORDER_ENABLE		(1<<0)
+
 #define _SPRA_LINOFF		0x70284
 #define _SPRA_STRIDE		0x70288
 #define _SPRA_POS		0x7028c
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index cd68a24..d245cca 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2169,18 +2169,27 @@ static int i9xx_update_primary_plane(struct drm_crtc *crtc,
 		break;
 	case DRM_FORMAT_XRGB1555:
 	case DRM_FORMAT_ARGB1555:
-		dspcntr |= DISPPLANE_BGRX555;
+		if (intel_crtc->primary_alpha)
+			dspcntr |= DISPPLANE_BGRA555;
+		else
+			dspcntr |= DISPPLANE_BGRX555;
 		break;
 	case DRM_FORMAT_RGB565:
 		dspcntr |= DISPPLANE_BGRX565;
 		break;
 	case DRM_FORMAT_XRGB8888:
 	case DRM_FORMAT_ARGB8888:
-		dspcntr |= DISPPLANE_BGRX888;
+		if (intel_crtc->primary_alpha)
+			dspcntr |= DISPPLANE_BGRA888;
+		else
+			dspcntr |= DISPPLANE_BGRX888;
 		break;
 	case DRM_FORMAT_XBGR8888:
 	case DRM_FORMAT_ABGR8888:
-		dspcntr |= DISPPLANE_RGBX888;
+		if (intel_crtc->primary_alpha)
+			dspcntr |= DISPPLANE_RGBA888;
+		else
+			dspcntr |= DISPPLANE_RGBX888;
 		break;
 	case DRM_FORMAT_XRGB2101010:
 	case DRM_FORMAT_ARGB2101010:
@@ -2188,7 +2197,10 @@ static int i9xx_update_primary_plane(struct drm_crtc *crtc,
 		break;
 	case DRM_FORMAT_XBGR2101010:
 	case DRM_FORMAT_ABGR2101010:
-		dspcntr |= DISPPLANE_RGBX101010;
+		if (intel_crtc->primary_alpha)
+			dspcntr |= DISPPLANE_RGBA101010;
+		else
+			dspcntr |= DISPPLANE_RGBX101010;
 		break;
 	default:
 		BUG();
@@ -8470,6 +8482,9 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
 
 	intel_crtc_cursor_set(crtc, NULL, 0, 0, 0);
 
+	if (intel_crtc->zorder_property)
+		drm_property_destroy(dev, intel_crtc->zorder_property);
+
 	drm_crtc_cleanup(crtc);
 
 	kfree(intel_crtc);
@@ -10422,6 +10437,27 @@ out_config:
 	return ret;
 }
 
+static int intel_crtc_set_property(struct drm_crtc *crtc,
+				   struct drm_property *prop,
+				   uint64_t val)
+{
+	struct intel_crtc *icrtc = to_intel_crtc(crtc);
+	int ret = -ENOENT;
+
+	if (prop && prop == icrtc->zorder_property) {
+		/* Simply return if no change in zorder */
+		if (icrtc->zorder == val)
+			return 0;
+
+		ret = i915_set_plane_zorder(crtc, val);
+
+		if (!ret)
+			icrtc->zorder = val;
+	}
+
+	return ret;
+}
+
 static const struct drm_crtc_funcs intel_crtc_funcs = {
 	.cursor_set = intel_crtc_cursor_set,
 	.cursor_move = intel_crtc_cursor_move,
@@ -10429,6 +10465,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = {
 	.set_config = intel_crtc_set_config,
 	.destroy = intel_crtc_destroy,
 	.page_flip = intel_crtc_page_flip,
+	.set_property = intel_crtc_set_property,
 };
 
 static void intel_cpu_pll_init(struct drm_device *dev)
@@ -10536,6 +10573,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_crtc *intel_crtc;
+	struct drm_property *prop = 0;
 	int i;
 
 	intel_crtc = kzalloc(sizeof(*intel_crtc), GFP_KERNEL);
@@ -10568,6 +10606,18 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 	dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
 
 	drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
+
+	intel_crtc->primary_alpha = false;
+	intel_crtc->sprite0_alpha = true;
+	intel_crtc->sprite1_alpha = true;
+
+	if (IS_VALLEYVIEW(dev))
+		prop = drm_property_create(dev, 0, "zorder", 1);
+
+	if (prop)
+		drm_object_attach_property(&intel_crtc->base.base, prop, 0);
+
+	intel_crtc->zorder_property = prop;
 }
 
 enum pipe intel_get_pipe_from_connector(struct intel_connector *connector)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index c551472..c684427 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -387,6 +387,9 @@ struct intel_crtc {
 	bool new_enabled;
 
 	uint32_t ddi_pll_sel;
+	bool primary_alpha;
+	bool sprite0_alpha;
+	bool sprite1_alpha;
 
 	/* reset counter value when the last flip was submitted */
 	unsigned int reset_counter;
@@ -400,6 +403,9 @@ struct intel_crtc {
 		/* watermarks currently being used  */
 		struct intel_pipe_wm active;
 	} wm;
+
+	struct drm_property *zorder_property;
+	uint64_t zorder;
 };
 
 struct intel_plane_wm_parameters {
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 336ae6c..fb495d9 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -37,6 +37,101 @@
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 
+static int vlv_set_plane_zorder(struct drm_crtc *crtc, uint64_t order)
+{
+	struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+	u32 val;
+	int s1_zorder = 0, s1_bottom = 0, s2_zorder = 0, s2_bottom = 0;
+	unsigned int i, z_order[I915_PLANE_MAX_NUM];
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	int pipe = intel_crtc->pipe;
+
+	/* Get plane order from bottom (lower bits) to top (higher bits) */
+	for (i = 0; i < I915_PLANE_MAX_NUM; i++)
+		z_order[i] = (order >> (i*4)) & 0xF;
+
+	/* The top layer must be cursor */
+	if (z_order[i-1] != I915_PLANE_CURSOR)
+		return -EINVAL;
+
+	/* No duplicated setting */
+	if (z_order[0] == z_order[1] ||
+		z_order[0] == z_order[2] ||
+		z_order[1] == z_order[2])
+		return -EINVAL;
+
+	switch (z_order[0]) {
+	case I915_PLANE_PRIMARY:
+		if (z_order[1] == I915_PLANE_SPRITE_B)
+			s1_zorder = 1;
+		break;
+	case I915_PLANE_SPRITE_A:
+		s1_bottom = 1;
+		if (z_order[1] == I915_PLANE_SPRITE_B)
+			s2_zorder = 1;
+		break;
+	case I915_PLANE_SPRITE_B:
+		s2_bottom = 1;
+		if (z_order[1] == I915_PLANE_SPRITE_A)
+			s1_zorder = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Clear the older Z-order */
+	val = I915_READ(SPCNTR(pipe, 0));
+	val &= ~(SPRITE_FORCE_BOTTOM | SPRITE_ZORDER_ENABLE);
+	I915_WRITE(SPCNTR(pipe, 0), val);
+
+	val = I915_READ(SPCNTR(pipe, 1));
+	val &= ~(SPRITE_FORCE_BOTTOM | SPRITE_ZORDER_ENABLE);
+	I915_WRITE(SPCNTR(pipe, 1), val);
+
+	/* Program new Z-order */
+	val = I915_READ(SPCNTR(pipe, 0));
+	if (s1_zorder)
+		val |= SPRITE_ZORDER_ENABLE;
+	if (s1_bottom)
+		val |= SPRITE_FORCE_BOTTOM;
+	I915_WRITE(SPCNTR(pipe, 0), val);
+
+	val = I915_READ(SPCNTR(pipe, 1));
+	if (s2_zorder)
+		val |= SPRITE_ZORDER_ENABLE;
+	if (s2_bottom)
+		val |= SPRITE_FORCE_BOTTOM;
+	I915_WRITE(SPCNTR(pipe, 1), val);
+
+	/* If a plane is not at bottom, the alpha channel should be on. */
+	if (z_order[0] != I915_PLANE_PRIMARY)
+		intel_crtc->primary_alpha = true;
+	else
+		intel_crtc->primary_alpha = false;
+
+	if (z_order[0] != I915_PLANE_SPRITE_A)
+		intel_crtc->sprite0_alpha = true;
+	else
+		intel_crtc->sprite0_alpha = false;
+
+	if (z_order[0] != I915_PLANE_SPRITE_B)
+		intel_crtc->sprite1_alpha = true;
+	else
+		intel_crtc->sprite1_alpha = false;
+
+	return 0;
+}
+
+int i915_set_plane_zorder(struct drm_crtc *crtc, uint64_t val)
+{
+	int ret = -ENOENT;
+
+	if (IS_VALLEYVIEW(crtc->dev))
+		ret = vlv_set_plane_zorder(crtc, val);
+
+	return ret;
+}
+
 static void
 vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
 		 struct drm_framebuffer *fb,
@@ -50,10 +145,19 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
 	struct intel_plane *intel_plane = to_intel_plane(dplane);
 	int pipe = intel_plane->pipe;
 	int plane = intel_plane->plane;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 	u32 sprctl;
+	bool alpha = true;
 	unsigned long sprsurf_offset, linear_offset;
 	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
 
+	if (plane && intel_crtc->sprite1_alpha)
+		alpha = true;
+	else if (!plane && intel_crtc->sprite0_alpha)
+		alpha = true;
+	else
+		alpha = false;
+
 	sprctl = I915_READ(SPCNTR(pipe, plane));
 
 	/* Mask out pixel format bits in case we change it */
@@ -81,19 +185,28 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
 		sprctl |= SP_FORMAT_BGRX8888;
 		break;
 	case DRM_FORMAT_ARGB8888:
-		sprctl |= SP_FORMAT_BGRA8888;
+		if (alpha)
+			sprctl |= SP_FORMAT_BGRA8888;
+		else
+			sprctl |= SP_FORMAT_BGRX8888;
 		break;
 	case DRM_FORMAT_XBGR2101010:
 		sprctl |= SP_FORMAT_RGBX1010102;
 		break;
 	case DRM_FORMAT_ABGR2101010:
-		sprctl |= SP_FORMAT_RGBA1010102;
+		if (alpha)
+			sprctl |= SP_FORMAT_RGBA1010102;
+		else
+			sprctl |= SP_FORMAT_RGBX1010102;
 		break;
 	case DRM_FORMAT_XBGR8888:
 		sprctl |= SP_FORMAT_RGBX8888;
 		break;
 	case DRM_FORMAT_ABGR8888:
-		sprctl |= SP_FORMAT_RGBA8888;
+		if (alpha)
+			sprctl |= SP_FORMAT_RGBA8888;
+		else
+			sprctl |= SP_FORMAT_RGBX8888;
 		break;
 	default:
 		/*
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
index 8a3e4ef00..3f6eb90 100644
--- a/include/uapi/drm/i915_drm.h
+++ b/include/uapi/drm/i915_drm.h
@@ -1050,4 +1050,10 @@ struct drm_i915_reset_stats {
 	__u32 pad;
 };
 
+#define I915_PLANE_PRIMARY		0
+#define I915_PLANE_SPRITE_A		1
+#define I915_PLANE_SPRITE_B		2
+#define I915_PLANE_CURSOR		3
+#define I915_PLANE_MAX_NUM		(I915_PLANE_CURSOR + 1)
+
 #endif /* _UAPI_I915_DRM_H_ */
