All of lore.kernel.org
 help / color / mirror / Atom feed
From: Paulo Zanoni <przanoni@gmail.com>
To: dri-devel@lists.freedesktop.org
Cc: intel-gfx@lists.freedesktop.org, Paulo Zanoni <paulo.r.zanoni@intel.com>
Subject: [RFC] drm: add a mechanism for drivers to schedule vblank callbacks
Date: Wed, 19 Nov 2014 17:47:08 -0200	[thread overview]
Message-ID: <1416426435-2237-2-git-send-email-przanoni@gmail.com> (raw)
In-Reply-To: <1416426435-2237-1-git-send-email-przanoni@gmail.com>

From: Paulo Zanoni <paulo.r.zanoni@intel.com>

The drivers need a way to schedule functions to be ran after a certain
number of vblanks. The i915.ko driver has plenty of examples where
this could be used, such as for unpinning buffers after a modeset.
Since the vblank wait IOCTL does basically what we want, but for the
user space, in this patch we add a new list of vblank events
(dev->vblank_kernel_list) and handle it exactly like we handle the
user space events.

Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
---
 drivers/gpu/drm/drm_drv.c           |   1 +
 drivers/gpu/drm/drm_irq.c           | 106 ++++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/i915/i915_debugfs.c |  81 +++++++++++++++++++++++++++
 include/drm/drmP.h                  |  22 ++++++++
 4 files changed, 206 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 2e5c7d9..b5ae6c8 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -557,6 +557,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
 	INIT_LIST_HEAD(&dev->vmalist);
 	INIT_LIST_HEAD(&dev->maplist);
 	INIT_LIST_HEAD(&dev->vblank_event_list);
+	INIT_LIST_HEAD(&dev->vblank_kernel_list);
 
 	spin_lock_init(&dev->buf_lock);
 	spin_lock_init(&dev->event_lock);
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 0e47df4..6e04035 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -1107,6 +1107,13 @@ void drm_crtc_wait_one_vblank(struct drm_crtc *crtc)
 }
 EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
 
+static void send_kernel_vblank_event(struct drm_kernel_vblank_item *item)
+{
+	if (item->callback_from_work)
+		schedule_work(&item->work);
+	else
+		item->callback(item);
+}
 /**
  * drm_vblank_off - disable vblank events on a CRTC
  * @dev: DRM device
@@ -1124,7 +1131,8 @@ EXPORT_SYMBOL(drm_crtc_wait_one_vblank);
 void drm_vblank_off(struct drm_device *dev, int crtc)
 {
 	struct drm_vblank_crtc *vblank = &dev->vblank[crtc];
-	struct drm_pending_vblank_event *e, *t;
+	struct drm_pending_vblank_event *e, *et;
+	struct drm_kernel_vblank_item *i, *it;
 	struct timeval now;
 	unsigned long irqflags;
 	unsigned int seq;
@@ -1151,7 +1159,7 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
 	/* Send any queued vblank events, lest the natives grow disquiet */
 	seq = drm_vblank_count_and_time(dev, crtc, &now);
 
-	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
+	list_for_each_entry_safe(e, et, &dev->vblank_event_list, base.link) {
 		if (e->pipe != crtc)
 			continue;
 		DRM_DEBUG("Sending premature vblank event on disable: \
@@ -1161,6 +1169,21 @@ void drm_vblank_off(struct drm_device *dev, int crtc)
 		drm_vblank_put(dev, e->pipe);
 		send_vblank_event(dev, e, seq, &now);
 	}
+
+	list_for_each_entry_safe(i, it, &dev->vblank_kernel_list, link) {
+		int pipe = drm_crtc_index(i->crtc);
+
+		if (pipe != crtc)
+			continue;
+
+		DRM_DEBUG("Sending premature kernel vblank event on disable: \
+			  wanted %d, current %d\n",
+			  i->target_seq, seq);
+		i->premature = true;
+		list_del(&i->link);
+		drm_vblank_put(dev, pipe);
+		send_kernel_vblank_event(i);
+	}
 	spin_unlock_irqrestore(&dev->event_lock, irqflags);
 }
 EXPORT_SYMBOL(drm_vblank_off);
@@ -1560,9 +1583,68 @@ done:
 	return ret;
 }
 
