Patchwork [RFC,v2,1/7] drm/atomic: initial support for asynchronous plane update

login
register
mail settings
Submitter Gustavo F. Padovan
Date April 27, 2017, 3:15 p.m.
Message ID <20170427151519.8308-2-gustavo@padovan.org>
Download mbox | patch
Permalink /patch/9703021/
State New
Headers show

Comments

Gustavo F. Padovan - April 27, 2017, 3:15 p.m.
From: Gustavo Padovan <gustavo.padovan@collabora.com>

In some cases, like cursor updates, it is interesting to update the
plane in an asynchronous fashion to avoid big delays. The current queued
update could be still waiting for a fence to signal and thus block any
subsequent update until its scan out. In cases like this if we update the
cursor synchronously through the atomic API it will cause significant
delays that would even be noticed by the final user.

This patch creates a fast path to jump ahead the current queued state and
do single planes updates without going through all atomic step in
drm_atomic_helper_commit().

We take this path for legacy cursor updates. Users can also set the
DRM_MODE_ATOMIC_ASYNC_UPDATE flag on atomic updates.

For now only single plane updates are supported, but we plan to support
multiple planes updates and async PageFlips through this interface as well
in the near future.

v2:
	- allow updates even if there is a queued update for the same
	plane.
        - fixes on the documentation (Emil Velikov)
        - unconditionally call ->atomic_async_update (Emil Velikov)
        - check for ->atomic_async_update earlier (Daniel Vetter)
        - make ->atomic_async_check() the last step (Daniel Vetter)
        - add ASYNC_UPDATE flag (Eric Anholt)
        - update state in core after ->atomic_async_update (Eric Anholt)
	- update docs (Eric Anholt)

Cc: Daniel Vetter <daniel.vetter@intel.com>
Cc: Rob Clark <robdclark@gmail.com>
Cc: Eric Anholt <eric@anholt.net>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.com>
---
 drivers/gpu/drm/drm_atomic.c             | 50 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_atomic_helper.c      | 48 ++++++++++++++++++++++++++++++
 include/drm/drm_atomic.h                 |  2 ++
 include/drm/drm_atomic_helper.h          |  2 ++
 include/drm/drm_modeset_helper_vtables.h | 45 ++++++++++++++++++++++++++++
 include/uapi/drm/drm_mode.h              |  4 ++-
 6 files changed, 150 insertions(+), 1 deletion(-)
Ville Syrjälä - April 27, 2017, 3:37 p.m.
On Thu, Apr 27, 2017 at 12:15:13PM -0300, Gustavo Padovan wrote:
> From: Gustavo Padovan <gustavo.padovan@collabora.com>
> 
> In some cases, like cursor updates, it is interesting to update the
> plane in an asynchronous fashion to avoid big delays. The current queued
> update could be still waiting for a fence to signal and thus block any
> subsequent update until its scan out. In cases like this if we update the
> cursor synchronously through the atomic API it will cause significant
> delays that would even be noticed by the final user.
> 
> This patch creates a fast path to jump ahead the current queued state and
> do single planes updates without going through all atomic step in
> drm_atomic_helper_commit().
> 
> We take this path for legacy cursor updates. Users can also set the
> DRM_MODE_ATOMIC_ASYNC_UPDATE flag on atomic updates.
> 
> For now only single plane updates are supported, but we plan to support
> multiple planes updates and async PageFlips through this interface as well
> in the near future.
> 
> v2:
> 	- allow updates even if there is a queued update for the same
> 	plane.
>         - fixes on the documentation (Emil Velikov)
>         - unconditionally call ->atomic_async_update (Emil Velikov)
>         - check for ->atomic_async_update earlier (Daniel Vetter)
>         - make ->atomic_async_check() the last step (Daniel Vetter)
>         - add ASYNC_UPDATE flag (Eric Anholt)
>         - update state in core after ->atomic_async_update (Eric Anholt)
> 	- update docs (Eric Anholt)
> 
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> Cc: Rob Clark <robdclark@gmail.com>
> Cc: Eric Anholt <eric@anholt.net>
> Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.com>
> ---
>  drivers/gpu/drm/drm_atomic.c             | 50 ++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_atomic_helper.c      | 48 ++++++++++++++++++++++++++++++
>  include/drm/drm_atomic.h                 |  2 ++
>  include/drm/drm_atomic_helper.h          |  2 ++
>  include/drm/drm_modeset_helper_vtables.h | 45 ++++++++++++++++++++++++++++
>  include/uapi/drm/drm_mode.h              |  4 ++-
>  6 files changed, 150 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 30229ab..7b60cf8 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -77,6 +77,7 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)
>  	 * setting this appropriately?
>  	 */
>  	state->allow_modeset = true;
> +	state->async_update = true;
>  
>  	state->crtcs = kcalloc(dev->mode_config.num_crtc,
>  			       sizeof(*state->crtcs), GFP_KERNEL);
> @@ -631,6 +632,51 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,
>  	return 0;
>  }
>  
> +static bool drm_atomic_async_check(struct drm_atomic_state *state)
> +{
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *crtc_state;
> +	struct drm_plane *__plane, *plane = NULL;
> +	struct drm_plane_state *__plane_state, *plane_state = NULL;
> +	const struct drm_plane_helper_funcs *funcs;
> +	int i, n_planes = 0;
> +
> +	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
> +		if (drm_atomic_crtc_needs_modeset(crtc_state))
> +			return false;
> +	}
> +
> +	for_each_new_plane_in_state(state, __plane, __plane_state, i) {
> +		n_planes++;
> +		plane = __plane;
> +		plane_state = __plane_state;
> +	}
> +
> +	/* FIXME: we support single plane updates for now */
> +	if (!plane || n_planes != 1)
> +		return false;
> +
> +	funcs = plane->helper_private;
> +	if (!funcs->atomic_async_update)
> +		return false;
> +
> +	if (plane_state->fence)
> +		return false;
> +
> +	if (!plane->state->crtc || (plane->state->crtc != plane_state->crtc))
> +		return false;
> +
> +	/* No configuring new scaling in the async path. */

