[PATCH v2 03/13] drm/rockchip: vop2: Balance state on atomic_enable error paths
Cristian Ciocaltea
cristian.ciocaltea at collabora.com
Mon Jul 6 12:32:15 PDT 2026
vop2_crtc_atomic_enable() prepares vp->dclk and, for the first CRTC,
calls vop2_enable(), incrementing the shared vop2->enable_count before
programming VOP2 registers. Two later failure points return early with
only vop2_unlock(), leaking the dclk prepare, the enable_count
increment, and the core clocks / PM runtime reference from
vop2_enable().
Since the disable path decrements enable_count unconditionally, the
leaked increment skews the count, causing an underflow or premature
shutdown of core clocks and PM runtime while another CRTC is active.
Cleaning up on the enable error paths alone is insufficient, however.
A CRTC whose atomic_enable() bailed is still committed as active, so
vop2_crtc_atomic_disable() still runs its full teardown, releasing the
same dclk prepare and enable_count a second time - underflowing both
counts and, where vop2_enable() ran, touching VOP registers with core
clocks gated.
Fix this by reverting exactly what was set up on failure, and track the
per-VP bring-up state so atomic_disable() skips teardown when
atomic_enable() never completed.
Fixes: 604be85547ce ("drm/rockchip: Add VOP2 driver")
Reported-by: Sashiko <sashiko-bot at kernel.org>
Closes: https://lore.kernel.org/all/20260623202043.8BFE21F000E9@smtp.kernel.org/
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea at collabora.com>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 34 +++++++++++++++++++++-------
drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 6 +++++
2 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index e11dc8c7f78b..c2328b35466d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -1033,6 +1033,9 @@ static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
vop2_lock(vop2);
+ if (!vp->enabled)
+ goto out_unlock;
+
old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
@@ -1063,11 +1066,14 @@ static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
clk_disable_unprepare(vp->dclk);
+ vp->enabled = false;
+
vop2->enable_count--;
if (!vop2->enable_count)
vop2_disable(vop2);
+out_unlock:
vop2_unlock(vop2);
if (crtc->state->event && !crtc->state->active) {
@@ -1803,8 +1809,11 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
return;
}
- if (!vop2->enable_count)
- vop2_enable(vop2);
+ if (!vop2->enable_count) {
+ ret = vop2_enable(vop2);
+ if (ret)
+ goto err_unprepare_dclk;
+ }
vop2->enable_count++;
@@ -1831,10 +1840,8 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
clock = vop2->ops->setup_intf_mux(vp, rkencoder->crtc_endpoint_id, polflags);
}
- if (!clock) {
- vop2_unlock(vop2);
- return;
- }
+ if (!clock)
+ goto err_put_enable_count;
if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
!(vp_data->feature & VOP2_VP_FEATURE_OUTPUT_10BIT))
@@ -1852,8 +1859,7 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
default:
drm_err(vop2->drm, "Unknown DRM_MODE_CONNECTOR %d\n",
vcstate->output_type);
- vop2_unlock(vop2);
- return;
+ goto err_put_enable_count;
}
else
out_mode = vcstate->output_mode;
@@ -1965,6 +1971,18 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
drm_crtc_vblank_on(crtc);
+ vp->enabled = true;
+
+ vop2_unlock(vop2);
+
+ return;
+
+err_put_enable_count:
+ if (!--vop2->enable_count)
+ vop2_disable(vop2);
+
+err_unprepare_dclk:
+ clk_disable_unprepare(vp->dclk);
vop2_unlock(vop2);
}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
index ffcb39c130aa..b209b2d5b96d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
@@ -243,6 +243,12 @@ struct vop2_video_port {
struct completion dsp_hold_completion;
+ /**
+ * @enabled: CRTC fully brought up by atomic_enable(); allows
+ * atomic_disable() to skip teardown after a failed enable.
+ */
+ bool enabled;
+
/**
* @win_mask: Bitmask of windows attached to the video port;
*/
--
2.54.0
More information about the linux-arm-kernel
mailing list