drm/i915: nonblocking commit
authorDaniel Vetter <daniel.vetter@ffwll.ch>
Tue, 14 Jun 2016 16:01:00 +0000 (18:01 +0200)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 16 Jun 2016 12:28:36 +0000 (14:28 +0200)
Simply split intel_atomic_commit in half and place the new
nonblocking commit helpers at the right spots.

NOTE: There's still trouble with obj->frontbuffer bits getting mangled
when pipelining atomic commits.

v2:
- Remove the check for nonblocking which returned -EINVAL.
- Do wait for requests in the worker thread before committing
  hw state.

v3: Move hw_done after the optimize_wm/post_plane_update step, plus
add FIXME comment how to fix that up again properly.

v4: Update FIXME for intel_atomic_commit - more stuff works now.

v5: Still reject nonblocking modeset commits (Maarten).

v6: Use intel_state->modeset (Maarten).

Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Reviewed-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1465920060-6388-1-git-send-email-daniel.vetter@ffwll.ch
drivers/gpu/drm/i915/intel_display.c

index 59a828b237ce6b49acec2b242499aa0252a7f4d5..b26e62473bbf66243065d8e124f0e10f2d2b4050 100644 (file)
@@ -13571,11 +13571,6 @@ static int intel_atomic_prepare_commit(struct drm_device *dev,
        struct drm_crtc *crtc;
        int i, ret;
 
-       if (nonblock) {
-               DRM_DEBUG_KMS("i915 does not yet support nonblocking commit\n");
-               return -EINVAL;
-       }
-
        for_each_crtc_in_state(state, crtc, crtc_state, i) {
                if (state->legacy_cursor_update)
                        continue;
@@ -13694,50 +13689,34 @@ static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
        return false;
 }
 
-/**
- * intel_atomic_commit - commit validated state object
- * @dev: DRM device
- * @state: the top-level driver state object
- * @nonblock: nonblocking commit
- *
- * This function commits a top-level state object that has been validated
- * with drm_atomic_helper_check().
- *
- * FIXME:  Atomic modeset support for i915 is not yet complete.  At the moment
- * we can only handle plane-related operations and do not yet support
- * nonblocking commit.
- *
- * RETURNS
- * Zero for success or -errno.
- */
-static int intel_atomic_commit(struct drm_device *dev,
-                              struct drm_atomic_state *state,
-                              bool nonblock)
+static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 {
+       struct drm_device *dev = state->dev;
        struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_crtc_state *old_crtc_state;
        struct drm_crtc *crtc;
        struct intel_crtc_state *intel_cstate;
-       int ret = 0, i;
+       struct drm_plane *plane;
+       struct drm_plane_state *plane_state;
        bool hw_check = intel_state->modeset;
        unsigned long put_domains[I915_MAX_PIPES] = {};
        unsigned crtc_vblank_mask = 0;
+       int i, ret;
 
-       ret = drm_atomic_helper_setup_commit(state, nonblock);
-       if (ret)
-               return ret;
+       for_each_plane_in_state(state, plane, plane_state, i) {
+               struct intel_plane_state *intel_plane_state =
+                       to_intel_plane_state(plane_state);
 
-       ret = intel_atomic_prepare_commit(dev, state, nonblock);
-       if (ret) {
-               DRM_DEBUG_ATOMIC("Preparing state failed with %i\n", ret);
-               return ret;
-       }
+               if (!intel_plane_state->wait_req)
+                       continue;
 
-       drm_atomic_helper_swap_state(state, true);
-       dev_priv->wm.distrust_bios_wm = false;
-       dev_priv->wm.skl_results = intel_state->wm_results;
-       intel_shared_dpll_commit(state);
+               ret = __i915_wait_request(intel_plane_state->wait_req,
+                                         true, NULL, NULL);
+               /* EIO should be eaten, and we can't get interrupted in the
+                * worker, and blocking commits have waited already. */
+               WARN_ON(ret);
+       }
 
        drm_atomic_helper_wait_for_dependencies(state);
 
@@ -13836,8 +13815,15 @@ static int intel_atomic_commit(struct drm_device *dev,
                        crtc_vblank_mask |= 1 << i;
        }
 
-       drm_atomic_helper_commit_hw_done(state);
-
+       /* FIXME: We should call drm_atomic_helper_commit_hw_done() here
+        * already, but still need the state for the delayed optimization. To
+        * fix this:
+        * - wrap the optimization/post_plane_update stuff into a per-crtc work.
+        * - schedule that vblank worker _before_ calling hw_done
+        * - at the start of commit_tail, cancel it _synchrously
+        * - switch over to the vblank wait helper in the core after that since
+        *   we don't need out special handling any more.
+        */
        if (!state->legacy_cursor_update)
                intel_atomic_wait_for_vblanks(dev, dev_priv, crtc_vblank_mask);
 
@@ -13864,6 +13850,8 @@ static int intel_atomic_commit(struct drm_device *dev,
                intel_modeset_verify_crtc(crtc, old_crtc_state, crtc->state);
        }
 
+       drm_atomic_helper_commit_hw_done(state);
+
        if (intel_state->modeset)
                intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET);
 
@@ -13887,6 +13875,66 @@ static int intel_atomic_commit(struct drm_device *dev,
         * can happen also when the device is completely off.
         */
        intel_uncore_arm_unclaimed_mmio_detection(dev_priv);
+}
+
+static void intel_atomic_commit_work(struct work_struct *work)
+{
+       struct drm_atomic_state *state = container_of(work,
+                                                     struct drm_atomic_state,
+                                                     commit_work);
+       intel_atomic_commit_tail(state);
+}
+
+/**
+ * intel_atomic_commit - commit validated state object
+ * @dev: DRM device
+ * @state: the top-level driver state object
+ * @nonblock: nonblocking commit
+ *
+ * This function commits a top-level state object that has been validated
+ * with drm_atomic_helper_check().
+ *
+ * FIXME:  Atomic modeset support for i915 is not yet complete.  At the moment
+ * nonblocking commits are only safe for pure plane updates. Everything else
+ * should work though.
+ *
+ * RETURNS
+ * Zero for success or -errno.
+ */
+static int intel_atomic_commit(struct drm_device *dev,
+                              struct drm_atomic_state *state,
+                              bool nonblock)
+{
+       struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret = 0;
+
+       if (intel_state->modeset && nonblock) {
+               DRM_DEBUG_KMS("nonblocking commit for modeset not yet implemented.\n");
+               return -EINVAL;
+       }
+
+       ret = drm_atomic_helper_setup_commit(state, nonblock);
+       if (ret)
+               return ret;
+
+       INIT_WORK(&state->commit_work, intel_atomic_commit_work);
+
+       ret = intel_atomic_prepare_commit(dev, state, nonblock);
+       if (ret) {
+               DRM_DEBUG_ATOMIC("Preparing state failed with %i\n", ret);
+               return ret;
+       }
+
+       drm_atomic_helper_swap_state(state, true);
+       dev_priv->wm.distrust_bios_wm = false;
+       dev_priv->wm.skl_results = intel_state->wm_results;
+       intel_shared_dpll_commit(state);
+
+       if (nonblock)
+               queue_work(system_unbound_wq, &state->commit_work);
+       else
+               intel_atomic_commit_tail(state);
 
        return 0;
 }