Those checks aren't really about scaling. Well, they are also about
scaling, but they're mainly about changing size.

What I don't really understand is why we're enforcing these restrictions
in the core but leaving other restrictions up to the driver. I don't see
size changes as anything really special compared to many of the other
restrictions that would now be up to each driver.

If you're really after some lowest common denominator as far as
exposed capabilities are concerned then I think the core should do
more checking. OTOH if you're not interested limiting what each
driver exposes then I don't see why the core checks anything at all.

> +	if (plane->state->crtc_w != plane_state->crtc_w ||
> +	    plane->state->crtc_h != plane_state->crtc_h ||
> +	    plane->state->src_w != plane_state->src_w ||
> +	    plane->state->src_h != plane_state->src_h) {
> +		return false;
> +	}
> +
> +	return !funcs->atomic_async_check(plane, plane_state);
> +}
> +
>  static void drm_atomic_crtc_print_state(struct drm_printer *p,
>  		const struct drm_crtc_state *state)
>  {
<snip> 
>  /**
> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> index 8c67fc0..7c067ca 100644
> --- a/include/uapi/drm/drm_mode.h
> +++ b/include/uapi/drm/drm_mode.h
> @@ -646,13 +646,15 @@ struct drm_mode_destroy_dumb {
>  #define DRM_MODE_ATOMIC_TEST_ONLY 0x0100
>  #define DRM_MODE_ATOMIC_NONBLOCK  0x0200
>  #define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400
> +#define DRM_MODE_ATOMIC_ASYNC_UPDATE 0x0800

What exactly is that flag supposed to mean?

>  
>  #define DRM_MODE_ATOMIC_FLAGS (\
>  		DRM_MODE_PAGE_FLIP_EVENT |\
>  		DRM_MODE_PAGE_FLIP_ASYNC |\
>  		DRM_MODE_ATOMIC_TEST_ONLY |\
>  		DRM_MODE_ATOMIC_NONBLOCK |\
> -		DRM_MODE_ATOMIC_ALLOW_MODESET)
> +		DRM_MODE_ATOMIC_ALLOW_MODESET |\
> +		DRM_MODE_ATOMIC_ASYNC_UPDATE)
>  
>  struct drm_mode_atomic {
>  	__u32 flags;
> -- 
> 2.9.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Gustavo F. Padovan - April 27, 2017, 7:35 p.m.
Hi Ville,

2017-04-27 Ville Syrjälä <ville.syrjala@linux.intel.com>:

> On Thu, Apr 27, 2017 at 12:15:13PM -0300, Gustavo Padovan wrote:
> > From: Gustavo Padovan <gustavo.padovan@collabora.com>
> > 
> > In some cases, like cursor updates, it is interesting to update the
> > plane in an asynchronous fashion to avoid big delays. The current queued
> > update could be still waiting for a fence to signal and thus block any
> > subsequent update until its scan out. In cases like this if we update the
> > cursor synchronously through the atomic API it will cause significant
> > delays that would even be noticed by the final user.
> > 
> > This patch creates a fast path to jump ahead the current queued state and
> > do single planes updates without going through all atomic step in
> > drm_atomic_helper_commit().
> > 
> > We take this path for legacy cursor updates. Users can also set the
> > DRM_MODE_ATOMIC_ASYNC_UPDATE flag on atomic updates.
> > 
> > For now only single plane updates are supported, but we plan to support
> > multiple planes updates and async PageFlips through this interface as well
> > in the near future.
> > 
> > v2:
> > 	- allow updates even if there is a queued update for the same
> > 	plane.
> >         - fixes on the documentation (Emil Velikov)
> >         - unconditionally call ->atomic_async_update (Emil Velikov)
> >         - check for ->atomic_async_update earlier (Daniel Vetter)
> >         - make ->atomic_async_check() the last step (Daniel Vetter)
> >         - add ASYNC_UPDATE flag (Eric Anholt)
> >         - update state in core after ->atomic_async_update (Eric Anholt)
> > 	- update docs (Eric Anholt)
> > 
> > Cc: Daniel Vetter <daniel.vetter@intel.com>
> > Cc: Rob Clark <robdclark@gmail.com>
> > Cc: Eric Anholt <eric@anholt.net>
> > Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.com>
> > ---
> >  drivers/gpu/drm/drm_atomic.c             | 50 ++++++++++++++++++++++++++++++++
> >  drivers/gpu/drm/drm_atomic_helper.c      | 48 ++++++++++++++++++++++++++++++
> >  include/drm/drm_atomic.h                 |  2 ++
> >  include/drm/drm_atomic_helper.h          |  2 ++
> >  include/drm/drm_modeset_helper_vtables.h | 45 ++++++++++++++++++++++++++++
> >  include/uapi/drm/drm_mode.h              |  4 ++-
> >  6 files changed, 150 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> > index 30229ab..7b60cf8 100644
> > --- a/drivers/gpu/drm/drm_atomic.c
> > +++ b/drivers/gpu/drm/drm_atomic.c
> > @@ -77,6 +77,7 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)
> >  	 * setting this appropriately?
> >  	 */
> >  	state->allow_modeset = true;
> > +	state->async_update = true;
> >  
> >  	state->crtcs = kcalloc(dev->mode_config.num_crtc,
> >  			       sizeof(*state->crtcs), GFP_KERNEL);
> > @@ -631,6 +632,51 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,
> >  	return 0;
> >  }
> >  
> > +static bool drm_atomic_async_check(struct drm_atomic_state *state)
> > +{
> > +	struct drm_crtc *crtc;
> > +	struct drm_crtc_state *crtc_state;
> > +	struct drm_plane *__plane, *plane = NULL;
> > +	struct drm_plane_state *__plane_state, *plane_state = NULL;
> > +	const struct drm_plane_helper_funcs *funcs;
> > +	int i, n_planes = 0;
> > +
> > +	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
> > +		if (drm_atomic_crtc_needs_modeset(crtc_state))
> > +			return false;
> > +	}
> > +
> > +	for_each_new_plane_in_state(state, __plane, __plane_state, i) {
> > +		n_planes++;
> > +		plane = __plane;
> > +		plane_state = __plane_state;
> > +	}
> > +
> > +	/* FIXME: we support single plane updates for now */
> > +	if (!plane || n_planes != 1)
> > +		return false;
> > +
> > +	funcs = plane->helper_private;
> > +	if (!funcs->atomic_async_update)
> > +		return false;
> > +
> > +	if (plane_state->fence)
> > +		return false;
> > +
> > +	if (!plane->state->crtc || (plane->state->crtc != plane_state->crtc))
> > +		return false;
> > +
> > +	/* No configuring new scaling in the async path. */
> 
> Those checks aren't really about scaling. Well, they are also about
> scaling, but they're mainly about changing size.

