diff mbox

[1/2] drm/atomic: Add a way to call remove_fb atomically, v2.

Message ID 1462795323-4303-3-git-send-email-maarten.lankhorst@linux.intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Maarten Lankhorst May 9, 2016, 12:02 p.m. UTC
Use this in drm_framebuffer_remove, this is to remove the fb in an atomic way.

i915 doesn't export DRIVER_ATOMIC yet by default but is atomic, so add a
function pointer to allow drivers to use the atomic framebuffer remove.

Changes since v1:
- Move the atomic framebuffer removal function from helpers to atomic core.
- Only override rmfb for i915, for atomic drivers it defaults to atomic rmfb.

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
---
 drivers/gpu/drm/drm_atomic.c    | 67 +++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_crtc.c      | 65 ++++++++++++++++++++++++++-------------
 drivers/gpu/drm/i915/i915_drv.c |  1 +
 include/drm/drmP.h              |  3 ++
 include/drm/drm_atomic.h        |  1 +
 5 files changed, 116 insertions(+), 21 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 86e89db02ed7..5a54d9e2d938 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1741,3 +1741,70 @@  out:
 
 	return ret;
 }
+
+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;
+	int ret = 0;
+	unsigned plane_mask;
+
+	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;
+		}
+
+		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;
+		plane->fb = NULL;
+	}
+
+	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;
+	}
+
+	if (ret || !plane_mask)
+		drm_atomic_state_free(state);
+
+	drm_modeset_drop_locks(&ctx);
+	drm_modeset_acquire_fini(&ctx);
+
+	return ret;
+}
+EXPORT_SYMBOL(drm_atomic_remove_fb);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 70f9c682d144..a9a3557f9f2b 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -557,6 +557,42 @@  void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
 }
 EXPORT_SYMBOL(drm_framebuffer_cleanup);
 
+static int
+legacy_remove_fb(struct drm_framebuffer *fb)
+{
+	struct drm_device *dev = fb->dev;
+	struct drm_crtc *crtc;
+	struct drm_plane *plane;
+	struct drm_mode_set set;
+	int ret = 0;
+
+	/* atomic drivers must use drm_atomic_helper_remove_fb */
+	WARN_ON(drm_core_check_feature(dev, DRIVER_ATOMIC));
+
+	drm_modeset_lock_all(dev);
+	/* remove from any CRTC */
+	drm_for_each_crtc(crtc, dev) {
+		if (crtc->primary->fb == fb) {
+			/* should turn off the crtc */
+			memset(&set, 0, sizeof(struct drm_mode_set));
+			set.crtc = crtc;
+			set.fb = NULL;
+			ret = drm_mode_set_config_internal(&set);
+			if (ret)
+				goto out;
+		}
+	}
+
+	drm_for_each_plane(plane, dev) {
+		if (plane->fb == fb)
+			drm_plane_force_disable(plane);
+	}
+
+out:
+	drm_modeset_unlock_all(dev);
+	return ret;
+}
+
 /**
  * drm_framebuffer_remove - remove and unreference a framebuffer object
  * @fb: framebuffer to remove
@@ -572,9 +608,6 @@  EXPORT_SYMBOL(drm_framebuffer_cleanup);
 void drm_framebuffer_remove(struct drm_framebuffer *fb)
 {
 	struct drm_device *dev;
-	struct drm_crtc *crtc;
-	struct drm_plane *plane;
-	struct drm_mode_set set;
 	int ret;
 
 	if (!fb)
@@ -600,25 +633,15 @@  void drm_framebuffer_remove(struct drm_framebuffer *fb)
 	 * in this manner.
 	 */
 	if (drm_framebuffer_read_refcount(fb) > 1) {
-		drm_modeset_lock_all(dev);
-		/* remove from any CRTC */
-		drm_for_each_crtc(crtc, dev) {
-			if (crtc->primary->fb == fb) {
-				/* should turn off the crtc */
-				memset(&set, 0, sizeof(struct drm_mode_set));
-				set.crtc = crtc;
-				set.fb = NULL;
-				ret = drm_mode_set_config_internal(&set);
-				if (ret)
-					DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
-			}
-		}
+		if (dev->driver->remove_fb)
+			ret = dev->driver->remove_fb(fb);
+		else if (drm_core_check_feature(dev, DRIVER_ATOMIC))
+			ret = drm_atomic_remove_fb(fb);
+		else
+			ret = legacy_remove_fb(fb);
 
-		drm_for_each_plane(plane, dev) {
-			if (plane->fb == fb)
-				drm_plane_force_disable(plane);
-		}
-		drm_modeset_unlock_all(dev);
+		if (ret)
+			DRM_ERROR("failed to remove fb %i/%p with %i\n", fb->base.id, fb, ret);
 	}
 
 	drm_framebuffer_unreference(fb);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 9fd221c97275..8736d15e8848 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1724,6 +1724,7 @@  static struct drm_driver driver = {
 	.dumb_create = i915_gem_dumb_create,
 	.dumb_map_offset = i915_gem_mmap_gtt,
 	.dumb_destroy = drm_gem_dumb_destroy,
+	.remove_fb = drm_atomic_remove_fb,
 	.ioctls = i915_ioctls,
 	.fops = &i915_driver_fops,
 	.name = DRIVER_NAME,
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 360b2a74e1ef..d63791039387 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -647,6 +647,9 @@  struct drm_driver {
 			    struct drm_device *dev,
 			    uint32_t handle);
 
+	/* rmfb and drm_framebuffer_remove hook */
+	int (*remove_fb)(struct drm_framebuffer *fb);
+
 	/* Driver private ops for this object */
 	const struct vm_operations_struct *gem_vm_ops;
 
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 92c84e9ab09a..0a04314534a5 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -138,6 +138,7 @@  drm_atomic_clean_old_fb(struct drm_device *dev, unsigned plane_mask, int ret);
 int __must_check drm_atomic_check_only(struct drm_atomic_state *state);
 int __must_check drm_atomic_commit(struct drm_atomic_state *state);
 int __must_check drm_atomic_nonblocking_commit(struct drm_atomic_state *state);
+int __must_check drm_atomic_remove_fb(struct drm_framebuffer *fb);
 
 #define for_each_connector_in_state(state, connector, connector_state, __i) \
 	for ((__i) = 0;							\