Hi
Am 30.10.26 um 17:06 schrieb Ville Syrjälä:
[...]
drm_mode_copy(&new_crtc_state->adjusted_mode, &new_crtc_state->mode);
+
+ if (!new_crtc_state->enable)
+ continue;
+
+ ret = drm_crtc_vblank_mode_fixup(crtc, &new_crtc_state->mode,
+
&new_crtc_state->adjusted_mode);
We don't want this hack applied to all drivers. It's a problem
for a single driver, so that one driver should handle it.
By single driver, you mean virtgpu?
The bug is in Spice' vdagent, which specifies the pixel clock in kHz
instead of Hz. (Apparently in the block at [1].) AFAIU Spice, it runs
on any graphics driver, with a strong preference for virtgpu or qxl. The
docs and bug reports are somewhat contradictory. But I'd say that at
least the drivers for virtual-guest graphics are affected; maybe others.
[1]
https://gitlab.freedesktop.org/spice/linux/vd_agent/-/blob/master/src/vdagent/x11-randr.c?ref_type=heads#L321
Best regards
Thomas
+ if (!ret) {
+ drm_dbg_atomic(crtc->dev, "[CRTC:%d:%s] vblank fixup
failed\n",
+ crtc->base.id, crtc->name);
+ return -EINVAL;
+ }
}
for_each_new_connector_in_state(state, connector, new_conn_state, i) {
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index f90fb2d13e42..6e826c3b6c42 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -620,6 +620,74 @@ wait_queue_head_t *drm_crtc_vblank_waitqueue(struct
drm_crtc *crtc)
}
EXPORT_SYMBOL(drm_crtc_vblank_waitqueue);
+static int drm_vblank_validate_dotclock(u16 crtc_htotal, u16 crtc_vtotal, u32 flags, int dotclock)
+{
+ unsigned int frame_size;
+ u64 linedur_ns, framedur_ns;
+
+ if (dotclock <= 0)
+ return -EINVAL;
+
+ frame_size = (unsigned int)crtc_htotal * (unsigned int)crtc_vtotal;
+
+ /*
+ * Convert scanline length in pixels and video dot clock to line
duration
+ * and frame duration in nanoseconds.
+ */
+ linedur_ns = div_u64((u64)crtc_htotal * 1000000, dotclock);
+ framedur_ns = div_u64((u64)frame_size * 1000000, dotclock);
+
+ /*
+ * Fields of interlaced scanout modes are only half a frame duration.
+ */
+ if (flags & DRM_MODE_FLAG_INTERLACE)
+ framedur_ns /= 2;
+
+ if (linedur_ns > INT_MAX || framedur_ns > INT_MAX)
+ return -ERANGE; /* dot clock too low */
+
+ return 0;
+}
+
+bool drm_crtc_vblank_mode_fixup(struct drm_crtc *crtc, const struct
drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = crtc->dev;
+ unsigned int pipe = drm_crtc_index(crtc);
+ int dotclock = adjusted_mode->crtc_clock;
+ int ret;
+
+ if (!drm_dev_has_vblank(dev))
+ return true;
+
+ if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
+ return false;
+
+ ret = drm_vblank_validate_dotclock(adjusted_mode->crtc_htotal,
+ adjusted_mode->crtc_vtotal,
+ adjusted_mode->flags, dotclock);
+ if (ret == -ERANGE) {
+ int dotclock_1000;
+
+ /*
+ * SPICE' vd_agent has a bug where it specifies the dot clock
in kHz
+ * instead of Hz. Setting up a vblank interrupt or timer for
such a
+ * mode overflows the calculation in
drm_calc_timestamping_constants().
+ * An errno code of ERANGE specifically points to this problem.
Try
+ * converting the dot clock to Hz and validate again.
+ */
+ if (check_mul_overflow(dotclock, 1000, &dotclock_1000)) {
+ ret =
drm_vblank_validate_dotclock(adjusted_mode->crtc_htotal,
+
adjusted_mode->crtc_vtotal,
+
adjusted_mode->flags, dotclock_1000);
+ if (!ret)
+ adjusted_mode->crtc_clock = dotclock_1000;
+ }
+ }
+
+ return !ret;
+}
+EXPORT_SYMBOL(drm_crtc_vblank_mode_fixup);
/**
* drm_calc_timestamping_constants - calculate vblank timestamp constants
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
index 2fcef9c0f5b1..a3628c477f59 100644
--- a/include/drm/drm_vblank.h
+++ b/include/drm/drm_vblank.h
@@ -311,6 +311,8 @@ void drm_crtc_vblank_on(struct drm_crtc *crtc);
u64 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc);
void drm_crtc_vblank_restore(struct drm_crtc *crtc);
+bool drm_crtc_vblank_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
void drm_calc_timestamping_constants(struct drm_crtc *crtc,
const struct drm_display_mode *mode);
wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc);
--
2.56.0
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri, (HRB 36809, AG Nürnberg)