[PATCH] drm/bridge: ti-sn65dsi83: Add and use hs_rate and lp_rate

Marek Vasut marex at denx.de
Mon Sep 19 18:17:11 UTC 2022


On 9/19/22 15:43, Maxime Ripard wrote:
> Hi,

Hello Maxime,

> On Sun, Sep 18, 2022 at 02:56:00PM +0200, Marek Vasut wrote:
>> On 8/1/22 15:11, Marek Vasut wrote:
>>> Fill in hs_rate and lp_rate to struct mipi_dsi_device for this bridge and
>>> adjust DSI input frequency calculations such that they expect the DSI host
>>> to configure HS clock according to hs_rate.
>>>
>>> This is an optimization for the DSI burst mode case. In case the DSI device
>>> supports DSI burst mode, it is recommended to operate the DSI interface at
>>> the highest possible HS clock frequency which the DSI device supports. This
>>> permits the DSI host to send as short as possible bursts of data on the DSI
>>> link and keep the DSI data lanes in LP mode otherwise, which reduces power
>>> consumption.
>>
>>> Signed-off-by: Marek Vasut <marex at denx.de>
>>> Cc: Jagan Teki <jagan at amarulasolutions.com>
>>> Cc: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
>>> Cc: Linus Walleij <linus.walleij at linaro.org>
>>> Cc: Robert Foss <robert.foss at linaro.org>
>>> Cc: Sam Ravnborg <sam at ravnborg.org>
>>> Cc: dri-devel at lists.freedesktop.org
>>> ---
>>>    drivers/gpu/drm/bridge/ti-sn65dsi83.c | 25 +++++++++++++------------
>>>    1 file changed, 13 insertions(+), 12 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
>>> index 14e7aa77e7584..b161f25c3a2f5 100644
>>> --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c
>>> +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c
>>> @@ -286,8 +286,7 @@ static u8 sn65dsi83_get_lvds_range(struct sn65dsi83 *ctx,
>>>    	return (mode_clock - 12500) / 25000;
>>>    }
>>> -static u8 sn65dsi83_get_dsi_range(struct sn65dsi83 *ctx,
>>> -				  const struct drm_display_mode *mode)
>>> +static u8 sn65dsi83_get_dsi_range(struct sn65dsi83 *ctx)
>>>    {
>>>    	/*
>>>    	 * The encoding of the CHA_DSI_CLK_RANGE is as follows:
>>> @@ -303,20 +302,20 @@ static u8 sn65dsi83_get_dsi_range(struct sn65dsi83 *ctx,
>>>    	 *  DSI_CLK = mode clock * bpp / dsi_data_lanes / 2
>>>    	 * the 2 is there because the bus is DDR.
>>>    	 */
>>> -	return DIV_ROUND_UP(clamp((unsigned int)mode->clock *
>>> -			    mipi_dsi_pixel_format_to_bpp(ctx->dsi->format) /
>>> -			    ctx->dsi->lanes / 2, 40000U, 500000U), 5000U);
>>> +	return DIV_ROUND_UP(ctx->dsi->hs_rate, 5000000U);
>>>    }
>>> -static u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx)
>>> +static u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx,
>>> +				const struct drm_display_mode *mode)
>>>    {
>>>    	/* The divider is (DSI_CLK / LVDS_CLK) - 1, which really is: */
>>> -	unsigned int dsi_div = mipi_dsi_pixel_format_to_bpp(ctx->dsi->format);
>>> +	unsigned int dsi_div;
>>> +	int mode_clock = mode->clock;
>>> -	dsi_div /= ctx->dsi->lanes;
>>> +	if (ctx->lvds_dual_link)
>>> +		mode_clock /= 2;
>>> -	if (!ctx->lvds_dual_link)
>>> -		dsi_div /= 2;
>>> +	dsi_div = (ctx->dsi->hs_rate / mode_clock) / 1000;
>>>    	return dsi_div - 1;
>>>    }
>>> @@ -397,9 +396,9 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
>>>    		     REG_RC_LVDS_PLL_LVDS_CLK_RANGE(sn65dsi83_get_lvds_range(ctx, mode)) |
>>>    		     REG_RC_LVDS_PLL_HS_CLK_SRC_DPHY);
>>>    	regmap_write(ctx->regmap, REG_DSI_CLK,
>>> -		     REG_DSI_CLK_CHA_DSI_CLK_RANGE(sn65dsi83_get_dsi_range(ctx, mode)));
>>> +		     REG_DSI_CLK_CHA_DSI_CLK_RANGE(sn65dsi83_get_dsi_range(ctx)));
>>>    	regmap_write(ctx->regmap, REG_RC_DSI_CLK,
>>> -		     REG_RC_DSI_CLK_DSI_CLK_DIVIDER(sn65dsi83_get_dsi_div(ctx)));
>>> +		     REG_RC_DSI_CLK_DSI_CLK_DIVIDER(sn65dsi83_get_dsi_div(ctx, mode)));
>>>    	/* Set number of DSI lanes and LVDS link config. */
>>>    	regmap_write(ctx->regmap, REG_DSI_LANE,
>>> @@ -643,6 +642,8 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx)
>>>    	dsi->lanes = dsi_lanes;
>>>    	dsi->format = MIPI_DSI_FMT_RGB888;
>>>    	dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST;
>>> +	dsi->hs_rate = 500000000;
>>> +	dsi->lp_rate = 16000000;
> 
> Let's leave aside the comment from Dave that the host might choose a
> lower HS rate, we can indeed assume it's true for now.
> 
> However.. Is there any guarantee that the host can even reach that
> frequency in the first place? IIRC, the maximum rate a DSI host can
> reach is implementation specific. So I'm not sure this solution flies.
> 
> It's not clear to me from that patch what problem / issue it's supposed
> to solve in the first place, but it really looks similar to the
> discussion we had some time ago about that bridge that could only
> operate at a set of fixed frequencies.

