diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c
index 08bd176..80f1cd2 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.c
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.c
@@ -40,7 +40,6 @@ static const struct pci_device_id pciidlist[] = {
 	{0,}
 };
 
-
 static int cirrus_kick_out_firmware_fb(struct pci_dev *pdev)
 {
 	struct apertures_struct *ap;
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h
index 7050615..bd1ff51 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.h
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.h
@@ -135,8 +135,9 @@ struct cirrus_device {
 	resource_size_t			rmmio_size;
 	void __iomem			*rmmio;
 
-	struct cirrus_mc			mc;
+	struct cirrus_mc		mc;
 	struct cirrus_mode_info		mode_info;
+	struct drm_plane		primary;
 
 	int				num_crtc;
 	int fb_mtrr;
diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c
index e4b9766..d95de34 100644
--- a/drivers/gpu/drm/cirrus/cirrus_main.c
+++ b/drivers/gpu/drm/cirrus/cirrus_main.c
@@ -308,10 +308,10 @@ cirrus_dumb_mmap_offset(struct drm_file *file,
 
 	drm_gem_object_unreference(obj);
 	ret = 0;
+
 out_unlock:
 	mutex_unlock(&dev->struct_mutex);
 	return ret;
-
 }
 
 bool cirrus_check_framebuffer(struct cirrus_device *cdev, int width, int height,
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
index 61385f2..7a7c874 100644
--- a/drivers/gpu/drm/cirrus/cirrus_mode.c
+++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
@@ -27,6 +27,10 @@
 #define PALETTE_INDEX 0x8
 #define PALETTE_DATA 0x9
 
+static const uint32_t cirrus_primary_formats[] = {
+	DRM_FORMAT_RGB888,
+};
+
 /*
  * This file contains setup code for the CRTC.
  */
@@ -126,77 +130,11 @@ static void cirrus_set_start_address(struct drm_crtc *crtc, unsigned offset)
 	WREG_CRT(0x1d, tmp);
 }
 
-/* cirrus is different - we will force move buffers out of VRAM */
-static int cirrus_crtc_do_set_base(struct drm_crtc *crtc,
-				struct drm_framebuffer *fb,
-				int x, int y, int atomic)
-{
-	struct cirrus_device *cdev = crtc->dev->dev_private;
-	struct drm_gem_object *obj;
-	struct cirrus_framebuffer *cirrus_fb;
-	struct cirrus_bo *bo;
-	int ret;
-	u64 gpu_addr;
-
-	/* push the previous fb to system ram */
-	if (!atomic && fb) {
-		cirrus_fb = to_cirrus_framebuffer(fb);
-		obj = cirrus_fb->obj;
-		bo = gem_to_cirrus_bo(obj);
-		ret = cirrus_bo_reserve(bo, false);
-		if (ret)
-			return ret;
-		cirrus_bo_push_sysram(bo);
-		cirrus_bo_unreserve(bo);
-	}
-
-	cirrus_fb = to_cirrus_framebuffer(crtc->primary->fb);
-	obj = cirrus_fb->obj;
-	bo = gem_to_cirrus_bo(obj);
-
-	ret = cirrus_bo_reserve(bo, false);
-	if (ret)
-		return ret;
-
-	ret = cirrus_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
-	if (ret) {
-		cirrus_bo_unreserve(bo);
-		return ret;
-	}
-
-	if (&cdev->mode_info.gfbdev->gfb == cirrus_fb) {
-		/* if pushing console in kmap it */
-		ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
-		if (ret)
-			DRM_ERROR("failed to kmap fbcon\n");
-	}
-	cirrus_bo_unreserve(bo);
-
-	cirrus_set_start_address(crtc, (u32)gpu_addr);
-	return 0;
-}
-
-static int cirrus_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-			     struct drm_framebuffer *old_fb)
-{
-	return cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0);
-}
-
-/*
- * The meat of this driver. The core passes us a mode and we have to program
- * it. The modesetting here is the bare minimum required to satisfy the qemu
- * emulation of this hardware, and running this against a real device is
- * likely to result in an inadequately programmed mode. We've already had
- * the opportunity to modify the mode, so whatever we receive here should
- * be something that can be correctly programmed and displayed
- */
-static int cirrus_crtc_mode_set(struct drm_crtc *crtc,
-				struct drm_display_mode *mode,
-				struct drm_display_mode *adjusted_mode,
-				int x, int y, struct drm_framebuffer *old_fb)
+static void cirrus_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
 	struct drm_device *dev = crtc->dev;
 	struct cirrus_device *cdev = dev->dev_private;
