diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 7e75b13..d145130 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -68,6 +68,11 @@ static const uint32_t intel_primary_formats_gen4[] = {
 	DRM_FORMAT_ABGR2101010,
 };
 
+/* Cursor formats */
+static const uint32_t intel_cursor_formats[] = {
+	DRM_FORMAT_ARGB8888,
+};
+
 #define DIV_ROUND_CLOSEST_ULL(ll, d)	\
 ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; })
 
@@ -7967,8 +7972,8 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 	int pipe = intel_crtc->pipe;
-	int x = intel_crtc->cursor_x;
-	int y = intel_crtc->cursor_y;
+	int x = crtc->cursor_x;
+	int y = crtc->cursor_y;
 	u32 base = 0, pos = 0;
 	bool visible;
 
@@ -8023,8 +8028,6 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
  * @height: cursor height
  *
  * Programs a crtc's cursor plane with the specified GEM object.
- * intel_crtc_cursor_set() should be used instead if we have a userspace buffer
- * handle rather than the actual GEM object.
  */
 static int intel_crtc_cursor_set_obj(struct drm_crtc *crtc,
 				     struct drm_i915_gem_object *obj,
@@ -8143,48 +8146,6 @@ fail:
 	return ret;
 }
 
-/**
- * intel_crtc_cursor_set - Update cursor buffer
- * @crtc: crtc to set cursor for
- * @handle: userspace handle of buffer to set cursor to
- * @width: cursor width
- * @height: cursor height
- *
- * Programs a crtc's cursor plane with the buffer referred to by userspace
- * handle @handle.
- */
-static int intel_crtc_cursor_set(struct drm_crtc *crtc,
-				 struct drm_file *file,
-				 uint32_t handle,
-				 uint32_t width, uint32_t height)
-{
-	struct drm_device *dev = crtc->dev;
-	struct drm_i915_gem_object *obj;
-
-	if (handle) {
-		obj = to_intel_bo(drm_gem_object_lookup(dev, file, handle));
-		if (&obj->base == NULL)
-			return -ENOENT;
-	} else {
-		obj = NULL;
-	}
-
-	return intel_crtc_cursor_set_obj(crtc, obj, width, height);
-}
-
-static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
-{
-	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-
-	intel_crtc->cursor_x = clamp_t(int, x, SHRT_MIN, SHRT_MAX);
-	intel_crtc->cursor_y = clamp_t(int, y, SHRT_MIN, SHRT_MAX);
-
-	if (intel_crtc->active)
-		intel_crtc_update_cursor(crtc, intel_crtc->cursor_bo != NULL);
-
-	return 0;
-}
-
 static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
 				 u16 *blue, uint32_t start, uint32_t size)
 {
@@ -8806,7 +8767,7 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
 		kfree(work);
 	}
 
-	intel_crtc_cursor_set(crtc, NULL, 0, 0, 0);
+	intel_crtc_cursor_set_obj(crtc, NULL, 0, 0);
 
 	drm_crtc_cleanup(crtc);
 
