diff mbox

[6/7] drm/i915/fbc: move from crtc_state->enable_fbc to plane_state->enable_fbc

Message ID 1478883461-20201-7-git-send-email-paulo.r.zanoni@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Zanoni, Paulo R Nov. 11, 2016, 4:57 p.m. UTC
Ville pointed out that intel_fbc_choose_crtc() is iterating over all
planes instead of just the primary planes. There are no real
consequences of this problem for HSW+, and for the other platforms it
just means that in some obscure multi-screen cases we'll keep FBC
disabled when we could have enabled it. Still, iterating over all
planes doesn't seem to be the best thing to do.

My initial idea was to just add a check for plane->type and be done,
but then I realized that in commits not involving the primary plane we
would reset crtc_state->enable_fbc back to false even when FBC is
enabled. That also wouldn't result in a bug due to the way the
enable_fbc variable is checked, but, still, our code can be better
than this.

So I went for the solution that involves tracking enable_fbc in the
primary plane state instead of the CRTC state. This way, if a commit
doesn't involve the primary plane for the CRTC we won't be resetting
enable_fbc back to false, so the variable will always reflect the
reality. And this change also makes more sense since FBC is actually
tied to the single plane and not the full pipe. As a bonus, we only
iterate over the CRTCs instead of iterating over all planes.

Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Reported-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
---
 drivers/gpu/drm/i915/intel_drv.h |  4 ++--
 drivers/gpu/drm/i915/intel_fbc.c | 36 +++++++++++++++++++-----------------
 2 files changed, 21 insertions(+), 19 deletions(-)

Comments