I just copied the comment from the previous code in the drivers...
I can fix it.

> 
> What I don't really understand is why we're enforcing these restrictions
> in the core but leaving other restrictions up to the driver. I don't see
> size changes as anything really special compared to many of the other
> restrictions that would now be up to each driver.

Because I extracted what was common between msm, vc4 and i915 on their
legacy cursor update calls. I didn't want to enforce anything else in
core for now.

> 
> If you're really after some lowest common denominator as far as
> exposed capabilities are concerned then I think the core should do
> more checking. OTOH if you're not interested limiting what each
> driver exposes then I don't see why the core checks anything at all.

I think a common denominator is what we want but we only have 3 drivers
using it at the moment. We can look for more checks that shoud be done
is core. Any suggestions?

> 
> > +	if (plane->state->crtc_w != plane_state->crtc_w ||
> > +	    plane->state->crtc_h != plane_state->crtc_h ||
> > +	    plane->state->src_w != plane_state->src_w ||
> > +	    plane->state->src_h != plane_state->src_h) {
> > +		return false;
> > +	}
> > +
> > +	return !funcs->atomic_async_check(plane, plane_state);
> > +}
> > +
> >  static void drm_atomic_crtc_print_state(struct drm_printer *p,
> >  		const struct drm_crtc_state *state)
> >  {
> <snip> 
> >  /**
> > diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> > index 8c67fc0..7c067ca 100644
> > --- a/include/uapi/drm/drm_mode.h
> > +++ b/include/uapi/drm/drm_mode.h
> > @@ -646,13 +646,15 @@ struct drm_mode_destroy_dumb {
> >  #define DRM_MODE_ATOMIC_TEST_ONLY 0x0100
> >  #define DRM_MODE_ATOMIC_NONBLOCK  0x0200
> >  #define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400
> > +#define DRM_MODE_ATOMIC_ASYNC_UPDATE 0x0800
> 
> What exactly is that flag supposed to mean?

I don't think I provided a good explanation for it, sorry. This flag
tells core to jump ahead the queued update if the conditions in 
drm_atomic_async_check() are met. Useful for cursors and async PageFlips
over the atomic ioctl.

Gustavo
Ville Syrjälä - April 28, 2017, 8:57 a.m.
On Thu, Apr 27, 2017 at 04:35:37PM -0300, Gustavo Padovan wrote:
> Hi Ville,
> 
> 2017-04-27 Ville Syrjälä <ville.syrjala@linux.intel.com>:
> 
> > On Thu, Apr 27, 2017 at 12:15:13PM -0300, Gustavo Padovan wrote:
> > > From: Gustavo Padovan <gustavo.padovan@collabora.com>
> > > 
> > > In some cases, like cursor updates, it is interesting to update the
> > > plane in an asynchronous fashion to avoid big delays. The current queued
> > > update could be still waiting for a fence to signal and thus block any
> > > subsequent update until its scan out. In cases like this if we update the
> > > cursor synchronously through the atomic API it will cause significant
> > > delays that would even be noticed by the final user.
> > > 
> > > This patch creates a fast path to jump ahead the current queued state and
> > > do single planes updates without going through all atomic step in
> > > drm_atomic_helper_commit().
> > > 
> > > We take this path for legacy cursor updates. Users can also set the
> > > DRM_MODE_ATOMIC_ASYNC_UPDATE flag on atomic updates.
> > > 
> > > For now only single plane updates are supported, but we plan to support
> > > multiple planes updates and async PageFlips through this interface as well
> > > in the near future.
> > > 
> > > v2:
> > > 	- allow updates even if there is a queued update for the same
> > > 	plane.
> > >         - fixes on the documentation (Emil Velikov)
> > >         - unconditionally call ->atomic_async_update (Emil Velikov)
> > >         - check for ->atomic_async_update earlier (Daniel Vetter)
> > >         - make ->atomic_async_check() the last step (Daniel Vetter)
> > >         - add ASYNC_UPDATE flag (Eric Anholt)
> > >         - update state in core after ->atomic_async_update (Eric Anholt)
> > > 	- update docs (Eric Anholt)
> > > 
> > > Cc: Daniel Vetter <daniel.vetter@intel.com>
> > > Cc: Rob Clark <robdclark@gmail.com>
> > > Cc: Eric Anholt <eric@anholt.net>
> > > Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.com>
> > > ---
> > >  drivers/gpu/drm/drm_atomic.c             | 50 ++++++++++++++++++++++++++++++++
> > >  drivers/gpu/drm/drm_atomic_helper.c      | 48 ++++++++++++++++++++++++++++++
> > >  include/drm/drm_atomic.h                 |  2 ++
> > >  include/drm/drm_atomic_helper.h          |  2 ++
> > >  include/drm/drm_modeset_helper_vtables.h | 45 ++++++++++++++++++++++++++++
> > >  include/uapi/drm/drm_mode.h              |  4 ++-
> > >  6 files changed, 150 insertions(+), 1 deletion(-)
> > > 
> > > diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> > > index 30229ab..7b60cf8 100644
> > > --- a/drivers/gpu/drm/drm_atomic.c
> > > +++ b/drivers/gpu/drm/drm_atomic.c
> > > @@ -77,6 +77,7 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)
> > >  	 * setting this appropriately?
> > >  	 */
> > >  	state->allow_modeset = true;
> > > +	state->async_update = true;
> > >  
> > >  	state->crtcs = kcalloc(dev->mode_config.num_crtc,
> > >  			       sizeof(*state->crtcs), GFP_KERNEL);
> > > @@ -631,6 +632,51 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,
> > >  	return 0;
> > >  }
> > >  
> > > +static bool drm_atomic_async_check(struct drm_atomic_state *state)
> > > +{
> > > +	struct drm_crtc *crtc;
> > > +	struct drm_crtc_state *crtc_state;
> > > +	struct drm_plane *__plane, *plane = NULL;
> > > +	struct drm_plane_state *__plane_state, *plane_state = NULL;
> > > +	const struct drm_plane_helper_funcs *funcs;
> > > +	int i, n_planes = 0;
> > > +
> > > +	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
> > > +		if (drm_atomic_crtc_needs_modeset(crtc_state))
> > > +			return false;
> > > +	}
> > > +
> > > +	for_each_new_plane_in_state(state, __plane, __plane_state, i) {
> > > +		n_planes++;
> > > +		plane = __plane;
> > > +		plane_state = __plane_state;
> > > +	}
> > > +
> > > +	/* FIXME: we support single plane updates for now */
> > > +	if (!plane || n_planes != 1)
> > > +		return false;
> > > +
> > > +	funcs = plane->helper_private;
> > > +	if (!funcs->atomic_async_update)
> > > +		return false;
> > > +
> > > +	if (plane_state->fence)
> > > +		return false;
> > > +
> > > +	if (!plane->state->crtc || (plane->state->crtc != plane_state->crtc))
> > > +		return false;
> > > +
> > > +	/* No configuring new scaling in the async path. */
> > 
> > Those checks aren't really about scaling. Well, they are also about
> > scaling, but they're mainly about changing size.
> 
> I just copied the comment from the previous code in the drivers...
> I can fix it.
> 
> > 
> > What I don't really understand is why we're enforcing these restrictions
> > in the core but leaving other restrictions up to the driver. I don't see
> > size changes as anything really special compared to many of the other
> > restrictions that would now be up to each driver.
> 
> Because I extracted what was common between msm, vc4 and i915 on their
> legacy cursor update calls. I didn't want to enforce anything else in
> core for now.
> 
> > 
> > If you're really after some lowest common denominator as far as
> > exposed capabilities are concerned then I think the core should do
> > more checking. OTOH if you're not interested limiting what each
> > driver exposes then I don't see why the core checks anything at all.
> 
> I think a common denominator is what we want but we only have 3 drivers
> using it at the moment. We can look for more checks that shoud be done
> is core. Any suggestions?

My suggestion is that we should come up with some proper justification
for the checks that are done by the core. Otherwise I think all the
checks should be in the drivers because the drivers can actually
justify them, or at least they should be able to.

> 
> > 
> > > +	if (plane->state->crtc_w != plane_state->crtc_w ||
> > > +	    plane->state->crtc_h != plane_state->crtc_h ||
> > > +	    plane->state->src_w != plane_state->src_w ||
> > > +	    plane->state->src_h != plane_state->src_h) {
> > > +		return false;
> > > +	}
> > > +
> > > +	return !funcs->atomic_async_check(plane, plane_state);
> > > +}
> > > +
> > >  static void drm_atomic_crtc_print_state(struct drm_printer *p,
> > >  		const struct drm_crtc_state *state)
> > >  {
> > <snip> 
> > >  /**
> > > diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> > > index 8c67fc0..7c067ca 100644
> > > --- a/include/uapi/drm/drm_mode.h
> > > +++ b/include/uapi/drm/drm_mode.h
> > > @@ -646,13 +646,15 @@ struct drm_mode_destroy_dumb {
> > >  #define DRM_MODE_ATOMIC_TEST_ONLY 0x0100
> > >  #define DRM_MODE_ATOMIC_NONBLOCK  0x0200
> > >  #define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400
> > > +#define DRM_MODE_ATOMIC_ASYNC_UPDATE 0x0800
> > 
> > What exactly is that flag supposed to mean?
> 
> I don't think I provided a good explanation for it, sorry. This flag
> tells core to jump ahead the queued update if the conditions in 
> drm_atomic_async_check() are met. Useful for cursors and async PageFlips
> over the atomic ioctl.

IMO mixing nea uapi in this patch is a mistake. It should be separate
and the intent of the new flag should be well documented.
Gustavo F. Padovan - April 28, 2017, 2:04 p.m.
Hi Ville,

2017-04-28 Ville Syrjälä <ville.syrjala@linux.intel.com>:

> On Thu, Apr 27, 2017 at 04:35:37PM -0300, Gustavo Padovan wrote:
> > Hi Ville,
> > 
> > 2017-04-27 Ville Syrjälä <ville.syrjala@linux.intel.com>:
> > 
> > > On Thu, Apr 27, 2017 at 12:15:13PM -0300, Gustavo Padovan wrote:
> > > > From: Gustavo Padovan <gustavo.padovan@collabora.com>
> > > > 
> > > > In some cases, like cursor updates, it is interesting to update the
> > > > plane in an asynchronous fashion to avoid big delays. The current queued
> > > > update could be still waiting for a fence to signal and thus block any
> > > > subsequent update until its scan out. In cases like this if we update the
> > > > cursor synchronously through the atomic API it will cause significant
> > > > delays that would even be noticed by the final user.
> > > > 
> > > > This patch creates a fast path to jump ahead the current queued state and
> > > > do single planes updates without going through all atomic step in
> > > > drm_atomic_helper_commit().
> > > > 
> > > > We take this path for legacy cursor updates. Users can also set the
> > > > DRM_MODE_ATOMIC_ASYNC_UPDATE flag on atomic updates.
> > > > 
> > > > For now only single plane updates are supported, but we plan to support
> > > > multiple planes updates and async PageFlips through this interface as well
> > > > in the near future.
> > > > 
> > > > v2:
> > > > 	- allow updates even if there is a queued update for the same
> > > > 	plane.
> > > >         - fixes on the documentation (Emil Velikov)
> > > >         - unconditionally call ->atomic_async_update (Emil Velikov)
> > > >         - check for ->atomic_async_update earlier (Daniel Vetter)
> > > >         - make ->atomic_async_check() the last step (Daniel Vetter)
> > > >         - add ASYNC_UPDATE flag (Eric Anholt)
> > > >         - update state in core after ->atomic_async_update (Eric Anholt)
> > > > 	- update docs (Eric Anholt)
> > > > 
> > > > Cc: Daniel Vetter <daniel.vetter@intel.com>
> > > > Cc: Rob Clark <robdclark@gmail.com>
> > > > Cc: Eric Anholt <eric@anholt.net>
> > > > Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.com>
> > > > ---
> > > >  drivers/gpu/drm/drm_atomic.c             | 50 ++++++++++++++++++++++++++++++++
> > > >  drivers/gpu/drm/drm_atomic_helper.c      | 48 ++++++++++++++++++++++++++++++
> > > >  include/drm/drm_atomic.h                 |  2 ++
> > > >  include/drm/drm_atomic_helper.h          |  2 ++
> > > >  include/drm/drm_modeset_helper_vtables.h | 45 ++++++++++++++++++++++++++++
> > > >  include/uapi/drm/drm_mode.h              |  4 ++-
> > > >  6 files changed, 150 insertions(+), 1 deletion(-)
> > > > 
> > > > diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> > > > index 30229ab..7b60cf8 100644
> > > > --- a/drivers/gpu/drm/drm_atomic.c
> > > > +++ b/drivers/gpu/drm/drm_atomic.c
> > > > @@ -77,6 +77,7 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)
> > > >  	 * setting this appropriately?
> > > >  	 */
> > > >  	state->allow_modeset = true;
> > > > +	state->async_update = true;
> > > >  
> > > >  	state->crtcs = kcalloc(dev->mode_config.num_crtc,
> > > >  			       sizeof(*state->crtcs), GFP_KERNEL);
> > > > @@ -631,6 +632,51 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,
> > > >  	return 0;
> > > >  }
> > > >  
> > > > +static bool drm_atomic_async_check(struct drm_atomic_state *state)
> > > > +{
> > > > +	struct drm_crtc *crtc;
> > > > +	struct drm_crtc_state *crtc_state;
> > > > +	struct drm_plane *__plane, *plane = NULL;
> > > > +	struct drm_plane_state *__plane_state, *plane_state = NULL;
> > > > +	const struct drm_plane_helper_funcs *funcs;
> > > > +	int i, n_planes = 0;
> > > > +
> > > > +	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
> > > > +		if (drm_atomic_crtc_needs_modeset(crtc_state))
> > > > +			return false;
> > > > +	}
> > > > +
> > > > +	for_each_new_plane_in_state(state, __plane, __plane_state, i) {
> > > > +		n_planes++;
> > > > +		plane = __plane;
> > > > +		plane_state = __plane_state;
> > > > +	}
> > > > +
> > > > +	/* FIXME: we support single plane updates for now */
> > > > +	if (!plane || n_planes != 1)
> > > > +		return false;
> > > > +
> > > > +	funcs = plane->helper_private;
> > > > +	if (!funcs->atomic_async_update)
> > > > +		return false;
> > > > +
> > > > +	if (plane_state->fence)
> > > > +		return false;
> > > > +
> > > > +	if (!plane->state->crtc || (plane->state->crtc != plane_state->crtc))
> > > > +		return false;
> > > > +
> > > > +	/* No configuring new scaling in the async path. */
> > > 
> > > Those checks aren't really about scaling. Well, they are also about
> > > scaling, but they're mainly about changing size.
> > 
> > I just copied the comment from the previous code in the drivers...
> > I can fix it.
> > 
> > > 
> > > What I don't really understand is why we're enforcing these restrictions
> > > in the core but leaving other restrictions up to the driver. I don't see
> > > size changes as anything really special compared to many of the other
> > > restrictions that would now be up to each driver.
> > 
> > Because I extracted what was common between msm, vc4 and i915 on their
> > legacy cursor update calls. I didn't want to enforce anything else in
> > core for now.
> > 
> > > 
> > > If you're really after some lowest common denominator as far as
> > > exposed capabilities are concerned then I think the core should do
> > > more checking. OTOH if you're not interested limiting what each
> > > driver exposes then I don't see why the core checks anything at all.
> > 
> > I think a common denominator is what we want but we only have 3 drivers
> > using it at the moment. We can look for more checks that shoud be done
> > is core. Any suggestions?
> 
> My suggestion is that we should come up with some proper justification
> for the checks that are done by the core. Otherwise I think all the
> checks should be in the drivers because the drivers can actually
> justify them, or at least they should be able to.

