diff mbox series

[15/23] drm/i915: Link planes in a bigjoiner configuration.

Message ID 20190920114235.22411-15-maarten.lankhorst@linux.intel.com (mailing list archive)
State New, archived
Headers show
Series [01/23] drm/i915/dp: Fix dsc bpp calculations, v2. | expand

Commit Message

Maarten Lankhorst Sept. 20, 2019, 11:42 a.m. UTC
Make sure that when a plane is set in a bigjoiner mode, we will add
their counterpart to the atomic state as well. This will allow us to
make sure all state is available when planes are checked.

Because of the funny interactions with bigjoiner and planar YUV
formats, we may end up adding a lot of planes, so we have to keep
iterating until we no longer add any planes.

Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
---
 .../gpu/drm/i915/display/intel_atomic_plane.c |  31 +++-
 .../gpu/drm/i915/display/intel_atomic_plane.h |   4 +
 drivers/gpu/drm/i915/display/intel_display.c  | 142 ++++++++++++++++--
 .../drm/i915/display/intel_display_types.h    |  11 ++
 4 files changed, 172 insertions(+), 16 deletions(-)

Comments

Matt Roper Oct. 1, 2019, 4:44 p.m. UTC | #1
On Fri, Sep 20, 2019 at 01:42:27PM +0200, Maarten Lankhorst wrote:
> Make sure that when a plane is set in a bigjoiner mode, we will add
> their counterpart to the atomic state as well. This will allow us to
> make sure all state is available when planes are checked.
> 
> Because of the funny interactions with bigjoiner and planar YUV
> formats, we may end up adding a lot of planes, so we have to keep
> iterating until we no longer add any planes.
> 
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> ---
>  .../gpu/drm/i915/display/intel_atomic_plane.c |  31 +++-
>  .../gpu/drm/i915/display/intel_atomic_plane.h |   4 +
>  drivers/gpu/drm/i915/display/intel_display.c  | 142 ++++++++++++++++--
>  .../drm/i915/display/intel_display_types.h    |  11 ++
>  4 files changed, 172 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.c b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
> index 964db7774d10..cc088676f0a2 100644
> --- a/drivers/gpu/drm/i915/display/intel_atomic_plane.c
> +++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
> @@ -182,16 +182,36 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
>  					       old_plane_state, new_plane_state);
>  }
>  
> -static struct intel_crtc *
> -get_crtc_from_states(const struct intel_plane_state *old_plane_state,
> -		     const struct intel_plane_state *new_plane_state)
> -{
> +struct intel_crtc *
> +intel_plane_get_crtc_from_states(struct intel_atomic_state *state,

This function name seems ambiguous now since it isn't clear whether
we're getting the plane's hardware CRTC or its uapi CRTC.  Maybe call it
something like intel_plane_state_get_uapi_crtc()?



> +				 const struct intel_plane_state *old_plane_state,
> +				 const struct intel_plane_state *new_plane_state)
> +  {
> +	struct drm_i915_private *dev_priv = to_i915(state->base.dev);
> +	struct intel_plane *plane = to_intel_plane(new_plane_state->base.plane);
> +
>  	if (new_plane_state->base.crtc)
>  		return to_intel_crtc(new_plane_state->base.crtc);
>  
>  	if (old_plane_state->base.crtc)
>  		return to_intel_crtc(old_plane_state->base.crtc);
>  
> +	if (new_plane_state->bigjoiner_slave) {
> +		const struct intel_plane_state *new_master_plane_state =
> +			intel_atomic_get_new_plane_state(state, new_plane_state->bigjoiner_plane);
> +
> +		if (new_master_plane_state->base.crtc)
> +			return intel_get_crtc_for_pipe(dev_priv, plane->pipe);
> +	}

Will this cause problems if we adjust properties of planes on a a
uapi-disabled CRTC?  E.g., I believe userspace can fiddle with stuff
like rotation properties on disabled planes/crtcs and now I believe that
causes us to needlessly pull in the bigjoiner master crtc and
corresponding plane?

> +
> +	if (old_plane_state->bigjoiner_slave) {
> +		const struct intel_plane_state *old_master_plane_state =
> +			intel_atomic_get_old_plane_state(state, old_plane_state->bigjoiner_plane);
> +
> +		if (old_master_plane_state->base.crtc)
> +			return intel_get_crtc_for_pipe(dev_priv, plane->pipe);
> +	}
> +
>  	return NULL;
>  }
>  
> @@ -206,7 +226,8 @@ static int intel_plane_atomic_check(struct drm_plane *_plane,
>  	const struct intel_plane_state *old_plane_state =
>  		intel_atomic_get_old_plane_state(state, plane);
>  	struct intel_crtc *crtc =
> -		get_crtc_from_states(old_plane_state, new_plane_state);
> +		intel_plane_get_crtc_from_states(state, old_plane_state,
> +						 new_plane_state);
>  	const struct intel_crtc_state *old_crtc_state;
>  	struct intel_crtc_state *new_crtc_state;
>  
> diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.h b/drivers/gpu/drm/i915/display/intel_atomic_plane.h
> index 33fb85cd3909..901a50e6e2d3 100644
> --- a/drivers/gpu/drm/i915/display/intel_atomic_plane.h
> +++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.h
> @@ -42,5 +42,9 @@ int intel_plane_atomic_calc_changes(const struct intel_crtc_state *old_crtc_stat
>  				    struct intel_crtc_state *crtc_state,
>  				    const struct intel_plane_state *old_plane_state,
>  				    struct intel_plane_state *plane_state);
> +struct intel_crtc *
> +intel_plane_get_crtc_from_states(struct intel_atomic_state *state,
> +				 const struct intel_plane_state *old_plane_state,
> +				 const struct intel_plane_state *new_plane_state);
>  
>  #endif /* __INTEL_ATOMIC_PLANE_H__ */
> diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
> index df588bf47559..06ceac4f1436 100644
> --- a/drivers/gpu/drm/i915/display/intel_display.c
> +++ b/drivers/gpu/drm/i915/display/intel_display.c
> @@ -11811,24 +11811,101 @@ static bool check_single_encoder_cloning(struct drm_atomic_state *state,
>  	return true;
>  }
>  
> +static int icl_add_dependent_planes(struct intel_atomic_state *state,
> +				    struct intel_plane_state *plane_state)
> +{
> +	struct intel_plane_state *new_plane_state;
> +	struct intel_plane *plane;
> +	int ret = 0;
> +
> +	plane = plane_state->bigjoiner_plane;
> +	if (plane && !intel_atomic_get_new_plane_state(state, plane)) {
> +		new_plane_state = intel_atomic_get_plane_state(state, plane);
> +		if (IS_ERR(new_plane_state))
> +			return PTR_ERR(new_plane_state);
> +
> +		ret = 1;
> +	}
> +
> +	plane = plane_state->planar_linked_plane;
> +	if (plane && !intel_atomic_get_new_plane_state(state, plane)) {
> +		new_plane_state = intel_atomic_get_plane_state(state, plane);
> +		if (IS_ERR(new_plane_state))
> +			return PTR_ERR(new_plane_state);
> +
> +		ret = 1;
> +	}
> +
> +	return ret;
> +}
> +
>  static int icl_add_linked_planes(struct intel_atomic_state *state)
>  {
> -	struct intel_plane *plane, *linked;
> -	struct intel_plane_state *plane_state, *linked_plane_state;
> +	struct intel_plane *plane;
> +	struct intel_plane_state *old_plane_state, *new_plane_state;
> +	struct intel_crtc *crtc, *linked_crtc;
> +	struct intel_crtc_state *old_crtc_state, *new_crtc_state, *linked_crtc_state;
> +	bool added;
>  	int i;
>  
> -	for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
> -		linked = plane_state->planar_linked_plane;
> +	/*
> +	 * Iteratively add plane_state->linked_plane and plane_state->bigjoiner_plane
> +	 *
> +	 * This needs to be done repeatedly, because of is a funny interaction;
> +	 * the Y-plane may be assigned differently on the other bigjoiner crtc,
> +	 * and we could end up with the following evil recursion, when only adding a
> +	 * single plane to state:
> +	 *
> +	 * XRGB8888 master plane 6 adds NV12 slave Y-plane 6, which adds slave UV plane 0,
> +	 * which adds master UV plane 0, which adds master Y-plane 7, which adds XRGB8888
> +	 * slave plane 7.
> +	 *
> +	 * We could pull in even more because of old_plane_state vs new_plane_state.
> +	 *
> +	 * Max depth = 5 (or 7 for evil case) in this case.
> +	 * Number of passes will be less, because newly added planes show up in the
> +	 * same iteration round when added_plane->index > plane->index.
> +	 */
> +	do {
> +		added = false;
>  
> -		if (!linked)
> -			continue;
> +		for_each_oldnew_intel_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
> +			int ret, ret2;
> +
> +			ret = icl_add_dependent_planes(state, old_plane_state);
> +			if (ret < 0)
> +				return ret;
> +
> +			ret2 = icl_add_dependent_planes(state, new_plane_state);
> +			if (ret2 < 0)
> +				return ret2;
>  
> -		linked_plane_state = intel_atomic_get_plane_state(state, linked);
> -		if (IS_ERR(linked_plane_state))
> -			return PTR_ERR(linked_plane_state);
> +			added |= ret || ret2;
> +		}
> +	} while (added);
> +
> +	/*
> +	 * Make sure bigjoiner slave crtc's are also pulled in. This is not done automatically
> +	 * when adding slave planes, because plane_state->crtc is null.
> +	 */
> +	for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
> +		linked_crtc = old_crtc_state->bigjoiner_linked_crtc;
> +		if (linked_crtc) {
> +			linked_crtc_state =
> +				intel_atomic_get_crtc_state(&state->base, linked_crtc);
> +
> +			if (IS_ERR(linked_crtc_state))
> +				return PTR_ERR(linked_crtc_state);
> +		}
> +
> +		linked_crtc = new_crtc_state->bigjoiner_linked_crtc;
> +		if (linked_crtc && linked_crtc != old_crtc_state->bigjoiner_linked_crtc) {
> +			linked_crtc_state =
> +				intel_atomic_get_crtc_state(&state->base, linked_crtc);
>  
> -		WARN_ON(linked_plane_state->planar_linked_plane != plane);
> -		WARN_ON(linked_plane_state->planar_slave == plane_state->planar_slave);
> +			if (IS_ERR(linked_crtc_state))
> +				return PTR_ERR(linked_crtc_state);
> +		}
>  	}
>  
>  	return 0;
> @@ -13799,6 +13876,7 @@ static int intel_atomic_check_bigjoiner(struct intel_atomic_state *state)
>  	struct drm_i915_private *dev_priv = to_i915(state->base.dev);
>  	struct intel_crtc_state *old_crtc_state, *new_crtc_state, *slave_crtc_state, *master_crtc_state;
>  	struct intel_crtc *crtc, *slave, *master;
> +	struct intel_plane *plane;
>  	int i, ret = 0;
>  
>  	if (INTEL_GEN(dev_priv) < 11)
> @@ -13894,6 +13972,48 @@ static int intel_atomic_check_bigjoiner(struct intel_atomic_state *state)
>  			return ret;
>  	}
>  
> +	/*
> +	 * Setup and teardown the new bigjoiner plane mappings.
> +	 */
> +	for_each_intel_plane(&dev_priv->drm, plane) {
> +		struct intel_plane_state *plane_state;
> +		struct intel_plane *other_plane = NULL;
> +
> +		crtc = intel_get_crtc_for_pipe(dev_priv, plane->pipe);
> +		old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc);
> +		new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
> +
> +		if (!new_crtc_state || !needs_modeset(new_crtc_state))
> +			continue;
> +
> +		if (new_crtc_state->bigjoiner) {
> +			struct intel_crtc *other_crtc =
> +				new_crtc_state->bigjoiner_linked_crtc;
> +			bool found = false;
> +
> +			for_each_intel_plane_on_crtc(&dev_priv->drm, other_crtc, other_plane) {
> +				if (other_plane->id != plane->id)
> +					continue;
> +
> +				found = true;
> +				break;
> +			}
> +
> +			/* All pipes should have identical planes. */
> +			if (WARN_ON(!found))
> +				return -EINVAL;
> +		} else if (!old_crtc_state->bigjoiner) {
> +			continue;
> +		}
> +
> +		plane_state = intel_atomic_get_plane_state(state, plane);
> +		if (IS_ERR(plane_state))
> +			return PTR_ERR(plane_state);
> +
> +		plane_state->bigjoiner_plane = other_plane;
> +		plane_state->bigjoiner_slave = new_crtc_state->bigjoiner_slave;
> +	}

