diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 27f5617..71e2420 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -579,8 +579,9 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 	kfree(helper->connector_info);
 	for (i = 0; i < helper->crtc_count; i++) {
 		kfree(helper->crtc_info[i].mode_set.connectors);
-		if (helper->crtc_info[i].mode_set.mode)
-			drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
+		if (helper->crtc_info[i].mode)
+			drm_mode_destroy(helper->dev,
+					 helper->crtc_info[i].mode);
 	}
 	kfree(helper->crtc_info);
 }
@@ -1525,11 +1526,17 @@ retry:
 			DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
 				      fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
 			modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
+			if (modes[i])
+				modes[i] = drm_mode_duplicate(fb_helper->dev,
+				                              modes[i]);
 		}
 		/* No preferred modes, pick one off the list */
 		if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
 			list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
 				break;
+			if (modes[i])
+				modes[i] = drm_mode_duplicate(fb_helper->dev,
+				                              modes[i]);
 		}
 		DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
 			  "none");
@@ -1695,10 +1702,11 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 			fb_crtc->desired_mode = mode;
 			fb_crtc->x = offset->x;
 			fb_crtc->y = offset->y;
-			if (modeset->mode)
-				drm_mode_destroy(dev, modeset->mode);
-			modeset->mode = drm_mode_duplicate(dev,
+			if (fb_crtc->mode)
+				drm_mode_destroy(dev, fb_crtc->mode);
+			fb_crtc->mode = drm_mode_duplicate(dev,
 							   fb_crtc->desired_mode);
+			modeset->mode = fb_crtc->mode;
 			modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
 			modeset->fb = fb_helper->fb;
 			modeset->x = offset->x;
@@ -1708,11 +1716,14 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 
 	/* Clear out any old modes if there are no more connected outputs. */
 	for (i = 0; i < fb_helper->crtc_count; i++) {
-		modeset = &fb_helper->crtc_info[i].mode_set;
+		struct drm_fb_helper_crtc *fb_crtc = &fb_helper->crtc_info[i];
+
+		modeset = &fb_crtc->mode_set;
 		if (modeset->num_connectors == 0) {
 			BUG_ON(modeset->fb);
-			if (modeset->mode)
-				drm_mode_destroy(dev, modeset->mode);
+			if (fb_crtc->mode)
+				drm_mode_destroy(dev, fb_crtc->mode);
+			fb_crtc->mode = NULL;
 			modeset->mode = NULL;
 		}
 	}
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index 757c0d2..83566e0 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -471,6 +471,10 @@ retry:
 						    to_intel_crtc(encoder->crtc)->config);
 			modes[i] = &encoder->crtc->hwmode;
 		}
+
+		if (modes[i])
+			modes[i] =
+				drm_mode_duplicate(encoder->crtc->dev, modes[i]);
 		crtcs[i] = new_crtc;
 
 		DRM_DEBUG_KMS("connector %s on pipe %c [CRTC:%d]: %dx%d%s\n",
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index 5c289f7..3c231b2 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -212,7 +212,7 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
 	struct vmw_private *dev_priv;
 	struct vmw_legacy_display_unit *ldu;
 	struct drm_connector *connector;
-	struct drm_display_mode *mode;
+	const struct drm_display_mode *mode;
 	struct drm_encoder *encoder;
 	struct vmw_framebuffer *vfb;
 	struct drm_framebuffer *fb;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index 7dc591d..213afa5 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -104,7 +104,7 @@ static void vmw_sou_add_active(struct vmw_private *vmw_priv,
 static int vmw_sou_fifo_create(struct vmw_private *dev_priv,
 			       struct vmw_screen_object_unit *sou,
 			       uint32_t x, uint32_t y,
-			       struct drm_display_mode *mode)
+			       const struct drm_display_mode *mode)
 {
 	size_t fifo_size;
 
@@ -254,7 +254,7 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set)
 	struct vmw_private *dev_priv;
 	struct vmw_screen_object_unit *sou;
 	struct drm_connector *connector;
-	struct drm_display_mode *mode;
+	const struct drm_display_mode *mode;
 	struct drm_encoder *encoder;
 	struct vmw_framebuffer *vfb;
 	struct drm_framebuffer *fb;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index dbc1510..7b141d837 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -961,7 +961,7 @@ struct drm_atomic_state {
 struct drm_mode_set {
 	struct drm_framebuffer *fb;
 	struct drm_crtc *crtc;
-	struct drm_display_mode *mode;
+	const struct drm_display_mode *mode;
 
 	uint32_t x;
 	uint32_t y;
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 0dfd94def..e6a4b8c 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -40,6 +40,9 @@ struct drm_fb_offset {
 
 struct drm_fb_helper_crtc {
 	struct drm_mode_set mode_set;
+	/* mode here is a duplicate of mode_set.mode, but non-const so we
+	 * can later free it */
+	struct drm_display_mode *mode;
 	struct drm_display_mode *desired_mode;
 	int x, y;
 };
