From b0ff4b93f646c39900ead9f6f7b6da78b5978273 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 24 Nov 2014 20:01:58 +0100 Subject: drm: Document that drm_dev_alloc doesn't need a parent Possible for purely virtual debug devices. Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_drv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 2e5c7d941313..4f41377b0b80 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -535,6 +535,8 @@ static void drm_fs_inode_free(struct inode *inode) * The initial ref-count of the object is 1. Use drm_dev_ref() and * drm_dev_unref() to take and drop further ref-counts. * + * Note that for purely virtual devices @parent can be NULL. + * * RETURNS: * Pointer to new DRM device, or NULL if out of memory. */ -- cgit v1.2.3 From ab58e3384b9f9863bfd029b458ff337d381bf6d2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 24 Nov 2014 20:42:42 +0100 Subject: drm/atomic-helper: Skip vblank waits for unchanged fbs Especially with legacy cursor ioctls existing userspace assumes that you can pile up lots of updates in one go. The super-proper way to support this would be a special commit mode which overwrites the last update. But getting there will be quite a bit of work. Meanwhile do what pretty much all the drivers have done for the plane update functions: Simply skip the vblank wait for the buffer cleanup if the buffer is the same. Since the universal cursor plane code will not recreate framebuffers needlessly this allows us to not slow down legacy pageflip events while someone moves the cursor around. v2: Drop the async plane update hunk from a previous attempt at this issue. v3: Fix up kerneldoc. v4: Don't oops so badly. Reported by Jasper. Cc: Rob Clark Cc: "Jasper St. Pierre" Reviewed-by: Rob Clark Reviewed-by: Jasper St. Pierre Tested-by: Jasper St. Pierre Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index a17b8e9c0a81..4368581ac1eb 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -751,6 +751,33 @@ static void wait_for_fences(struct drm_device *dev, } } +static bool framebuffer_changed(struct drm_device *dev, + struct drm_atomic_state *old_state, + struct drm_crtc *crtc) +{ + struct drm_plane *plane; + struct drm_plane_state *old_plane_state; + int nplanes = old_state->dev->mode_config.num_total_plane; + int i; + + for (i = 0; i < nplanes; i++) { + plane = old_state->planes[i]; + old_plane_state = old_state->plane_states[i]; + + if (!plane) + continue; + + if (plane->state->crtc != crtc && + old_plane_state->crtc != crtc) + continue; + + if (plane->state->fb != old_plane_state->fb) + return true; + } + + return false; +} + /** * drm_atomic_helper_wait_for_vblanks - wait for vblank on crtcs * @dev: DRM device @@ -758,7 +785,9 @@ static void wait_for_fences(struct drm_device *dev, * * Helper to, after atomic commit, wait for vblanks on all effected * crtcs (ie. before cleaning up old framebuffers using - * drm_atomic_helper_cleanup_planes()) + * drm_atomic_helper_cleanup_planes()). It will only wait on crtcs where the + * framebuffers have actually changed to optimize for the legacy cursor and + * plane update use-case. */ void drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, @@ -784,6 +813,9 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, if (!crtc->state->enable) continue; + if (!framebuffer_changed(dev, old_state, crtc)) + continue; + ret = drm_crtc_vblank_get(crtc); if (ret != 0) continue; -- cgit v1.2.3 From 9c04b7e3698a2e77b3473ef91a26ecb384459a04 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 24 Nov 2014 20:51:21 +0100 Subject: drm/atomic: Drop per-plane locking TODO I've forgotten to remove that in my per-plane locking patch. Reported-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index d3b46746b611..ba49b5ca822f 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -243,12 +243,6 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, if (state->plane_states[index]) return state->plane_states[index]; - /* - * TODO: We currently don't have per-plane mutexes. So instead of trying - * crazy tricks with deferring plane->crtc and hoping for the best just - * grab all crtc locks. Once we have per-plane locks we must update this - * to only take the plane mutex. - */ ret = drm_modeset_lock(&plane->mutex, state->acquire_ctx); if (ret) return ERR_PTR(ret); -- cgit v1.2.3 From aa54e2ee80b4f653f75b9139ae7500ee8cd5ad5f Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Thu, 20 Nov 2014 19:59:15 -0800 Subject: drm/atomic_helper: Cope with plane->crtc == NULL in disable helper The drm core can call the plane disable hook multiple times, which means it can get called when plane->crtc is already NULL. That in turn means we can't get at the implicit acquire ctx we use in the atomic helpers for legacy entries points. We could try to pass drm_modeset_legacy_acquire_ctx a drm_device pointer so that it can cope with a NULL crtc. But that still doesn't work since the cursor ioctls (remapped with the universal cursor plane support code) only grabs the crtc locks. So the global acquire context isn't set eitehr. The real solution here would be to bite the bullet and wire up explicit acquire context parameters to all relevant functions. We need to do that anyway (to be able to get rid of some small allocations which we can't cope with failing). But that's a lot of work and better done once atomic has settled a bit. So meanwhile just catch this case in the helper and bail out. Signed-off-by: Jasper St. Pierre Reviewed-by: Rob Clark Cc: Daniel Vetter [danvet: Completely rewrite commit message and comment but keep Jasper's logic and author credits since his patch is the only short-term solution that works.] Tested-by: Thierry Reding Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 4368581ac1eb..d981d07d50cd 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1275,6 +1275,17 @@ int drm_atomic_helper_disable_plane(struct drm_plane *plane) struct drm_plane_state *plane_state; int ret = 0; + /* + * FIXME: Without plane->crtc set we can't get at the implicit legacy + * acquire context. The real fix will be to wire the acquire ctx through + * everywhere we need it, but meanwhile prevent chaos by just skipping + * this noop. The critical case is the cursor ioctls which a) only grab + * crtc/cursor-plane locks (so we need the crtc to get at the right + * acquire context) and b) can try to disable the plane multiple times. + */ + if (!plane->crtc) + return 0; + state = drm_atomic_state_alloc(plane->dev); if (!state) return -ENOMEM; -- cgit v1.2.3 From f1c37e1adc6eca1fb492c74d466141d9b01e0428 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 25 Nov 2014 12:09:44 +0100 Subject: drm/plane: Pass old state to ->atomic_update() In most situations it will be useful to have the old state passed to the ->atomic_update() callback. For example if a plane is being disabled the new state's .crtc field will be NULL, but some drivers may rely on this field to program the CRTCs registers. v2: rename variable to old_plane_state and remove redundant comment as suggested by Daniel Vetter, remove an Exynos hunk that doesn't apply to drm-next and add a hunk for pending MSM mdp5 changes Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 5 ++++- drivers/gpu/drm/drm_plane_helper.c | 2 +- drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c | 3 ++- drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 3 ++- include/drm/drm_plane_helper.h | 3 ++- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index d981d07d50cd..2fa0840694d0 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1046,6 +1046,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, for (i = 0; i < nplanes; i++) { struct drm_plane_helper_funcs *funcs; struct drm_plane *plane = old_state->planes[i]; + struct drm_plane_state *old_plane_state; if (!plane) continue; @@ -1055,7 +1056,9 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, if (!funcs || !funcs->atomic_update) continue; - funcs->atomic_update(plane); + old_plane_state = old_state->plane_states[i]; + + funcs->atomic_update(plane, old_plane_state); } for (i = 0; i < ncrtcs; i++) { diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 93c6533c25da..18a1ac6ac22f 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -443,7 +443,7 @@ int drm_plane_helper_commit(struct drm_plane *plane, crtc_funcs[i]->atomic_begin(crtc[i]); } - plane_funcs->atomic_update(plane); + plane_funcs->atomic_update(plane, plane_state); for (i = 0; i < 2; i++) { if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush) diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c index 76d0a40c7138..1e5ebe83647d 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c @@ -107,7 +107,8 @@ static int mdp4_plane_atomic_check(struct drm_plane *plane, return 0; } -static void mdp4_plane_atomic_update(struct drm_plane *plane) +static void mdp4_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) { struct drm_plane_state *state = plane->state; int ret; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 533df7caa310..26e5fdea6594 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -213,7 +213,8 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane, return 0; } -static void mdp5_plane_atomic_update(struct drm_plane *plane) +static void mdp5_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct drm_plane_state *state = plane->state; diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h index c48f14d88690..fcfa969523fb 100644 --- a/include/drm/drm_plane_helper.h +++ b/include/drm/drm_plane_helper.h @@ -60,7 +60,8 @@ struct drm_plane_helper_funcs { int (*atomic_check)(struct drm_plane *plane, struct drm_plane_state *state); - void (*atomic_update)(struct drm_plane *plane); + void (*atomic_update)(struct drm_plane *plane, + struct drm_plane_state *old_state); }; static inline void drm_plane_helper_add(struct drm_plane *plane, -- cgit v1.2.3 From 5d571690154cbe7e7b4d24ea8af2393146609b0e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 25 Nov 2014 12:09:45 +0100 Subject: drm/plane: Add missing kerneldoc The plane helpers aren't pulled into the DocBook yet, so these weren't noticed. Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding Signed-off-by: Daniel Vetter --- include/drm/drm_plane_helper.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h index fcfa969523fb..a185392cafeb 100644 --- a/include/drm/drm_plane_helper.h +++ b/include/drm/drm_plane_helper.h @@ -49,6 +49,10 @@ extern int drm_crtc_init(struct drm_device *dev, /** * drm_plane_helper_funcs - helper operations for CRTCs + * @prepare_fb: prepare a framebuffer for use by the plane + * @cleanup_fb: cleanup a framebuffer when it's no longer used by the plane + * @atomic_check: check that a given atomic state is valid and can be applied + * @atomic_update: apply an atomic state to the plane * * The helper operations are called by the mid-layer CRTC helper. */ -- cgit v1.2.3 From 0254951d9c581ea21f989f7c8248079fb414a152 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 25 Nov 2014 12:09:47 +0100 Subject: drm: Make drm_atomic_helper.h standalone includible This header uses a bunch of declarations from the drm/drm_crtc.h header, so make sure to include that as well so that drm_atomic_helper.h can be included standalone. Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding Signed-off-by: Daniel Vetter --- include/drm/drm_atomic_helper.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 64b4e91b93bc..70a83197ef66 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -28,6 +28,8 @@ #ifndef DRM_ATOMIC_HELPER_H_ #define DRM_ATOMIC_HELPER_H_ +#include + int drm_atomic_helper_check(struct drm_device *dev, struct drm_atomic_state *state); int drm_atomic_helper_commit(struct drm_device *dev, -- cgit v1.2.3 From 37cc0148773abbd8e7dbf8b2cb3399e887233fc2 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 25 Nov 2014 12:09:48 +0100 Subject: drm: Make drm_atomic.h standalone includible This header file makes use of a bunch of structures declared in the drm_crtc.h header file. Include that to make sure the drm_atomic.h header can be included standalone. Signed-off-by: Thierry Reding Signed-off-by: Daniel Vetter --- include/drm/drm_atomic.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 9d919168bc11..e224ccfa11ca 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -28,6 +28,8 @@ #ifndef DRM_ATOMIC_H_ #define DRM_ATOMIC_H_ +#include + struct drm_atomic_state * __must_check drm_atomic_state_alloc(struct drm_device *dev); void drm_atomic_state_clear(struct drm_atomic_state *state); -- cgit v1.2.3 From 3009c0377f25c29852b218a6933a969d02cbdc5d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 25 Nov 2014 12:09:49 +0100 Subject: drm: Free atomic state during cleanup The current state of CRTCs, planes and connectors currently leaks during DRM driver ->unload() unless drivers explicitly clean it up. Since there is nothing driver-specific about it, that cleanup can be done within the DRM core. Reviewed-by: Daniel Vetter Signed-off-by: Thierry Reding Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 589a921d4313..e4e7b92f6d32 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -721,6 +721,10 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) drm_mode_object_put(dev, &crtc->base); list_del(&crtc->head); dev->mode_config.num_crtc--; + + WARN_ON(crtc->state && !crtc->funcs->atomic_destroy_state); + if (crtc->state && crtc->funcs->atomic_destroy_state) + crtc->funcs->atomic_destroy_state(crtc, crtc->state); } EXPORT_SYMBOL(drm_crtc_cleanup); @@ -918,6 +922,11 @@ void drm_connector_cleanup(struct drm_connector *connector) connector->name = NULL; list_del(&connector->head); dev->mode_config.num_connector--; + + WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); + if (connector->state && connector->funcs->atomic_destroy_state) + connector->funcs->atomic_destroy_state(connector, + connector->state); } EXPORT_SYMBOL(drm_connector_cleanup); @@ -1244,6 +1253,10 @@ void drm_plane_cleanup(struct drm_plane *plane) if (plane->type == DRM_PLANE_TYPE_OVERLAY) dev->mode_config.num_overlay_plane--; drm_modeset_unlock_all(dev); + + WARN_ON(plane->state && !plane->funcs->atomic_destroy_state); + if (plane->state && plane->funcs->atomic_destroy_state) + plane->funcs->atomic_destroy_state(plane, plane->state); } EXPORT_SYMBOL(drm_plane_cleanup); -- cgit v1.2.3 From 6ddd388ab222b66b596342becc76d5031c0e2fc8 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Fri, 21 Nov 2014 15:28:31 -0500 Subject: drm/atomic: track bitmask of planes attached to crtc Chasing plane->state->crtc of planes that are *not* part of the same atomic update is racy, making it incredibly awkward (or impossible) to do something simple like iterate over all planes and figure out which ones are attached to a crtc. Solve this by adding a bitmask of currently attached planes in the crtc-state. Note that the transitional helpers do not maintain the plane_mask. But they only support the legacy ioctls, which have sufficient brute-force locking around plane updates that they can continue to loop over all planes to see what is attached to a crtc the old way. Signed-off-by: Rob Clark [danvet: - Drop comments about locking in set_crtc_for_plane since they're a bit misleading - we already should hold lock for the current crtc. - Also WARN_ON if get_state on the old crtc fails since that should have been done already. - Squash in fixup to check get_plane_state return value, reported by Dan Carpenter and acked by Rob Clark.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 26 +++++++++++++++++++++----- drivers/gpu/drm/drm_atomic_helper.c | 8 ++++---- include/drm/drm_atomic.h | 4 ++-- include/drm/drm_crtc.h | 14 +++++++++++--- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index ba49b5ca822f..ff5f034cc405 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -344,7 +344,8 @@ EXPORT_SYMBOL(drm_atomic_get_connector_state); /** * drm_atomic_set_crtc_for_plane - set crtc for plane - * @plane_state: atomic state object for the plane + * @state: the incoming atomic state + * @plane: the plane whose incoming state to update * @crtc: crtc to use for the plane * * Changing the assigned crtc for a plane requires us to grab the lock and state @@ -357,20 +358,35 @@ EXPORT_SYMBOL(drm_atomic_get_connector_state); * sequence must be restarted. All other errors are fatal. */ int -drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, - struct drm_crtc *crtc) +drm_atomic_set_crtc_for_plane(struct drm_atomic_state *state, + struct drm_plane *plane, struct drm_crtc *crtc) { + struct drm_plane_state *plane_state = + drm_atomic_get_plane_state(state, plane); struct drm_crtc_state *crtc_state; + if (WARN_ON(IS_ERR(plane_state))) + return PTR_ERR(plane_state); + + if (plane_state->crtc) { + crtc_state = drm_atomic_get_crtc_state(plane_state->state, + plane_state->crtc); + if (WARN_ON(IS_ERR(crtc_state))) + return PTR_ERR(crtc_state); + + crtc_state->plane_mask &= ~(1 << drm_plane_index(plane)); + } + + plane_state->crtc = crtc; + if (crtc) { crtc_state = drm_atomic_get_crtc_state(plane_state->state, crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state); + crtc_state->plane_mask |= (1 << drm_plane_index(plane)); } - plane_state->crtc = crtc; - if (crtc) DRM_DEBUG_KMS("Link plane state %p to [CRTC:%d]\n", plane_state, crtc->base.id); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 2fa0840694d0..2ee509c92034 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1222,7 +1222,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); + ret = drm_atomic_set_crtc_for_plane(state, plane, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, fb); @@ -1301,7 +1301,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); + ret = drm_atomic_set_crtc_for_plane(state, plane, NULL); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, NULL); @@ -1472,7 +1472,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); + ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(primary_state, set->fb); @@ -1744,7 +1744,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); + ret = drm_atomic_set_crtc_for_plane(state, plane, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, fb); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index e224ccfa11ca..ad2229574dd9 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -46,8 +46,8 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, struct drm_connector *connector); int __must_check -drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, - struct drm_crtc *crtc); +drm_atomic_set_crtc_for_plane(struct drm_atomic_state *state, + struct drm_plane *plane, struct drm_crtc *crtc); void drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state, struct drm_framebuffer *fb); int __must_check diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b459e8fbbc25..4cf6905b57f5 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -231,6 +231,7 @@ struct drm_atomic_state; * struct drm_crtc_state - mutable CRTC state * @enable: whether the CRTC should be enabled, gates all other state * @mode_changed: for use by helpers and drivers when computing state updates + * @plane_mask: bitmask of (1 << drm_plane_index(plane)) of attached planes * @last_vblank_count: for helpers and drivers to capture the vblank of the * update to ensure framebuffer cleanup isn't done too early * @planes_changed: for use by helpers and drivers when computing state updates @@ -247,6 +248,13 @@ struct drm_crtc_state { bool planes_changed : 1; bool mode_changed : 1; + /* attached planes bitmask: + * WARNING: transitional helpers do not maintain plane_mask so + * drivers not converted over to atomic helpers should not rely + * on plane_mask being accurate! + */ + u32 plane_mask; + /* last_vblank_count: for vblank waits before cleanup */ u32 last_vblank_count; @@ -438,7 +446,7 @@ struct drm_crtc { * @state: backpointer to global drm_atomic_state */ struct drm_connector_state { - struct drm_crtc *crtc; + struct drm_crtc *crtc; /* do not write directly, use drm_atomic_set_crtc_for_connector() */ struct drm_encoder *best_encoder; @@ -673,8 +681,8 @@ struct drm_connector { * @state: backpointer to global drm_atomic_state */ struct drm_plane_state { - struct drm_crtc *crtc; - struct drm_framebuffer *fb; + struct drm_crtc *crtc; /* do not write directly, use drm_atomic_set_crtc_for_plane() */ + struct drm_framebuffer *fb; /* do not write directly, use drm_atomic_set_fb_for_plane() */ struct fence *fence; /* Signed dest location allows it to be partially off screen */ -- cgit v1.2.3 From dd275956aa176cf2ff00d5437c45f1552f1f3a11 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 25 Nov 2014 20:29:46 -0500 Subject: drm/atomic: add plane iterator macros Add helper macros to iterate the current, or incoming set of planes attached to a crtc. These helpers are only available for drivers converted to use atomic-helpers. Signed-off-by: Rob Clark [danvet: Squash in fixup from Rob to move the planemask iterator to drm_crtc.h and document it. That one is needed by the atomic ioctl so can't be in a helper library.] Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 1 + include/drm/drm_atomic_helper.h | 24 ++++++++++++++++++++++++ include/drm/drm_crtc.h | 13 +++++++++++++ 3 files changed, 38 insertions(+) diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 8c54f9a393cf..3789f2db3c21 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2343,6 +2343,7 @@ void intel_crt_init(struct drm_device *dev) Atomic State Reset and Initialization !Pdrivers/gpu/drm/drm_atomic_helper.c atomic state reset and initialization +!Iinclude/drm/drm_atomic_helper.h !Edrivers/gpu/drm/drm_atomic_helper.c diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 70a83197ef66..f956b413311e 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -98,5 +98,29 @@ drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector); void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state); +/** + * drm_atomic_crtc_for_each_plane - iterate over planes currently attached to CRTC + * @plane: the loop cursor + * @crtc: the crtc whose planes are iterated + * + * This iterates over the current state, useful (for example) when applying + * atomic state after it has been checked and swapped. To iterate over the + * planes which *will* be attached (for ->atomic_check()) see + * drm_crtc_for_each_pending_plane() + */ +#define drm_atomic_crtc_for_each_plane(plane, crtc) \ + drm_for_each_plane_mask(plane, (crtc)->dev, (crtc)->state->plane_mask) + +/** + * drm_crtc_atomic_state_for_each_plane - iterate over attached planes in new state + * @plane: the loop cursor + * @crtc_state: the incoming crtc-state + * + * Similar to drm_crtc_for_each_plane(), but iterates the planes that will be + * attached if the specified state is applied. Useful during (for example) + * ->atomic_check() operations, to validate the incoming state + */ +#define drm_atomic_crtc_state_for_each_plane(plane, crtc_state) \ + drm_for_each_plane_mask(plane, (crtc_state)->state->dev, (crtc_state)->plane_mask) #endif /* DRM_ATOMIC_HELPER_H_ */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 4cf6905b57f5..dd2c16e43333 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1062,6 +1062,19 @@ struct drm_mode_config { uint32_t cursor_width, cursor_height; }; +/** + * drm_for_each_plane_mask - iterate over planes specified by bitmask + * @plane: the loop cursor + * @dev: the DRM device + * @plane_mask: bitmask of plane indices + * + * Iterate over all planes specified by bitmask. + */ +#define drm_for_each_plane_mask(plane, dev, plane_mask) \ + list_for_each_entry((plane), &(dev)->mode_config.plane_list, head) \ + if ((plane_mask) & (1 << drm_plane_index(plane))) + + #define obj_to_crtc(x) container_of(x, struct drm_crtc, base) #define obj_to_connector(x) container_of(x, struct drm_connector, base) #define obj_to_encoder(x) container_of(x, struct drm_encoder, base) -- cgit v1.2.3 From 93b02beb41b06b3c43036fa600156448c51c2aaf Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 25 Nov 2014 20:29:47 -0500 Subject: drm/msm: switch to atomic-helpers iterator macros Signed-off-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 6 +++--- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 9 +++++---- drivers/gpu/drm/msm/msm_kms.h | 23 ----------------------- 3 files changed, 8 insertions(+), 30 deletions(-) diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index 6781aa994613..a7672e100d8b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -84,7 +84,7 @@ static void crtc_flush(struct drm_crtc *crtc) struct drm_plane *plane; uint32_t flush = 0; - for_each_plane_on_crtc(crtc, plane) { + drm_atomic_crtc_for_each_plane(plane, crtc) { enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane); flush |= pipe2flush(pipe_id); } @@ -197,7 +197,7 @@ static void setup_mixer(struct mdp4_kms *mdp4_kms) struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc); struct drm_plane *plane; - for_each_plane_on_crtc(crtc, plane) { + drm_atomic_crtc_for_each_plane(plane, crtc) { enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane); int idx = idxs[pipe_id]; mixer_cfg = mixercfg(mixer_cfg, mdp4_crtc->mixer, @@ -221,7 +221,7 @@ static void blend_setup(struct drm_crtc *crtc) mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH0(ovlp), 0); mdp4_write(mdp4_kms, REG_MDP4_OVLP_TRANSP_HIGH1(ovlp), 0); - for_each_plane_on_crtc(crtc, plane) { + drm_atomic_crtc_for_each_plane(plane, crtc) { enum mdp4_pipe pipe_id = mdp4_plane_pipe(plane); int idx = idxs[pipe_id]; if (idx > 0) { diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 0598bdea4ff4..0e9a2e3a82d7 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -91,7 +91,7 @@ static void crtc_flush_all(struct drm_crtc *crtc) if (!mdp5_crtc->ctl) return; - for_each_plane_on_crtc(crtc, plane) { + drm_atomic_crtc_for_each_plane(plane, crtc) { flush_mask |= mdp5_plane_get_flush(plane); } flush_mask |= mdp5_ctl_get_flush(mdp5_crtc->ctl); @@ -124,8 +124,9 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) } spin_unlock_irqrestore(&dev->event_lock, flags); - for_each_plane_on_crtc(crtc, plane) + drm_atomic_crtc_for_each_plane(plane, crtc) { mdp5_plane_complete_flip(plane); + } } static void mdp5_crtc_destroy(struct drm_crtc *crtc) @@ -195,7 +196,7 @@ static void blend_setup(struct drm_crtc *crtc) if (!mdp5_crtc->ctl) goto out; - for_each_plane_on_crtc(crtc, plane) { + drm_atomic_crtc_for_each_plane(plane, crtc) { enum mdp_mixer_stage_id stage = to_mdp5_plane_state(plane->state)->stage; @@ -317,7 +318,7 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, /* verify that there are not too many planes attached to crtc * and that we don't have conflicting mixer stages: */ - for_each_pending_plane_on_crtc(state->state, crtc, plane) { + drm_atomic_crtc_state_for_each_plane(plane, state) { struct drm_plane_state *pstate; if (cnt >= ARRAY_SIZE(pstates)) { diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h index 7fb4876388e7..06437745bc2c 100644 --- a/drivers/gpu/drm/msm/msm_kms.h +++ b/drivers/gpu/drm/msm/msm_kms.h @@ -65,27 +65,4 @@ static inline void msm_kms_init(struct msm_kms *kms, struct msm_kms *mdp4_kms_init(struct drm_device *dev); struct msm_kms *mdp5_kms_init(struct drm_device *dev); -/* TODO move these helper iterator macro somewhere common: */ -#define for_each_plane_on_crtc(_crtc, _plane) \ - list_for_each_entry((_plane), &(_crtc)->dev->mode_config.plane_list, head) \ - if ((_plane)->state->crtc == (_crtc)) - -static inline bool -__plane_will_be_attached_to_crtc(struct drm_atomic_state *state, - struct drm_plane *plane, struct drm_crtc *crtc) -{ - int idx = drm_plane_index(plane); - - /* if plane is modified in incoming state, use the new state: */ - if (state->plane_states[idx]) - return state->plane_states[idx]->crtc == crtc; - - /* otherwise, current state: */ - return plane->state->crtc == crtc; -} - -#define for_each_pending_plane_on_crtc(_state, _crtc, _plane) \ - list_for_each_entry((_plane), &(_crtc)->dev->mode_config.plane_list, head) \ - if (__plane_will_be_attached_to_crtc((_state), (_plane), (_crtc))) - #endif /* __MSM_KMS_H__ */ -- cgit v1.2.3 From 417009fb12f9821c30bc9540cba418321c2f14d3 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 25 Nov 2014 20:33:10 -0500 Subject: drm: fix indentation Signed-off-by: Rob Clark Signed-off-by: Daniel Vetter --- include/drm/drmP.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index be776fb2db18..8ba35c622e22 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -809,7 +809,7 @@ struct drm_device { struct drm_local_map *agp_buffer_map; unsigned int agp_buffer_token; - struct drm_mode_config mode_config; /**< Current mode config */ + struct drm_mode_config mode_config; /**< Current mode config */ /** \name GEM information */ /*@{ */ -- cgit v1.2.3 From 933f622fc25c7d14f8d435357f9146cfe58a5d7a Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Tue, 25 Nov 2014 20:33:11 -0500 Subject: drm: use mode_object_find helpers Signed-off-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index e4e7b92f6d32..3fd85772afb8 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2405,7 +2405,6 @@ int drm_mode_setplane(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_set_plane *plane_req = data; - struct drm_mode_object *obj; struct drm_plane *plane; struct drm_crtc *crtc = NULL; struct drm_framebuffer *fb = NULL; @@ -2428,14 +2427,12 @@ int drm_mode_setplane(struct drm_device *dev, void *data, * First, find the plane, crtc, and fb objects. If not available, * we don't bother to call the driver. */ - obj = drm_mode_object_find(dev, plane_req->plane_id, - DRM_MODE_OBJECT_PLANE); - if (!obj) { + plane = drm_plane_find(dev, plane_req->plane_id); + if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n", plane_req->plane_id); return -ENOENT; } - plane = obj_to_plane(obj); if (plane_req->fb_id) { fb = drm_framebuffer_lookup(dev, plane_req->fb_id); @@ -2445,14 +2442,12 @@ int drm_mode_setplane(struct drm_device *dev, void *data, return -ENOENT; } - obj = drm_mode_object_find(dev, plane_req->crtc_id, - DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, plane_req->crtc_id); + if (!crtc) { DRM_DEBUG_KMS("Unknown crtc ID %d\n", plane_req->crtc_id); return -ENOENT; } - crtc = obj_to_crtc(obj); } /* -- cgit v1.2.3 From abd69c55dd8f1f71b33b8c6165217f4329db8f25 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 25 Nov 2014 23:50:05 +0100 Subject: drm: Handle atomic state properly in kms getfoo ioctl So the problem with async commit (especially async modeset commit) is that the legacy pointers only get updated after the point of no return, in the async part of the modeset sequence. At least as implemented by the current helper functions. This is done in the set_routing_links function in drm_atomic_helper.c. Which also means that access isn't protected by locks but only coordinated by synchronizing with async workers. No problem thus far, until we lock at the getconnector/encoder ioctls. So fix this up by adding special cases for atomic drivers: For those we need to look at state objects. Unfortunately digging out the correct encoder->crtc link is a bit of work, so wrap this up in a helper function. Moving the assignments of connector->encoder and encoder->crtc earlier isn't a good idea because the point of the atomic helpers is that we stage the state updates. That way the disable functions can still inspect the links and rely upon them. v2: Extract full encoder->crtc lookup into helper (Rob). v3: Extract drm_connector_get_encoder too since - we need to always return state->best_encoder when there is a state otherwise we might return stale data if there's a pending async disable (and chase unlocked pointers, too). Same issue with encoder_get_crtc but there it's a bit more tricky to handle. Cc: Rob Clark Cc: Sean Paul Reviewed-by: Sean Paul Lightly-Tested-by: Sean Paul Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_crtc.c | 49 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 3fd85772afb8..de79283eaea7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1968,6 +1968,15 @@ static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, return true; } +static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector) +{ + /* For atomic drivers only state objects are synchronously updated and + * protected by modeset locks, so check those first. */ + if (connector->state) + return connector->state->best_encoder; + return connector->encoder; +} + /** * drm_mode_getconnector - get connector configuration * @dev: drm device for the ioctl @@ -1986,6 +1995,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, { struct drm_mode_get_connector *out_resp = data; struct drm_connector *connector; + struct drm_encoder *encoder; struct drm_display_mode *mode; int mode_count = 0; int props_count = 0; @@ -2041,8 +2051,10 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->subpixel = connector->display_info.subpixel_order; out_resp->connection = connector->status; drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - if (connector->encoder) - out_resp->encoder_id = connector->encoder->base.id; + + encoder = drm_connector_get_encoder(connector); + if (encoder) + out_resp->encoder_id = encoder->base.id; else out_resp->encoder_id = 0; drm_modeset_unlock(&dev->mode_config.connection_mutex); @@ -2112,6 +2124,33 @@ out: return ret; } +static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) +{ + struct drm_connector *connector; + struct drm_device *dev = encoder->dev; + bool uses_atomic = false; + + /* For atomic drivers only state objects are synchronously updated and + * protected by modeset locks, so check those first. */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->state) + continue; + + uses_atomic = true; + + if (connector->state->best_encoder != encoder) + continue; + + return connector->state->crtc; + } + + /* Don't return stale data (e.g. pending async disable). */ + if (uses_atomic) + return NULL; + + return encoder->crtc; +} + /** * drm_mode_getencoder - get encoder configuration * @dev: drm device for the ioctl @@ -2130,6 +2169,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, { struct drm_mode_get_encoder *enc_resp = data; struct drm_encoder *encoder; + struct drm_crtc *crtc; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -2139,7 +2179,10 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, return -ENOENT; drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - if (encoder->crtc) + crtc = drm_encoder_get_crtc(encoder); + if (crtc) + enc_resp->crtc_id = crtc->base.id; + else if (encoder->crtc) enc_resp->crtc_id = encoder->crtc->base.id; else enc_resp->crtc_id = 0; -- cgit v1.2.3 From e5b5341c28c66a122982d3d8822a4f9a0938f923 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 26 Nov 2014 18:58:04 -0500 Subject: drm/atomic: clear plane's CRTC and FB when shutting down Otherwise we'd still end up w/ the plane attached to the CRTC, and seemingly active, but without an FB. Which ends up going *boom* in the drivers. Slightly modified version of Daniel's irc suggestion. Note that the big problem isn't drivers going *boom* here (since we already have the situation of planes being left enabled when the crtc goes down). The real issue is that the core assumes the primary plane always goes down when calling ->set_config with a NULL mode. Ignoring that assumption leads to the legacy state pointers plane->fb/crtc getting out of sync with atomic, and that then leads to the subsequent *boom* all over the place. CC: Daniel Vetter Signed-off-by: Rob Clark [danvet: Drop my opinion of what's going sidewides here into the commit message as a note.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 2ee509c92034..4a78a773151c 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1452,11 +1452,24 @@ retry: goto fail; } + primary_state = drm_atomic_get_plane_state(state, crtc->primary); + if (IS_ERR(primary_state)) { + ret = PTR_ERR(primary_state); + goto fail; + } + if (!set->mode) { WARN_ON(set->fb); WARN_ON(set->num_connectors); crtc_state->enable = false; + + ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, NULL); + if (ret != 0) + goto fail; + + drm_atomic_set_fb_for_plane(primary_state, NULL); + goto commit; } @@ -1466,12 +1479,6 @@ retry: crtc_state->enable = true; drm_mode_copy(&crtc_state->mode, set->mode); - primary_state = drm_atomic_get_plane_state(state, crtc->primary); - if (IS_ERR(primary_state)) { - ret = PTR_ERR(primary_state); - goto fail; - } - ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, crtc); if (ret != 0) goto fail; -- cgit v1.2.3