[PATCH v2] [PATCH v2] drm/i915/dsi: Fix command mode line time calculation

From: Jinman Ma

Date: Mon Aug 03 2026 - 11:57:09 EST


Thank you for the review. I verified both reported issues and addressed
them in v2.

For dual-link DSI configurations, intel_dsi->pclk and afe_clk() describe
the per-link bandwidth, while the previous calculation used the full
horizontal total. This made the calculated line time twice the actual
per-link line time and provided only about half of the required 400 us
vertical blanking interval.

v2 now calculates the line time using a per-link horizontal total by
dividing htotal by two for dual-link configurations.

The second DIV_ROUND_UP_ULL() call also used line_time_ns, a u64 value,
as its divisor. This does not match the macro's u32 divisor contract.
v2 replaces it with DIV64_U64_ROUND_UP(), which supports a 64-bit
numerator and divisor.

The changes were tested on a Huawei MateBook E with a Tiger Lake GPU and
a 2560x1600 RGB888 dual-link front-back command-mode DSI panel.

Before v2, the driver programmed a vtotal below vsync_end and reported:

i915 0000:00:02.0: [drm] *ERROR* Invalid vsync_end value
i915 0000:00:02.0: [drm] *ERROR*
[CRTC:171:pipe A] mismatch in hw.pipe_mode.crtc_vtotal
(expected 1710, found 1622)

With v2, the per-link line time is approximately 8.18 us. The driver
requests 49 blanking lines, providing approximately 400.9 us of vertical
blanking and programming vtotal to 1649.

After a clean build and boot:

- vtotal is programmed to 1649
- Invalid vsync_end is no longer reported
- no divide error or kernel Oops occurs
- no FIFO underrun, GPU hang, or atomic update failure is reported
- the DSI connector and display pipe initialize successfully

The existing modeset verification differences between the mode timings
and command-mode transcoder timings remain unchanged and are outside the
scope of this patch.


Signed-off-by: Jinman Ma <justdreemurr@xxxxxxxxxx>
---

Changes in v2:
- Use the per-link htotal when calculating dual-link DSI line time.
- Use DIV64_U64_ROUND_UP() when dividing by the u64 line_time_ns.
- Preserve the existing bpp selection for compressed and uncompressed
configurations.

Testing notes:

The corrected dual-link calculation removes the Invalid vsync_end error
and provides the required 400 us vertical blanking interval. A slight
intermittent display jitter is still visible, so the dual-link line-time
error was not the sole cause of the previously reported jitter.

TTY switching and suspend/resume can also leave the internal panel
blank. These appear to involve separate fbcon and DSI command-mode
resume paths and are not addressed by this patch. They will be reported
and investigated separately.

drivers/gpu/drm/i915/display/icl_dsi.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c
index ea0cdb782..0f942cc6e 100644
--- a/drivers/gpu/drm/i915/display/icl_dsi.c
+++ b/drivers/gpu/drm/i915/display/icl_dsi.c
@@ -917,16 +917,24 @@ gen11_dsi_set_transcoder_timings(struct intel_encoder *encoder,
if (is_vid_mode(intel_dsi)) {
vtotal = adjusted_mode->crtc_vtotal;
} else {
- int bpp, line_time_us, byte_clk_period_ns;
+ int bpp;
+ u64 line_time_ns;
+ u16 link_htotal = htotal;
+
+ if (intel_dsi->dual_link)
+ link_htotal /= 2;

if (crtc_state->dsc.compression_enable)
bpp = fxp_q4_to_int(crtc_state->dsc.compressed_bpp_x16);
else
bpp = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format);
+ line_time_ns =
+ DIV_ROUND_UP_ULL((u64)link_htotal * bpp * 1000000,
+ afe_clk(encoder, crtc_state) *
+ intel_dsi->lane_count);

- byte_clk_period_ns = 1000000 / afe_clk(encoder, crtc_state);
- line_time_us = (htotal * (bpp / 8) * byte_clk_period_ns) / (1000 * intel_dsi->lane_count);
- vtotal = vactive + DIV_ROUND_UP(400, line_time_us);
+ vtotal = vactive +
+ DIV64_U64_ROUND_UP(400000ULL, line_time_ns);
}
vsync_start = adjusted_mode->crtc_vsync_start;
vsync_end = adjusted_mode->crtc_vsync_end;
--
2.55.0