From: Michel Dänzer <mdaen...@redhat.com> Also add DRM_CAP_ATOMIC_HW_DONE_EVENT to let user space know it can use the DRM_MODE_ATOMIC_HW_DONE_EVENT flag.
Signed-off-by: Michel Dänzer <mdaen...@redhat.com> --- drivers/gpu/drm/drm_atomic.c | 1 + drivers/gpu/drm/drm_atomic_helper.c | 23 ++++++++++++++++++ drivers/gpu/drm/drm_atomic_uapi.c | 37 +++++++++++++++++++++++++++-- drivers/gpu/drm/drm_ioctl.c | 3 +++ include/drm/drm_atomic.h | 22 +++++++++++++++++ include/uapi/drm/drm_mode.h | 3 ++- 6 files changed, 86 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 0138cf0b8b63..159894381a45 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -108,6 +108,7 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) kfree(state->crtcs); kfree(state->planes); kfree(state->private_objs); + kfree(state->hw_done_event); } EXPORT_SYMBOL(drm_atomic_state_default_release); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index ee64ca1b1bec..e55edc42a317 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -2511,6 +2511,27 @@ void drm_atomic_helper_fake_vblank(struct drm_atomic_state *state) } EXPORT_SYMBOL(drm_atomic_helper_fake_vblank); +static void send_hw_done_event(struct drm_device *dev, + struct drm_pending_atomic_hw_done_event **e, + ktime_t done) +{ + struct timespec64 tv; + unsigned long irqflags; + + if (!*e) + return; + + tv = ktime_to_timespec64(done); + (*e)->event.tv_sec = tv.tv_sec; + (*e)->event.tv_usec = tv.tv_nsec / 1000; + + spin_lock_irqsave(&dev->event_lock, irqflags); + drm_send_event_timestamp_locked(dev, &(*e)->base, done); + spin_unlock_irqrestore(&dev->event_lock, irqflags); + + *e = NULL; +} + /** * drm_atomic_helper_commit_hw_done - setup possible nonblocking commit * @state: atomic state object being committed @@ -2533,6 +2554,8 @@ void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state) struct drm_crtc_commit *commit; int i; + send_hw_done_event(state->dev, &state->hw_done_event, ktime_get()); + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { commit = new_crtc_state->commit; if (!commit) diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index c2726af6698e..43c16bfe65a9 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -939,6 +939,21 @@ static struct drm_pending_vblank_event *create_vblank_event( return e; } +static struct drm_pending_atomic_hw_done_event *create_hw_done_event(uint64_t user_data) +{ + struct drm_pending_atomic_hw_done_event *e = NULL; + + e = kzalloc(sizeof *e, GFP_KERNEL); + if (!e) + return NULL; + + e->event.base.type = DRM_EVENT_ATOMIC_HW_DONE; + e->event.base.length = sizeof(e->event); + e->event.user_data = user_data; + + return e; +} + int drm_atomic_connector_commit_dpms(struct drm_atomic_state *state, struct drm_connector *connector, int mode) @@ -1314,6 +1329,24 @@ static int prepare_signaling(struct drm_device *dev, return -EINVAL; } + if (arg->flags & DRM_MODE_ATOMIC_HW_DONE_EVENT && + file_priv) { + struct drm_pending_atomic_hw_done_event *e; + + e = create_hw_done_event(arg->user_data); + if (!e) + return -ENOMEM; + + ret = drm_event_reserve_init(dev, file_priv, &e->base, + &e->event.base); + if (ret) { + kfree(e); + return ret; + } + + state->hw_done_event = e; + } + return 0; } @@ -1431,9 +1464,9 @@ int drm_mode_atomic_ioctl(struct drm_device *dev, /* can't test and expect an event at the same time. */ if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) && - (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) { + (arg->flags & (DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_HW_DONE_EVENT))) { drm_dbg_atomic(dev, - "commit failed: page-flip event requested with test-only commit\n"); + "commit failed: event requested with test-only commit\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index f593dc569d31..3b2221748dca 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -304,6 +304,9 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_ req->value = drm_core_check_feature(dev, DRIVER_ATOMIC) && dev->mode_config.async_page_flip; break; + case DRM_CAP_ATOMIC_HW_DONE_EVENT: + req->value = 1; + break; default: return -EINVAL; } diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 38636a593c9d..e34c2b08e759 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -29,8 +29,23 @@ #define DRM_ATOMIC_H_ #include <drm/drm_crtc.h> +#include <drm/drm_file.h> #include <drm/drm_util.h> +/** + * struct drm_pending_atomic_hw_done_event - pending atomic HW done event tracking + */ +struct drm_pending_atomic_hw_done_event { + /** + * @base: Base structure for tracking pending DRM events. + */ + struct drm_pending_event base; + /** + * @event: Actual event which will be sent to userspace. + */ + struct drm_event_atomic_hw_done event; +}; + /** * struct drm_crtc_commit - track modeset commits on a CRTC * @@ -517,6 +532,13 @@ struct drm_atomic_state { */ struct drm_crtc_commit *fake_commit; + /** + * @hw_done_event: + * + * Used for sending an event to user space when programming a commit to HW is done. + */ + struct drm_pending_atomic_hw_done_event *hw_done_event; + /** * @commit_work: * diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index d7921e633f1a..463e32919093 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -1156,7 +1156,8 @@ struct drm_mode_destroy_dumb { DRM_MODE_PAGE_FLIP_ASYNC |\ DRM_MODE_ATOMIC_TEST_ONLY |\ DRM_MODE_ATOMIC_NONBLOCK |\ - DRM_MODE_ATOMIC_ALLOW_MODESET) + DRM_MODE_ATOMIC_ALLOW_MODESET |\ + DRM_MODE_ATOMIC_HW_DONE_EVENT) struct drm_mode_atomic { __u32 flags; -- 2.50.0