Re: [PATCH 2/3] drm/bridge: ti-sn65dsi83: halve horizontal syncs for dual LVDS output

Louis Chauvet
2026-04-08T15:33:27Z

On 2/26/26 17:16, Luca Ceresoli wrote:
> Dual LVDS output (available on the SN65DSI84) requires HSYNC_PULSE_WIDTH
> and HORIZONTAL_BACK_PORCH to be divided by two with respect to the values
> used for single LVDS output.
>
> While not clearly stated in the datasheet, this is needed according to the
> DSI Tuner [0] output. It also makes sense intuitively because in dual LVDS
> output two pixels at a time are output and so the output clock is half of
> the pixel clock.
>
> Some dual-LVDS panels refuse to show any picture without this fix.
>
> Divide by two HORIZONTAL_FRONT_PORCH too, even though this register is used
> only for test pattern generation which is not currently implemented by this
> driver.
>
> [0] https://www.ti.com/tool/DSI-TUNER
>
> Fixes: ceb515ba29ba ("drm/bridge: ti-sn65dsi83: Add TI SN65DSI83 and SN65DSI84 driver")
> Cc: stable@vger.kernel.org
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>

> ---
> drivers/gpu/drm/bridge/ti-sn65dsi83.c | 7 ++++---
> 1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
> index d2a81175d279..17a885244e1e 100644
> --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
> +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
> @@ -517,6 +517,7 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
> struct drm_atomic_state *state)
> {
> struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
> + const unsigned int dual_factor = ctx->lvds_dual_link ? 2 : 1;
> const struct drm_bridge_state *bridge_state;
> const struct drm_crtc_state *crtc_state;
> const struct drm_display_mode *mode;
> @@ -653,18 +654,18 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
> /* 32 + 1 pixel clock to ensure proper operation */
> le16val = cpu_to_le16(32 + 1);
> regmap_bulk_write(ctx->regmap, REG_VID_CHA_SYNC_DELAY_LOW, &le16val, 2);
> - le16val = cpu_to_le16(mode->hsync_end - mode->hsync_start);
> + le16val = cpu_to_le16((mode->hsync_end - mode->hsync_start) / dual_factor);
> regmap_bulk_write(ctx->regmap, REG_VID_CHA_HSYNC_PULSE_WIDTH_LOW,
> &le16val, 2);
> le16val = cpu_to_le16(mode->vsync_end - mode->vsync_start);
> regmap_bulk_write(ctx->regmap, REG_VID_CHA_VSYNC_PULSE_WIDTH_LOW,
> &le16val, 2);
> regmap_write(ctx->regmap, REG_VID_CHA_HORIZONTAL_BACK_PORCH,
> - mode->htotal - mode->hsync_end);
> + (mode->htotal - mode->hsync_end) / dual_factor);
> regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_BACK_PORCH,
> mode->vtotal - mode->vsync_end);
> regmap_write(ctx->regmap, REG_VID_CHA_HORIZONTAL_FRONT_PORCH,
> - mode->hsync_start - mode->hdisplay);
> + (mode->hsync_start - mode->hdisplay) / dual_factor);
> regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_FRONT_PORCH,
> mode->vsync_start - mode->vdisplay);
> regmap_write(ctx->regmap, REG_VID_CHA_TEST_PATTERN, 0x00);
>