diff mbox

[1/3] drm: Add vblank prepare and unprepare hooks.

Message ID 1465436764-29950-2-git-send-email-dhinakaran.pandiyan@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Dhinakaran Pandiyan June 9, 2016, 1:46 a.m. UTC
From: Rodrigo Vivi <rodrigo.vivi@intel.com>

This will allow drivers to control specific power saving
feature and power domains when dealing with vblanks.

Vblanks code are protected by spin_locks where we can't
have anything that can sleep. While power saving features
and power domain code have mutexes to control the states.

Mutex can sleep so they cannot be used inside spin lock areas.
So the easiest way to deal with them currently is to add these
prepare hook for pre enabling vblanks
and unprepare one for post disabling them.

Let's introduce this optional prepare and unprepare
hooks so drivers can deal with cases like this and any other
case that should require sleeping codes interacting with vblanks.

Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
---
 drivers/gpu/drm/drm_irq.c | 28 +++++++++++++++++++++++++++-
 include/drm/drmP.h        | 30 ++++++++++++++++++++++++++++++
 2 files changed, 57 insertions(+), 1 deletion(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index d3124b6..c833a5d 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -411,6 +411,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);
@@ -419,6 +421,20 @@  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;
+
+	do {
+		if (dev->driver->unprepare_vblank)
+			dev->driver->unprepare_vblank(dev, vblank->pipe);
+	} while (!atomic_dec_and_test(&vblank->unprepare.counter));
+}
+
 /**
  * drm_vblank_init - initialize vblank support
  * @dev: DRM device
@@ -451,6 +467,8 @@  int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs)
 		init_waitqueue_head(&vblank->queue);
 		setup_timer(&vblank->disable_timer, vblank_disable_fn,
 			    (unsigned long)vblank);
+		INIT_WORK(&vblank->unprepare.work,
+			  drm_vblank_unprepare_work_fn);
 	}
 
 	DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
@@ -1241,6 +1259,9 @@  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)
+		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) {
@@ -1253,6 +1274,9 @@  int drm_vblank_get(struct drm_device *dev, unsigned int pipe)
 	}
 	spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
 
+	if (ret != 0 && dev->driver->unprepare_vblank)
+		dev->driver->unprepare_vblank(dev, pipe);
+
 	return ret;
 }
 EXPORT_SYMBOL(drm_vblank_get);
@@ -1305,6 +1329,9 @@  void drm_vblank_put(struct drm_device *dev, unsigned int pipe)
 			mod_timer(&vblank->disable_timer,
 				  jiffies + ((drm_vblank_offdelay * HZ)/1000));
 	}
+
+	atomic_inc(&vblank->unprepare.counter);
+	schedule_work(&vblank->unprepare.work);
 }
 EXPORT_SYMBOL(drm_vblank_put);
 
@@ -1740,7 +1767,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 ed89038..544c65f 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -447,6 +447,30 @@  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.
+	 */
+	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.
+	 */
+	void (*unprepare_vblank) (struct drm_device *dev, unsigned int pipe);
+
+
+	/**
 	 * enable_vblank - enable vblank interrupt events
 	 * @dev: DRM device
 	 * @pipe: which irq to enable
@@ -720,6 +744,11 @@  struct drm_pending_vblank_event {
 	struct drm_event_vblank event;
 };
 
+struct drm_vblank_unprepare {
+	struct work_struct work;	/* Post disable worker */
+	atomic_t counter;		/* Number of vblanks handled */
+};
+
 struct drm_vblank_crtc {
 	struct drm_device *dev;		/* pointer to the drm_device */
 	wait_queue_head_t queue;	/**< VBLANK wait queue */
@@ -740,6 +769,7 @@  struct drm_vblank_crtc {
 	int linedur_ns;			/* line duration in ns */
 	bool enabled;			/* so we don't call enable more than
 					   once per disable */
+	struct drm_vblank_unprepare unprepare;  /* Unprepare work helper */
 };
 
 /**