[Intel-gfx] [PATCH 2/7] drm/i915/gen11: Link nv12 Y and UV planes in the atomic state, v3.
Ville Syrjälä
ville.syrjala at linux.intel.com
Fri Sep 21 18:35:52 UTC 2018
On Fri, Sep 21, 2018 at 07:39:40PM +0200, Maarten Lankhorst wrote:
> To make NV12 working on icl, we need to update 2 planes simultaneously.
> I've chosen to do this in the CRTC step after plane validation is done,
> so we know what planes are (in)visible. The linked Y plane will get
> updated in intel_plane_update_planes_on_crtc(), by the call to
> update_slave, which gets the master's plane_state as argument.
>
> The link requires both planes for atomic_update to work,
> so make sure skl_ddb_add_affected_planes() adds both states.
>
> Changes since v1:
> - Introduce icl_is_nv12_y_plane(), instead of hardcoding sprite numbers.
> - Put all the state updating login in intel_plane_atomic_check_with_state().
> - Clean up changes in intel_plane_atomic_check().
> Changes since v2:
> - Fix intel_atomic_get_old_plane_state() to actually return old state.
> - Move visibility changes to preparation patch.
> - Only try to find a Y plane on gen11, earlier platforms only require a single plane.
>
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst at linux.intel.com>
>
> fixup Y/UV Linkage
>
> Signed-off-by: Maarten Lankhorst <maarten.lankhorst at linux.intel.com>
> ---
> drivers/gpu/drm/i915/intel_atomic_plane.c | 106 ++++++++++++++++++----
> drivers/gpu/drm/i915/intel_display.c | 57 ++++++++++++
> drivers/gpu/drm/i915/intel_drv.h | 53 +++++++++++
> drivers/gpu/drm/i915/intel_pm.c | 12 ++-
> 4 files changed, 210 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c b/drivers/gpu/drm/i915/intel_atomic_plane.c
> index 984bc1f26625..522699085a59 100644
> --- a/drivers/gpu/drm/i915/intel_atomic_plane.c
> +++ b/drivers/gpu/drm/i915/intel_atomic_plane.c
> @@ -121,7 +121,11 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
> crtc_state->nv12_planes &= ~BIT(intel_plane->id);
> intel_state->base.visible = false;
>
> - /* If this is a cursor plane, no further checks are needed. */
> + /* Destroy the link */
> + intel_state->linked_plane = NULL;
> + intel_state->slave = false;
> +
> + /* If this is a cursor or Y plane, no further checks are needed. */
> if (!intel_state->base.crtc && !old_plane_state->base.crtc)
> return 0;
>
> @@ -142,27 +146,76 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
> state);
> }
>
> -static int intel_plane_atomic_check(struct drm_plane *plane,
> - struct drm_plane_state *new_plane_state)
> +static int intel_plane_atomic_check(struct drm_plane *drm_plane,
> + struct drm_plane_state *new_drm_plane_state)
> {
> - struct drm_atomic_state *state = new_plane_state->state;
> - const struct drm_plane_state *old_plane_state =
> - drm_atomic_get_old_plane_state(state, plane);
> - struct drm_crtc *crtc = new_plane_state->crtc ?: old_plane_state->crtc;
> - const struct drm_crtc_state *old_crtc_state;
> - struct drm_crtc_state *new_crtc_state;
> -
> - new_plane_state->visible = false;
> + struct intel_atomic_state *state =
> + to_intel_atomic_state(new_drm_plane_state->state);
> + struct intel_plane *plane = to_intel_plane(drm_plane);
> + const struct intel_plane_state *old_plane_state =
> + intel_atomic_get_old_plane_state(state, plane);
> + struct intel_plane_state *new_plane_state =
> + to_intel_plane_state(new_drm_plane_state);
> + struct intel_crtc *crtc = to_intel_crtc(
> + new_plane_state->base.crtc ?:
> + old_plane_state->base.crtc);
> + const struct intel_crtc_state *old_crtc_state;
> + struct intel_crtc_state *new_crtc_state;
> + struct intel_plane *linked = old_plane_state->linked_plane;
> + int ret;
> + const struct intel_plane_state *old_linked_state;
> + struct intel_plane_state *new_linked_state = NULL;
> +
> + if (linked) {
> + /*
> + * Make sure a previously linked plane (and implicitly, the CRTC)
> + * is part of the atomic commit.
> + */
> + if (!intel_atomic_get_new_plane_state(state, linked)) {
> + new_linked_state = intel_atomic_get_plane_state(state, linked);
> + if (IS_ERR(new_linked_state))
> + return PTR_ERR(new_linked_state);
> + }
> +
> + old_linked_state =
> + intel_atomic_get_old_plane_state(state, linked);
> +
> + /*
> + * This will happen when we're the Y plane. In which case
> + * old/new_state->crtc are both NULL. We still need to perform
> + * updates on the linked plane.
> + */
> + if (!crtc)
> + crtc = to_intel_crtc(old_linked_state->base.crtc);
> +
> + WARN_ON(!crtc);
> + }
> +
> + new_plane_state->base.visible = false;
> if (!crtc)
> return 0;
>
> - old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
> - new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
> + old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc);
> + new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
> +
> + if (new_linked_state &&
> + drm_plane_index(&linked->base) < drm_plane_index(&plane->base)) {
> + /*
> + * This function is called from drm_atomic_helper_check_planes(), which
> + * will normally check the newly added plane for us, but since we're
> + * already in that function, it won't check the plane if our index
> + * is bigger than the linked index because of the
> + * for_each_oldnew_plane_in_state() call.
> + */
> + new_crtc_state->base.planes_changed = true;
> + ret = intel_plane_atomic_check_with_state(old_crtc_state, new_crtc_state,
> + old_linked_state, new_linked_state);
> + if (ret)
> + return ret;
> + }
This is all rather confusing. Can't we just do a preprocessing step
before check_planes() to add the linked planes as needed, and then
let the normal check_planes() do its thing?
>
> - return intel_plane_atomic_check_with_state(to_intel_crtc_state(old_crtc_state),
> - to_intel_crtc_state(new_crtc_state),
> - to_intel_plane_state(old_plane_state),
> - to_intel_plane_state(new_plane_state));
> + return intel_plane_atomic_check_with_state(old_crtc_state, new_crtc_state,
> + old_plane_state, new_plane_state);
> }
>
> void intel_update_planes_on_crtc(struct intel_atomic_state *old_state,
> @@ -187,6 +240,25 @@ void intel_update_planes_on_crtc(struct intel_atomic_state *old_state,
> trace_intel_update_plane(&plane->base, crtc);
>
> plane->update_plane(plane, new_crtc_state, new_plane_state);
> + } else if (new_plane_state->slave) {
> + struct intel_plane *master =
> + new_plane_state->linked_plane;
> +
> + /*
> + * We update the slave plane from this function because
> + * programming it from the master plane's update_plane
> + * callback runs into issues when the Y plane is
> + * reassigned, disabled or used by a different plane.
> + *
> + * The slave plane is updated with the master plane's
> + * plane_state.
> + */
> + new_plane_state =
> + intel_atomic_get_new_plane_state(old_state, master);
> +
> + trace_intel_update_plane(&plane->base, crtc);
> +
> + plane->update_slave(plane, new_crtc_state, new_plane_state);
> } else {
> trace_intel_disable_plane(&plane->base, crtc);
>
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 390907d77ecd..19cd6bbb43c4 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -10726,6 +10726,60 @@ static bool check_single_encoder_cloning(struct drm_atomic_state *state,
> return true;
> }
>
> +static int icl_check_nv12_planes(struct drm_i915_private *dev_priv,
> + struct intel_crtc *crtc,
> + struct intel_crtc_state *crtc_state)
> +{
> + struct intel_atomic_state *state = to_intel_atomic_state(crtc_state->base.state);
> + struct intel_plane *plane, *aux;
> +
> + if (INTEL_GEN(dev_priv) < 11 || !crtc_state->nv12_planes)
> + return 0;
> +
> + for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) {
> + struct intel_plane_state *plane_state, *aux_state;
> + struct drm_plane_state *drm_aux_state = NULL;
> +
> + if (!(crtc_state->nv12_planes & BIT(plane->id)))
> + continue;
> +
> + plane_state = intel_atomic_get_new_plane_state(state, plane);
> + if (!plane_state)
> + continue;
> +
> + for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, aux) {
> + if (!icl_is_nv12_y_plane(aux->id))
> + continue;
> +
> + if (crtc_state->active_planes & BIT(aux->id))
> + continue;
> +
> + drm_aux_state = drm_atomic_get_plane_state(&state->base, &aux->base);
> + if (IS_ERR(drm_aux_state))
> + return PTR_ERR(drm_aux_state);
> +
> + break;
> + }
> +
> + if (!drm_aux_state) {
> + DRM_DEBUG_KMS("Need %d free Y planes for NV12\n",
> + hweight8(crtc_state->nv12_planes));
> +
> + return -EINVAL;
> + }
> +
> + plane_state->linked_plane = aux;
> +
> + aux_state = to_intel_plane_state(drm_aux_state);
Don't really like the aux name here. Each plane can have an aux surface
so now having aux planes is getting a bit overloady. You called it slave
elsewhere so I'd stick to that everywhere.
> + aux_state->slave = true;
> + aux_state->linked_plane = plane;
> + crtc_state->active_planes |= BIT(aux->id);
> + DRM_DEBUG_KMS("Using %s as Y plane for %s\n", aux->base.name, plane->base.name);
> + }
> +
> + return 0;
> +}
> +
> static int intel_crtc_atomic_check(struct drm_crtc *crtc,
> struct drm_crtc_state *crtc_state)
> {
> @@ -10797,6 +10851,9 @@ static int intel_crtc_atomic_check(struct drm_crtc *crtc,
> if (mode_changed)
> ret = skl_update_scaler_crtc(pipe_config);
>
> + if (!ret)
> + ret = icl_check_nv12_planes(dev_priv, intel_crtc,
> + pipe_config);
> if (!ret)
> ret = skl_check_pipe_max_pixel_rate(intel_crtc,
> pipe_config);
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 8073a85d7178..29c7a4bb484d 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -539,6 +539,26 @@ struct intel_plane_state {
> */
> int scaler_id;
>
> + /*
> + * linked_plane:
> + *
> + * ICL planar formats require 2 planes that are updated as pairs.
> + * This member is used to make sure the other plane is also updated
> + * when required, and for update_slave() to find the correct
> + * plane_state to pass as argument.
> + */
> + struct intel_plane *linked_plane;
> +
> + /*
> + * slave:
> + * If set don't update use the linked plane's state for updating
> + * this plane during atomic commit with the update_slave() callback.
> + *
> + * It's also used by the watermark code to ignore wm calculations on
> + * this plane. They're calculated by the linked plane's wm code.
> + */
> + bool slave;
Wondering if separate *slave and *master pointers would be make things
more obvioius which one we're looking at in each piece of code.
> +
> struct drm_intel_sprite_colorkey ckey;
> };
>
> @@ -973,6 +993,9 @@ struct intel_plane {
> void (*update_plane)(struct intel_plane *plane,
> const struct intel_crtc_state *crtc_state,
> const struct intel_plane_state *plane_state);
> + void (*update_slave)(struct intel_plane *plane,
> + const struct intel_crtc_state *crtc_state,
> + const struct intel_plane_state *plane_state);
> void (*disable_plane)(struct intel_plane *plane,
> struct intel_crtc *crtc);
> bool (*get_hw_state)(struct intel_plane *plane, enum pipe *pipe);
> @@ -1330,6 +1353,27 @@ hdmi_to_dig_port(struct intel_hdmi *intel_hdmi)
> return container_of(intel_hdmi, struct intel_digital_port, hdmi);
> }
>
> +static inline struct intel_plane_state *
> +intel_atomic_get_plane_state(struct intel_atomic_state *state,
> + struct intel_plane *plane)
> +{
> + struct drm_plane_state *ret =
> + drm_atomic_get_plane_state(&state->base, &plane->base);
> +
> + if (IS_ERR(ret))
> + return ERR_CAST(ret);
> +
> + return to_intel_plane_state(ret);
> +}
> +
> +static inline struct intel_plane_state *
> +intel_atomic_get_old_plane_state(struct intel_atomic_state *state,
> + struct intel_plane *plane)
> +{
> + return to_intel_plane_state(drm_atomic_get_old_plane_state(&state->base,
> + &plane->base));
> +}
> +
> static inline struct intel_plane_state *
> intel_atomic_get_new_plane_state(struct intel_atomic_state *state,
> struct intel_plane *plane)
> @@ -2143,6 +2187,15 @@ int skl_plane_check(struct intel_crtc_state *crtc_state,
> int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state);
> int chv_plane_check_rotation(const struct intel_plane_state *plane_state);
>
> +static inline bool icl_is_nv12_y_plane(enum plane_id id)
> +{
> + /* Don't need to do a gen check, these planes are only available on gen11 */
> + if (id == PLANE_SPRITE4 || id == PLANE_SPRITE5)
> + return true;
> +
> + return false;
> +}
> +
> /* intel_tv.c */
> void intel_tv_init(struct drm_i915_private *dev_priv);
>
> diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
> index 1db9b8328275..d76d93452137 100644
> --- a/drivers/gpu/drm/i915/intel_pm.c
> +++ b/drivers/gpu/drm/i915/intel_pm.c
> @@ -5137,11 +5137,12 @@ skl_ddb_add_affected_planes(struct intel_crtc_state *cstate)
> struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
> struct skl_ddb_allocation *new_ddb = &intel_state->wm_results.ddb;
> struct skl_ddb_allocation *cur_ddb = &dev_priv->wm.skl_hw.ddb;
> - struct drm_plane_state *plane_state;
> struct drm_plane *plane;
> enum pipe pipe = intel_crtc->pipe;
>
> drm_for_each_plane_mask(plane, dev, cstate->base.plane_mask) {
> + struct drm_plane_state *plane_state;
> + struct intel_plane *linked;
> enum plane_id plane_id = to_intel_plane(plane)->id;
>
> if (skl_ddb_entry_equal(&cur_ddb->plane[pipe][plane_id],
> @@ -5153,6 +5154,15 @@ skl_ddb_add_affected_planes(struct intel_crtc_state *cstate)
> plane_state = drm_atomic_get_plane_state(state, plane);
> if (IS_ERR(plane_state))
> return PTR_ERR(plane_state);
> +
> + /* Make sure linked plane is updated too */
> + linked = to_intel_plane_state(plane_state)->linked_plane;
> + if (!linked)
> + continue;
> +
> + plane_state = drm_atomic_get_plane_state(state, &linked->base);
> + if (IS_ERR(plane_state))
> + return PTR_ERR(plane_state);
> }
>
> return 0;
> --
> 2.18.0
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx
--
Ville Syrjälä
Intel
More information about the Intel-gfx
mailing list