The use of exact frequency on DSI HS clock for bridges which derive 
their PLL clock frequency from DSI HS clock discussion is separate a 
topic from this patch.

This patch is defining the maximum DSI HS clock frequency this bridge 
supports per datasheet (500 MHz) AND then assumes the DSI host would use 
that frequency on the DSI link (in burst mode, the highest frequency on 
the link permits the link to be in LP mode for longest time and thus be 
as power efficient as possible).

About the assumption -- currently the DSI HS clock frequency expected by 
the DSI bridge driver is calculated by its own driver-private formula ; 
the DSI HS clock frequency generated by the DSI host is calculated by 
the DSI host driver-private formula. Those two might not even match and 
thus each driver might come to different DSI HS clock they expect or 
generate. This cannot work reliably.

With this patch, it is at least possible for the DSI bridge driver to 
indicate what DSI HS clock it expects from the DSI host driver and what 
are its maximum DSI HS clock. The DSI host driver can check the nearest 
bridge flags (to verify the bridge uses burst mode) and hs_rate and 
configure itself accordingly. If the DSI host cannot generate suitable 
DSI HS clock which the bridge expects, it should likely refuse to bind 
with the bridge (also read on below how to deal with this). I consider 
this an improvement over current situation which is a total guesswork.

Regarding DSI hosts which cannot generate the specified hs_rate , they 
should likely just reject binding with the DSI bridge. However, there 
has been a valid point raised that the highest DSI HS clock supported by 
the DSI bridge might not always be the desired clock, e.g. in case EMI 
reduction is required by lowering the clock. I would propose to add a DT 
property which would allow limiting the maximum DSI HS clock frequency 
per DSI OF graph link in the DT, and a helper which would check the DT 
property, compare it with bridge limits, and set hs_rate accordingly.

This should work well for DSI burst mode links in general.

> You basically want to propagate the clock constraints along the bridge
> chain, and make sure every body is fine with that. The most reasonable
> would be to make it part of the bridge state, and possibly add a bunch
> of extra functions to query the upstream bridge clock output for a given
> state.

The bridge hardware constraints are static, just like the bridge 
hardware itself, so they shouldn't be part of the bridge state, should 
they ?

I agree we do need a way to implement frequency negotiation between DSI 
bridge and DSI host, but that's something which can be added on top of 
the actual bridge HW constraints, and yes, possibly by passing those 
dynamic constraints via bridge state.


More information about the dri-devel mailing list