diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 77f357b..d0d1dde 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -339,6 +339,8 @@ void drm_vblank_cleanup(struct drm_device *dev)
 			drm_core_check_feature(dev, DRIVER_MODESET));
 
 		del_timer_sync(&vblank->disable_timer);
+
+		flush_work(&vblank->unprepare.work);
 	}
 
 	kfree(dev->vblank);
@@ -347,6 +349,24 @@ void drm_vblank_cleanup(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_vblank_cleanup);
 
+static void drm_vblank_unprepare_work_fn(struct work_struct *work)
+{
+	struct drm_vblank_crtc *vblank;
+	struct drm_device *dev;
+
+	vblank = container_of(work, typeof(*vblank), unprepare.work);
+	dev = vblank->dev;
+
+	if (atomic_read(&vblank->unprepare.counter) == 0)
+		return;
+
+	do {
+		if (dev->driver->unprepare_vblank &&
+		    atomic_read(&vblank->refcount) == 0)
+			dev->driver->unprepare_vblank(dev, vblank->pipe);
+	} while (!atomic_dec_and_test(&vblank->unprepare.counter));
+}
+
 /**
  * drm_vblank_init - initialize vblank support
  * @dev: DRM device
@@ -380,6 +400,9 @@ int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs)
 		setup_timer(&vblank->disable_timer, vblank_disable_fn,
 			    (unsigned long)vblank);
 		seqlock_init(&vblank->seqlock);
+
+		INIT_WORK(&vblank->unprepare.work,
+			  drm_vblank_unprepare_work_fn);
 	}
 
 	DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
@@ -1117,6 +1140,10 @@ static int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
 	if (WARN_ON(pipe >= dev->num_crtcs))
 		return -EINVAL;
 
+	if (dev->driver->prepare_vblank &&
+	    atomic_read(&vblank->refcount) == 0)
+		dev->driver->prepare_vblank(dev, pipe);
+
 	spin_lock_irqsave(&dev->vbl_lock, irqflags);
 	/* Going from 0->1 means we have to enable interrupts again */
 	if (atomic_add_return(1, &vblank->refcount) == 1) {
@@ -1129,6 +1156,10 @@ static int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
 	}
 	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
 
+	if (dev->driver->unprepare_vblank &&
+	    atomic_read(&vblank->refcount) == 0)
+		dev->driver->unprepare_vblank(dev, pipe);
+
 	return ret;
 }
 
@@ -1178,6 +1209,11 @@ static void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
 			mod_timer(&vblank->disable_timer,
 				  jiffies + ((drm_vblank_offdelay * HZ)/1000));
 	}
+
+	if (atomic_read(&vblank->refcount) == 0) {
+		atomic_inc(&vblank->unprepare.counter);
+		schedule_work(&vblank->unprepare.work);
+	}
 }
 
 /**
@@ -1603,7 +1639,6 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
 	}
 
 	spin_unlock_irqrestore(&dev->event_lock, flags);
-
 	return 0;
 
 err_unlock:
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index d377865..c6ca061 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -422,6 +422,49 @@ struct drm_driver {
 	u32 (*get_vblank_counter) (struct drm_device *dev, unsigned int pipe);
 
 	/**
+	 * prepare_vblank - Optional prepare vblank hook.
+	 * @dev: DRM device
+	 * @pipe: counter to fetch
+	 *
+	 * Drivers that need to handle any kind of mutex or any other sleeping
+	 * code in combination with vblanks need to implement this hook
+	 * that will be called before drm_vblank_get spin_lock gets.
+	 *
+	 * Prepare/Unprepare was designed to be able to sleep so they are not
+	 * free from concurrence. There might be few simultaneous calls.
+	 * This concurrence needs to be handled on the driver side that is
+	 * implementing these hooks. Probably with protected get/put counters.
+	 *
+	 * If this is used with sleeping code you need to make sure you are
+	 * not calling drm_crtc_vblank_get inside spinlock areas or with local
+	 * irq disabled.
+	 *
+	 */
+	void (*prepare_vblank) (struct drm_device *dev, unsigned int pipe);
+
+	/**
+	 * unprepare_vblank - Optional unprepare vblank hook.
+	 * @dev: DRM device
+	 * @pipe: counter to fetch
+	 *
+	 * Drivers that need to handle any kind of mutex or any other sleeping
+	 * code in combination with vblanks need to implement this hook
+	 * that will be called in a work queue to be executed after spin lock
+	 * areas of drm_vblank_put.
+	 *
+	 * Prepare/Unprepare was designed to be able to sleep so they are not
+	 * free from concurrence. There might be few simultaneous calls.
+	 * This concurrence needs to be handled on the driver side that is
+	 * implementing these hooks. Probably with protected get/put counters.
+	 *
+	 * Unprepare is called from a workqueue because asynchronous
+	 * drm_crtc_vblank_put will be in spinlock areas. So it is safe to
+	 * keep drm_crtc_vblank_put in areas that cannot spleep.
+	 */
+	void (*unprepare_vblank) (struct drm_device *dev, unsigned int pipe);
+
+
+	/**
 	 * enable_vblank - enable vblank interrupt events
 	 * @dev: DRM device
 	 * @pipe: which irq to enable
diff --git a/include/drm/drm_irq.h b/include/drm/drm_irq.h
index 2401b14..93a9e9d 100644
--- a/include/drm/drm_irq.h
+++ b/include/drm/drm_irq.h
@@ -45,6 +45,20 @@ struct drm_pending_vblank_event {
 };
 
 /**
+ * struct drm_vblank_unprepare - unprepare vblank
+ */
+struct drm_vblank_unprepare {
+	/**
+	 * @work: Delayed work to handle unprepare out of spinlock areas
+	 */
+	struct work_struct work;
+	/**
+	 * @counter: Number of vblanks handled
+	 */
+	atomic_t counter;
+};
+
+/**
  * struct drm_vblank_crtc - vblank tracking for a CRTC
  *
  * This structure tracks the vblank state for one CRTC.
@@ -128,6 +142,12 @@ struct drm_vblank_crtc {
 	 * disabling functions multiple times.
 	 */
 	bool enabled;
+	/**
+	 * @unprepare: Tracks the amount of drm_vblank_put handled that needs
+	 * a delayed unprepare call so the unprepare can run out of protected
+	 * areas where code can sleep.
+	 */
+	struct drm_vblank_unprepare unprepare;
 };
 
 extern int drm_irq_install(struct drm_device *dev, int irq);
