From: Matt Roper <matthew.d.roper@intel.com>
To: intel-gfx@lists.freedesktop.org
Subject: [PATCH 3/4] drm/i915: Enable asynchronous nuclear flips
Date: Fri, 30 Jan 2015 16:22:38 -0800 [thread overview]
Message-ID: <1422663759-21589-4-git-send-email-matthew.d.roper@intel.com> (raw)
In-Reply-To: <1422663759-21589-1-git-send-email-matthew.d.roper@intel.com>
The initial i915 nuclear pageflip support rejected asynchronous updates.
Allow all work after we swap in the new state structures to run
asynchronously. We also need to start sending completion events to
userspace if they were requested.
Signed-off-by: Matt Roper <matthew.d.roper@intel.com>
---
drivers/gpu/drm/i915/i915_drv.h | 3 +
drivers/gpu/drm/i915/intel_atomic.c | 162 +++++++++++++++++++++++++++++++-----
drivers/gpu/drm/i915/intel_drv.h | 8 ++
3 files changed, 150 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 8fad702..c7a520a 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1777,6 +1777,9 @@ struct drm_i915_private {
struct drm_crtc *pipe_to_crtc_mapping[I915_MAX_PIPES];
wait_queue_head_t pending_flip_queue;
+ /* CRTC mask of pending atomic flips */
+ uint32_t pending_atomic;
+
#ifdef CONFIG_DEBUG_FS
struct intel_pipe_crc pipe_crc[I915_MAX_PIPES];
#endif
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
index 19a9dd5..5dd7897 100644
--- a/drivers/gpu/drm/i915/intel_atomic.c
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -76,6 +76,8 @@ int intel_atomic_check(struct drm_device *dev,
state->allow_modeset = false;
for (i = 0; i < ncrtcs; i++) {
struct intel_crtc *crtc = to_intel_crtc(state->crtcs[i]);
+ if (crtc)
+ state->crtc_states[i]->enable = crtc->active;
if (crtc && crtc->pipe != nuclear_pipe)
not_nuclear = true;
}
@@ -96,6 +98,87 @@ int intel_atomic_check(struct drm_device *dev,
}
+/*
+ * Wait until CRTC's have no pending flip, then atomically mark those CRTC's
+ * as busy.
+ */
+static int wait_for_pending_flip(uint32_t crtc_mask,
+ struct intel_pending_atomic *commit)
+{
+ struct drm_i915_private *dev_priv = commit->dev->dev_private;
+ int ret;
+
+ spin_lock_irq(&dev_priv->pending_flip_queue.lock);
+ ret = wait_event_interruptible_locked(dev_priv->pending_flip_queue,
+ !(dev_priv->pending_atomic & crtc_mask));
+ if (ret == 0)
+ dev_priv->pending_atomic |= crtc_mask;
+ spin_unlock_irq(&dev_priv->pending_flip_queue.lock);
+
+ return ret;
+}
+
+/* Finish pending flip operation on specified CRTC's */
+static void flip_completion(struct intel_pending_atomic *commit)
+{
+ struct drm_i915_private *dev_priv = commit->dev->dev_private;
+
+ spin_lock_irq(&dev_priv->pending_flip_queue.lock);
+ dev_priv->pending_atomic &= ~commit->crtc_mask;
+ wake_up_all_locked(&dev_priv->pending_flip_queue);
+ spin_unlock_irq(&dev_priv->pending_flip_queue.lock);
+}
+
+/*
+ * Finish an atomic commit. The work here can be performed asynchronously
+ * if desired. The new state has already been applied to the DRM objects
+ * and no modeset locks are needed.
+ */
+static void finish_atomic_commit(struct work_struct *work)
+{
+ struct intel_pending_atomic *commit =
+ container_of(work, struct intel_pending_atomic, work);
+ struct drm_device *dev = commit->dev;
+ struct drm_crtc *crtc;
+ struct drm_atomic_state *state = commit->state;
+ int i;
+
+ /*
+ * FIXME: The proper sequence here will eventually be:
+ *
+ * drm_atomic_helper_commit_pre_planes(dev, state);
+ * drm_atomic_helper_commit_planes(dev, state);
+ * drm_atomic_helper_commit_post_planes(dev, state);
+ * drm_atomic_helper_wait_for_vblanks(dev, state);
+ * drm_atomic_helper_cleanup_planes(dev, state);
+ * drm_atomic_state_free(state);
+ *
+ * once we have full atomic modeset. For now, just manually update
+ * plane states to avoid clobbering good states with dummy states
+ * while nuclear pageflipping.
+ */
+ drm_atomic_helper_commit_planes(dev, state);
+ drm_atomic_helper_wait_for_vblanks(dev, state);
+
+ /* Send CRTC completion events. */
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
+ crtc = state->crtcs[i];
+ if (crtc && crtc->state->event) {
+ spin_lock_irq(&dev->event_lock);
+ drm_send_vblank_event(dev, to_intel_crtc(crtc)->pipe,
+ crtc->state->event);
+ spin_unlock_irq(&dev->event_lock);
+ crtc->state->event = NULL;
+ }
+ }
+
+ drm_atomic_helper_cleanup_planes(dev, state);
+ drm_atomic_state_free(state);
+
+ flip_completion(commit);
+ kfree(commit);
+}
+
/**
* intel_atomic_commit - commit validated state object
* @dev: DRM device
@@ -116,34 +199,48 @@ int intel_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *state,
bool async)
{
+ struct intel_pending_atomic *commit;
int ret;
int i;
- if (async) {
- DRM_DEBUG_KMS("i915 does not yet support async commit\n");
- return -EINVAL;
- }
-
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret)
return ret;
- /* Point of no return */
+ commit = kzalloc(sizeof(*commit), GFP_KERNEL);
+ if (!commit)
+ return -ENOMEM;
+
+ commit->dev = dev;
+ commit->state = state;
+
+ for (i = 0; i < dev->mode_config.num_crtc; i++)
+ if (state->crtcs[i])
+ commit->crtc_mask |=
+ (1 << drm_crtc_index(state->crtcs[i]));
/*
- * FIXME: The proper sequence here will eventually be:
- *
- * drm_atomic_helper_swap_state(dev, state)
- * drm_atomic_helper_commit_pre_planes(dev, state);
- * drm_atomic_helper_commit_planes(dev, state);
- * drm_atomic_helper_commit_post_planes(dev, state);
- * drm_atomic_helper_wait_for_vblanks(dev, state);
- * drm_atomic_helper_cleanup_planes(dev, state);
- * drm_atomic_state_free(state);
- *
- * once we have full atomic modeset. For now, just manually update
- * plane states to avoid clobbering good states with dummy states
- * while nuclear pageflipping.
+ * If there's already a flip pending, we won't schedule another one
+ * until it completes. We may relax this in the future, but for now
+ * this matches the legacy pageflip behavior.
+ */
+ ret = wait_for_pending_flip(commit->crtc_mask, commit);
+ if (ret) {
+ kfree(commit);
+ return ret;
+ }
+
+ /*
+ * Point of no return; everything from here on can't fail, so swap
+ * the new state into the DRM objects.
+ */
+
+ /*
+ * FIXME: Should eventually use drm_atomic_helper_swap_state().
+ * However since we only handle planes at the moment, we don't
+ * want to clobber our good crtc state with something bogus,
+ * so just manually swap the plane states and copy over any completion
+ * events for CRTC's.
*/
for (i = 0; i < dev->mode_config.num_total_plane; i++) {
struct drm_plane *plane = state->planes[i];
@@ -155,10 +252,29 @@ int intel_atomic_commit(struct drm_device *dev,
swap(state->plane_states[i], plane->state);
plane->state->state = NULL;
}
- drm_atomic_helper_commit_planes(dev, state);
- drm_atomic_helper_wait_for_vblanks(dev, state);
- drm_atomic_helper_cleanup_planes(dev, state);
- drm_atomic_state_free(state);
+
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
+ struct drm_crtc *crtc = state->crtcs[i];
+ struct drm_crtc_state *crtc_state = state->crtc_states[i];
+
+ if (!crtc || !crtc_state)
+ continue;
+
+ /* n.b., we only copy the event and enabled flag for now */
+ swap(crtc->state->event, crtc_state->event);
+ swap(crtc->state->enable, crtc_state->enable);
+ }
+
+ /*
+ * From here on, we can do everything asynchronously without any
+ * modeset locks.
+ */
+ if (async) {
+ INIT_WORK(&commit->work, finish_atomic_commit);
+ schedule_work(&commit->work);
+ } else {
+ finish_atomic_commit(&commit->work);
+ }
return 0;
}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index eef79cc..15b02b0 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -391,6 +391,14 @@ struct intel_crtc_state {
int pbn;
};
+/* In-flight atomic operation */
+struct intel_pending_atomic {
+ struct work_struct work;
+ struct drm_device *dev;
+ struct drm_atomic_state *state;
+ uint32_t crtc_mask;
+};
+
struct intel_pipe_wm {
struct intel_wm_level wm[5];
uint32_t linetime;
--
1.8.5.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx
next prev parent reply other threads:[~2015-01-31 0:23 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-01-31 0:22 [PATCH 0/4] More nuclear pageflip Matt Roper
2015-01-31 0:22 ` [PATCH 1/4] drm/i915: Keep plane->state updated on pageflip Matt Roper
2015-01-31 9:36 ` Daniel Vetter
2015-01-31 0:22 ` [PATCH 2/4] drm/i915: Switch planes from transitional helpers to full atomic helpers Matt Roper
2015-01-31 0:22 ` Matt Roper [this message]
2015-01-31 9:30 ` [PATCH 3/4] drm/i915: Enable asynchronous nuclear flips Daniel Vetter
2015-01-31 20:07 ` Matt Roper
2015-02-02 9:36 ` Daniel Vetter
2015-01-31 0:22 ` [PATCH 4/4] drm/i915: Use atomic helper for pageflips Matt Roper
2015-01-31 9:35 ` [PATCH 0/4] More nuclear pageflip Daniel Vetter
2015-01-31 20:22 ` Matt Roper
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=1422663759-21589-4-git-send-email-matthew.d.roper@intel.com \
--to=matthew.d.roper@intel.com \
--cc=intel-gfx@lists.freedesktop.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox