- 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 4342f3dfc0e1..326ee34cdba4 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1165,7 +1165,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 @@ -1536,8 +1536,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 @@ -1559,8 +1559,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 843b21c540b3..4a679fb9bb02 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 60cba1956c0d..a59cc0e2e5ca 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13726,7 +13726,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 b7e5f4a736f0..04e901a80234 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 8c3b463620bd..4a8a6f1f1151 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 d9848f1fc4e8..7e844a9c727c 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -174,7 +174,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 86c109b16876..6bb032d8ac6b 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 755cfdba61cd..3348c0878d4b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -289,7 +289,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 b03bd83703b4..07ede3a82d54 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,
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.
v2: Rebase it!
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 7675bbc70133..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); - kfree(&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",
Op 08-06-16 om 14:18 schreef Daniel Vetter:
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
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); } }
On Wed, Jun 08, 2016 at 04:14:38PM +0200, Maarten Lankhorst wrote:
I'm not going to fix up other people's drivers completely, just enough to hopefully not break them. If arc also blocks vblank interrupts with the go bit, then doing this in _begin is correct. Either way it needs hw-specific knowledge to asses whether it's correct, since doing the vblank event stuff in _flush is also racy without some prevention. -Daniel
Hi Daniel,
On Wed, 2016-06-08 at 16:30 +0200, Daniel Vetter wrote:
Actually in ARC PGU driver that was one of many other copy-pastes from other drivers. I.e. for me this is another boilerplate and if that's the same for other drivers as well probably that's a good candidate for generalization into something like drm_helper_crtc_atomic_check().
-Alexey
On Thu, Jun 09, 2016 at 10:54:45AM +0000, Alexey Brodkin wrote:
I checked them all, you are special with your code here. And this can't be generalized since you must send out vblank events in a race-free manner against the actual hw update. This requires deep knowledge of the actual hw, and it's not something the helpers can take care of you. It is very much not boilerplate, but crucial for a correct implementation. And most likely arcpgu is wrong, but since I don't have that hw knowledge I'm not going to change it more than absolutely required. -Daniel
Hi Daniel,
On Thu, 2016-06-09 at 14:26 +0200, Daniel Vetter wrote:
Well I meant as of today we don't support vblank interrupts and so arc_pgu_crtc_atomic_begin() barely makes any sense.
Still in the future we do plan to add support of interrupts.
-Alexey
On Thu, Jun 09, 2016 at 12:48:31PM +0000, Alexey Brodkin wrote:
If you don't support vblank interrupts you're driver isn't compliant with atomic. You need to at least fake them. -Daniel
Hi Daniel,
On Thu, 2016-06-09 at 15:23 +0200, Daniel Vetter wrote:
Indeed. So my assumption was there are (or could appear) other simple drivers of the same kind and that fake implementation might be generic.
-Alexey
On Thu, Jun 09, 2016 at 01:27:55PM +0000, Alexey Brodkin wrote:
The fake implementation is fundamentally racy, and I don't want to write helpers which can't be used correctly. Anyway I think without this patch (or something similar) arcpgu will stall badly with the new nonblocking helpers, because arcpgu didn't bother at all to implement nonblocking. Can you pls ack this, or even better, test the entire patch series? The helpers themselves should work, but in all 5 drivers tested thus far they discovered some bugs. -Daniel
On Thu, Jun 9, 2016 at 4:29 PM, Alexey Brodkin Alexey.Brodkin@synopsys.com wrote:
It should apply on drm-next from Dave. -Daniel
On Thu, Jun 9, 2016 at 4:31 PM, Daniel Vetter daniel@ffwll.ch wrote:
And indeed it won't work at all because arcpgu doesn't call drm_crtc_handle_vblank anywhere. So you need to add your patch to enable vblank interrupts somewhere. Note that as long as you leave max_vblank_counter as 0, the only bits you need is drm_vblank_init and drm_crtc_handle_vblanke() from the irq handler. -Daniel
Hi Daniel,
On Thu, 2016-06-09 at 16:37 +0200, Daniel Vetter wrote:
So is there any sense in testing that series if vblank interrupt is not yet supported (I'm looking forward to implementing it sometime soon but definitely I'm not there yet)?
-Alexey
On Fri, Jun 10, 2016 at 01:23:22PM +0000, Alexey Brodkin wrote:
Well, it might break your driver, so yes. I'm ofc happy to help unbreak it, but without someone who tests there's not much I can do, so will just go ahead and apply and hope it works. -Daniel
On Fri, Jun 10, 2016 at 04:19:27PM +0200, Daniel Vetter wrote:
Ok I went ahead and pushed a slight revised version of that patch which just unconditionally sends out the event. That's not correct, but at least that way the nonblocking changes won't totally break arcpgu and I can move ahead with those. -Daniel
Hi Daniel,
On Fri, 2016-06-10 at 16:54 +0200, Daniel Vetter wrote:
Thanks for that. In the meantime I tried previously sent patches: --------------->8------------- 9267484 drm/arc: Actually bother with handling atomic events. cf4a489 drm/arc: Nuke event_list 9c3152e drm/atomic-helper: Massage swap_state signature somewhat --------------->8------------- and on both boards (axs103 and nSIM OSCI) video works quite fine.
-Alexey
On Fri, Jun 10, 2016 at 03:01:03PM +0000, Alexey Brodkin wrote:
The possible breakage only starts when you move further into the series, up to patch 10. That implements generic nonblocking commit, but that support relies upon crtc_state->event being signalled. Anyway I'm pulling it all into drm-misc now, so you can just test that branch (or linux-next when it's rebuild next week). -Daniel
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,
Op 08-06-16 om 14:18 schreef Daniel Vetter:
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
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,
Op 08-06-16 om 14:18 schreef Daniel Vetter:
^I keep wondering why we set this to NULL every time. Nothing should depend on this right? duplicate_state sets this member to NULL.. I'd rather have crtc_state be constified after commit. Other than that..
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
~Maarten
On Wed, Jun 08, 2016 at 04:17:55PM +0200, Maarten Lankhorst wrote:
I added a WARN_ON in hw_done to make sure drivers have indeed consumed the event. Given that lots of drivers don't bother I think that's good, since I want to avoid getting flooded with bug reports along the lines of "my atomic driver hangs" with these new helpers. WARN_ON(state->event) in hw_done together with the commen is hopefully hint enough. -Daniel
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
~Maarten
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)
Op 08-06-16 om 14:18 schreef Daniel Vetter:
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
Hi Daniel,
On Wednesday 08 Jun 2016 14:18:58 Daniel Vetter wrote:
Is it me, or is it entirely useless given the code in sun4i_crtc_atomic_flush() that sets crtc->state->event to NULL ?
AuthorDate: Wed Jun 8 14:18:58 2016 +0200 CommitDate: Fri Jun 10 16:55:48 2016 +0200
It would have been worth it waiting for the maintainers to review the patch...
You've added similar code to a bunch of drivers. How do you protect against the race conditions documented in the drm_crtc_arm_vblank_event() function ?
}
static void sun4i_crtc_disable(struct drm_crtc *crtc)
Just a bit of drive-by ocd.
v2: Improve per Liviu's feedback.
Cc: Liviu Dudau Liviu.Dudau@arm.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- include/drm/drm_atomic.h | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index d12cfb9c6062..a16861c882aa 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -198,6 +198,16 @@ 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 + * + * To give drivers flexibility struct &drm_crtc_state has 3 booleans to track + * whether the state CRTC changed enough to need a full modeset cycle: + * connectors_changed, mode_changed and active_change. This helper simply + * combines these three to compute the overall need for a modeset for @state. + */ static inline bool drm_atomic_crtc_needs_modeset(struct drm_crtc_state *state) {
On Wed, Jun 08, 2016 at 02:18:59PM +0200, Daniel Vetter wrote:
Acked-by: Liviu Dudau Liviu.Dudau@arm.com
Op 08-06-16 om 14:18 schreef Daniel Vetter:
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
Split out from my big nonblocking atomic commit helper code as prep work. While add it, also add some neat asciiart to document how it's supposed to be used.
v2: Resurrect misplaced hunk in the kerneldoc.
Tested-by: Tomeu Vizoso tomeu.vizoso@collabora.com Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Tomeu Vizoso tomeu.vizoso@gmail.com Cc: Daniel Stone daniels@collabora.com Tested-by: Liviu Dudau Liviu.Dudau@arm.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_atomic.c | 22 +++++++ 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_crtc.h | 149 +++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 181 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 5e4b820a977c..d99ab2f6663f 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_crtc.c b/drivers/gpu/drm/drm_crtc.c index d3d0b4162e76..ce0569c3f942 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 44b3ecdeca63..323c238fcac7 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 15fe4a21a9da..dce655abd23d 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 a16861c882aa..856a9c85a838 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_crtc.h b/include/drm/drm_crtc.h index 520abafc9d00..14aa8212e30f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -727,9 +727,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. @@ -786,11 +783,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; }; @@ -1732,6 +1755,111 @@ struct drm_bridge { void *driver_private; };
+/** + * struct drm_crtc_commit - track modeset commits on a CRTC + * + * This structure is used to track pending modeset changes and atomic commit on + * a per-CRTC basis. Since updating the list should never block this structure + * is reference counted to allow waiters to safely wait on an event to complete, + * without holding any locks. + * + * It has 3 different events in total to allow a fine-grained synchronoization + * between outstanding updates:: + * + * atomic commit thread hardware + * + * write new state into hardware ----> ... + * signal hw_done + * switch to new state on next + * ... v/hblank + * + * wait for buffers to show up ... + * + * ... send completion irq + * irq handler signals flip_done + * cleanup old buffers + * + * signal cleanup_done + * + * wait for flip_done <---- + * clean up atomic state + * + * The important bit to know is that cleanup_done is the terminal event, but the + * ordering between flip_done and hw_done is entirely up to the specific driver + * and modeset state change. + * + * For an implementation of how to use this look at + * drm_atomic_helper_setup_commit() from the atomic helper library. + */ +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; @@ -1740,6 +1868,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 { @@ -1770,6 +1899,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; };
Op 08-06-16 om 14:19 schreef Daniel Vetter:
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
On Wed, Jun 08, 2016 at 02:19:00PM +0200, Daniel Vetter wrote:
spelling police: synchronization
what do you mean by "cleaned up *again*" ? If you are trying to emphasize the following sentence, I think you can drop "again" from it, but again, I'm only a software developer :)
Best regards, Liviu
To facilitate easier reviewing this is split out from the overall nonblocking commit rework. It just rolls out the helper functions and uses them in the main drm_atomic_helper_commit() function to make it clear where in the flow they're used.
The next patch will actually split drm_atomic_helper_commit() into 2 pieces, with the tail being run asynchronously from a worker.
v2: Improve kerneldocs (Maarten).
v3: Don't convert ERESTARTSYS to EINTR (Maarten). Also don't fail if the wait succeed in stall_check - we need to convert that case (it returns the remaining jiffies) to 0 for success.
Tested-by: Tomeu Vizoso tomeu.vizoso@collabora.com Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Tomeu Vizoso tomeu.vizoso@gmail.com Cc: Daniel Stone daniels@collabora.com Tested-by: Liviu Dudau Liviu.Dudau@arm.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 346 ++++++++++++++++++++++++++++++++++++ include/drm/drm_atomic_helper.h | 7 + 2 files changed, 353 insertions(+)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 326ee34cdba4..63e46827b303 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1155,6 +1155,10 @@ int drm_atomic_helper_commit(struct drm_device *dev, if (nonblock) return -EBUSY;
+ ret = drm_atomic_helper_setup_commit(state, nonblock); + if (ret) + return ret; + ret = drm_atomic_helper_prepare_planes(dev, state); if (ret) return ret; @@ -1185,16 +1189,22 @@ int drm_atomic_helper_commit(struct drm_device *dev,
drm_atomic_helper_wait_for_fences(dev, state);
+ drm_atomic_helper_wait_for_dependencies(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_commit_hw_done(state); + drm_atomic_helper_wait_for_vblanks(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
+ drm_atomic_helper_commit_cleanup_done(state); + drm_atomic_state_free(state);
return 0; @@ -1239,6 +1249,305 @@ 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); + + drm_crtc_commit_put(stall_commit); + + return ret < 0 ? ret : 0; +} + +/** + * drm_atomic_helper_setup_commit - setup possibly nonblocking commit + * @state: new modeset state to be committed + * @nonblock: whether nonblocking behavior is requested. + * + * This function prepares @state to be used by the atomic helper's support for + * nonblocking commits. Drivers using the nonblocking commit infrastructure + * should always call this function from their ->atomic_commit hook. + * + * To be able to use this support drivers need to use a few more helper + * functions. drm_atomic_helper_wait_for_dependencies() must be called before + * actually committing the hardware state, and for nonblocking commits this call + * must be placed in the async worker. See also drm_atomic_helper_swap_state() + * and it's stall parameter, for when a driver's commit hooks look at the + * ->state pointers of struct &drm_crtc, &drm_plane or &drm_connector directly. + * + * Completion of the hardware commit step must be signalled using + * drm_atomic_helper_commit_hw_done(). After this step the driver is not allowed + * to read or change any permanent software or hardware modeset state. The only + * exception is state protected by other means than &drm_modeset_lock locks. + * Only the free standing @state with pointers to the old state structures can + * be inspected, e.g. to clean up old buffers using + * drm_atomic_helper_cleanup_planes(). + * + * At the very end, before cleaning up @state drivers must call + * drm_atomic_helper_commit_cleanup_done(). + * + * This is all implemented by in drm_atomic_helper_commit(), giving drivers a + * complete and esay-to-use default implementation of the atomic_commit() hook. + * + * The tracking of asynchronously executed and still pending commits is done + * using the core structure &drm_crtc_commit. + * + * By default there's no need to clean up resources allocated by this function + * explicitly: drm_atomic_state_default_clear() will take care of that + * automatically. + * + * 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; +} + +/** + * drm_atomic_helper_wait_for_dependencies - wait for required preceeding commits + * @state: new modeset state to be committed + * + * This function waits for all preceeding commits that touch the same CRTC as + * @state to both be committed to the hardware (as signalled by + * drm_atomic_Helper_commit_hw_done) and executed by the hardware (as signalled + * by calling drm_crtc_vblank_send_event on the event member of + * &drm_crtc_state). + * + * This is part of the atomic helper support for nonblocking commits, see + * drm_atomic_helper_setup_commit() for an overview. + */ +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); + +/** + * drm_atomic_helper_commit_hw_done - setup possible nonblocking commit + * @state: new modeset state to be committed + * + * This function is used to signal completion of the hardware commit step. After + * this step the driver is not allowed to read or change any permanent software + * or hardware modeset state. The only exception is state protected by other + * means than &drm_modeset_lock locks. + * + * Drivers should try to postpone any expensive or delayed cleanup work after + * this function is called. + * + * This is part of the atomic helper support for nonblocking commits, see + * drm_atomic_helper_setup_commit() for an overview. + */ +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); + +/** + * drm_atomic_helper_commit_cleanup_done - signal completion of commit + * @state: new modeset state to be committed + * + * This signals completion of the atomic update @state, including any cleanup + * work. If used, it must be called right before calling + * drm_atomic_state_free(). + * + * This is part of the atomic helper support for nonblocking commits, see + * drm_atomic_helper_setup_commit() for an overview. + */ +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 (WARN_ON(!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. But only after it actually + * completed, otherwise subsequent commits won't stall properly. */ + if (try_wait_for_completion(&commit->flip_done)) { + list_del(&commit->commit_entry); + spin_unlock(&crtc->commit_lock); + continue; + } + + 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); + + spin_lock(&crtc->commit_lock); + list_del(&commit->commit_entry); + spin_unlock(&crtc->commit_lock); + } +} +EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done); + /** * drm_atomic_helper_prepare_planes - prepare plane resources before commit * @dev: DRM device @@ -1558,17 +1867,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; @@ -1580,6 +1917,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/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 07ede3a82d54..368cbffc54ac 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -74,6 +74,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,
Op 08-06-16 om 14:19 schreef Daniel Vetter:
^Should probably be a long ret, or truncation gets done too early if we decide to bump the timeout to infinity.
Must admit I missed the error of ret > 0 in v2.
Looking good though..
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
To facilitate easier reviewing this is split out from the overall nonblocking commit rework. It just rolls out the helper functions and uses them in the main drm_atomic_helper_commit() function to make it clear where in the flow they're used.
The next patch will actually split drm_atomic_helper_commit() into 2 pieces, with the tail being run asynchronously from a worker.
v2: Improve kerneldocs (Maarten).
v3: Don't convert ERESTARTSYS to EINTR (Maarten). Also don't fail if the wait succeed in stall_check - we need to convert that case (it returns the remaining jiffies) to 0 for success.
v4: Switch to long for wait_for_completion_timeout return value everywhere (Maarten).
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com Tested-by: Tomeu Vizoso tomeu.vizoso@collabora.com Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Tomeu Vizoso tomeu.vizoso@gmail.com Cc: Daniel Stone daniels@collabora.com Tested-by: Liviu Dudau Liviu.Dudau@arm.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 347 ++++++++++++++++++++++++++++++++++++ include/drm/drm_atomic_helper.h | 7 + 2 files changed, 354 insertions(+)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 326ee34cdba4..fa2f89253b17 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1155,6 +1155,10 @@ int drm_atomic_helper_commit(struct drm_device *dev, if (nonblock) return -EBUSY;
+ ret = drm_atomic_helper_setup_commit(state, nonblock); + if (ret) + return ret; + ret = drm_atomic_helper_prepare_planes(dev, state); if (ret) return ret; @@ -1185,16 +1189,22 @@ int drm_atomic_helper_commit(struct drm_device *dev,
drm_atomic_helper_wait_for_fences(dev, state);
+ drm_atomic_helper_wait_for_dependencies(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_commit_hw_done(state); + drm_atomic_helper_wait_for_vblanks(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
+ drm_atomic_helper_commit_cleanup_done(state); + drm_atomic_state_free(state);
return 0; @@ -1239,6 +1249,306 @@ 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; + long 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); + + drm_crtc_commit_put(stall_commit); + + return ret < 0 ? ret : 0; +} + +/** + * drm_atomic_helper_setup_commit - setup possibly nonblocking commit + * @state: new modeset state to be committed + * @nonblock: whether nonblocking behavior is requested. + * + * This function prepares @state to be used by the atomic helper's support for + * nonblocking commits. Drivers using the nonblocking commit infrastructure + * should always call this function from their ->atomic_commit hook. + * + * To be able to use this support drivers need to use a few more helper + * functions. drm_atomic_helper_wait_for_dependencies() must be called before + * actually committing the hardware state, and for nonblocking commits this call + * must be placed in the async worker. See also drm_atomic_helper_swap_state() + * and it's stall parameter, for when a driver's commit hooks look at the + * ->state pointers of struct &drm_crtc, &drm_plane or &drm_connector directly. + * + * Completion of the hardware commit step must be signalled using + * drm_atomic_helper_commit_hw_done(). After this step the driver is not allowed + * to read or change any permanent software or hardware modeset state. The only + * exception is state protected by other means than &drm_modeset_lock locks. + * Only the free standing @state with pointers to the old state structures can + * be inspected, e.g. to clean up old buffers using + * drm_atomic_helper_cleanup_planes(). + * + * At the very end, before cleaning up @state drivers must call + * drm_atomic_helper_commit_cleanup_done(). + * + * This is all implemented by in drm_atomic_helper_commit(), giving drivers a + * complete and esay-to-use default implementation of the atomic_commit() hook. + * + * The tracking of asynchronously executed and still pending commits is done + * using the core structure &drm_crtc_commit. + * + * By default there's no need to clean up resources allocated by this function + * explicitly: drm_atomic_state_default_clear() will take care of that + * automatically. + * + * 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; +} + +/** + * drm_atomic_helper_wait_for_dependencies - wait for required preceeding commits + * @state: new modeset state to be committed + * + * This function waits for all preceeding commits that touch the same CRTC as + * @state to both be committed to the hardware (as signalled by + * drm_atomic_Helper_commit_hw_done) and executed by the hardware (as signalled + * by calling drm_crtc_vblank_send_event on the event member of + * &drm_crtc_state). + * + * This is part of the atomic helper support for nonblocking commits, see + * drm_atomic_helper_setup_commit() for an overview. + */ +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); + +/** + * drm_atomic_helper_commit_hw_done - setup possible nonblocking commit + * @state: new modeset state to be committed + * + * This function is used to signal completion of the hardware commit step. After + * this step the driver is not allowed to read or change any permanent software + * or hardware modeset state. The only exception is state protected by other + * means than &drm_modeset_lock locks. + * + * Drivers should try to postpone any expensive or delayed cleanup work after + * this function is called. + * + * This is part of the atomic helper support for nonblocking commits, see + * drm_atomic_helper_setup_commit() for an overview. + */ +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); + +/** + * drm_atomic_helper_commit_cleanup_done - signal completion of commit + * @state: new modeset state to be committed + * + * This signals completion of the atomic update @state, including any cleanup + * work. If used, it must be called right before calling + * drm_atomic_state_free(). + * + * This is part of the atomic helper support for nonblocking commits, see + * drm_atomic_helper_setup_commit() for an overview. + */ +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 (WARN_ON(!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. But only after it actually + * completed, otherwise subsequent commits won't stall properly. */ + if (try_wait_for_completion(&commit->flip_done)) { + list_del(&commit->commit_entry); + spin_unlock(&crtc->commit_lock); + continue; + } + + 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); + + spin_lock(&crtc->commit_lock); + list_del(&commit->commit_entry); + spin_unlock(&crtc->commit_lock); + } +} +EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done); + /** * drm_atomic_helper_prepare_planes - prepare plane resources before commit * @dev: DRM device @@ -1558,17 +1868,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; @@ -1580,6 +1918,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/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 07ede3a82d54..368cbffc54ac 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -74,6 +74,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,
Hi Daniel,
Thank you for the patch.
On Wednesday 08 Jun 2016 17:15:36 Daniel Vetter wrote:
previous
stall.
or
properly. */
Why is this needed ? drm_atomic_helper_commit_cleanup_done() is called in commit_tail() after the call to drm_atomic_helper_commit_tail() or to the driver's .atomic_commit_tail() handler. If I'm not mistaken both already wait for the page flip to complete, either with a call to drm_atomic_helper_wait_for_vblanks() in drm_atomic_helper_commit_tail() or with a custom method in the driver's .atomic_commit_tail() handler.
On Mon, Jan 02, 2017 at 02:09:13PM +0200, Laurent Pinchart wrote:
You might still race with the event handling, and for legacy cursor updates we've ommitted the vblank waits. Anyway there's a patch from me on the m-l to switch over to refcounting, which makes this code here unecessary. I guess I should apply it to drm-misc. -Daniel
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!!
v8: - Fixup trailing whitespace, spotted by Maarten. - Actually wait for flip_done in cleanup_done, like the comment says we should do. Thanks a lot for Tomeu for helping with debugging this on.
v9: Now with awesome kerneldoc!
v10: Split out drm_crtc_commit tracking infrastructure.
v: - Add missing static (Gustavo). - Split out the sync functions, only do the actual nonblocking logic in this patch (Maarten).
Cc: Gustavo Padovan gustavo.padovan@collabora.co.uk Tested-by: Tomeu Vizoso tomeu.vizoso@collabora.com Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Tomeu Vizoso tomeu.vizoso@gmail.com Cc: Daniel Stone daniels@collabora.com Tested-by: Liviu Dudau Liviu.Dudau@arm.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_atomic_helper.c | 144 +++++++++++++++++++++---------- include/drm/drm_atomic_helper.h | 1 + include/drm/drm_crtc.h | 3 + include/drm/drm_modeset_helper_vtables.h | 39 +++++++++ 4 files changed, 141 insertions(+), 46 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 63e46827b303..73b345323cf1 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1118,22 +1118,17 @@ 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 + * For drivers supporting runtime PM the recommended sequence is instead :: * * drm_atomic_helper_commit_modeset_disables(dev, state); * @@ -1141,7 +1136,73 @@ 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. + * for committing the atomic update to hardware. 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); + +static 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 + * @nonblock: 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. This + * function implements nonblocking commits, using + * drm_atomic_helper_setup_commit() and related functions. + * + * 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. @@ -1152,13 +1213,12 @@ 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) return ret; @@ -1185,27 +1245,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_wait_for_dependencies(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_commit_hw_done(state); - - drm_atomic_helper_wait_for_vblanks(dev, state); - - drm_atomic_helper_cleanup_planes(dev, state); - - drm_atomic_helper_commit_cleanup_done(state); - - drm_atomic_state_free(state); + if (nonblock) + queue_work(system_unbound_wq, &state->commit_work); + else + commit_tail(state);
return 0; } @@ -1214,12 +1263,7 @@ EXPORT_SYMBOL(drm_atomic_helper_commit); /** * DOC: implementing nonblocking commit * - * For now the atomic helpers don't support nonblocking commit directly. If - * there is real need it could be added though, using the dma-buf fence - * infrastructure for generic synchronization with outstanding rendering. - * - * For now drivers have to implement nonblocking commit themselves, with the - * following sequence being the recommended one: + * Nonblocking atomic commits have to be implemented in the following sequence: * * 1. Run drm_atomic_helper_prepare_planes() first. This is the only function * which commit needs to call which can fail, so we want to run it first and @@ -1231,10 +1275,14 @@ EXPORT_SYMBOL(drm_atomic_helper_commit); * cancelled updates. Note that it is important to ensure that the framebuffer * cleanup is still done when cancelling. * - * For sufficient parallelism it is recommended to have a work item per crtc - * (for updates which don't touch global state) and a global one. Then we only - * need to synchronize with the crtc work items for changed crtcs and the global - * work item, which allows nice concurrent updates on disjoint sets of crtcs. + * Asynchronous workers need to have sufficient parallelism to be able to run + * different atomic commits on different CRTCs in parallel. The simplest way to + * achive this is by running them on the &system_unbound_wq work queue. Note + * that drivers are not required to split up atomic commits and run an + * individual commit in parallel - userspace is supposed to do that if it cares. + * But it might be beneficial to do that for modesets, since those necessarily + * must be done as one global operation, and enabling or disabling a CRTC can + * take a long time. But even that is not required. * * 3. The software state is updated synchronously with * drm_atomic_helper_swap_state(). Doing this under the protection of all modeset @@ -1247,6 +1295,10 @@ EXPORT_SYMBOL(drm_atomic_helper_commit); * commit helpers: a) pre-plane commit b) plane commit c) post-plane commit and * then cleaning up the framebuffers after the old framebuffer is no longer * being displayed. + * + * The above scheme is implemented in the atomic helper libraries in + * drm_atomic_helper_commit() using a bunch of helper functions. See + * drm_atomic_helper_setup_commit() for a starting point. */
static int stall_checks(struct drm_crtc *crtc, bool nonblock) diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 368cbffc54ac..31c11e3d6887 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); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 14aa8212e30f..7ecc8bad753e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -2247,6 +2247,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 @@ -2390,6 +2391,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..4723fb96bc1a 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -925,4 +925,43 @@ 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 is used by the default atomic_commit() hook implemented in + * drm_atomic_helper_commit() together with the nonblocking commit + * helpers (see drm_atomic_helper_setup_commit() for a starting point) + * to implement blocking and nonblocking commits easily. It is not used + * by the atomic helpers + * + * This hook should first commit the given atomic state to the hardware. + * But drivers can add more waiting calls at the start of their + * implementation, e.g. to wait for driver-internal request for implicit + * syncing, before starting to commit the update to the hardware. + * + * After the atomic update is committed to the hardware this hook needs + * to call drm_atomic_helper_commit_hw_done(). Then wait for the upate + * to be executed by the hardware, for example using + * drm_atomic_helper_wait_for_vblanks(), and then clean up the old + * framebuffers using drm_atomic_helper_cleanup_planes(). + * + * When disabling a CRTC 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 08-06-16 om 14:19 schreef Daniel Vetter:
^Typo, and was done 9 commits before?
Once that's done stall can be set to false in swap_states.
Features: Contains bugs because totally untested.
^I Hope not..
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
On Wed, Jun 08, 2016 at 04:44:24PM +0200, Maarten Lankhorst wrote:
Hm, what typo? And the patches are just prep, what we still need is explicitly passing crtc/plane/connector state into driver callbacks, so that they don't look at crtc/plane/connector->state any more.
Indeed, tested on iirc 5 drivers now. Will remove when merging. -Daniel
On Wed, Jun 08, 2016 at 05:05:04PM +0200, Daniel Vetter wrote:
What new tests were written for bugs uncovered when developing your series? :) Must be one or two corner cases left untouched...
How much stubbing do we need to write a test-drm-atomic module that just exercised the various paths with a fake device and so fail in a controlled manner? -Chris
On Wed, Jun 8, 2016 at 5:54 PM, Chris Wilson chris@chris-wilson.co.uk wrote:
Most of the discovered bugs are failing to handle events as atomic expects it too, and the coverage is that the new helpers are super-strict in enforcing this. They create events for any kind of modeset (even blocking ones), and if the driver fails to signal it properly the ioctl call stalls until it times out after 10s. So I believe that the driver-side is sufficiently covered with runtime checks already.
The other bit is the correctness of the helpers themselves. And I think for that it doesn't matter much what kind of atomic commits we do, but how badly. IOW I believe the existing igt coverage (especially after the various extensions already done) is good enough.
E.g. this does fixes the -EBUSY checks in kms_flip on all !i915 drivers (since no one bothered to implement that particular bit of evolved uapi ever really). And kms_flip makes sure it works and keeps working. Same for other corner cases. At least I think I didn't spot any bugs that igt didn't catch. Hence:
Testcase: igt/kms_flip/* Testcase: igt/kms_cursor* Testcase: igt/kms*plane*
But the main one is kms_flip - this is one shockingly nasty testcase ;-)
With all the recent efforts to no-op out callbacks propably very little. The big thing to appeal igt is that vblank events are sensibly spaced, which needs a rearming hrtimer. There's always discussions going on whether virtual hardware should do that faking already (weston runs in a busy-loop without that), and if we have this in a nice little helper almost no code would be required. Not sure how much value that has, since the real atomic tests is checking for tearing, and that needs CRC checksums. -Daniel
On Wed, Jun 8, 2016 at 6:19 PM, Daniel Vetter daniel@ffwll.ch wrote:
But the main one is kms_flip - this is one shockingly nasty testcase ;-)
Also note that kms_flip wasn't just run on i915, but also on other drivers in this conversion (not all iirc), thanks to collabora's work to port igt testcase to generic kms. Again it mostly uncovered bugs in drivers (after I debugged some silly gotchas in the helpers) with a combination of kms_flip sanity checks and the runtime checks in the helpers themselves. I think it's fair to claim that if you have an atomic driver and switch over to these helpers + run kms_flip, you're driver will have gained a few bugfixes until it manages to pass - all of them needed some ;-) -Daniel
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 08-06-16 om 14:19 schreef Daniel Vetter:
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
With the fixed up drm event handling for crtc_state->event we can just use the helper support for nonblocking commits.
Cc: Liviu Dudau Liviu.Dudau@arm.com Tested-by: Liviu Dudau Liviu.Dudau@arm.com Acked-by: 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 49e586d67595..74279be20b75 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -106,17 +106,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)
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 a59cc0e2e5ca..0618916f825b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13797,13 +13797,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));
@@ -13811,8 +13819,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 324ccb06397d..fc654173c491 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 &&
Op 08-06-16 om 14:19 schreef Daniel Vetter:
What happened to the patch that added event tests to the kms_atomic testcase? :(
Same comment about crtc_state->event, not sure it should be reset to null.
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
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 0618916f825b..77ff0903540d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13720,6 +13720,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); @@ -13731,6 +13735,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)); @@ -13826,7 +13832,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); @@ -13861,6 +13867,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
Op 08-06-16 om 14:19 schreef Daniel Vetter:
Right now still all blocking, no worker anywhere to be seen.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
Simply split intel_atomic_commit in half and place the new nonblocking commit helpers at the right spots.
NOTE: There's still trouble with obj->frontbuffer bits getting mangled when pipelining atomic commits.
v2: - Remove the check for nonblocking which returned -EINVAL. - Do wait for requests in the worker thread before committing hw state.
v3: Move hw_done after the optimize_wm/post_plane_update step, plus add FIXME comment how to fix that up again properly.
v4: Update FIXME for intel_atomic_commit - more stuff works now.
Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/i915/intel_display.c | 121 ++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 39 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 77ff0903540d..586fc40b9abf 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13567,11 +13567,6 @@ static int intel_atomic_prepare_commit(struct drm_device *dev, struct drm_crtc *crtc; int i, ret;
- if (nonblock) { - DRM_DEBUG_KMS("i915 does not yet support nonblocking commit\n"); - return -EINVAL; - } - for_each_crtc_in_state(state, crtc, crtc_state, i) { if (state->legacy_cursor_update) continue; @@ -13690,50 +13685,34 @@ static bool needs_vblank_wait(struct intel_crtc_state *crtc_state) return false; }
-/** - * intel_atomic_commit - commit validated state object - * @dev: DRM device - * @state: the top-level driver state object - * @nonblock: nonblocking commit - * - * This function commits a top-level state object that has been validated - * with drm_atomic_helper_check(). - * - * FIXME: Atomic modeset support for i915 is not yet complete. At the moment - * we can only handle plane-related operations and do not yet support - * nonblocking commit. - * - * RETURNS - * Zero for success or -errno. - */ -static int intel_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, - bool nonblock) +static void intel_atomic_commit_tail(struct drm_atomic_state *state) { + struct drm_device *dev = state->dev; struct intel_atomic_state *intel_state = to_intel_atomic_state(state); struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc_state *old_crtc_state; struct drm_crtc *crtc; struct intel_crtc_state *intel_cstate; - int ret = 0, i; + struct drm_plane *plane; + struct drm_plane_state *plane_state; bool hw_check = intel_state->modeset; unsigned long put_domains[I915_MAX_PIPES] = {}; unsigned crtc_vblank_mask = 0; + int i, ret;
- ret = drm_atomic_helper_setup_commit(state, nonblock); - if (ret) - return ret; + for_each_plane_in_state(state, plane, plane_state, i) { + struct intel_plane_state *intel_plane_state = + to_intel_plane_state(plane_state);
- ret = intel_atomic_prepare_commit(dev, state, nonblock); - if (ret) { - DRM_DEBUG_ATOMIC("Preparing state failed with %i\n", ret); - return ret; - } + if (!intel_plane_state->wait_req) + continue;
- drm_atomic_helper_swap_state(state, true); - dev_priv->wm.distrust_bios_wm = false; - dev_priv->wm.skl_results = intel_state->wm_results; - intel_shared_dpll_commit(state); + ret = __i915_wait_request(intel_plane_state->wait_req, + true, NULL, NULL); + /* EIO should be eaten, and we can't get interrupted in the + * worker, and blocking commits have waited already. */ + WARN_ON(ret); + }
drm_atomic_helper_wait_for_dependencies(state);
@@ -13832,8 +13811,15 @@ static int intel_atomic_commit(struct drm_device *dev, crtc_vblank_mask |= 1 << i; }
- drm_atomic_helper_commit_hw_done(state); - + /* FIXME: We should call drm_atomic_helper_commit_hw_done() here + * already, but still need the state for the delayed optimization. To + * fix this: + * - wrap the optimization/post_plane_update stuff into a per-crtc work. + * - schedule that vblank worker _before_ calling hw_done + * - at the start of commit_tail, cancel it _synchrously + * - switch over to the vblank wait helper in the core after that since + * we don't need out special handling any more. + */ if (!state->legacy_cursor_update) intel_atomic_wait_for_vblanks(dev, dev_priv, crtc_vblank_mask);
@@ -13860,6 +13846,8 @@ static int intel_atomic_commit(struct drm_device *dev, intel_modeset_verify_crtc(crtc, old_crtc_state, crtc->state); }
+ drm_atomic_helper_commit_hw_done(state); + if (intel_state->modeset) intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET);
@@ -13883,6 +13871,61 @@ static int intel_atomic_commit(struct drm_device *dev, * can happen also when the device is completely off. */ intel_uncore_arm_unclaimed_mmio_detection(dev_priv); +} + +static void intel_atomic_commit_work(struct work_struct *work) +{ + struct drm_atomic_state *state = container_of(work, + struct drm_atomic_state, + commit_work); + intel_atomic_commit_tail(state); +} + +/** + * intel_atomic_commit - commit validated state object + * @dev: DRM device + * @state: the top-level driver state object + * @nonblock: nonblocking commit + * + * This function commits a top-level state object that has been validated + * with drm_atomic_helper_check(). + * + * FIXME: Atomic modeset support for i915 is not yet complete. At the moment + * nonblocking commits are only safe for pure plane updates. Everything else + * should work though. + * + * RETURNS + * Zero for success or -errno. + */ +static int intel_atomic_commit(struct drm_device *dev, + struct drm_atomic_state *state, + bool nonblock) +{ + struct intel_atomic_state *intel_state = to_intel_atomic_state(state); + struct drm_i915_private *dev_priv = dev->dev_private; + int ret = 0; + + ret = drm_atomic_helper_setup_commit(state, nonblock); + if (ret) + return ret; + + INIT_WORK(&state->commit_work, intel_atomic_commit_work); + + ret = intel_atomic_prepare_commit(dev, state, nonblock); + if (ret) { + DRM_DEBUG_ATOMIC("Preparing state failed with %i\n", ret); + return ret; + } + + drm_atomic_helper_swap_state(state, true); + dev_priv->wm.distrust_bios_wm = false; + dev_priv->wm.skl_results = intel_state->wm_results; + intel_shared_dpll_commit(state); + + if (nonblock) + queue_work(system_unbound_wq, &state->commit_work); + else + intel_atomic_commit_tail(state);
return 0; }
Op 08-06-16 om 14:19 schreef Daniel Vetter:
This is not a good idea for now, there are still some places in modeset that rely on being run synchronously. Lockdep will be unhappy and some WARN_ONs will trigger.
I think that you should first only allow nonblocking page flips, and return -EBUSY if intel_state->modeset is set.
~Maarten
On Thu, Jun 09, 2016 at 04:03:24PM +0200, Maarten Lankhorst wrote:
Good idea, I'll respin. -Daniel
Note that I didn't start garbage collecting all the legacy flip code yet, to make it easier to revert this. But there will be _lots_ of code that can be removed once this is tested on all platforms.
FIXME: obj->frontbuffer_bits gets out of whack when pipelining commits too hard.
Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch --- drivers/gpu/drm/i915/intel_display.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 586fc40b9abf..eb02901551d8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11642,6 +11642,7 @@ void intel_check_page_flip(struct drm_i915_private *dev_priv, int pipe) spin_unlock(&dev->event_lock); }
+__attribute__((unused)) static int intel_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, @@ -13975,7 +13976,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .set_property = drm_atomic_helper_crtc_set_property, .destroy = intel_crtc_destroy, - .page_flip = intel_crtc_page_flip, + .page_flip = drm_atomic_helper_page_flip, .atomic_duplicate_state = intel_crtc_duplicate_state, .atomic_destroy_state = intel_crtc_destroy_state, };
On Wed, Jun 08, 2016 at 02:19:08PM +0200, Daniel Vetter wrote:
Possible to write a test case using dumb kms, or just point me at one you have already? :) -Chris
On Wed, Jun 08, 2016 at 03:24:01PM +0100, Chris Wilson wrote:
Argh, that FIXME is addressed by the next patch. Forgot to reorder them and drop the FIXME here. The rmfb worker vs. flips was enough concurrency to hit this bug. -Daniel
Currently it's part of prepare_fb, still in the first phase of atomic_commit which might fail. Which means that we need to have some heuristics in cleanup_fb to figure out whether things failed, or whether we just clean up the old fbs.
That's fragile, and worse, once we start pipelining commits gets confused: While the last commit is still getting cleanup up we already hammer in the new one, and fb_bits aren't refcounted, resulting in lost bits and WARN_ON galore. We could instead try to make cleanup_fb more clever, but a simpler fix is to postpone the fb_bits tracking past the point of no return, where we commit all the software state.
That also makes conceptually more sense, since fb_bits must be updated synchronously from the ioctl (they track usage from userspace pov, not from the hw pov), right before we're fully committed to the updated.
This fixes WARNING splats from track_fb with page_flip implemented through atomic_commit.
Testcase: igt/kms_flip/flip-vs-rmfb Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/i915/intel_display.c | 41 ++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index eb02901551d8..8bb8d36f5cb9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13882,6 +13882,25 @@ static void intel_atomic_commit_work(struct work_struct *work) intel_atomic_commit_tail(state); }
+static void intel_atomic_track_fbs(struct drm_atomic_state *state) +{ + struct drm_plane_state *old_plane_state; + struct drm_plane *plane; + struct drm_i915_gem_object *obj, *old_obj; + struct intel_plane *intel_plane; + int i; + + mutex_lock(&state->dev->struct_mutex); + for_each_plane_in_state(state, plane, old_plane_state, i) { + obj = intel_fb_obj(plane->state->fb); + old_obj = intel_fb_obj(old_plane_state->fb); + intel_plane = to_intel_plane(plane); + + i915_gem_track_fb(old_obj, obj, intel_plane->frontbuffer_bit); + } + mutex_unlock(&state->dev->struct_mutex); +} + /** * intel_atomic_commit - commit validated state object * @dev: DRM device @@ -13922,6 +13941,7 @@ static int intel_atomic_commit(struct drm_device *dev, dev_priv->wm.distrust_bios_wm = false; dev_priv->wm.skl_results = intel_state->wm_results; intel_shared_dpll_commit(state); + intel_atomic_track_fbs(state);
if (nonblock) queue_work(system_unbound_wq, &state->commit_work); @@ -14001,7 +14021,6 @@ intel_prepare_plane_fb(struct drm_plane *plane, { struct drm_device *dev = plane->dev; struct drm_framebuffer *fb = new_state->fb; - struct intel_plane *intel_plane = to_intel_plane(plane); struct drm_i915_gem_object *obj = intel_fb_obj(fb); struct drm_i915_gem_object *old_obj = intel_fb_obj(plane->state->fb); int ret = 0; @@ -14058,16 +14077,12 @@ intel_prepare_plane_fb(struct drm_plane *plane, ret = intel_pin_and_fence_fb_obj(fb, new_state->rotation); }
- if (ret == 0) { - if (obj) { - struct intel_plane_state *plane_state = - to_intel_plane_state(new_state); - - i915_gem_request_assign(&plane_state->wait_req, - obj->last_write_req); - } + if (ret == 0 && obj) { + struct intel_plane_state *plane_state = + to_intel_plane_state(new_state);
- i915_gem_track_fb(old_obj, obj, intel_plane->frontbuffer_bit); + i915_gem_request_assign(&plane_state->wait_req, + obj->last_write_req); }
return ret; @@ -14087,7 +14102,6 @@ intel_cleanup_plane_fb(struct drm_plane *plane, const struct drm_plane_state *old_state) { struct drm_device *dev = plane->dev; - struct intel_plane *intel_plane = to_intel_plane(plane); struct intel_plane_state *old_intel_state; struct drm_i915_gem_object *old_obj = intel_fb_obj(old_state->fb); struct drm_i915_gem_object *obj = intel_fb_obj(plane->state->fb); @@ -14101,11 +14115,6 @@ intel_cleanup_plane_fb(struct drm_plane *plane, !INTEL_INFO(dev)->cursor_needs_physical)) intel_unpin_fb_obj(old_state->fb, old_state->rotation);
- /* prepare_fb aborted? */ - if ((old_obj && (old_obj->frontbuffer_bits & intel_plane->frontbuffer_bit)) || - (obj && !(obj->frontbuffer_bits & intel_plane->frontbuffer_bit))) - i915_gem_track_fb(old_obj, obj, intel_plane->frontbuffer_bit); - i915_gem_request_assign(&old_intel_state->wait_req, NULL); }
With atomic helpers there's no need to track the enabled state of a pipe any more, because atomic helpers track this accurately already.
Just disable the early returns, since the debug checks might be useful.
v2: Don't call drm_helper_disable_unused_functions, it blows up without this check. At least explains why rockchip still needed this old legacy-style state tracing - to work around issues from calling other legacy style functions!
Cc: Tomeu Vizoso tomeu.vizoso@collabora.com Cc: Mark yao mark.yao@rock-chips.com Tested-by: Tomeu Vizoso tomeu.vizoso@collabora.com Reviewed-by: Tomeu Vizoso tomeu.vizoso@collabora.com Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch --- drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 3 --- drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 6 ------ 2 files changed, 9 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c index f261512bb4a0..245a567f7b8c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c @@ -156,9 +156,6 @@ int rockchip_drm_fbdev_init(struct drm_device *dev) goto err_drm_fb_helper_fini; }
- /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); - ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); if (ret < 0) { dev_err(dev->dev, "Failed to set initial hw config - %d.\n", diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 5567fb43e674..957a6b4917c8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -431,9 +431,6 @@ static void vop_enable(struct drm_crtc *crtc) struct vop *vop = to_vop(crtc); int ret;
- if (vop->is_enabled) - return; - ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); @@ -501,9 +498,6 @@ static void vop_crtc_disable(struct drm_crtc *crtc) struct vop *vop = to_vop(crtc); int i;
- if (!vop->is_enabled) - return; - /* * We need to make sure that all windows are disabled before we * disable that crtc. Otherwise we might try to scan from a destroyed
It's not permissible to look at plane->state from interrupt context, since doing that would need the irq handler to acquire the plane->mutex lock.
The other problem is that if we pipeline updates using the new nonblocking atomic helpers new state gets commit before the irq handler fires, resulting in a lost event.
Fix both issues by caching the necessary values in vop_win, protected by dev->event_lock.
Cc: Tomeu Vizoso tomeu.vizoso@collabora.com Cc: Mark yao mark.yao@rock-chips.com Tested-by: Tomeu Vizoso tomeu.vizoso@collabora.com Reviewed-by: Tomeu Vizoso tomeu.vizoso@collabora.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 957a6b4917c8..94eaeec29b6b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -98,7 +98,9 @@ struct vop_win { const struct vop_win_data *data; struct vop *vop;
- struct vop_plane_state state; + /* protected by dev->event_lock */ + bool enable; + dma_addr_t yrgb_mst; };
struct vop { @@ -112,6 +114,8 @@ struct vop { bool vsync_work_pending; struct completion dsp_hold_completion; struct completion wait_update_complete; + + /* protected by dev->event_lock */ struct drm_pending_vblank_event *event;
const struct vop_data *data; @@ -652,6 +656,11 @@ static void vop_plane_atomic_disable(struct drm_plane *plane, if (!old_state->crtc) return;
+ spin_lock_irq(&plane->dev->event_lock); + vop_win->enable = false; + vop_win->yrgb_mst = 0; + spin_unlock_irq(&plane->dev->event_lock); + spin_lock(&vop->reg_lock);
VOP_WIN_SET(vop, win, enable, 0); @@ -686,7 +695,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, /* * can't update plane when vop is disabled. */ - if (!crtc) + if (WARN_ON(!crtc)) return;
if (WARN_ON(!vop->is_enabled)) @@ -715,6 +724,11 @@ static void vop_plane_atomic_update(struct drm_plane *plane, offset += (src->y1 >> 16) * fb->pitches[0]; vop_plane_state->yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
+ spin_lock_irq(&plane->dev->event_lock); + vop_win->enable = true; + vop_win->yrgb_mst = vop_plane_state->yrgb_mst; + spin_unlock_irq(&plane->dev->event_lock); + spin_lock(&vop->reg_lock);
VOP_WIN_SET(vop, win, format, vop_plane_state->format); @@ -1074,16 +1088,14 @@ static const struct drm_crtc_funcs vop_crtc_funcs = {
static bool vop_win_pending_is_complete(struct vop_win *vop_win) { - struct drm_plane *plane = &vop_win->base; - struct vop_plane_state *state = to_vop_plane_state(plane->state); dma_addr_t yrgb_mst;
- if (!state->enable) + if (!vop_win->enable) return VOP_WIN_GET(vop_win->vop, vop_win->data, enable) == 0;
yrgb_mst = VOP_WIN_GET_YRGBADDR(vop_win->vop, vop_win->data);
- return yrgb_mst == state->yrgb_mst; + return yrgb_mst == vop_win->yrgb_mst; }
static void vop_handle_vblank(struct vop *vop)
With the various bits fixed rockchip now has an atomic compliant handling/signalling of crtc_state->event, which means we can just switch over to the new nonblocking helpers and remove some code.
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.
v5: Add more WARN_ON to check vop->event isn't clobbered.
Cc: Tomeu Vizoso tomeu.vizoso@collabora.com Cc: Mark yao mark.yao@rock-chips.com Tested-by: Tomeu Vizoso tomeu.vizoso@collabora.com Reviewed-by: Tomeu Vizoso tomeu.vizoso@collabora.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 | 20 +++++++- 4 files changed, 27 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 3348c0878d4b..20f12bc5a386 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -228,87 +228,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 * @@ -339,4 +284,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 94eaeec29b6b..d2932478ff59 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -502,6 +502,8 @@ static void vop_crtc_disable(struct drm_crtc *crtc) struct vop *vop = to_vop(crtc); int i;
+ WARN_ON(vop->event); + /* * We need to make sure that all windows are disabled before we * disable that crtc. Otherwise we might try to scan from a destroyed @@ -551,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) @@ -939,6 +949,8 @@ static void vop_crtc_enable(struct drm_crtc *crtc) u16 vact_end = vact_st + vdisplay; uint32_t val;
+ WARN_ON(vop->event); + vop_enable(crtc); /* * If dclk rate is zero, mean that scanout is stop, @@ -1035,12 +1047,15 @@ 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); + WARN_ON(vop->event);
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 = { @@ -1110,15 +1125,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 Tested-by: Tomeu Vizoso tomeu.vizoso@collabora.com Reviewed-by: Tomeu Vizoso tomeu.vizoso@collabora.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 d2932478ff59..8cd840f602b7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -894,30 +894,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,
On Wed, Jun 08, 2016 at 02:19:13PM +0200, Daniel Vetter wrote:
Ok, I merged up to this one, excluding i915 code (that needs a tiny bit of polish, plus close inspection by CI). Thanks to everyone who helped testing and provided feedback on this work. -Daniel
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 Tested-by: Gerd Hoffmann kraxel@redhat.com Reviewed-by: 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 ba5e11ba9f3a..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_crtc_send_vblank_event(crtc, 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); }
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!).
v2: Rebased on top of
commit e7cf0963f816fa44190caaf51aeffaa614c340c6 Author: Gerd Hoffmann kraxel@redhat.com Date: Tue May 31 08:50:47 2016 +0200
virtio-gpu: add atomic_commit function
Cc: Gerd Hoffmann kraxel@redhat.com Tested-by: Gerd Hoffmann kraxel@redhat.com (v1) Reviewed-by: Gerd Hoffmann kraxel@redhat.com (v1) Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/virtio/virtgpu_display.c | 71 ++++++-------------------------- 1 file changed, 13 insertions(+), 58 deletions(-)
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 3d0fa049b34c..a09dc57fea9c 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -38,56 +38,11 @@ #define XRES_MAX 8192 #define YRES_MAX 8192
-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_crtc_send_vblank_event(crtc, event); - spin_unlock_irqrestore(&crtc->dev->event_lock, irqflags); - } - - return 0; -} - static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = { .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, @@ -185,6 +140,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); }
@@ -388,30 +344,28 @@ virtio_gpu_user_framebuffer_create(struct drm_device *dev, return &virtio_gpu_fb->base; }
-static int vgdev_atomic_commit(struct drm_device *dev, - struct drm_atomic_state *state, - bool nonblock) +static void vgdev_atomic_commit_tail(struct drm_atomic_state *state) { - if (nonblock) - return -EBUSY; - - drm_atomic_helper_swap_state(state, true); - drm_atomic_helper_wait_for_fences(dev, state); + struct drm_device *dev = state->dev;
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); + drm_atomic_helper_wait_for_vblanks(dev, state); drm_atomic_helper_cleanup_planes(dev, state); - drm_atomic_state_free(state); - return 0; }
+struct drm_mode_config_helper_funcs virtio_mode_config_helpers = { + .atomic_commit_tail = vgdev_atomic_commit_tail, +}; + static const struct drm_mode_config_funcs virtio_gpu_mode_funcs = { .fb_create = virtio_gpu_user_framebuffer_create, .atomic_check = drm_atomic_helper_check, - .atomic_commit = vgdev_atomic_commit, + .atomic_commit = drm_atomic_helper_commit, };
int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) @@ -419,7 +373,8 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) int i;
drm_mode_config_init(vgdev->ddev); - vgdev->ddev->mode_config.funcs = (void *)&virtio_gpu_mode_funcs; + vgdev->ddev->mode_config.funcs = &virtio_gpu_mode_funcs; + vgdev->ddev->mode_config.helper_private = &virtio_mode_config_helpers;
/* modes will be validated against the framebuffer size */ vgdev->ddev->mode_config.min_width = XRES_MIN;
On Fri, Jun 10, 2016 at 12:07:53AM +0200, Daniel Vetter wrote:
Gerd, can you pls retest? I think due to your change in the above referenced commit to switch to active_only=true in commit_planes() this is now broken. -Daniel
On Fr, 2016-06-10 at 17:20 +0200, Daniel Vetter wrote:
Yes, probably it'll break things.
Any branch I can test? Your "stuff" branch seems to not yet have the commit above.
I guess virtio-gpu should switch over to override .atomic_commit_tail instead of .atomic_commit?
cheers, Gerd
On Mon, Jun 13, 2016 at 11:20 AM, Gerd Hoffmann kraxel@redhat.com wrote:
See v2 that I've sent out - it contains these changes already. Patch applies on top of topic/drm-misc, but I'll be pushing out a new version of stuff asap. -Daniel
On Tue, Jun 14, 2016 at 04:25:45PM +0200, Gerd Hoffmann wrote:
If no noise in dmesg and not stalls and no issues, then it should be all fine. I've removed the (v1) disclaimer and applied the patch to drm-misc, thanks a lot for testing and reviewing. -Daniel
Drivers transitioning to atomic might not yet want to enable full DRIVER_ATOMIC support when it's not entirely working. But using atomic internally makes a lot more sense earlier.
Instead of spreading such flags to more places I figured it's simpler to just check for mode_config->funcs->atomic_commit, and use atomic paths if that is set. For the only driver currently transitioning (i915) this does the right thing.
Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_fb_helper.c | 6 ++---- drivers/gpu/drm/i915/intel_fbdev.c | 2 -- include/drm/drm_fb_helper.h | 11 ----------- 3 files changed, 2 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index c0e0a2e78d75..ba2fcb2a68ad 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -385,7 +385,7 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
drm_warn_on_modeset_not_all_locked(dev);
- if (fb_helper->atomic) + if (dev->mode_config.funcs->atomic_commit) return restore_fbdev_mode_atomic(fb_helper);
drm_for_each_plane(plane, dev) { @@ -716,8 +716,6 @@ int drm_fb_helper_init(struct drm_device *dev, i++; }
- fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC); - return 0; out_free: drm_fb_helper_crtc_free(fb_helper); @@ -1344,7 +1342,7 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, return -EBUSY; }
- if (fb_helper->atomic) { + if (dev->mode_config.funcs->atomic_commit) { ret = pan_display_atomic(var, info); goto unlock; } diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index ef8e67690f3d..4c725ad6fb54 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -724,8 +724,6 @@ int intel_fbdev_init(struct drm_device *dev) return ret; }
- ifbdev->helper.atomic = true; - dev_priv->fbdev = ifbdev; INIT_WORK(&dev_priv->fbdev_suspend_work, intel_fbdev_suspend_worker);
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 5b4aa35026a3..db8d4780eaa2 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -212,17 +212,6 @@ struct drm_fb_helper { * needs to be reprobe when fbdev is in control again. */ bool delayed_hotplug; - - /** - * @atomic: - * - * Use atomic updates for restore_fbdev_mode(), etc. This defaults to - * true if driver has DRIVER_ATOMIC feature flag, but drivers can - * override it to true after drm_fb_helper_init() if they support atomic - * modeset but do not yet advertise DRIVER_ATOMIC (note that fb-helper - * does not require ASYNC commits). - */ - bool atomic; };
#ifdef CONFIG_DRM_FBDEV_EMULATION
On Wed, 8 Jun 2016 14:19:15 +0200 Daniel Vetter daniel.vetter@ffwll.ch wrote:
Reviewed-by: Boris Brezillon boris.brezillon@free-electrons.com
On Wed, Jun 8, 2016 at 8:19 PM, Daniel Vetter daniel.vetter@ffwll.ch wrote:
Well, imx-drm is transitioning. Unfortunately, after applying this patch, legacy fbdev cannot enable displays when the imx-drm transitional patch set reaches phase 3 step 1[1].
For those transitioning drivers with fine-grained steps, this patch is likely to expose the atomic function __drm_atomic_helper_set_config to legacy fbdev support too early. They could still be using drm_crtc_helper_set_config when adding ->atomic_commit.
BTW, this patch and those for generic nonblocking commit are making the imx-drm transition a bit harder. Not good timing probably. But, I'll go on with the task anyway.
[1] https://patchwork.kernel.org/patch/9144035/
Regards, Liu Ying
On Sun, Jun 12, 2016 at 05:01:27PM +0800, Ying Liu wrote:
Hm, making transition harder wasn't really the intention. What exactly is blowing up for you? At least how I planned the transition the first thing you should be able to do is basic modesets (once you fill out ->atomic_commit), so I hoped that this wouldn't cause trouble.
Wrt nonblocking commit helpers breaking transitioning drivers: The most likely cause is your driver isn't handling crtc_state->event yet. Before you start using ->atomic_commit or one of the helpers to map legacy ioctl to atomic, you need to fill out some code to handle that in ->atomic_begin or ->atomic_flush. Then the transition should still work as before.
I'll be happy to help you out debug this, and then update my blog post with the transition plan with the latest findings.
Thanks, Daniel
On Mon, Jun 13, 2016 at 3:58 PM, Daniel Vetter daniel@ffwll.ch wrote:
At the imx-drm transition phase 1, ipu_plane_atomic_check checks crtc->enabled and hopes it to be true. Till phase 3 step 1, this function is not changed. This patch exposes restore_fbdev_mode_atomic and pan_display_atomic to legacy fbdev too early. Both of them call drm_atomic_commit which does plane check at atomic check stage. Then, the plane check fails for the legacy fbdev, because drm_atomic_commit sets crtc->enabled later than the legacy path drm_crtc_helper_set_mode does. Actually, drm_atomic_commit doesn't set crtc->enabled until the commit stage if you use the atomic helper.
OTOH, we fill out ->atomic_commit with drm_atomic_helper_commit at phase 3 step 1, then exposing drm_atomic_commit means that we need to handle crtc_state->event no later than phase 3 step 1... I haven't decided the right order/process to add the handling.
So, would it be an option to revert this patch...
I do have confusion on the event handling in some drivers' ->atomic_flush. At least sun4i, kirin and fsl-dcu have the below snip: ========================================== 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); } ========================================== It looks drm_crtc_vblank_get seldom fails? And why do we send vblank event when it fails?
Regards, Liu Ying
On Mon, Jun 13, 2016 at 05:26:28PM +0800, Ying Liu wrote:
Hm, my idea was that in phase 2 (when you roll out state structures and checks), you'd change your atomic_check functions from looking at legacy stuff like crtc->enabled, to instead look at new state like crtc_state->enabled.
Yes, this is a change: Before you can start with phase 3 you need to handle crtc_state->event in atomic_flush (or a similar place).
So, would it be an option to revert this patch...
I'd really like to avoid that - the old hack of adding a knob for every place we should use atomic was getting complicated :( And right now there's many more atomic drivers than legacy ones, and only 2 drivers in transition (i915 and imx). Given that I think we should optimize more for atomic drivers.
But like I said, of course transition shouldn't be too painful.
If it never fails for you, then you haven't put drm_crtc_vblank_on/off() into your crtc enable/disable callbacks. Ofc this assumes you have real vblank support.
And why do we send vblank event when it fails?
This is just the most failsafe way to make sure the event gets out. It's rather inaccurate though, so would be much better if you can make that decision by instead looking at crtc->state->active, like i915 does. drm_crtc_vblank_get always works in-betwen drm_crtc_vblank_on/off() calls, i.e. when the crtc is supposed to be on. And when you disable the crtc and it stays disable then you should send out the vblank event directly, after the pipe is off. But that's really the only case where you should call drm_crtc_send_vblank_event directly.
The kerneldoc for these functions try to explain this a bit ...
Anyway, pls don't look at these drivers - I just hacked them up because they didn't implement event handling at all. Which is breaking atomic. For a good example of how it should be done, look at rockchip (already merged in drm-misc) or i915 (patches on the m-l). -Daniel
On Mon, Jun 13, 2016 at 10:01 PM, Daniel Vetter daniel@ffwll.ch wrote:
It looks calling drm_vblank_off in crtc_helper->disable will set vblank->enabled to false and increase vblank->refcount by one. The next enable operation could call crtc_helper->atomic_begin or atomic_flush and then crtc_helper->enable. In ->atomic_begin or atomic_flush, it looks drm_crtc_vblank_get could be called. The problem is that drm_crtc_vblank_get finds vblank->refcount is not 1 after an add-1-operation and vblank->enabled is false, which finally makes the function return -EINVAL. How to handle this problem?
Regards, Liu Ying
On Mon, Jun 20, 2016 at 01:55:57PM +0800, Ying Liu wrote:
That's how it's supposed to work: When the vblank is off, then you can't send out vblank events. If the pipe gets (re)enabled, then you need to move the vblank generation to where it's fully on. If it stays disabled, then you can directly call drm_crtc_send_vblank_event.
btw much easier to discuss this on irc. -Daniel
This was somehow lost between v3 and the merged version in Maarten's patch merged as:
commit f2d580b9a8149735cbc4b59c4a8df60173658140 Author: Maarten Lankhorst maarten.lankhorst@linux.intel.com Date: Wed May 4 14:38:26 2016 +0200
drm/core: Do not preserve framebuffer on rmfb, v4.
Actual code copied from Maarten's patch, but with the slight change to just use dev->mode_config.funcs->atomic_commit to decide whether to use the atomic path or not.
Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/drm_atomic.c | 66 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_crtc.c | 6 ++++ drivers/gpu/drm/drm_crtc_internal.h | 1 + 3 files changed, 73 insertions(+)
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index d99ab2f6663f..dac0875e669c 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1564,6 +1564,72 @@ void drm_atomic_clean_old_fb(struct drm_device *dev, } EXPORT_SYMBOL(drm_atomic_clean_old_fb);
+int drm_atomic_remove_fb(struct drm_framebuffer *fb) +{ + struct drm_modeset_acquire_ctx ctx; + struct drm_device *dev = fb->dev; + struct drm_atomic_state *state; + struct drm_plane *plane; + int ret = 0; + unsigned plane_mask; + + state = drm_atomic_state_alloc(dev); + if (!state) + return -ENOMEM; + + drm_modeset_acquire_init(&ctx, 0); + state->acquire_ctx = &ctx; + +retry: + plane_mask = 0; + ret = drm_modeset_lock_all_ctx(dev, &ctx); + if (ret) + goto unlock; + + drm_for_each_plane(plane, dev) { + struct drm_plane_state *plane_state; + + if (plane->state->fb != fb) + continue; + + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto unlock; + } + + drm_atomic_set_fb_for_plane(plane_state, NULL); + ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); + if (ret) + goto unlock; + + plane_mask |= BIT(drm_plane_index(plane)); + + plane->old_fb = plane->fb; + plane->fb = NULL; + } + + if (plane_mask) + ret = drm_atomic_commit(state); + +unlock: + if (plane_mask) + drm_atomic_clean_old_fb(dev, plane_mask, ret); + + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + + if (ret || !plane_mask) + drm_atomic_state_free(state); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return ret; +} + int drm_mode_atomic_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index ce0569c3f942..60a0a646dd6e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -600,6 +600,11 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) * in this manner. */ if (drm_framebuffer_read_refcount(fb) > 1) { + if (dev->mode_config.funcs->atomic_commit) { + drm_atomic_remove_fb(fb); + goto out; + } + drm_modeset_lock_all(dev); /* remove from any CRTC */ drm_for_each_crtc(crtc, dev) { @@ -621,6 +626,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) drm_modeset_unlock_all(dev); }
+out: drm_framebuffer_unreference(fb); } EXPORT_SYMBOL(drm_framebuffer_remove); diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 8186c0e05c42..b426d37bc916 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -125,4 +125,5 @@ int drm_atomic_get_property(struct drm_mode_object *obj, struct drm_property *property, uint64_t *val); int drm_mode_atomic_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int drm_atomic_remove_fb(struct drm_framebuffer *fb);
Op 08-06-16 om 14:19 schreef Daniel Vetter:
It passes the IGT kms_rmfb test, so I'm happy. :)
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
On Wed, Jul 13, 2016 at 12:15:17PM +0200, Maarten Lankhorst wrote:
Applied to drm-misc, thanks for the review. -Daniel
Atomic drivers are supposed to do hw/sw state reset with the drm_mode_config_reset() call right above it.
Cc: Benjamin Gaignard benjamin.gaignard@linaro.org Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch --- drivers/gpu/drm/sti/sti_drv.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index dd2c400c4a46..26aa85d4b872 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -279,7 +279,6 @@ static int sti_load(struct drm_device *dev, unsigned long flags)
drm_mode_config_reset(dev);
- drm_helper_disable_unused_functions(dev); drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc, dev->mode_config.num_connector);
Rockchip just blew up here on testing, because I removed some "is this crtc already disabled/enabled" state tracking from callbacks (not needed with atomic). Turns out that was needed to work around rockchip still calling legacy helper code.
Since me explaining on irc/mailing-list plus kerneldoc isn't enough, be more verbose and add dmesg output. Not that anyone actually reads that, either.
Cc: Tomeu Vizoso tomeu.vizoso@collabora.com Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_crtc_helper.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index a6e42433ef0e..b47ec24939a0 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -232,6 +232,9 @@ static void __drm_helper_disable_unused_functions(struct drm_device *dev) */ void drm_helper_disable_unused_functions(struct drm_device *dev) { + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) + DRM_ERROR("Called for atomic driver, this is not what you want.\n"); + drm_modeset_lock_all(dev); __drm_helper_disable_unused_functions(dev); drm_modeset_unlock_all(dev);
Hi Daniel,
Thank you for the patch.
On Wednesday 08 Jun 2016 14:19:18 Daniel Vetter wrote:
How about also removing it from atomic drivers then ? At least omapdrm calls this function.
On Thu, Jun 09, 2016 at 01:36:30AM +0300, Laurent Pinchart wrote:
I tried, and thought I caught them all. See preceeding sti patch. Not sure why I missed it, will remedy. -Daniel
Hi Daniel,
On Thursday 09 Jun 2016 10:26:44 Daniel Vetter wrote:
With the call removed from drivers,
Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
On Tue, Jun 21, 2016 at 12:12:54PM +0300, Laurent Pinchart wrote:
Applied to drm-misc, thanks for double-checking. -Daniel
It's a legacy helper function which won't do good with atomic helpers.
Cc: Tomi Valkeinen tomi.valkeinen@ti.com Cc: Laurent Pinchart laurent.pinchart@ideasonboard.com Signed-off-by: Daniel Vetter daniel.vetter@intel.com --- drivers/gpu/drm/omapdrm/omap_fbdev.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 31dfa0893416..adb10fbe918d 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -279,9 +279,6 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) if (ret) goto fini;
- /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); - ret = drm_fb_helper_initial_config(helper, 32); if (ret) goto fini;
Hi Daniel,
Thank you for the patch.
On Friday 10 Jun 2016 00:14:39 Daniel Vetter wrote:
Acked-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
On Fri, Jun 10, 2016 at 09:26:42AM +0300, Tomi Valkeinen wrote:
Sorry for the long delay. I merged this into drm-misc so that I can merge the patch to WARN_ON when using this function for atomic drivers, too. Too many people hit this little snag ;-) -Daniel
kernel-doc wants a : at the end.
Signed-off-by: Daniel Vetter daniel.vetter@ffwll.ch --- drivers/gpu/drm/drm_atomic_helper.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 73b345323cf1..2bcba98976a2 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -463,7 +463,7 @@ mode_fixup(struct drm_atomic_state *state) * times for the same update, e.g. when the ->atomic_check functions depend upon * the adjusted dotclock for fifo space allocation and watermark computation. * - * RETURNS + * RETURNS: * Zero for success or -errno */ int @@ -577,7 +577,7 @@ EXPORT_SYMBOL(drm_atomic_helper_check_modeset); * It also sets crtc_state->planes_changed to indicate that a crtc has * updated planes. * - * RETURNS + * RETURNS: * Zero for success or -errno */ int @@ -645,7 +645,7 @@ EXPORT_SYMBOL(drm_atomic_helper_check_planes); * ->atomic_check functions depend upon an updated adjusted_mode.clock to * e.g. properly compute watermarks. * - * RETURNS + * RETURNS: * Zero for success or -errno */ int drm_atomic_helper_check(struct drm_device *dev, @@ -1204,7 +1204,7 @@ static void commit_work(struct work_struct *work) * ->atomic_commit_tail() callback of the &drm_mode_config_helper_funcs vtable, * or it's default implementation drm_atomic_helper_commit_tail(). * - * RETURNS + * RETURNS: * Zero for success or -errno. */ int drm_atomic_helper_commit(struct drm_device *dev,
Hey,
Op 08-06-16 om 14:18 schreef Daniel Vetter:
Would be better if swap_state could error out, but this will do for now.
Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com
- 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.
v2: Rebased on top of
commit e7cf0963f816fa44190caaf51aeffaa614c340c6 Author: Gerd Hoffmann kraxel@redhat.com Date: Tue May 31 08:50:47 2016 +0200
virtio-gpu: add atomic_commit function
Cc: Gerd Hoffmann kraxel@redhat.com Reviewed-by: Maarten Lankhorst maarten.lankhorst@linux.intel.com Cc: Maarten Lankhorst maarten.lankhorst@linux.intel.com 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 +- drivers/gpu/drm/virtio/virtgpu_display.c | 2 +- include/drm/drm_atomic_helper.h | 4 ++-- 14 files changed, 18 insertions(+), 18 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 4b2c1d27e74b..aa2cad922791 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1167,7 +1167,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 @@ -1538,8 +1538,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 @@ -1561,8 +1561,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 843b21c540b3..4a679fb9bb02 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 473c8fdb38b9..3bc0c82bb58e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13703,7 +13703,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 b7e5f4a736f0..04e901a80234 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 8c3b463620bd..4a8a6f1f1151 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 3b702230a88c..6b97011154bf 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -174,7 +174,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 86c109b16876..6bb032d8ac6b 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 755cfdba61cd..3348c0878d4b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -289,7 +289,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 a5bc2037e9df..9a217fd025f3 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -156,7 +156,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/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index d82ae1cddfcf..3d0fa049b34c 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -395,7 +395,7 @@ static int vgdev_atomic_commit(struct drm_device *dev, if (nonblock) return -EBUSY;
- drm_atomic_helper_swap_state(dev, state); + drm_atomic_helper_swap_state(state, true); drm_atomic_helper_wait_for_fences(dev, state);
drm_atomic_helper_commit_modeset_disables(dev, state); diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 1877a7c18d8e..51c57eaa2c8f 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,
dri-devel@lists.freedesktop.org