diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index b6d6c04..3afdc45 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -2476,6 +2476,107 @@ out:
 	return ret;
 }
 
+/**
+ * drm_mode_cursor_universal - translate legacy cursor ioctl call into a
+ *     universal plane handler call
+ * @crtc: crtc to update cursor for
+ * @req: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Legacy cursor ioctl's work directly with driver buffer handles.  To
+ * translate legacy ioctl calls into universal plane handler calls, we need to
+ * wrap the native buffer handle in a drm_framebuffer.
+ *
+ * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB
+ * buffer with a pitch of 4*width; the universal plane interface should be used
+ * directly in cases where the hardware can support other buffer settings and
+ * userspace wants to make use of these capabilities.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
+static int drm_mode_cursor_universal(struct drm_crtc *crtc,
+				     struct drm_mode_cursor2 *req,
+				     struct drm_file *file_priv)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_framebuffer *fb = NULL;
+	struct drm_mode_fb_cmd2 fbreq = {
+		.width = req->width,
+		.height = req->height,
+		.pixel_format = DRM_FORMAT_ARGB8888,
+		.pitches = { req->width * 4 },
+		.handles = { req->handle },
+	};
+	struct drm_mode_set_plane planereq = { 0 };
+	int ret = 0;
+
+	BUG_ON(!crtc->cursor);
+
+	if (req->flags & DRM_MODE_CURSOR_BO) {
+		if (req->handle) {
+			ret = drm_mode_addfb2(dev, &fbreq, file_priv);
+			if (ret) {
+				DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
+				return ret;
+			}
+
+			/*
+			 * Get framebuffer we just created (but use
+			 * __drm_framebuffer_lookup() so that we don't take an
+			 * extra reference to it
+			 */
+			mutex_lock(&dev->mode_config.fb_lock);
+			fb = __drm_framebuffer_lookup(dev, fbreq.fb_id);
+			mutex_unlock(&dev->mode_config.fb_lock);
+
+			/*
+			 * We just created this; we shouldn't be able to fail
+			 * the lookup
+			 */
+			BUG_ON(!fb);
+		} else {
+			fb = NULL;
+		}
+	} else {
+		fb = crtc->cursor->fb;
+	}
+
+	planereq.plane_id = crtc->cursor->base.id;
+	planereq.crtc_id = crtc->base.id;
+	planereq.fb_id = fb ? fb->base.id : 0;
+
+	if (req->flags & DRM_MODE_CURSOR_MOVE) {
+		planereq.crtc_x = req->x;
+		planereq.crtc_y = req->y;
+	} else {
+		planereq.crtc_x = crtc->cursor_x;
+		planereq.crtc_y = crtc->cursor_y;
+	}
+
+	if (fb) {
+		planereq.crtc_w = fb->width;
+		planereq.crtc_h = fb->height;
+		planereq.src_w = fb->width << 16;
+		planereq.src_h = fb->height << 16;
+	}
+
+	ret = drm_mode_setplane(dev, &planereq, file_priv);
+	if (ret) {
+		if (req->flags & DRM_MODE_CURSOR_BO)
+			drm_mode_rmfb(dev, &fb->base.id, file_priv);
+		return ret;
+	}
+
+	/* Update successful; save new cursor position, if necessary */
+	if (req->flags & DRM_MODE_CURSOR_MOVE) {
+		crtc->cursor_x = req->x;
+		crtc->cursor_y = req->y;
+	}
+
+	return 0;
+}
+
 static int drm_mode_cursor_common(struct drm_device *dev,
 				  struct drm_mode_cursor2 *req,
 				  struct drm_file *file_priv)
@@ -2497,6 +2598,13 @@ static int drm_mode_cursor_common(struct drm_device *dev,
 	}
 	crtc = obj_to_crtc(obj);
 
+	/*
+	 * If this crtc has a universal cursor plane, call that plane's update
+	 * handler rather than using legacy cursor handlers.
+	 */
+	if (crtc->cursor)
+		return drm_mode_cursor_universal(crtc, req, file_priv);
+
 	mutex_lock(&crtc->mutex);
 	if (req->flags & DRM_MODE_CURSOR_BO) {
 		if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index c061bb3..e5d22ff 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -311,6 +311,10 @@ struct drm_crtc {
 	struct drm_plane *primary;
 	struct drm_plane *cursor;
 
+	/* position of cursor plane on crtc */
+	int cursor_x;
+	int cursor_y;
+
 	/* Temporary tracking of the old fb while a modeset is ongoing. Used
 	 * by drm_mode_set_config_internal to implement correct refcounting. */
 	struct drm_framebuffer *old_fb;