Is my understanding correct that we always grab the corresponding plane
on the other CRTC when the big joiner is active?  I.e., even in cases
where the uapi plane is restricted to just one half of the screen, we're
still going to grab the bigjoiner slave plane?  I guess trying to
restrict this to only cases where the uapi plane covers both pipes would
make this whole thing even more complicated.


Matt

> +
>  	return 0;
>  }
>  
> diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
> index ace372a76330..f05f4830a529 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_types.h
> +++ b/drivers/gpu/drm/i915/display/intel_display_types.h
> @@ -571,6 +571,17 @@ struct intel_plane_state {
>  	 */
>  	struct intel_plane *planar_linked_plane;
>  
> +	/*
> +	 * bigjoiner_plane:
> +	 *
> +	 * When 2 pipes are joined in a bigjoiner configuration,
> +	 * points to the same plane on the other pipe.
> +	 *
> +	 * bigjoiner_slave is set on the slave pipe.
> +	 */
> +	struct intel_plane *bigjoiner_plane;
> +	u32 bigjoiner_slave;
> +
>  	/*
>  	 * planar_slave:
>  	 * If set don't update use the linked plane's state for updating
> -- 
> 2.20.1
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Ville Syrjala Oct. 1, 2019, 5:21 p.m. UTC | #2
On Tue, Oct 01, 2019 at 09:44:09AM -0700, Matt Roper wrote:
> On Fri, Sep 20, 2019 at 01:42:27PM +0200, Maarten Lankhorst wrote:
> > Make sure that when a plane is set in a bigjoiner mode, we will add
> > their counterpart to the atomic state as well. This will allow us to
> > make sure all state is available when planes are checked.
> > 
> > Because of the funny interactions with bigjoiner and planar YUV
> > formats, we may end up adding a lot of planes, so we have to keep
> > iterating until we no longer add any planes.
> > 
> > Signed-off-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> > ---
> >  .../gpu/drm/i915/display/intel_atomic_plane.c |  31 +++-
> >  .../gpu/drm/i915/display/intel_atomic_plane.h |   4 +
> >  drivers/gpu/drm/i915/display/intel_display.c  | 142 ++++++++++++++++--
> >  .../drm/i915/display/intel_display_types.h    |  11 ++
> >  4 files changed, 172 insertions(+), 16 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.c b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
> > index 964db7774d10..cc088676f0a2 100644
> > --- a/drivers/gpu/drm/i915/display/intel_atomic_plane.c
> > +++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
> > @@ -182,16 +182,36 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
> >  					       old_plane_state, new_plane_state);
> >  }
> >  
> > -static struct intel_crtc *
> > -get_crtc_from_states(const struct intel_plane_state *old_plane_state,
> > -		     const struct intel_plane_state *new_plane_state)
> > -{
> > +struct intel_crtc *
> > +intel_plane_get_crtc_from_states(struct intel_atomic_state *state,
> 
> This function name seems ambiguous now since it isn't clear whether
> we're getting the plane's hardware CRTC or its uapi CRTC.  Maybe call it
> something like intel_plane_state_get_uapi_crtc()?

I was actually wondering if we shouldn't just do 
get_crtc(plane->pipe) here. We still don't expose any planes
that can switch pipes so feels like this stuff is unecessary
complexity we don't particularly need.

> 
> 
> 
> > +				 const struct intel_plane_state *old_plane_state,
> > +				 const struct intel_plane_state *new_plane_state)
> > +  {
> > +	struct drm_i915_private *dev_priv = to_i915(state->base.dev);
> > +	struct intel_plane *plane = to_intel_plane(new_plane_state->base.plane);
> > +
> >  	if (new_plane_state->base.crtc)
> >  		return to_intel_crtc(new_plane_state->base.crtc);
> >  
> >  	if (old_plane_state->base.crtc)
> >  		return to_intel_crtc(old_plane_state->base.crtc);
> >  
> > +	if (new_plane_state->bigjoiner_slave) {
> > +		const struct intel_plane_state *new_master_plane_state =
> > +			intel_atomic_get_new_plane_state(state, new_plane_state->bigjoiner_plane);
> > +
> > +		if (new_master_plane_state->base.crtc)
> > +			return intel_get_crtc_for_pipe(dev_priv, plane->pipe);
> > +	}
> 
> Will this cause problems if we adjust properties of planes on a a
> uapi-disabled CRTC?  E.g., I believe userspace can fiddle with stuff
> like rotation properties on disabled planes/crtcs and now I believe that
> causes us to needlessly pull in the bigjoiner master crtc and
> corresponding plane?

Not sure it's worth the effort to really try to avoid such things. If
userspace keeps fiddling with disabled stuff all the time it feels a
bit broken to me.

On a semi-related note I've occasionally pondered about trying to
filter out nop commits which don't send out events, even on active
planes/crtcs. But I guess that's already starting to be a change to
the semantics so probably not something that could be done without
a new flag/etc.

> 
> > +
> > +	if (old_plane_state->bigjoiner_slave) {
> > +		const struct intel_plane_state *old_master_plane_state =
> > +			intel_atomic_get_old_plane_state(state, old_plane_state->bigjoiner_plane);
> > +
> > +		if (old_master_plane_state->base.crtc)
> > +			return intel_get_crtc_for_pipe(dev_priv, plane->pipe);
> > +	}
> > +
> >  	return NULL;
> >  }
> >  
> > @@ -206,7 +226,8 @@ static int intel_plane_atomic_check(struct drm_plane *_plane,
> >  	const struct intel_plane_state *old_plane_state =
> >  		intel_atomic_get_old_plane_state(state, plane);
> >  	struct intel_crtc *crtc =
> > -		get_crtc_from_states(old_plane_state, new_plane_state);
> > +		intel_plane_get_crtc_from_states(state, old_plane_state,
> > +						 new_plane_state);
> >  	const struct intel_crtc_state *old_crtc_state;
> >  	struct intel_crtc_state *new_crtc_state;
> >  
> > diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.h b/drivers/gpu/drm/i915/display/intel_atomic_plane.h
> > index 33fb85cd3909..901a50e6e2d3 100644
> > --- a/drivers/gpu/drm/i915/display/intel_atomic_plane.h
> > +++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.h
> > @@ -42,5 +42,9 @@ int intel_plane_atomic_calc_changes(const struct intel_crtc_state *old_crtc_stat
> >  				    struct intel_crtc_state *crtc_state,
> >  				    const struct intel_plane_state *old_plane_state,
> >  				    struct intel_plane_state *plane_state);
> > +struct intel_crtc *
> > +intel_plane_get_crtc_from_states(struct intel_atomic_state *state,
> > +				 const struct intel_plane_state *old_plane_state,
> > +				 const struct intel_plane_state *new_plane_state);
> >  
> >  #endif /* __INTEL_ATOMIC_PLANE_H__ */
> > diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
> > index df588bf47559..06ceac4f1436 100644
> > --- a/drivers/gpu/drm/i915/display/intel_display.c
> > +++ b/drivers/gpu/drm/i915/display/intel_display.c
> > @@ -11811,24 +11811,101 @@ static bool check_single_encoder_cloning(struct drm_atomic_state *state,
> >  	return true;
> >  }
> >  
> > +static int icl_add_dependent_planes(struct intel_atomic_state *state,
> > +				    struct intel_plane_state *plane_state)
> > +{
> > +	struct intel_plane_state *new_plane_state;
> > +	struct intel_plane *plane;
> > +	int ret = 0;
> > +
> > +	plane = plane_state->bigjoiner_plane;
> > +	if (plane && !intel_atomic_get_new_plane_state(state, plane)) {
> > +		new_plane_state = intel_atomic_get_plane_state(state, plane);
> > +		if (IS_ERR(new_plane_state))
> > +			return PTR_ERR(new_plane_state);
> > +
> > +		ret = 1;
> > +	}
> > +
> > +	plane = plane_state->planar_linked_plane;
> > +	if (plane && !intel_atomic_get_new_plane_state(state, plane)) {
> > +		new_plane_state = intel_atomic_get_plane_state(state, plane);
> > +		if (IS_ERR(new_plane_state))
> > +			return PTR_ERR(new_plane_state);
> > +
> > +		ret = 1;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> >  static int icl_add_linked_planes(struct intel_atomic_state *state)
> >  {
> > -	struct intel_plane *plane, *linked;
> > -	struct intel_plane_state *plane_state, *linked_plane_state;
> > +	struct intel_plane *plane;
> > +	struct intel_plane_state *old_plane_state, *new_plane_state;
> > +	struct intel_crtc *crtc, *linked_crtc;
> > +	struct intel_crtc_state *old_crtc_state, *new_crtc_state, *linked_crtc_state;
> > +	bool added;
> >  	int i;
> >  
> > -	for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
> > -		linked = plane_state->planar_linked_plane;
> > +	/*
> > +	 * Iteratively add plane_state->linked_plane and plane_state->bigjoiner_plane
> > +	 *
> > +	 * This needs to be done repeatedly, because of is a funny interaction;
> > +	 * the Y-plane may be assigned differently on the other bigjoiner crtc,
> > +	 * and we could end up with the following evil recursion, when only adding a
> > +	 * single plane to state:
> > +	 *
> > +	 * XRGB8888 master plane 6 adds NV12 slave Y-plane 6, which adds slave UV plane 0,
> > +	 * which adds master UV plane 0, which adds master Y-plane 7, which adds XRGB8888
> > +	 * slave plane 7.
> > +	 *
> > +	 * We could pull in even more because of old_plane_state vs new_plane_state.
> > +	 *
> > +	 * Max depth = 5 (or 7 for evil case) in this case.
> > +	 * Number of passes will be less, because newly added planes show up in the
> > +	 * same iteration round when added_plane->index > plane->index.
> > +	 */
> > +	do {
> > +		added = false;
> >  
> > -		if (!linked)
> > -			continue;
> > +		for_each_oldnew_intel_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
> > +			int ret, ret2;
> > +
> > +			ret = icl_add_dependent_planes(state, old_plane_state);
> > +			if (ret < 0)
> > +				return ret;
> > +
> > +			ret2 = icl_add_dependent_planes(state, new_plane_state);
> > +			if (ret2 < 0)
> > +				return ret2;
> >  
> > -		linked_plane_state = intel_atomic_get_plane_state(state, linked);
> > -		if (IS_ERR(linked_plane_state))
> > -			return PTR_ERR(linked_plane_state);
> > +			added |= ret || ret2;

I suspect passing &added to the functions would be less annoying
than this magic return 1 stuff.

> > +		}
> > +	} while (added);
> > +
> > +	/*
> > +	 * Make sure bigjoiner slave crtc's are also pulled in. This is not done automatically
> > +	 * when adding slave planes, because plane_state->crtc is null.
> > +	 */
> > +	for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
> > +		linked_crtc = old_crtc_state->bigjoiner_linked_crtc;
> > +		if (linked_crtc) {
> > +			linked_crtc_state =
> > +				intel_atomic_get_crtc_state(&state->base, linked_crtc);
> > +
> > +			if (IS_ERR(linked_crtc_state))
> > +				return PTR_ERR(linked_crtc_state);
> > +		}
> > +
> > +		linked_crtc = new_crtc_state->bigjoiner_linked_crtc;
> > +		if (linked_crtc && linked_crtc != old_crtc_state->bigjoiner_linked_crtc) {
> > +			linked_crtc_state =
> > +				intel_atomic_get_crtc_state(&state->base, linked_crtc);
> >  
> > -		WARN_ON(linked_plane_state->planar_linked_plane != plane);
> > -		WARN_ON(linked_plane_state->planar_slave == plane_state->planar_slave);
> > +			if (IS_ERR(linked_crtc_state))
> > +				return PTR_ERR(linked_crtc_state);
> > +		}
> >  	}
> >  
> >  	return 0;
> > @@ -13799,6 +13876,7 @@ static int intel_atomic_check_bigjoiner(struct intel_atomic_state *state)
> >  	struct drm_i915_private *dev_priv = to_i915(state->base.dev);
> >  	struct intel_crtc_state *old_crtc_state, *new_crtc_state, *slave_crtc_state, *master_crtc_state;
> >  	struct intel_crtc *crtc, *slave, *master;
> > +	struct intel_plane *plane;
> >  	int i, ret = 0;
> >  
> >  	if (INTEL_GEN(dev_priv) < 11)
> > @@ -13894,6 +13972,48 @@ static int intel_atomic_check_bigjoiner(struct intel_atomic_state *state)
> >  			return ret;
> >  	}
> >  
> > +	/*
> > +	 * Setup and teardown the new bigjoiner plane mappings.
> > +	 */
> > +	for_each_intel_plane(&dev_priv->drm, plane) {
> > +		struct intel_plane_state *plane_state;
> > +		struct intel_plane *other_plane = NULL;
> > +
> > +		crtc = intel_get_crtc_for_pipe(dev_priv, plane->pipe);
> > +		old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc);
> > +		new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
> > +
> > +		if (!new_crtc_state || !needs_modeset(new_crtc_state))
> > +			continue;
> > +
> > +		if (new_crtc_state->bigjoiner) {
> > +			struct intel_crtc *other_crtc =
> > +				new_crtc_state->bigjoiner_linked_crtc;
> > +			bool found = false;
> > +
> > +			for_each_intel_plane_on_crtc(&dev_priv->drm, other_crtc, other_plane) {
> > +				if (other_plane->id != plane->id)
> > +					continue;
> > +
> > +				found = true;
> > +				break;
> > +			}
> > +
> > +			/* All pipes should have identical planes. */
> > +			if (WARN_ON(!found))
> > +				return -EINVAL;
> > +		} else if (!old_crtc_state->bigjoiner) {
> > +			continue;
> > +		}
> > +
> > +		plane_state = intel_atomic_get_plane_state(state, plane);
> > +		if (IS_ERR(plane_state))
> > +			return PTR_ERR(plane_state);
> > +
> > +		plane_state->bigjoiner_plane = other_plane;
> > +		plane_state->bigjoiner_slave = new_crtc_state->bigjoiner_slave;
> > +	}
> 
> Is my understanding correct that we always grab the corresponding plane
> on the other CRTC when the big joiner is active?  I.e., even in cases
> where the uapi plane is restricted to just one half of the screen, we're
> still going to grab the bigjoiner slave plane?  I guess trying to
> restrict this to only cases where the uapi plane covers both pipes would
> make this whole thing even more complicated.

