Hi all,
So I finally unlazied and implemented generic nonblocking atomic commit support in the atomic helpers. Still an early draft, but stopped being a fireworks show on i915 at least. Rockchip still oopses somewhere, virtio conversion is entirely untested. Same for arc/hdlcd/fsl-du/sun4i. All these untested drivers must be converted since they've been a bit too lazy in their atomic implementations and simply didn't bother implementing nonblocking.
The main patch adding the helpers explains the design, I just want to highlight one key aspect: This code fully relies on correct handling of crtc_state->event, drivers which don't get this right will fall over. To avoid too much trouble the helpers all have a 10s timeout, in case of broken drivers. This is both good - hopefully no more atomic drivers that just outright forget to implement drm event handling. And also a bit annoying for getting this series in since the above mentioned 5 atomic drivers all look like they haven't been properly tested with events.
Stuff left to do: - Debug at least rockchip&virtio I'd say to make sure it works there flawlessly. Would be good to also convert some other drivers. - Get as much debugging on the other 4 drivers which have to be converted. Otoh they already have a broken atomic implementation in-tree, so me. - Kerneldoc for a bunch of functions is still missing, plus the updated overview section for nonblocking commits. - Bugfixing, but I hope that the helpers themselves are solid now.
Cheers, Daniel
Daniel Vetter (25): drm/atomic-helper: use for_each_*_in_state more drm/i915: Use drm_atomic_get_existing_plane_state drm/msm: Use for_each_*_in_state drm/rcar-du: Use for_each_*_in_state drm/vc4: Use for_each_plane_in_state drm/atomic: Add __drm_atomic_get_current_plane_state drm/exynos: Use for_each_crtc_in_state drm: Consolidate connector arrays in drm_atomic_state drm: Consolidate plane arrays in drm_atomic_state drm: Consolidate crtc arrays in drm_atomic_state drm/atomic-helper: Massage swap_state signature somewhat drm/arc: Nuke event_list drm/arc: Actually bother with handling atomic events. drm/arc: Implement nonblocking commit correctly drm/hdlcd: Use helper support for nonblocking commits drm/fsl-du: Implement some semblance of vblank event handling drm/hisilicon: Implement some semblance of vblank event handling drm/sun4i: Implement some semblance of vblank event handling drm/atomic: kerneldoc for drm_atomic_crtc_needs_modeset drm/atomic-helper: nonblocking commit support drm/i915: Signal drm events for atomic drm/i915: Roll out the helper nonblock tracking drm/rockchip: convert to helper nonblocking atomic commit drm/rockchip: Nuke pending event handling in preclose drm/virtio: Don't reinvent a flipping wheel
Gustavo Padovan (1): drm/fence: add fence to drm_pending_event
drivers/gpu/drm/arc/arcpgu.h | 1 - drivers/gpu/drm/arc/arcpgu_crtc.c | 19 +- drivers/gpu/drm/arc/arcpgu_drv.c | 27 +- drivers/gpu/drm/arm/hdlcd_drv.c | 8 +- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 2 +- drivers/gpu/drm/drm_atomic.c | 102 +++--- drivers/gpu/drm/drm_atomic_helper.c | 421 ++++++++++++++++++++---- drivers/gpu/drm/drm_crtc.c | 3 + drivers/gpu/drm/drm_fops.c | 22 +- drivers/gpu/drm/exynos/exynos_drm_drv.c | 10 +- drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c | 23 +- drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 20 +- drivers/gpu/drm/i915/intel_atomic.c | 6 +- drivers/gpu/drm/i915/intel_display.c | 25 +- drivers/gpu/drm/i915/intel_sprite.c | 14 + drivers/gpu/drm/mediatek/mtk_drm_drv.c | 2 +- drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c | 20 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 10 +- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 12 +- drivers/gpu/drm/msm/msm_atomic.c | 37 +-- drivers/gpu/drm/nouveau/nouveau_usif.c | 1 - drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 10 +- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 20 +- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 25 -- drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 11 - drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 73 +--- drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 35 +- drivers/gpu/drm/sti/sti_drv.c | 2 +- drivers/gpu/drm/sun4i/sun4i_crtc.c | 12 + drivers/gpu/drm/tegra/drm.c | 2 +- drivers/gpu/drm/vc4/vc4_crtc.c | 11 +- drivers/gpu/drm/vc4/vc4_drv.h | 2 +- drivers/gpu/drm/vc4/vc4_kms.c | 12 +- drivers/gpu/drm/vc4/vc4_plane.c | 5 +- drivers/gpu/drm/virtio/virtgpu_display.c | 48 +-- include/drm/drmP.h | 4 +- include/drm/drm_atomic.h | 79 ++++- include/drm/drm_atomic_helper.h | 12 +- include/drm/drm_crtc.h | 149 ++++++++- include/drm/drm_modeset_helper_vtables.h | 36 ++ 41 files changed, 847 insertions(+), 488 deletions(-)
This avois leaking drm_atomic_state internals into the helpers. The only place where this still happens after this patch is drm_atomic_helper_swap_state(). It's unavoidable there, and maybe a good indicator we should actually move that function into drm_atomic.c.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 47 +++++++++++++------------------------ 1 file changed, 16 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index ddfa0d120e39..464541c87c40 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -611,7 +611,7 @@ drm_atomic_helper_check_planes(struct drm_device *dev, if (!funcs || !funcs->atomic_check) continue;
- ret = funcs->atomic_check(crtc, state->crtc_states[i]); + ret = funcs->atomic_check(crtc, crtc_state); if (ret) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] atomic driver check failed\n", crtc->base.id, crtc->name); @@ -1249,16 +1249,12 @@ EXPORT_SYMBOL(drm_atomic_helper_commit); int drm_atomic_helper_prepare_planes(struct drm_device *dev, struct drm_atomic_state *state) { - int nplanes = dev->mode_config.num_total_plane; - int ret, i; + struct drm_plane *plane; + struct drm_plane_state *plane_state; + int ret, i, j;
- for (i = 0; i < nplanes; i++) { + for_each_plane_in_state(state, plane, plane_state, i) { const struct drm_plane_helper_funcs *funcs; - struct drm_plane *plane = state->planes[i]; - struct drm_plane_state *plane_state = state->plane_states[i]; - - if (!plane) - continue;
funcs = plane->helper_private;
@@ -1272,12 +1268,10 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev, return 0;
fail: - for (i--; i >= 0; i--) { + for_each_plane_in_state(state, plane, plane_state, j) { const struct drm_plane_helper_funcs *funcs; - struct drm_plane *plane = state->planes[i]; - struct drm_plane_state *plane_state = state->plane_states[i];
- if (!plane) + if (j >= i) continue;
funcs = plane->helper_private; @@ -1564,35 +1558,26 @@ void drm_atomic_helper_swap_state(struct drm_device *dev, struct drm_atomic_state *state) { int i; + struct drm_connector *connector; + struct drm_connector_state *conn_state; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_plane *plane; + struct drm_plane_state *plane_state;
- for (i = 0; i < state->num_connector; i++) { - struct drm_connector *connector = state->connectors[i]; - - if (!connector) - continue; - + for_each_connector_in_state(state, connector, conn_state, i) { connector->state->state = state; swap(state->connector_states[i], connector->state); connector->state->state = NULL; }
- for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct drm_crtc *crtc = state->crtcs[i]; - - if (!crtc) - continue; - + for_each_crtc_in_state(state, crtc, crtc_state, i) { crtc->state->state = state; swap(state->crtc_states[i], crtc->state); crtc->state->state = NULL; }
- for (i = 0; i < dev->mode_config.num_total_plane; i++) { - struct drm_plane *plane = state->planes[i]; - - if (!plane) - continue; - + for_each_plane_in_state(state, plane, plane_state, i) { plane->state->state = state; swap(state->plane_states[i], plane->state); plane->state->state = NULL;
Op 29-05-16 om 20:34 schreef Daniel Vetter:
This avois leaking drm_atomic_state internals into the helpers. The only place where this still happens after this patch is drm_atomic_helper_swap_state(). It's unavoidable there, and maybe a good indicator we should actually move that function into drm_atomic.c.
Would be a good idea, commit is part of atomic core and there's really only one way to do swap_state. Drivers can swap any required internal state before or after the call.
~Maarten.
On Mon, May 30, 2016 at 02:17:06PM +0200, Maarten Lankhorst wrote:
Op 29-05-16 om 20:34 schreef Daniel Vetter:
This avois leaking drm_atomic_state internals into the helpers. The only place where this still happens after this patch is drm_atomic_helper_swap_state(). It's unavoidable there, and maybe a good indicator we should actually move that function into drm_atomic.c.
Would be a good idea, commit is part of atomic core and there's really only one way to do swap_state. Drivers can swap any required internal state before or after the call.
Later on I'm adding some of the nonblocking stuff to swap_state, and I think at least the nonblocking support should be part of the helpers.
I'm torn again about where to put it ;-) -Daniel
We want to encapsulate the drm_atomic_state internals.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 50ff90aea721..3e6d9ff8840a 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -223,7 +223,9 @@ int intel_atomic_setup_scalers(struct drm_device *dev, continue; }
- plane_state = to_intel_plane_state(drm_state->plane_states[i]); + plane_state = to_intel_plane_state( + drm_atomic_get_existing_plane_state(drm_state, + plane)); scaler_id = &plane_state->scaler_id; }
Op 29-05-16 om 20:34 schreef Daniel Vetter:
We want to encapsulate the drm_atomic_state internals.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/i915/intel_atomic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 50ff90aea721..3e6d9ff8840a 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -223,7 +223,9 @@ int intel_atomic_setup_scalers(struct drm_device *dev, continue; }
plane_state = to_intel_plane_state(drm_state->plane_states[i]);
plane_state = to_intel_plane_state(
drm_atomic_get_existing_plane_state(drm_state,
}plane)); scaler_id = &plane_state->scaler_id;
Use intel_atomic_get_existing_plane_state with intel_plane. :)
We want to hide drm_atomic_state internals
Cc: Rob Clark robdclark@gmail.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c | 20 +++++++---------- drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 12 +++------- drivers/gpu/drm/msm/msm_atomic.c | 35 ++++++++++-------------------- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 1 + 4 files changed, 23 insertions(+), 45 deletions(-)
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 67442d50a6c2..f145d256e332 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -106,31 +106,27 @@ out: static void mdp4_prepare_commit(struct msm_kms *kms, struct drm_atomic_state *state) { struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); - int i, ncrtcs = state->dev->mode_config.num_crtc; + int i; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state;
mdp4_enable(mdp4_kms);
/* see 119ecb7fd */ - for (i = 0; i < ncrtcs; i++) { - struct drm_crtc *crtc = state->crtcs[i]; - if (!crtc) - continue; + for_each_crtc_in_state(state, crtc, crtc_state, i) drm_crtc_vblank_get(crtc); - } }
static void mdp4_complete_commit(struct msm_kms *kms, struct drm_atomic_state *state) { struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); - int i, ncrtcs = state->dev->mode_config.num_crtc; + int i; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state;
/* see 119ecb7fd */ - for (i = 0; i < ncrtcs; i++) { - struct drm_crtc *crtc = state->crtcs[i]; - if (!crtc) - continue; + for_each_crtc_in_state(state, crtc, crtc_state, i) drm_crtc_vblank_put(crtc); - }
mdp4_disable(mdp4_kms); } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index 484b4d15e71d..f0c285b1c027 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -78,17 +78,11 @@ static void mdp5_complete_commit(struct msm_kms *kms, struct drm_atomic_state *s { int i; struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms)); - int nplanes = mdp5_kms->dev->mode_config.num_total_plane; - - for (i = 0; i < nplanes; i++) { - struct drm_plane *plane = state->planes[i]; - struct drm_plane_state *plane_state = state->plane_states[i]; - - if (!plane) - continue; + struct drm_plane *plane; + struct drm_plane_state *plane_state;
+ for_each_plane_in_state(state, plane, plane_state, i) mdp5_plane_complete_commit(plane, plane_state); - }
mdp5_disable(mdp5_kms); } diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index e3892c263f27..9c0e4261dbba 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -84,17 +84,12 @@ static void msm_atomic_wait_for_commit_done(struct drm_device *dev, struct drm_atomic_state *old_state) { struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; struct msm_drm_private *priv = old_state->dev->dev_private; struct msm_kms *kms = priv->kms; - int ncrtcs = old_state->dev->mode_config.num_crtc; int i;
- for (i = 0; i < ncrtcs; i++) { - crtc = old_state->crtcs[i]; - - if (!crtc) - continue; - + for_each_crtc_in_state(old_state, crtc, crtc_state, i) { if (!crtc->state->enable) continue;
@@ -192,9 +187,11 @@ int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, bool nonblock) { struct msm_drm_private *priv = dev->dev_private; - int nplanes = dev->mode_config.num_total_plane; - int ncrtcs = dev->mode_config.num_crtc; struct msm_commit *c; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_plane *plane; + struct drm_plane_state *plane_state; int i, ret;
ret = drm_atomic_helper_prepare_planes(dev, state); @@ -210,28 +207,18 @@ int msm_atomic_commit(struct drm_device *dev, /* * Figure out what crtcs we have: */ - for (i = 0; i < ncrtcs; i++) { - struct drm_crtc *crtc = state->crtcs[i]; - if (!crtc) - continue; + for_each_crtc_in_state(state, crtc, crtc_state, i) c->crtc_mask |= (1 << drm_crtc_index(crtc)); - }
/* * Figure out what fence to wait for: */ - for (i = 0; i < nplanes; i++) { - struct drm_plane *plane = state->planes[i]; - struct drm_plane_state *new_state = state->plane_states[i]; - - if (!plane) - continue; - - if ((plane->state->fb != new_state->fb) && new_state->fb) { - struct drm_gem_object *obj = msm_framebuffer_bo(new_state->fb, 0); + for_each_plane_in_state(state, plane, plane_state, i) { + if ((plane->state->fb != plane_state->fb) && plane_state->fb) { + struct drm_gem_object *obj = msm_framebuffer_bo(plane_state->fb, 0); struct msm_gem_object *msm_obj = to_msm_bo(obj);
- new_state->fence = reservation_object_get_excl_rcu(msm_obj->resv); + plane_state->fence = reservation_object_get_excl_rcu(msm_obj->resv); } }
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 755cfdba61cd..03913b483506 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -197,6 +197,7 @@ rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_stat struct drm_crtc *crtc; int i, ret;
+ for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { /* No one cares about the old state, so abuse it for tracking * and store whether we hold a vblank reference (and should do a
We want to hide drm_atomic_state internals better.
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 8 ++++---- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 20 ++++++++------------ 2 files changed, 12 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index e70a4f33d970..f315c55c1f65 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -288,6 +288,8 @@ static int rcar_du_atomic_commit(struct drm_device *dev, { struct rcar_du_device *rcdu = dev->dev_private; struct rcar_du_commit *commit; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; unsigned int i; int ret;
@@ -309,10 +311,8 @@ static int rcar_du_atomic_commit(struct drm_device *dev, /* Wait until all affected CRTCs have completed previous commits and * mark them as pending. */ - for (i = 0; i < dev->mode_config.num_crtc; ++i) { - if (state->crtcs[i]) - commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]); - } + for_each_crtc_in_state(state, crtc, crtc_state, i) + commit->crtcs |= 1 << drm_crtc_index(crtc);
spin_lock(&rcdu->commit.wait.lock); ret = wait_event_interruptible_locked(rcdu->commit.wait, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index d445e67f78e1..bfe31ca870cc 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -140,18 +140,17 @@ int rcar_du_atomic_check_planes(struct drm_device *dev, bool needs_realloc = false; unsigned int groups = 0; unsigned int i; + struct drm_plane *drm_plane; + struct drm_plane_state *drm_plane_state;
/* Check if hardware planes need to be reallocated. */ - for (i = 0; i < dev->mode_config.num_total_plane; ++i) { + for_each_plane_in_state(state, drm_plane, drm_plane_state, i) { struct rcar_du_plane_state *plane_state; struct rcar_du_plane *plane; unsigned int index;
- if (!state->planes[i]) - continue; - - plane = to_rcar_plane(state->planes[i]); - plane_state = to_rcar_plane_state(state->plane_states[i]); + plane = to_rcar_plane(drm_plane); + plane_state = to_rcar_plane_state(drm_plane_state);
dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__, plane->group->index, plane - plane->group->planes); @@ -247,18 +246,15 @@ int rcar_du_atomic_check_planes(struct drm_device *dev, }
/* Reallocate hardware planes for each plane that needs it. */ - for (i = 0; i < dev->mode_config.num_total_plane; ++i) { + for_each_plane_in_state(state, drm_plane, drm_plane_state, i) { struct rcar_du_plane_state *plane_state; struct rcar_du_plane *plane; unsigned int crtc_planes; unsigned int free; int idx;
- if (!state->planes[i]) - continue; - - plane = to_rcar_plane(state->planes[i]); - plane_state = to_rcar_plane_state(state->plane_states[i]); + plane = to_rcar_plane(drm_plane); + plane_state = to_rcar_plane_state(drm_plane_state);
dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__, plane->group->index, plane - plane->group->planes);
Hi Daniel,
Thank you for the patch.
This looks good to me as the resulting code is mostly similar. However, the for_each_*_in_state macros end with an for_each_if() that tests if the object's state is NULL, which isn't present in this code. I'm wondering whether that was an oversight on my side possibly leading to a crash when dereferencing a NULL state, or an unneeded check in the macros. Can atomic_state->*_states[i] be NULL if atomic_state->*[i] is not NULL ?
On Sunday 29 May 2016 20:35:01 Daniel Vetter wrote:
We want to hide drm_atomic_state internals better.
Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/rcar-du/rcar_du_kms.c | 8 ++++---- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 20 ++++++++------------ 2 files changed, 12 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index e70a4f33d970..f315c55c1f65 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -288,6 +288,8 @@ static int rcar_du_atomic_commit(struct drm_device *dev, { struct rcar_du_device *rcdu = dev->dev_private; struct rcar_du_commit *commit;
- struct drm_crtc *crtc;
- struct drm_crtc_state *crtc_state; unsigned int i; int ret;
@@ -309,10 +311,8 @@ static int rcar_du_atomic_commit(struct drm_device *dev, /* Wait until all affected CRTCs have completed previous commits and
- mark them as pending. */
- for (i = 0; i < dev->mode_config.num_crtc; ++i) {
if (state->crtcs[i])
commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]);
- }
for_each_crtc_in_state(state, crtc, crtc_state, i)
commit->crtcs |= 1 << drm_crtc_index(crtc);
spin_lock(&rcdu->commit.wait.lock); ret = wait_event_interruptible_locked(rcdu->commit.wait,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index d445e67f78e1..bfe31ca870cc 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -140,18 +140,17 @@ int rcar_du_atomic_check_planes(struct drm_device *dev, bool needs_realloc = false; unsigned int groups = 0; unsigned int i;
struct drm_plane *drm_plane;
struct drm_plane_state *drm_plane_state;
/* Check if hardware planes need to be reallocated. */
- for (i = 0; i < dev->mode_config.num_total_plane; ++i) {
- for_each_plane_in_state(state, drm_plane, drm_plane_state, i) { struct rcar_du_plane_state *plane_state; struct rcar_du_plane *plane; unsigned int index;
if (!state->planes[i])
continue;
plane = to_rcar_plane(state->planes[i]);
plane_state = to_rcar_plane_state(state->plane_states[i]);
plane = to_rcar_plane(drm_plane);
plane_state = to_rcar_plane_state(drm_plane_state);
dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__, plane->group->index, plane - plane->group->planes);
@@ -247,18 +246,15 @@ int rcar_du_atomic_check_planes(struct drm_device *dev, }
/* Reallocate hardware planes for each plane that needs it. */
- for (i = 0; i < dev->mode_config.num_total_plane; ++i) {
- for_each_plane_in_state(state, drm_plane, drm_plane_state, i) { struct rcar_du_plane_state *plane_state; struct rcar_du_plane *plane; unsigned int crtc_planes; unsigned int free; int idx;
if (!state->planes[i])
continue;
plane = to_rcar_plane(state->planes[i]);
plane_state = to_rcar_plane_state(state->plane_states[i]);
plane = to_rcar_plane(drm_plane);
plane_state = to_rcar_plane_state(drm_plane_state);
dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__, plane->group->index, plane - plane->group->planes);
Op 30-05-16 om 11:18 schreef Laurent Pinchart:
Hi Daniel,
Thank you for the patch.
This looks good to me as the resulting code is mostly similar. However, the for_each_*_in_state macros end with an for_each_if() that tests if the object's state is NULL, which isn't present in this code. I'm wondering whether that was an oversight on my side possibly leading to a crash when dereferencing a NULL state, or an unneeded check in the macros. Can atomic_state->*_states[i] be NULL if atomic_state->*[i] is not NULL ?
Not in any normal case.
On Mon, May 30, 2016 at 11:58:27AM +0200, Maarten Lankhorst wrote:
Op 30-05-16 om 11:18 schreef Laurent Pinchart:
Hi Daniel,
Thank you for the patch.
This looks good to me as the resulting code is mostly similar. However, the for_each_*_in_state macros end with an for_each_if() that tests if the object's state is NULL, which isn't present in this code. I'm wondering whether that was an oversight on my side possibly leading to a crash when dereferencing a NULL state, or an unneeded check in the macros. Can atomic_state->*_states[i] be NULL if atomic_state->*[i] is not NULL ?
Not in any normal case.
Yeah, the drm_atomic_get_*_state functions only ever fill in both of neither. If this gets out of sync it's a bug ;-) -Daniel
Hi Daniel,
On Monday 30 May 2016 16:54:10 Daniel Vetter wrote:
On Mon, May 30, 2016 at 11:58:27AM +0200, Maarten Lankhorst wrote:
Op 30-05-16 om 11:18 schreef Laurent Pinchart:
Hi Daniel,
Thank you for the patch.
This looks good to me as the resulting code is mostly similar. However, the for_each_*_in_state macros end with an for_each_if() that tests if the object's state is NULL, which isn't present in this code. I'm wondering whether that was an oversight on my side possibly leading to a crash when dereferencing a NULL state, or an unneeded check in the macros. Can atomic_state->*_states[i] be NULL if atomic_state->*[i] is not NULL ?
Not in any normal case.
Yeah, the drm_atomic_get_*_state functions only ever fill in both of neither. If this gets out of sync it's a bug ;-)
Should the check be removed then ? Or replaced by a WARN_ON() ?
On Fri, Jun 03, 2016 at 01:54:44AM +0300, Laurent Pinchart wrote:
Hi Daniel,
On Monday 30 May 2016 16:54:10 Daniel Vetter wrote:
On Mon, May 30, 2016 at 11:58:27AM +0200, Maarten Lankhorst wrote:
Op 30-05-16 om 11:18 schreef Laurent Pinchart:
Hi Daniel,
Thank you for the patch.
This looks good to me as the resulting code is mostly similar. However, the for_each_*_in_state macros end with an for_each_if() that tests if the object's state is NULL, which isn't present in this code. I'm wondering whether that was an oversight on my side possibly leading to a crash when dereferencing a NULL state, or an unneeded check in the macros. Can atomic_state->*_states[i] be NULL if atomic_state->*[i] is not NULL ?
Not in any normal case.
Yeah, the drm_atomic_get_*_state functions only ever fill in both of neither. If this gets out of sync it's a bug ;-)
Should the check be removed then ? Or replaced by a WARN_ON() ?
In all the places I converted here I nuked those checks, since they moved into the loop now. Not sure what checks you're talking about. -Daniel
Hi Daniel,
On Friday 03 Jun 2016 08:55:36 Daniel Vetter wrote:
On Fri, Jun 03, 2016 at 01:54:44AM +0300, Laurent Pinchart wrote:
On Monday 30 May 2016 16:54:10 Daniel Vetter wrote:
On Mon, May 30, 2016 at 11:58:27AM +0200, Maarten Lankhorst wrote:
Op 30-05-16 om 11:18 schreef Laurent Pinchart:
Hi Daniel,
Thank you for the patch.
This looks good to me as the resulting code is mostly similar. However, the for_each_*_in_state macros end with an for_each_if() that tests if the object's state is NULL, which isn't present in this code. I'm wondering whether that was an oversight on my side possibly leading to a crash when dereferencing a NULL state, or an unneeded check in the macros. Can atomic_state->*_states[i] be NULL if atomic_state->*[i] is not NULL ?
Not in any normal case.
Yeah, the drm_atomic_get_*_state functions only ever fill in both of neither. If this gets out of sync it's a bug ;-)
Should the check be removed then ? Or replaced by a WARN_ON() ?
In all the places I converted here I nuked those checks, since they moved into the loop now. Not sure what checks you're talking about.
I'm talking about the for_each_if() check inside the for_each_*_in_state macros.
On Fri, Jun 3, 2016 at 11:40 AM, Laurent Pinchart laurent.pinchart@ideasonboard.com wrote:
On Friday 03 Jun 2016 08:55:36 Daniel Vetter wrote:
On Fri, Jun 03, 2016 at 01:54:44AM +0300, Laurent Pinchart wrote:
On Monday 30 May 2016 16:54:10 Daniel Vetter wrote:
On Mon, May 30, 2016 at 11:58:27AM +0200, Maarten Lankhorst wrote:
Op 30-05-16 om 11:18 schreef Laurent Pinchart:
Hi Daniel,
Thank you for the patch.
This looks good to me as the resulting code is mostly similar. However, the for_each_*_in_state macros end with an for_each_if() that tests if the object's state is NULL, which isn't present in this code. I'm wondering whether that was an oversight on my side possibly leading to a crash when dereferencing a NULL state, or an unneeded check in the macros. Can atomic_state->*_states[i] be NULL if atomic_state->*[i] is not NULL ?
Not in any normal case.
Yeah, the drm_atomic_get_*_state functions only ever fill in both of neither. If this gets out of sync it's a bug ;-)
Should the check be removed then ? Or replaced by a WARN_ON() ?
In all the places I converted here I nuked those checks, since they moved into the loop now. Not sure what checks you're talking about.
I'm talking about the for_each_if() check inside the for_each_*_in_state macros.
The rule is drm_atomic_state->plane[i] != NULL iff drm_atomic_state->plane_state != NULL. So you can check either of them for the same result. But you still need to check one of them, otherwise all the loops in drivers and helpers will oops. Not sure why you want to remove that check, your driver had the equivalent (which I removed) too. -Daniel
Hi Daniel,
On Friday 03 Jun 2016 11:45:56 Daniel Vetter wrote:
On Fri, Jun 3, 2016 at 11:40 AM, Laurent Pinchart wrote:
On Friday 03 Jun 2016 08:55:36 Daniel Vetter wrote:
On Fri, Jun 03, 2016 at 01:54:44AM +0300, Laurent Pinchart wrote:
On Monday 30 May 2016 16:54:10 Daniel Vetter wrote:
On Mon, May 30, 2016 at 11:58:27AM +0200, Maarten Lankhorst wrote:
Op 30-05-16 om 11:18 schreef Laurent Pinchart: > Hi Daniel, > > Thank you for the patch. > > This looks good to me as the resulting code is mostly similar. > However, the for_each_*_in_state macros end with an for_each_if() > that tests if the object's state is NULL, which isn't present in > this code. I'm wondering whether that was an oversight on my side > possibly leading to a crash when dereferencing a NULL state, or an > unneeded check in the macros. Can atomic_state->*_states[i] be NULL > if atomic_state->*[i] is not NULL ?
Not in any normal case.
Yeah, the drm_atomic_get_*_state functions only ever fill in both of neither. If this gets out of sync it's a bug ;-)
Should the check be removed then ? Or replaced by a WARN_ON() ?
In all the places I converted here I nuked those checks, since they moved into the loop now. Not sure what checks you're talking about.
I'm talking about the for_each_if() check inside the for_each_*_in_state macros.
The rule is drm_atomic_state->plane[i] != NULL iff drm_atomic_state->plane_state != NULL. So you can check either of them for the same result. But you still need to check one of them, otherwise all the loops in drivers and helpers will oops. Not sure why you want to remove that check, your driver had the equivalent (which I removed) too.
My bad, I had misread the for_each_*_in_state macros and thought they were checking both. Sorry for the noise.
We want to hide drm_atomic_stat internals a bit better.
Cc: Eric Anholt eric@anholt.net Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/vc4/vc4_kms.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index cb37751bc99f..39c0b2048bfd 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -111,6 +111,8 @@ static int vc4_atomic_commit(struct drm_device *dev, int i; uint64_t wait_seqno = 0; struct vc4_commit *c; + struct drm_plane *plane; + struct drm_plane_state *new_state;
c = commit_init(state); if (!c) @@ -130,13 +132,7 @@ static int vc4_atomic_commit(struct drm_device *dev, return ret; }
- for (i = 0; i < dev->mode_config.num_total_plane; i++) { - struct drm_plane *plane = state->planes[i]; - struct drm_plane_state *new_state = state->plane_states[i]; - - if (!plane) - continue; - + for_each_plane_in_state(state, plane, new_state, i) { if ((plane->state->fb != new_state->fb) && new_state->fb) { struct drm_gem_cma_object *cma_bo = drm_fb_cma_get_gem_obj(new_state->fb, 0);
... and use it in msm&vc4. Again just want to encapsulate drm_atomic_state internals a bit.
The const threading is a bit awkward in vc4 since C sucks, but I still think it's worth to enforce this. Eventually I want to make all the obj->state pointers const too, but that's a lot more work ...
Cc: Eric Anholt eric@anholt.net Cc: Rob Clark robdclark@gmail.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 10 +++------ drivers/gpu/drm/vc4/vc4_crtc.c | 11 +++------- drivers/gpu/drm/vc4/vc4_drv.h | 2 +- drivers/gpu/drm/vc4/vc4_plane.c | 5 +++-- include/drm/drm_atomic.h | 36 ++++++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 88fe256c1931..6d4086ee0503 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -383,19 +383,15 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, */ hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); drm_atomic_crtc_state_for_each_plane(plane, state) { - struct drm_plane_state *pstate; + const struct drm_plane_state *pstate; if (cnt >= (hw_cfg->lm.nb_stages)) { dev_err(dev->dev, "too many planes!\n"); return -EINVAL; }
- pstate = state->state->plane_states[drm_plane_index(plane)]; + pstate = __drm_atomic_get_current_plane_state(state->state, + plane);
- /* plane might not have changed, in which case take - * current state: - */ - if (!pstate) - pstate = plane->state; pstates[cnt].plane = plane; pstates[cnt].state = to_mdp5_plane_state(pstate);
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 904d0754ad78..703bda170105 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -405,14 +405,9 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, return -EINVAL;
drm_atomic_crtc_state_for_each_plane(plane, state) { - struct drm_plane_state *plane_state = - state->state->plane_states[drm_plane_index(plane)]; - - /* plane might not have changed, in which case take - * current state: - */ - if (!plane_state) - plane_state = plane->state; + const struct drm_plane_state *plane_state = + __drm_atomic_get_current_plane_state(state->state, + plane);
dlist_count += vc4_plane_dlist_size(plane_state); } diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 37cac59401d7..c799baabc008 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -469,7 +469,7 @@ int vc4_kms_load(struct drm_device *dev); struct drm_plane *vc4_plane_init(struct drm_device *dev, enum drm_plane_type type); u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); -u32 vc4_plane_dlist_size(struct drm_plane_state *state); +u32 vc4_plane_dlist_size(const struct drm_plane_state *state); void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb);
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 4037b52fde31..5d2c3d9fd17a 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -690,9 +690,10 @@ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) return vc4_state->dlist_count; }
-u32 vc4_plane_dlist_size(struct drm_plane_state *state) +u32 vc4_plane_dlist_size(const struct drm_plane_state *state) { - struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + const struct vc4_plane_state *vc4_state = + container_of(state, typeof(*vc4_state), base);
return vc4_state->dlist_count; } diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 92c84e9ab09a..4e97186293be 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -109,6 +109,42 @@ drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, return state->connector_states[index]; }
+/** + * __drm_atomic_get_current_plane_state - get current plane state + * @state: global atomic state object + * @plane: plane to grab + * + * This function returns the plane state for the given plane, either from + * @state, or if the plane isn't part of the atomic state update, from @plane. + * This is useful in atomic check callbacks, when drivers need to peek at, but + * not change, state of other planes, since it avoids threading an error code + * back up the call chain. + * + * WARNING: + * + * Note that this function is in general unsafe since it doesn't check for the + * required locking for access state structures. Drivers must ensure that it is + * save to access the returned state structure through other means. One commone + * example is when planes are fixed to a single CRTC, and the driver knows that + * the CRTC locks is held already. In that case holding the CRTC locks gives a + * read-lock on all planes connected to that CRTC. But if planes can be + * reassigned things get more tricky. In that case it's better to use + * drm_atomic_get_plane_state and wire up full error handling. + * + * Returns: + * + * Read-only pointer to the current plane state. + */ +static inline const struct drm_plane_state * +__drm_atomic_get_current_plane_state(struct drm_atomic_state *state, + struct drm_plane *plane) +{ + if (state->plane_states[drm_plane_index(plane)]) + return state->plane_states[drm_plane_index(plane)]; + + return plane->state; +} + int __must_check drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, struct drm_display_mode *mode);
Op 29-05-16 om 20:35 schreef Daniel Vetter:
... and use it in msm&vc4. Again just want to encapsulate drm_atomic_state internals a bit.
The const threading is a bit awkward in vc4 since C sucks, but I still think it's worth to enforce this. Eventually I want to make all the obj->state pointers const too, but that's a lot more work ...
Cc: Eric Anholt eric@anholt.net Cc: Rob Clark robdclark@gmail.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 10 +++------ drivers/gpu/drm/vc4/vc4_crtc.c | 11 +++------- drivers/gpu/drm/vc4/vc4_drv.h | 2 +- drivers/gpu/drm/vc4/vc4_plane.c | 5 +++-- include/drm/drm_atomic.h | 36 ++++++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 88fe256c1931..6d4086ee0503 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -383,19 +383,15 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, */ hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); drm_atomic_crtc_state_for_each_plane(plane, state) {
struct drm_plane_state *pstate;
if (cnt >= (hw_cfg->lm.nb_stages)) { dev_err(dev->dev, "too many planes!\n"); return -EINVAL; }const struct drm_plane_state *pstate;
pstate = state->state->plane_states[drm_plane_index(plane)];
pstate = __drm_atomic_get_current_plane_state(state->state,
plane);
/* plane might not have changed, in which case take
* current state:
*/
if (!pstate)
pstates[cnt].plane = plane; pstates[cnt].state = to_mdp5_plane_state(pstate);pstate = plane->state;
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 904d0754ad78..703bda170105 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -405,14 +405,9 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, return -EINVAL;
drm_atomic_crtc_state_for_each_plane(plane, state) {
struct drm_plane_state *plane_state =
state->state->plane_states[drm_plane_index(plane)];
/* plane might not have changed, in which case take
* current state:
*/
if (!plane_state)
plane_state = plane->state;
const struct drm_plane_state *plane_state =
__drm_atomic_get_current_plane_state(state->state,
plane);
dlist_count += vc4_plane_dlist_size(plane_state); }
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 37cac59401d7..c799baabc008 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -469,7 +469,7 @@ int vc4_kms_load(struct drm_device *dev); struct drm_plane *vc4_plane_init(struct drm_device *dev, enum drm_plane_type type); u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); -u32 vc4_plane_dlist_size(struct drm_plane_state *state); +u32 vc4_plane_dlist_size(const struct drm_plane_state *state); void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb);
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 4037b52fde31..5d2c3d9fd17a 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -690,9 +690,10 @@ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) return vc4_state->dlist_count; }
-u32 vc4_plane_dlist_size(struct drm_plane_state *state) +u32 vc4_plane_dlist_size(const struct drm_plane_state *state) {
- struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
const struct vc4_plane_state *vc4_state =
container_of(state, typeof(*vc4_state), base);
return vc4_state->dlist_count;
} diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 92c84e9ab09a..4e97186293be 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -109,6 +109,42 @@ drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, return state->connector_states[index]; }
+/**
- __drm_atomic_get_current_plane_state - get current plane state
- @state: global atomic state object
- @plane: plane to grab
- This function returns the plane state for the given plane, either from
- @state, or if the plane isn't part of the atomic state update, from @plane.
- This is useful in atomic check callbacks, when drivers need to peek at, but
- not change, state of other planes, since it avoids threading an error code
- back up the call chain.
- WARNING:
- Note that this function is in general unsafe since it doesn't check for the
- required locking for access state structures. Drivers must ensure that it is
- save to access the returned state structure through other means. One commone
- example is when planes are fixed to a single CRTC, and the driver knows that
- the CRTC locks is held already. In that case holding the CRTC locks gives a
- read-lock on all planes connected to that CRTC. But if planes can be
- reassigned things get more tricky. In that case it's better to use
- drm_atomic_get_plane_state and wire up full error handling.
- Returns:
- Read-only pointer to the current plane state.
- */
+static inline const struct drm_plane_state * +__drm_atomic_get_current_plane_state(struct drm_atomic_state *state,
struct drm_plane *plane)
+{
- if (state->plane_states[drm_plane_index(plane)])
return state->plane_states[drm_plane_index(plane)];
Could use a debug WARN_ON here. Just in case someone tries to use this for a plane that can't be safely checked:
WARN_ON(!plane->state->crtc || !drm_modeset_is_locked(&plane->state->crtc->mutex));
Also use drm_atomic_get_existing_plane_state?
~Maarten
On Mon, May 30, 2016 at 01:42:40PM +0200, Maarten Lankhorst wrote:
Op 29-05-16 om 20:35 schreef Daniel Vetter:
... and use it in msm&vc4. Again just want to encapsulate drm_atomic_state internals a bit.
The const threading is a bit awkward in vc4 since C sucks, but I still think it's worth to enforce this. Eventually I want to make all the obj->state pointers const too, but that's a lot more work ...
Cc: Eric Anholt eric@anholt.net Cc: Rob Clark robdclark@gmail.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 10 +++------ drivers/gpu/drm/vc4/vc4_crtc.c | 11 +++------- drivers/gpu/drm/vc4/vc4_drv.h | 2 +- drivers/gpu/drm/vc4/vc4_plane.c | 5 +++-- include/drm/drm_atomic.h | 36 ++++++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 88fe256c1931..6d4086ee0503 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -383,19 +383,15 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, */ hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); drm_atomic_crtc_state_for_each_plane(plane, state) {
struct drm_plane_state *pstate;
if (cnt >= (hw_cfg->lm.nb_stages)) { dev_err(dev->dev, "too many planes!\n"); return -EINVAL; }const struct drm_plane_state *pstate;
pstate = state->state->plane_states[drm_plane_index(plane)];
pstate = __drm_atomic_get_current_plane_state(state->state,
plane);
/* plane might not have changed, in which case take
* current state:
*/
if (!pstate)
pstates[cnt].plane = plane; pstates[cnt].state = to_mdp5_plane_state(pstate);pstate = plane->state;
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 904d0754ad78..703bda170105 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -405,14 +405,9 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, return -EINVAL;
drm_atomic_crtc_state_for_each_plane(plane, state) {
struct drm_plane_state *plane_state =
state->state->plane_states[drm_plane_index(plane)];
/* plane might not have changed, in which case take
* current state:
*/
if (!plane_state)
plane_state = plane->state;
const struct drm_plane_state *plane_state =
__drm_atomic_get_current_plane_state(state->state,
plane);
dlist_count += vc4_plane_dlist_size(plane_state); }
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 37cac59401d7..c799baabc008 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -469,7 +469,7 @@ int vc4_kms_load(struct drm_device *dev); struct drm_plane *vc4_plane_init(struct drm_device *dev, enum drm_plane_type type); u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); -u32 vc4_plane_dlist_size(struct drm_plane_state *state); +u32 vc4_plane_dlist_size(const struct drm_plane_state *state); void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb);
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 4037b52fde31..5d2c3d9fd17a 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -690,9 +690,10 @@ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) return vc4_state->dlist_count; }
-u32 vc4_plane_dlist_size(struct drm_plane_state *state) +u32 vc4_plane_dlist_size(const struct drm_plane_state *state) {
- struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
const struct vc4_plane_state *vc4_state =
container_of(state, typeof(*vc4_state), base);
return vc4_state->dlist_count;
} diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 92c84e9ab09a..4e97186293be 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -109,6 +109,42 @@ drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, return state->connector_states[index]; }
+/**
- __drm_atomic_get_current_plane_state - get current plane state
- @state: global atomic state object
- @plane: plane to grab
- This function returns the plane state for the given plane, either from
- @state, or if the plane isn't part of the atomic state update, from @plane.
- This is useful in atomic check callbacks, when drivers need to peek at, but
- not change, state of other planes, since it avoids threading an error code
- back up the call chain.
- WARNING:
- Note that this function is in general unsafe since it doesn't check for the
- required locking for access state structures. Drivers must ensure that it is
- save to access the returned state structure through other means. One commone
- example is when planes are fixed to a single CRTC, and the driver knows that
- the CRTC locks is held already. In that case holding the CRTC locks gives a
- read-lock on all planes connected to that CRTC. But if planes can be
- reassigned things get more tricky. In that case it's better to use
- drm_atomic_get_plane_state and wire up full error handling.
- Returns:
- Read-only pointer to the current plane state.
- */
+static inline const struct drm_plane_state * +__drm_atomic_get_current_plane_state(struct drm_atomic_state *state,
struct drm_plane *plane)
+{
- if (state->plane_states[drm_plane_index(plane)])
return state->plane_states[drm_plane_index(plane)];
Could use a debug WARN_ON here. Just in case someone tries to use this for a plane that can't be safely checked:
WARN_ON(!plane->state->crtc || !drm_modeset_is_locked(&plane->state->crtc->mutex));
If you have hw like i915, where you can't move planes, this check isn't good enough, because plane->state->crtc might be NULL, but if you hold the right crtc mutex it's still perfectly safe. That's why I didn't add that check, and instead typed the really long warning.
Also use drm_atomic_get_existing_plane_state?
Hm, feels like overkill since this is a core atomic function. We don't use get_existing_* in drm_atomic.c much either. -Daniel
Op 30-05-16 om 17:05 schreef Daniel Vetter:
On Mon, May 30, 2016 at 01:42:40PM +0200, Maarten Lankhorst wrote:
Op 29-05-16 om 20:35 schreef Daniel Vetter:
... and use it in msm&vc4. Again just want to encapsulate drm_atomic_state internals a bit.
The const threading is a bit awkward in vc4 since C sucks, but I still think it's worth to enforce this. Eventually I want to make all the obj->state pointers const too, but that's a lot more work ...
Cc: Eric Anholt eric@anholt.net Cc: Rob Clark robdclark@gmail.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 10 +++------ drivers/gpu/drm/vc4/vc4_crtc.c | 11 +++------- drivers/gpu/drm/vc4/vc4_drv.h | 2 +- drivers/gpu/drm/vc4/vc4_plane.c | 5 +++-- include/drm/drm_atomic.h | 36 ++++++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 88fe256c1931..6d4086ee0503 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -383,19 +383,15 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc, */ hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg); drm_atomic_crtc_state_for_each_plane(plane, state) {
struct drm_plane_state *pstate;
if (cnt >= (hw_cfg->lm.nb_stages)) { dev_err(dev->dev, "too many planes!\n"); return -EINVAL; }const struct drm_plane_state *pstate;
pstate = state->state->plane_states[drm_plane_index(plane)];
pstate = __drm_atomic_get_current_plane_state(state->state,
plane);
/* plane might not have changed, in which case take
* current state:
*/
if (!pstate)
pstates[cnt].plane = plane; pstates[cnt].state = to_mdp5_plane_state(pstate);pstate = plane->state;
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 904d0754ad78..703bda170105 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -405,14 +405,9 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, return -EINVAL;
drm_atomic_crtc_state_for_each_plane(plane, state) {
struct drm_plane_state *plane_state =
state->state->plane_states[drm_plane_index(plane)];
/* plane might not have changed, in which case take
* current state:
*/
if (!plane_state)
plane_state = plane->state;
const struct drm_plane_state *plane_state =
__drm_atomic_get_current_plane_state(state->state,
plane);
dlist_count += vc4_plane_dlist_size(plane_state); }
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 37cac59401d7..c799baabc008 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -469,7 +469,7 @@ int vc4_kms_load(struct drm_device *dev); struct drm_plane *vc4_plane_init(struct drm_device *dev, enum drm_plane_type type); u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); -u32 vc4_plane_dlist_size(struct drm_plane_state *state); +u32 vc4_plane_dlist_size(const struct drm_plane_state *state); void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb);
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 4037b52fde31..5d2c3d9fd17a 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -690,9 +690,10 @@ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) return vc4_state->dlist_count; }
-u32 vc4_plane_dlist_size(struct drm_plane_state *state) +u32 vc4_plane_dlist_size(const struct drm_plane_state *state) {
- struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
const struct vc4_plane_state *vc4_state =
container_of(state, typeof(*vc4_state), base);
return vc4_state->dlist_count;
} diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 92c84e9ab09a..4e97186293be 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -109,6 +109,42 @@ drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, return state->connector_states[index]; }
+/**
- __drm_atomic_get_current_plane_state - get current plane state
- @state: global atomic state object
- @plane: plane to grab
- This function returns the plane state for the given plane, either from
- @state, or if the plane isn't part of the atomic state update, from @plane.
- This is useful in atomic check callbacks, when drivers need to peek at, but
- not change, state of other planes, since it avoids threading an error code
- back up the call chain.
- WARNING:
- Note that this function is in general unsafe since it doesn't check for the
- required locking for access state structures. Drivers must ensure that it is
- save to access the returned state structure through other means. One commone
- example is when planes are fixed to a single CRTC, and the driver knows that
- the CRTC locks is held already. In that case holding the CRTC locks gives a
- read-lock on all planes connected to that CRTC. But if planes can be
- reassigned things get more tricky. In that case it's better to use
- drm_atomic_get_plane_state and wire up full error handling.
- Returns:
- Read-only pointer to the current plane state.
- */
+static inline const struct drm_plane_state * +__drm_atomic_get_current_plane_state(struct drm_atomic_state *state,
struct drm_plane *plane)
+{
- if (state->plane_states[drm_plane_index(plane)])
return state->plane_states[drm_plane_index(plane)];
Could use a debug WARN_ON here. Just in case someone tries to use this for a plane that can't be safely checked:
WARN_ON(!plane->state->crtc || !drm_modeset_is_locked(&plane->state->crtc->mutex));
If you have hw like i915, where you can't move planes, this check isn't good enough, because plane->state->crtc might be NULL, but if you hold the right crtc mutex it's still perfectly safe. That's why I didn't add that check, and instead typed the really long warning.
Wrong! If crtc == NULL, you can set for example a different source rectangle, and plane->state would change from underneath you since you don't hold the plane mutex.
Also use drm_atomic_get_existing_plane_state?
Hm, feels like overkill since this is a core atomic function. We don't use get_existing_* in drm_atomic.c much either.
Ok, but that's an oversight..
Looking at drm_atomic.c it seems to me that drm_atomic_state_default_clear should use for_each_xx_in too..
drm_atomic_get_{crtc,plane}_state uses get_existing_state, drm_atomic_get_connector_state does not, since it duplicated some checks in drm_atomic_get_connector_state. It is probably worth it to convert drm_atomic_get_connector_state too.
Apart from that I don't see what you mean..
~Maarten
We want to hide drm_atomic_state internals better.
Cc: Inki Dae inki.dae@samsung.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/exynos/exynos_drm_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 2dd820e23b0c..cabc5fd0246d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -267,6 +267,8 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, { struct exynos_drm_private *priv = dev->dev_private; struct exynos_atomic_commit *commit; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; int i, ret;
commit = kzalloc(sizeof(*commit), GFP_KERNEL); @@ -288,10 +290,8 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, /* Wait until all affected CRTCs have completed previous commits and * mark them as pending. */ - for (i = 0; i < dev->mode_config.num_crtc; ++i) { - if (state->crtcs[i]) - commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]); - } + for_each_crtc_in_state(state, crtc, crtc_state, i) + commit->crtcs |= 1 << drm_crtc_index(crtc);
wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs));
2016-05-30 3:35 GMT+09:00 Daniel Vetter daniel.vetter@ffwll.ch:
We want to hide drm_atomic_state internals better.
Acked-by: Inki Dae inki.dae@samsung.com
Cc: Inki Dae inki.dae@samsung.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/exynos/exynos_drm_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 2dd820e23b0c..cabc5fd0246d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -267,6 +267,8 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, { struct exynos_drm_private *priv = dev->dev_private; struct exynos_atomic_commit *commit;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state; int i, ret; commit = kzalloc(sizeof(*commit), GFP_KERNEL);
@@ -288,10 +290,8 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, /* Wait until all affected CRTCs have completed previous commits and * mark them as pending. */
for (i = 0; i < dev->mode_config.num_crtc; ++i) {
if (state->crtcs[i])
commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]);
}
for_each_crtc_in_state(state, crtc, crtc_state, i)
commit->crtcs |= 1 << drm_crtc_index(crtc); wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs));
-- 2.8.1
dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
2016년 05월 30일 03:35에 Daniel Vetter 이(가) 쓴 글:
We want to hide drm_atomic_state internals better.
Acked-by: Inki Dae inki.dae@samsung.com
Thanks, Inki Dae
Cc: Inki Dae inki.dae@samsung.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/exynos/exynos_drm_drv.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 2dd820e23b0c..cabc5fd0246d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -267,6 +267,8 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, { struct exynos_drm_private *priv = dev->dev_private; struct exynos_atomic_commit *commit;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state; int i, ret;
commit = kzalloc(sizeof(*commit), GFP_KERNEL);
@@ -288,10 +290,8 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, /* Wait until all affected CRTCs have completed previous commits and * mark them as pending. */
- for (i = 0; i < dev->mode_config.num_crtc; ++i) {
if (state->crtcs[i])
commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]);
- }
for_each_crtc_in_state(state, crtc, crtc_state, i)
commit->crtcs |= 1 << drm_crtc_index(crtc);
wait_event(priv->wait, !commit_is_pending(priv, commit->crtcs));
It's kinda pointless to have 2 separate mallocs for these. And when we add more per-connector state in the future it's even more pointless.
Right now there's no such thing planned, but both Gustavo's per-crtc fence patches, and some nonblocking commit helpers I'm playing around with will add more per-crtc stuff. It makes sense to also consolidate connectors, just for consistency.
In the future we can use this to store a pointer to the preceeding state, making an atomic update entirely free-standing. This will be needed to be able to queue them up with a depth > 1.
Cc: Gustavo Padovan gustavo@padovan.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_atomic.c | 27 +++++++++------------------ drivers/gpu/drm/drm_atomic_helper.c | 2 +- include/drm/drm_atomic.h | 10 +++++----- include/drm/drm_crtc.h | 11 +++++++---- 4 files changed, 22 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 3ff1ed7b33db..a6395e9654af 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -44,7 +44,6 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) { kfree(state->connectors); - kfree(state->connector_states); kfree(state->crtcs); kfree(state->crtc_states); kfree(state->planes); @@ -139,15 +138,15 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state);
for (i = 0; i < state->num_connector; i++) { - struct drm_connector *connector = state->connectors[i]; + struct drm_connector *connector = state->connectors[i].ptr;
if (!connector) continue;
connector->funcs->atomic_destroy_state(connector, - state->connector_states[i]); - state->connectors[i] = NULL; - state->connector_states[i] = NULL; + state->connectors[i].state); + state->connectors[i].ptr = NULL; + state->connectors[i].state = NULL; drm_connector_unreference(connector); }
@@ -896,8 +895,7 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, index = drm_connector_index(connector);
if (index >= state->num_connector) { - struct drm_connector **c; - struct drm_connector_state **cs; + struct __drm_connnectors_state *c; int alloc = max(index + 1, config->num_connector);
c = krealloc(state->connectors, alloc * sizeof(*state->connectors), GFP_KERNEL); @@ -908,26 +906,19 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, memset(&state->connectors[state->num_connector], 0, sizeof(*state->connectors) * (alloc - state->num_connector));
- cs = krealloc(state->connector_states, alloc * sizeof(*state->connector_states), GFP_KERNEL); - if (!cs) - return ERR_PTR(-ENOMEM); - - state->connector_states = cs; - memset(&state->connector_states[state->num_connector], 0, - sizeof(*state->connector_states) * (alloc - state->num_connector)); state->num_connector = alloc; }
- if (state->connector_states[index]) - return state->connector_states[index]; + if (state->connectors[index].state) + return state->connectors[index].state;
connector_state = connector->funcs->atomic_duplicate_state(connector); if (!connector_state) return ERR_PTR(-ENOMEM);
drm_connector_reference(connector); - state->connector_states[index] = connector_state; - state->connectors[index] = connector; + state->connectors[index].state = connector_state; + state->connectors[index].ptr = connector; connector_state->state = state;
DRM_DEBUG_ATOMIC("Added [CONNECTOR:%d] %p state to %p\n", diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 464541c87c40..0fb4868fdad6 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1567,7 +1567,7 @@ void drm_atomic_helper_swap_state(struct drm_device *dev,
for_each_connector_in_state(state, connector, conn_state, i) { connector->state->state = state; - swap(state->connector_states[i], connector->state); + swap(state->connectors[i].state, connector->state); connector->state->state = NULL; }
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 4e97186293be..37478adb6a16 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -106,7 +106,7 @@ drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, if (index >= state->num_connector) return NULL;
- return state->connector_states[index]; + return state->connectors[index].state; }
/** @@ -175,11 +175,11 @@ 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);
-#define for_each_connector_in_state(state, connector, connector_state, __i) \ +#define for_each_connector_in_state(__state, connector, connector_state, __i) \ for ((__i) = 0; \ - (__i) < (state)->num_connector && \ - ((connector) = (state)->connectors[__i], \ - (connector_state) = (state)->connector_states[__i], 1); \ + (__i) < (__state)->num_connector && \ + ((connector) = (__state)->connectors[__i].ptr, \ + (connector_state) = (__state)->connectors[__i].state, 1); \ (__i)++) \ for_each_if (connector)
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 5bffb5c86ea8..d4c46bfe4142 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1694,6 +1694,11 @@ struct drm_bridge { void *driver_private; };
+struct __drm_connnectors_state { + struct drm_connector *ptr; + struct drm_connector_state *state; +}; + /** * struct drm_atomic_state - the global state object for atomic updates * @dev: parent DRM device @@ -1705,8 +1710,7 @@ struct drm_bridge { * @crtcs: pointer to array of CRTC pointers * @crtc_states: pointer to array of CRTC states pointers * @num_connector: size of the @connectors and @connector_states arrays - * @connectors: pointer to array of connector pointers - * @connector_states: pointer to array of connector states pointers + * @connectors: pointer to array of structures with per-connector data * @acquire_ctx: acquire context for this atomic modeset state update */ struct drm_atomic_state { @@ -1719,8 +1723,7 @@ struct drm_atomic_state { struct drm_crtc **crtcs; struct drm_crtc_state **crtc_states; int num_connector; - struct drm_connector **connectors; - struct drm_connector_state **connector_states; + struct __drm_connnectors_state *connectors;
struct drm_modeset_acquire_ctx *acquire_ctx; };
On Sun, May 29, 2016 at 08:35:05PM +0200, Daniel Vetter wrote:
It's kinda pointless to have 2 separate mallocs for these. And when we add more per-connector state in the future it's even more pointless.
Right now there's no such thing planned, but both Gustavo's per-crtc fence patches, and some nonblocking commit helpers I'm playing around with will add more per-crtc stuff. It makes sense to also consolidate connectors, just for consistency.
In the future we can use this to store a pointer to the preceeding state, making an atomic update entirely free-standing. This will be needed to be able to queue them up with a depth > 1.
Cc: Gustavo Padovan gustavo@padovan.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/drm_atomic.c | 27 +++++++++------------------ drivers/gpu/drm/drm_atomic_helper.c | 2 +- include/drm/drm_atomic.h | 10 +++++----- include/drm/drm_crtc.h | 11 +++++++---- 4 files changed, 22 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 3ff1ed7b33db..a6395e9654af 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -44,7 +44,6 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) { kfree(state->connectors);
- kfree(state->connector_states); kfree(state->crtcs); kfree(state->crtc_states); kfree(state->planes);
@@ -139,15 +138,15 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state);
for (i = 0; i < state->num_connector; i++) {
struct drm_connector *connector = state->connectors[i];
struct drm_connector *connector = state->connectors[i].ptr;
'ptr' is not a very good name.
On Mon, May 30, 2016 at 05:59:24PM +0300, Ville Syrjälä wrote:
On Sun, May 29, 2016 at 08:35:05PM +0200, Daniel Vetter wrote:
It's kinda pointless to have 2 separate mallocs for these. And when we add more per-connector state in the future it's even more pointless.
Right now there's no such thing planned, but both Gustavo's per-crtc fence patches, and some nonblocking commit helpers I'm playing around with will add more per-crtc stuff. It makes sense to also consolidate connectors, just for consistency.
In the future we can use this to store a pointer to the preceeding state, making an atomic update entirely free-standing. This will be needed to be able to queue them up with a depth > 1.
Cc: Gustavo Padovan gustavo@padovan.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/drm_atomic.c | 27 +++++++++------------------ drivers/gpu/drm/drm_atomic_helper.c | 2 +- include/drm/drm_atomic.h | 10 +++++----- include/drm/drm_crtc.h | 11 +++++++---- 4 files changed, 22 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 3ff1ed7b33db..a6395e9654af 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -44,7 +44,6 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) { kfree(state->connectors);
- kfree(state->connector_states); kfree(state->crtcs); kfree(state->crtc_states); kfree(state->planes);
@@ -139,15 +138,15 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state);
for (i = 0; i < state->num_connector; i++) {
struct drm_connector *connector = state->connectors[i];
struct drm_connector *connector = state->connectors[i].ptr;
'ptr' is not a very good name.
Suggestions for something better? I was lacking good inspiration ... -Daniel
On Mon, May 30, 2016 at 05:13:56PM +0200, Daniel Vetter wrote:
On Mon, May 30, 2016 at 05:59:24PM +0300, Ville Syrjälä wrote:
On Sun, May 29, 2016 at 08:35:05PM +0200, Daniel Vetter wrote:
It's kinda pointless to have 2 separate mallocs for these. And when we add more per-connector state in the future it's even more pointless.
Right now there's no such thing planned, but both Gustavo's per-crtc fence patches, and some nonblocking commit helpers I'm playing around with will add more per-crtc stuff. It makes sense to also consolidate connectors, just for consistency.
In the future we can use this to store a pointer to the preceeding state, making an atomic update entirely free-standing. This will be needed to be able to queue them up with a depth > 1.
Cc: Gustavo Padovan gustavo@padovan.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/drm_atomic.c | 27 +++++++++------------------ drivers/gpu/drm/drm_atomic_helper.c | 2 +- include/drm/drm_atomic.h | 10 +++++----- include/drm/drm_crtc.h | 11 +++++++---- 4 files changed, 22 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 3ff1ed7b33db..a6395e9654af 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -44,7 +44,6 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) { kfree(state->connectors);
- kfree(state->connector_states); kfree(state->crtcs); kfree(state->crtc_states); kfree(state->planes);
@@ -139,15 +138,15 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state);
for (i = 0; i < state->num_connector; i++) {
struct drm_connector *connector = state->connectors[i];
struct drm_connector *connector = state->connectors[i].ptr;
'ptr' is not a very good name.
Suggestions for something better? I was lacking good inspiration ...
Maybe just 'connector' ?
On Mon, May 30, 2016 at 06:25:50PM +0300, Ville Syrjälä wrote:
On Mon, May 30, 2016 at 05:13:56PM +0200, Daniel Vetter wrote:
On Mon, May 30, 2016 at 05:59:24PM +0300, Ville Syrjälä wrote:
On Sun, May 29, 2016 at 08:35:05PM +0200, Daniel Vetter wrote:
It's kinda pointless to have 2 separate mallocs for these. And when we add more per-connector state in the future it's even more pointless.
Right now there's no such thing planned, but both Gustavo's per-crtc fence patches, and some nonblocking commit helpers I'm playing around with will add more per-crtc stuff. It makes sense to also consolidate connectors, just for consistency.
In the future we can use this to store a pointer to the preceeding state, making an atomic update entirely free-standing. This will be needed to be able to queue them up with a depth > 1.
Cc: Gustavo Padovan gustavo@padovan.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/drm_atomic.c | 27 +++++++++------------------ drivers/gpu/drm/drm_atomic_helper.c | 2 +- include/drm/drm_atomic.h | 10 +++++----- include/drm/drm_crtc.h | 11 +++++++---- 4 files changed, 22 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 3ff1ed7b33db..a6395e9654af 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -44,7 +44,6 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) { kfree(state->connectors);
- kfree(state->connector_states); kfree(state->crtcs); kfree(state->crtc_states); kfree(state->planes);
@@ -139,15 +138,15 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state);
for (i = 0; i < state->num_connector; i++) {
struct drm_connector *connector = state->connectors[i];
struct drm_connector *connector = state->connectors[i].ptr;
'ptr' is not a very good name.
Suggestions for something better? I was lacking good inspiration ...
Maybe just 'connector' ?
state->connectors[i].connector is really long, and makes a lot of code look ugly. "obj" might be a bit better than "ptr" at least. Something else? -Daniel
On Mon, May 30, 2016 at 05:33:56PM +0200, Daniel Vetter wrote:
On Mon, May 30, 2016 at 06:25:50PM +0300, Ville Syrjälä wrote:
On Mon, May 30, 2016 at 05:13:56PM +0200, Daniel Vetter wrote:
On Mon, May 30, 2016 at 05:59:24PM +0300, Ville Syrjälä wrote:
On Sun, May 29, 2016 at 08:35:05PM +0200, Daniel Vetter wrote:
It's kinda pointless to have 2 separate mallocs for these. And when we add more per-connector state in the future it's even more pointless.
Right now there's no such thing planned, but both Gustavo's per-crtc fence patches, and some nonblocking commit helpers I'm playing around with will add more per-crtc stuff. It makes sense to also consolidate connectors, just for consistency.
In the future we can use this to store a pointer to the preceeding state, making an atomic update entirely free-standing. This will be needed to be able to queue them up with a depth > 1.
Cc: Gustavo Padovan gustavo@padovan.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
drivers/gpu/drm/drm_atomic.c | 27 +++++++++------------------ drivers/gpu/drm/drm_atomic_helper.c | 2 +- include/drm/drm_atomic.h | 10 +++++----- include/drm/drm_crtc.h | 11 +++++++---- 4 files changed, 22 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 3ff1ed7b33db..a6395e9654af 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -44,7 +44,6 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) { kfree(state->connectors);
- kfree(state->connector_states); kfree(state->crtcs); kfree(state->crtc_states); kfree(state->planes);
@@ -139,15 +138,15 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) DRM_DEBUG_ATOMIC("Clearing atomic state %p\n", state);
for (i = 0; i < state->num_connector; i++) {
struct drm_connector *connector = state->connectors[i];
struct drm_connector *connector = state->connectors[i].ptr;
'ptr' is not a very good name.
Suggestions for something better? I was lacking good inspiration ...
Maybe just 'connector' ?
state->connectors[i].connector is really long, and makes a lot of code look ugly. "obj" might be a bit better than "ptr" at least. Something else?
How often are you expecting to have to type this anyway? Using any kind of generic name here will make life harder for cscope users. Atomic is really bad in this regard, escecially with all the identically named function pointers. It's seriosuly hard to navigate that maze these days. Someone should do a bit of renaming of stuff to make things more unique.
On Mon, May 30, 2016 at 5:45 PM, Ville Syrjälä ville.syrjala@linux.intel.com wrote:
state->connectors[i].connector is really long, and makes a lot of code look ugly. "obj" might be a bit better than "ptr" at least. Something else?
How often are you expecting to have to type this anyway? Using any kind of generic name here will make life harder for cscope users. Atomic is really bad in this regard, escecially with all the identically named function pointers. It's seriosuly hard to navigate that maze these days. Someone should do a bit of renaming of stuff to make things more unique.
We have the same aliasing with legacy hooks shared between encoder/bridge&crtc. I just always search for the containing structure since indeed there's no other way to find stuff. Plus big screens ;-) -Daniel
It's kinda pointless to have 2 separate mallocs for these. And when we add more per-plane state in the future it's even more pointless.
Right now there's no such thing planned, but both Gustavo's per-crtc fence patches, and some nonblocking commit helpers I'm playing around with will add more per-crtc stuff. It makes sense to also consolidate planes, just for consistency.
In the future we can use this to store a pointer to the preceeding state, making an atomic update entirely free-standing. This will be needed to be able to queue them up with a depth > 1.
Cc: Gustavo Padovan gustavo@padovan.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_atomic.c | 17 ++++++----------- drivers/gpu/drm/drm_atomic_helper.c | 2 +- drivers/gpu/drm/i915/intel_atomic.c | 2 +- include/drm/drm_atomic.h | 14 +++++++------- include/drm/drm_crtc.h | 11 +++++++---- 5 files changed, 22 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index a6395e9654af..68fd99d2fd01 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -47,7 +47,6 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) kfree(state->crtcs); kfree(state->crtc_states); kfree(state->planes); - kfree(state->plane_states); } EXPORT_SYMBOL(drm_atomic_state_default_release);
@@ -79,10 +78,6 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) sizeof(*state->planes), GFP_KERNEL); if (!state->planes) goto fail; - state->plane_states = kcalloc(dev->mode_config.num_total_plane, - sizeof(*state->plane_states), GFP_KERNEL); - if (!state->plane_states) - goto fail;
state->dev = dev;
@@ -163,15 +158,15 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) }
for (i = 0; i < config->num_total_plane; i++) { - struct drm_plane *plane = state->planes[i]; + struct drm_plane *plane = state->planes[i].ptr;
if (!plane) continue;
plane->funcs->atomic_destroy_state(plane, - state->plane_states[i]); - state->planes[i] = NULL; - state->plane_states[i] = NULL; + state->planes[i].state); + state->planes[i].ptr = NULL; + state->planes[i].state = NULL; } } EXPORT_SYMBOL(drm_atomic_state_default_clear); @@ -630,8 +625,8 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, if (!plane_state) return ERR_PTR(-ENOMEM);
- state->plane_states[index] = plane_state; - state->planes[index] = plane; + state->planes[index].state = plane_state; + state->planes[index].ptr = plane; plane_state->state = state;
DRM_DEBUG_ATOMIC("Added [PLANE:%d:%s] %p state to %p\n", diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 0fb4868fdad6..f70d576f745e 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1579,7 +1579,7 @@ void drm_atomic_helper_swap_state(struct drm_device *dev,
for_each_plane_in_state(state, plane, plane_state, i) { plane->state->state = state; - swap(state->plane_states[i], plane->state); + swap(state->planes[i].state, plane->state); plane->state->state = NULL; } } diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 3e6d9ff8840a..7542f5f5db1d 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -191,7 +191,7 @@ int intel_atomic_setup_scalers(struct drm_device *dev,
/* plane scaler case: assign as a plane scaler */ /* find the plane that set the bit as scaler_user */ - plane = drm_state->planes[i]; + plane = drm_state->planes[i].ptr;
/* * to enable/disable hq mode, add planes that are using scaler diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 37478adb6a16..8e616d39353b 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -86,7 +86,7 @@ static inline struct drm_plane_state * drm_atomic_get_existing_plane_state(struct drm_atomic_state *state, struct drm_plane *plane) { - return state->plane_states[drm_plane_index(plane)]; + return state->planes[drm_plane_index(plane)].state; }
/** @@ -139,8 +139,8 @@ static inline const struct drm_plane_state * __drm_atomic_get_current_plane_state(struct drm_atomic_state *state, struct drm_plane *plane) { - if (state->plane_states[drm_plane_index(plane)]) - return state->plane_states[drm_plane_index(plane)]; + if (state->planes[drm_plane_index(plane)].state) + return state->planes[drm_plane_index(plane)].state;
return plane->state; } @@ -191,11 +191,11 @@ int __must_check drm_atomic_nonblocking_commit(struct drm_atomic_state *state); (__i)++) \ for_each_if (crtc_state)
-#define for_each_plane_in_state(state, plane, plane_state, __i) \ +#define for_each_plane_in_state(__state, plane, plane_state, __i) \ for ((__i) = 0; \ - (__i) < (state)->dev->mode_config.num_total_plane && \ - ((plane) = (state)->planes[__i], \ - (plane_state) = (state)->plane_states[__i], 1); \ + (__i) < (__state)->dev->mode_config.num_total_plane && \ + ((plane) = (__state)->planes[__i].ptr, \ + (plane_state) = (__state)->planes[__i].state, 1); \ (__i)++) \ for_each_if (plane_state) static inline bool diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index d4c46bfe4142..c7c2b3fa7179 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1694,6 +1694,11 @@ struct drm_bridge { void *driver_private; };
+struct __drm_planes_state { + struct drm_plane *ptr; + struct drm_plane_state *state; +}; + struct __drm_connnectors_state { struct drm_connector *ptr; struct drm_connector_state *state; @@ -1705,8 +1710,7 @@ struct __drm_connnectors_state { * @allow_modeset: allow full modeset * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics * @legacy_set_config: Disable conflicting encoders instead of failing with -EINVAL. - * @planes: pointer to array of plane pointers - * @plane_states: pointer to array of plane states pointers + * @planes: pointer to array of structures with per-plane data * @crtcs: pointer to array of CRTC pointers * @crtc_states: pointer to array of CRTC states pointers * @num_connector: size of the @connectors and @connector_states arrays @@ -1718,8 +1722,7 @@ struct drm_atomic_state { bool allow_modeset : 1; bool legacy_cursor_update : 1; bool legacy_set_config : 1; - struct drm_plane **planes; - struct drm_plane_state **plane_states; + struct __drm_planes_state *planes; struct drm_crtc **crtcs; struct drm_crtc_state **crtc_states; int num_connector;
It's silly to have 2 mallocs when we could tie these two together.
Also, Gustavo adds another one in his per-crtc out-fence patches. And I want to add more stuff here for nonblocking commit helpers.
In the future we can use this to store a pointer to the preceeding state, making an atomic update entirely free-standing. This will be needed to be able to queue them up with a depth > 1.
Cc: Gustavo Padovan gustavo@padovan.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_atomic.c | 17 ++++++----------- drivers/gpu/drm/drm_atomic_helper.c | 2 +- include/drm/drm_atomic.h | 10 +++++----- include/drm/drm_crtc.h | 8 ++++++-- 4 files changed, 18 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 68fd99d2fd01..674b2e490aa9 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -45,7 +45,6 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state) { kfree(state->connectors); kfree(state->crtcs); - kfree(state->crtc_states); kfree(state->planes); } EXPORT_SYMBOL(drm_atomic_state_default_release); @@ -70,10 +69,6 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state) sizeof(*state->crtcs), GFP_KERNEL); if (!state->crtcs) goto fail; - state->crtc_states = kcalloc(dev->mode_config.num_crtc, - sizeof(*state->crtc_states), GFP_KERNEL); - if (!state->crtc_states) - goto fail; state->planes = kcalloc(dev->mode_config.num_total_plane, sizeof(*state->planes), GFP_KERNEL); if (!state->planes) @@ -146,15 +141,15 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) }
for (i = 0; i < config->num_crtc; i++) { - struct drm_crtc *crtc = state->crtcs[i]; + struct drm_crtc *crtc = state->crtcs[i].ptr;
if (!crtc) continue;
crtc->funcs->atomic_destroy_state(crtc, - state->crtc_states[i]); - state->crtcs[i] = NULL; - state->crtc_states[i] = NULL; + state->crtcs[i].state); + state->crtcs[i].ptr = NULL; + state->crtcs[i].state = NULL; }
for (i = 0; i < config->num_total_plane; i++) { @@ -264,8 +259,8 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, if (!crtc_state) return ERR_PTR(-ENOMEM);
- state->crtc_states[index] = crtc_state; - state->crtcs[index] = crtc; + state->crtcs[index].state = crtc_state; + state->crtcs[index].ptr = crtc; crtc_state->state = state;
DRM_DEBUG_ATOMIC("Added [CRTC:%d:%s] %p state to %p\n", diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index f70d576f745e..5298eb668ca7 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1573,7 +1573,7 @@ void drm_atomic_helper_swap_state(struct drm_device *dev,
for_each_crtc_in_state(state, crtc, crtc_state, i) { crtc->state->state = state; - swap(state->crtc_states[i], crtc->state); + swap(state->crtcs[i].state, crtc->state); crtc->state->state = NULL; }
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 8e616d39353b..d9504dfcd1cc 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -71,7 +71,7 @@ static inline struct drm_crtc_state * drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state, struct drm_crtc *crtc) { - return state->crtc_states[drm_crtc_index(crtc)]; + return state->crtcs[drm_crtc_index(crtc)].state; }
/** @@ -183,11 +183,11 @@ int __must_check drm_atomic_nonblocking_commit(struct drm_atomic_state *state); (__i)++) \ for_each_if (connector)
-#define for_each_crtc_in_state(state, crtc, crtc_state, __i) \ +#define for_each_crtc_in_state(__state, crtc, crtc_state, __i) \ for ((__i) = 0; \ - (__i) < (state)->dev->mode_config.num_crtc && \ - ((crtc) = (state)->crtcs[__i], \ - (crtc_state) = (state)->crtc_states[__i], 1); \ + (__i) < (__state)->dev->mode_config.num_crtc && \ + ((crtc) = (__state)->crtcs[__i].ptr, \ + (crtc_state) = (__state)->crtcs[__i].state, 1); \ (__i)++) \ for_each_if (crtc_state)
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c7c2b3fa7179..d5d5e343531e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1699,6 +1699,11 @@ struct __drm_planes_state { struct drm_plane_state *state; };
+struct __drm_crtcs_state { + struct drm_crtc *ptr; + struct drm_crtc_state *state; +}; + struct __drm_connnectors_state { struct drm_connector *ptr; struct drm_connector_state *state; @@ -1723,8 +1728,7 @@ struct drm_atomic_state { bool legacy_cursor_update : 1; bool legacy_set_config : 1; struct __drm_planes_state *planes; - struct drm_crtc **crtcs; - struct drm_crtc_state **crtc_states; + struct __drm_crtcs_state *crtcs; int num_connector; struct __drm_connnectors_state *connectors;
From: Gustavo Padovan gustavo.padovan@collabora.co.uk
Now a drm_pending_event can either send a real drm_event or signal a fence, or both. It allow us to signal via fences when the buffer is displayed on the screen. Which in turn means that the previous buffer is not in use anymore and can be freed or sent back to another driver for processing.
v2: Comments from Daniel Vetter - call fence_signal in drm_send_event_locked() - remove unneeded !e->event check
v3: Remove drm_pending_event->destroy to fix a leak when e->file_priv is not set.
Signed-off-by: Gustavo Padovan gustavo.padovan@collabora.co.uk (v2) Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_atomic.c | 19 +++++++++++++------ drivers/gpu/drm/drm_fops.c | 16 +++++++++------- drivers/gpu/drm/nouveau/nouveau_usif.c | 1 - drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 2 +- include/drm/drmP.h | 3 ++- 5 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 674b2e490aa9..1db198df3014 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1412,7 +1412,8 @@ EXPORT_SYMBOL(drm_atomic_nonblocking_commit); */
static struct drm_pending_vblank_event *create_vblank_event( - struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data) + struct drm_device *dev, struct drm_file *file_priv, + struct fence *fence, uint64_t user_data) { struct drm_pending_vblank_event *e = NULL; int ret; @@ -1425,12 +1426,17 @@ static struct drm_pending_vblank_event *create_vblank_event( e->event.base.length = sizeof(e->event); e->event.user_data = user_data;
- ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base); - if (ret) { - kfree(e); - return NULL; + if (file_priv) { + ret = drm_event_reserve_init(dev, file_priv, &e->base, + &e->event.base); + if (ret) { + kfree(e); + return NULL; + } }
+ e->base.fence = fence; + return e; }
@@ -1670,7 +1676,8 @@ retry: for_each_crtc_in_state(state, crtc, crtc_state, i) { struct drm_pending_vblank_event *e;
- e = create_vblank_event(dev, file_priv, arg->user_data); + e = create_vblank_event(dev, file_priv, NULL, + arg->user_data); if (!e) { ret = -ENOMEM; goto out; diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 868871068956..4c4b30f7a9f2 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -294,7 +294,7 @@ static void drm_events_release(struct drm_file *file_priv) /* Remove unconsumed events */ list_for_each_entry_safe(e, et, &file_priv->event_list, link) { list_del(&e->link); - e->destroy(e); + kfree(e); }
spin_unlock_irqrestore(&dev->event_lock, flags); @@ -525,7 +525,7 @@ put_back_event: }
ret += length; - e->destroy(e); + kfree(e); } } mutex_unlock(&file_priv->event_read_lock); @@ -602,9 +602,6 @@ int drm_event_reserve_init_locked(struct drm_device *dev, list_add(&p->pending_link, &file_priv->pending_event_list); p->file_priv = file_priv;
- /* we *could* pass this in as arg, but everyone uses kfree: */ - p->destroy = (void (*) (struct drm_pending_event *)) kfree; - return 0; } EXPORT_SYMBOL(drm_event_reserve_init_locked); @@ -667,7 +664,7 @@ void drm_event_cancel_free(struct drm_device *dev, list_del(&p->pending_link); } spin_unlock_irqrestore(&dev->event_lock, flags); - p->destroy(p); + kfree(p); } EXPORT_SYMBOL(drm_event_cancel_free);
@@ -689,8 +686,13 @@ void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e) { assert_spin_locked(&dev->event_lock);
+ if (e->fence) { + fence_signal(e->fence); + fence_put(e->fence); + } + if (!e->file_priv) { - e->destroy(e); + kfree(e); return; }
diff --git a/drivers/gpu/drm/nouveau/nouveau_usif.c b/drivers/gpu/drm/nouveau/nouveau_usif.c index 675e9e077a95..08f9c6fa0f7f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_usif.c +++ b/drivers/gpu/drm/nouveau/nouveau_usif.c @@ -212,7 +212,6 @@ usif_notify_get(struct drm_file *f, void *data, u32 size, void *argv, u32 argc) ntfy->p->base.event = &ntfy->p->e.base; ntfy->p->base.file_priv = f; ntfy->p->base.pid = current->pid; - ntfy->p->base.destroy =(void(*)(struct drm_pending_event *))kfree; ntfy->p->e.base.type = DRM_NOUVEAU_EVENT_NVIF; ntfy->p->e.base.length = sizeof(ntfy->p->e.base) + ntfy->reply;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 1c4d5b5a70a2..5567fb43e674 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -889,7 +889,7 @@ static void vop_crtc_cancel_pending_vblank(struct drm_crtc *crtc, if (e && e->base.file_priv == file_priv) { vop->event = NULL;
- e->base.destroy(&e->base); + kfree(&e->base); file_priv->event_space += sizeof(e->event); } spin_unlock_irqrestore(&drm->event_lock, flags); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index f51fa4328494..51f751d1c8a4 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -57,6 +57,7 @@ #include <linux/types.h> #include <linux/vmalloc.h> #include <linux/workqueue.h> +#include <linux/fence.h>
#include <asm/mman.h> #include <asm/pgalloc.h> @@ -283,12 +284,12 @@ struct drm_ioctl_desc { /* Event queued up for userspace to read */ struct drm_pending_event { struct drm_event *event; + struct fence *fence; struct list_head link; struct list_head pending_link; struct drm_file *file_priv; pid_t pid; /* pid of requester, no guarantee it's valid by the time we deliver the event, for tracing only */ - void (*destroy)(struct drm_pending_event *event); };
/* initial implementaton using a linked list - todo hashtab */
- dev is redundant, we have state->atomic - add stall parameter, which must be set when swapping needs to stall for preceeding commits to stop looking at ->state pointers. Currently all drivers need this to be, just prep work for a glorious future.
Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch --- drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 2 +- drivers/gpu/drm/drm_atomic_helper.c | 8 ++++---- drivers/gpu/drm/exynos/exynos_drm_drv.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 2 +- drivers/gpu/drm/msm/msm_atomic.c | 2 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 2 +- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 2 +- drivers/gpu/drm/sti/sti_drv.c | 2 +- drivers/gpu/drm/tegra/drm.c | 2 +- drivers/gpu/drm/vc4/vc4_kms.c | 2 +- include/drm/drm_atomic_helper.h | 4 ++-- 13 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 6485fa5bee8b..9ecf16c7911d 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -519,7 +519,7 @@ static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev, }
/* Swap the state, this is the point of no return. */ - drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true);
if (async) queue_work(dc->wq, &commit->work); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 5298eb668ca7..fecbb52cbb85 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1160,7 +1160,7 @@ int drm_atomic_helper_commit(struct drm_device *dev, * the software side now. */
- drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true);
/* * Everything below can be run asynchronously without the need to grab @@ -1531,8 +1531,8 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
/** * drm_atomic_helper_swap_state - store atomic state into current sw state - * @dev: DRM device * @state: atomic state + * @stall: stall for proceeding commits * * This function stores the atomic state into the current state pointers in all * driver objects. It should be called after all failing steps have been done @@ -1554,8 +1554,8 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes); * 5. Call drm_atomic_helper_cleanup_planes() with @state, which since step 3 * contains the old state. Also do any other cleanup required with that state. */ -void drm_atomic_helper_swap_state(struct drm_device *dev, - struct drm_atomic_state *state) +void drm_atomic_helper_swap_state(struct drm_atomic_state *state, + bool stall) { int i; struct drm_connector *connector; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index cabc5fd0246d..deba76982358 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -299,7 +299,7 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, priv->pending |= commit->crtcs; spin_unlock(&priv->lock);
- drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true);
if (nonblock) schedule_work(&commit->work); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9ccd76699f48..d32274071393 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13660,7 +13660,7 @@ static int intel_atomic_commit(struct drm_device *dev, return ret; }
- drm_atomic_helper_swap_state(dev, state); + 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); diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 06a417b2f91e..c33bf98c5d5e 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -91,7 +91,7 @@ static int mtk_atomic_commit(struct drm_device *drm, mutex_lock(&private->commit.lock); flush_work(&private->commit.work);
- drm_atomic_helper_swap_state(drm, state); + drm_atomic_helper_swap_state(state, true);
if (async) mtk_atomic_schedule(private, state); diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 9c0e4261dbba..d02bd6a50e90 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -238,7 +238,7 @@ int msm_atomic_commit(struct drm_device *dev, * the software side now. */
- drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true);
/* * Everything below can be run asynchronously without the need to grab diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index d86f5479345b..5d8377aad639 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -175,7 +175,7 @@ static int omap_atomic_commit(struct drm_device *dev, spin_unlock(&priv->commit.lock);
/* Swap the state, this is the point of no return. */ - drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true);
if (nonblock) schedule_work(&commit->work); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index f315c55c1f65..3d04a506cf33 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -327,7 +327,7 @@ static int rcar_du_atomic_commit(struct drm_device *dev, }
/* Swap the state, this is the point of no return. */ - drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true);
if (nonblock) schedule_work(&commit->work); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 03913b483506..5ea141dfae5b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -290,7 +290,7 @@ int rockchip_drm_atomic_commit(struct drm_device *dev, mutex_lock(&commit->lock); flush_work(&commit->work);
- drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true);
commit->dev = dev; commit->state = state; diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index b440617a7019..dd2c400c4a46 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -215,7 +215,7 @@ static int sti_atomic_commit(struct drm_device *drm, * the software side now. */
- drm_atomic_helper_swap_state(drm, state); + drm_atomic_helper_swap_state(state, true);
if (nonblock) sti_atomic_schedule(private, state); diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index b59c3bf0df44..a177a42a9849 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -93,7 +93,7 @@ static int tegra_atomic_commit(struct drm_device *drm, * the software side now. */
- drm_atomic_helper_swap_state(drm, state); + drm_atomic_helper_swap_state(state, true);
if (nonblock) tegra_atomic_schedule(tegra, state); diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 39c0b2048bfd..8f4d5ffc32be 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -148,7 +148,7 @@ static int vc4_atomic_commit(struct drm_device *dev, * the software side now. */
- drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true);
/* * Everything below can be run asynchronously without the need to grab diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index d473dcc91f54..0276447225ed 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -71,8 +71,8 @@ void drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_sta void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, bool atomic);
-void drm_atomic_helper_swap_state(struct drm_device *dev, - struct drm_atomic_state *state); +void drm_atomic_helper_swap_state(struct drm_atomic_state *state, + bool stall);
/* implementations for legacy interfaces */ int drm_atomic_helper_update_plane(struct drm_plane *plane,
Op 29-05-16 om 20:35 schreef Daniel Vetter:
- dev is redundant, we have state->atomic
- add stall parameter, which must be set when swapping needs to stall for preceeding commits to stop looking at ->state pointers. Currently all drivers need this to be, just prep work for a glorious future.
I have to disagree, if you want to stall it should be done in a separate helper before swapping state. That function should also have the ability to fail, since we haven't called swap_state yet. :)
Maybe with nonblock as parameter, so it could fail with -EBUSY if it would stall, and -EINTR if interrupted?
Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 2 +- drivers/gpu/drm/drm_atomic_helper.c | 8 ++++---- drivers/gpu/drm/exynos/exynos_drm_drv.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 2 +- drivers/gpu/drm/msm/msm_atomic.c | 2 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 2 +- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 2 +- drivers/gpu/drm/sti/sti_drv.c | 2 +- drivers/gpu/drm/tegra/drm.c | 2 +- drivers/gpu/drm/vc4/vc4_kms.c | 2 +- include/drm/drm_atomic_helper.h | 4 ++-- 13 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 6485fa5bee8b..9ecf16c7911d 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -519,7 +519,7 @@ static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev, }
/* Swap the state, this is the point of no return. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
if (async) queue_work(dc->wq, &commit->work);
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 5298eb668ca7..fecbb52cbb85 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1160,7 +1160,7 @@ int drm_atomic_helper_commit(struct drm_device *dev, * the software side now. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
/*
- Everything below can be run asynchronously without the need to grab
@@ -1531,8 +1531,8 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
/**
- drm_atomic_helper_swap_state - store atomic state into current sw state
- @dev: DRM device
- @state: atomic state
- @stall: stall for proceeding commits
- This function stores the atomic state into the current state pointers in all
- driver objects. It should be called after all failing steps have been done
@@ -1554,8 +1554,8 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
- Call drm_atomic_helper_cleanup_planes() with @state, which since step 3
- contains the old state. Also do any other cleanup required with that state.
*/ -void drm_atomic_helper_swap_state(struct drm_device *dev,
struct drm_atomic_state *state)
+void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
bool stall)
{ int i; struct drm_connector *connector; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index cabc5fd0246d..deba76982358 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -299,7 +299,7 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, priv->pending |= commit->crtcs; spin_unlock(&priv->lock);
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
if (nonblock) schedule_work(&commit->work);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9ccd76699f48..d32274071393 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13660,7 +13660,7 @@ static int intel_atomic_commit(struct drm_device *dev, return ret; }
- drm_atomic_helper_swap_state(dev, state);
- 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);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 06a417b2f91e..c33bf98c5d5e 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -91,7 +91,7 @@ static int mtk_atomic_commit(struct drm_device *drm, mutex_lock(&private->commit.lock); flush_work(&private->commit.work);
- drm_atomic_helper_swap_state(drm, state);
drm_atomic_helper_swap_state(state, true);
if (async) mtk_atomic_schedule(private, state);
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 9c0e4261dbba..d02bd6a50e90 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -238,7 +238,7 @@ int msm_atomic_commit(struct drm_device *dev, * the software side now. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
/*
- Everything below can be run asynchronously without the need to grab
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index d86f5479345b..5d8377aad639 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -175,7 +175,7 @@ static int omap_atomic_commit(struct drm_device *dev, spin_unlock(&priv->commit.lock);
/* Swap the state, this is the point of no return. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
if (nonblock) schedule_work(&commit->work);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index f315c55c1f65..3d04a506cf33 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -327,7 +327,7 @@ static int rcar_du_atomic_commit(struct drm_device *dev, }
/* Swap the state, this is the point of no return. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
if (nonblock) schedule_work(&commit->work);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 03913b483506..5ea141dfae5b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -290,7 +290,7 @@ int rockchip_drm_atomic_commit(struct drm_device *dev, mutex_lock(&commit->lock); flush_work(&commit->work);
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
commit->dev = dev; commit->state = state;
diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index b440617a7019..dd2c400c4a46 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -215,7 +215,7 @@ static int sti_atomic_commit(struct drm_device *drm, * the software side now. */
- drm_atomic_helper_swap_state(drm, state);
drm_atomic_helper_swap_state(state, true);
if (nonblock) sti_atomic_schedule(private, state);
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index b59c3bf0df44..a177a42a9849 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -93,7 +93,7 @@ static int tegra_atomic_commit(struct drm_device *drm, * the software side now. */
- drm_atomic_helper_swap_state(drm, state);
drm_atomic_helper_swap_state(state, true);
if (nonblock) tegra_atomic_schedule(tegra, state);
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 39c0b2048bfd..8f4d5ffc32be 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -148,7 +148,7 @@ static int vc4_atomic_commit(struct drm_device *dev, * the software side now. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
/*
- Everything below can be run asynchronously without the need to grab
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index d473dcc91f54..0276447225ed 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -71,8 +71,8 @@ void drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_sta void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, bool atomic);
-void drm_atomic_helper_swap_state(struct drm_device *dev,
struct drm_atomic_state *state);
+void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
bool stall);
/* implementations for legacy interfaces */ int drm_atomic_helper_update_plane(struct drm_plane *plane,
On Mon, May 30, 2016 at 03:08:39PM +0200, Maarten Lankhorst wrote:
Op 29-05-16 om 20:35 schreef Daniel Vetter:
- dev is redundant, we have state->atomic
- add stall parameter, which must be set when swapping needs to stall for preceeding commits to stop looking at ->state pointers. Currently all drivers need this to be, just prep work for a glorious future.
I have to disagree, if you want to stall it should be done in a separate helper before swapping state. That function should also have the ability to fail, since we haven't called swap_state yet. :)
Maybe with nonblock as parameter, so it could fail with -EBUSY if it would stall, and -EINTR if interrupted?
This is not the stalling you're thinking of. The EBUSY check is in drm_atomic_helper_setup_commit (and as such entirely optional).
And if you don't ever use the nonblocking helpers, no struct drm_crtc_commit will ever show up in crtc->commit_list, which means this here also doesn't do anything.
This stall here is just to make sure that the previous commit doesn't get confused because we've ripped out crtc/plane/connector->state from underneath it. And as soon as we add previous_state pointers to drm_atomic_state and wire it up through all the hooks we can set stall=false here, since then pipelining with a queue depth > 1 is possible. -Daniel
Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c | 2 +- drivers/gpu/drm/drm_atomic_helper.c | 8 ++++---- drivers/gpu/drm/exynos/exynos_drm_drv.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/mediatek/mtk_drm_drv.c | 2 +- drivers/gpu/drm/msm/msm_atomic.c | 2 +- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 2 +- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 2 +- drivers/gpu/drm/sti/sti_drv.c | 2 +- drivers/gpu/drm/tegra/drm.c | 2 +- drivers/gpu/drm/vc4/vc4_kms.c | 2 +- include/drm/drm_atomic_helper.h | 4 ++-- 13 files changed, 17 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 6485fa5bee8b..9ecf16c7911d 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -519,7 +519,7 @@ static int atmel_hlcdc_dc_atomic_commit(struct drm_device *dev, }
/* Swap the state, this is the point of no return. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
if (async) queue_work(dc->wq, &commit->work);
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 5298eb668ca7..fecbb52cbb85 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1160,7 +1160,7 @@ int drm_atomic_helper_commit(struct drm_device *dev, * the software side now. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
/*
- Everything below can be run asynchronously without the need to grab
@@ -1531,8 +1531,8 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
/**
- drm_atomic_helper_swap_state - store atomic state into current sw state
- @dev: DRM device
- @state: atomic state
- @stall: stall for proceeding commits
- This function stores the atomic state into the current state pointers in all
- driver objects. It should be called after all failing steps have been done
@@ -1554,8 +1554,8 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
- Call drm_atomic_helper_cleanup_planes() with @state, which since step 3
- contains the old state. Also do any other cleanup required with that state.
*/ -void drm_atomic_helper_swap_state(struct drm_device *dev,
struct drm_atomic_state *state)
+void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
bool stall)
{ int i; struct drm_connector *connector; diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index cabc5fd0246d..deba76982358 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -299,7 +299,7 @@ int exynos_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, priv->pending |= commit->crtcs; spin_unlock(&priv->lock);
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
if (nonblock) schedule_work(&commit->work);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9ccd76699f48..d32274071393 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13660,7 +13660,7 @@ static int intel_atomic_commit(struct drm_device *dev, return ret; }
- drm_atomic_helper_swap_state(dev, state);
- 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);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 06a417b2f91e..c33bf98c5d5e 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -91,7 +91,7 @@ static int mtk_atomic_commit(struct drm_device *drm, mutex_lock(&private->commit.lock); flush_work(&private->commit.work);
- drm_atomic_helper_swap_state(drm, state);
drm_atomic_helper_swap_state(state, true);
if (async) mtk_atomic_schedule(private, state);
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 9c0e4261dbba..d02bd6a50e90 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -238,7 +238,7 @@ int msm_atomic_commit(struct drm_device *dev, * the software side now. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
/*
- Everything below can be run asynchronously without the need to grab
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index d86f5479345b..5d8377aad639 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -175,7 +175,7 @@ static int omap_atomic_commit(struct drm_device *dev, spin_unlock(&priv->commit.lock);
/* Swap the state, this is the point of no return. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
if (nonblock) schedule_work(&commit->work);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index f315c55c1f65..3d04a506cf33 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -327,7 +327,7 @@ static int rcar_du_atomic_commit(struct drm_device *dev, }
/* Swap the state, this is the point of no return. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
if (nonblock) schedule_work(&commit->work);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 03913b483506..5ea141dfae5b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -290,7 +290,7 @@ int rockchip_drm_atomic_commit(struct drm_device *dev, mutex_lock(&commit->lock); flush_work(&commit->work);
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
commit->dev = dev; commit->state = state;
diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index b440617a7019..dd2c400c4a46 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -215,7 +215,7 @@ static int sti_atomic_commit(struct drm_device *drm, * the software side now. */
- drm_atomic_helper_swap_state(drm, state);
drm_atomic_helper_swap_state(state, true);
if (nonblock) sti_atomic_schedule(private, state);
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index b59c3bf0df44..a177a42a9849 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -93,7 +93,7 @@ static int tegra_atomic_commit(struct drm_device *drm, * the software side now. */
- drm_atomic_helper_swap_state(drm, state);
drm_atomic_helper_swap_state(state, true);
if (nonblock) tegra_atomic_schedule(tegra, state);
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 39c0b2048bfd..8f4d5ffc32be 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -148,7 +148,7 @@ static int vc4_atomic_commit(struct drm_device *dev, * the software side now. */
- drm_atomic_helper_swap_state(dev, state);
drm_atomic_helper_swap_state(state, true);
/*
- Everything below can be run asynchronously without the need to grab
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index d473dcc91f54..0276447225ed 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -71,8 +71,8 @@ void drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_sta void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, bool atomic);
-void drm_atomic_helper_swap_state(struct drm_device *dev,
struct drm_atomic_state *state);
+void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
bool stall);
/* implementations for legacy interfaces */ int drm_atomic_helper_update_plane(struct drm_plane *plane,
Intel-gfx mailing list Intel-gfx@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Op 30-05-16 om 17:09 schreef Daniel Vetter:
On Mon, May 30, 2016 at 03:08:39PM +0200, Maarten Lankhorst wrote:
Op 29-05-16 om 20:35 schreef Daniel Vetter:
- dev is redundant, we have state->atomic
- add stall parameter, which must be set when swapping needs to stall for preceeding commits to stop looking at ->state pointers. Currently all drivers need this to be, just prep work for a glorious future.
I have to disagree, if you want to stall it should be done in a separate helper before swapping state. That function should also have the ability to fail, since we haven't called swap_state yet. :)
Maybe with nonblock as parameter, so it could fail with -EBUSY if it would stall, and -EINTR if interrupted?
This is not the stalling you're thinking of. The EBUSY check is in drm_atomic_helper_setup_commit (and as such entirely optional).
And if you don't ever use the nonblocking helpers, no struct drm_crtc_commit will ever show up in crtc->commit_list, which means this here also doesn't do anything.
This stall here is just to make sure that the previous commit doesn't get confused because we've ripped out crtc/plane/connector->state from underneath it. And as soon as we add previous_state pointers to drm_atomic_state and wire it up through all the hooks we can set stall=false here, since then pipelining with a queue depth > 1 is possible.
If a driver supports depth > 1 it could skip the extra help waiter and just call swap_state. The helper should return -EINTR when needed.
if (nonblock) { ret = drm_atomic_helper_wait_for_completion(state); if (ret) return ret; }
drm_atomic_helper_swap_state..
On Tue, May 31, 2016 at 10:43:56AM +0200, Maarten Lankhorst wrote:
Op 30-05-16 om 17:09 schreef Daniel Vetter:
On Mon, May 30, 2016 at 03:08:39PM +0200, Maarten Lankhorst wrote:
Op 29-05-16 om 20:35 schreef Daniel Vetter:
- dev is redundant, we have state->atomic
- add stall parameter, which must be set when swapping needs to stall for preceeding commits to stop looking at ->state pointers. Currently all drivers need this to be, just prep work for a glorious future.
I have to disagree, if you want to stall it should be done in a separate helper before swapping state. That function should also have the ability to fail, since we haven't called swap_state yet. :)
Maybe with nonblock as parameter, so it could fail with -EBUSY if it would stall, and -EINTR if interrupted?
This is not the stalling you're thinking of. The EBUSY check is in drm_atomic_helper_setup_commit (and as such entirely optional).
And if you don't ever use the nonblocking helpers, no struct drm_crtc_commit will ever show up in crtc->commit_list, which means this here also doesn't do anything.
This stall here is just to make sure that the previous commit doesn't get confused because we've ripped out crtc/plane/connector->state from underneath it. And as soon as we add previous_state pointers to drm_atomic_state and wire it up through all the hooks we can set stall=false here, since then pipelining with a queue depth > 1 is possible.
If a driver supports depth > 1 it could skip the extra help waiter and just call swap_state. The helper should return -EINTR when needed.
if (nonblock) { ret = drm_atomic_helper_wait_for_completion(state); if (ret) return ret; }
drm_atomic_helper_swap_state..
That's why there's this stall parameter ... Also, wait_for_completion(state) is not enough, there's 3 different completions. -Daniel
Op 31-05-16 om 12:46 schreef Daniel Vetter:
On Tue, May 31, 2016 at 10:43:56AM +0200, Maarten Lankhorst wrote:
Op 30-05-16 om 17:09 schreef Daniel Vetter:
On Mon, May 30, 2016 at 03:08:39PM +0200, Maarten Lankhorst wrote:
Op 29-05-16 om 20:35 schreef Daniel Vetter:
- dev is redundant, we have state->atomic
- add stall parameter, which must be set when swapping needs to stall for preceeding commits to stop looking at ->state pointers. Currently all drivers need this to be, just prep work for a glorious future.
I have to disagree, if you want to stall it should be done in a separate helper before swapping state. That function should also have the ability to fail, since we haven't called swap_state yet. :)
Maybe with nonblock as parameter, so it could fail with -EBUSY if it would stall, and -EINTR if interrupted?
This is not the stalling you're thinking of. The EBUSY check is in drm_atomic_helper_setup_commit (and as such entirely optional).
And if you don't ever use the nonblocking helpers, no struct drm_crtc_commit will ever show up in crtc->commit_list, which means this here also doesn't do anything.
This stall here is just to make sure that the previous commit doesn't get confused because we've ripped out crtc/plane/connector->state from underneath it. And as soon as we add previous_state pointers to drm_atomic_state and wire it up through all the hooks we can set stall=false here, since then pipelining with a queue depth > 1 is possible.
If a driver supports depth > 1 it could skip the extra help waiter and just call swap_state. The helper should return -EINTR when needed.
if (nonblock) { ret = drm_atomic_helper_wait_for_completion(state); if (ret) return ret; }
drm_atomic_helper_swap_state..
That's why there's this stall parameter ... Also, wait_for_completion(state) is not enough, there's 3 different completions.
Yeah but this is pseudocode. I don't think waiting belongs to swap_state, should be done before so we can still back out if wait fails..
~Maarten
This is just used for cleanup in preclose, and with the reworked event handling code this is now done properly by the core.
Nuke it!
But it also shows that arc totally fails at sending out drm events for flips. Next patch will hack that up.
Cc: Carlos Palminha palminha@synopsys.com Cc: Alexey Brodkin abrodkin@synopsys.com Cc: linux-snps-arc@lists.infradead.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/arc/arcpgu.h | 1 - drivers/gpu/drm/arc/arcpgu_crtc.c | 4 ---- drivers/gpu/drm/arc/arcpgu_drv.c | 19 ------------------- 3 files changed, 24 deletions(-)
diff --git a/drivers/gpu/drm/arc/arcpgu.h b/drivers/gpu/drm/arc/arcpgu.h index 86574b698a78..8c01a25d279a 100644 --- a/drivers/gpu/drm/arc/arcpgu.h +++ b/drivers/gpu/drm/arc/arcpgu.h @@ -22,7 +22,6 @@ struct arcpgu_drm_private { struct clk *clk; struct drm_fbdev_cma *fbdev; struct drm_framebuffer *fb; - struct list_head event_list; struct drm_crtc crtc; struct drm_plane *plane; }; diff --git a/drivers/gpu/drm/arc/arcpgu_crtc.c b/drivers/gpu/drm/arc/arcpgu_crtc.c index 92f8beff8e60..d5ca0c280e68 100644 --- a/drivers/gpu/drm/arc/arcpgu_crtc.c +++ b/drivers/gpu/drm/arc/arcpgu_crtc.c @@ -155,10 +155,6 @@ static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc, event->pipe = drm_crtc_index(crtc);
WARN_ON(drm_crtc_vblank_get(crtc) != 0); - - spin_lock_irqsave(&crtc->dev->event_lock, flags); - list_add_tail(&event->base.link, &arcpgu->event_list); - spin_unlock_irqrestore(&crtc->dev->event_lock, flags); } }
diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c index bc53ebb83f75..d407fd79a400 100644 --- a/drivers/gpu/drm/arc/arcpgu_drv.c +++ b/drivers/gpu/drm/arc/arcpgu_drv.c @@ -81,22 +81,6 @@ static const struct file_operations arcpgu_drm_ops = { .mmap = arcpgu_gem_mmap, };
-static void arcpgu_preclose(struct drm_device *drm, struct drm_file *file) -{ - struct arcpgu_drm_private *arcpgu = drm->dev_private; - struct drm_pending_vblank_event *e, *t; - unsigned long flags; - - spin_lock_irqsave(&drm->event_lock, flags); - list_for_each_entry_safe(e, t, &arcpgu->event_list, base.link) { - if (e->base.file_priv != file) - continue; - list_del(&e->base.link); - e->base.destroy(&e->base); - } - spin_unlock_irqrestore(&drm->event_lock, flags); -} - static void arcpgu_lastclose(struct drm_device *drm) { struct arcpgu_drm_private *arcpgu = drm->dev_private; @@ -122,8 +106,6 @@ static int arcpgu_load(struct drm_device *drm) if (IS_ERR(arcpgu->clk)) return PTR_ERR(arcpgu->clk);
- INIT_LIST_HEAD(&arcpgu->event_list); - arcpgu_setup_mode_config(drm);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -192,7 +174,6 @@ int arcpgu_unload(struct drm_device *drm) static struct drm_driver arcpgu_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, - .preclose = arcpgu_preclose, .lastclose = arcpgu_lastclose, .name = "drm-arcpgu", .desc = "ARC PGU Controller",
The drm core has a nice ready-made helper for exactly the simple case where it should fire on the next vblank.
Note that arming the vblank event in _begin is probably too early, and might easily result in the vblank firing too early, before the new set of planes are actually disabled. But that's kinda a minor issue compared to just outright hanging userspace.
v2: Be more robust and either arm, when the CRTC is on, or just send the event out right away.
Cc: Carlos Palminha palminha@synopsys.com Cc: Alexey Brodkin abrodkin@synopsys.com Cc: linux-snps-arc@lists.infradead.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/arc/arcpgu_crtc.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/arc/arcpgu_crtc.c b/drivers/gpu/drm/arc/arcpgu_crtc.c index d5ca0c280e68..c9f183b11df9 100644 --- a/drivers/gpu/drm/arc/arcpgu_crtc.c +++ b/drivers/gpu/drm/arc/arcpgu_crtc.c @@ -145,16 +145,17 @@ static int arc_pgu_crtc_atomic_check(struct drm_crtc *crtc, static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_crtc_state *state) { - struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); - unsigned long flags; - - if (crtc->state->event) { - struct drm_pending_vblank_event *event = crtc->state->event; + struct drm_pending_vblank_event *event = crtc->state->event;
+ if (event) { crtc->state->event = NULL; - event->pipe = drm_crtc_index(crtc);
- WARN_ON(drm_crtc_vblank_get(crtc) != 0); + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); } }
Committing with block it is not.
Thanks to the fixed up vblank event handling we can just use the helper support for nonblocking commits now.
Cc: Carlos Palminha palminha@synopsys.com Cc: Alexey Brodkin abrodkin@synopsys.com Cc: linux-snps-arc@lists.infradead.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/arc/arcpgu_drv.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c index d407fd79a400..a92e533531c3 100644 --- a/drivers/gpu/drm/arc/arcpgu_drv.c +++ b/drivers/gpu/drm/arc/arcpgu_drv.c @@ -32,17 +32,11 @@ static void arcpgu_fb_output_poll_changed(struct drm_device *dev) drm_fbdev_cma_hotplug_event(arcpgu->fbdev); }
-static int arcpgu_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, bool async) -{ - return drm_atomic_helper_commit(dev, state, false); -} - static struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = arcpgu_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check, - .atomic_commit = arcpgu_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, };
static void arcpgu_setup_mode_config(struct drm_device *drm)
Op 29-05-16 om 20:35 schreef Daniel Vetter:
Committing with block it is not.
Thanks to the fixed up vblank event handling we can just use the helper support for nonblocking commits now.
Cc: Carlos Palminha palminha@synopsys.com Cc: Alexey Brodkin abrodkin@synopsys.com Cc: linux-snps-arc@lists.infradead.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
This breaks page flip on arc, it uses drm_atomic_helper_page_flip.
On Mon, May 30, 2016 at 10:15:14AM +0200, Maarten Lankhorst wrote:
Op 29-05-16 om 20:35 schreef Daniel Vetter:
Committing with block it is not.
Thanks to the fixed up vblank event handling we can just use the helper support for nonblocking commits now.
Cc: Carlos Palminha palminha@synopsys.com Cc: Alexey Brodkin abrodkin@synopsys.com Cc: linux-snps-arc@lists.infradead.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
This breaks page flip on arc, it uses drm_atomic_helper_page_flip.
How exactly does this break page_flip? Note that the commit to implement generic nonblocking commit in drm_atomic_helper_commit is _before_ this patch in the series. -Daniel
Op 30-05-16 om 11:24 schreef Daniel Vetter:
On Mon, May 30, 2016 at 10:15:14AM +0200, Maarten Lankhorst wrote:
Op 29-05-16 om 20:35 schreef Daniel Vetter:
Committing with block it is not.
Thanks to the fixed up vblank event handling we can just use the helper support for nonblocking commits now.
Cc: Carlos Palminha palminha@synopsys.com Cc: Alexey Brodkin abrodkin@synopsys.com Cc: linux-snps-arc@lists.infradead.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
This breaks page flip on arc, it uses drm_atomic_helper_page_flip.
How exactly does this break page_flip? Note that the commit to implement generic nonblocking commit in drm_atomic_helper_commit is _before_ this patch in the series. -Daniel
This is patch 15/16, I found this in patch 21/26:
@@ -1147,8 +1206,11 @@ int drm_atomic_helper_commit(struct drm_device *dev, { int ret;
- if (nonblock) - return -EBUSY;
On Mon, May 30, 2016 at 11:36:06AM +0200, Maarten Lankhorst wrote:
Op 30-05-16 om 11:24 schreef Daniel Vetter:
On Mon, May 30, 2016 at 10:15:14AM +0200, Maarten Lankhorst wrote:
Op 29-05-16 om 20:35 schreef Daniel Vetter:
Committing with block it is not.
Thanks to the fixed up vblank event handling we can just use the helper support for nonblocking commits now.
Cc: Carlos Palminha palminha@synopsys.com Cc: Alexey Brodkin abrodkin@synopsys.com Cc: linux-snps-arc@lists.infradead.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com
This breaks page flip on arc, it uses drm_atomic_helper_page_flip.
How exactly does this break page_flip? Note that the commit to implement generic nonblocking commit in drm_atomic_helper_commit is _before_ this patch in the series. -Daniel
This is patch 15/16, I found this in patch 21/26:
@@ -1147,8 +1206,11 @@ int drm_atomic_helper_commit(struct drm_device *dev, { int ret;
- if (nonblock)
return -EBUSY;
Oh right, I mixed things up. Most of the prep work is for drm event handling. We need to do that _before_ the non-blocking commit lands for the drivers that just directly reuse drm_atomic_helper_commit, since the nonblocking helpers will time out when drm events don't work.
But this one indeed should only happen after that commit. I'll reorder. -Daniel
Again, just doing them as blocking commits isn't cool.
From a cursory check hdlcd does seem to at least try to handle
drm_pending_vblank_event, so hopefully this works.
Cc: Liviu Dudau Liviu.Dudau@arm.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/arm/hdlcd_drv.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 4f909378d581..6d1877238cf0 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -112,17 +112,11 @@ static void hdlcd_fb_output_poll_changed(struct drm_device *drm) drm_fbdev_cma_hotplug_event(hdlcd->fbdev); }
-static int hdlcd_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, bool nonblock) -{ - return drm_atomic_helper_commit(dev, state, false); -} - static const struct drm_mode_config_funcs hdlcd_mode_config_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = hdlcd_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check, - .atomic_commit = hdlcd_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, };
static void hdlcd_setup_mode_config(struct drm_device *drm)
On Sun, May 29, 2016 at 08:35:13PM +0200, Daniel Vetter wrote:
Again, just doing them as blocking commits isn't cool.
From a cursory check hdlcd does seem to at least try to handle drm_pending_vblank_event, so hopefully this works.
Cc: Liviu Dudau Liviu.Dudau@arm.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com
Daniel,
I have a proper implementation for this, which I will send out shortly, so I will NAK this patch.
Best regards, Liviu
drivers/gpu/drm/arm/hdlcd_drv.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 4f909378d581..6d1877238cf0 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -112,17 +112,11 @@ static void hdlcd_fb_output_poll_changed(struct drm_device *drm) drm_fbdev_cma_hotplug_event(hdlcd->fbdev); }
-static int hdlcd_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *state, bool nonblock)
-{
- return drm_atomic_helper_commit(dev, state, false);
-}
static const struct drm_mode_config_funcs hdlcd_mode_config_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = hdlcd_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check,
- .atomic_commit = hdlcd_atomic_commit,
- .atomic_commit = drm_atomic_helper_commit,
};
static void hdlcd_setup_mode_config(struct drm_device *drm)
2.8.1
On Tue, May 31, 2016 at 12:02:23PM +0100, Liviu Dudau wrote:
On Sun, May 29, 2016 at 08:35:13PM +0200, Daniel Vetter wrote:
Again, just doing them as blocking commits isn't cool.
From a cursory check hdlcd does seem to at least try to handle drm_pending_vblank_event, so hopefully this works.
Cc: Liviu Dudau Liviu.Dudau@arm.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com
Daniel,
I have a proper implementation for this, which I will send out shortly, so I will NAK this patch.
Please don't implement your own atomic nonblocking commit any more. I have a generic solution, which this patch series implements and then also converts hdlcd over to it. I admit the commit message is a bit unclear with that.
If you don't want to pick this up from the mailing lists it's also in my fdo git repo:
https://cgit.freedesktop.org/~danvet/drm/log/
So nack on your nack ;-)
Cheers, Daniel
Best regards, Liviu
drivers/gpu/drm/arm/hdlcd_drv.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 4f909378d581..6d1877238cf0 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -112,17 +112,11 @@ static void hdlcd_fb_output_poll_changed(struct drm_device *drm) drm_fbdev_cma_hotplug_event(hdlcd->fbdev); }
-static int hdlcd_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *state, bool nonblock)
-{
- return drm_atomic_helper_commit(dev, state, false);
-}
static const struct drm_mode_config_funcs hdlcd_mode_config_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = hdlcd_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check,
- .atomic_commit = hdlcd_atomic_commit,
- .atomic_commit = drm_atomic_helper_commit,
};
static void hdlcd_setup_mode_config(struct drm_device *drm)
2.8.1
--
| I would like to | | fix the world, | | but they're not | | giving me the | \ source code! /
¯\_(ツ)_/¯
On Tue, May 31, 2016 at 01:07:15PM +0200, Daniel Vetter wrote:
On Tue, May 31, 2016 at 12:02:23PM +0100, Liviu Dudau wrote:
On Sun, May 29, 2016 at 08:35:13PM +0200, Daniel Vetter wrote:
Again, just doing them as blocking commits isn't cool.
From a cursory check hdlcd does seem to at least try to handle drm_pending_vblank_event, so hopefully this works.
Cc: Liviu Dudau Liviu.Dudau@arm.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com
Daniel,
I have a proper implementation for this, which I will send out shortly, so I will NAK this patch.
Please don't implement your own atomic nonblocking commit any more. I have a generic solution, which this patch series implements and then also converts hdlcd over to it. I admit the commit message is a bit unclear with that.
If you don't want to pick this up from the mailing lists it's also in my fdo git repo:
https://cgit.freedesktop.org/~danvet/drm/log/
So nack on your nack ;-)
OK, in that case:
Acked-by: Liviu Dudau Liviu.Dudau@arm.com
Working towards validating your patch series with the HDLCD driver as well.
Best regards, Liviu
Cheers, Daniel
Best regards, Liviu
drivers/gpu/drm/arm/hdlcd_drv.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 4f909378d581..6d1877238cf0 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -112,17 +112,11 @@ static void hdlcd_fb_output_poll_changed(struct drm_device *drm) drm_fbdev_cma_hotplug_event(hdlcd->fbdev); }
-static int hdlcd_atomic_commit(struct drm_device *dev,
struct drm_atomic_state *state, bool nonblock)
-{
- return drm_atomic_helper_commit(dev, state, false);
-}
static const struct drm_mode_config_funcs hdlcd_mode_config_funcs = { .fb_create = drm_fb_cma_create, .output_poll_changed = hdlcd_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check,
- .atomic_commit = hdlcd_atomic_commit,
- .atomic_commit = drm_atomic_helper_commit,
};
static void hdlcd_setup_mode_config(struct drm_device *drm)
2.8.1
--
| I would like to | | fix the world, | | but they're not | | giving me the | \ source code! /
¯\_(ツ)_/¯
-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
No idea how exactly fsl-du commits hw state changes, but here in flush is probably the safest place.
While at it nuke the dummy functions.
v2: Be more robust and either arm, when the CRTC is on, or just send the event out right away.
Cc: Stefan Agner stefan@agner.ch Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c index 89c0084c2814..706de3278f1c 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c @@ -22,20 +22,21 @@ #include "fsl_dcu_drm_drv.h" #include "fsl_dcu_drm_plane.h"
-static void fsl_dcu_drm_crtc_atomic_begin(struct drm_crtc *crtc, +static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { -} + struct drm_pending_vblank_event *event = crtc->state->event;
-static int fsl_dcu_drm_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - return 0; -} + if (event) { + crtc->state->event = NULL;
-static void fsl_dcu_drm_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) -{ + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } }
static void fsl_dcu_drm_disable_crtc(struct drm_crtc *crtc) @@ -117,8 +118,6 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) }
static const struct drm_crtc_helper_funcs fsl_dcu_drm_crtc_helper_funcs = { - .atomic_begin = fsl_dcu_drm_crtc_atomic_begin, - .atomic_check = fsl_dcu_drm_crtc_atomic_check, .atomic_flush = fsl_dcu_drm_crtc_atomic_flush, .disable = fsl_dcu_drm_disable_crtc, .enable = fsl_dcu_drm_crtc_enable,
atomic_flush seems to be the right place, but I'm not entirely sure whether this will catch them all. It could be that when disabling the crtc we'll miss the vblank.
While at it nuke the dummy functions.
v2: Be more robust and either arm, when the CRTC is on, or just send the event out right away.
Cc: Xinliang Liu xinliang.liu@linaro.org Cc: Xinwei Kong kong.kongxinwei@hisilicon.com Cc: Archit Taneja architt@codeaurora.org Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c index fba6372d060e..ed76baad525f 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c @@ -502,13 +502,6 @@ static void ade_crtc_disable(struct drm_crtc *crtc) acrtc->enable = false; }
-static int ade_crtc_atomic_check(struct drm_crtc *crtc, - struct drm_crtc_state *state) -{ - /* do nothing */ - return 0; -} - static void ade_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct ade_crtc *acrtc = to_ade_crtc(crtc); @@ -537,6 +530,7 @@ static void ade_crtc_atomic_flush(struct drm_crtc *crtc, { struct ade_crtc *acrtc = to_ade_crtc(crtc); struct ade_hw_ctx *ctx = acrtc->ctx; + struct drm_pending_vblank_event *event = crtc->state->event; void __iomem *base = ctx->base;
/* only crtc is enabled regs take effect */ @@ -545,12 +539,22 @@ static void ade_crtc_atomic_flush(struct drm_crtc *crtc, /* flush ade registers */ writel(ADE_ENABLE, base + ADE_EN); } + + if (event) { + crtc->state->event = NULL; + + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } }
static const struct drm_crtc_helper_funcs ade_crtc_helper_funcs = { .enable = ade_crtc_enable, .disable = ade_crtc_disable, - .atomic_check = ade_crtc_atomic_check, .mode_set_nofb = ade_crtc_mode_set_nofb, .atomic_begin = ade_crtc_atomic_begin, .atomic_flush = ade_crtc_atomic_flush,
atomic_flush seems to be the right place, right after we commit the plane updates. Again use the fullproof version, since the pipe might be off.
Cc: Boris Brezillon boris.brezillon@free-electrons.com Cc: Maxime Ripard maxime.ripard@free-electrons.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/sun4i/sun4i_crtc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/drivers/gpu/drm/sun4i/sun4i_crtc.c b/drivers/gpu/drm/sun4i/sun4i_crtc.c index 4182a21f5923..f628b6d8f23f 100644 --- a/drivers/gpu/drm/sun4i/sun4i_crtc.c +++ b/drivers/gpu/drm/sun4i/sun4i_crtc.c @@ -51,10 +51,22 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc, { struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc); struct sun4i_drv *drv = scrtc->drv; + struct drm_pending_vblank_event *event = crtc->state->event;
DRM_DEBUG_DRIVER("Committing plane changes\n");
sun4i_backend_commit(drv->backend); + + if (event) { + crtc->state->event = NULL; + + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } }
static void sun4i_crtc_disable(struct drm_crtc *crtc)
Hi Daniel,
On Sun, May 29, 2016 at 08:35:16PM +0200, Daniel Vetter wrote:
atomic_flush seems to be the right place, right after we commit the plane updates. Again use the fullproof version, since the pipe might be off.
This looks fine.
How can that be tested? modetest requires async vblank, which is not there yet, and X doesn't seem to use it at all (since it works fine without it).
Thanks! Maxime
On Wed, Jun 01, 2016 at 06:18:59PM +0200, Maxime Ripard wrote:
Hi Daniel,
On Sun, May 29, 2016 at 08:35:16PM +0200, Daniel Vetter wrote:
atomic_flush seems to be the right place, right after we commit the plane updates. Again use the fullproof version, since the pipe might be off.
This looks fine.
How can that be tested? modetest requires async vblank, which is not there yet, and X doesn't seem to use it at all (since it works fine without it).
Run the entire series. It implements nonblocking commit for everyone. I'm just working on submitting the non-RFC version of this series. -Daniel
Just a bit of drive-by ocd.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- include/drm/drm_atomic.h | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index d9504dfcd1cc..465a1212f4f0 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -198,6 +198,13 @@ int __must_check drm_atomic_nonblocking_commit(struct drm_atomic_state *state); (plane_state) = (__state)->planes[__i].state, 1); \ (__i)++) \ for_each_if (plane_state) + +/** + * drm_atomic_crtc_needs_modeset - compute combined modeset need + * @state: &drm_crtc_state for the CRTC + * + * This computes the combined need for a modeset for @state. + */ static inline bool drm_atomic_crtc_needs_modeset(struct drm_crtc_state *state) {
Design ideas:
- split up the actual commit into different phases, and have completions for each of them. This will be useful for the future when we want to interleave phases much more aggressively, for e.g. queue depth > 1. For not it's just a minimal optimization compared to current common nonblocking implementation patterns from drivers, which all stall for the entire commit to complete, including vblank waits and cleanups.
- Extract a separate atomic_commit_hw hook since that's the part most drivers will need to overwrite, hopefully allowing even more shared code.
- Enforce EBUSY seamntics by attaching one of the completions to the flip_done vblank event. Side benefit of forcing atomic drivers using these helpers to implement event handlign at least semi-correct. I'm evil that way ;-)
- Ridiculously modular, as usual.
- The main tracking unit for a commit stays struct drm_atomic_state, and the ownership rules for that are unchanged. Ownership still gets transferred to the driver (and subsequently to the worker) on successful commits. What is added is a small, per-crtc, refcounted structure to track pending commits called struct drm_crtc_commit. No actual state is attached to that though, it's purely for ordering and waiting.
- Dependencies are implicitly handled by assuming that any CRTC part of &drm_atomic_state is a dependency, and that the current commit must wait for any commits to complete on those CRTC. This way drivers can easily add more depencies using drm_atomic_get_crtc_state(), which is very natural since in most case a dependency exists iff there's some bit of state that needs to be cross checked.
Removing depencies is not possible, drivers simply need to be careful to not include every CRTC in a commit if that's not necessary. Which is a good idea anyway, since that also avoids ww_mutex lock contention.
- Queue depth > 1 sees some prep work in this patch by adding a stall paramater to drm_atomic_helper_swap_states(). To be able to push commits entirely free-standing and in a deeper queue through the back-end the driver must not access any obj->state pointers. This means we need to track the old state in drm_atomic_state (much easier with the consolidated arrays), and pass them all explicitly to driver backends (this will be serious amounts of churn).
Once that's done stall can be set to false in swap_states.
Features: Contains bugs because totally untested.
v2: Dont ask for flip_done signalling when the CRTC is off and stays off: Drivers don't handle events in that case. Instead complete right away. This way future commits don't need to have special-case logic, but can keep blocking for the flip_done completion.
v3: Tons of fixes: - Stall for preceeding commit for real, not the current one by accident. - Add WARN_ON in case drivers don't fire the drm event. - Don't double-free drm events.
v4: Make legacy cursor not stall.
v5: Extend the helper hook to cover the entire commit tail. Some drivers need special code for cleanup and vblank waiting, this makes it a bit more useful. Inspired by the rockchip driver.
v6: Add WARN_ON to catch drivers who forget to send out the drm event.
Cc: Tomeu Vizoso tomeu.vizoso@gmail.com Cc: Daniel Stone daniels@collabora.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_atomic.c | 22 ++ drivers/gpu/drm/drm_atomic_helper.c | 366 ++++++++++++++++++++++++++++--- drivers/gpu/drm/drm_crtc.c | 3 + drivers/gpu/drm/drm_fops.c | 6 + include/drm/drmP.h | 1 + include/drm/drm_atomic.h | 6 + include/drm/drm_atomic_helper.h | 8 + include/drm/drm_crtc.h | 119 +++++++++- include/drm/drm_modeset_helper_vtables.h | 36 +++ 9 files changed, 534 insertions(+), 33 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 1db198df3014..50aa341ca9ff 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -33,6 +33,20 @@
#include "drm_crtc_internal.h"
+static void crtc_commit_free(struct kref *kref) +{ + struct drm_crtc_commit *commit = + container_of(kref, struct drm_crtc_commit, ref); + + kfree(commit); +} + +void drm_crtc_commit_put(struct drm_crtc_commit *commit) +{ + kref_put(&commit->ref, crtc_commit_free); +} +EXPORT_SYMBOL(drm_crtc_commit_put); + /** * drm_atomic_state_default_release - * release memory initialized by drm_atomic_state_init @@ -148,6 +162,14 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
crtc->funcs->atomic_destroy_state(crtc, state->crtcs[i].state); + + if (state->crtcs[i].commit) { + kfree(state->crtcs[i].commit->event); + state->crtcs[i].commit->event = NULL; + drm_crtc_commit_put(state->crtcs[i].commit); + } + + state->crtcs[i].commit = NULL; state->crtcs[i].ptr = NULL; state->crtcs[i].state = NULL; } diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index fecbb52cbb85..a5854a31f493 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1113,20 +1113,15 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
/** - * drm_atomic_helper_commit - commit validated state object - * @dev: DRM device - * @state: the driver state object - * @nonblocking: whether nonblocking behavior is requested. + * drm_atomic_helper_commit_tail - commit atomic update to hardware + * @state: new modeset state to be committed * - * This function commits a with drm_atomic_helper_check() pre-validated state - * object. This can still fail when e.g. the framebuffer reservation fails. For - * now this doesn't implement nonblocking commits. + * This is the default implemenation for the ->atomic_commit_tail() hook of the + * &drm_mode_config_helper_funcs vtable. * - * Note that right now this function does not support nonblocking commits, hence - * driver writers must implement their own version for now. Also note that the - * default ordering of how the various stages are called is to match the legacy - * modeset helper library closest. One peculiarity of that is that it doesn't - * mesh well with runtime PM at all. + * Note that the default ordering of how the various stages are called is to + * match the legacy modeset helper library closest. One peculiarity of that is + * that it doesn't mesh well with runtime PM at all. * * For drivers supporting runtime PM the recommended sequence is * @@ -1137,6 +1132,70 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); * drm_atomic_helper_commit_planes(dev, state, true); * * See the kerneldoc entries for these three functions for more details. + */ +void drm_atomic_helper_commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + + drm_atomic_helper_commit_modeset_disables(dev, state); + + drm_atomic_helper_commit_planes(dev, state, false); + + drm_atomic_helper_commit_modeset_enables(dev, state); + + drm_atomic_helper_commit_hw_done(state); + + drm_atomic_helper_wait_for_vblanks(dev, state); + + drm_atomic_helper_cleanup_planes(dev, state); +} +EXPORT_SYMBOL(drm_atomic_helper_commit_tail); + +void commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + struct drm_mode_config_helper_funcs *funcs; + + funcs = dev->mode_config.helper_private; + + drm_atomic_helper_wait_for_fences(dev, state); + + drm_atomic_helper_wait_for_dependencies(state); + + if (funcs && funcs->atomic_commit_tail) + funcs->atomic_commit_tail(state); + else + drm_atomic_helper_commit_tail(state); + + drm_atomic_helper_commit_cleanup_done(state); + + drm_atomic_state_free(state); +} + +static void commit_work(struct work_struct *work) +{ + struct drm_atomic_state *state = container_of(work, + struct drm_atomic_state, + commit_work); + commit_tail(state); +} + +/** + * drm_atomic_helper_commit - commit validated state object + * @dev: DRM device + * @state: the driver state object + * @nonblocking: whether nonblocking behavior is requested. + * + * This function commits a with drm_atomic_helper_check() pre-validated state + * object. This can still fail when e.g. the framebuffer reservation fails. For + * now this doesn't implement nonblocking commits. + * + * Note that right now this function does not support nonblocking commits, hence + * driver writers must implement their own version for now. + * + * Committing the actual hardware state is done through the + * ->atomic_commit_tail() callback of the &drm_mode_config_helper_funcs vtable, + * or it's default implementation drm_atomic_helper_commit_tail(). * * RETURNS * Zero for success or -errno. @@ -1147,8 +1206,11 @@ int drm_atomic_helper_commit(struct drm_device *dev, { int ret;
- if (nonblock) - return -EBUSY; + ret = drm_atomic_helper_setup_commit(state, nonblock); + if (ret) + return ret; + + INIT_WORK(&state->commit_work, commit_work);
ret = drm_atomic_helper_prepare_planes(dev, state); if (ret) @@ -1176,21 +1238,16 @@ int drm_atomic_helper_commit(struct drm_device *dev, * update. Which is important since compositors need to figure out the * composition of the next frame right after having submitted the * current layout. + * + * NOTE: Commit work has multiple phases, first hardware commit, then + * cleanup. We want them to overlap, hence need system_unbound_wq to + * make sure work items don't artifically stall on each another. */
- drm_atomic_helper_wait_for_fences(dev, state); - - drm_atomic_helper_commit_modeset_disables(dev, state); - - drm_atomic_helper_commit_planes(dev, state, false); - - drm_atomic_helper_commit_modeset_enables(dev, state); - - drm_atomic_helper_wait_for_vblanks(dev, state); - - drm_atomic_helper_cleanup_planes(dev, state); - - drm_atomic_state_free(state); + if (nonblock) + queue_work(system_unbound_wq, &state->commit_work); + else + commit_tail(state);
return 0; } @@ -1234,6 +1291,225 @@ EXPORT_SYMBOL(drm_atomic_helper_commit); * being displayed. */
+static int stall_checks(struct drm_crtc *crtc, bool nonblock) +{ + struct drm_crtc_commit *commit, *stall_commit = NULL; + bool completed = true; + int i, ret = 0; + + spin_lock(&crtc->commit_lock); + i = 0; + list_for_each_entry(commit, &crtc->commit_list, commit_entry) { + if (i == 0) { + completed = try_wait_for_completion(&commit->flip_done); + /* Userspace is not allowed to get ahead of the previous + * commit with nonblocking ones. */ + if (!completed && nonblock) { + spin_unlock(&crtc->commit_lock); + return -EBUSY; + } + } else if (i == 1) { + stall_commit = commit; + drm_crtc_commit_get(stall_commit); + } else + break; + + i++; + } + spin_unlock(&crtc->commit_lock); + + if (!stall_commit) + return 0; + + /* We don't want to let commits get ahead of cleanup work too much, + * stalling on 2nd previous commit means triple-buffer won't ever stall. + */ + ret = wait_for_completion_interruptible_timeout(&commit->cleanup_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] cleanup_done timed out\n", + crtc->base.id, crtc->name); + + if (ret == -ERESTARTSYS) + ret = -EINTR; + + drm_crtc_commit_put(stall_commit); + + return ret; +} + +/** + * drm_atomic_helper_setup_commit - setup possible nonblocking commit + * + * Returns: + * + * 0 on success. -EBUSY when userspace schedules nonblocking commits too fast, + * -ENOMEM on allocation failures and -EINTR when a signal is pending. + */ +int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, + bool nonblock) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i, ret; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + commit = kzalloc(sizeof(*commit), GFP_KERNEL); + if (!commit) + return -ENOMEM; + + init_completion(&commit->flip_done); + init_completion(&commit->hw_done); + init_completion(&commit->cleanup_done); + INIT_LIST_HEAD(&commit->commit_entry); + kref_init(&commit->ref); + commit->crtc = crtc; + + state->crtcs[i].commit = commit; + + ret = stall_checks(crtc, nonblock); + if (ret) + return ret; + + /* Drivers only send out events when at least either current or + * new CRTC state is active. Complete right away if everything + * stays off. */ + if (!crtc->state->active && !crtc_state->active) { + complete_all(&commit->flip_done); + continue; + } + + /* Legacy cursor updates are fully unsynced. */ + if (state->legacy_cursor_update) { + complete_all(&commit->flip_done); + continue; + } + + if (!crtc_state->event) { + commit->event = kzalloc(sizeof(*commit->event), + GFP_KERNEL); + if (!commit->event) + return -ENOMEM; + + crtc_state->event = commit->event; + } + + crtc_state->event->base.completion = &commit->flip_done; + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_helper_setup_commit); + + +static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc) +{ + struct drm_crtc_commit *commit; + int i = 0; + + list_for_each_entry(commit, &crtc->commit_list, commit_entry) { + /* skip the first entry, that's the current commit */ + if (i == 1) + return commit; + i++; + } + + return NULL; +} + +void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i; + long ret; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + spin_lock(&crtc->commit_lock); + commit = preceeding_commit(crtc); + if (commit) + drm_crtc_commit_get(commit); + spin_unlock(&crtc->commit_lock); + + if (!commit) + continue; + + ret = wait_for_completion_timeout(&commit->hw_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] hw_done timed out\n", + crtc->base.id, crtc->name); + + /* Currently no support for overwriting flips, hence + * stall for previous one to execute completely. */ + ret = wait_for_completion_timeout(&commit->flip_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n", + crtc->base.id, crtc->name); + + drm_crtc_commit_put(commit); + } +} +EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies); + +void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + commit = state->crtcs[i].commit; + if (!commit) + continue; + + /* backend must have consumed any event by now */ + WARN_ON(crtc->state->event); + spin_lock(&crtc->commit_lock); + complete_all(&commit->hw_done); + spin_unlock(&crtc->commit_lock); + } +} +EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done); + +void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i; + long ret; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + commit = state->crtcs[i].commit; + if (!commit) + continue; + spin_lock(&crtc->commit_lock); + complete_all(&commit->cleanup_done); + + WARN_ON(!try_wait_for_completion(&commit->hw_done)); + + /* commit_list borrows our reference, need to remove before we + * clean up our drm_atomic_state. */ + list_del(&commit->commit_entry); + spin_unlock(&crtc->commit_lock); + + /* We must wait for the vblank event to signal our completion + * before releasing our reference, since the vblank work does + * not hold a reference of its own. */ + ret = wait_for_completion_timeout(&commit->flip_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n", + crtc->base.id, crtc->name); + } +} +EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done); + /** * drm_atomic_helper_prepare_planes - prepare plane resources before commit * @dev: DRM device @@ -1553,17 +1829,44 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes); * * 5. Call drm_atomic_helper_cleanup_planes() with @state, which since step 3 * contains the old state. Also do any other cleanup required with that state. + * + * @stall must be set when nonblocking commits for this driver directly access + * the ->state pointer of &drm_plane, &drm_crtc or &drm_connector. With the + * current atomic helpers this is almost always the case, since the helpers + * don't pass the right state structures to the callbacks. */ void drm_atomic_helper_swap_state(struct drm_atomic_state *state, bool stall) { int i; + long ret; struct drm_connector *connector; struct drm_connector_state *conn_state; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; struct drm_plane *plane; struct drm_plane_state *plane_state; + struct drm_crtc_commit *commit; + + if (stall) { + for_each_crtc_in_state(state, crtc, crtc_state, i) { + spin_lock(&crtc->commit_lock); + commit = preceeding_commit(crtc); + if (commit) + drm_crtc_commit_get(commit); + spin_unlock(&crtc->commit_lock); + + if (!commit) + continue; + + ret = wait_for_completion_timeout(&commit->hw_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] hw_done timed out\n", + crtc->base.id, crtc->name); + drm_crtc_commit_put(commit); + } + }
for_each_connector_in_state(state, connector, conn_state, i) { connector->state->state = state; @@ -1575,6 +1878,15 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state, crtc->state->state = state; swap(state->crtcs[i].state, crtc->state); crtc->state->state = NULL; + + if (state->crtcs[i].commit) { + spin_lock(&crtc->commit_lock); + list_add(&state->crtcs[i].commit->commit_entry, + &crtc->commit_list); + spin_unlock(&crtc->commit_lock); + + state->crtcs[i].commit->event = NULL; + } }
for_each_plane_in_state(state, plane, plane_state, i) { diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 88cf22052a69..3e52a6ecf6c0 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -669,6 +669,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, crtc->dev = dev; crtc->funcs = funcs;
+ INIT_LIST_HEAD(&crtc->commit_list); + spin_lock_init(&crtc->commit_lock); + drm_modeset_lock_init(&crtc->mutex); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 4c4b30f7a9f2..5921b203503a 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -686,6 +686,12 @@ void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e) { assert_spin_locked(&dev->event_lock);
+ if (e->completion) { + /* ->completion might disappear as soon as it signalled. */ + complete_all(e->completion); + e->completion = NULL; + } + if (e->fence) { fence_signal(e->fence); fence_put(e->fence); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 51f751d1c8a4..781db4f562d4 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -283,6 +283,7 @@ struct drm_ioctl_desc {
/* Event queued up for userspace to read */ struct drm_pending_event { + struct completion *completion; struct drm_event *event; struct fence *fence; struct list_head link; diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 465a1212f4f0..abec2a3f0225 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -30,6 +30,12 @@
#include <drm/drm_crtc.h>
+void drm_crtc_commit_put(struct drm_crtc_commit *commit); +static inline void drm_crtc_commit_get(struct drm_crtc_commit *commit) +{ + kref_get(&commit->ref); +} + struct drm_atomic_state * __must_check drm_atomic_state_alloc(struct drm_device *dev); void drm_atomic_state_clear(struct drm_atomic_state *state); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 0276447225ed..5fb8c306e16b 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -38,6 +38,7 @@ int drm_atomic_helper_check_planes(struct drm_device *dev, struct drm_atomic_state *state); int drm_atomic_helper_check(struct drm_device *dev, struct drm_atomic_state *state); +void drm_atomic_helper_commit_tail(struct drm_atomic_state *state); int drm_atomic_helper_commit(struct drm_device *dev, struct drm_atomic_state *state, bool nonblock); @@ -74,6 +75,13 @@ void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, void drm_atomic_helper_swap_state(struct drm_atomic_state *state, bool stall);
+/* nonblocking commit helpers */ +int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, + bool nonblock); +void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state); +void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state); +void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state); + /* implementations for legacy interfaces */ int drm_atomic_helper_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index d5d5e343531e..c0edd87fa0ee 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -725,9 +725,6 @@ struct drm_crtc_funcs { * @gamma_store: gamma ramp values * @helper_private: mid-layer private data * @properties: property tracking for this CRTC - * @state: current atomic state for this CRTC - * @acquire_ctx: per-CRTC implicit acquire context used by atomic drivers for - * legacy IOCTLs * * Each CRTC may have one or more connectors associated with it. This structure * allows the CRTC to be controlled. @@ -780,11 +777,37 @@ struct drm_crtc {
struct drm_object_properties properties;
+ /** + * @state: + * + * Current atomic state for this CRTC. + */ struct drm_crtc_state *state;
- /* - * For legacy crtc IOCTLs so that atomic drivers can get at the locking - * acquire context. + /** + * @commit_list: + * + * List of &drm_crtc_commit structures tracking pending commits. + * Protected by @commit_lock. This list doesn't hold its own full + * reference, but burrows it from the ongoing commit. Commit entries + * must be removed from this list once the commit is fully completed, + * but before it's correspoding &drm_atomic_state gets destroyed. + */ + struct list_head commit_list; + + /** + * @commit_lock: + * + * Spinlock to protect @commit_list. + */ + spinlock_t commit_lock; + + /** + * @acquire_ctx: + * + * Per-CRTC implicit acquire context used by atomic drivers for legacy + * IOCTLs, so that atomic drivers can get at the locking acquire + * context. */ struct drm_modeset_acquire_ctx *acquire_ctx; }; @@ -1694,6 +1717,78 @@ struct drm_bridge { void *driver_private; };
+/** + * struct drm_crtc_commit - track modeset commits on a CRTC + */ +struct drm_crtc_commit { + /** + * @crtc: + * + * DRM CRTC for this commit. + */ + struct drm_crtc *crtc; + + /** + * @ref: + * + * Reference count for this structure. Needed to allow blocking on + * completions without the risk of the completion disappearing + * meanwhile. + */ + struct kref ref; + + /** + * @flip_done: + * + * Will be signaled when the hardware has flipped to the new set of + * buffers. Signals at the same time as when the drm event for this + * commit is sent to userspace, or when an out-fence is singalled. Note + * that for most hardware, in most cases this happens after @hw_done is + * signalled. + */ + struct completion flip_done; + + /** + * @hw_done: + * + * Will be signalled when all hw register changes for this commit have + * been written out. Especially when disabling a pipe this can be much + * later than than @flip_done, since that can signal already when the + * screen goes black, whereas to fully shut down a pipe more register + * I/O is required. + * + * Note that this does not need to include separately reference-counted + * resources like backing storage buffer pinning, or runtime pm + * management. + */ + struct completion hw_done; + + /** + * cleanup_done: + * + * Will be signalled after old buffers have been cleaned up again by + * calling drm_atomic_helper_cleanup_planes(). Since this can only + * happen after a vblank wait completed it might be a bit later. This + * completion is useful to throttle updates and avoid hardware updates + * getting ahead of the buffer cleanup too much. + */ + struct completion cleanup_done; + + /** + * @commit_entry: + * + * Entry on the per-CRTC commit_list. Protected by crtc->commit_lock. + */ + struct list_head commit_entry; + + /** + * @event: + * + * &drm_pending_vblank_event pointer to clean up private events. + */ + struct drm_pending_vblank_event *event; +}; + struct __drm_planes_state { struct drm_plane *ptr; struct drm_plane_state *state; @@ -1702,6 +1797,7 @@ struct __drm_planes_state { struct __drm_crtcs_state { struct drm_crtc *ptr; struct drm_crtc_state *state; + struct drm_crtc_commit *commit; };
struct __drm_connnectors_state { @@ -1733,6 +1829,14 @@ struct drm_atomic_state { struct __drm_connnectors_state *connectors;
struct drm_modeset_acquire_ctx *acquire_ctx; + + /** + * @commit_work: + * + * Work item which can be used by the driver or helpers to execute the + * commit without blocking. + */ + struct work_struct commit_work; };
@@ -2074,6 +2178,7 @@ struct drm_mode_config_funcs { * @async_page_flip: does this device support async flips on the primary plane? * @cursor_width: hint to userspace for max cursor width * @cursor_height: hint to userspace for max cursor height + * @helper_private: mid-layer private data * * Core mode resource tracking structure. All CRTC, encoders, and connectors * enumerated by the driver are added here, as are global properties. Some @@ -2193,6 +2298,8 @@ struct drm_mode_config {
/* cursor size */ uint32_t cursor_width, cursor_height; + + struct drm_mode_config_helper_funcs *helper_private; };
/** diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index d4619dc2eecb..145c147705d8 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -925,4 +925,40 @@ static inline void drm_plane_helper_add(struct drm_plane *plane, plane->helper_private = funcs; }
+/** + * struct drm_mode_config_helper_funcs - global modeset helper operations + * + * These helper functions are used by the atomic helpers. + */ +struct drm_mode_config_helper_funcs { + /** + * @atomic_commit_tail: + * + * This hook should commit the given atomic state to the hardware, but + * nothing else. This excludes specifically any prepare and cleanup work + * done by drm_atomic_helper_prepare_planes() and + * drm_atomic_helper_cleanup_planes(). And also any synchronization and + * stalling necessary around the around the hardware commit as done for + * example by drm_atomic_helper_wait_for_fences() or + * drm_atomic_helper_wait_for_vblanks(). + * + * The function is not required to stall for the commit to actually + * complete and for the new buffers to be displayed on the screen. This + * is handled by properly signalling the &drm_pending_vblank_event in each + * &drm_crtc_state through a call to drm_send_vblank_event() or + * drm_arm_vblank_event(), depending upon how the driver works. + * + * When disabling a CRTC otoh this hook _must_ stall for the commit to + * complete. Vblank waits don't work on disabled CRTC, hence the core + * can't take care of this. And it also can't rely on the vblank event, + * since that can be signalled already when the screen shows black, + * which can happen much earlier than the last hardware access needed to + * shut off the display pipeline completely. + * + * This hook is optional, the default implementation is + * drm_atomic_helper_commit_tail(). + */ + void (*atomic_commit_tail)(struct drm_atomic_state *state); +}; + #endif
Design ideas:
- split up the actual commit into different phases, and have completions for each of them. This will be useful for the future when we want to interleave phases much more aggressively, for e.g. queue depth > 1. For not it's just a minimal optimization compared to current common nonblocking implementation patterns from drivers, which all stall for the entire commit to complete, including vblank waits and cleanups.
- Extract a separate atomic_commit_hw hook since that's the part most drivers will need to overwrite, hopefully allowing even more shared code.
- Enforce EBUSY seamntics by attaching one of the completions to the flip_done vblank event. Side benefit of forcing atomic drivers using these helpers to implement event handlign at least semi-correct. I'm evil that way ;-)
- Ridiculously modular, as usual.
- The main tracking unit for a commit stays struct drm_atomic_state, and the ownership rules for that are unchanged. Ownership still gets transferred to the driver (and subsequently to the worker) on successful commits. What is added is a small, per-crtc, refcounted structure to track pending commits called struct drm_crtc_commit. No actual state is attached to that though, it's purely for ordering and waiting.
- Dependencies are implicitly handled by assuming that any CRTC part of &drm_atomic_state is a dependency, and that the current commit must wait for any commits to complete on those CRTC. This way drivers can easily add more depencies using drm_atomic_get_crtc_state(), which is very natural since in most case a dependency exists iff there's some bit of state that needs to be cross checked.
Removing depencies is not possible, drivers simply need to be careful to not include every CRTC in a commit if that's not necessary. Which is a good idea anyway, since that also avoids ww_mutex lock contention.
- Queue depth > 1 sees some prep work in this patch by adding a stall paramater to drm_atomic_helper_swap_states(). To be able to push commits entirely free-standing and in a deeper queue through the back-end the driver must not access any obj->state pointers. This means we need to track the old state in drm_atomic_state (much easier with the consolidated arrays), and pass them all explicitly to driver backends (this will be serious amounts of churn).
Once that's done stall can be set to false in swap_states.
Features: Contains bugs because totally untested.
v2: Dont ask for flip_done signalling when the CRTC is off and stays off: Drivers don't handle events in that case. Instead complete right away. This way future commits don't need to have special-case logic, but can keep blocking for the flip_done completion.
v3: Tons of fixes: - Stall for preceeding commit for real, not the current one by accident. - Add WARN_ON in case drivers don't fire the drm event. - Don't double-free drm events.
v4: Make legacy cursor not stall.
v5: Extend the helper hook to cover the entire commit tail. Some drivers need special code for cleanup and vblank waiting, this makes it a bit more useful. Inspired by the rockchip driver.
v6: Add WARN_ON to catch drivers who forget to send out the drm event.
v7: Fixup the stalls in swap_state for real!!
Cc: Tomeu Vizoso tomeu.vizoso@gmail.com Cc: Daniel Stone daniels@collabora.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_atomic.c | 22 ++ drivers/gpu/drm/drm_atomic_helper.c | 367 ++++++++++++++++++++++++++++--- drivers/gpu/drm/drm_crtc.c | 3 + drivers/gpu/drm/drm_fops.c | 6 + include/drm/drmP.h | 1 + include/drm/drm_atomic.h | 6 + include/drm/drm_atomic_helper.h | 8 + include/drm/drm_crtc.h | 119 +++++++++- include/drm/drm_modeset_helper_vtables.h | 36 +++ 9 files changed, 535 insertions(+), 33 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 1db198df3014..50aa341ca9ff 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -33,6 +33,20 @@
#include "drm_crtc_internal.h"
+static void crtc_commit_free(struct kref *kref) +{ + struct drm_crtc_commit *commit = + container_of(kref, struct drm_crtc_commit, ref); + + kfree(commit); +} + +void drm_crtc_commit_put(struct drm_crtc_commit *commit) +{ + kref_put(&commit->ref, crtc_commit_free); +} +EXPORT_SYMBOL(drm_crtc_commit_put); + /** * drm_atomic_state_default_release - * release memory initialized by drm_atomic_state_init @@ -148,6 +162,14 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
crtc->funcs->atomic_destroy_state(crtc, state->crtcs[i].state); + + if (state->crtcs[i].commit) { + kfree(state->crtcs[i].commit->event); + state->crtcs[i].commit->event = NULL; + drm_crtc_commit_put(state->crtcs[i].commit); + } + + state->crtcs[i].commit = NULL; state->crtcs[i].ptr = NULL; state->crtcs[i].state = NULL; } diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index fecbb52cbb85..0389917c5dfd 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1113,20 +1113,15 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
/** - * drm_atomic_helper_commit - commit validated state object - * @dev: DRM device - * @state: the driver state object - * @nonblocking: whether nonblocking behavior is requested. + * drm_atomic_helper_commit_tail - commit atomic update to hardware + * @state: new modeset state to be committed * - * This function commits a with drm_atomic_helper_check() pre-validated state - * object. This can still fail when e.g. the framebuffer reservation fails. For - * now this doesn't implement nonblocking commits. + * This is the default implemenation for the ->atomic_commit_tail() hook of the + * &drm_mode_config_helper_funcs vtable. * - * Note that right now this function does not support nonblocking commits, hence - * driver writers must implement their own version for now. Also note that the - * default ordering of how the various stages are called is to match the legacy - * modeset helper library closest. One peculiarity of that is that it doesn't - * mesh well with runtime PM at all. + * Note that the default ordering of how the various stages are called is to + * match the legacy modeset helper library closest. One peculiarity of that is + * that it doesn't mesh well with runtime PM at all. * * For drivers supporting runtime PM the recommended sequence is * @@ -1137,6 +1132,70 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); * drm_atomic_helper_commit_planes(dev, state, true); * * See the kerneldoc entries for these three functions for more details. + */ +void drm_atomic_helper_commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + + drm_atomic_helper_commit_modeset_disables(dev, state); + + drm_atomic_helper_commit_planes(dev, state, false); + + drm_atomic_helper_commit_modeset_enables(dev, state); + + drm_atomic_helper_commit_hw_done(state); + + drm_atomic_helper_wait_for_vblanks(dev, state); + + drm_atomic_helper_cleanup_planes(dev, state); +} +EXPORT_SYMBOL(drm_atomic_helper_commit_tail); + +void commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + struct drm_mode_config_helper_funcs *funcs; + + funcs = dev->mode_config.helper_private; + + drm_atomic_helper_wait_for_fences(dev, state); + + drm_atomic_helper_wait_for_dependencies(state); + + if (funcs && funcs->atomic_commit_tail) + funcs->atomic_commit_tail(state); + else + drm_atomic_helper_commit_tail(state); + + drm_atomic_helper_commit_cleanup_done(state); + + drm_atomic_state_free(state); +} + +static void commit_work(struct work_struct *work) +{ + struct drm_atomic_state *state = container_of(work, + struct drm_atomic_state, + commit_work); + commit_tail(state); +} + +/** + * drm_atomic_helper_commit - commit validated state object + * @dev: DRM device + * @state: the driver state object + * @nonblocking: whether nonblocking behavior is requested. + * + * This function commits a with drm_atomic_helper_check() pre-validated state + * object. This can still fail when e.g. the framebuffer reservation fails. For + * now this doesn't implement nonblocking commits. + * + * Note that right now this function does not support nonblocking commits, hence + * driver writers must implement their own version for now. + * + * Committing the actual hardware state is done through the + * ->atomic_commit_tail() callback of the &drm_mode_config_helper_funcs vtable, + * or it's default implementation drm_atomic_helper_commit_tail(). * * RETURNS * Zero for success or -errno. @@ -1147,8 +1206,11 @@ int drm_atomic_helper_commit(struct drm_device *dev, { int ret;
- if (nonblock) - return -EBUSY; + ret = drm_atomic_helper_setup_commit(state, nonblock); + if (ret) + return ret; + + INIT_WORK(&state->commit_work, commit_work);
ret = drm_atomic_helper_prepare_planes(dev, state); if (ret) @@ -1176,21 +1238,16 @@ int drm_atomic_helper_commit(struct drm_device *dev, * update. Which is important since compositors need to figure out the * composition of the next frame right after having submitted the * current layout. + * + * NOTE: Commit work has multiple phases, first hardware commit, then + * cleanup. We want them to overlap, hence need system_unbound_wq to + * make sure work items don't artifically stall on each another. */
- drm_atomic_helper_wait_for_fences(dev, state); - - drm_atomic_helper_commit_modeset_disables(dev, state); - - drm_atomic_helper_commit_planes(dev, state, false); - - drm_atomic_helper_commit_modeset_enables(dev, state); - - drm_atomic_helper_wait_for_vblanks(dev, state); - - drm_atomic_helper_cleanup_planes(dev, state); - - drm_atomic_state_free(state); + if (nonblock) + queue_work(system_unbound_wq, &state->commit_work); + else + commit_tail(state);
return 0; } @@ -1234,6 +1291,225 @@ EXPORT_SYMBOL(drm_atomic_helper_commit); * being displayed. */
+static int stall_checks(struct drm_crtc *crtc, bool nonblock) +{ + struct drm_crtc_commit *commit, *stall_commit = NULL; + bool completed = true; + int i, ret = 0; + + spin_lock(&crtc->commit_lock); + i = 0; + list_for_each_entry(commit, &crtc->commit_list, commit_entry) { + if (i == 0) { + completed = try_wait_for_completion(&commit->flip_done); + /* Userspace is not allowed to get ahead of the previous + * commit with nonblocking ones. */ + if (!completed && nonblock) { + spin_unlock(&crtc->commit_lock); + return -EBUSY; + } + } else if (i == 1) { + stall_commit = commit; + drm_crtc_commit_get(stall_commit); + } else + break; + + i++; + } + spin_unlock(&crtc->commit_lock); + + if (!stall_commit) + return 0; + + /* We don't want to let commits get ahead of cleanup work too much, + * stalling on 2nd previous commit means triple-buffer won't ever stall. + */ + ret = wait_for_completion_interruptible_timeout(&commit->cleanup_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] cleanup_done timed out\n", + crtc->base.id, crtc->name); + + if (ret == -ERESTARTSYS) + ret = -EINTR; + + drm_crtc_commit_put(stall_commit); + + return ret; +} + +/** + * drm_atomic_helper_setup_commit - setup possible nonblocking commit + * + * Returns: + * + * 0 on success. -EBUSY when userspace schedules nonblocking commits too fast, + * -ENOMEM on allocation failures and -EINTR when a signal is pending. + */ +int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, + bool nonblock) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i, ret; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + commit = kzalloc(sizeof(*commit), GFP_KERNEL); + if (!commit) + return -ENOMEM; + + init_completion(&commit->flip_done); + init_completion(&commit->hw_done); + init_completion(&commit->cleanup_done); + INIT_LIST_HEAD(&commit->commit_entry); + kref_init(&commit->ref); + commit->crtc = crtc; + + state->crtcs[i].commit = commit; + + ret = stall_checks(crtc, nonblock); + if (ret) + return ret; + + /* Drivers only send out events when at least either current or + * new CRTC state is active. Complete right away if everything + * stays off. */ + if (!crtc->state->active && !crtc_state->active) { + complete_all(&commit->flip_done); + continue; + } + + /* Legacy cursor updates are fully unsynced. */ + if (state->legacy_cursor_update) { + complete_all(&commit->flip_done); + continue; + } + + if (!crtc_state->event) { + commit->event = kzalloc(sizeof(*commit->event), + GFP_KERNEL); + if (!commit->event) + return -ENOMEM; + + crtc_state->event = commit->event; + } + + crtc_state->event->base.completion = &commit->flip_done; + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_helper_setup_commit); + + +static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc) +{ + struct drm_crtc_commit *commit; + int i = 0; + + list_for_each_entry(commit, &crtc->commit_list, commit_entry) { + /* skip the first entry, that's the current commit */ + if (i == 1) + return commit; + i++; + } + + return NULL; +} + +void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i; + long ret; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + spin_lock(&crtc->commit_lock); + commit = preceeding_commit(crtc); + if (commit) + drm_crtc_commit_get(commit); + spin_unlock(&crtc->commit_lock); + + if (!commit) + continue; + + ret = wait_for_completion_timeout(&commit->hw_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] hw_done timed out\n", + crtc->base.id, crtc->name); + + /* Currently no support for overwriting flips, hence + * stall for previous one to execute completely. */ + ret = wait_for_completion_timeout(&commit->flip_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n", + crtc->base.id, crtc->name); + + drm_crtc_commit_put(commit); + } +} +EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies); + +void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + commit = state->crtcs[i].commit; + if (!commit) + continue; + + /* backend must have consumed any event by now */ + WARN_ON(crtc->state->event); + spin_lock(&crtc->commit_lock); + complete_all(&commit->hw_done); + spin_unlock(&crtc->commit_lock); + } +} +EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done); + +void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_crtc_commit *commit; + int i; + long ret; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + commit = state->crtcs[i].commit; + if (!commit) + continue; + spin_lock(&crtc->commit_lock); + complete_all(&commit->cleanup_done); + + WARN_ON(!try_wait_for_completion(&commit->hw_done)); + + /* commit_list borrows our reference, need to remove before we + * clean up our drm_atomic_state. */ + list_del(&commit->commit_entry); + spin_unlock(&crtc->commit_lock); + + /* We must wait for the vblank event to signal our completion + * before releasing our reference, since the vblank work does + * not hold a reference of its own. */ + ret = wait_for_completion_timeout(&commit->flip_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n", + crtc->base.id, crtc->name); + } +} +EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done); + /** * drm_atomic_helper_prepare_planes - prepare plane resources before commit * @dev: DRM device @@ -1553,17 +1829,45 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes); * * 5. Call drm_atomic_helper_cleanup_planes() with @state, which since step 3 * contains the old state. Also do any other cleanup required with that state. + * + * @stall must be set when nonblocking commits for this driver directly access + * the ->state pointer of &drm_plane, &drm_crtc or &drm_connector. With the + * current atomic helpers this is almost always the case, since the helpers + * don't pass the right state structures to the callbacks. */ void drm_atomic_helper_swap_state(struct drm_atomic_state *state, bool stall) { int i; + long ret; struct drm_connector *connector; struct drm_connector_state *conn_state; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; struct drm_plane *plane; struct drm_plane_state *plane_state; + struct drm_crtc_commit *commit; + + if (stall) { + for_each_crtc_in_state(state, crtc, crtc_state, i) { + spin_lock(&crtc->commit_lock); + commit = list_first_entry_or_null(&crtc->commit_list, + struct drm_crtc_commit, commit_entry); + if (commit) + drm_crtc_commit_get(commit); + spin_unlock(&crtc->commit_lock); + + if (!commit) + continue; + + ret = wait_for_completion_timeout(&commit->hw_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CRTC:%d:%s] hw_done timed out\n", + crtc->base.id, crtc->name); + drm_crtc_commit_put(commit); + } + }
for_each_connector_in_state(state, connector, conn_state, i) { connector->state->state = state; @@ -1575,6 +1879,15 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state, crtc->state->state = state; swap(state->crtcs[i].state, crtc->state); crtc->state->state = NULL; + + if (state->crtcs[i].commit) { + spin_lock(&crtc->commit_lock); + list_add(&state->crtcs[i].commit->commit_entry, + &crtc->commit_list); + spin_unlock(&crtc->commit_lock); + + state->crtcs[i].commit->event = NULL; + } }
for_each_plane_in_state(state, plane, plane_state, i) { diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 88cf22052a69..3e52a6ecf6c0 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -669,6 +669,9 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, crtc->dev = dev; crtc->funcs = funcs;
+ INIT_LIST_HEAD(&crtc->commit_list); + spin_lock_init(&crtc->commit_lock); + drm_modeset_lock_init(&crtc->mutex); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 4c4b30f7a9f2..5921b203503a 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -686,6 +686,12 @@ void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e) { assert_spin_locked(&dev->event_lock);
+ if (e->completion) { + /* ->completion might disappear as soon as it signalled. */ + complete_all(e->completion); + e->completion = NULL; + } + if (e->fence) { fence_signal(e->fence); fence_put(e->fence); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 51f751d1c8a4..781db4f562d4 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -283,6 +283,7 @@ struct drm_ioctl_desc {
/* Event queued up for userspace to read */ struct drm_pending_event { + struct completion *completion; struct drm_event *event; struct fence *fence; struct list_head link; diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 465a1212f4f0..abec2a3f0225 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -30,6 +30,12 @@
#include <drm/drm_crtc.h>
+void drm_crtc_commit_put(struct drm_crtc_commit *commit); +static inline void drm_crtc_commit_get(struct drm_crtc_commit *commit) +{ + kref_get(&commit->ref); +} + struct drm_atomic_state * __must_check drm_atomic_state_alloc(struct drm_device *dev); void drm_atomic_state_clear(struct drm_atomic_state *state); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 0276447225ed..5fb8c306e16b 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -38,6 +38,7 @@ int drm_atomic_helper_check_planes(struct drm_device *dev, struct drm_atomic_state *state); int drm_atomic_helper_check(struct drm_device *dev, struct drm_atomic_state *state); +void drm_atomic_helper_commit_tail(struct drm_atomic_state *state); int drm_atomic_helper_commit(struct drm_device *dev, struct drm_atomic_state *state, bool nonblock); @@ -74,6 +75,13 @@ void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, void drm_atomic_helper_swap_state(struct drm_atomic_state *state, bool stall);
+/* nonblocking commit helpers */ +int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, + bool nonblock); +void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state); +void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state); +void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state); + /* implementations for legacy interfaces */ int drm_atomic_helper_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index d5d5e343531e..c0edd87fa0ee 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -725,9 +725,6 @@ struct drm_crtc_funcs { * @gamma_store: gamma ramp values * @helper_private: mid-layer private data * @properties: property tracking for this CRTC - * @state: current atomic state for this CRTC - * @acquire_ctx: per-CRTC implicit acquire context used by atomic drivers for - * legacy IOCTLs * * Each CRTC may have one or more connectors associated with it. This structure * allows the CRTC to be controlled. @@ -780,11 +777,37 @@ struct drm_crtc {
struct drm_object_properties properties;
+ /** + * @state: + * + * Current atomic state for this CRTC. + */ struct drm_crtc_state *state;
- /* - * For legacy crtc IOCTLs so that atomic drivers can get at the locking - * acquire context. + /** + * @commit_list: + * + * List of &drm_crtc_commit structures tracking pending commits. + * Protected by @commit_lock. This list doesn't hold its own full + * reference, but burrows it from the ongoing commit. Commit entries + * must be removed from this list once the commit is fully completed, + * but before it's correspoding &drm_atomic_state gets destroyed. + */ + struct list_head commit_list; + + /** + * @commit_lock: + * + * Spinlock to protect @commit_list. + */ + spinlock_t commit_lock; + + /** + * @acquire_ctx: + * + * Per-CRTC implicit acquire context used by atomic drivers for legacy + * IOCTLs, so that atomic drivers can get at the locking acquire + * context. */ struct drm_modeset_acquire_ctx *acquire_ctx; }; @@ -1694,6 +1717,78 @@ struct drm_bridge { void *driver_private; };
+/** + * struct drm_crtc_commit - track modeset commits on a CRTC + */ +struct drm_crtc_commit { + /** + * @crtc: + * + * DRM CRTC for this commit. + */ + struct drm_crtc *crtc; + + /** + * @ref: + * + * Reference count for this structure. Needed to allow blocking on + * completions without the risk of the completion disappearing + * meanwhile. + */ + struct kref ref; + + /** + * @flip_done: + * + * Will be signaled when the hardware has flipped to the new set of + * buffers. Signals at the same time as when the drm event for this + * commit is sent to userspace, or when an out-fence is singalled. Note + * that for most hardware, in most cases this happens after @hw_done is + * signalled. + */ + struct completion flip_done; + + /** + * @hw_done: + * + * Will be signalled when all hw register changes for this commit have + * been written out. Especially when disabling a pipe this can be much + * later than than @flip_done, since that can signal already when the + * screen goes black, whereas to fully shut down a pipe more register + * I/O is required. + * + * Note that this does not need to include separately reference-counted + * resources like backing storage buffer pinning, or runtime pm + * management. + */ + struct completion hw_done; + + /** + * cleanup_done: + * + * Will be signalled after old buffers have been cleaned up again by + * calling drm_atomic_helper_cleanup_planes(). Since this can only + * happen after a vblank wait completed it might be a bit later. This + * completion is useful to throttle updates and avoid hardware updates + * getting ahead of the buffer cleanup too much. + */ + struct completion cleanup_done; + + /** + * @commit_entry: + * + * Entry on the per-CRTC commit_list. Protected by crtc->commit_lock. + */ + struct list_head commit_entry; + + /** + * @event: + * + * &drm_pending_vblank_event pointer to clean up private events. + */ + struct drm_pending_vblank_event *event; +}; + struct __drm_planes_state { struct drm_plane *ptr; struct drm_plane_state *state; @@ -1702,6 +1797,7 @@ struct __drm_planes_state { struct __drm_crtcs_state { struct drm_crtc *ptr; struct drm_crtc_state *state; + struct drm_crtc_commit *commit; };
struct __drm_connnectors_state { @@ -1733,6 +1829,14 @@ struct drm_atomic_state { struct __drm_connnectors_state *connectors;
struct drm_modeset_acquire_ctx *acquire_ctx; + + /** + * @commit_work: + * + * Work item which can be used by the driver or helpers to execute the + * commit without blocking. + */ + struct work_struct commit_work; };
@@ -2074,6 +2178,7 @@ struct drm_mode_config_funcs { * @async_page_flip: does this device support async flips on the primary plane? * @cursor_width: hint to userspace for max cursor width * @cursor_height: hint to userspace for max cursor height + * @helper_private: mid-layer private data * * Core mode resource tracking structure. All CRTC, encoders, and connectors * enumerated by the driver are added here, as are global properties. Some @@ -2193,6 +2298,8 @@ struct drm_mode_config {
/* cursor size */ uint32_t cursor_width, cursor_height; + + struct drm_mode_config_helper_funcs *helper_private; };
/** diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index d4619dc2eecb..145c147705d8 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -925,4 +925,40 @@ static inline void drm_plane_helper_add(struct drm_plane *plane, plane->helper_private = funcs; }
+/** + * struct drm_mode_config_helper_funcs - global modeset helper operations + * + * These helper functions are used by the atomic helpers. + */ +struct drm_mode_config_helper_funcs { + /** + * @atomic_commit_tail: + * + * This hook should commit the given atomic state to the hardware, but + * nothing else. This excludes specifically any prepare and cleanup work + * done by drm_atomic_helper_prepare_planes() and + * drm_atomic_helper_cleanup_planes(). And also any synchronization and + * stalling necessary around the around the hardware commit as done for + * example by drm_atomic_helper_wait_for_fences() or + * drm_atomic_helper_wait_for_vblanks(). + * + * The function is not required to stall for the commit to actually + * complete and for the new buffers to be displayed on the screen. This + * is handled by properly signalling the &drm_pending_vblank_event in each + * &drm_crtc_state through a call to drm_send_vblank_event() or + * drm_arm_vblank_event(), depending upon how the driver works. + * + * When disabling a CRTC otoh this hook _must_ stall for the commit to + * complete. Vblank waits don't work on disabled CRTC, hence the core + * can't take care of this. And it also can't rely on the vblank event, + * since that can be signalled already when the screen shows black, + * which can happen much earlier than the last hardware access needed to + * shut off the display pipeline completely. + * + * This hook is optional, the default implementation is + * drm_atomic_helper_commit_tail(). + */ + void (*atomic_commit_tail)(struct drm_atomic_state *state); +}; + #endif
Op 30-05-16 om 10:01 schreef Daniel Vetter:
Design ideas:
split up the actual commit into different phases, and have completions for each of them. This will be useful for the future when we want to interleave phases much more aggressively, for e.g. queue depth > 1. For not it's just a minimal optimization compared to current common nonblocking implementation patterns from drivers, which all stall for the entire commit to complete, including vblank waits and cleanups.
Extract a separate atomic_commit_hw hook since that's the part most drivers will need to overwrite, hopefully allowing even more shared code.
Enforce EBUSY seamntics by attaching one of the completions to the flip_done vblank event. Side benefit of forcing atomic drivers using these helpers to implement event handlign at least semi-correct. I'm evil that way ;-)
Ridiculously modular, as usual.
The main tracking unit for a commit stays struct drm_atomic_state, and the ownership rules for that are unchanged. Ownership still gets transferred to the driver (and subsequently to the worker) on successful commits. What is added is a small, per-crtc, refcounted structure to track pending commits called struct drm_crtc_commit. No actual state is attached to that though, it's purely for ordering and waiting.
Dependencies are implicitly handled by assuming that any CRTC part of &drm_atomic_state is a dependency, and that the current commit must wait for any commits to complete on those CRTC. This way drivers can easily add more depencies using drm_atomic_get_crtc_state(), which is very natural since in most case a dependency exists iff there's some bit of state that needs to be cross checked.
Removing depencies is not possible, drivers simply need to be careful to not include every CRTC in a commit if that's not necessary. Which is a good idea anyway, since that also avoids ww_mutex lock contention.
Queue depth > 1 sees some prep work in this patch by adding a stall paramater to drm_atomic_helper_swap_states(). To be able to push commits entirely free-standing and in a deeper queue through the back-end the driver must not access any obj->state pointers. This means we need to track the old state in drm_atomic_state (much easier with the consolidated arrays), and pass them all explicitly to driver backends (this will be serious amounts of churn).
Once that's done stall can be set to false in swap_states.
Features: Contains bugs because totally untested.
v2: Dont ask for flip_done signalling when the CRTC is off and stays off: Drivers don't handle events in that case. Instead complete right away. This way future commits don't need to have special-case logic, but can keep blocking for the flip_done completion.
v3: Tons of fixes:
- Stall for preceeding commit for real, not the current one by accident.
- Add WARN_ON in case drivers don't fire the drm event.
- Don't double-free drm events.
v4: Make legacy cursor not stall.
v5: Extend the helper hook to cover the entire commit tail. Some drivers need special code for cleanup and vblank waiting, this makes it a bit more useful. Inspired by the rockchip driver.
v6: Add WARN_ON to catch drivers who forget to send out the drm event.
v7: Fixup the stalls in swap_state for real!!
I don't think stalling belongs to swap_state, that should be a separate helper call.
When nonblocking = false the waiting is still performed uninterruptibly. I believe that this is an error, and all blocking waiting should be completed before calling swap_state to ensure -EINTR can be propagated correctly as much as possible. Perhaps also return -EBUSY if wait times out.
You specified a timeout of 10 HZ, why is that? If we wait interruptibly then there's no need for a timeout. I also think the timeout may be too short, if we commit 3 crtc's it leaves 3.3 seconds for each. In case there's a modeset enable and disable, that leaves 1.6 seconds for each enable/disable. Might be too short..
Patch is also a bit hard to review with so many lines changed, could this be done in pieces instead of all at once?
On Tue, May 31, 2016 at 04:22:57PM +0200, Maarten Lankhorst wrote:
Op 30-05-16 om 10:01 schreef Daniel Vetter:
Design ideas:
split up the actual commit into different phases, and have completions for each of them. This will be useful for the future when we want to interleave phases much more aggressively, for e.g. queue depth > 1. For not it's just a minimal optimization compared to current common nonblocking implementation patterns from drivers, which all stall for the entire commit to complete, including vblank waits and cleanups.
Extract a separate atomic_commit_hw hook since that's the part most drivers will need to overwrite, hopefully allowing even more shared code.
Enforce EBUSY seamntics by attaching one of the completions to the flip_done vblank event. Side benefit of forcing atomic drivers using these helpers to implement event handlign at least semi-correct. I'm evil that way ;-)
Ridiculously modular, as usual.
The main tracking unit for a commit stays struct drm_atomic_state, and the ownership rules for that are unchanged. Ownership still gets transferred to the driver (and subsequently to the worker) on successful commits. What is added is a small, per-crtc, refcounted structure to track pending commits called struct drm_crtc_commit. No actual state is attached to that though, it's purely for ordering and waiting.
Dependencies are implicitly handled by assuming that any CRTC part of &drm_atomic_state is a dependency, and that the current commit must wait for any commits to complete on those CRTC. This way drivers can easily add more depencies using drm_atomic_get_crtc_state(), which is very natural since in most case a dependency exists iff there's some bit of state that needs to be cross checked.
Removing depencies is not possible, drivers simply need to be careful to not include every CRTC in a commit if that's not necessary. Which is a good idea anyway, since that also avoids ww_mutex lock contention.
Queue depth > 1 sees some prep work in this patch by adding a stall paramater to drm_atomic_helper_swap_states(). To be able to push commits entirely free-standing and in a deeper queue through the back-end the driver must not access any obj->state pointers. This means we need to track the old state in drm_atomic_state (much easier with the consolidated arrays), and pass them all explicitly to driver backends (this will be serious amounts of churn).
Once that's done stall can be set to false in swap_states.
Features: Contains bugs because totally untested.
v2: Dont ask for flip_done signalling when the CRTC is off and stays off: Drivers don't handle events in that case. Instead complete right away. This way future commits don't need to have special-case logic, but can keep blocking for the flip_done completion.
v3: Tons of fixes:
- Stall for preceeding commit for real, not the current one by accident.
- Add WARN_ON in case drivers don't fire the drm event.
- Don't double-free drm events.
v4: Make legacy cursor not stall.
v5: Extend the helper hook to cover the entire commit tail. Some drivers need special code for cleanup and vblank waiting, this makes it a bit more useful. Inspired by the rockchip driver.
v6: Add WARN_ON to catch drivers who forget to send out the drm event.
v7: Fixup the stalls in swap_state for real!!
I don't think stalling belongs to swap_state, that should be a separate helper call.
Imo swap_state and stalling for hw_done is crucial that they're never split up. At least until we have queue depth > 1. Splitting it up just means there's more stuff drivers can get wrong, and I haven't found a single driver where there's a need to split things up. And I looked at all 17 of them. That's why I don't think splitting makes sense.
All the other helpers might seem like things are split up a lot, but for each separate helper function I'm adding here there's that absolutely needs that split to insert/replace a bit.
When nonblocking = false the waiting is still performed uninterruptibly. I believe that this is an error, and all blocking waiting should be completed before calling swap_state to ensure -EINTR can be propagated correctly as much as possible. Perhaps also return -EBUSY if wait times out.
The trouble with failing with -EBUSY is that drivers are broken in their drm even handling, which means they'll fail. By eventually continuing there's at least some chances that you'll see something on the screen.
Wrt interruptible waits, the stall check does wait interruptible. Everything else is imo part of the commit, which isn't allowed to fail/get interrupted. And for queue depth > 1 we'll actually move the stall for hw_done down into the worker.
You specified a timeout of 10 HZ, why is that? If we wait interruptibly then there's no need for a timeout. I also think the timeout may be too short, if we commit 3 crtc's it leaves 3.3 seconds for each. In case there's a modeset enable and disable, that leaves 1.6 seconds for each enable/disable. Might be too short..
Random value between "long enough to never be a problem in practice" and "short enought that it's still ok to wait it out on boot-up for the initial modeset". Note that wait_for_dependencies has another wait for hw_done, and that one is non-optional (and in the async worker).
Patch is also a bit hard to review with so many lines changed, could this be done in pieces instead of all at once?
The only bits I can split out is extracting commit_tail, and adding events to drm_atomic_commit->crtcs[]. If I split more that just means the code I add and the the places where it actually gets used are split up, which imo doesn't make sense since it's not longer a complete functional change on its own.
What do you want me to split out to make it easier to read? -Daniel
This is part of what atomic must implement. And it's also required to be able to use the helper nonblocking support.
v2: Always send out the drm event, remove the planes_changed check.
Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch --- drivers/gpu/drm/i915/intel_display.c | 13 ++++++++++--- drivers/gpu/drm/i915/intel_sprite.c | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d32274071393..66a254fa2a96 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13731,13 +13731,21 @@ static int intel_atomic_commit(struct drm_device *dev, bool modeset = needs_modeset(crtc->state); struct intel_crtc_state *pipe_config = to_intel_crtc_state(crtc->state); - bool update_pipe = !modeset && pipe_config->update_pipe;
if (modeset && crtc->state->active) { update_scanline_offset(to_intel_crtc(crtc)); dev_priv->display.crtc_enable(crtc); }
+ /* Complete events for now disable pipes here. */ + if (modeset && !crtc->state->active && crtc->state->event) { + spin_lock_irq(&dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&dev->event_lock); + + crtc->state->event = NULL; + } + if (!modeset) intel_pre_plane_update(to_intel_crtc_state(old_crtc_state));
@@ -13745,8 +13753,7 @@ static int intel_atomic_commit(struct drm_device *dev, drm_atomic_get_existing_plane_state(state, crtc->primary)) intel_fbc_enable(intel_crtc);
- if (crtc->state->active && - (crtc->state->planes_changed || update_pipe)) + if (crtc->state->active) drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
if (pipe_config->base.active && needs_vblank_wait(pipe_config)) diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 97b1a54eb09f..8864cc983ea2 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -166,6 +166,20 @@ void intel_pipe_update_end(struct intel_crtc *crtc, struct intel_flip_work *work
trace_i915_pipe_update_end(crtc, end_vbl_count, scanline_end);
+ /* We're still in the vblank-evade critical section, this can't race. + * Would be slightly nice to just grab the vblank count and arm the + * event outside of the critical section - the spinlock might spin for a + * while ... */ + if (crtc->base.state->event) { + WARN_ON(drm_crtc_vblank_get(&crtc->base) != 0); + + spin_lock(&crtc->base.dev->event_lock); + drm_crtc_arm_vblank_event(&crtc->base, crtc->base.state->event); + spin_unlock(&crtc->base.dev->event_lock); + + crtc->base.state->event = NULL; + } + local_irq_enable();
if (crtc->debug.start_vbl_count &&
Right now still all blocking, no worker anywhere to be seen.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/i915/intel_display.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 66a254fa2a96..4b81cffef24e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13654,6 +13654,10 @@ static int intel_atomic_commit(struct drm_device *dev, unsigned long put_domains[I915_MAX_PIPES] = {}; unsigned crtc_vblank_mask = 0;
+ ret = drm_atomic_helper_setup_commit(state, nonblock); + if (ret) + return ret; + ret = intel_atomic_prepare_commit(dev, state, nonblock); if (ret) { DRM_DEBUG_ATOMIC("Preparing state failed with %i\n", ret); @@ -13665,6 +13669,8 @@ static int intel_atomic_commit(struct drm_device *dev, dev_priv->wm.skl_results = intel_state->wm_results; intel_shared_dpll_commit(state);
+ drm_atomic_helper_wait_for_dependencies(state); + if (intel_state->modeset) { memcpy(dev_priv->min_pixclk, intel_state->min_pixclk, sizeof(intel_state->min_pixclk)); @@ -13760,7 +13766,7 @@ static int intel_atomic_commit(struct drm_device *dev, crtc_vblank_mask |= 1 << i; }
- /* FIXME: add subpixel order */ + drm_atomic_helper_commit_hw_done(state);
if (!state->legacy_cursor_update) intel_atomic_wait_for_vblanks(dev, dev_priv, crtc_vblank_mask); @@ -13795,6 +13801,8 @@ static int intel_atomic_commit(struct drm_device *dev, drm_atomic_helper_cleanup_planes(dev, state); mutex_unlock(&dev->struct_mutex);
+ drm_atomic_helper_commit_cleanup_done(state); + drm_atomic_state_free(state);
/* As one of the primary mmio accessors, KMS has a high likelihood
So totally untested ...
v2: Fixes from Tomeu.
v3: Send out vblank events correctly when shutting down a crtc for good. This is part of the atomic interface contract.
v4: Properly protect vop->event.
Cc: Tomeu Vizoso tomeu.vizoso@collabora.com Cc: Mark yao mark.yao@rock-chips.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 3 -- drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 10 ---- drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 72 ++++------------------------- drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 15 +++++- 4 files changed, 22 insertions(+), 78 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 09a4d429c0f0..2fac6799ceb2 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -145,9 +145,6 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags) if (!private) return -ENOMEM;
- mutex_init(&private->commit.lock); - INIT_WORK(&private->commit.work, rockchip_drm_atomic_work); - drm_dev->dev_private = private;
drm_mode_config_init(drm_dev); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 56f43a364c7f..7684503ff765 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -43,13 +43,6 @@ struct rockchip_crtc_funcs { void (*cancel_pending_vblank)(struct drm_crtc *crtc, struct drm_file *file_priv); };
-struct rockchip_atomic_commit { - struct work_struct work; - struct drm_atomic_state *state; - struct drm_device *dev; - struct mutex lock; -}; - struct rockchip_crtc_state { struct drm_crtc_state base; int output_type; @@ -68,11 +61,8 @@ struct rockchip_drm_private { struct drm_fb_helper fbdev_helper; struct drm_gem_object *fbdev_bo; const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC]; - - struct rockchip_atomic_commit commit; };
-void rockchip_drm_atomic_work(struct work_struct *work); int rockchip_register_crtc_funcs(struct drm_crtc *crtc, const struct rockchip_crtc_funcs *crtc_funcs); void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 5ea141dfae5b..9198ee1ddc87 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -229,87 +229,32 @@ rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_stat }
static void -rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit) +rockchip_atomic_commit_tail(struct drm_atomic_state *state) { - struct drm_atomic_state *state = commit->state; - struct drm_device *dev = commit->dev; + struct drm_device *dev = state->dev;
- /* - * TODO: do fence wait here. - */ - - /* - * Rockchip crtc support runtime PM, can't update display planes - * when crtc is disabled. - * - * drm_atomic_helper_commit comments detail that: - * For drivers supporting runtime PM the recommended sequence is - * - * drm_atomic_helper_commit_modeset_disables(dev, state); - * - * drm_atomic_helper_commit_modeset_enables(dev, state); - * - * drm_atomic_helper_commit_planes(dev, state, true); - * - * See the kerneldoc entries for these three functions for more details. - */ drm_atomic_helper_commit_modeset_disables(dev, state);
drm_atomic_helper_commit_modeset_enables(dev, state);
drm_atomic_helper_commit_planes(dev, state, true);
+ drm_atomic_helper_commit_hw_done(state); + rockchip_atomic_wait_for_complete(dev, state);
drm_atomic_helper_cleanup_planes(dev, state); - - drm_atomic_state_free(state); -} - -void rockchip_drm_atomic_work(struct work_struct *work) -{ - struct rockchip_atomic_commit *commit = container_of(work, - struct rockchip_atomic_commit, work); - - rockchip_atomic_commit_complete(commit); }
-int rockchip_drm_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, - bool nonblock) -{ - struct rockchip_drm_private *private = dev->dev_private; - struct rockchip_atomic_commit *commit = &private->commit; - int ret; - - ret = drm_atomic_helper_prepare_planes(dev, state); - if (ret) - return ret; - - /* serialize outstanding nonblocking commits */ - mutex_lock(&commit->lock); - flush_work(&commit->work); - - drm_atomic_helper_swap_state(state, true); - - commit->dev = dev; - commit->state = state; - - if (nonblock) - schedule_work(&commit->work); - else - rockchip_atomic_commit_complete(commit); - - mutex_unlock(&commit->lock); - - return 0; -} +struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = { + .atomic_commit_tail = rockchip_atomic_commit_tail, +};
static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = { .fb_create = rockchip_user_fb_create, .output_poll_changed = rockchip_drm_output_poll_changed, .atomic_check = drm_atomic_helper_check, - .atomic_commit = rockchip_drm_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, };
struct drm_framebuffer * @@ -340,4 +285,5 @@ void rockchip_drm_mode_config_init(struct drm_device *dev) dev->mode_config.max_height = 4096;
dev->mode_config.funcs = &rockchip_drm_mode_config_funcs; + dev->mode_config.helper_private = &rockchip_mode_config_helpers; } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 5567fb43e674..8cde1ea62978 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -553,6 +553,14 @@ static void vop_crtc_disable(struct drm_crtc *crtc) clk_disable(vop->aclk); clk_disable(vop->hclk); pm_runtime_put(vop->dev); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + + crtc->state->event = NULL; + } }
static void vop_plane_destroy(struct drm_plane *plane) @@ -1027,12 +1035,14 @@ static void vop_crtc_atomic_begin(struct drm_crtc *crtc, { struct vop *vop = to_vop(crtc);
+ spin_lock_irq(&crtc->dev->event_lock); if (crtc->state->event) { WARN_ON(drm_crtc_vblank_get(crtc) != 0);
vop->event = crtc->state->event; crtc->state->event = NULL; } + spin_unlock_irq(&crtc->dev->event_lock); }
static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = { @@ -1104,15 +1114,16 @@ static void vop_handle_vblank(struct vop *vop) return; }
+ spin_lock_irqsave(&drm->event_lock, flags); if (vop->event) { - spin_lock_irqsave(&drm->event_lock, flags);
drm_crtc_send_vblank_event(crtc, vop->event); drm_crtc_vblank_put(crtc); vop->event = NULL;
- spin_unlock_irqrestore(&drm->event_lock, flags); } + spin_unlock_irqrestore(&drm->event_lock, flags); + if (!completion_done(&vop->wait_update_complete)) complete(&vop->wait_update_complete); }
This is now handled by the core, drivers can totally ignore lifetime issues of drm events.
Cc: Tomeu Vizoso tomeu.vizoso@collabora.com Cc: Mark yao mark.yao@rock-chips.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 22 ---------------------- drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 - drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 20 -------------------- 3 files changed, 43 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index 2fac6799ceb2..2251121343e6 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -257,27 +257,6 @@ static int rockchip_drm_unload(struct drm_device *drm_dev) return 0; }
-static void rockchip_drm_crtc_cancel_pending_vblank(struct drm_crtc *crtc, - struct drm_file *file_priv) -{ - struct rockchip_drm_private *priv = crtc->dev->dev_private; - int pipe = drm_crtc_index(crtc); - - if (pipe < ROCKCHIP_MAX_CRTC && - priv->crtc_funcs[pipe] && - priv->crtc_funcs[pipe]->cancel_pending_vblank) - priv->crtc_funcs[pipe]->cancel_pending_vblank(crtc, file_priv); -} - -static void rockchip_drm_preclose(struct drm_device *dev, - struct drm_file *file_priv) -{ - struct drm_crtc *crtc; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - rockchip_drm_crtc_cancel_pending_vblank(crtc, file_priv); -} - void rockchip_drm_lastclose(struct drm_device *dev) { struct rockchip_drm_private *priv = dev->dev_private; @@ -303,7 +282,6 @@ static struct drm_driver rockchip_drm_driver = { DRIVER_PRIME | DRIVER_ATOMIC, .load = rockchip_drm_load, .unload = rockchip_drm_unload, - .preclose = rockchip_drm_preclose, .lastclose = rockchip_drm_lastclose, .get_vblank_counter = drm_vblank_no_hw_counter, .enable_vblank = rockchip_drm_crtc_enable_vblank, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 7684503ff765..005634484441 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -40,7 +40,6 @@ struct rockchip_crtc_funcs { int (*enable_vblank)(struct drm_crtc *crtc); void (*disable_vblank)(struct drm_crtc *crtc); void (*wait_for_update)(struct drm_crtc *crtc); - void (*cancel_pending_vblank)(struct drm_crtc *crtc, struct drm_file *file_priv); };
struct rockchip_crtc_state { diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 8cde1ea62978..37a1339c30ae 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -884,30 +884,10 @@ static void vop_crtc_wait_for_update(struct drm_crtc *crtc) WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100)); }
-static void vop_crtc_cancel_pending_vblank(struct drm_crtc *crtc, - struct drm_file *file_priv) -{ - struct drm_device *drm = crtc->dev; - struct vop *vop = to_vop(crtc); - struct drm_pending_vblank_event *e; - unsigned long flags; - - spin_lock_irqsave(&drm->event_lock, flags); - e = vop->event; - if (e && e->base.file_priv == file_priv) { - vop->event = NULL; - - kfree(&e->base); - file_priv->event_space += sizeof(e->event); - } - spin_unlock_irqrestore(&drm->event_lock, flags); -} - static const struct rockchip_crtc_funcs private_crtc_funcs = { .enable_vblank = vop_crtc_enable_vblank, .disable_vblank = vop_crtc_disable_vblank, .wait_for_update = vop_crtc_wait_for_update, - .cancel_pending_vblank = vop_crtc_cancel_pending_vblank, };
static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
Now that the core helpers support nonblocking atomic commits there's no need to invent that wheel separately (instead of fixing the bug in the atomic implementation of virtio, as it should have been done!).
Cc: Gerd Hoffmann kraxel@redhat.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/virtio/virtgpu_display.c | 48 ++------------------------------ 1 file changed, 2 insertions(+), 46 deletions(-)
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index d4305da88f44..325c6f73814b 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -118,58 +118,13 @@ static int virtio_gpu_crtc_cursor_move(struct drm_crtc *crtc, return 0; }
-static int virtio_gpu_page_flip(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, - uint32_t flags) -{ - struct virtio_gpu_device *vgdev = crtc->dev->dev_private; - struct virtio_gpu_output *output = - container_of(crtc, struct virtio_gpu_output, crtc); - struct drm_plane *plane = crtc->primary; - struct virtio_gpu_framebuffer *vgfb; - struct virtio_gpu_object *bo; - unsigned long irqflags; - uint32_t handle; - - plane->fb = fb; - vgfb = to_virtio_gpu_framebuffer(plane->fb); - bo = gem_to_virtio_gpu_obj(vgfb->obj); - handle = bo->hw_res_handle; - - DRM_DEBUG("handle 0x%x%s, crtc %dx%d\n", handle, - bo->dumb ? ", dumb" : "", - crtc->mode.hdisplay, crtc->mode.vdisplay); - if (bo->dumb) { - virtio_gpu_cmd_transfer_to_host_2d - (vgdev, handle, 0, - cpu_to_le32(crtc->mode.hdisplay), - cpu_to_le32(crtc->mode.vdisplay), - 0, 0, NULL); - } - virtio_gpu_cmd_set_scanout(vgdev, output->index, handle, - crtc->mode.hdisplay, - crtc->mode.vdisplay, 0, 0); - virtio_gpu_cmd_resource_flush(vgdev, handle, 0, 0, - crtc->mode.hdisplay, - crtc->mode.vdisplay); - - if (event) { - spin_lock_irqsave(&crtc->dev->event_lock, irqflags); - drm_send_vblank_event(crtc->dev, -1, event); - spin_unlock_irqrestore(&crtc->dev->event_lock, irqflags); - } - - return 0; -} - static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = { .cursor_set2 = virtio_gpu_crtc_cursor_set, .cursor_move = virtio_gpu_crtc_cursor_move, .set_config = drm_atomic_helper_set_config, .destroy = drm_crtc_cleanup,
- .page_flip = virtio_gpu_page_flip, + .page_flip = drm_atomic_helper_page_flip, .reset = drm_atomic_helper_crtc_reset, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, @@ -267,6 +222,7 @@ static void virtio_gpu_crtc_atomic_flush(struct drm_crtc *crtc, spin_lock_irqsave(&crtc->dev->event_lock, flags); if (crtc->state->event) drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; spin_unlock_irqrestore(&crtc->dev->event_lock, flags); }
dri-devel@lists.freedesktop.org