+	struct drm_display_mode *mode = &crtc->mode;
 	int hsyncstart, hsyncend, htotal, hdispend;
 	int vtotal, vdispend;
 	int tmp;
@@ -286,7 +224,7 @@ static int cirrus_crtc_mode_set(struct drm_crtc *crtc,
 		hdr = 0xc5;
 		break;
 	default:
-		return -1;
+		return;
 	}
 
 	WREG_SEQ(0x7, sr07);
@@ -308,11 +246,9 @@ static int cirrus_crtc_mode_set(struct drm_crtc *crtc,
 	WREG_GFX(VGA_GFX_MISC, 0x01);
 
 	WREG_HDR(hdr);
-	cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0);
 
 	/* Unblank (needed on S3 resume, vgabios doesn't do it then) */
 	outb(0x20, 0x3c0);
-	return 0;
 }
 
 /*
@@ -373,8 +309,9 @@ static const struct drm_crtc_funcs cirrus_crtc_funcs = {
 static const struct drm_crtc_helper_funcs cirrus_helper_funcs = {
 	.dpms = cirrus_crtc_dpms,
 	.mode_fixup = cirrus_crtc_mode_fixup,
-	.mode_set = cirrus_crtc_mode_set,
-	.mode_set_base = cirrus_crtc_mode_set_base,
+	.mode_set = drm_helper_crtc_mode_set,
+	.mode_set_base = drm_helper_crtc_mode_set_base,
+	.mode_set_nofb = cirrus_crtc_mode_set_nofb,
 	.prepare = cirrus_crtc_prepare,
 	.commit = cirrus_crtc_commit,
 	.load_lut = cirrus_crtc_load_lut,
@@ -394,7 +331,8 @@ static void cirrus_crtc_init(struct drm_device *dev)
 	if (cirrus_crtc == NULL)
 		return;
 
-	drm_crtc_init(dev, &cirrus_crtc->base, &cirrus_crtc_funcs);
+	drm_crtc_init_with_planes(dev, &cirrus_crtc->base, &cdev->primary,
+				  NULL, &cirrus_crtc_funcs);
 
 	drm_mode_crtc_set_gamma_size(&cirrus_crtc->base, CIRRUS_LUT_SIZE);
 	cdev->mode_info.crtc = cirrus_crtc;
@@ -408,6 +346,119 @@ static void cirrus_crtc_init(struct drm_device *dev)
 	drm_crtc_helper_add(&cirrus_crtc->base, &cirrus_helper_funcs);
 }
 
+static int cirrus_plane_prepare_fb(struct drm_plane *plane,
+				struct drm_framebuffer *fb,
+				const struct drm_plane_state *new_state)
+{
+	struct cirrus_device *cdev =
+		container_of(plane, struct cirrus_device, primary);
+	struct cirrus_framebuffer *cirrus_fb;
+	struct drm_gem_object *obj;
+	struct cirrus_bo *bo;
+	int ret;
+
+	cirrus_fb = to_cirrus_framebuffer(fb);
+	obj = cirrus_fb->obj;
+	bo = gem_to_cirrus_bo(obj);
+
+	ret = cirrus_bo_reserve(bo, false);
+	if (ret)
+		return ret;
+
+	ret = cirrus_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL);
+	if (ret) {
+		cirrus_bo_unreserve(bo);
+		return ret;
+	}
+
+	if (&cdev->mode_info.gfbdev->gfb == cirrus_fb) {
+		/* if pushing console in kmap it */
+		ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
+		if (ret)
+			DRM_ERROR("failed to kmap fbcon\n");
+	}
+	cirrus_bo_unreserve(bo);
+
+	return 0;
+}
+
+static void cirrus_plane_cleanup_fb(struct drm_plane *plane,
+				struct drm_framebuffer *old_fb,
+				const struct drm_plane_state *old_state)
+{
+	struct cirrus_framebuffer *cirrus_fb;
+	struct drm_gem_object *obj;
+	struct cirrus_bo *bo;
+
+	cirrus_fb = to_cirrus_framebuffer(old_fb);
+	obj = cirrus_fb->obj;
+	bo = gem_to_cirrus_bo(obj);
+
+	cirrus_bo_reserve(bo, false);
+	cirrus_bo_push_sysram(bo);
+	cirrus_bo_unreserve(bo);
+}
+
+static int cirrus_plane_atomic_check(struct drm_plane *plane,
+				struct drm_plane_state *plane_state)
+{
+	return 0;
+}
+
+static void cirrus_plane_atomic_update(struct drm_plane *plane,
+				struct drm_plane_state *old_state)
+{
+	struct cirrus_framebuffer *cirrus_fb;
+	struct drm_gem_object *obj;
+	struct cirrus_bo *bo;
+	u64 gpu_addr;
+
+	cirrus_fb = to_cirrus_framebuffer(plane->state->fb);
+	obj = cirrus_fb->obj;
+	bo = gem_to_cirrus_bo(obj);
+	gpu_addr = bo->bo.offset;
+
+	cirrus_set_start_address(plane->state->crtc, (u32)gpu_addr);
+}
+
+static void cirrus_plane_atomic_disable(struct drm_plane *plane,
+				struct drm_plane_state *old_state)
+{
+}
+
+static const struct drm_plane_funcs cirrus_plane_funcs = {
+	.update_plane = drm_plane_helper_update,
+	.disable_plane = drm_plane_helper_disable,
+};
+
+static const struct drm_plane_helper_funcs cirrus_plane_helper_funcs = {
+	.prepare_fb = cirrus_plane_prepare_fb,
+	.cleanup_fb = cirrus_plane_cleanup_fb,
+	.atomic_check = cirrus_plane_atomic_check,
+	.atomic_update = cirrus_plane_atomic_update,
+	.atomic_disable = cirrus_plane_atomic_disable,
+};
+
+static void cirrus_plane_init(struct drm_device *dev)
+{
+	struct cirrus_device *cirrus = dev->dev_private;
+	struct drm_plane *primary = &cirrus->primary;
+	int r;
+
+	r = drm_universal_plane_init(dev, primary, 0,
+				     &cirrus_plane_funcs,
+				     cirrus_primary_formats,
+				     ARRAY_SIZE(cirrus_primary_formats),
+				     DRM_PLANE_TYPE_PRIMARY);
+
+	if (r) {
+		DRM_DEBUG_KMS("Failed to init primary plane.\n");
+		return;
+	}
+
+	drm_plane_helper_add(primary, &cirrus_plane_helper_funcs);
+}
+
 /** Sets the color ramps on behalf of fbcon */
 void cirrus_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
 			      u16 blue, int regno)
@@ -430,7 +481,6 @@ void cirrus_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
 	*blue = cirrus_crtc->lut_b[regno];
 }
 
-
 static bool cirrus_encoder_mode_fixup(struct drm_encoder *encoder,
 				      const struct drm_display_mode *mode,
 				      struct drm_display_mode *adjusted_mode)
@@ -583,6 +633,7 @@ int cirrus_modeset_init(struct cirrus_device *cdev)
 	/* don't prefer a shadow on virt GPU */
 	cdev->dev->mode_config.prefer_shadow = 0;
 
+	cirrus_plane_init(cdev->dev);
 	cirrus_crtc_init(cdev->dev);
 
 	encoder = cirrus_encoder_init(cdev->dev);