I agree with you, we can't just mix these checks. I'll move the drivers
one and keep only the core ones here.

> 
> > 
> > > 
> > > > +	if (plane->state->crtc_w != plane_state->crtc_w ||
> > > > +	    plane->state->crtc_h != plane_state->crtc_h ||
> > > > +	    plane->state->src_w != plane_state->src_w ||
> > > > +	    plane->state->src_h != plane_state->src_h) {
> > > > +		return false;
> > > > +	}
> > > > +
> > > > +	return !funcs->atomic_async_check(plane, plane_state);
> > > > +}
> > > > +
> > > >  static void drm_atomic_crtc_print_state(struct drm_printer *p,
> > > >  		const struct drm_crtc_state *state)
> > > >  {
> > > <snip> 
> > > >  /**
> > > > diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> > > > index 8c67fc0..7c067ca 100644
> > > > --- a/include/uapi/drm/drm_mode.h
> > > > +++ b/include/uapi/drm/drm_mode.h
> > > > @@ -646,13 +646,15 @@ struct drm_mode_destroy_dumb {
> > > >  #define DRM_MODE_ATOMIC_TEST_ONLY 0x0100
> > > >  #define DRM_MODE_ATOMIC_NONBLOCK  0x0200
> > > >  #define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400
> > > > +#define DRM_MODE_ATOMIC_ASYNC_UPDATE 0x0800
> > > 
> > > What exactly is that flag supposed to mean?
> > 
> > I don't think I provided a good explanation for it, sorry. This flag
> > tells core to jump ahead the queued update if the conditions in 
> > drm_atomic_async_check() are met. Useful for cursors and async PageFlips
> > over the atomic ioctl.
> 
> IMO mixing nea uapi in this patch is a mistake. It should be separate
> and the intent of the new flag should be well documented.

Yes, a separated patch is a good idea.

Gustavo

Patch

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 30229ab..7b60cf8 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -77,6 +77,7 @@  drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)
 	 * setting this appropriately?
 	 */
 	state->allow_modeset = true;
+	state->async_update = true;
 
 	state->crtcs = kcalloc(dev->mode_config.num_crtc,
 			       sizeof(*state->crtcs), GFP_KERNEL);
@@ -631,6 +632,51 @@  static int drm_atomic_crtc_check(struct drm_crtc *crtc,
 	return 0;
 }
 
+static bool drm_atomic_async_check(struct drm_atomic_state *state)
+{
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *crtc_state;
+	struct drm_plane *__plane, *plane = NULL;
+	struct drm_plane_state *__plane_state, *plane_state = NULL;
+	const struct drm_plane_helper_funcs *funcs;
+	int i, n_planes = 0;
+
+	for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+		if (drm_atomic_crtc_needs_modeset(crtc_state))
+			return false;
+	}
+
+	for_each_new_plane_in_state(state, __plane, __plane_state, i) {
+		n_planes++;
+		plane = __plane;
+		plane_state = __plane_state;
+	}
+
+	/* FIXME: we support single plane updates for now */
+	if (!plane || n_planes != 1)
+		return false;
+
+	funcs = plane->helper_private;
+	if (!funcs->atomic_async_update)
+		return false;
+
+	if (plane_state->fence)
+		return false;
+
+	if (!plane->state->crtc || (plane->state->crtc != plane_state->crtc))
+		return false;
+
+	/* No configuring new scaling in the async path. */
+	if (plane->state->crtc_w != plane_state->crtc_w ||
+	    plane->state->crtc_h != plane_state->crtc_h ||
+	    plane->state->src_w != plane_state->src_w ||
+	    plane->state->src_h != plane_state->src_h) {
+		return false;
+	}
+
+	return !funcs->atomic_async_check(plane, plane_state);
+}
+
 static void drm_atomic_crtc_print_state(struct drm_printer *p,
 		const struct drm_crtc_state *state)
 {
@@ -1591,6 +1637,9 @@  int drm_atomic_check_only(struct drm_atomic_state *state)
 		}
 	}
 
