[PATCH RFC 11/25] drm/rockchip: vop2: Enable VRR
From: Nicolas Frattaroli
Date: Mon Sep 21 2026 - 13:37:42 EST
Use the new VRR state tracking to handle VRR front porch adjustment.
To avoid indenting too deeply, refactor the vblank handling into its own
function while at it, where scope-based cleanup helpers for the lock can
be used.
Co-developed-by: Derek Foreman <derek.foreman@xxxxxxxxxxxxx>
Signed-off-by: Derek Foreman <derek.foreman@xxxxxxxxxxxxx>
Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@xxxxxxxxxxxxx>
---
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 62 ++++++++++++++++++++++------
drivers/gpu/drm/rockchip/rockchip_drm_vop2.h | 9 ++++
2 files changed, 59 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index bfb384142568..e79081c04437 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -24,6 +24,7 @@
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_blend.h>
#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
#include <linux/debugfs.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_flip_work.h>
@@ -2011,6 +2012,25 @@ static void vop2_crtc_atomic_begin(struct drm_crtc *crtc,
vop2->ops->setup_overlay(vp);
}
+static void vop2_vp_set_vrr_vtotal(struct vop2_video_port *vp, unsigned int vtotal)
+{
+ const u32 mask = RK3568_VP_DSP_VTOTAL_VS_END__DSP_VTOTAL |
+ RK3568_VP_DSP_VTOTAL_VS_END__IMD;
+
+ if (!vtotal) {
+ vop2_vp_update_bits(vp, RK3568_VP_DSP_VTOTAL_VS_END,
+ RK3568_VP_DSP_VTOTAL_VS_END__IMD, 0);
+ return;
+ }
+
+ /* Value is 13 bits at most, saturate it if too large */
+ if (vtotal > RK3568_VP_DSP_VTOTAL_VS_END__DSP_VTOTAL >> 16)
+ vtotal = RK3568_VP_DSP_VTOTAL_VS_END__DSP_VTOTAL >> 16;
+
+ vop2_vp_update_bits(vp, RK3568_VP_DSP_VTOTAL_VS_END, mask,
+ vtotal << 16 | RK3568_VP_DSP_VTOTAL_VS_END__IMD);
+}
+
static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_atomic_commit *state)
{
@@ -2029,6 +2049,11 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc,
spin_lock_irq(&crtc->dev->event_lock);
+ if (crtc_state->vrr_enabled) {
+ if (drm_crtc_helper_vrr_flush(crtc_state))
+ vop2_vp_set_vrr_vtotal(vp, crtc_state->vrr_state.cur_vtotal);
+ }
+
if (crtc->state->event) {
WARN_ON(drm_crtc_vblank_get(crtc));
vp->event = crtc->state->event;
@@ -2371,6 +2396,30 @@ static irqreturn_t rk3576_vp_isr(int irq, void *data)
return ret;
}
+static void vop2_vp_handle_vblank(struct vop2_video_port *vp)
+{
+ struct drm_crtc *crtc = &vp->crtc;
+ struct vop2 *vop2 = vp->vop2;
+ u32 val;
+
+ guard(spinlock)(&crtc->dev->event_lock);
+
+ if (!vp->event)
+ return;
+
+ val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
+
+ if (!(val & BIT(vp->id))) {
+ drm_crtc_send_vblank_event(crtc, vp->event);
+ vp->event = NULL;
+ drm_crtc_vblank_put(crtc);
+
+ if (crtc->state->vrr_enabled)
+ if (drm_crtc_helper_vrr_vsync(crtc->state))
+ vop2_vp_set_vrr_vtotal(vp, crtc->state->vrr_state.cur_vtotal);
+ }
+}
+
static irqreturn_t vop2_isr(int irq, void *data)
{
struct vop2 *vop2 = data;
@@ -2402,18 +2451,7 @@ static irqreturn_t vop2_isr(int irq, void *data)
if (irqs & VP_INT_FS_FIELD) {
drm_crtc_handle_vblank(crtc);
- spin_lock(&crtc->dev->event_lock);
- if (vp->event) {
- u32 val = vop2_readl(vop2, RK3568_REG_CFG_DONE);
-
- if (!(val & BIT(vp->id))) {
- drm_crtc_send_vblank_event(crtc, vp->event);
- vp->event = NULL;
- drm_crtc_vblank_put(crtc);
- }
- }
- spin_unlock(&crtc->dev->event_lock);
-
+ vop2_vp_handle_vblank(vp);
ret = IRQ_HANDLED;
}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
index ffcb39c130aa..fdb2030e3ad4 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h
@@ -655,6 +655,9 @@ enum dst_factor_mode {
#define RK3588_VP_DSP_CTRL__GAMMA_UPDATE_EN BIT(22)
+#define RK3568_VP_DSP_VTOTAL_VS_END__DSP_VTOTAL GENMASK(28, 16)
+#define RK3568_VP_DSP_VTOTAL_VS_END__IMD BIT(15)
+
#define RK3588_VP_CLK_CTRL__DCLK_OUT_DIV GENMASK(3, 2)
#define RK3588_VP_CLK_CTRL__DCLK_CORE_DIV GENMASK(1, 0)
@@ -810,6 +813,12 @@ static inline void vop2_vp_write(struct vop2_video_port *vp, u32 offset, u32 v)
regmap_write(vp->vop2->map, vp->data->offset + offset, v);
}
+static inline void vop2_vp_update_bits(struct vop2_video_port *vp, u32 offset,
+ u32 mask, u32 v)
+{
+ regmap_update_bits(vp->vop2->map, vp->data->offset + offset, mask, v);
+}
+
static inline u32 vop2_readl(struct vop2 *vop2, u32 offset)
{
u32 val;
--
2.55.0