Hi Simon, Thanks for the detailed bug report!
I submitted a while ago a patch series [1] providing clock fixes (among others) to samsung-hdptx PHY driver, but sadly it hasn't been merged yet. I think this should also address your audio issue, hence please give it a try and let me know how it goes. Regards, Cristian [1] https://lore.kernel.org/all/[email protected]/ On 5/5/26 11:18 AM, Simon Wright wrote: > [Resent in plain text — first attempt was rejected by the list's HTML filter.] > > Hi, > > I have tracked down a bug in dw-hdmi-qp that silences audio on strict > HDMI 2.1 sinks (tested: LG G3 OLED) when booting with HDMI seamless > handoff from U-Boot. HDMI 2.0 sinks are unaffected. The root cause is > a mismatch between the AVI InfoFrame colour depth and the ACR embedded > CTS value. > > Hardware tested: > Source: ArmSoM Sige5 (Rockchip RK3576) > Armbian-edge kernel 7.0.2 (Linux mainline v7.0 plus > Armbian board-enable patches; not a BSP kernel) > Failing sink: LG G3 OLED 4K (HDMI 2.1, strict embedded-CTS mode) > Passing sink: Various HDMI 2.0 TVs (self-measuring CTS, unaffected) > > The bug is structural and is expected to affect RK3588 under the same > boot sequence. > > > Root cause > ---------- > > When the system boots with seamless handoff active, U-Boot has configured > the PHY for 8-bit 1080p60 (TMDS = 148,500,000 Hz). The DRM connector > state, negotiated from EDID, selects 10-bit deep colour (TMDS = > 185,625,000 Hz). In dw_hdmi_qp_bridge_atomic_enable(): > > hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate; > > This stores 185,625,000. The PHY ops->init() is called but the PHY is > already running at 148,500,000 from U-Boot and is not reconfigured. > > The result is an inconsistency between what the sink is told over HDMI > and what the wire actually carries: > > AVI InfoFrame: declares 10-bit deep colour at the connector-state > TMDS rate (185.625 MHz) > ACR embedded CTS: driver uses hdmi->tmds_char_rate = 185,625,000 Hz > to compute CTS via dw_hdmi_qp_set_sample_rate(), > producing CTS = 185,625 (internally consistent > with the AVI declaration) > Wire TMDS clock: PHY untouched from U-Boot, still running at > 148,500,000 Hz (8-bit) > > The dmesg signature (full pr_err output, lower in this report) confirms > both AVI and ACR are computed from the nominal 185.625 MHz value: > "tmds=185625000 ... cts=185625". > > A standard HDMI 2.0 sink does not cross-check AVI against the wire TMDS > in any way that matters for audio, so the mismatch is harmless. A strict > HDMI 2.1 sink (LG G3) measures the wire TMDS and verifies it matches the > AVI declaration; with declared 185.625 MHz vs measured 148.5 MHz, the > check fails and audio is muted, permanently, until the next mode set. > > The N/CTS values themselves are correct CEA-861 ratios. At a wire TMDS > of 148.5 MHz with N=6144, CTS=148,500 yields f_audio = 48 kHz; at a > wire TMDS of 185.625 MHz with N=6144, CTS=185,625 also yields f_audio > = 48 kHz. The driver picks one TMDS, computes a self-consistent N/CTS > pair, and embeds it. The bug is solely that the TMDS the driver picks > (connector-state nominal) differs from the TMDS on the wire (PHY > actual). > > > Dmesg diagnostic signature > -------------------------- > > Failing (seamless handoff, DRM selects 10-bit 1080p from EDID): > > dwhdmiqp-rockchip 27da0000.hdmi: atomic_enable: \ > tmds_candidate=185625000 is_hdmi=1 > DW_HDMI_QP set_sample_rate: tmds=185625000 sr=48000 n=6144 cts=185625 > [audio silent on LG G3] > > Working (8-bit mode forced, see workaround below): > > dwhdmiqp-rockchip 27da0000.hdmi: atomic_enable: \ > tmds_candidate=148500000 is_hdmi=1 > DW_HDMI_QP set_sample_rate: tmds=148500000 sr=48000 n=6144 cts=148500 > [audio working on LG G3] > > The difference is entirely in which tmds_char_rate bridge_atomic_enable() > receives. If the PHY and DRM state agree (both 148.5 MHz), audio works > because what the AVI declares matches what the wire carries. If they > disagree (DRM: 185.625 MHz, PHY: 148.5 MHz), the AVI declares 185.625 > but the wire carries 148.5, and any sink that cross-checks the wire > TMDS against the declared rate (e.g. LG G3 OLED) mutes audio. > > > Confirmed workaround > -------------------- > > In dw_hdmi_qp_bridge_tmds_char_rate_valid(), reject 185,625,000 Hz: > > if (rate == 185625000) > return MODE_CLOCK_HIGH; > > This forces DRM to select 8-bit mode (148,500,000 Hz). bridge_atomic_enable() > then stores the correct tmds_char_rate, the AVI InfoFrame declares 8-bit, > the ACR CTS is computed correctly, and the LG G3 produces audio. > > Note: 185,625,000 Hz (1080p@60 10-bit) is only selected via EDID > negotiation during seamless handoff. A genuine modeset at that rate would > correctly reconfigure the PHY and the AVI/ACR inconsistency would not > arise. Blocking this rate in tmds_char_rate_valid() therefore has no > correct-use-case cost on RK3576 with the current BSP. 4K@60Hz (594 MHz) > is unaffected and continues to work correctly, including audio. > > > Proposed proper fix > ------------------- > > The fundamental issue is that bridge_atomic_enable() accepts > conn_state->hdmi.tmds_char_rate without verifying it against the PHY's > actual output frequency. > > Option A (no display disruption): > > The HDPTX PHY PLL is already registered as a clock provider. On > RK3576, rk3576.dtsi gives the hdptxphy node #clock-cells = <0>; > VOP2 consumes it as "pll_hdmiphy0". If the bridge driver adds a > new clk pointer (e.g. hdmi->tmds_clk) populated via > devm_clk_get_optional() at probe and queries clk_get_rate() > in bridge_atomic_enable(), it can detect the seamless-handoff > mismatch and use the actual rate: > > /* hdmi->tmds_clk added in struct dw_hdmi_qp; populated at > * probe via devm_clk_get_optional(dev, "tmds") or similar */ > actual_rate = hdmi->tmds_clk ? clk_get_rate(hdmi->tmds_clk) : 0; > if (actual_rate && actual_rate != conn_state->hdmi.tmds_char_rate) { > dev_warn(hdmi->dev, > "seamless handoff: PHY at %lu Hz, DRM selected %llu Hz; " > "using actual PHY rate for audio\n", > actual_rate, conn_state->hdmi.tmds_char_rate); > hdmi->tmds_char_rate = actual_rate; > } else { > hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate; > } > > This corrects the ACR CTS and AVI InfoFrame without touching the > PHY configuration or causing a display blank. > > Option B (correct but causes brief blank): > > If the driver detects a mismatch, call phy.ops->init() to bring > the PHY into agreement with the DRM-negotiated state. All > parameters become consistent at the cost of a single mode change > on first enable after seamless handoff. This is arguably the more > correct approach if the intent is to honour what the EDID reports. > > Option A is preferred for a seamless user experience. Option B is > preferable if the seamless-handoff-inherited U-Boot mode is not > otherwise suitable for production use. > > > Relation to Collabora's HDMI 2.0 patch series > ---------------------------------------------- > > The "Add HDMI 2.0 support to DW HDMI QP TX" series (v3, January 2026) > addresses high-TMDS-clock-ratio and scrambling. It does not appear to > address the seamless-handoff tmds_char_rate mismatch described here. > If a revision of that series reads PHY state at probe time it may > incidentally close this gap, but that is not a stated goal. Reviewers > may wish to verify the seamless-handoff path is exercised in testing. > > > Summary > ------- > > Bug: bridge_atomic_enable() takes EDID-negotiated > tmds_char_rate (185,625,000) while PHY runs at U-Boot > rate (148,500,000); the AVI InfoFrame and the ACR CTS > both reflect the connector-state nominal value, but > the wire TMDS clock disagrees with what AVI declares > Symptom: audio silent on any HDMI 2.1 sink that cross-checks > wire TMDS against the AVI declaration; HDMI 2.0 sinks > unaffected (no such cross-check) > Scope: RK3576 confirmed; RK3588 structurally identical > Not a bug: N/CTS table, IEC60958 channel status, ARC, ALSA path > Workaround: reject 185,625,000 Hz in tmds_char_rate_valid() so > DRM picks 8-bit 1080p (148,500,000 Hz) which matches > the actual PHY rate > Fix: compare conn_state->hdmi.tmds_char_rate against actual > PHY clock at bridge_atomic_enable() and use the > measured rate when they disagree (Option A) or > reconfigure the PHY (Option B) > > I am happy to test patches on Sige5 + LG G3. > > Regards, > Simon > Symple Solutions, Dunedin, New Zealand