Ville Syrjala Nov. 11, 2016, 6:51 p.m. UTC | #1
On Fri, Nov 11, 2016 at 02:57:40PM -0200, Paulo Zanoni wrote:
> Ville pointed out that intel_fbc_choose_crtc() is iterating over all
> planes instead of just the primary planes. There are no real
> consequences of this problem for HSW+, and for the other platforms it
> just means that in some obscure multi-screen cases we'll keep FBC
> disabled when we could have enabled it. Still, iterating over all
> planes doesn't seem to be the best thing to do.
> 
> My initial idea was to just add a check for plane->type and be done,
> but then I realized that in commits not involving the primary plane we
> would reset crtc_state->enable_fbc back to false even when FBC is
> enabled. That also wouldn't result in a bug due to the way the
> enable_fbc variable is checked, but, still, our code can be better
> than this.
> 
> So I went for the solution that involves tracking enable_fbc in the
> primary plane state instead of the CRTC state. This way, if a commit
> doesn't involve the primary plane for the CRTC we won't be resetting
> enable_fbc back to false, so the variable will always reflect the
> reality. And this change also makes more sense since FBC is actually
> tied to the single plane and not the full pipe. As a bonus, we only
> iterate over the CRTCs instead of iterating over all planes.
> 
> Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Reported-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
> ---
>  drivers/gpu/drm/i915/intel_drv.h |  4 ++--
>  drivers/gpu/drm/i915/intel_fbc.c | 36 +++++++++++++++++++-----------------
>  2 files changed, 21 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 003afb8..025cb74 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -403,6 +403,8 @@ struct intel_plane_state {
>  	int scaler_id;
>  
>  	struct drm_intel_sprite_colorkey ckey;
> +
> +	bool enable_fbc;
>  };
>  
>  struct intel_initial_plane_config {
> @@ -648,8 +650,6 @@ struct intel_crtc_state {
>  
>  	bool ips_enabled;
>  
> -	bool enable_fbc;
> -
>  	bool double_wide;
>  
>  	bool dp_encoder_is_mst;
> diff --git a/drivers/gpu/drm/i915/intel_fbc.c b/drivers/gpu/drm/i915/intel_fbc.c
> index b095175..fc4ac57 100644
> --- a/drivers/gpu/drm/i915/intel_fbc.c
> +++ b/drivers/gpu/drm/i915/intel_fbc.c
> @@ -1055,16 +1055,17 @@ void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv,
>  			   struct drm_atomic_state *state)
>  {
>  	struct intel_fbc *fbc = &dev_priv->fbc;
> -	struct drm_plane *plane;
> -	struct drm_plane_state *plane_state;
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *crtc_state;
>  	bool crtc_chosen = false;
>  	int i;
>  
>  	mutex_lock(&fbc->lock);
>  
> -	/* Does this atomic commit involve the CRTC currently tied to FBC? */
> +	/* Does this atomic commit involve the plane currently tied to FBC? */
>  	if (fbc->crtc &&
> -	    !drm_atomic_get_existing_crtc_state(state, &fbc->crtc->base))
> +	    !drm_atomic_get_existing_plane_state(state,
> +						 fbc->crtc->base.primary))
>  		goto out;
>  
>  	if (!intel_fbc_can_enable(dev_priv))
> @@ -1074,25 +1075,26 @@ void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv,
>  	 * plane. We could go for fancier schemes such as checking the plane
>  	 * size, but this would just affect the few platforms that don't tie FBC
>  	 * to pipe or plane A. */
> -	for_each_plane_in_state(state, plane, plane_state, i) {
> -		struct intel_plane_state *intel_plane_state =
> -			to_intel_plane_state(plane_state);
> -		struct intel_crtc_state *intel_crtc_state;
> -		struct intel_crtc *crtc = to_intel_crtc(plane_state->crtc);
> +	for_each_crtc_in_state(state, crtc, crtc_state, i) {
> +		struct intel_plane_state *plane_state = to_intel_plane_state(
> +			drm_atomic_get_existing_plane_state(state,
> +							    crtc->primary));
> +		struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>  
> -		if (!intel_plane_state->base.visible)
> +		if (!plane_state)
>  			continue;
>  
> -		if (fbc_on_pipe_a_only(dev_priv) && crtc->pipe != PIPE_A)
> +		if (!plane_state->base.visible)
>  			continue;
>  
> -		if (fbc_on_plane_a_only(dev_priv) && crtc->plane != PLANE_A)
> +		if (fbc_on_pipe_a_only(dev_priv) && intel_crtc->pipe != PIPE_A)
>  			continue;
>  
> -		intel_crtc_state = to_intel_crtc_state(
> -			drm_atomic_get_existing_crtc_state(state, &crtc->base));
> +		if (fbc_on_plane_a_only(dev_priv) &&
> +		    intel_crtc->plane != PLANE_A)
> +			continue;
>  
> -		intel_crtc_state->enable_fbc = true;
> +		plane_state->enable_fbc = true;

So looking at this whole thing, I can't see anything that would prevent
enable_fbc being true for multiple primary planes at the same time
Well, apart from the whole "we enable it only for platforms that can only do
pipe A" thing.

So what happens in that case? FBC just ends up getting enabling on
one of the pipes based on the order intel_fbc_enable() gets called,
or something?

>  		crtc_chosen = true;
>  		break;
>  	}
> @@ -1130,13 +1132,13 @@ void intel_fbc_enable(struct intel_crtc *crtc,
>  	if (fbc->enabled) {
>  		WARN_ON(fbc->crtc == NULL);
>  		if (fbc->crtc == crtc) {
> -			WARN_ON(!crtc_state->enable_fbc);
> +			WARN_ON(!plane_state->enable_fbc);
>  			WARN_ON(fbc->active);
>  		}
>  		goto out;
>  	}
>  
> -	if (!crtc_state->enable_fbc)
> +	if (!plane_state->enable_fbc)
>  		goto out;
>  
>  	WARN_ON(fbc->active);
> -- 
> 2.7.4
Zanoni, Paulo R Nov. 11, 2016, 7:01 p.m. UTC | #2
Em Sex, 2016-11-11 às 20:51 +0200, Ville Syrjälä escreveu:
> On Fri, Nov 11, 2016 at 02:57:40PM -0200, Paulo Zanoni wrote:
> > 
> > Ville pointed out that intel_fbc_choose_crtc() is iterating over
> > all
> > planes instead of just the primary planes. There are no real
> > consequences of this problem for HSW+, and for the other platforms
> > it
> > just means that in some obscure multi-screen cases we'll keep FBC
> > disabled when we could have enabled it. Still, iterating over all
> > planes doesn't seem to be the best thing to do.
> > 
> > My initial idea was to just add a check for plane->type and be
> > done,
> > but then I realized that in commits not involving the primary plane
> > we
> > would reset crtc_state->enable_fbc back to false even when FBC is
> > enabled. That also wouldn't result in a bug due to the way the
> > enable_fbc variable is checked, but, still, our code can be better
> > than this.
> > 
> > So I went for the solution that involves tracking enable_fbc in the
> > primary plane state instead of the CRTC state. This way, if a
> > commit
> > doesn't involve the primary plane for the CRTC we won't be
> > resetting
> > enable_fbc back to false, so the variable will always reflect the
> > reality. And this change also makes more sense since FBC is
> > actually
> > tied to the single plane and not the full pipe. As a bonus, we only
> > iterate over the CRTCs instead of iterating over all planes.
> > 
> > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > Reported-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
> > ---
> >  drivers/gpu/drm/i915/intel_drv.h |  4 ++--
> >  drivers/gpu/drm/i915/intel_fbc.c | 36 +++++++++++++++++++---------
> > --------
> >  2 files changed, 21 insertions(+), 19 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/i915/intel_drv.h
> > b/drivers/gpu/drm/i915/intel_drv.h
> > index 003afb8..025cb74 100644
> > --- a/drivers/gpu/drm/i915/intel_drv.h
> > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > @@ -403,6 +403,8 @@ struct intel_plane_state {
> >  	int scaler_id;
> >  
> >  	struct drm_intel_sprite_colorkey ckey;
> > +
> > +	bool enable_fbc;
> >  };
> >  
> >  struct intel_initial_plane_config {
> > @@ -648,8 +650,6 @@ struct intel_crtc_state {
> >  
> >  	bool ips_enabled;
> >  
> > -	bool enable_fbc;
> > -
> >  	bool double_wide;
> >  
> >  	bool dp_encoder_is_mst;
> > diff --git a/drivers/gpu/drm/i915/intel_fbc.c
> > b/drivers/gpu/drm/i915/intel_fbc.c
> > index b095175..fc4ac57 100644
> > --- a/drivers/gpu/drm/i915/intel_fbc.c
> > +++ b/drivers/gpu/drm/i915/intel_fbc.c
> > @@ -1055,16 +1055,17 @@ void intel_fbc_choose_crtc(struct
> > drm_i915_private *dev_priv,
> >  			   struct drm_atomic_state *state)
> >  {
> >  	struct intel_fbc *fbc = &dev_priv->fbc;
> > -	struct drm_plane *plane;
> > -	struct drm_plane_state *plane_state;
> > +	struct drm_crtc *crtc;
> > +	struct drm_crtc_state *crtc_state;
> >  	bool crtc_chosen = false;
> >  	int i;
> >  
> >  	mutex_lock(&fbc->lock);
> >  
> > -	/* Does this atomic commit involve the CRTC currently tied
> > to FBC? */
> > +	/* Does this atomic commit involve the plane currently
> > tied to FBC? */
> >  	if (fbc->crtc &&
> > -	    !drm_atomic_get_existing_crtc_state(state, &fbc->crtc-
> > >base))
> > +	    !drm_atomic_get_existing_plane_state(state,
> > +						 fbc->crtc-
> > >base.primary))
> >  		goto out;
> >  
> >  	if (!intel_fbc_can_enable(dev_priv))
> > @@ -1074,25 +1075,26 @@ void intel_fbc_choose_crtc(struct
> > drm_i915_private *dev_priv,
> >  	 * plane. We could go for fancier schemes such as checking
> > the plane
> >  	 * size, but this would just affect the few platforms that
> > don't tie FBC
> >  	 * to pipe or plane A. */
> > -	for_each_plane_in_state(state, plane, plane_state, i) {
> > -		struct intel_plane_state *intel_plane_state =
> > -			to_intel_plane_state(plane_state);
> > -		struct intel_crtc_state *intel_crtc_state;
> > -		struct intel_crtc *crtc =
> > to_intel_crtc(plane_state->crtc);
> > +	for_each_crtc_in_state(state, crtc, crtc_state, i) {
> > +		struct intel_plane_state *plane_state =
> > to_intel_plane_state(
> > +			drm_atomic_get_existing_plane_state(state,
> > +							    crtc-
> > >primary));
> > +		struct intel_crtc *intel_crtc =
> > to_intel_crtc(crtc);
> >  
> > -		if (!intel_plane_state->base.visible)
> > +		if (!plane_state)
> >  			continue;
> >  
> > -		if (fbc_on_pipe_a_only(dev_priv) && crtc->pipe !=
> > PIPE_A)
> > +		if (!plane_state->base.visible)
> >  			continue;
> >  
> > -		if (fbc_on_plane_a_only(dev_priv) && crtc->plane
> > != PLANE_A)
> > +		if (fbc_on_pipe_a_only(dev_priv) && intel_crtc-
> > >pipe != PIPE_A)
> >  			continue;
> >  
> > -		intel_crtc_state = to_intel_crtc_state(
> > -			drm_atomic_get_existing_crtc_state(state,
> > &crtc->base));
> > +		if (fbc_on_plane_a_only(dev_priv) &&
> > +		    intel_crtc->plane != PLANE_A)
> > +			continue;
> >  
> > -		intel_crtc_state->enable_fbc = true;
> > +		plane_state->enable_fbc = true;
> 
> So looking at this whole thing, I can't see anything that would
> prevent
> enable_fbc being true for multiple primary planes at the same time
> Well, apart from the whole "we enable it only for platforms that can
> only do
> pipe A" thing.
> 
> So what happens in that case? FBC just ends up getting enabling on
> one of the pipes based on the order intel_fbc_enable() gets called,
> or something?

The first check of intel_fbc_choose_crtc() is supposed to prevent this
case: if fbc->crtc->primary is not included in the commit we just
return without selecting any plane. Otherwise, we only pick one CRTC
due to the "break;" statement after setting plane_state->enable_fbc to
true.

> 
> > 
> >  		crtc_chosen = true;
> >  		break;
> >  	}
> > @@ -1130,13 +1132,13 @@ void intel_fbc_enable(struct intel_crtc
> > *crtc,
> >  	if (fbc->enabled) {
> >  		WARN_ON(fbc->crtc == NULL);
> >  		if (fbc->crtc == crtc) {
> > -			WARN_ON(!crtc_state->enable_fbc);
> > +			WARN_ON(!plane_state->enable_fbc);
> >  			WARN_ON(fbc->active);
> >  		}
> >  		goto out;
> >  	}
> >  
> > -	if (!crtc_state->enable_fbc)
> > +	if (!plane_state->enable_fbc)
> >  		goto out;
> >  
> >  	WARN_ON(fbc->active);
> > -- 
> > 2.7.4
>
Ville Syrjala Nov. 11, 2016, 7:13 p.m. UTC | #3
On Fri, Nov 11, 2016 at 05:01:54PM -0200, Paulo Zanoni wrote:
> Em Sex, 2016-11-11 às 20:51 +0200, Ville Syrjälä escreveu:
> > On Fri, Nov 11, 2016 at 02:57:40PM -0200, Paulo Zanoni wrote:
> > > 
> > > Ville pointed out that intel_fbc_choose_crtc() is iterating over
> > > all
> > > planes instead of just the primary planes. There are no real
> > > consequences of this problem for HSW+, and for the other platforms
> > > it
> > > just means that in some obscure multi-screen cases we'll keep FBC
> > > disabled when we could have enabled it. Still, iterating over all
> > > planes doesn't seem to be the best thing to do.
> > > 
> > > My initial idea was to just add a check for plane->type and be
> > > done,
> > > but then I realized that in commits not involving the primary plane
> > > we
> > > would reset crtc_state->enable_fbc back to false even when FBC is
> > > enabled. That also wouldn't result in a bug due to the way the
> > > enable_fbc variable is checked, but, still, our code can be better
> > > than this.
> > > 
> > > So I went for the solution that involves tracking enable_fbc in the
> > > primary plane state instead of the CRTC state. This way, if a
> > > commit
> > > doesn't involve the primary plane for the CRTC we won't be
> > > resetting
> > > enable_fbc back to false, so the variable will always reflect the
> > > reality. And this change also makes more sense since FBC is
> > > actually
> > > tied to the single plane and not the full pipe. As a bonus, we only
> > > iterate over the CRTCs instead of iterating over all planes.
> > > 
> > > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > Reported-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
> > > ---
> > >  drivers/gpu/drm/i915/intel_drv.h |  4 ++--
> > >  drivers/gpu/drm/i915/intel_fbc.c | 36 +++++++++++++++++++---------
> > > --------
> > >  2 files changed, 21 insertions(+), 19 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/i915/intel_drv.h
> > > b/drivers/gpu/drm/i915/intel_drv.h
> > > index 003afb8..025cb74 100644
> > > --- a/drivers/gpu/drm/i915/intel_drv.h
> > > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > > @@ -403,6 +403,8 @@ struct intel_plane_state {
> > >  	int scaler_id;
> > >  
> > >  	struct drm_intel_sprite_colorkey ckey;
> > > +
> > > +	bool enable_fbc;
> > >  };
> > >  
> > >  struct intel_initial_plane_config {
> > > @@ -648,8 +650,6 @@ struct intel_crtc_state {
> > >  
> > >  	bool ips_enabled;
> > >  
> > > -	bool enable_fbc;
> > > -
> > >  	bool double_wide;
> > >  
> > >  	bool dp_encoder_is_mst;
> > > diff --git a/drivers/gpu/drm/i915/intel_fbc.c
> > > b/drivers/gpu/drm/i915/intel_fbc.c
> > > index b095175..fc4ac57 100644
> > > --- a/drivers/gpu/drm/i915/intel_fbc.c
> > > +++ b/drivers/gpu/drm/i915/intel_fbc.c
> > > @@ -1055,16 +1055,17 @@ void intel_fbc_choose_crtc(struct
> > > drm_i915_private *dev_priv,
> > >  			   struct drm_atomic_state *state)
> > >  {
> > >  	struct intel_fbc *fbc = &dev_priv->fbc;
> > > -	struct drm_plane *plane;
> > > -	struct drm_plane_state *plane_state;
> > > +	struct drm_crtc *crtc;
> > > +	struct drm_crtc_state *crtc_state;
> > >  	bool crtc_chosen = false;
> > >  	int i;
> > >  
> > >  	mutex_lock(&fbc->lock);
> > >  
> > > -	/* Does this atomic commit involve the CRTC currently tied
> > > to FBC? */
> > > +	/* Does this atomic commit involve the plane currently
> > > tied to FBC? */
> > >  	if (fbc->crtc &&
> > > -	    !drm_atomic_get_existing_crtc_state(state, &fbc->crtc-
> > > >base))
> > > +	    !drm_atomic_get_existing_plane_state(state,
> > > +						 fbc->crtc-
> > > >base.primary))
> > >  		goto out;
> > >  
> > >  	if (!intel_fbc_can_enable(dev_priv))
> > > @@ -1074,25 +1075,26 @@ void intel_fbc_choose_crtc(struct
> > > drm_i915_private *dev_priv,
> > >  	 * plane. We could go for fancier schemes such as checking
> > > the plane
> > >  	 * size, but this would just affect the few platforms that
> > > don't tie FBC
> > >  	 * to pipe or plane A. */
> > > -	for_each_plane_in_state(state, plane, plane_state, i) {
> > > -		struct intel_plane_state *intel_plane_state =
> > > -			to_intel_plane_state(plane_state);
> > > -		struct intel_crtc_state *intel_crtc_state;
> > > -		struct intel_crtc *crtc =
> > > to_intel_crtc(plane_state->crtc);
> > > +	for_each_crtc_in_state(state, crtc, crtc_state, i) {
> > > +		struct intel_plane_state *plane_state =
> > > to_intel_plane_state(
> > > +			drm_atomic_get_existing_plane_state(state,
> > > +							    crtc-
> > > >primary));
> > > +		struct intel_crtc *intel_crtc =
> > > to_intel_crtc(crtc);
> > >  
> > > -		if (!intel_plane_state->base.visible)
> > > +		if (!plane_state)
> > >  			continue;
> > >  
> > > -		if (fbc_on_pipe_a_only(dev_priv) && crtc->pipe !=
> > > PIPE_A)
> > > +		if (!plane_state->base.visible)
> > >  			continue;
> > >  
> > > -		if (fbc_on_plane_a_only(dev_priv) && crtc->plane
> > > != PLANE_A)
> > > +		if (fbc_on_pipe_a_only(dev_priv) && intel_crtc-
> > > >pipe != PIPE_A)
> > >  			continue;
> > >  
> > > -		intel_crtc_state = to_intel_crtc_state(
> > > -			drm_atomic_get_existing_crtc_state(state,
> > > &crtc->base));
> > > +		if (fbc_on_plane_a_only(dev_priv) &&
> > > +		    intel_crtc->plane != PLANE_A)
> > > +			continue;
> > >  
> > > -		intel_crtc_state->enable_fbc = true;
> > > +		plane_state->enable_fbc = true;
> > 
> > So looking at this whole thing, I can't see anything that would
> > prevent
> > enable_fbc being true for multiple primary planes at the same time
> > Well, apart from the whole "we enable it only for platforms that can
> > only do
> > pipe A" thing.
> > 
> > So what happens in that case? FBC just ends up getting enabling on
> > one of the pipes based on the order intel_fbc_enable() gets called,
> > or something?
> 
> The first check of intel_fbc_choose_crtc() is supposed to prevent this
> case: if fbc->crtc->primary is not included in the commit we just
> return without selecting any plane.

The fbc->crtc thing only works if intel_fbc_enable() was already called
for some crtc. But what it it wasn't?

> Otherwise, we only pick one CRTC
> due to the "break;" statement after setting plane_state->enable_fbc to
> true.

Only one per atomic operation. But what if there are several happening
in parallel on different crtcs?

> 
> > 
> > > 
> > >  		crtc_chosen = true;
> > >  		break;
> > >  	}
> > > @@ -1130,13 +1132,13 @@ void intel_fbc_enable(struct intel_crtc
> > > *crtc,
> > >  	if (fbc->enabled) {
> > >  		WARN_ON(fbc->crtc == NULL);
> > >  		if (fbc->crtc == crtc) {
> > > -			WARN_ON(!crtc_state->enable_fbc);
> > > +			WARN_ON(!plane_state->enable_fbc);
> > >  			WARN_ON(fbc->active);
> > >  		}
> > >  		goto out;
> > >  	}
> > >  
> > > -	if (!crtc_state->enable_fbc)
> > > +	if (!plane_state->enable_fbc)
> > >  		goto out;
> > >  
> > >  	WARN_ON(fbc->active);
> > > -- 
> > > 2.7.4
> >
Zanoni, Paulo R Nov. 11, 2016, 7:57 p.m. UTC | #4
Em Sex, 2016-11-11 às 21:13 +0200, Ville Syrjälä escreveu:
> On Fri, Nov 11, 2016 at 05:01:54PM -0200, Paulo Zanoni wrote:
> > 
> > Em Sex, 2016-11-11 às 20:51 +0200, Ville Syrjälä escreveu:
> > > 
> > > On Fri, Nov 11, 2016 at 02:57:40PM -0200, Paulo Zanoni wrote:
> > > > 
> > > > 
> > > > Ville pointed out that intel_fbc_choose_crtc() is iterating
> > > > over
> > > > all
> > > > planes instead of just the primary planes. There are no real
> > > > consequences of this problem for HSW+, and for the other
> > > > platforms
> > > > it
> > > > just means that in some obscure multi-screen cases we'll keep
> > > > FBC
> > > > disabled when we could have enabled it. Still, iterating over
> > > > all
> > > > planes doesn't seem to be the best thing to do.
> > > > 
> > > > My initial idea was to just add a check for plane->type and be
> > > > done,
> > > > but then I realized that in commits not involving the primary
> > > > plane
> > > > we
> > > > would reset crtc_state->enable_fbc back to false even when FBC
> > > > is
> > > > enabled. That also wouldn't result in a bug due to the way the
> > > > enable_fbc variable is checked, but, still, our code can be
> > > > better
> > > > than this.
> > > > 
> > > > So I went for the solution that involves tracking enable_fbc in
> > > > the
> > > > primary plane state instead of the CRTC state. This way, if a
> > > > commit
> > > > doesn't involve the primary plane for the CRTC we won't be
> > > > resetting
> > > > enable_fbc back to false, so the variable will always reflect
> > > > the
> > > > reality. And this change also makes more sense since FBC is
> > > > actually
> > > > tied to the single plane and not the full pipe. As a bonus, we
> > > > only
> > > > iterate over the CRTCs instead of iterating over all planes.
> > > > 
> > > > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > Reported-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
> > > > ---
> > > >  drivers/gpu/drm/i915/intel_drv.h |  4 ++--
> > > >  drivers/gpu/drm/i915/intel_fbc.c | 36 +++++++++++++++++++-----
> > > > ----
> > > > --------
> > > >  2 files changed, 21 insertions(+), 19 deletions(-)
> > > > 
> > > > diff --git a/drivers/gpu/drm/i915/intel_drv.h
> > > > b/drivers/gpu/drm/i915/intel_drv.h
> > > > index 003afb8..025cb74 100644
> > > > --- a/drivers/gpu/drm/i915/intel_drv.h
> > > > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > > > @@ -403,6 +403,8 @@ struct intel_plane_state {
> > > >  	int scaler_id;
> > > >  
> > > >  	struct drm_intel_sprite_colorkey ckey;
> > > > +
> > > > +	bool enable_fbc;
> > > >  };
> > > >  
> > > >  struct intel_initial_plane_config {
> > > > @@ -648,8 +650,6 @@ struct intel_crtc_state {
> > > >  
> > > >  	bool ips_enabled;
> > > >  
> > > > -	bool enable_fbc;
> > > > -
> > > >  	bool double_wide;
> > > >  
> > > >  	bool dp_encoder_is_mst;
> > > > diff --git a/drivers/gpu/drm/i915/intel_fbc.c
> > > > b/drivers/gpu/drm/i915/intel_fbc.c
> > > > index b095175..fc4ac57 100644
> > > > --- a/drivers/gpu/drm/i915/intel_fbc.c
> > > > +++ b/drivers/gpu/drm/i915/intel_fbc.c
> > > > @@ -1055,16 +1055,17 @@ void intel_fbc_choose_crtc(struct
> > > > drm_i915_private *dev_priv,
> > > >  			   struct drm_atomic_state *state)
> > > >  {
> > > >  	struct intel_fbc *fbc = &dev_priv->fbc;
> > > > -	struct drm_plane *plane;
> > > > -	struct drm_plane_state *plane_state;
> > > > +	struct drm_crtc *crtc;
> > > > +	struct drm_crtc_state *crtc_state;
> > > >  	bool crtc_chosen = false;
> > > >  	int i;
> > > >  
> > > >  	mutex_lock(&fbc->lock);
> > > >  
> > > > -	/* Does this atomic commit involve the CRTC currently
> > > > tied
> > > > to FBC? */
> > > > +	/* Does this atomic commit involve the plane currently
> > > > tied to FBC? */
> > > >  	if (fbc->crtc &&
> > > > -	    !drm_atomic_get_existing_crtc_state(state, &fbc-
> > > > >crtc-
> > > > > 
> > > > > base))
> > > > +	    !drm_atomic_get_existing_plane_state(state,
> > > > +						 fbc->crtc-
> > > > > 
> > > > > base.primary))
> > > >  		goto out;
> > > >  
> > > >  	if (!intel_fbc_can_enable(dev_priv))
> > > > @@ -1074,25 +1075,26 @@ void intel_fbc_choose_crtc(struct
> > > > drm_i915_private *dev_priv,
> > > >  	 * plane. We could go for fancier schemes such as
> > > > checking
> > > > the plane
> > > >  	 * size, but this would just affect the few platforms
> > > > that
> > > > don't tie FBC
> > > >  	 * to pipe or plane A. */
> > > > -	for_each_plane_in_state(state, plane, plane_state, i)
> > > > {
> > > > -		struct intel_plane_state *intel_plane_state =
> > > > -			to_intel_plane_state(plane_state);
> > > > -		struct intel_crtc_state *intel_crtc_state;
> > > > -		struct intel_crtc *crtc =
> > > > to_intel_crtc(plane_state->crtc);
> > > > +	for_each_crtc_in_state(state, crtc, crtc_state, i) {
> > > > +		struct intel_plane_state *plane_state =
> > > > to_intel_plane_state(
> > > > +			drm_atomic_get_existing_plane_state(st
> > > > ate,
> > > > +							    cr
> > > > tc-
> > > > > 
> > > > > primary));
> > > > +		struct intel_crtc *intel_crtc =
> > > > to_intel_crtc(crtc);
> > > >  
> > > > -		if (!intel_plane_state->base.visible)
> > > > +		if (!plane_state)
> > > >  			continue;
> > > >  
> > > > -		if (fbc_on_pipe_a_only(dev_priv) && crtc->pipe 
> > > > !=
> > > > PIPE_A)
> > > > +		if (!plane_state->base.visible)
> > > >  			continue;
> > > >  
> > > > -		if (fbc_on_plane_a_only(dev_priv) && crtc-
> > > > >plane
> > > > != PLANE_A)
> > > > +		if (fbc_on_pipe_a_only(dev_priv) &&
> > > > intel_crtc-
> > > > > 
> > > > > pipe != PIPE_A)
> > > >  			continue;
> > > >  
> > > > -		intel_crtc_state = to_intel_crtc_state(
> > > > -			drm_atomic_get_existing_crtc_state(sta
> > > > te,
> > > > &crtc->base));
> > > > +		if (fbc_on_plane_a_only(dev_priv) &&
> > > > +		    intel_crtc->plane != PLANE_A)
> > > > +			continue;
> > > >  
> > > > -		intel_crtc_state->enable_fbc = true;
> > > > +		plane_state->enable_fbc = true;
> > > 
> > > So looking at this whole thing, I can't see anything that would
> > > prevent
> > > enable_fbc being true for multiple primary planes at the same
> > > time
> > > Well, apart from the whole "we enable it only for platforms that
> > > can
> > > only do
> > > pipe A" thing.
> > > 
> > > So what happens in that case? FBC just ends up getting enabling
> > > on
> > > one of the pipes based on the order intel_fbc_enable() gets
> > > called,
> > > or something?
> > 
> > The first check of intel_fbc_choose_crtc() is supposed to prevent
> > this
> > case: if fbc->crtc->primary is not included in the commit we just
> > return without selecting any plane.
> 
> The fbc->crtc thing only works if intel_fbc_enable() was already
> called
> for some crtc. But what it it wasn't?
> 
> > 
> > Otherwise, we only pick one CRTC
> > due to the "break;" statement after setting plane_state->enable_fbc 
> > to
> > true.
> 
> Only one per atomic operation. But what if there are several
> happening
> in parallel on different crtcs?

I see your point now. Yeah, we'll end up with
plane_state.enable_fbc=true for two different planes. Later, the first
one to call intel_fbc_enable() will win, and the others will be
ignored, so we'll indeed end up with plane states having
enable_fbc=true but FBC not enabled by them. Not a real bug, I would
still like to avoid this confusion.

The simplest solution I can think would be to just
s/plane_state.enable_fbc/plane_state.can_enable_fbc/ and just let the
first one to call intel_fbc_enable() win... And then, if we ever decide
to enable FBC on the older platforms, we can choose to maybe implement
a better method (and fix all the possible FBC problems for those
platforms). Is this solution worth a R-B from you?

If you have any better idea here, feel free to suggest it. I'm not sure
it's possible to come up with something much better due to all the
parallelism involved. Making one CRTC "take over" FBC from the other is
something I'd really love to avoid.

Thanks,
Paulo

> 
> > 
> > 
> > > 
> > > 
> > > > 
> > > > 
> > > >  		crtc_chosen = true;
> > > >  		break;
> > > >  	}
> > > > @@ -1130,13 +1132,13 @@ void intel_fbc_enable(struct intel_crtc
> > > > *crtc,
> > > >  	if (fbc->enabled) {
> > > >  		WARN_ON(fbc->crtc == NULL);
> > > >  		if (fbc->crtc == crtc) {
> > > > -			WARN_ON(!crtc_state->enable_fbc);
> > > > +			WARN_ON(!plane_state->enable_fbc);
> > > >  			WARN_ON(fbc->active);
> > > >  		}
> > > >  		goto out;
> > > >  	}
> > > >  
> > > > -	if (!crtc_state->enable_fbc)
> > > > +	if (!plane_state->enable_fbc)
> > > >  		goto out;
> > > >  
> > > >  	WARN_ON(fbc->active);
> > > > -- 
> > > > 2.7.4
> > > 
>
Ville Syrjala Nov. 11, 2016, 8:24 p.m. UTC | #5
On Fri, Nov 11, 2016 at 05:57:28PM -0200, Paulo Zanoni wrote:
> Em Sex, 2016-11-11 às 21:13 +0200, Ville Syrjälä escreveu:
> > On Fri, Nov 11, 2016 at 05:01:54PM -0200, Paulo Zanoni wrote:
> > > 
> > > Em Sex, 2016-11-11 às 20:51 +0200, Ville Syrjälä escreveu:
> > > > 
> > > > On Fri, Nov 11, 2016 at 02:57:40PM -0200, Paulo Zanoni wrote:
> > > > > 
> > > > > 
> > > > > Ville pointed out that intel_fbc_choose_crtc() is iterating
> > > > > over
> > > > > all
> > > > > planes instead of just the primary planes. There are no real
> > > > > consequences of this problem for HSW+, and for the other
> > > > > platforms
> > > > > it
> > > > > just means that in some obscure multi-screen cases we'll keep
> > > > > FBC
> > > > > disabled when we could have enabled it. Still, iterating over
> > > > > all
> > > > > planes doesn't seem to be the best thing to do.
> > > > > 
> > > > > My initial idea was to just add a check for plane->type and be
> > > > > done,
> > > > > but then I realized that in commits not involving the primary
> > > > > plane
> > > > > we
> > > > > would reset crtc_state->enable_fbc back to false even when FBC
> > > > > is
> > > > > enabled. That also wouldn't result in a bug due to the way the
> > > > > enable_fbc variable is checked, but, still, our code can be
> > > > > better
> > > > > than this.
> > > > > 
> > > > > So I went for the solution that involves tracking enable_fbc in
> > > > > the
> > > > > primary plane state instead of the CRTC state. This way, if a
> > > > > commit
> > > > > doesn't involve the primary plane for the CRTC we won't be
> > > > > resetting
> > > > > enable_fbc back to false, so the variable will always reflect
> > > > > the
> > > > > reality. And this change also makes more sense since FBC is
> > > > > actually
> > > > > tied to the single plane and not the full pipe. As a bonus, we
> > > > > only
> > > > > iterate over the CRTCs instead of iterating over all planes.
> > > > > 
> > > > > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > Reported-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
> > > > > ---
> > > > >  drivers/gpu/drm/i915/intel_drv.h |  4 ++--
> > > > >  drivers/gpu/drm/i915/intel_fbc.c | 36 +++++++++++++++++++-----
> > > > > ----
> > > > > --------
> > > > >  2 files changed, 21 insertions(+), 19 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/gpu/drm/i915/intel_drv.h
> > > > > b/drivers/gpu/drm/i915/intel_drv.h
> > > > > index 003afb8..025cb74 100644
> > > > > --- a/drivers/gpu/drm/i915/intel_drv.h
> > > > > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > > > > @@ -403,6 +403,8 @@ struct intel_plane_state {
> > > > >  	int scaler_id;
> > > > >  
> > > > >  	struct drm_intel_sprite_colorkey ckey;
> > > > > +
> > > > > +	bool enable_fbc;
> > > > >  };
> > > > >  
> > > > >  struct intel_initial_plane_config {
> > > > > @@ -648,8 +650,6 @@ struct intel_crtc_state {
> > > > >  
> > > > >  	bool ips_enabled;
> > > > >  
> > > > > -	bool enable_fbc;
> > > > > -
> > > > >  	bool double_wide;
> > > > >  
> > > > >  	bool dp_encoder_is_mst;
> > > > > diff --git a/drivers/gpu/drm/i915/intel_fbc.c
> > > > > b/drivers/gpu/drm/i915/intel_fbc.c
> > > > > index b095175..fc4ac57 100644
> > > > > --- a/drivers/gpu/drm/i915/intel_fbc.c
> > > > > +++ b/drivers/gpu/drm/i915/intel_fbc.c
> > > > > @@ -1055,16 +1055,17 @@ void intel_fbc_choose_crtc(struct
> > > > > drm_i915_private *dev_priv,
> > > > >  			   struct drm_atomic_state *state)
> > > > >  {
> > > > >  	struct intel_fbc *fbc = &dev_priv->fbc;
> > > > > -	struct drm_plane *plane;
> > > > > -	struct drm_plane_state *plane_state;
> > > > > +	struct drm_crtc *crtc;
> > > > > +	struct drm_crtc_state *crtc_state;
> > > > >  	bool crtc_chosen = false;
> > > > >  	int i;
> > > > >  
> > > > >  	mutex_lock(&fbc->lock);
> > > > >  
> > > > > -	/* Does this atomic commit involve the CRTC currently
> > > > > tied
> > > > > to FBC? */
> > > > > +	/* Does this atomic commit involve the plane currently
> > > > > tied to FBC? */
> > > > >  	if (fbc->crtc &&
> > > > > -	    !drm_atomic_get_existing_crtc_state(state, &fbc-
> > > > > >crtc-
> > > > > > 
> > > > > > base))
> > > > > +	    !drm_atomic_get_existing_plane_state(state,
> > > > > +						 fbc->crtc-
> > > > > > 
> > > > > > base.primary))
> > > > >  		goto out;
> > > > >  
> > > > >  	if (!intel_fbc_can_enable(dev_priv))
> > > > > @@ -1074,25 +1075,26 @@ void intel_fbc_choose_crtc(struct
> > > > > drm_i915_private *dev_priv,
> > > > >  	 * plane. We could go for fancier schemes such as
> > > > > checking
> > > > > the plane
> > > > >  	 * size, but this would just affect the few platforms
> > > > > that
> > > > > don't tie FBC
> > > > >  	 * to pipe or plane A. */
> > > > > -	for_each_plane_in_state(state, plane, plane_state, i)
> > > > > {
> > > > > -		struct intel_plane_state *intel_plane_state =
> > > > > -			to_intel_plane_state(plane_state);
> > > > > -		struct intel_crtc_state *intel_crtc_state;
> > > > > -		struct intel_crtc *crtc =
> > > > > to_intel_crtc(plane_state->crtc);
> > > > > +	for_each_crtc_in_state(state, crtc, crtc_state, i) {
> > > > > +		struct intel_plane_state *plane_state =
> > > > > to_intel_plane_state(
> > > > > +			drm_atomic_get_existing_plane_state(st
> > > > > ate,
> > > > > +							    cr
> > > > > tc-
> > > > > > 
> > > > > > primary));
> > > > > +		struct intel_crtc *intel_crtc =
> > > > > to_intel_crtc(crtc);
> > > > >  
> > > > > -		if (!intel_plane_state->base.visible)
> > > > > +		if (!plane_state)
> > > > >  			continue;
> > > > >  
> > > > > -		if (fbc_on_pipe_a_only(dev_priv) && crtc->pipe 
> > > > > !=
> > > > > PIPE_A)
> > > > > +		if (!plane_state->base.visible)
> > > > >  			continue;
> > > > >  
> > > > > -		if (fbc_on_plane_a_only(dev_priv) && crtc-
> > > > > >plane
> > > > > != PLANE_A)
> > > > > +		if (fbc_on_pipe_a_only(dev_priv) &&
> > > > > intel_crtc-
> > > > > > 
> > > > > > pipe != PIPE_A)
> > > > >  			continue;
> > > > >  
> > > > > -		intel_crtc_state = to_intel_crtc_state(
> > > > > -			drm_atomic_get_existing_crtc_state(sta
> > > > > te,
> > > > > &crtc->base));
> > > > > +		if (fbc_on_plane_a_only(dev_priv) &&
> > > > > +		    intel_crtc->plane != PLANE_A)
> > > > > +			continue;
> > > > >  
> > > > > -		intel_crtc_state->enable_fbc = true;
> > > > > +		plane_state->enable_fbc = true;
> > > > 
> > > > So looking at this whole thing, I can't see anything that would
> > > > prevent
> > > > enable_fbc being true for multiple primary planes at the same
> > > > time
> > > > Well, apart from the whole "we enable it only for platforms that
> > > > can
> > > > only do
> > > > pipe A" thing.
> > > > 
> > > > So what happens in that case? FBC just ends up getting enabling
> > > > on
> > > > one of the pipes based on the order intel_fbc_enable() gets
> > > > called,
> > > > or something?
> > > 
> > > The first check of intel_fbc_choose_crtc() is supposed to prevent
> > > this
> > > case: if fbc->crtc->primary is not included in the commit we just
> > > return without selecting any plane.
> > 
> > The fbc->crtc thing only works if intel_fbc_enable() was already
> > called
> > for some crtc. But what it it wasn't?
> > 
> > > 
> > > Otherwise, we only pick one CRTC
> > > due to the "break;" statement after setting plane_state->enable_fbc 
> > > to
> > > true.
> > 
> > Only one per atomic operation. But what if there are several
> > happening
> > in parallel on different crtcs?
> 
> I see your point now. Yeah, we'll end up with
> plane_state.enable_fbc=true for two different planes. Later, the first
> one to call intel_fbc_enable() will win, and the others will be
> ignored, so we'll indeed end up with plane states having
> enable_fbc=true but FBC not enabled by them. Not a real bug, I would
> still like to avoid this confusion.
> 
> The simplest solution I can think would be to just
> s/plane_state.enable_fbc/plane_state.can_enable_fbc/ and just let the
> first one to call intel_fbc_enable() win... And then, if we ever decide
> to enable FBC on the older platforms, we can choose to maybe implement
> a better method

Maybe something like "fbc_score"? ;)

> (and fix all the possible FBC problems for those
> platforms).

I think I fixed all the platforms specific issues back in the day. All
the actual problems were in the common code. Although I guess some of
my patches might not have made it in, or I just missed some things.

> Is this solution worth a R-B from you?
> 
> If you have any better idea here, feel free to suggest it. I'm not sure
> it's possible to come up with something much better due to all the
> parallelism involved. Making one CRTC "take over" FBC from the other is
> something I'd really love to avoid.

Yeah, it looked safe enough to me to have the thing set to true on
multiple planes, and if you came to the same conclusion I think we
should be good.

With the rename of the flag to avoid confusion:
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>

> 
> Thanks,
> Paulo
> 
> > 
> > > 
> > > 
> > > > 
> > > > 
> > > > > 
> > > > > 
> > > > >  		crtc_chosen = true;
> > > > >  		break;
> > > > >  	}
> > > > > @@ -1130,13 +1132,13 @@ void intel_fbc_enable(struct intel_crtc
> > > > > *crtc,
> > > > >  	if (fbc->enabled) {
> > > > >  		WARN_ON(fbc->crtc == NULL);
> > > > >  		if (fbc->crtc == crtc) {
> > > > > -			WARN_ON(!crtc_state->enable_fbc);
> > > > > +			WARN_ON(!plane_state->enable_fbc);
> > > > >  			WARN_ON(fbc->active);
> > > > >  		}
> > > > >  		goto out;
> > > > >  	}
> > > > >  
> > > > > -	if (!crtc_state->enable_fbc)
> > > > > +	if (!plane_state->enable_fbc)
> > > > >  		goto out;
> > > > >  
> > > > >  	WARN_ON(fbc->active);
> > > > > -- 
> > > > > 2.7.4
> > > > 
> >
Zanoni, Paulo R Nov. 11, 2016, 8:49 p.m. UTC | #6
Em Sex, 2016-11-11 às 22:24 +0200, Ville Syrjälä escreveu:
> On Fri, Nov 11, 2016 at 05:57:28PM -0200, Paulo Zanoni wrote:
> > 
> > Em Sex, 2016-11-11 às 21:13 +0200, Ville Syrjälä escreveu:
> > > 
> > > On Fri, Nov 11, 2016 at 05:01:54PM -0200, Paulo Zanoni wrote:
> > > > 
> > > > 
> > > > Em Sex, 2016-11-11 às 20:51 +0200, Ville Syrjälä escreveu:
> > > > > 
> > > > > 
> > > > > On Fri, Nov 11, 2016 at 02:57:40PM -0200, Paulo Zanoni wrote:
> > > > > > 
> > > > > > 
> > > > > > 
> > > > > > Ville pointed out that intel_fbc_choose_crtc() is iterating
> > > > > > over
> > > > > > all
> > > > > > planes instead of just the primary planes. There are no
> > > > > > real
> > > > > > consequences of this problem for HSW+, and for the other
> > > > > > platforms
> > > > > > it
> > > > > > just means that in some obscure multi-screen cases we'll
> > > > > > keep
> > > > > > FBC
> > > > > > disabled when we could have enabled it. Still, iterating
> > > > > > over
> > > > > > all
> > > > > > planes doesn't seem to be the best thing to do.
> > > > > > 
> > > > > > My initial idea was to just add a check for plane->type and
> > > > > > be
> > > > > > done,
> > > > > > but then I realized that in commits not involving the
> > > > > > primary
> > > > > > plane
> > > > > > we
> > > > > > would reset crtc_state->enable_fbc back to false even when
> > > > > > FBC
> > > > > > is
> > > > > > enabled. That also wouldn't result in a bug due to the way
> > > > > > the
> > > > > > enable_fbc variable is checked, but, still, our code can be
> > > > > > better
> > > > > > than this.
> > > > > > 
> > > > > > So I went for the solution that involves tracking
> > > > > > enable_fbc in
> > > > > > the
> > > > > > primary plane state instead of the CRTC state. This way, if
> > > > > > a
> > > > > > commit
> > > > > > doesn't involve the primary plane for the CRTC we won't be
> > > > > > resetting
> > > > > > enable_fbc back to false, so the variable will always
> > > > > > reflect
> > > > > > the
> > > > > > reality. And this change also makes more sense since FBC is
> > > > > > actually
> > > > > > tied to the single plane and not the full pipe. As a bonus,
> > > > > > we
> > > > > > only
> > > > > > iterate over the CRTCs instead of iterating over all
> > > > > > planes.
> > > > > > 
> > > > > > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > > Reported-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > > Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
> > > > > > ---
> > > > > >  drivers/gpu/drm/i915/intel_drv.h |  4 ++--
> > > > > >  drivers/gpu/drm/i915/intel_fbc.c | 36 +++++++++++++++++++-
> > > > > > ----
> > > > > > ----
> > > > > > --------
> > > > > >  2 files changed, 21 insertions(+), 19 deletions(-)
> > > > > > 
> > > > > > diff --git a/drivers/gpu/drm/i915/intel_drv.h
> > > > > > b/drivers/gpu/drm/i915/intel_drv.h
> > > > > > index 003afb8..025cb74 100644
> > > > > > --- a/drivers/gpu/drm/i915/intel_drv.h
> > > > > > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > > > > > @@ -403,6 +403,8 @@ struct intel_plane_state {
> > > > > >  	int scaler_id;
> > > > > >  
> > > > > >  	struct drm_intel_sprite_colorkey ckey;
> > > > > > +
> > > > > > +	bool enable_fbc;
> > > > > >  };
> > > > > >  
> > > > > >  struct intel_initial_plane_config {
> > > > > > @@ -648,8 +650,6 @@ struct intel_crtc_state {
> > > > > >  
> > > > > >  	bool ips_enabled;
> > > > > >  
> > > > > > -	bool enable_fbc;
> > > > > > -
> > > > > >  	bool double_wide;
> > > > > >  
> > > > > >  	bool dp_encoder_is_mst;
> > > > > > diff --git a/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > b/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > index b095175..fc4ac57 100644
> > > > > > --- a/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > +++ b/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > @@ -1055,16 +1055,17 @@ void intel_fbc_choose_crtc(struct
> > > > > > drm_i915_private *dev_priv,
> > > > > >  			   struct drm_atomic_state *state)
> > > > > >  {
> > > > > >  	struct intel_fbc *fbc = &dev_priv->fbc;
> > > > > > -	struct drm_plane *plane;
> > > > > > -	struct drm_plane_state *plane_state;
> > > > > > +	struct drm_crtc *crtc;
> > > > > > +	struct drm_crtc_state *crtc_state;
> > > > > >  	bool crtc_chosen = false;
> > > > > >  	int i;
> > > > > >  
> > > > > >  	mutex_lock(&fbc->lock);
> > > > > >  
> > > > > > -	/* Does this atomic commit involve the CRTC
> > > > > > currently
> > > > > > tied
> > > > > > to FBC? */
> > > > > > +	/* Does this atomic commit involve the plane
> > > > > > currently
> > > > > > tied to FBC? */
> > > > > >  	if (fbc->crtc &&
> > > > > > -	    !drm_atomic_get_existing_crtc_state(state,
> > > > > > &fbc-
> > > > > > > 
> > > > > > > crtc-
> > > > > > > 
> > > > > > > base))
> > > > > > +	    !drm_atomic_get_existing_plane_state(state,
> > > > > > +						 fbc-
> > > > > > >crtc-
> > > > > > > 
> > > > > > > 
> > > > > > > base.primary))
> > > > > >  		goto out;
> > > > > >  
> > > > > >  	if (!intel_fbc_can_enable(dev_priv))
> > > > > > @@ -1074,25 +1075,26 @@ void intel_fbc_choose_crtc(struct
> > > > > > drm_i915_private *dev_priv,
> > > > > >  	 * plane. We could go for fancier schemes such as
> > > > > > checking
> > > > > > the plane
> > > > > >  	 * size, but this would just affect the few
> > > > > > platforms
> > > > > > that
> > > > > > don't tie FBC
> > > > > >  	 * to pipe or plane A. */
> > > > > > -	for_each_plane_in_state(state, plane, plane_state,
> > > > > > i)
> > > > > > {
> > > > > > -		struct intel_plane_state
> > > > > > *intel_plane_state =
> > > > > > -			to_intel_plane_state(plane_state);
> > > > > > -		struct intel_crtc_state *intel_crtc_state;
> > > > > > -		struct intel_crtc *crtc =
> > > > > > to_intel_crtc(plane_state->crtc);
> > > > > > +	for_each_crtc_in_state(state, crtc, crtc_state, i)
> > > > > > {
> > > > > > +		struct intel_plane_state *plane_state =
> > > > > > to_intel_plane_state(
> > > > > > +			drm_atomic_get_existing_plane_stat
> > > > > > e(st
> > > > > > ate,
> > > > > > +							  
> > > > > >   cr
> > > > > > tc-
> > > > > > > 
> > > > > > > 
> > > > > > > primary));
> > > > > > +		struct intel_crtc *intel_crtc =
> > > > > > to_intel_crtc(crtc);
> > > > > >  
> > > > > > -		if (!intel_plane_state->base.visible)
> > > > > > +		if (!plane_state)
> > > > > >  			continue;
> > > > > >  
> > > > > > -		if (fbc_on_pipe_a_only(dev_priv) && crtc-
> > > > > > >pipe 
> > > > > > !=
> > > > > > PIPE_A)
> > > > > > +		if (!plane_state->base.visible)
> > > > > >  			continue;
> > > > > >  
> > > > > > -		if (fbc_on_plane_a_only(dev_priv) && crtc-
> > > > > > > 
> > > > > > > plane
> > > > > > != PLANE_A)
> > > > > > +		if (fbc_on_pipe_a_only(dev_priv) &&
> > > > > > intel_crtc-
> > > > > > > 
> > > > > > > 
> > > > > > > pipe != PIPE_A)
> > > > > >  			continue;
> > > > > >  
> > > > > > -		intel_crtc_state = to_intel_crtc_state(
> > > > > > -			drm_atomic_get_existing_crtc_state
> > > > > > (sta
> > > > > > te,
> > > > > > &crtc->base));
> > > > > > +		if (fbc_on_plane_a_only(dev_priv) &&
> > > > > > +		    intel_crtc->plane != PLANE_A)
> > > > > > +			continue;
> > > > > >  
> > > > > > -		intel_crtc_state->enable_fbc = true;
> > > > > > +		plane_state->enable_fbc = true;
> > > > > 
> > > > > So looking at this whole thing, I can't see anything that
> > > > > would
> > > > > prevent
> > > > > enable_fbc being true for multiple primary planes at the same
> > > > > time
> > > > > Well, apart from the whole "we enable it only for platforms
> > > > > that
> > > > > can
> > > > > only do
> > > > > pipe A" thing.
> > > > > 
> > > > > So what happens in that case? FBC just ends up getting
> > > > > enabling
> > > > > on
> > > > > one of the pipes based on the order intel_fbc_enable() gets
> > > > > called,
> > > > > or something?
> > > > 
> > > > The first check of intel_fbc_choose_crtc() is supposed to
> > > > prevent
> > > > this
> > > > case: if fbc->crtc->primary is not included in the commit we
> > > > just
> > > > return without selecting any plane.
> > > 
> > > The fbc->crtc thing only works if intel_fbc_enable() was already
> > > called
> > > for some crtc. But what it it wasn't?
> > > 
> > > > 
> > > > 
> > > > Otherwise, we only pick one CRTC
> > > > due to the "break;" statement after setting plane_state-
> > > > >enable_fbc 
> > > > to
> > > > true.
> > > 
> > > Only one per atomic operation. But what if there are several
> > > happening
> > > in parallel on different crtcs?
> > 
> > I see your point now. Yeah, we'll end up with
> > plane_state.enable_fbc=true for two different planes. Later, the
> > first
> > one to call intel_fbc_enable() will win, and the others will be
> > ignored, so we'll indeed end up with plane states having
> > enable_fbc=true but FBC not enabled by them. Not a real bug, I
> > would
> > still like to avoid this confusion.
> > 
> > The simplest solution I can think would be to just
> > s/plane_state.enable_fbc/plane_state.can_enable_fbc/ and just let
> > the
> > first one to call intel_fbc_enable() win... And then, if we ever
> > decide
> > to enable FBC on the older platforms, we can choose to maybe
> > implement
> > a better method
> 
> Maybe something like "fbc_score"? ;)

The design of the current function was supposed to allow Ville to
implement his fbc_score in case he wanted. But this certainly didn't
take into account multiple parallel commits: it would only work if
multiple CRTCs were included in the same commit (as you just pointed
today).

But then: if we're having separate parallel commits, when would we be
able to loop through the scores to only actually enable FBC on the best
score? 

For example, if we do two parallel atomic_check()s and end with
plane_a_score=1 and plane_b_score=2, then later we do A's commit() and
call intel_fbc_enable() for it, how do we conclude that we shouldn't
enable FBC for plane A? We're not even sure if plane B is going to
actually commit the plane state it calculated (maybe it was
check_only).

And then, if we decide to only compute everything during commit()
instead of check(), we'll just also end up enabling FBC for plane A
since A's commit() will run first and we'll have no idea that B's
commit is incoming.

The only option would be to disable FBC for plane A and enable for
plane B during B's commit. But I'm not looking forward to implement
this right now.

So we kinda have 3 options:

1 - Keep the current approach (with the renamed variable) that at least
allows us to pick the best CRTC in case a single commit includes
multiple CRTCs.

2 - Go the simplest route and not even bother picking the best CRTC
among a single commit due to the fact that parallel commits won't
guarantee we always have the best FBC plane enabled, so why bother.

3 - Wait for someone to implement the takeover approach.

Thanks for the review,
Paulo


> > 
> > (and fix all the possible FBC problems for those
> > platforms).
> 
> I think I fixed all the platforms specific issues back in the day.
> All
> the actual problems were in the common code. Although I guess some of
> my patches might not have made it in, or I just missed some things.
> 
> > 
> > Is this solution worth a R-B from you?
> > 
> > If you have any better idea here, feel free to suggest it. I'm not
> > sure
> > it's possible to come up with something much better due to all the
> > parallelism involved. Making one CRTC "take over" FBC from the
> > other is
> > something I'd really love to avoid.
> 
> Yeah, it looked safe enough to me to have the thing set to true on
> multiple planes, and if you came to the same conclusion I think we
> should be good.
> 
> With the rename of the flag to avoid confusion:
> Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>

> > 
> > 
> > Thanks,
> > Paulo
> > 
> > > 
> > > 
> > > > 
> > > > 
> > > > 
> > > > > 
> > > > > 
> > > > > 
> > > > > > 
> > > > > > 
> > > > > > 
> > > > > >  		crtc_chosen = true;
> > > > > >  		break;
> > > > > >  	}
> > > > > > @@ -1130,13 +1132,13 @@ void intel_fbc_enable(struct
> > > > > > intel_crtc
> > > > > > *crtc,
> > > > > >  	if (fbc->enabled) {
> > > > > >  		WARN_ON(fbc->crtc == NULL);
> > > > > >  		if (fbc->crtc == crtc) {
> > > > > > -			WARN_ON(!crtc_state->enable_fbc);
> > > > > > +			WARN_ON(!plane_state->enable_fbc);
> > > > > >  			WARN_ON(fbc->active);
> > > > > >  		}
> > > > > >  		goto out;
> > > > > >  	}
> > > > > >  
> > > > > > -	if (!crtc_state->enable_fbc)
> > > > > > +	if (!plane_state->enable_fbc)
> > > > > >  		goto out;
> > > > > >  
> > > > > >  	WARN_ON(fbc->active);
> > > > > > -- 
> > > > > > 2.7.4
> > > > > 
> > > 
>
Ville Syrjala Nov. 14, 2016, 8:26 p.m. UTC | #7
On Fri, Nov 11, 2016 at 06:49:59PM -0200, Paulo Zanoni wrote:
> Em Sex, 2016-11-11 às 22:24 +0200, Ville Syrjälä escreveu:
> > On Fri, Nov 11, 2016 at 05:57:28PM -0200, Paulo Zanoni wrote:
> > > 
> > > Em Sex, 2016-11-11 às 21:13 +0200, Ville Syrjälä escreveu:
> > > > 
> > > > On Fri, Nov 11, 2016 at 05:01:54PM -0200, Paulo Zanoni wrote:
> > > > > 
> > > > > 
> > > > > Em Sex, 2016-11-11 às 20:51 +0200, Ville Syrjälä escreveu:
> > > > > > 
> > > > > > 
> > > > > > On Fri, Nov 11, 2016 at 02:57:40PM -0200, Paulo Zanoni wrote:
> > > > > > > 
> > > > > > > 
> > > > > > > 
> > > > > > > Ville pointed out that intel_fbc_choose_crtc() is iterating
> > > > > > > over
> > > > > > > all
> > > > > > > planes instead of just the primary planes. There are no
> > > > > > > real
> > > > > > > consequences of this problem for HSW+, and for the other
> > > > > > > platforms
> > > > > > > it
> > > > > > > just means that in some obscure multi-screen cases we'll
> > > > > > > keep
> > > > > > > FBC
> > > > > > > disabled when we could have enabled it. Still, iterating
> > > > > > > over
> > > > > > > all
> > > > > > > planes doesn't seem to be the best thing to do.
> > > > > > > 
> > > > > > > My initial idea was to just add a check for plane->type and
> > > > > > > be
> > > > > > > done,
> > > > > > > but then I realized that in commits not involving the
> > > > > > > primary
> > > > > > > plane
> > > > > > > we
> > > > > > > would reset crtc_state->enable_fbc back to false even when
> > > > > > > FBC
> > > > > > > is
> > > > > > > enabled. That also wouldn't result in a bug due to the way
> > > > > > > the
> > > > > > > enable_fbc variable is checked, but, still, our code can be
> > > > > > > better
> > > > > > > than this.
> > > > > > > 
> > > > > > > So I went for the solution that involves tracking
> > > > > > > enable_fbc in
> > > > > > > the
> > > > > > > primary plane state instead of the CRTC state. This way, if
> > > > > > > a
> > > > > > > commit
> > > > > > > doesn't involve the primary plane for the CRTC we won't be
> > > > > > > resetting
> > > > > > > enable_fbc back to false, so the variable will always
> > > > > > > reflect
> > > > > > > the
> > > > > > > reality. And this change also makes more sense since FBC is
> > > > > > > actually
> > > > > > > tied to the single plane and not the full pipe. As a bonus,
> > > > > > > we
> > > > > > > only
> > > > > > > iterate over the CRTCs instead of iterating over all
> > > > > > > planes.
> > > > > > > 
> > > > > > > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > > > Reported-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > > > Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
> > > > > > > ---
> > > > > > >  drivers/gpu/drm/i915/intel_drv.h |  4 ++--
> > > > > > >  drivers/gpu/drm/i915/intel_fbc.c | 36 +++++++++++++++++++-
> > > > > > > ----
> > > > > > > ----
> > > > > > > --------
> > > > > > >  2 files changed, 21 insertions(+), 19 deletions(-)
> > > > > > > 
> > > > > > > diff --git a/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > b/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > index 003afb8..025cb74 100644
> > > > > > > --- a/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > @@ -403,6 +403,8 @@ struct intel_plane_state {
> > > > > > >  	int scaler_id;
> > > > > > >  
> > > > > > >  	struct drm_intel_sprite_colorkey ckey;
> > > > > > > +
> > > > > > > +	bool enable_fbc;
> > > > > > >  };
> > > > > > >  
> > > > > > >  struct intel_initial_plane_config {
> > > > > > > @@ -648,8 +650,6 @@ struct intel_crtc_state {
> > > > > > >  
> > > > > > >  	bool ips_enabled;
> > > > > > >  
> > > > > > > -	bool enable_fbc;
> > > > > > > -
> > > > > > >  	bool double_wide;
> > > > > > >  
> > > > > > >  	bool dp_encoder_is_mst;
> > > > > > > diff --git a/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > b/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > index b095175..fc4ac57 100644
> > > > > > > --- a/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > +++ b/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > @@ -1055,16 +1055,17 @@ void intel_fbc_choose_crtc(struct
> > > > > > > drm_i915_private *dev_priv,
> > > > > > >  			   struct drm_atomic_state *state)
> > > > > > >  {
> > > > > > >  	struct intel_fbc *fbc = &dev_priv->fbc;
> > > > > > > -	struct drm_plane *plane;
> > > > > > > -	struct drm_plane_state *plane_state;
> > > > > > > +	struct drm_crtc *crtc;
> > > > > > > +	struct drm_crtc_state *crtc_state;
> > > > > > >  	bool crtc_chosen = false;
> > > > > > >  	int i;
> > > > > > >  
> > > > > > >  	mutex_lock(&fbc->lock);
> > > > > > >  
> > > > > > > -	/* Does this atomic commit involve the CRTC
> > > > > > > currently
> > > > > > > tied
> > > > > > > to FBC? */
> > > > > > > +	/* Does this atomic commit involve the plane
> > > > > > > currently
> > > > > > > tied to FBC? */
> > > > > > >  	if (fbc->crtc &&
> > > > > > > -	    !drm_atomic_get_existing_crtc_state(state,
> > > > > > > &fbc-
> > > > > > > > 
> > > > > > > > crtc-
> > > > > > > > 
> > > > > > > > base))
> > > > > > > +	    !drm_atomic_get_existing_plane_state(state,
> > > > > > > +						 fbc-
> > > > > > > >crtc-
> > > > > > > > 
> > > > > > > > 
> > > > > > > > base.primary))
> > > > > > >  		goto out;
> > > > > > >  
> > > > > > >  	if (!intel_fbc_can_enable(dev_priv))
> > > > > > > @@ -1074,25 +1075,26 @@ void intel_fbc_choose_crtc(struct
> > > > > > > drm_i915_private *dev_priv,
> > > > > > >  	 * plane. We could go for fancier schemes such as
> > > > > > > checking
> > > > > > > the plane
> > > > > > >  	 * size, but this would just affect the few
> > > > > > > platforms
> > > > > > > that
> > > > > > > don't tie FBC
> > > > > > >  	 * to pipe or plane A. */
> > > > > > > -	for_each_plane_in_state(state, plane, plane_state,
> > > > > > > i)
> > > > > > > {
> > > > > > > -		struct intel_plane_state
> > > > > > > *intel_plane_state =
> > > > > > > -			to_intel_plane_state(plane_state);
> > > > > > > -		struct intel_crtc_state *intel_crtc_state;
> > > > > > > -		struct intel_crtc *crtc =
> > > > > > > to_intel_crtc(plane_state->crtc);
> > > > > > > +	for_each_crtc_in_state(state, crtc, crtc_state, i)
> > > > > > > {
> > > > > > > +		struct intel_plane_state *plane_state =
> > > > > > > to_intel_plane_state(
> > > > > > > +			drm_atomic_get_existing_plane_stat
> > > > > > > e(st
> > > > > > > ate,
> > > > > > > +							  
> > > > > > >   cr
> > > > > > > tc-
> > > > > > > > 
> > > > > > > > 
> > > > > > > > primary));
> > > > > > > +		struct intel_crtc *intel_crtc =
> > > > > > > to_intel_crtc(crtc);
> > > > > > >  
> > > > > > > -		if (!intel_plane_state->base.visible)
> > > > > > > +		if (!plane_state)
> > > > > > >  			continue;
> > > > > > >  
> > > > > > > -		if (fbc_on_pipe_a_only(dev_priv) && crtc-
> > > > > > > >pipe 
> > > > > > > !=
> > > > > > > PIPE_A)
> > > > > > > +		if (!plane_state->base.visible)
> > > > > > >  			continue;
> > > > > > >  
> > > > > > > -		if (fbc_on_plane_a_only(dev_priv) && crtc-
> > > > > > > > 
> > > > > > > > plane
> > > > > > > != PLANE_A)
> > > > > > > +		if (fbc_on_pipe_a_only(dev_priv) &&
> > > > > > > intel_crtc-
> > > > > > > > 
> > > > > > > > 
> > > > > > > > pipe != PIPE_A)
> > > > > > >  			continue;
> > > > > > >  
> > > > > > > -		intel_crtc_state = to_intel_crtc_state(
> > > > > > > -			drm_atomic_get_existing_crtc_state
> > > > > > > (sta
> > > > > > > te,
> > > > > > > &crtc->base));
> > > > > > > +		if (fbc_on_plane_a_only(dev_priv) &&
> > > > > > > +		    intel_crtc->plane != PLANE_A)
> > > > > > > +			continue;
> > > > > > >  
> > > > > > > -		intel_crtc_state->enable_fbc = true;
> > > > > > > +		plane_state->enable_fbc = true;
> > > > > > 
> > > > > > So looking at this whole thing, I can't see anything that
> > > > > > would
> > > > > > prevent
> > > > > > enable_fbc being true for multiple primary planes at the same
> > > > > > time
> > > > > > Well, apart from the whole "we enable it only for platforms
> > > > > > that
> > > > > > can
> > > > > > only do
> > > > > > pipe A" thing.
> > > > > > 
> > > > > > So what happens in that case? FBC just ends up getting
> > > > > > enabling
> > > > > > on
> > > > > > one of the pipes based on the order intel_fbc_enable() gets
> > > > > > called,
> > > > > > or something?
> > > > > 
> > > > > The first check of intel_fbc_choose_crtc() is supposed to
> > > > > prevent
> > > > > this
> > > > > case: if fbc->crtc->primary is not included in the commit we
> > > > > just
> > > > > return without selecting any plane.
> > > > 
> > > > The fbc->crtc thing only works if intel_fbc_enable() was already
> > > > called
> > > > for some crtc. But what it it wasn't?
> > > > 
> > > > > 
> > > > > 
> > > > > Otherwise, we only pick one CRTC
> > > > > due to the "break;" statement after setting plane_state-
> > > > > >enable_fbc 
> > > > > to
> > > > > true.
> > > > 
> > > > Only one per atomic operation. But what if there are several
> > > > happening
> > > > in parallel on different crtcs?
> > > 
> > > I see your point now. Yeah, we'll end up with
> > > plane_state.enable_fbc=true for two different planes. Later, the
> > > first
> > > one to call intel_fbc_enable() will win, and the others will be
> > > ignored, so we'll indeed end up with plane states having
> > > enable_fbc=true but FBC not enabled by them. Not a real bug, I
> > > would
> > > still like to avoid this confusion.
> > > 
> > > The simplest solution I can think would be to just
> > > s/plane_state.enable_fbc/plane_state.can_enable_fbc/ and just let
> > > the
> > > first one to call intel_fbc_enable() win... And then, if we ever
> > > decide
> > > to enable FBC on the older platforms, we can choose to maybe
> > > implement
> > > a better method
> > 
> > Maybe something like "fbc_score"? ;)
> 
> The design of the current function was supposed to allow Ville to
> implement his fbc_score in case he wanted. But this certainly didn't
> take into account multiple parallel commits: it would only work if
> multiple CRTCs were included in the same commit (as you just pointed
> today).
> 
> But then: if we're having separate parallel commits, when would we be
> able to loop through the scores to only actually enable FBC on the best
> score? 
> 
> For example, if we do two parallel atomic_check()s and end with
> plane_a_score=1 and plane_b_score=2, then later we do A's commit() and
> call intel_fbc_enable() for it, how do we conclude that we shouldn't
> enable FBC for plane A? We're not even sure if plane B is going to
> actually commit the plane state it calculated (maybe it was
> check_only).
> 
> And then, if we decide to only compute everything during commit()
> instead of check(), we'll just also end up enabling FBC for plane A
> since A's commit() will run first and we'll have no idea that B's
> commit is incoming.
> 
> The only option would be to disable FBC for plane A and enable for
> plane B during B's commit. But I'm not looking forward to implement
> this right now.

All the enable/disable should be totally async and so you should be
able to kick them off from anywhere. All the state, including the scores,
would be protected by the fbc mutex. I had a vblank worker type of
thing for this purpose in my patches.
Zanoni, Paulo R Nov. 14, 2016, 8:49 p.m. UTC | #8
Em Seg, 2016-11-14 às 22:26 +0200, Ville Syrjälä escreveu:
> On Fri, Nov 11, 2016 at 06:49:59PM -0200, Paulo Zanoni wrote:
> > 
> > Em Sex, 2016-11-11 às 22:24 +0200, Ville Syrjälä escreveu:
> > > 
> > > On Fri, Nov 11, 2016 at 05:57:28PM -0200, Paulo Zanoni wrote:
> > > > 
> > > > 
> > > > Em Sex, 2016-11-11 às 21:13 +0200, Ville Syrjälä escreveu:
> > > > > 
> > > > > 
> > > > > On Fri, Nov 11, 2016 at 05:01:54PM -0200, Paulo Zanoni wrote:
> > > > > > 
> > > > > > 
> > > > > > 
> > > > > > Em Sex, 2016-11-11 às 20:51 +0200, Ville Syrjälä escreveu:
> > > > > > > 
> > > > > > > 
> > > > > > > 
> > > > > > > On Fri, Nov 11, 2016 at 02:57:40PM -0200, Paulo Zanoni
> > > > > > > wrote:
> > > > > > > > 
> > > > > > > > 
> > > > > > > > 
> > > > > > > > 
> > > > > > > > Ville pointed out that intel_fbc_choose_crtc() is
> > > > > > > > iterating
> > > > > > > > over
> > > > > > > > all
> > > > > > > > planes instead of just the primary planes. There are no
> > > > > > > > real
> > > > > > > > consequences of this problem for HSW+, and for the
> > > > > > > > other
> > > > > > > > platforms
> > > > > > > > it
> > > > > > > > just means that in some obscure multi-screen cases
> > > > > > > > we'll
> > > > > > > > keep
> > > > > > > > FBC
> > > > > > > > disabled when we could have enabled it. Still,
> > > > > > > > iterating
> > > > > > > > over
> > > > > > > > all
> > > > > > > > planes doesn't seem to be the best thing to do.
> > > > > > > > 
> > > > > > > > My initial idea was to just add a check for plane->type 
> > > > > > > > and
> > > > > > > > be
> > > > > > > > done,
> > > > > > > > but then I realized that in commits not involving the
> > > > > > > > primary
> > > > > > > > plane
> > > > > > > > we
> > > > > > > > would reset crtc_state->enable_fbc back to false even
> > > > > > > > when
> > > > > > > > FBC
> > > > > > > > is
> > > > > > > > enabled. That also wouldn't result in a bug due to the
> > > > > > > > way
> > > > > > > > the
> > > > > > > > enable_fbc variable is checked, but, still, our code
> > > > > > > > can be
> > > > > > > > better
> > > > > > > > than this.
> > > > > > > > 
> > > > > > > > So I went for the solution that involves tracking
> > > > > > > > enable_fbc in
> > > > > > > > the
> > > > > > > > primary plane state instead of the CRTC state. This
> > > > > > > > way, if
> > > > > > > > a
> > > > > > > > commit
> > > > > > > > doesn't involve the primary plane for the CRTC we won't
> > > > > > > > be
> > > > > > > > resetting
> > > > > > > > enable_fbc back to false, so the variable will always
> > > > > > > > reflect
> > > > > > > > the
> > > > > > > > reality. And this change also makes more sense since
> > > > > > > > FBC is
> > > > > > > > actually
> > > > > > > > tied to the single plane and not the full pipe. As a
> > > > > > > > bonus,
> > > > > > > > we
> > > > > > > > only
> > > > > > > > iterate over the CRTCs instead of iterating over all
> > > > > > > > planes.
> > > > > > > > 
> > > > > > > > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > > > > Reported-by: Ville Syrjälä <ville.syrjala@linux.intel.c
> > > > > > > > om>
> > > > > > > > Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
> > > > > > > > ---
> > > > > > > >  drivers/gpu/drm/i915/intel_drv.h |  4 ++--
> > > > > > > >  drivers/gpu/drm/i915/intel_fbc.c | 36
> > > > > > > > +++++++++++++++++++-
> > > > > > > > ----
> > > > > > > > ----
> > > > > > > > --------
> > > > > > > >  2 files changed, 21 insertions(+), 19 deletions(-)
> > > > > > > > 
> > > > > > > > diff --git a/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > > b/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > > index 003afb8..025cb74 100644
> > > > > > > > --- a/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > > @@ -403,6 +403,8 @@ struct intel_plane_state {
> > > > > > > >  	int scaler_id;
> > > > > > > >  
> > > > > > > >  	struct drm_intel_sprite_colorkey ckey;
> > > > > > > > +
> > > > > > > > +	bool enable_fbc;
> > > > > > > >  };
> > > > > > > >  
> > > > > > > >  struct intel_initial_plane_config {
> > > > > > > > @@ -648,8 +650,6 @@ struct intel_crtc_state {
> > > > > > > >  
> > > > > > > >  	bool ips_enabled;
> > > > > > > >  
> > > > > > > > -	bool enable_fbc;
> > > > > > > > -
> > > > > > > >  	bool double_wide;
> > > > > > > >  
> > > > > > > >  	bool dp_encoder_is_mst;
> > > > > > > > diff --git a/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > > b/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > > index b095175..fc4ac57 100644
> > > > > > > > --- a/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > > +++ b/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > > @@ -1055,16 +1055,17 @@ void
> > > > > > > > intel_fbc_choose_crtc(struct
> > > > > > > > drm_i915_private *dev_priv,
> > > > > > > >  			   struct drm_atomic_state
> > > > > > > > *state)
> > > > > > > >  {
> > > > > > > >  	struct intel_fbc *fbc = &dev_priv->fbc;
> > > > > > > > -	struct drm_plane *plane;
> > > > > > > > -	struct drm_plane_state *plane_state;
> > > > > > > > +	struct drm_crtc *crtc;
> > > > > > > > +	struct drm_crtc_state *crtc_state;
> > > > > > > >  	bool crtc_chosen = false;
> > > > > > > >  	int i;
> > > > > > > >  
> > > > > > > >  	mutex_lock(&fbc->lock);
> > > > > > > >  
> > > > > > > > -	/* Does this atomic commit involve the CRTC
> > > > > > > > currently
> > > > > > > > tied
> > > > > > > > to FBC? */
> > > > > > > > +	/* Does this atomic commit involve the plane
> > > > > > > > currently
> > > > > > > > tied to FBC? */
> > > > > > > >  	if (fbc->crtc &&
> > > > > > > > -	    !drm_atomic_get_existing_crtc_state(state,
> > > > > > > > &fbc-
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > crtc-
> > > > > > > > > 
> > > > > > > > > base))
> > > > > > > > +	    !drm_atomic_get_existing_plane_state(state
> > > > > > > > ,
> > > > > > > > +						 fbc-
> > > > > > > > > 
> > > > > > > > > crtc-
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > base.primary))
> > > > > > > >  		goto out;
> > > > > > > >  
> > > > > > > >  	if (!intel_fbc_can_enable(dev_priv))
> > > > > > > > @@ -1074,25 +1075,26 @@ void
> > > > > > > > intel_fbc_choose_crtc(struct
> > > > > > > > drm_i915_private *dev_priv,
> > > > > > > >  	 * plane. We could go for fancier schemes such
> > > > > > > > as
> > > > > > > > checking
> > > > > > > > the plane
> > > > > > > >  	 * size, but this would just affect the few
> > > > > > > > platforms
> > > > > > > > that
> > > > > > > > don't tie FBC
> > > > > > > >  	 * to pipe or plane A. */
> > > > > > > > -	for_each_plane_in_state(state, plane,
> > > > > > > > plane_state,
> > > > > > > > i)
> > > > > > > > {
> > > > > > > > -		struct intel_plane_state
> > > > > > > > *intel_plane_state =
> > > > > > > > -			to_intel_plane_state(plane_sta
> > > > > > > > te);
> > > > > > > > -		struct intel_crtc_state
> > > > > > > > *intel_crtc_state;
> > > > > > > > -		struct intel_crtc *crtc =
> > > > > > > > to_intel_crtc(plane_state->crtc);
> > > > > > > > +	for_each_crtc_in_state(state, crtc,
> > > > > > > > crtc_state, i)
> > > > > > > > {
> > > > > > > > +		struct intel_plane_state *plane_state
> > > > > > > > =
> > > > > > > > to_intel_plane_state(
> > > > > > > > +			drm_atomic_get_existing_plane_
> > > > > > > > stat
> > > > > > > > e(st
> > > > > > > > ate,
> > > > > > > > +							
> > > > > > > >   
> > > > > > > >   cr
> > > > > > > > tc-
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > primary));
> > > > > > > > +		struct intel_crtc *intel_crtc =
> > > > > > > > to_intel_crtc(crtc);
> > > > > > > >  
> > > > > > > > -		if (!intel_plane_state->base.visible)
> > > > > > > > +		if (!plane_state)
> > > > > > > >  			continue;
> > > > > > > >  
> > > > > > > > -		if (fbc_on_pipe_a_only(dev_priv) &&
> > > > > > > > crtc-
> > > > > > > > > 
> > > > > > > > > pipe 
> > > > > > > > !=
> > > > > > > > PIPE_A)
> > > > > > > > +		if (!plane_state->base.visible)
> > > > > > > >  			continue;
> > > > > > > >  
> > > > > > > > -		if (fbc_on_plane_a_only(dev_priv) &&
> > > > > > > > crtc-
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > plane
> > > > > > > > != PLANE_A)
> > > > > > > > +		if (fbc_on_pipe_a_only(dev_priv) &&
> > > > > > > > intel_crtc-
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > pipe != PIPE_A)
> > > > > > > >  			continue;
> > > > > > > >  
> > > > > > > > -		intel_crtc_state =
> > > > > > > > to_intel_crtc_state(
> > > > > > > > -			drm_atomic_get_existing_crtc_s
> > > > > > > > tate
> > > > > > > > (sta
> > > > > > > > te,
> > > > > > > > &crtc->base));
> > > > > > > > +		if (fbc_on_plane_a_only(dev_priv) &&
> > > > > > > > +		    intel_crtc->plane != PLANE_A)
> > > > > > > > +			continue;
> > > > > > > >  
> > > > > > > > -		intel_crtc_state->enable_fbc = true;
> > > > > > > > +		plane_state->enable_fbc = true;
> > > > > > > 
> > > > > > > So looking at this whole thing, I can't see anything that
> > > > > > > would
> > > > > > > prevent
> > > > > > > enable_fbc being true for multiple primary planes at the
> > > > > > > same
> > > > > > > time
> > > > > > > Well, apart from the whole "we enable it only for
> > > > > > > platforms
> > > > > > > that
> > > > > > > can
> > > > > > > only do
> > > > > > > pipe A" thing.
> > > > > > > 
> > > > > > > So what happens in that case? FBC just ends up getting
> > > > > > > enabling
> > > > > > > on
> > > > > > > one of the pipes based on the order intel_fbc_enable()
> > > > > > > gets
> > > > > > > called,
> > > > > > > or something?
> > > > > > 
> > > > > > The first check of intel_fbc_choose_crtc() is supposed to
> > > > > > prevent
> > > > > > this
> > > > > > case: if fbc->crtc->primary is not included in the commit
> > > > > > we
> > > > > > just
> > > > > > return without selecting any plane.
> > > > > 
> > > > > The fbc->crtc thing only works if intel_fbc_enable() was
> > > > > already
> > > > > called
> > > > > for some crtc. But what it it wasn't?
> > > > > 
> > > > > > 
> > > > > > 
> > > > > > 
> > > > > > Otherwise, we only pick one CRTC
> > > > > > due to the "break;" statement after setting plane_state-
> > > > > > > 
> > > > > > > enable_fbc 
> > > > > > to
> > > > > > true.
> > > > > 
> > > > > Only one per atomic operation. But what if there are several
> > > > > happening
> > > > > in parallel on different crtcs?
> > > > 
> > > > I see your point now. Yeah, we'll end up with
> > > > plane_state.enable_fbc=true for two different planes. Later,
> > > > the
> > > > first
> > > > one to call intel_fbc_enable() will win, and the others will be
> > > > ignored, so we'll indeed end up with plane states having
> > > > enable_fbc=true but FBC not enabled by them. Not a real bug, I
> > > > would
> > > > still like to avoid this confusion.
> > > > 
> > > > The simplest solution I can think would be to just
> > > > s/plane_state.enable_fbc/plane_state.can_enable_fbc/ and just
> > > > let
> > > > the
> > > > first one to call intel_fbc_enable() win... And then, if we
> > > > ever
> > > > decide
> > > > to enable FBC on the older platforms, we can choose to maybe
> > > > implement
> > > > a better method
> > > 
> > > Maybe something like "fbc_score"? ;)
> > 
> > The design of the current function was supposed to allow Ville to
> > implement his fbc_score in case he wanted. But this certainly
> > didn't
> > take into account multiple parallel commits: it would only work if
> > multiple CRTCs were included in the same commit (as you just
> > pointed
> > today).
> > 
> > But then: if we're having separate parallel commits, when would we
> > be
> > able to loop through the scores to only actually enable FBC on the
> > best
> > score? 
> > 
> > For example, if we do two parallel atomic_check()s and end with
> > plane_a_score=1 and plane_b_score=2, then later we do A's commit()
> > and
> > call intel_fbc_enable() for it, how do we conclude that we
> > shouldn't
> > enable FBC for plane A? We're not even sure if plane B is going to
> > actually commit the plane state it calculated (maybe it was
> > check_only).
> > 
> > And then, if we decide to only compute everything during commit()
> > instead of check(), we'll just also end up enabling FBC for plane A
> > since A's commit() will run first and we'll have no idea that B's
> > commit is incoming.
> > 
> > The only option would be to disable FBC for plane A and enable for
> > plane B during B's commit. But I'm not looking forward to implement
> > this right now.
> 
> All the enable/disable should be totally async and so you should be
> able to kick them off from anywhere. All the state, including the
> scores,
> would be protected by the fbc mutex. I had a vblank worker type of
> thing for this purpose in my patches.

As far as I remember, the complicated part here was related to the CFB
allocation/deallocation. If we disable FBC while the pipe is running we
need to wait for a vblank before we deallocate the CFB, otherwise the
HW is going to keep writing to the stolen memory. While we certainly
can adjust to this, in my FBC reworks I simplified this a lot, and now
we only enable/disable the CFB during crtc_{en,dis}able, so we only
ever free the CFB while the pipe is off, and we don't
deallocate+reallocate it at every single page flip.

The other complicated part is that if we try to allocate the new CFB
for the new plane without first freeing the old one (waiting for the
vblank on the other CRTC) we'll probably get an -ENOMEM from the stolen
mm. On the other hand, if we actually do all the vblank the waits,
well, we'll have to implement the code to properly wait for everything.
And while we're doing the waits, the world may change, new modesets may
happen, we may have to abort or change our minds, and handling all
these cases is there things get complicating.

And we have intel_fbc_work_fn() to help.

Anyway, it is possible to do what you're suggesting. It's just that a
lot of simple things will become complicated.

>
Ville Syrjala Nov. 15, 2016, 9:43 a.m. UTC | #9
On Mon, Nov 14, 2016 at 06:49:22PM -0200, Paulo Zanoni wrote:
> Em Seg, 2016-11-14 às 22:26 +0200, Ville Syrjälä escreveu:
> > On Fri, Nov 11, 2016 at 06:49:59PM -0200, Paulo Zanoni wrote:
> > > 
> > > Em Sex, 2016-11-11 às 22:24 +0200, Ville Syrjälä escreveu:
> > > > 
> > > > On Fri, Nov 11, 2016 at 05:57:28PM -0200, Paulo Zanoni wrote:
> > > > > 
> > > > > 
> > > > > Em Sex, 2016-11-11 às 21:13 +0200, Ville Syrjälä escreveu:
> > > > > > 
> > > > > > 
> > > > > > On Fri, Nov 11, 2016 at 05:01:54PM -0200, Paulo Zanoni wrote:
> > > > > > > 
> > > > > > > 
> > > > > > > 
> > > > > > > Em Sex, 2016-11-11 às 20:51 +0200, Ville Syrjälä escreveu:
> > > > > > > > 
> > > > > > > > 
> > > > > > > > 
> > > > > > > > On Fri, Nov 11, 2016 at 02:57:40PM -0200, Paulo Zanoni
> > > > > > > > wrote:
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > 
> > > > > > > > > Ville pointed out that intel_fbc_choose_crtc() is
> > > > > > > > > iterating
> > > > > > > > > over
> > > > > > > > > all
> > > > > > > > > planes instead of just the primary planes. There are no
> > > > > > > > > real
> > > > > > > > > consequences of this problem for HSW+, and for the
> > > > > > > > > other
> > > > > > > > > platforms
> > > > > > > > > it
> > > > > > > > > just means that in some obscure multi-screen cases
> > > > > > > > > we'll
> > > > > > > > > keep
> > > > > > > > > FBC
> > > > > > > > > disabled when we could have enabled it. Still,
> > > > > > > > > iterating
> > > > > > > > > over
> > > > > > > > > all
> > > > > > > > > planes doesn't seem to be the best thing to do.
> > > > > > > > > 
> > > > > > > > > My initial idea was to just add a check for plane->type 
> > > > > > > > > and
> > > > > > > > > be
> > > > > > > > > done,
> > > > > > > > > but then I realized that in commits not involving the
> > > > > > > > > primary
> > > > > > > > > plane
> > > > > > > > > we
> > > > > > > > > would reset crtc_state->enable_fbc back to false even
> > > > > > > > > when
> > > > > > > > > FBC
> > > > > > > > > is
> > > > > > > > > enabled. That also wouldn't result in a bug due to the
> > > > > > > > > way
> > > > > > > > > the
> > > > > > > > > enable_fbc variable is checked, but, still, our code
> > > > > > > > > can be
> > > > > > > > > better
> > > > > > > > > than this.
> > > > > > > > > 
> > > > > > > > > So I went for the solution that involves tracking
> > > > > > > > > enable_fbc in
> > > > > > > > > the
> > > > > > > > > primary plane state instead of the CRTC state. This
> > > > > > > > > way, if
> > > > > > > > > a
> > > > > > > > > commit
> > > > > > > > > doesn't involve the primary plane for the CRTC we won't
> > > > > > > > > be
> > > > > > > > > resetting
> > > > > > > > > enable_fbc back to false, so the variable will always
> > > > > > > > > reflect
> > > > > > > > > the
> > > > > > > > > reality. And this change also makes more sense since
> > > > > > > > > FBC is
> > > > > > > > > actually
> > > > > > > > > tied to the single plane and not the full pipe. As a
> > > > > > > > > bonus,
> > > > > > > > > we
> > > > > > > > > only
> > > > > > > > > iterate over the CRTCs instead of iterating over all
> > > > > > > > > planes.
> > > > > > > > > 
> > > > > > > > > Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > > > > > Reported-by: Ville Syrjälä <ville.syrjala@linux.intel.c
> > > > > > > > > om>
> > > > > > > > > Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
> > > > > > > > > ---
> > > > > > > > >  drivers/gpu/drm/i915/intel_drv.h |  4 ++--
> > > > > > > > >  drivers/gpu/drm/i915/intel_fbc.c | 36
> > > > > > > > > +++++++++++++++++++-
> > > > > > > > > ----
> > > > > > > > > ----
> > > > > > > > > --------
> > > > > > > > >  2 files changed, 21 insertions(+), 19 deletions(-)
> > > > > > > > > 
> > > > > > > > > diff --git a/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > > > b/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > > > index 003afb8..025cb74 100644
> > > > > > > > > --- a/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > > > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > > > > > > > > @@ -403,6 +403,8 @@ struct intel_plane_state {
> > > > > > > > >  	int scaler_id;
> > > > > > > > >  
> > > > > > > > >  	struct drm_intel_sprite_colorkey ckey;
> > > > > > > > > +
> > > > > > > > > +	bool enable_fbc;
> > > > > > > > >  };
> > > > > > > > >  
> > > > > > > > >  struct intel_initial_plane_config {
> > > > > > > > > @@ -648,8 +650,6 @@ struct intel_crtc_state {
> > > > > > > > >  
> > > > > > > > >  	bool ips_enabled;
> > > > > > > > >  
> > > > > > > > > -	bool enable_fbc;
> > > > > > > > > -
> > > > > > > > >  	bool double_wide;
> > > > > > > > >  
> > > > > > > > >  	bool dp_encoder_is_mst;
> > > > > > > > > diff --git a/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > > > b/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > > > index b095175..fc4ac57 100644
> > > > > > > > > --- a/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > > > +++ b/drivers/gpu/drm/i915/intel_fbc.c
> > > > > > > > > @@ -1055,16 +1055,17 @@ void
> > > > > > > > > intel_fbc_choose_crtc(struct
> > > > > > > > > drm_i915_private *dev_priv,
> > > > > > > > >  			   struct drm_atomic_state
> > > > > > > > > *state)
> > > > > > > > >  {
> > > > > > > > >  	struct intel_fbc *fbc = &dev_priv->fbc;
> > > > > > > > > -	struct drm_plane *plane;
> > > > > > > > > -	struct drm_plane_state *plane_state;
> > > > > > > > > +	struct drm_crtc *crtc;
> > > > > > > > > +	struct drm_crtc_state *crtc_state;
> > > > > > > > >  	bool crtc_chosen = false;
> > > > > > > > >  	int i;
> > > > > > > > >  
> > > > > > > > >  	mutex_lock(&fbc->lock);
> > > > > > > > >  
> > > > > > > > > -	/* Does this atomic commit involve the CRTC
> > > > > > > > > currently
> > > > > > > > > tied
> > > > > > > > > to FBC? */
> > > > > > > > > +	/* Does this atomic commit involve the plane
> > > > > > > > > currently
> > > > > > > > > tied to FBC? */
> > > > > > > > >  	if (fbc->crtc &&
> > > > > > > > > -	    !drm_atomic_get_existing_crtc_state(state,
> > > > > > > > > &fbc-
> > > > > > > > > > 
> > > > > > > > > > 
> > > > > > > > > > crtc-
> > > > > > > > > > 
> > > > > > > > > > base))
> > > > > > > > > +	    !drm_atomic_get_existing_plane_state(state
> > > > > > > > > ,
> > > > > > > > > +						 fbc-
> > > > > > > > > > 
> > > > > > > > > > crtc-
> > > > > > > > > > 
> > > > > > > > > > 
> > > > > > > > > > base.primary))
> > > > > > > > >  		goto out;
> > > > > > > > >  
> > > > > > > > >  	if (!intel_fbc_can_enable(dev_priv))
> > > > > > > > > @@ -1074,25 +1075,26 @@ void
> > > > > > > > > intel_fbc_choose_crtc(struct
> > > > > > > > > drm_i915_private *dev_priv,
> > > > > > > > >  	 * plane. We could go for fancier schemes such
> > > > > > > > > as
> > > > > > > > > checking
> > > > > > > > > the plane
> > > > > > > > >  	 * size, but this would just affect the few
> > > > > > > > > platforms
> > > > > > > > > that
> > > > > > > > > don't tie FBC
> > > > > > > > >  	 * to pipe or plane A. */
> > > > > > > > > -	for_each_plane_in_state(state, plane,
> > > > > > > > > plane_state,
> > > > > > > > > i)
> > > > > > > > > {
> > > > > > > > > -		struct intel_plane_state
> > > > > > > > > *intel_plane_state =
> > > > > > > > > -			to_intel_plane_state(plane_sta
> > > > > > > > > te);
> > > > > > > > > -		struct intel_crtc_state
> > > > > > > > > *intel_crtc_state;
> > > > > > > > > -		struct intel_crtc *crtc =
> > > > > > > > > to_intel_crtc(plane_state->crtc);
> > > > > > > > > +	for_each_crtc_in_state(state, crtc,
> > > > > > > > > crtc_state, i)
> > > > > > > > > {
> > > > > > > > > +		struct intel_plane_state *plane_state
> > > > > > > > > =
> > > > > > > > > to_intel_plane_state(
> > > > > > > > > +			drm_atomic_get_existing_plane_
> > > > > > > > > stat
> > > > > > > > > e(st
> > > > > > > > > ate,
> > > > > > > > > +							
> > > > > > > > >   
> > > > > > > > >   cr
> > > > > > > > > tc-
> > > > > > > > > > 
> > > > > > > > > > 
> > > > > > > > > > 
> > > > > > > > > > primary));
> > > > > > > > > +		struct intel_crtc *intel_crtc =
> > > > > > > > > to_intel_crtc(crtc);
> > > > > > > > >  
> > > > > > > > > -		if (!intel_plane_state->base.visible)
> > > > > > > > > +		if (!plane_state)
> > > > > > > > >  			continue;
> > > > > > > > >  
> > > > > > > > > -		if (fbc_on_pipe_a_only(dev_priv) &&
> > > > > > > > > crtc-
> > > > > > > > > > 
> > > > > > > > > > pipe 
> > > > > > > > > !=
> > > > > > > > > PIPE_A)
> > > > > > > > > +		if (!plane_state->base.visible)
> > > > > > > > >  			continue;
> > > > > > > > >  
> > > > > > > > > -		if (fbc_on_plane_a_only(dev_priv) &&
> > > > > > > > > crtc-
> > > > > > > > > > 
> > > > > > > > > > 
> > > > > > > > > > plane
> > > > > > > > > != PLANE_A)
> > > > > > > > > +		if (fbc_on_pipe_a_only(dev_priv) &&
> > > > > > > > > intel_crtc-
> > > > > > > > > > 
> > > > > > > > > > 
> > > > > > > > > > 
> > > > > > > > > > pipe != PIPE_A)
> > > > > > > > >  			continue;
> > > > > > > > >  
> > > > > > > > > -		intel_crtc_state =
> > > > > > > > > to_intel_crtc_state(
> > > > > > > > > -			drm_atomic_get_existing_crtc_s
> > > > > > > > > tate
> > > > > > > > > (sta
> > > > > > > > > te,
> > > > > > > > > &crtc->base));
> > > > > > > > > +		if (fbc_on_plane_a_only(dev_priv) &&
> > > > > > > > > +		    intel_crtc->plane != PLANE_A)
> > > > > > > > > +			continue;
> > > > > > > > >  
> > > > > > > > > -		intel_crtc_state->enable_fbc = true;
> > > > > > > > > +		plane_state->enable_fbc = true;
> > > > > > > > 
> > > > > > > > So looking at this whole thing, I can't see anything that
> > > > > > > > would
> > > > > > > > prevent
> > > > > > > > enable_fbc being true for multiple primary planes at the
> > > > > > > > same
> > > > > > > > time
> > > > > > > > Well, apart from the whole "we enable it only for
> > > > > > > > platforms
> > > > > > > > that
> > > > > > > > can
> > > > > > > > only do
> > > > > > > > pipe A" thing.
> > > > > > > > 
> > > > > > > > So what happens in that case? FBC just ends up getting
> > > > > > > > enabling
> > > > > > > > on
> > > > > > > > one of the pipes based on the order intel_fbc_enable()
> > > > > > > > gets
> > > > > > > > called,
> > > > > > > > or something?
> > > > > > > 
> > > > > > > The first check of intel_fbc_choose_crtc() is supposed to
> > > > > > > prevent
> > > > > > > this
> > > > > > > case: if fbc->crtc->primary is not included in the commit
> > > > > > > we
> > > > > > > just
> > > > > > > return without selecting any plane.
> > > > > > 
> > > > > > The fbc->crtc thing only works if intel_fbc_enable() was
> > > > > > already
> > > > > > called
> > > > > > for some crtc. But what it it wasn't?
> > > > > > 
> > > > > > > 
> > > > > > > 
> > > > > > > 
> > > > > > > Otherwise, we only pick one CRTC
> > > > > > > due to the "break;" statement after setting plane_state-
> > > > > > > > 
> > > > > > > > enable_fbc 
> > > > > > > to
> > > > > > > true.
> > > > > > 
> > > > > > Only one per atomic operation. But what if there are several
> > > > > > happening
> > > > > > in parallel on different crtcs?
> > > > > 
> > > > > I see your point now. Yeah, we'll end up with
> > > > > plane_state.enable_fbc=true for two different planes. Later,
> > > > > the
> > > > > first
> > > > > one to call intel_fbc_enable() will win, and the others will be
> > > > > ignored, so we'll indeed end up with plane states having
> > > > > enable_fbc=true but FBC not enabled by them. Not a real bug, I
> > > > > would
> > > > > still like to avoid this confusion.
> > > > > 
> > > > > The simplest solution I can think would be to just
> > > > > s/plane_state.enable_fbc/plane_state.can_enable_fbc/ and just
> > > > > let
> > > > > the
> > > > > first one to call intel_fbc_enable() win... And then, if we
> > > > > ever
> > > > > decide
> > > > > to enable FBC on the older platforms, we can choose to maybe
> > > > > implement
> > > > > a better method
> > > > 
> > > > Maybe something like "fbc_score"? ;)
> > > 
> > > The design of the current function was supposed to allow Ville to
> > > implement his fbc_score in case he wanted. But this certainly
> > > didn't
> > > take into account multiple parallel commits: it would only work if
> > > multiple CRTCs were included in the same commit (as you just
> > > pointed
> > > today).
> > > 
> > > But then: if we're having separate parallel commits, when would we
> > > be
> > > able to loop through the scores to only actually enable FBC on the
> > > best
> > > score? 
> > > 
> > > For example, if we do two parallel atomic_check()s and end with
> > > plane_a_score=1 and plane_b_score=2, then later we do A's commit()
> > > and
> > > call intel_fbc_enable() for it, how do we conclude that we
> > > shouldn't
> > > enable FBC for plane A? We're not even sure if plane B is going to
> > > actually commit the plane state it calculated (maybe it was
> > > check_only).
> > > 
> > > And then, if we decide to only compute everything during commit()
> > > instead of check(), we'll just also end up enabling FBC for plane A
> > > since A's commit() will run first and we'll have no idea that B's
> > > commit is incoming.
> > > 
> > > The only option would be to disable FBC for plane A and enable for
> > > plane B during B's commit. But I'm not looking forward to implement
> > > this right now.
> > 
> > All the enable/disable should be totally async and so you should be
> > able to kick them off from anywhere. All the state, including the
> > scores,
> > would be protected by the fbc mutex. I had a vblank worker type of
> > thing for this purpose in my patches.
> 
> As far as I remember, the complicated part here was related to the CFB
> allocation/deallocation. If we disable FBC while the pipe is running we
> need to wait for a vblank before we deallocate the CFB, otherwise the
> HW is going to keep writing to the stolen memory. While we certainly
> can adjust to this, in my FBC reworks I simplified this a lot, and now
> we only enable/disable the CFB during crtc_{en,dis}able, so we only
> ever free the CFB while the pipe is off, and we don't
> deallocate+reallocate it at every single page flip.

Well you already have to disable FBC in the hardware when you flip to
a buffer that can't be used with FBC. So that part is there. I admit
there is a little bit of extra complication from making sure you don't
deallocate the cfb before FBC has finished, but that complication is
neatly encapsulated in the FBC code. From the flip/modeset path there
should be just two function calls around the update to signal the FBC
machinery.

> The other complicated part is that if we try to allocate the new CFB
> for the new plane without first freeing the old one (waiting for the
> vblank on the other CRTC) we'll probably get an -ENOMEM from the stolen
> mm. On the other hand, if we actually do all the vblank the waits,
> well, we'll have to implement the code to properly wait for everything.
> And while we're doing the waits, the world may change, new modesets may
> happen, we may have to abort or change our minds, and handling all
> these cases is there things get complicating.
> 
> And we have intel_fbc_work_fn() to help.
> 
> Anyway, it is possible to do what you're suggesting. It's just that a
> lot of simple things will become complicated.

As I see it the FBC machinery effectively just needs three states:
- cfb deallocated + fbc disabled
- cfb allocated + fbc disabled
- cfb allocated + fbc enabled

and you just move between these states, with some synchronization in
between. I guess you may want to consider enabling/disabling as two
more states which makes the synchronization more clear.
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 003afb8..025cb74 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -403,6 +403,8 @@  struct intel_plane_state {
 	int scaler_id;
 
 	struct drm_intel_sprite_colorkey ckey;
+
+	bool enable_fbc;
 };
 
 struct intel_initial_plane_config {
@@ -648,8 +650,6 @@  struct intel_crtc_state {
 
 	bool ips_enabled;
 
-	bool enable_fbc;
-
 	bool double_wide;
 
 	bool dp_encoder_is_mst;
diff --git a/drivers/gpu/drm/i915/intel_fbc.c b/drivers/gpu/drm/i915/intel_fbc.c
index b095175..fc4ac57 100644
--- a/drivers/gpu/drm/i915/intel_fbc.c
+++ b/drivers/gpu/drm/i915/intel_fbc.c
@@ -1055,16 +1055,17 @@  void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv,
 			   struct drm_atomic_state *state)
 {
 	struct intel_fbc *fbc = &dev_priv->fbc;
-	struct drm_plane *plane;
-	struct drm_plane_state *plane_state;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *crtc_state;
 	bool crtc_chosen = false;
 	int i;
 
 	mutex_lock(&fbc->lock);
 
-	/* Does this atomic commit involve the CRTC currently tied to FBC? */
+	/* Does this atomic commit involve the plane currently tied to FBC? */
 	if (fbc->crtc &&
-	    !drm_atomic_get_existing_crtc_state(state, &fbc->crtc->base))
+	    !drm_atomic_get_existing_plane_state(state,
+						 fbc->crtc->base.primary))
 		goto out;
 
 	if (!intel_fbc_can_enable(dev_priv))
@@ -1074,25 +1075,26 @@  void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv,
 	 * plane. We could go for fancier schemes such as checking the plane
 	 * size, but this would just affect the few platforms that don't tie FBC
 	 * to pipe or plane A. */
-	for_each_plane_in_state(state, plane, plane_state, i) {
-		struct intel_plane_state *intel_plane_state =
-			to_intel_plane_state(plane_state);
-		struct intel_crtc_state *intel_crtc_state;
-		struct intel_crtc *crtc = to_intel_crtc(plane_state->crtc);
+	for_each_crtc_in_state(state, crtc, crtc_state, i) {
+		struct intel_plane_state *plane_state = to_intel_plane_state(
+			drm_atomic_get_existing_plane_state(state,
+							    crtc->primary));
+		struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
-		if (!intel_plane_state->base.visible)
+		if (!plane_state)
 			continue;
 
-		if (fbc_on_pipe_a_only(dev_priv) && crtc->pipe != PIPE_A)
+		if (!plane_state->base.visible)
 			continue;
 
-		if (fbc_on_plane_a_only(dev_priv) && crtc->plane != PLANE_A)
+		if (fbc_on_pipe_a_only(dev_priv) && intel_crtc->pipe != PIPE_A)
 			continue;
 
-		intel_crtc_state = to_intel_crtc_state(
-			drm_atomic_get_existing_crtc_state(state, &crtc->base));
+		if (fbc_on_plane_a_only(dev_priv) &&
+		    intel_crtc->plane != PLANE_A)
+			continue;
 
-		intel_crtc_state->enable_fbc = true;
+		plane_state->enable_fbc = true;
 		crtc_chosen = true;
 		break;
 	}
@@ -1130,13 +1132,13 @@  void intel_fbc_enable(struct intel_crtc *crtc,
 	if (fbc->enabled) {
 		WARN_ON(fbc->crtc == NULL);
 		if (fbc->crtc == crtc) {
-			WARN_ON(!crtc_state->enable_fbc);
+			WARN_ON(!plane_state->enable_fbc);
 			WARN_ON(fbc->active);
 		}
 		goto out;
 	}
 
-	if (!crtc_state->enable_fbc)
+	if (!plane_state->enable_fbc)
 		goto out;
 
 	WARN_ON(fbc->active);