+static void drm_kernel_vblank_work_func(struct work_struct *work)
+{
+	struct drm_kernel_vblank_item *item =
+		container_of(work, struct drm_kernel_vblank_item, work);
+
+	item->callback(item);
+}
+
+int drm_wait_vblank_kernel(struct drm_kernel_vblank_item *item)
+{
+	struct drm_crtc *crtc = item->crtc;
+	struct drm_device *dev = crtc->dev;
+	int pipe = drm_crtc_index(crtc);
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+	unsigned int seq;
+	unsigned long irqflags;
+	int ret = 0;
+
+	if (!dev->irq_enabled)
+		return -EINVAL;
+
+	if (item->callback_from_work)
+		INIT_WORK(&item->work, drm_kernel_vblank_work_func);
+
+	ret = drm_vblank_get(dev, pipe);
+	if (ret) {
+		DRM_DEBUG("failed to acquire vblank counter, %d\n", ret);
+		return ret;
+	}
+
+	seq = drm_vblank_count(dev, pipe);
+	if (!item->absolute)
+		item->target_seq += seq;
+
+	spin_lock_irqsave(&dev->event_lock, irqflags);
+
+	if (!vblank->enabled) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (seq - item->target_seq <= (1 << 23)) {
+		drm_vblank_put(dev, pipe);
+		send_kernel_vblank_event(item);
+	} else {
+		list_add_tail(&item->link, &dev->vblank_kernel_list);
+	}
+
+	spin_unlock_irqrestore(&dev->event_lock, irqflags);
+	return 0;
+
+out:
+	spin_unlock_irqrestore(&dev->event_lock, irqflags);
+	drm_vblank_put(dev, pipe);
+	return ret;
+}
+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;
+	struct drm_pending_vblank_event *e, *et;
+	struct drm_kernel_vblank_item *i, *it;
 	struct timeval now;
 	unsigned int seq;
 
@@ -1570,7 +1652,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
 
 	seq = drm_vblank_count_and_time(dev, crtc, &now);
 
-	list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
+	list_for_each_entry_safe(e, et, &dev->vblank_event_list, base.link) {
 		if (e->pipe != crtc)
 			continue;
 		if ((seq - e->event.sequence) > (1<<23))
@@ -1584,6 +1666,22 @@ static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
 		send_vblank_event(dev, e, seq, &now);
 	}
 
+	list_for_each_entry_safe(i, it, &dev->vblank_kernel_list, link) {
+		int pipe = drm_crtc_index(i->crtc);
+
+		if (pipe != crtc)
+			continue;
+		if ((seq - i->target_seq) > (1<<23))
+			continue;
+
+		DRM_DEBUG("kernel vblank event on %d, current %d\n",
+			  i->target_seq, seq);
+
+		list_del(&i->link);
+		drm_vblank_put(dev, pipe);
+		send_kernel_vblank_event(i);
+	}
+
 	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..e705976 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -2715,6 +2715,86 @@ static int i915_ddb_info(struct seq_file *m, void *unused)
 	return 0;
 }
 