@@ -10784,8 +10745,6 @@ out_config:
 }
 
 static const struct drm_crtc_funcs intel_crtc_funcs = {
-	.cursor_set = intel_crtc_cursor_set,
-	.cursor_move = intel_crtc_cursor_move,
 	.gamma_set = intel_crtc_gamma_set,
 	.set_config = intel_crtc_set_config,
 	.destroy = intel_crtc_destroy,
@@ -11018,7 +10977,8 @@ intel_primary_plane_setplane(struct drm_plane *plane, struct drm_crtc *crtc,
 	return 0;
 }
 
-static void intel_primary_plane_destroy(struct drm_plane *plane)
+/* Common destruction function for both primary and cursor planes */
+static void intel_plane_destroy(struct drm_plane *plane)
 {
 	struct intel_plane *intel_plane = to_intel_plane(plane);
 	drm_plane_cleanup(plane);
@@ -11028,7 +10988,7 @@ static void intel_primary_plane_destroy(struct drm_plane *plane)
 static const struct drm_plane_funcs intel_primary_plane_funcs = {
 	.update_plane = intel_primary_plane_setplane,
 	.disable_plane = intel_primary_plane_disable,
-	.destroy = intel_primary_plane_destroy,
+	.destroy = intel_plane_destroy,
 };
 
 static struct drm_plane *intel_primary_plane_create(struct drm_device *dev,
@@ -11064,11 +11024,116 @@ static struct drm_plane *intel_primary_plane_create(struct drm_device *dev,
 	return &primary->base;
 }
 
+static int
+intel_cursor_plane_disable(struct drm_plane *plane)
+{
+	if (!plane->fb)
+		return 0;
+
+	BUG_ON(!plane->crtc);
+
+	return intel_crtc_cursor_set_obj(plane->crtc, NULL, 0, 0);
+}
+
+static int
+intel_cursor_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
+			  struct drm_framebuffer *fb, int crtc_x, int crtc_y,
+			  unsigned int crtc_w, unsigned int crtc_h,
+			  uint32_t src_x, uint32_t src_y,
+			  uint32_t src_w, uint32_t src_h)
+{
+	struct drm_device *dev = crtc->dev;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+	struct drm_i915_gem_object *obj = intel_fb->obj;
+	struct drm_rect dest = {
+		/* integer pixels */
+		.x1 = crtc_x,
+		.y1 = crtc_y,
+		.x2 = crtc_x + crtc_w,
+		.y2 = crtc_y + crtc_h,
+	};
+	struct drm_rect src = {
+		/* 16.16 fixed point */
+		.x1 = src_x,
+		.y1 = src_y,
+		.x2 = src_x + src_w,
+		.y2 = src_y + src_h,
+	};
+	const struct drm_rect clip = {
+		/* integer pixels */
+		.x2 = intel_crtc->config.pipe_src_w,
+		.y2 = intel_crtc->config.pipe_src_h,
+	};
+	int hscale, vscale;
+	bool visible;
+
+	/* Check scaling */
+	hscale = drm_rect_calc_hscale(&src, &dest,
+				      DRM_PLANE_HELPER_NO_SCALING,
+				      DRM_PLANE_HELPER_NO_SCALING);
+	vscale = drm_rect_calc_vscale(&src, &dest,
+				      DRM_PLANE_HELPER_NO_SCALING,
+				      DRM_PLANE_HELPER_NO_SCALING);
+	if (hscale < 0 || vscale < 0) {
+		DRM_DEBUG_KMS("Invalid scaling of cursor plane\n");
+		return -ERANGE;
+	}
+
+	/* Check dimensions */
+	if (!((crtc_w == 64 && crtc_h == 64) ||
+			(crtc_w == 128 && crtc_h == 128 && !IS_GEN2(dev)) ||
+			(crtc_w == 256 && crtc_h == 256 && !IS_GEN2(dev)))) {
+		DRM_DEBUG_KMS("Cursor dimension not supported: %dx%d\n",
+			      crtc_w, crtc_h);
+		return -EINVAL;
+	}
+
+	/* Clip to display; if no longer visible after clipping, disable */
+	visible = drm_rect_clip_scaled(&src, &dest, &clip, hscale, vscale);
+
+	crtc->cursor_x = crtc_x;
+	crtc->cursor_y = crtc_y;
+	if (fb != crtc->cursor->fb) {
+		return intel_crtc_cursor_set_obj(crtc, visible ? obj : NULL,
+						 crtc_w, crtc_h);
+	} else {
+		intel_crtc_update_cursor(crtc, true);
+		return 0;
+	}
+}
+static const struct drm_plane_funcs intel_cursor_plane_funcs = {
+	.update_plane = intel_cursor_plane_update,
+	.disable_plane = intel_cursor_plane_disable,
+	.destroy = intel_plane_destroy,
+};
+
+static struct drm_plane *intel_cursor_plane_create(struct drm_device *dev,
+						   int pipe)
+{
+	struct intel_plane *cursor;
+
+	cursor = kzalloc(sizeof(*cursor), GFP_KERNEL);
+	if (cursor == NULL)
+		return NULL;
+
+	cursor->can_scale = false;
+	cursor->max_downscale = 1;
+	cursor->pipe = pipe;
+	cursor->plane = pipe;
+
+	drm_plane_init(dev, &cursor->base, 0,
+		       &intel_cursor_plane_funcs, intel_cursor_formats,
+		       ARRAY_SIZE(intel_cursor_formats), DRM_PLANE_TYPE_CURSOR);
+	return &cursor->base;
+}
+
 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_plane *primary;
+	struct drm_plane *primary = NULL;
+	struct drm_plane *cursor = NULL;
 	int i, ret;
 
 	intel_crtc = kzalloc(sizeof(*intel_crtc), GFP_KERNEL);
@@ -11076,13 +11141,17 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 		return;
 
 	primary = intel_primary_plane_create(dev, pipe);
+	if (!primary)
+		goto fail;
+
+	cursor = intel_cursor_plane_create(dev, pipe);
+	if (!cursor)
+		goto fail;
+
 	ret = drm_crtc_init_with_planes(dev, &intel_crtc->base, primary,
-					NULL, &intel_crtc_funcs);
-	if (ret) {
-		drm_plane_cleanup(primary);
-		kfree(intel_crtc);
-		return;
-	}
+					cursor, &intel_crtc_funcs);
+	if (ret)
+		goto fail;
 
 	drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
 	for (i = 0; i < 256; i++) {
@@ -11110,6 +11179,15 @@ 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);
+
+	return;
+
+fail:
+	if (primary)
+		drm_plane_cleanup(primary);
+	if (cursor)
+		drm_plane_cleanup(cursor);
+	kfree(intel_crtc);
 }
 
 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 32a74e1..8374f37a 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -378,7 +378,6 @@ struct intel_crtc {
 
 	struct drm_i915_gem_object *cursor_bo;
 	uint32_t cursor_addr;
-	int16_t cursor_x, cursor_y;
 	int16_t cursor_width, cursor_height;
 	bool cursor_visible;
 