Yeah, I'm thinking we can just ignore that particular optimization.
Would need to somehow convince ourselves early that the plane still
remains on its own half of the screen, or we'd need to come up with some
kind of retry loop for this that can restart if a new plane gets added
in the middle of the process. IMO not worth the hassle.
diff mbox series

Patch

diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.c b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
index 964db7774d10..cc088676f0a2 100644
--- a/drivers/gpu/drm/i915/display/intel_atomic_plane.c
+++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.c
@@ -182,16 +182,36 @@  int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
 					       old_plane_state, new_plane_state);
 }
 
-static struct intel_crtc *
-get_crtc_from_states(const struct intel_plane_state *old_plane_state,
-		     const struct intel_plane_state *new_plane_state)
-{
+struct intel_crtc *
+intel_plane_get_crtc_from_states(struct intel_atomic_state *state,
+				 const struct intel_plane_state *old_plane_state,
+				 const struct intel_plane_state *new_plane_state)
+  {
+	struct drm_i915_private *dev_priv = to_i915(state->base.dev);
+	struct intel_plane *plane = to_intel_plane(new_plane_state->base.plane);
+
 	if (new_plane_state->base.crtc)
 		return to_intel_crtc(new_plane_state->base.crtc);
 
 	if (old_plane_state->base.crtc)
 		return to_intel_crtc(old_plane_state->base.crtc);
 
+	if (new_plane_state->bigjoiner_slave) {
+		const struct intel_plane_state *new_master_plane_state =
+			intel_atomic_get_new_plane_state(state, new_plane_state->bigjoiner_plane);
+
+		if (new_master_plane_state->base.crtc)
+			return intel_get_crtc_for_pipe(dev_priv, plane->pipe);
+	}
+
+	if (old_plane_state->bigjoiner_slave) {
+		const struct intel_plane_state *old_master_plane_state =
+			intel_atomic_get_old_plane_state(state, old_plane_state->bigjoiner_plane);
+
+		if (old_master_plane_state->base.crtc)
+			return intel_get_crtc_for_pipe(dev_priv, plane->pipe);
+	}
+
 	return NULL;
 }
 
@@ -206,7 +226,8 @@  static int intel_plane_atomic_check(struct drm_plane *_plane,
 	const struct intel_plane_state *old_plane_state =
 		intel_atomic_get_old_plane_state(state, plane);
 	struct intel_crtc *crtc =
-		get_crtc_from_states(old_plane_state, new_plane_state);
+		intel_plane_get_crtc_from_states(state, old_plane_state,
+						 new_plane_state);
 	const struct intel_crtc_state *old_crtc_state;
 	struct intel_crtc_state *new_crtc_state;
 
diff --git a/drivers/gpu/drm/i915/display/intel_atomic_plane.h b/drivers/gpu/drm/i915/display/intel_atomic_plane.h
index 33fb85cd3909..901a50e6e2d3 100644
--- a/drivers/gpu/drm/i915/display/intel_atomic_plane.h
+++ b/drivers/gpu/drm/i915/display/intel_atomic_plane.h
@@ -42,5 +42,9 @@  int intel_plane_atomic_calc_changes(const struct intel_crtc_state *old_crtc_stat
 				    struct intel_crtc_state *crtc_state,
 				    const struct intel_plane_state *old_plane_state,
 				    struct intel_plane_state *plane_state);
+struct intel_crtc *
+intel_plane_get_crtc_from_states(struct intel_atomic_state *state,
+				 const struct intel_plane_state *old_plane_state,
+				 const struct intel_plane_state *new_plane_state);
 
 #endif /* __INTEL_ATOMIC_PLANE_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index df588bf47559..06ceac4f1436 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -11811,24 +11811,101 @@  static bool check_single_encoder_cloning(struct drm_atomic_state *state,
 	return true;
 }
 
+static int icl_add_dependent_planes(struct intel_atomic_state *state,
+				    struct intel_plane_state *plane_state)
+{
+	struct intel_plane_state *new_plane_state;
+	struct intel_plane *plane;
+	int ret = 0;
+
+	plane = plane_state->bigjoiner_plane;
+	if (plane && !intel_atomic_get_new_plane_state(state, plane)) {
+		new_plane_state = intel_atomic_get_plane_state(state, plane);
+		if (IS_ERR(new_plane_state))
+			return PTR_ERR(new_plane_state);
+
+		ret = 1;
+	}
+
+	plane = plane_state->planar_linked_plane;
+	if (plane && !intel_atomic_get_new_plane_state(state, plane)) {
+		new_plane_state = intel_atomic_get_plane_state(state, plane);
+		if (IS_ERR(new_plane_state))
+			return PTR_ERR(new_plane_state);
+
+		ret = 1;
+	}
+
+	return ret;
+}
+
 static int icl_add_linked_planes(struct intel_atomic_state *state)
 {
-	struct intel_plane *plane, *linked;
-	struct intel_plane_state *plane_state, *linked_plane_state;
+	struct intel_plane *plane;
+	struct intel_plane_state *old_plane_state, *new_plane_state;
+	struct intel_crtc *crtc, *linked_crtc;
+	struct intel_crtc_state *old_crtc_state, *new_crtc_state, *linked_crtc_state;
+	bool added;
 	int i;
 
-	for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
-		linked = plane_state->planar_linked_plane;
+	/*
+	 * Iteratively add plane_state->linked_plane and plane_state->bigjoiner_plane
+	 *
+	 * This needs to be done repeatedly, because of is a funny interaction;
+	 * the Y-plane may be assigned differently on the other bigjoiner crtc,
+	 * and we could end up with the following evil recursion, when only adding a
+	 * single plane to state:
+	 *
+	 * XRGB8888 master plane 6 adds NV12 slave Y-plane 6, which adds slave UV plane 0,
+	 * which adds master UV plane 0, which adds master Y-plane 7, which adds XRGB8888
+	 * slave plane 7.
+	 *
+	 * We could pull in even more because of old_plane_state vs new_plane_state.
+	 *
+	 * Max depth = 5 (or 7 for evil case) in this case.
+	 * Number of passes will be less, because newly added planes show up in the
+	 * same iteration round when added_plane->index > plane->index.
+	 */
+	do {
+		added = false;
 
-		if (!linked)
-			continue;
+		for_each_oldnew_intel_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
+			int ret, ret2;
+
+			ret = icl_add_dependent_planes(state, old_plane_state);
+			if (ret < 0)
+				return ret;
+
+			ret2 = icl_add_dependent_planes(state, new_plane_state);
+			if (ret2 < 0)
+				return ret2;
 
-		linked_plane_state = intel_atomic_get_plane_state(state, linked);
-		if (IS_ERR(linked_plane_state))
-			return PTR_ERR(linked_plane_state);
+			added |= ret || ret2;
+		}
+	} while (added);
+
+	/*
+	 * Make sure bigjoiner slave crtc's are also pulled in. This is not done automatically
+	 * when adding slave planes, because plane_state->crtc is null.
+	 */
+	for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
+		linked_crtc = old_crtc_state->bigjoiner_linked_crtc;
+		if (linked_crtc) {
+			linked_crtc_state =
+				intel_atomic_get_crtc_state(&state->base, linked_crtc);
+
+			if (IS_ERR(linked_crtc_state))
+				return PTR_ERR(linked_crtc_state);
+		}
+
+		linked_crtc = new_crtc_state->bigjoiner_linked_crtc;
+		if (linked_crtc && linked_crtc != old_crtc_state->bigjoiner_linked_crtc) {
+			linked_crtc_state =
+				intel_atomic_get_crtc_state(&state->base, linked_crtc);
 
-		WARN_ON(linked_plane_state->planar_linked_plane != plane);
-		WARN_ON(linked_plane_state->planar_slave == plane_state->planar_slave);
+			if (IS_ERR(linked_crtc_state))
+				return PTR_ERR(linked_crtc_state);
+		}
 	}
 
 	return 0;
