diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index e2bfdda..59a583f 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -527,10 +527,11 @@ static int i915_drm_freeze(struct drm_device *dev)
 		 * Disable CRTCs directly since we want to preserve sw state
 		 * for _thaw.
 		 */
+		cancel_work_sync(&dev_priv->crtc_work);
 		mutex_lock(&dev->mode_config.mutex);
 		for_each_crtc(dev, crtc) {
 			mutex_lock(&crtc->mutex);
-			dev_priv->display.crtc_disable(crtc);
+			dev_priv->display._crtc_disable(crtc);
 			mutex_unlock(&crtc->mutex);
 		}
 		mutex_unlock(&dev->mode_config.mutex);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index bea9ab40..bbfe402 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -447,8 +447,8 @@ struct drm_i915_display_funcs {
 	int (*crtc_mode_set)(struct drm_crtc *crtc,
 			     int x, int y,
 			     struct drm_framebuffer *old_fb);
-	void (*crtc_enable)(struct drm_crtc *crtc);
-	void (*crtc_disable)(struct drm_crtc *crtc);
+	void (*_crtc_enable)(struct drm_crtc *crtc);
+	void (*_crtc_disable)(struct drm_crtc *crtc);
 	void (*off)(struct drm_crtc *crtc);
 	void (*write_eld)(struct drm_connector *connector,
 			  struct drm_crtc *crtc,
@@ -1432,6 +1432,14 @@ struct drm_i915_private {
 	/* Display functions */
 	struct drm_i915_display_funcs display;
 
+	/**
+	 * CRTC work queue handling.  Enable/disable calls are queued
+	 * into the list and processed by the CRTC work function at some
+	 * later time, or inline by a call to intel_sync_crtcs().
+	 */
+	struct list_head crtc_work_queue;
+	struct work_struct crtc_work;
+
 	/* PCH chipset type */
 	enum intel_pch pch_type;
 	unsigned short pch_id;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 731cd01..d9e5d36 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -1973,6 +1973,141 @@ static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv)
 	I915_WRITE(_TRANSA_CHICKEN2, val);
 }
 
+struct intel_crtc_work {
+	/**
+	 * Whether to enable or disable the given CRTC
+	 */
+	bool enable;
+	/**
+	 * CRTC to operate on
+	 */
+	struct intel_crtc *intel_crtc;
+	/**
+	 * Used to link to dev_priv->crtc_work_queue, protected
+	 * by mode_config mutex.
+	 */
+	struct list_head head;
+};
+
+/**
+ * intel_sync_crtcs - complete any pending CRTC enable/disable calls
+ * @dev_priv: i915 driver struct
+ *
+ * Walk the CRTC work queue and perform the enable/disable calls in the
+ * order they were added.
+ *
+ * This function will return when the enable/disable calls have been completed,
+ * and so may take many milliseconds before returning.
+ */
+static void intel_sync_crtcs(struct drm_i915_private *dev_priv)
+{
+	struct drm_device *dev = dev_priv->dev;
+	struct intel_crtc_work *crtc_work, *tmp;
+
+	WARN(!mutex_is_locked(&dev->mode_config.mutex),
+	     "need mode_config mutex\n");
+
+	list_for_each_entry_safe(crtc_work, tmp, &dev_priv->crtc_work_queue,
+				 head) {
+		struct drm_crtc *crtc = &crtc_work->intel_crtc->base;
+
+		mutex_lock(&crtc->mutex);
+		if (crtc_work->enable)
+			dev_priv->display._crtc_enable(crtc);
+		else
+			dev_priv->display._crtc_disable(crtc);
+		mutex_unlock(&crtc->mutex);
+		list_del(&crtc_work->head);
+		kfree(crtc_work);
+	}
+
+	intel_modeset_check_state(dev);
+}
+
+/**
+ * intel_crtc_work - CRTC queue processing function
+ * @work: crtc_work struct from drm_i915_private
+ *
+ * Just calls intel_sync_crtcs() to take the lock and process the list if any
+ * entries are present.
+ */
+static void intel_crtc_work(struct work_struct *work)
+{
+	struct drm_i915_private *dev_priv =
+		container_of(work, struct drm_i915_private, crtc_work);
+	struct drm_device *dev = dev_priv->dev;
+
+	mutex_lock(&dev->mode_config.mutex);
+	intel_sync_crtcs(dev_priv);
+	mutex_unlock(&dev->mode_config.mutex);
+}
+
+/**
+ * intel_queue_crtc_disable - queue a disable on a given crtc
+ * @crtc: drm CRTC to disable
+ *
+ * Allocates an intel_crtc_work struct and adds it to the crtc_work_queue
+ * for later processing by the worker thread or an intel_sync_crtcs() call.
+ */
+void intel_queue_crtc_disable(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	struct intel_crtc_work *work;
+
+	WARN(!mutex_is_locked(&dev->mode_config.mutex),
+	     "need mode_config mutex\n");
+
+	work = kmalloc(sizeof(*work), GFP_KERNEL);
+	if (!work) {
+		intel_sync_crtcs(dev_priv);
+		mutex_lock(&crtc->mutex);
+		dev_priv->display._crtc_disable(&intel_crtc->base);
+		mutex_unlock(&crtc->mutex);
+		return;
+	}
+
+	work->enable = false;
+	work->intel_crtc = intel_crtc;
+
+	list_add_tail(&work->head, &dev_priv->crtc_work_queue);
+	schedule_work(&dev_priv->crtc_work);
+}
+
+/**
+ * intel_queue_crtc_enable - queue an enable on a given crtc
+ * @crtc: drm CRTC to enable
+ *
+ * Allocates an intel_crtc_work struct and adds it to the crtc_work_queue
+ * for later processing by the worker thread or an intel_sync_crtcs() call.
+ */
+static void intel_queue_crtc_enable(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	struct intel_crtc_work *work;
+
+	WARN(!mutex_is_locked(&dev->mode_config.mutex),
+	     "need mode_config mutex\n");
+
+	work = kmalloc(sizeof(*work), GFP_KERNEL);
+	if (!work) {
+		intel_sync_crtcs(dev_priv);
+		mutex_lock(&crtc->mutex);
+		dev_priv->display._crtc_enable(&intel_crtc->base);
+		mutex_unlock(&crtc->mutex);
+		return;
+	}
+
+	work->enable = true;
+	work->intel_crtc = intel_crtc;
+
+	list_add_tail(&work->head, &dev_priv->crtc_work_queue);
+	schedule_work(&dev_priv->crtc_work);
+}
+
 /**
  * intel_enable_pipe - enable a pipe, asserting requirements
  * @crtc: crtc responsible for the pipe
@@ -4845,7 +4980,6 @@ static void intel_crtc_update_sarea(struct drm_crtc *crtc,
 void intel_crtc_update_dpms(struct drm_crtc *crtc)
 {
 	struct drm_device *dev = crtc->dev;
-	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_encoder *intel_encoder;
 	bool enable = false;
 
@@ -4853,9 +4987,9 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc)
 		enable |= intel_encoder->connectors_active;
 
 	if (enable)
-		dev_priv->display.crtc_enable(crtc);
+		intel_queue_crtc_enable(crtc);
 	else
-		dev_priv->display.crtc_disable(crtc);
+		intel_queue_crtc_disable(crtc);
 
 	intel_crtc_update_sarea(crtc, enable);
 }
@@ -4869,7 +5003,7 @@ static void intel_crtc_disable(struct drm_crtc *crtc)
 	/* crtc should still be enabled when we disable it. */
 	WARN_ON(!crtc->enabled);
 
-	dev_priv->display.crtc_disable(crtc);
+	dev_priv->display._crtc_disable(crtc);
 	intel_crtc_update_sarea(crtc, false);
 	dev_priv->display.off(crtc);
 
@@ -8081,6 +8215,10 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
 		goto fail;
 	}
 
