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

Reply via email to