+	if (state->async_update || state->legacy_cursor_update)
+		state->async_update = drm_atomic_async_check(state);
+
 	return ret;
 }
 EXPORT_SYMBOL(drm_atomic_check_only);
@@ -2132,6 +2181,7 @@  int drm_mode_atomic_ioctl(struct drm_device *dev,
 
 	state->acquire_ctx = &ctx;
 	state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET);
+	state->async_update = !!(arg->flags & DRM_MODE_ATOMIC_ASYNC_UPDATE);
 
 retry:
 	plane_mask = 0;
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 5a3c9c0..73cd07d 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1235,6 +1235,43 @@  static void commit_work(struct work_struct *work)
 }
 
 /**
+ * drm_atomic_helper_async_commit - commit state asynchronously
+ *
+ * This function commits a state asynchronously, i.e., not vblank
+ * synchronized. It should be used on a state only when
+ * drm_atomic_async_check() succeds. Async commits are not supposed to swap the
+ * states like normal sync commits, but just do in-place change on the current
+ * state.
+ */
+void drm_atomic_helper_async_commit(struct drm_device *dev,
+				    struct drm_atomic_state *state)
+{
+	struct drm_plane *plane;
+	struct drm_plane_state *plane_state;
+	const struct drm_plane_helper_funcs *funcs;
+	int i;
+
+	for_each_new_plane_in_state(state, plane, plane_state, i) {
+		struct drm_framebuffer *old_fb;
+
+		funcs = plane->helper_private;
+		funcs->atomic_async_update(plane, plane_state);
+
+		plane->state->src_x = plane_state->src_x;
+		plane->state->src_y = plane_state->src_y;
+		plane->state->crtc_x = plane_state->crtc_x;
+		plane->state->crtc_y = plane_state->crtc_y;
+
+		if (plane->state->fb != plane_state->fb) {
+			old_fb = plane->state->fb;
+			plane->state->fb = plane_state->fb;
+			plane_state->fb = old_fb;
+		}
+	}
+}
+EXPORT_SYMBOL(drm_atomic_helper_async_commit);
+
+/**
  * drm_atomic_helper_commit - commit validated state object
  * @dev: DRM device
  * @state: the driver state object
@@ -1258,6 +1295,17 @@  int drm_atomic_helper_commit(struct drm_device *dev,
 {
 	int ret;
 
+	if (state->async_update) {
+		ret = drm_atomic_helper_prepare_planes(dev, state);
+		if (ret)
+			return ret;
+
+		drm_atomic_helper_async_commit(dev, state);
+		drm_atomic_helper_cleanup_planes(dev, state);
+
+		return 0;
+	}
+
 	ret = drm_atomic_helper_setup_commit(state, nonblock);
 	if (ret)
 		return ret;
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 788daf7..70ca279 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -160,6 +160,7 @@  struct __drm_connnectors_state {
  * @dev: parent DRM device
  * @allow_modeset: allow full modeset
  * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics
+ * @async_update: hint for asynchronous plane update
  * @planes: pointer to array of structures with per-plane data
  * @crtcs: pointer to array of CRTC pointers
  * @num_connector: size of the @connectors and @connector_states arrays
@@ -172,6 +173,7 @@  struct drm_atomic_state {
 	struct drm_device *dev;
 	bool allow_modeset : 1;
 	bool legacy_cursor_update : 1;
+	bool async_update : 1;
 	struct __drm_planes_state *planes;
 	struct __drm_crtcs_state *crtcs;
 	int num_connector;
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index f0a8678..8571b51 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -44,6 +44,8 @@  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);
+void drm_atomic_helper_async_commit(struct drm_device *dev,
+				    struct drm_atomic_state *state);
 
 int drm_atomic_helper_wait_for_fences(struct drm_device *dev,
 					struct drm_atomic_state *state,
diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
index c01c328..b28ffa2 100644
--- a/include/drm/drm_modeset_helper_vtables.h
+++ b/include/drm/drm_modeset_helper_vtables.h
@@ -1048,6 +1048,51 @@  struct drm_plane_helper_funcs {
 	 */
 	void (*atomic_disable)(struct drm_plane *plane,
 			       struct drm_plane_state *old_state);
