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]>
---
 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