drm: Resurrect atomic rmfb code, v3
authorMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
Wed, 25 Jan 2017 11:30:47 +0000 (12:30 +0100)
committerJani Nikula <jani.nikula@intel.com>
Wed, 15 Feb 2017 13:40:57 +0000 (15:40 +0200)
This was somehow lost between v3 and the merged version in Maarten's
patch merged as:

commit f2d580b9a8149735cbc4b59c4a8df60173658140
Author: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Date:   Wed May 4 14:38:26 2016 +0200

    drm/core: Do not preserve framebuffer on rmfb, v4.

This introduces a slight behavioral change to rmfb. Instead of
disabling a crtc when the primary plane is disabled, we try to
preserve it.

Apart from old versions of the vmwgfx xorg driver, there is
nothing depending on rmfb disabling a crtc. Since vmwgfx is
a legacy driver we can safely only disable the plane with atomic.

If this commit is rejected by the driver then we will still fall
back to the old behavior and turn off the crtc.

v2:
- Remove plane->fb assignment, done by drm_atomic_clean_old_fb.
- Add WARN_ON when atomic_remove_fb fails.
- Always call drm_atomic_state_put.
v3:
- Use drm_drv_uses_atomic_modeset
- Handle the case where the first plane-disable-only commit fails
  with -EINVAL. Some drivers do not support this, fall back to
  disabling all crtc's in this case.

Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/66fc3da5-697b-1613-0a67-a5293209f0dc@linux.intel.com
drivers/gpu/drm/drm_atomic.c
drivers/gpu/drm/drm_crtc_internal.h
drivers/gpu/drm/drm_framebuffer.c

index c97588a2821636eb9577122b0e4d4fb14e115221..11f102e7ddfd3c47a5fea52a0b7174f136d76743 100644 (file)
@@ -2046,6 +2046,112 @@ static void complete_crtc_signaling(struct drm_device *dev,
        kfree(fence_state);
 }
 
+int drm_atomic_remove_fb(struct drm_framebuffer *fb)
+{
+       struct drm_modeset_acquire_ctx ctx;
+       struct drm_device *dev = fb->dev;
+       struct drm_atomic_state *state;
+       struct drm_plane *plane;
+       struct drm_connector *conn;
+       struct drm_connector_state *conn_state;
+       int i, ret = 0;
+       unsigned plane_mask, disable_crtcs = false;
+
+       state = drm_atomic_state_alloc(dev);
+       if (!state)
+               return -ENOMEM;
+
+       drm_modeset_acquire_init(&ctx, 0);
+       state->acquire_ctx = &ctx;
+
+retry:
+       plane_mask = 0;
+       ret = drm_modeset_lock_all_ctx(dev, &ctx);
+       if (ret)
+               goto unlock;
+
+       drm_for_each_plane(plane, dev) {
+               struct drm_plane_state *plane_state;
+
+               if (plane->state->fb != fb)
+                       continue;
+
+               plane_state = drm_atomic_get_plane_state(state, plane);
+               if (IS_ERR(plane_state)) {
+                       ret = PTR_ERR(plane_state);
+                       goto unlock;
+               }
+
+               /*
+                * Some drivers do not support keeping crtc active with the
+                * primary plane disabled. If we fail to commit with -EINVAL
+                * then we will try to perform the same commit but with all
+                * crtc's disabled for primary planes as well.
+                */
+               if (disable_crtcs && plane_state->crtc->primary == plane) {
+                       struct drm_crtc_state *crtc_state;
+
+                       crtc_state = drm_atomic_get_existing_crtc_state(state, plane_state->crtc);
+
+                       ret = drm_atomic_add_affected_connectors(state, plane_state->crtc);
+                       if (ret)
+                               goto unlock;
+
+                       crtc_state->active = false;
+                       ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
+                       if (ret)
+                               goto unlock;
+               }
+
+               drm_atomic_set_fb_for_plane(plane_state, NULL);
+               ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
+               if (ret)
+                       goto unlock;
+
+               plane_mask |= BIT(drm_plane_index(plane));
+
+               plane->old_fb = plane->fb;
+       }
+
+       /* This list is only not empty when disable_crtcs is set. */
+       for_each_connector_in_state(state, conn, conn_state, i) {
+               ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
+
+               if (ret)
+                       goto unlock;
+       }
+
+       if (plane_mask)
+               ret = drm_atomic_commit(state);
+
+unlock:
+       if (plane_mask)
+               drm_atomic_clean_old_fb(dev, plane_mask, ret);
+
+       if (ret == -EDEADLK) {
+               drm_modeset_backoff(&ctx);
+               goto retry;
+       }
+
+       drm_atomic_state_put(state);
+
+       if (ret == -EINVAL && !disable_crtcs) {
+               disable_crtcs = true;
+
+               state = drm_atomic_state_alloc(dev);
+               if (state) {
+                       state->acquire_ctx = &ctx;
+                       goto retry;
+               }
+               ret = -ENOMEM;
+       }
+
+       drm_modeset_drop_locks(&ctx);
+       drm_modeset_acquire_fini(&ctx);
+
+       return ret;
+}
+
 int drm_mode_atomic_ioctl(struct drm_device *dev,
                          void *data, struct drm_file *file_priv)
 {
index 955c5690bf643117fc00e17108da9708acdc09cb..e0678f8a51cf21a52af0da17eca8d74ee942fb39 100644 (file)
@@ -183,6 +183,7 @@ int drm_atomic_get_property(struct drm_mode_object *obj,
                            struct drm_property *property, uint64_t *val);
 int drm_mode_atomic_ioctl(struct drm_device *dev,
                          void *data, struct drm_file *file_priv);
+int drm_atomic_remove_fb(struct drm_framebuffer *fb);
 
 
 /* drm_plane.c */
index 28a0108a1ab85f9a4fbd87c39bff668027597585..c0e593a7f9b4a0629e35fbe918e22c0593a54faf 100644 (file)
@@ -773,6 +773,12 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
         * in this manner.
         */
        if (drm_framebuffer_read_refcount(fb) > 1) {
+               if (drm_drv_uses_atomic_modeset(dev)) {
+                       int ret = drm_atomic_remove_fb(fb);
+                       WARN(ret, "atomic remove_fb failed with %i\n", ret);
+                       goto out;
+               }
+
                drm_modeset_lock_all(dev);
                /* remove from any CRTC */
                drm_for_each_crtc(crtc, dev) {
@@ -790,6 +796,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
                drm_modeset_unlock_all(dev);
        }
 
+out:
        drm_framebuffer_unreference(fb);
 }
 EXPORT_SYMBOL(drm_framebuffer_remove);