diff mbox

[RFC,1/7] drm: allow the drivers to call the vblank IOCTL internally

Message ID 1416426435-2237-3-git-send-email-przanoni@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Paulo Zanoni Nov. 19, 2014, 7:47 p.m. UTC
From: Paulo Zanoni <paulo.r.zanoni@intel.com>

The i915.ko driver needs a way to schedule certain functions to run
after some amount of vblanks. There are many different pieces of the
driver which could benefit from that.

Since what we want is essentially the vblank ioctl, this patch does
the minimum change required to allow this ioctl to be called
internally.  The noticeable thing here is that the drivers pass a
callback function, which is called by drm.ko after the specified
amount of vblanks passes.

The great benefit of this minimal change is that all the code
responsible for taking care of properly emptying the queues (e.g.,
when the CRTC is disabled) is already there, so we don't need to
rewrite it.

The current wait vblank IOCTL is now implemented on top of these
changes, and it provides its own callback: send_vblank_event().

Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
---
 drivers/gpu/drm/drm_ioctl.c         |  2 +-
 drivers/gpu/drm/drm_irq.c           | 65 +++++++++++++++++++++++++++++--------
 drivers/gpu/drm/i915/i915_debugfs.c | 62 +++++++++++++++++++++++++++++++++++
 include/drm/drmP.h                  | 13 ++++++--
 4 files changed, 125 insertions(+), 17 deletions(-)

Comments

Matt Roper Dec. 3, 2014, 2:14 a.m. UTC | #1
On Wed, Nov 19, 2014 at 05:47:09PM -0200, Paulo Zanoni wrote:
> From: Paulo Zanoni <paulo.r.zanoni@intel.com>
> 
> The i915.ko driver needs a way to schedule certain functions to run
> after some amount of vblanks. There are many different pieces of the
> driver which could benefit from that.
> 
> Since what we want is essentially the vblank ioctl, this patch does
> the minimum change required to allow this ioctl to be called
> internally.  The noticeable thing here is that the drivers pass a
> callback function, which is called by drm.ko after the specified
> amount of vblanks passes.
> 
> The great benefit of this minimal change is that all the code
> responsible for taking care of properly emptying the queues (e.g.,
> when the CRTC is disabled) is already there, so we don't need to
> rewrite it.
> 
> The current wait vblank IOCTL is now implemented on top of these
> changes, and it provides its own callback: send_vblank_event().
> 
> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
> ---
...
> +
> +int drm_wait_vblank_kernel(struct drm_crtc *crtc, int count, bool absolute,
> +			   drm_vblank_callback_t callback,
> +			   unsigned long user_data)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	union drm_wait_vblank vblwait;
> +	int type = 0;
> +
> +	type |= absolute ? _DRM_VBLANK_ABSOLUTE : _DRM_VBLANK_RELATIVE;
> +	type |= drm_crtc_index(crtc) << _DRM_VBLANK_HIGH_CRTC_SHIFT;
> +	if (callback)
> +		type |= _DRM_VBLANK_EVENT;

Need some kerneldoc on this function.  It looks like if we have a NULL
callback this turns into a more general version of drm_wait_one_vblank()
that can handle arbitrary delay counts, right?  Is there a case where a
multi-vblank wait would be useful internal to the kernel?  If not, it
might be worth just returning failure on that case for now until we have
an actual caller.


Matt
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 00587a1..fb35173 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -578,7 +578,7 @@  static const struct drm_ioctl_desc drm_ioctls[] = {
 	DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_legacy_sg_alloc, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 	DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_legacy_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 
-	DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, DRM_UNLOCKED),
+	DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank_ioctl, DRM_UNLOCKED),
 
 	DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0),
 
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 0e47df4..f031f77 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -882,7 +882,8 @@  EXPORT_SYMBOL(drm_vblank_count_and_time);
 
 static void send_vblank_event(struct drm_device *dev,
 		struct drm_pending_vblank_event *e,
-		unsigned long seq, struct timeval *now)
+		unsigned long seq, struct timeval *now,
+		bool premature)
 {
 	WARN_ON_SMP(!spin_is_locked(&dev->event_lock));
 	e->event.sequence = seq;
@@ -918,7 +919,7 @@  void drm_send_vblank_event(struct drm_device *dev, int crtc,
 		now = get_drm_timestamp();
 	}
 	e->pipe = crtc;
-	send_vblank_event(dev, e, seq, &now);
+	send_vblank_event(dev, e, seq, &now, false);
 }
 EXPORT_SYMBOL(drm_send_vblank_event);
 
