[PATCH 6.19 269/342] drm/i915: Unlink NV12 planes earlier

Greg Kroah-Hartman
2026-03-31T16:44:02Z

From: Ville Syrjälä <ville.syrjala@linux.intel.com>

commit bfa71b7a9dc6b5b8af157686e03308291141d00c upstream.

unlink_nv12_plane() will clobber parts of the plane state
potentially already set up by plane_atomic_check(), so we
must make sure not to call the two in the wrong order.
The problem happens when a plane previously selected as
a Y plane is now configured as a normal plane by user space.
plane_atomic_check() will first compute the proper plane
state based on the userspace request, and unlink_nv12_plane()
later clears some of the state.

This used to work on account of unlink_nv12_plane() skipping
the state clearing based on the plane visibility. But I removed
that check, thinking it was an impossible situation. Now when
that situation happens unlink_nv12_plane() will just WARN
and proceed to clobber the state.

Rather than reverting to the old way of doing things, I think
it's more clear if we unlink the NV12 planes before we even
compute the new plane state.

Cc: stable@vger.kernel.org
Reported-by: Khaled Almahallawy <khaled.almahallawy@intel.com>
Closes: https://lore.kernel.org/intel-gfx/20260212004852.1920270-1-khaled.almahallawy@intel.com/
Tested-by: Khaled Almahallawy <khaled.almahallawy@intel.com>
Fixes: 6a01df2f1b2a ("drm/i915: Remove pointless visible check in unlink_nv12_plane()")
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patch.msgid.link/20260316163953.12905-2-ville.syrjala@linux.intel.com
Reviewed-by: Uma Shankar <uma.shankar@intel.com>
(cherry picked from commit 017ecd04985573eeeb0745fa2c23896fb22ee0cc)
Signed-off-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/gpu/drm/i915/display/intel_plane.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)

--- a/drivers/gpu/drm/i915/display/intel_plane.c
+++ b/drivers/gpu/drm/i915/display/intel_plane.c
@@ -433,11 +433,16 @@ void intel_plane_copy_hw_state(struct in
drm_framebuffer_get(plane_state->hw.fb);
}

+static void unlink_nv12_plane(struct intel_crtc_state *crtc_state,
+ struct intel_plane_state *plane_state);
+
void intel_plane_set_invisible(struct intel_crtc_state *crtc_state,
struct intel_plane_state *plane_state)
{
struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);

+ unlink_nv12_plane(crtc_state, plane_state);
+
crtc_state->active_planes &= ~BIT(plane->id);
crtc_state->scaled_planes &= ~BIT(plane->id);
crtc_state->nv12_planes &= ~BIT(plane->id);
@@ -1511,6 +1516,9 @@ static void unlink_nv12_plane(struct int
struct intel_display *display = to_intel_display(plane_state);
struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);

+ if (!plane_state->planar_linked_plane)
+ return;
+
plane_state->planar_linked_plane = NULL;

if (!plane_state->is_y_plane)
@@ -1548,8 +1556,7 @@ static int icl_check_nv12_planes(struct
if (plane->pipe != crtc->pipe)
continue;

- if (plane_state->planar_linked_plane)
- unlink_nv12_plane(crtc_state, plane_state);
+ unlink_nv12_plane(crtc_state, plane_state);
}

if (!crtc_state->nv12_planes)