+
+	/**
+	 * @atomic_async_check:
+	 *
+	 * Drivers should set this function pointer to check if the plane state
+	 * can be updated in a async fashion. Here async means "not vblank
+	 * synchronized".
+	 *
+	 * This hook is called by drm_atomic_async_check() to establish if a
+	 * given update can be committed asynchronously, that is, if it can
+	 * jump ahead the state currently queued for update.
+	 *
+	 * RETURNS:
+	 *
+	 * Return 0 on success and any error returned indicates that the update
+	 * can not be applied in asynchronous manner.
+	 *
+	 * FIXME:
+	 *  - It only works for single plane updates
+	 *  - Async Pageflips are not supported yet
+	 */
+	int (*atomic_async_check)(struct drm_plane *plane,
+				  struct drm_plane_state *state);
+
+	/**
+	 * @atomic_async_update:
+	 *
+	 * Drivers should set this function pointer to perform asynchronous
+	 * updates of planes, that is, jump ahead the currently queued
+	 * state for and update the plane. Here async means "not vblank
+	 * synchronized".
+	 *
+	 * This hook is called by drm_atomic_helper_async_commit().
+	 *
+	 * An async update will happen if the user set the
+	 * DRM_MODE_ATOMIC_ASYNC_UPDATE flag or on legacy cursor updates.
+	 *
+	 * Note that unlike &drm_plane_helper_funcs.atomic_update this hook
+	 * takes the new &drm_plane_state as parameter. When doing async_update
+	 * drivers shouldn't replace the &drm_plane_state but update the
+	 * current one with the new plane configurations in the new
+	 * plane_state.
+	 */
+	void (*atomic_async_update)(struct drm_plane *plane,
+				    struct drm_plane_state *new_state);
 };
 
 /**
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 8c67fc0..7c067ca 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -646,13 +646,15 @@  struct drm_mode_destroy_dumb {
 #define DRM_MODE_ATOMIC_TEST_ONLY 0x0100
 #define DRM_MODE_ATOMIC_NONBLOCK  0x0200
 #define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400
+#define DRM_MODE_ATOMIC_ASYNC_UPDATE 0x0800
 
 #define DRM_MODE_ATOMIC_FLAGS (\
 		DRM_MODE_PAGE_FLIP_EVENT |\
 		DRM_MODE_PAGE_FLIP_ASYNC |\
 		DRM_MODE_ATOMIC_TEST_ONLY |\
 		DRM_MODE_ATOMIC_NONBLOCK |\
-		DRM_MODE_ATOMIC_ALLOW_MODESET)
+		DRM_MODE_ATOMIC_ALLOW_MODESET |\
+		DRM_MODE_ATOMIC_ASYNC_UPDATE)
 
 struct drm_mode_atomic {
 	__u32 flags;