@@ -1159,7 +1160,7 @@  void drm_vblank_off(struct drm_device *dev, int crtc)
 			  e->event.sequence, seq);
 		list_del(&e->base.link);
 		drm_vblank_put(dev, e->pipe);
-		send_vblank_event(dev, e, seq, &now);
+		e->callback(dev, e, seq, &now, true);
 	}
 	spin_unlock_irqrestore(&dev->event_lock, irqflags);
 }
@@ -1373,7 +1374,8 @@  int drm_modeset_ctl(struct drm_device *dev, void *data,
 
 static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
 				  union drm_wait_vblank *vblwait,
-				  struct drm_file *file_priv)
+				  struct drm_file *file_priv,
+				  drm_vblank_callback_t callback)
 {
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	struct drm_pending_vblank_event *e;
@@ -1396,6 +1398,7 @@  static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
 	e->base.event = &e->event.base;
 	e->base.file_priv = file_priv;
 	e->base.destroy = (void (*) (struct drm_pending_event *)) kfree;
+	e->callback = callback;
 
 	spin_lock_irqsave(&dev->event_lock, flags);
 
@@ -1411,12 +1414,15 @@  static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
 		goto err_unlock;
 	}
 
-	if (file_priv->event_space < sizeof e->event) {
-		ret = -EBUSY;
-		goto err_unlock;
+	if (file_priv) {
+		if (file_priv->event_space < sizeof e->event) {
+			ret = -EBUSY;
+			goto err_unlock;
+		}
+
+		file_priv->event_space -= sizeof e->event;
 	}
 
-	file_priv->event_space -= sizeof e->event;
 	seq = drm_vblank_count_and_time(dev, pipe, &now);
 
 	if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) &&
@@ -1434,7 +1440,7 @@  static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
 	e->event.sequence = vblwait->request.sequence;
 	if ((seq - vblwait->request.sequence) <= (1 << 23)) {
 		drm_vblank_put(dev, pipe);
-		send_vblank_event(dev, e, seq, &now);
+		e->callback(dev, e, seq, &now, false);
 		vblwait->reply.sequence = seq;
 	} else {
 		/* drm_handle_vblank_events will call drm_vblank_put */
@@ -1468,11 +1474,12 @@  err_put:
  * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that
  * after a timeout with no further vblank waits scheduled).
  */
-int drm_wait_vblank(struct drm_device *dev, void *data,
-		    struct drm_file *file_priv)
+static int __drm_wait_vblank(struct drm_device *dev,
+			     union drm_wait_vblank *vblwait,
+			     struct drm_file *file_priv,
+			     drm_vblank_callback_t callback)
 {
 	struct drm_vblank_crtc *vblank;
-	union drm_wait_vblank *vblwait = data;
 	int ret;
 	unsigned int flags, seq, crtc, high_crtc;
 
@@ -1525,7 +1532,8 @@  int drm_wait_vblank(struct drm_device *dev, void *data,
 		/* must hold on to the vblank ref until the event fires
 		 * drm_vblank_put will be called asynchronously
 		 */
-		return drm_queue_vblank_event(dev, crtc, vblwait, file_priv);
+		return drm_queue_vblank_event(dev, crtc, vblwait, file_priv,
+					      callback);
 	}
 
 	if ((flags & _DRM_VBLANK_NEXTONMISS) &&
@@ -1560,6 +1568,35 @@  done:
 	return ret;
 }
 
+int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
+			  struct drm_file *file_priv)
+{
+	union drm_wait_vblank *vblwait = data;
+
+	return __drm_wait_vblank(dev, vblwait, file_priv, send_vblank_event);
+}
+
+int drm_wait_vblank_kernel(struct drm_crtc *crtc, int count, bool absolute,
+			   drm_vblank_callback_t callback,
+			   unsigned long user_data)
+{
+	struct drm_device *dev = crtc->dev;
+	union drm_wait_vblank vblwait;
+	int type = 0;
+
+	type |= absolute ? _DRM_VBLANK_ABSOLUTE : _DRM_VBLANK_RELATIVE;
+	type |= drm_crtc_index(crtc) << _DRM_VBLANK_HIGH_CRTC_SHIFT;
+	if (callback)
+		type |= _DRM_VBLANK_EVENT;
+
+	vblwait.request.type = type;
+	vblwait.request.sequence = count;
+	vblwait.request.signal = user_data;
+
+	return __drm_wait_vblank(dev, &vblwait, NULL, callback);
+}
+EXPORT_SYMBOL(drm_wait_vblank_kernel);
+
 static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
 {
 	struct drm_pending_vblank_event *e, *t;
@@ -1581,7 +1618,7 @@  static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
 
 		list_del(&e->base.link);
 		drm_vblank_put(dev, e->pipe);
-		send_vblank_event(dev, e, seq, &now);
+		e->callback(dev, e, seq, &now, false);
 	}
 
 	trace_drm_vblank_event(crtc, seq);
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 319da61..f989587 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -2715,6 +2715,67 @@  static int i915_ddb_info(struct seq_file *m, void *unused)
 	return 0;
 }
 
