drm/atomic-helper: nonblocking commit support
Design ideas:
- split up the actual commit into different phases, and have
completions for each of them. This will be useful for the future
when we want to interleave phases much more aggressively, for e.g.
queue depth > 1. For not it's just a minimal optimization compared
to current common nonblocking implementation patterns from drivers,
which all stall for the entire commit to complete, including vblank
waits and cleanups.
- Extract a separate atomic_commit_hw hook since that's the part most
drivers will need to overwrite, hopefully allowing even more shared
code.
- Enforce EBUSY seamntics by attaching one of the completions to the
flip_done vblank event. Side benefit of forcing atomic drivers using
these helpers to implement event handlign at least semi-correct. I'm
evil that way ;-)
- Ridiculously modular, as usual.
- The main tracking unit for a commit stays struct drm_atomic_state,
and the ownership rules for that are unchanged. Ownership still
gets transferred to the driver (and subsequently to the worker) on
successful commits. What is added is a small, per-crtc, refcounted
structure to track pending commits called struct drm_crtc_commit.
No actual state is attached to that though, it's purely for ordering
and waiting.
- Dependencies are implicitly handled by assuming that any CRTC part
of &drm_atomic_state is a dependency, and that the current commit
must wait for any commits to complete on those CRTC. This way
drivers can easily add more depencies using
drm_atomic_get_crtc_state(), which is very natural since in most
case a dependency exists iff there's some bit of state that needs to
be cross checked.
Removing depencies is not possible, drivers simply need to be
careful to not include every CRTC in a commit if that's not
necessary. Which is a good idea anyway, since that also avoids
ww_mutex lock contention.
- Queue depth > 1 sees some prep work in this patch by adding a stall
paramater to drm_atomic_helper_swap_states(). To be able to push
commits entirely free-standing and in a deeper queue through the
back-end the driver must not access any obj->state pointers. This
means we need to track the old state in drm_atomic_state (much
easier with the consolidated arrays), and pass them all explicitly
to driver backends (this will be serious amounts of churn).
Once that's done stall can be set to false in swap_states.
v2: Dont ask for flip_done signalling when the CRTC is off and stays
off: Drivers don't handle events in that case. Instead complete right
away. This way future commits don't need to have special-case logic,
but can keep blocking for the flip_done completion.
v3: Tons of fixes:
- Stall for preceeding commit for real, not the current one by
accident.
- Add WARN_ON in case drivers don't fire the drm event.
- Don't double-free drm events.
v4: Make legacy cursor not stall.
v5: Extend the helper hook to cover the entire commit tail. Some
drivers need special code for cleanup and vblank waiting, this makes
it a bit more useful. Inspired by the rockchip driver.
v6: Add WARN_ON to catch drivers who forget to send out the
drm event.
v7: Fixup the stalls in swap_state for real!!
v8:
- Fixup trailing whitespace, spotted by Maarten.
- Actually wait for flip_done in cleanup_done, like the comment says
we should do. Thanks a lot for Tomeu for helping with debugging this
on.
v9: Now with awesome kerneldoc!
v10: Split out drm_crtc_commit tracking infrastructure.
v:
- Add missing static (Gustavo).
- Split out the sync functions, only do the actual nonblocking
logic in this patch (Maarten).
Cc: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
Tested-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Tomeu Vizoso <tomeu.vizoso@gmail.com>
Cc: Daniel Stone <daniels@collabora.com>
Tested-by: Liviu Dudau <Liviu.Dudau@arm.com>
Reviewed-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Testcase: igt/kms_flip/*
Testcase: igt/kms_cursor*
Testcase: igt/kms*plane*
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1465388359-8070-10-git-send-email-daniel.vetter@ffwll.ch
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index d90b3a6..e833061 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -38,6 +38,7 @@
struct drm_atomic_state *state);
int drm_atomic_helper_check(struct drm_device *dev,
struct drm_atomic_state *state);
+void drm_atomic_helper_commit_tail(struct drm_atomic_state *state);
int drm_atomic_helper_commit(struct drm_device *dev,
struct drm_atomic_state *state,
bool nonblock);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 5eb1f08..914baa8 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -2248,6 +2248,7 @@
* @async_page_flip: does this device support async flips on the primary plane?
* @cursor_width: hint to userspace for max cursor width
* @cursor_height: hint to userspace for max cursor height
+ * @helper_private: mid-layer private data
*
* Core mode resource tracking structure. All CRTC, encoders, and connectors
* enumerated by the driver are added here, as are global properties. Some
@@ -2391,6 +2392,8 @@
/* cursor size */
uint32_t cursor_width, cursor_height;
+
+ struct drm_mode_config_helper_funcs *helper_private;
};
/**
diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
index 4e7a53b..b55f218 100644
--- a/include/drm/drm_modeset_helper_vtables.h
+++ b/include/drm/drm_modeset_helper_vtables.h
@@ -931,4 +931,43 @@
plane->helper_private = funcs;
}
+/**
+ * struct drm_mode_config_helper_funcs - global modeset helper operations
+ *
+ * These helper functions are used by the atomic helpers.
+ */
+struct drm_mode_config_helper_funcs {
+ /**
+ * @atomic_commit_tail:
+ *
+ * This hook is used by the default atomic_commit() hook implemented in
+ * drm_atomic_helper_commit() together with the nonblocking commit
+ * helpers (see drm_atomic_helper_setup_commit() for a starting point)
+ * to implement blocking and nonblocking commits easily. It is not used
+ * by the atomic helpers
+ *
+ * This hook should first commit the given atomic state to the hardware.
+ * But drivers can add more waiting calls at the start of their
+ * implementation, e.g. to wait for driver-internal request for implicit
+ * syncing, before starting to commit the update to the hardware.
+ *
+ * After the atomic update is committed to the hardware this hook needs
+ * to call drm_atomic_helper_commit_hw_done(). Then wait for the upate
+ * to be executed by the hardware, for example using
+ * drm_atomic_helper_wait_for_vblanks(), and then clean up the old
+ * framebuffers using drm_atomic_helper_cleanup_planes().
+ *
+ * When disabling a CRTC this hook _must_ stall for the commit to
+ * complete. Vblank waits don't work on disabled CRTC, hence the core
+ * can't take care of this. And it also can't rely on the vblank event,
+ * since that can be signalled already when the screen shows black,
+ * which can happen much earlier than the last hardware access needed to
+ * shut off the display pipeline completely.
+ *
+ * This hook is optional, the default implementation is
+ * drm_atomic_helper_commit_tail().
+ */
+ void (*atomic_commit_tail)(struct drm_atomic_state *state);
+};
+
#endif