+struct vblank_data {
+	int eight;
+	const char *message;
+	struct drm_kernel_vblank_item item;
+};
+
+static void vblank_callback(struct drm_kernel_vblank_item *item)
+{
+	struct vblank_data *data = container_of(item, struct vblank_data, item);
+
+	WARN_ON(data->eight != 8);
+	WARN_ON(item->callback_from_work != drm_can_sleep());
+	DRM_DEBUG_KMS("vblank callback, premature: %s, message: %s\n",
+		      yesno(item->premature), data->message);
+
+	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 *data1, *data2;
+
+	for_each_intel_crtc(dev, crtc)
+		if (crtc->pipe == PIPE_A)
+			break;
+	if (WARN_ON(!crtc))
+		return -EINVAL;
+
+	data1 = kzalloc(sizeof *data1, GFP_KERNEL);
+	if (!data1)
+		return -ENOMEM;
+
+	data2 = kzalloc(sizeof *data2, GFP_KERNEL);
+	if (!data2) {
+		kfree(data1);
+		return -ENOMEM;
+	}
+
+	data1->message = "hello world (atomic)";
+	data1->eight = 8;
+	data1->item.crtc = &crtc->base;
+	data1->item.target_seq = 60;
+	data1->item.absolute = false;
+	data1->item.callback = vblank_callback;
+	data1->item.callback_from_work = false;
+
+	data2->message = "hello world (work)";
+	data2->eight = 8;
+	data2->item.crtc = &crtc->base;
+	data2->item.target_seq = 60;
+	data2->item.absolute = false;
+	data2->item.callback = vblank_callback;
+	data2->item.callback_from_work = true;
+
+	DRM_DEBUG_KMS("scheduling 60 vblanks (no work)\n");
+	ret = drm_wait_vblank_kernel(&data1->item);
+	if (ret) {
+		DRM_DEBUG_KMS("vblank schedule error: %d\n", ret);
+		kfree(data1);
+		kfree(data2);
+		return ret;
+	}
+	DRM_DEBUG_KMS("vblanks scheduled\n");
+
+	DRM_DEBUG_KMS("scheduling 60 vblanks (with work)\n");
+	ret = drm_wait_vblank_kernel(&data2->item);
+	if (ret) {
+		DRM_DEBUG_KMS("vblank schedule error: %d\n", ret);
+		kfree(data2);
+		return ret;
+	}
+	DRM_DEBUG_KMS("vblanks scheduled\n");
+
+	return 0;
+}
+
 struct pipe_crc_info {
 	const char *name;
 	struct drm_device *dev;
@@ -4289,6 +4369,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..295d0e0 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -667,6 +667,26 @@ struct drm_pending_vblank_event {
 	struct drm_event_vblank event;
 };
 
+struct drm_kernel_vblank_item {
+	/* Internal members, set by drm.ko. */
+	struct list_head link;
+	struct work_struct work;
+
+	/* Parameters: set by the caller of drm_wait_vblank_kernel(). */
+	struct drm_crtc *crtc;
+
+	int target_seq;
+	bool absolute; /* Tells if target_seq is a relative offset to the
+			* current vblank count, or just an absolute value. */
+
+	void (*callback)(struct drm_kernel_vblank_item *item);
+
+	bool callback_from_work;
+
+	/* Output: to be used by the callback function. */
+	bool premature;
+};
+
 struct drm_vblank_crtc {
 	struct drm_device *dev;		/* pointer to the drm_device */
 	wait_queue_head_t queue;	/**< VBLANK wait queue */
@@ -784,6 +804,7 @@ struct drm_device {
 	 * List of events
 	 */
 	struct list_head vblank_event_list;
+	struct list_head vblank_kernel_list;
 	spinlock_t event_lock;
 
 	/*@} */
@@ -900,6 +921,7 @@ 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_kernel(struct drm_kernel_vblank_item *item);
 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);
-- 
2.1.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

  reply	other threads:[~2014-11-19 19:47 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-11-19 19:47 [RFC 0/7+1] Add in-kernel vblank delaying mechanism Paulo Zanoni
2014-11-19 19:47 ` Paulo Zanoni [this message]
2014-12-03  2:13   ` [Intel-gfx] [RFC] drm: add a mechanism for drivers to schedule vblank callbacks Matt Roper
2014-11-19 19:47 ` [RFC 1/7] drm: allow the drivers to call the vblank IOCTL internally Paulo Zanoni
2014-12-03  2:14   ` Matt Roper
2014-11-19 19:47 ` [RFC 2/7] drm: allow drm_wait_vblank_kernel() callback from workqueues Paulo Zanoni
2014-12-05  0:34   ` [Intel-gfx] " Matt Roper
2014-11-19 19:47 ` [RFC 3/7] drm: introduce struct drm_vblank_wait_item Paulo Zanoni
2014-12-05  2:27   ` Matt Roper
2014-11-19 19:47 ` [RFC 4/7] drm: add wanted_seq to drm_vblank_wait_item Paulo Zanoni
2014-11-19 19:47 ` [RFC 5/7] drm: change the drm vblank callback item type Paulo Zanoni
2014-11-19 19:47 ` [RFC 6/7] drm: make vblank_event_list handle drm_vblank_wait_item types Paulo Zanoni
2014-12-05  2:27   ` [Intel-gfx] " Matt Roper
2014-11-19 19:47 ` [RFC 7/7] drm: make the callers of drm_wait_vblank() allocate the memory Paulo Zanoni
2014-11-26 17:19 ` [RFC 0/7+1] Add in-kernel vblank delaying mechanism Daniel Vetter
2014-11-26 23:25   ` Dave Airlie
2014-11-27 10:06     ` Daniel Vetter
2014-12-04 17:09 ` Daniel Vetter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1416426435-2237-2-git-send-email-przanoni@gmail.com \
    --to=przanoni@gmail.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=paulo.r.zanoni@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.