Re: [PATCH] drm/bridge: ti-sn65dsi86: Fix ti_sn_bridge_set_dsi_rate function
From: Doug Anderson
Date: Mon Apr 08 2024 - 05:03:47 EST
Hi,
On Mon, Apr 8, 2024 at 12:36 AM Jayesh Choudhary <j-choudhary@xxxxxx> wrote:
>
> Due to integer calculations, the rounding off can cause errors in the final
> value propagated in the registers.
> Considering the example of 1080p (very common resolution), the mode->clock
> is 148500, dsi->lanes = 4, and bpp = 24, with the previous logic, the DSI
> clock frequency would come as 444 when we are expecting the value 445.5
> which would reflect in SN_DSIA_CLK_FREQ_REG.
> So move the division to be the last operation where rounding off will not
> impact the register value.
Given that this driver is used on a whole pile of shipping Chromebooks
and those devices have been working just fine (with 1080p resolution)
for years, I'm curious how you noticed this. Was it actually causing
real problems for you, or did you notice it just from code inspection?
You should include this information in the commit message.
I'm travelling for the next two weeks so I can't actually check on a
device to see if your patch makes any difference on hardware I have,
but I'd presume that things were working "well enough" with the old
value and they'll still work with the new value?
> Also according to the SN65DSI86 datasheet[0], the minimum value for that
> reg is 0x08 (inclusive) and the maximum value is 0x97 (exclusive). So add
> check for that.
Maybe the range checking should be a separate patch?
> [0]: <https://www.ti.com/lit/gpn/sn65dsi86>
>
> Fixes: ca1b885cbe9e ("drm/bridge: ti-sn65dsi86: Split the setting of the dp and dsi rates")
Are you sure that's the commit you're fixing? In the commit text of
that commit I wrote that it was supposed to "have zero functional
change". Looking back at the change I still believe it had zero
functional change. The rounding error looks like it predates the
patch.
As far as I can tell the rounding error has been there since commit
a095f15c00e2 ("drm/bridge: add support for sn65dsi86 bridge driver").
> Signed-off-by: Jayesh Choudhary <j-choudhary@xxxxxx>
It's great to see a TI engineer contributing to this driver! Awesome!
> ---
> drivers/gpu/drm/bridge/ti-sn65dsi86.c | 48 +++++++++++++++++++++------
> 1 file changed, 37 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> index 84698a0b27a8..f9cf6b14d85e 100644
> --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> @@ -111,7 +111,14 @@
> #define AUX_IRQ_STATUS_AUX_SHORT BIT(5)
> #define AUX_IRQ_STATUS_NAT_I2C_FAIL BIT(6)
>
> -#define MIN_DSI_CLK_FREQ_MHZ 40
> +/*
> + * NOTE: DSI clock frequency range: [40MHz,755MHz)
> + * DSI clock frequency range is in 5-MHz increments
> + * So minimum frequency 40MHz translates to 0x08
> + * And maximum frequency 755MHz translates to 0x97
> + */
> +#define MIN_DSI_CLK_RANGE 0x8
> +#define MAX_DSI_CLK_RANGE 0x97
It's a little weird to call this min/max and have one be inclusive and
one be exclusive. Be consistent and say that this is the minimum legal
value and the maximum legal value. I think that means the MAX should
be 0x96.
> /* fudge factor required to account for 8b/10b encoding */
> #define DP_CLK_FUDGE_NUM 10
> @@ -820,22 +827,37 @@ static void ti_sn_bridge_atomic_disable(struct drm_bridge *bridge,
> regmap_update_bits(pdata->regmap, SN_ENH_FRAME_REG, VSTREAM_ENABLE, 0);
> }
>
> -static void ti_sn_bridge_set_dsi_rate(struct ti_sn65dsi86 *pdata)
> +static int ti_sn_bridge_set_dsi_rate(struct ti_sn65dsi86 *pdata)
> {
> - unsigned int bit_rate_mhz, clk_freq_mhz;
> + unsigned int bit_rate_khz;
> unsigned int val;
> struct drm_display_mode *mode =
> &pdata->bridge.encoder->crtc->state->adjusted_mode;
>
> - /* set DSIA clk frequency */
> - bit_rate_mhz = (mode->clock / 1000) *
> - mipi_dsi_pixel_format_to_bpp(pdata->dsi->format);
> - clk_freq_mhz = bit_rate_mhz / (pdata->dsi->lanes * 2);
> + /*
> + * Set DSIA clk frequency
> + * Maximum supported value of bit_rate_khz turns out to be
> + * 6040000 which can be put in 32-bit variable so no overflow
> + * possible in this calculation.
The way you've written this comment makes me worried. You're saying
that the only supported result of the math has to fit in 32-bits so
we're OK. ...and then _after_ you do the math you check to see if the
clock rate is within the supported range. It makes me feel like you
could still overflow.
I think your comment here should say something like:
The maximum value returned by mipi_dsi_pixel_format_to_bpp() is 24.
That means that as long as "mode->clock" is less than 178,956,971 kHz
then the calculation can't overflow and can fit in 32-bits.
If you wanted to be really good you could even put a check earlier in
the function to make sure that mode->clock wasn't something totally
crazy (could confirm it's < 100GHz maybe?) so you absolutely knew it
couldn't overflow.
> + */
> + bit_rate_khz = mode->clock *
> + mipi_dsi_pixel_format_to_bpp(pdata->dsi->format);
> +
> + /*
> + * For each increment in val, frequency increases by 5MHz
> + * and the factor of 1000 comes from kHz to MHz conversion
> + */
> + val = (bit_rate_khz / (pdata->dsi->lanes * 2 * 1000 * 5)) & 0xFF;
> +
> + if (val >= MAX_DSI_CLK_RANGE || val < MIN_DSI_CLK_RANGE) {
> + drm_err(pdata->bridge.dev,
> + "DSI clock frequency not in the supported range\n");
> + return -EINVAL;
> + }
Shouldn't the above be in atomic_check()? There's a reason why
atomic_enable() can't return error codes.
-Doug