+	mutex_lock(&dev->mode_config.mutex);
+	intel_sync_crtcs(dev_priv);
+	mutex_unlock(&dev->mode_config.mutex);
+
 	/* we only need to pin inside GTT if cursor is non-phy */
 	mutex_lock(&dev->struct_mutex);
 	if (!INTEL_INFO(dev)->cursor_needs_physical) {
@@ -9228,6 +9366,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
 	if (work == NULL)
 		return -ENOMEM;
 
+	intel_sync_crtcs(dev_priv);
+
 	work->event = event;
 	work->crtc = crtc;
 	work->old_fb_obj = to_intel_framebuffer(old_fb)->obj;
@@ -10326,7 +10466,7 @@ static int __intel_set_mode(struct drm_crtc *crtc,
 
 	for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) {
 		if (intel_crtc->base.enabled)
-			dev_priv->display.crtc_disable(&intel_crtc->base);
+			intel_queue_crtc_disable(&intel_crtc->base);
 	}
 
 	/* crtc->mode is already used by the ->mode_set callbacks, hence we need
@@ -10389,7 +10529,7 @@ static int __intel_set_mode(struct drm_crtc *crtc,
 	for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) {
 		update_scanline_offset(intel_crtc);
 
-		dev_priv->display.crtc_enable(&intel_crtc->base);
+		intel_queue_crtc_enable(&intel_crtc->base);
 	}
 
 	/* FIXME: add subpixel order */
@@ -10411,6 +10551,7 @@ static int intel_set_mode(struct drm_crtc *crtc,
 
 	ret = __intel_set_mode(crtc, mode, x, y, fb);
 
+	intel_sync_crtcs(crtc->dev->dev_private);
 	if (ret == 0)
 		intel_modeset_check_state(crtc->dev);
 
@@ -11397,8 +11538,8 @@ static void intel_init_display(struct drm_device *dev)
 		dev_priv->display.get_pipe_config = haswell_get_pipe_config;
 		dev_priv->display.get_plane_config = ironlake_get_plane_config;
 		dev_priv->display.crtc_mode_set = haswell_crtc_mode_set;
-		dev_priv->display.crtc_enable = haswell_crtc_enable;
-		dev_priv->display.crtc_disable = haswell_crtc_disable;
+		dev_priv->display._crtc_enable = haswell_crtc_enable;
+		dev_priv->display._crtc_disable = haswell_crtc_disable;
 		dev_priv->display.off = haswell_crtc_off;
 		dev_priv->display.update_primary_plane =
 			ironlake_update_primary_plane;
@@ -11406,8 +11547,8 @@ static void intel_init_display(struct drm_device *dev)
 		dev_priv->display.get_pipe_config = ironlake_get_pipe_config;
 		dev_priv->display.get_plane_config = ironlake_get_plane_config;
 		dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set;
-		dev_priv->display.crtc_enable = ironlake_crtc_enable;
-		dev_priv->display.crtc_disable = ironlake_crtc_disable;
+		dev_priv->display._crtc_enable = ironlake_crtc_enable;
+		dev_priv->display._crtc_disable = ironlake_crtc_disable;
 		dev_priv->display.off = ironlake_crtc_off;
 		dev_priv->display.update_primary_plane =
 			ironlake_update_primary_plane;
@@ -11415,8 +11556,8 @@ static void intel_init_display(struct drm_device *dev)
 		dev_priv->display.get_pipe_config = i9xx_get_pipe_config;
 		dev_priv->display.get_plane_config = i9xx_get_plane_config;
 		dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
-		dev_priv->display.crtc_enable = valleyview_crtc_enable;
-		dev_priv->display.crtc_disable = i9xx_crtc_disable;
+		dev_priv->display._crtc_enable = valleyview_crtc_enable;
+		dev_priv->display._crtc_disable = i9xx_crtc_disable;
 		dev_priv->display.off = i9xx_crtc_off;
 		dev_priv->display.update_primary_plane =
 			i9xx_update_primary_plane;
@@ -11424,8 +11565,8 @@ static void intel_init_display(struct drm_device *dev)
 		dev_priv->display.get_pipe_config = i9xx_get_pipe_config;
 		dev_priv->display.get_plane_config = i9xx_get_plane_config;
 		dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
-		dev_priv->display.crtc_enable = i9xx_crtc_enable;
-		dev_priv->display.crtc_disable = i9xx_crtc_disable;
+		dev_priv->display._crtc_enable = i9xx_crtc_enable;
+		dev_priv->display._crtc_disable = i9xx_crtc_disable;
 		dev_priv->display.off = i9xx_crtc_off;
 		dev_priv->display.update_primary_plane =
 			i9xx_update_primary_plane;
@@ -11732,6 +11873,9 @@ void intel_modeset_init(struct drm_device *dev)
 		      INTEL_INFO(dev)->num_pipes,
 		      INTEL_INFO(dev)->num_pipes > 1 ? "s" : "");
 
+	INIT_WORK(&dev_priv->crtc_work, intel_crtc_work);
+	INIT_LIST_HEAD(&dev_priv->crtc_work_queue);
+
 	for_each_pipe(pipe) {
 		intel_crtc_init(dev, pipe);
 		for_each_sprite(pipe, sprite) {
@@ -11860,7 +12004,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
 		 * ...  */
 		plane = crtc->plane;
 		crtc->plane = !plane;
-		dev_priv->display.crtc_disable(&crtc->base);
+		intel_queue_crtc_disable(&crtc->base);
 		crtc->plane = plane;
 
 		/* ... and break all links. */
@@ -12180,8 +12324,6 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
 	} else {
 		intel_modeset_update_staged_output_state(dev);
 	}
-
-	intel_modeset_check_state(dev);
 }
 
 void intel_modeset_gem_init(struct drm_device *dev)
@@ -12239,6 +12381,7 @@ void intel_modeset_cleanup(struct drm_device *dev)
 	 */
 	drm_irq_uninstall(dev);
 	cancel_work_sync(&dev_priv->hotplug_work);
+	cancel_work_sync(&dev_priv->crtc_work);
 	/*
 	 * Due to the hpd irq storm handling the hotplug work can re-arm the
 	 * poll handlers. Hence disable polling after hpd handling is shut down.
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index c597b0d..5ef8bdb 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -804,7 +804,7 @@ void intel_mode_from_pipe_config(struct drm_display_mode *mode,
 				 struct intel_crtc_config *pipe_config);
 int intel_format_to_fourcc(int format);
 void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc);
-
+void intel_queue_crtc_disable(struct drm_crtc *crtc);
 
 /* intel_dp.c */
 void intel_dp_init(struct drm_device *dev, int output_reg, enum port port);