+struct vblank_data {
+	int eight;
+	const char *message;
+};
+
+static void vblank_callback(struct drm_device *dev,
+			    struct drm_pending_vblank_event *e,
+			    unsigned long seq, struct timeval *now,
+			    bool premature)
+{
+	struct vblank_data *data = (struct vblank_data *)e->event.user_data;
+
+	WARN_ON(data->eight != 8);
+	DRM_DEBUG_KMS("vblank callback, seq: %lu, premature: %s message:%s\n",
+		      seq, yesno(premature), data->message);
+
+	e->base.destroy(&e->base);
+	kfree(data);
+}
+
+static int i915_vblank_queue_test_new(struct seq_file *m, void *unused)
+{
+	struct drm_info_node *node = m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct intel_crtc *crtc = NULL;
+	int ret = 0;
+	struct vblank_data *data;
+
+	for_each_intel_crtc(dev, crtc)
+		if (crtc->pipe == PIPE_A)
+			break;
+	if (WARN_ON(!crtc))
+		return -EINVAL;
+
+	data = kzalloc(sizeof *data, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->message = "hello world";
+	data->eight = 8;
+
+	DRM_DEBUG_KMS("waiting 60 vblanks (no callback)\n");
+	ret = drm_wait_vblank_kernel(&crtc->base, 60, false, NULL, 0);
+	if (ret) {
+		DRM_DEBUG_KMS("vblank wait error: %d\n", ret);
+		return ret;
+	}
+	DRM_DEBUG_KMS("vblank wait done\n");
+
+	DRM_DEBUG_KMS("scheduling 60 vblanks (with callback)\n");
+	ret = drm_wait_vblank_kernel(&crtc->base, 60, false, vblank_callback,
+				     (unsigned long) data);
+	if (ret) {
+		DRM_DEBUG_KMS("vblank schedule error: %d\n", ret);
+		return ret;
+	}
+	DRM_DEBUG_KMS("vblanks scheduled\n");
+
+	return 0;
+}
+
 struct pipe_crc_info {
 	const char *name;
 	struct drm_device *dev;
@@ -4289,6 +4350,7 @@  static const struct drm_info_list i915_debugfs_list[] = {
 	{"i915_dp_mst_info", i915_dp_mst_info, 0},
 	{"i915_wa_registers", i915_wa_registers, 0},
 	{"i915_ddb_info", i915_ddb_info, 0},
+	{"i915_vblank_queue_test_new", i915_vblank_queue_test_new, 0},
 };
 #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
 
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index be776fb..39d0d87 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -660,11 +660,16 @@  struct drm_minor {
 	struct drm_mode_group mode_group;
 };
 
+typedef void (*drm_vblank_callback_t)(struct drm_device *dev,
+				      struct drm_pending_vblank_event *e,
+				      unsigned long seq, struct timeval *now,
+				      bool premature);
 
 struct drm_pending_vblank_event {
 	struct drm_pending_event base;
 	int pipe;
 	struct drm_event_vblank event;
+	drm_vblank_callback_t callback;
 };
 
 struct drm_vblank_crtc {
@@ -898,8 +903,12 @@  extern int drm_irq_install(struct drm_device *dev, int irq);
 extern int drm_irq_uninstall(struct drm_device *dev);
 
 extern int drm_vblank_init(struct drm_device *dev, int num_crtcs);
-extern int drm_wait_vblank(struct drm_device *dev, void *data,
-			   struct drm_file *filp);
+extern int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
+				 struct drm_file *filp);
+extern int drm_wait_vblank_kernel(struct drm_crtc *crtc, int count,
+				  bool absolute,
+				  drm_vblank_callback_t callback,
+				  unsigned long user_data);
 extern u32 drm_vblank_count(struct drm_device *dev, int crtc);
 extern u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc,
 				     struct timeval *vblanktime);