@@ -13799,6 +13876,7 @@  static int intel_atomic_check_bigjoiner(struct intel_atomic_state *state)
 	struct drm_i915_private *dev_priv = to_i915(state->base.dev);
 	struct intel_crtc_state *old_crtc_state, *new_crtc_state, *slave_crtc_state, *master_crtc_state;
 	struct intel_crtc *crtc, *slave, *master;
+	struct intel_plane *plane;
 	int i, ret = 0;
 
 	if (INTEL_GEN(dev_priv) < 11)
@@ -13894,6 +13972,48 @@  static int intel_atomic_check_bigjoiner(struct intel_atomic_state *state)
 			return ret;
 	}
 
+	/*
+	 * Setup and teardown the new bigjoiner plane mappings.
+	 */
+	for_each_intel_plane(&dev_priv->drm, plane) {
+		struct intel_plane_state *plane_state;
+		struct intel_plane *other_plane = NULL;
+
+		crtc = intel_get_crtc_for_pipe(dev_priv, plane->pipe);
+		old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc);
+		new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
+
+		if (!new_crtc_state || !needs_modeset(new_crtc_state))
+			continue;
+
+		if (new_crtc_state->bigjoiner) {
+			struct intel_crtc *other_crtc =
+				new_crtc_state->bigjoiner_linked_crtc;
+			bool found = false;
+
+			for_each_intel_plane_on_crtc(&dev_priv->drm, other_crtc, other_plane) {
+				if (other_plane->id != plane->id)
+					continue;
+
+				found = true;
+				break;
+			}
+
+			/* All pipes should have identical planes. */
+			if (WARN_ON(!found))
+				return -EINVAL;
+		} else if (!old_crtc_state->bigjoiner) {
+			continue;
+		}
+
+		plane_state = intel_atomic_get_plane_state(state, plane);
+		if (IS_ERR(plane_state))
+			return PTR_ERR(plane_state);
+
+		plane_state->bigjoiner_plane = other_plane;
+		plane_state->bigjoiner_slave = new_crtc_state->bigjoiner_slave;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index ace372a76330..f05f4830a529 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -571,6 +571,17 @@  struct intel_plane_state {
 	 */
 	struct intel_plane *planar_linked_plane;
 
+	/*
+	 * bigjoiner_plane:
+	 *
+	 * When 2 pipes are joined in a bigjoiner configuration,
+	 * points to the same plane on the other pipe.
+	 *
+	 * bigjoiner_slave is set on the slave pipe.
+	 */
+	struct intel_plane *bigjoiner_plane;
+	u32 bigjoiner_slave;
+
 	/*
 	 * planar_slave:
 	 * If set don't update use the linked plane's state for updating