On Wed, 14 Oct 2026 at 02:03, Cristian Ciocaltea
<[email protected]> wrote:
>
> The CEC clock divider computed in vc4_hdmi_cec_update_clk_div() is
> written unmasked into the 12-bit DIV_CLK_CNT field of the CEC_CNTRL_1
> register.  On BCM2835, the CEC clock is obtained by dividing the HSM
> clock, which in turn is scaled to 101% of the pixel clock.  For HSM
> clock rates above 163.8 MHz, the divider exceeds the field limit (4095),
> corrupting the adjacent CEC_ADDR field and truncating the effective
> divider, which breaks CEC entirely.
>
> The BCM2835 maximum pixel clock of 162 MHz yields an HSM clock of 163.62
> MHz, so the overflow cannot currently be triggered.
>
> Clamp the divider to the field maximum.  This is a prerequisite for
> raising the BCM2835 maximum pixel clock to 165 MHz, which pushes the HSM
> clock up to 166.65 MHz.  The CEC bit clock will then run up to ~1.75%
> faster than the nominal 40 kHz, i.e. ~40.7 kHz, which is still within
> the CEC bit-timing tolerances.
>
> Reported-by: Sashiko <[email protected]>
> Closes: https://lore.kernel.org/all/[email protected]/
> Signed-off-by: Cristian Ciocaltea <[email protected]>

Reviewed-by: Dave Stevenson <[email protected]>

> ---
>  drivers/gpu/drm/vc4/vc4_hdmi.c | 9 +++++++--
>  1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> index 2451ff85e759..0a95e14792e8 100644
> --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> @@ -231,9 +231,14 @@ static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi 
> *vc4_hdmi)
>
>         /*
>          * Set the clock divider: the hsm_clock rate and this divider
> -        * setting will give a 40 kHz CEC clock.
> +        * setting will give a 40 kHz CEC clock. Clamp the divider to
> +        * its 12-bit field to avoid corrupting CEC_ADDR; for HSM rates
> +        * above 163.8 MHz, the CEC clock runs marginally faster, but
> +        * still within CEC timing tolerances.
>          */
> -       clk_cnt = cec_rate / CEC_CLOCK_FREQ;
> +       clk_cnt = min_t(unsigned long, cec_rate / CEC_CLOCK_FREQ,
> +                       VC4_HDMI_CEC_DIV_CLK_CNT_MASK >>
> +                       VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT);
>         value |= clk_cnt << VC4_HDMI_CEC_DIV_CLK_CNT_SHIFT;
>         HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
>
>
> --
> 2.55.0
>

Reply via email to