From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 44063CD6E4C for ; Mon, 1 Jun 2026 04:07:00 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Transfer-Encoding: Content-Type:In-Reply-To:From:References:Cc:To:Subject:MIME-Version:Date: Message-ID:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=Tga5971EEa630O6n+PhHzaOHaTe0vvPmQiFQjog/n0Q=; b=u7nWqJ8SLu20IsOreq74zuCgHv eaYppprGSwJM5z6reYE+ITmZ39fvXUIweQOQquYLr9IlUxU245XRD3fbmZlJQw9Zj6Ah8W5SAvVwG zrl0bYtmnQjSHpASYwahySjK6tarx2hJlMGgT6nE4NztBB9sxHmcl4m50P0f9FYMg3l9aOHKY4y4F Syp53JwvR4VfwdbFvu1mZNzhnjKaXlYsKuTAuyTdM5RaeTfK077crA0zHmgfbNwFQyVhulu4t1Ozg b+NBTMUywDcrMSk55qgb0PdtTkax33I2/uN1Hft3TarWqDHcPRdsJ+Mhb3U0H/FayC4LkVNH/CZZ8 Zw9X/kTA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.99.1 #2 (Red Hat Linux)) id 1wTtvN-0000000A7AZ-17ne; Mon, 01 Jun 2026 04:06:49 +0000 Received: from mail-m254108.xmail.ntesmail.com ([103.129.254.108]) by bombadil.infradead.org with esmtps (Exim 4.99.1 #2 (Red Hat Linux)) id 1wTtvI-0000000A79q-1FQm; Mon, 01 Jun 2026 04:06:48 +0000 Received: from [172.16.12.77] (unknown [61.154.14.86]) by smtp.qiye.163.com (Hmail) with ESMTP id 40886331e; Mon, 1 Jun 2026 12:06:37 +0800 (GMT+08:00) Message-ID: <9a7855a7-6b0c-4aa2-8516-13d94beabac6@rock-chips.com> Date: Mon, 1 Jun 2026 12:06:37 +0800 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v5 05/10] drm/bridge: dw-hdmi-qp: Add HDMI 2.0 SCDC scrambling and high TMDS clock ratio support To: Cristian Ciocaltea , Andrzej Hajda , Neil Armstrong , Robert Foss , Laurent Pinchart , Jonas Karlman , Jernej Skrabec , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Sandy Huang , =?UTF-8?Q?Heiko_St=C3=BCbner?= Cc: kernel@collabora.com, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, Diederik de Haas , Maud Spierings References: <20260426-dw-hdmi-qp-scramb-v5-0-d778e70c317b@collabora.com> <20260426-dw-hdmi-qp-scramb-v5-5-d778e70c317b@collabora.com> Content-Language: en-US From: Andy Yan In-Reply-To: <20260426-dw-hdmi-qp-scramb-v5-5-d778e70c317b@collabora.com> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-HM-Tid: 0a9e815cb66b09d8kunm1304045b34b196 X-HM-MType: 1 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFITzdXWRgWCB1ZQUpXWS1ZQUlXWQ8JGhUIEh9ZQVkaSxlMVkxCSBpDQ00fGklJTVYVFA kWGhdVEwETFhoSFyQUDg9ZV1kYEgtZQVlNSlVKTk9VSk9VQ01ZV1kWGg8SFR0UWUFZT0tIVUpLSU 9PT0hVSktLVUpCS0tZBg++ DKIM-Signature: a=rsa-sha256; b=QQov8Rb4dnNQBMbsVmDBFtU6l/xXf8ishvwt3UTIx9DJPlxOJx45/2rfVpGoJdwjJWBhUBPxCEnWzmLOsMJ+jqHutehOIcywAkJRcAQ5HrBDkNRgzoblh8R29PrLlVnlyl0hzZXEXpU8m87T67X+NrBlw6OKqZnnqlIiXgUMvaQ=; c=relaxed/relaxed; s=default; d=rock-chips.com; v=1; bh=Tga5971EEa630O6n+PhHzaOHaTe0vvPmQiFQjog/n0Q=; h=date:mime-version:subject:message-id:from; X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.9.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20260531_210645_825556_D602AF60 X-CRM114-Status: GOOD ( 27.75 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Hello, On 4/26/26 08:20, Cristian Ciocaltea wrote: > Enable HDMI 2.0 display modes (e.g. 4K@60Hz) by adding SCDC management > for the high TMDS clock ratio and scrambling, required when the TMDS > character rate exceeds the 340 MHz HDMI 1.4b limit. > > A periodic work item monitors the sink's scrambling status to recover > from sink-side resets. On hotplug detect, if SCDC scrambling state is > out of sync with the driver, trigger a CRTC reset to re-establish the > link. > > Reject modes requiring TMDS rates above 600 MHz, as those fall in the > HDMI 2.1 FRL domain which is not supported. In no_hpd configurations, > further restrict to 340 MHz since SCDC requires a connected sink. > > Tested-by: Diederik de Haas > Tested-by: Maud Spierings > Signed-off-by: Cristian Ciocaltea > --- > drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 188 ++++++++++++++++++++++++--- > 1 file changed, 172 insertions(+), 16 deletions(-) > > diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c > index d649a1cf07f5..c482a8e7da25 100644 > --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c > +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c > @@ -2,6 +2,7 @@ > /* > * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd. > * Copyright (c) 2024 Collabora Ltd. > + * Copyright (c) 2025 Amazon.com, Inc. or its affiliates. > * > * Author: Algea Cao > * Author: Cristian Ciocaltea > @@ -21,9 +22,11 @@ > #include > #include > #include > +#include > #include > #include > #include > +#include > #include > #include > #include > @@ -39,7 +42,9 @@ > #define DDC_SEGMENT_ADDR 0x30 > > #define HDMI14_MAX_TMDSCLK 340000000 > +#define HDMI20_MAX_TMDSRATE 600000000 > > +#define SCDC_MAX_SOURCE_VERSION 0x1 > #define SCRAMB_POLL_DELAY_MS 3000 > > /* > @@ -164,6 +169,11 @@ struct dw_hdmi_qp { > } phy; > > unsigned long ref_clk_rate; > + > + struct drm_connector *curr_conn; > + struct delayed_work scramb_work; > + bool scramb_enabled; > + > struct regmap *regm; > int main_irq; > > @@ -749,28 +759,124 @@ static struct i2c_adapter *dw_hdmi_qp_i2c_adapter(struct dw_hdmi_qp *hdmi) > return adap; > } > > +static bool dw_hdmi_qp_supports_scrambling(struct drm_display_info *display) > +{ > + if (!display->is_hdmi) > + return false; > + > + return display->hdmi.scdc.supported && > + display->hdmi.scdc.scrambling.supported; > +} > + > +static int dw_hdmi_qp_set_scramb(struct dw_hdmi_qp *hdmi) > +{ > + bool done; > + > + dev_dbg(hdmi->dev, "set scrambling\n"); > + > + done = drm_scdc_set_high_tmds_clock_ratio(hdmi->curr_conn, true); > + if (!done) > + return -EIO; > + > + done = drm_scdc_set_scrambling(hdmi->curr_conn, true); > + if (!done) { > + drm_scdc_set_high_tmds_clock_ratio(hdmi->curr_conn, false); > + return -EIO; > + } > + > + schedule_delayed_work(&hdmi->scramb_work, > + msecs_to_jiffies(SCRAMB_POLL_DELAY_MS)); > + return 0; > +} > + > +static void dw_hdmi_qp_scramb_work(struct work_struct *work) > +{ > + struct dw_hdmi_qp *hdmi = container_of(to_delayed_work(work), > + struct dw_hdmi_qp, > + scramb_work); > + if (READ_ONCE(hdmi->scramb_enabled) && > + !drm_scdc_get_scrambling_status(hdmi->curr_conn)) > + dw_hdmi_qp_set_scramb(hdmi); > +} > + > +static void dw_hdmi_qp_enable_scramb(struct dw_hdmi_qp *hdmi) > +{ > + int ret; > + u8 ver; > + > + if (!dw_hdmi_qp_supports_scrambling(&hdmi->curr_conn->display_info)) > + return; > + > + ret = drm_scdc_readb(hdmi->bridge.ddc, SCDC_SINK_VERSION, &ver); > + if (ret) { > + dev_err(hdmi->dev, "Failed to read SCDC_SINK_VERSION: %d\n", ret); > + return; > + } > + > + ret = drm_scdc_writeb(hdmi->bridge.ddc, SCDC_SOURCE_VERSION, > + min_t(u8, ver, SCDC_MAX_SOURCE_VERSION)); > + if (ret) { > + dev_err(hdmi->dev, "Failed to write SCDC_SOURCE_VERSION: %d\n", ret); > + return; > + } > + > + WRITE_ONCE(hdmi->scramb_enabled, true); > + > + ret = dw_hdmi_qp_set_scramb(hdmi); > + if (ret) { > + hdmi->scramb_enabled = false; > + return; > + } > + > + dw_hdmi_qp_write(hdmi, 1, SCRAMB_CONFIG0); > + > + /* Wait at least 1 ms before resuming TMDS transmission */ > + usleep_range(1000, 5000); > +} > + > +static void dw_hdmi_qp_disable_scramb(struct dw_hdmi_qp *hdmi) > +{ > + if (!hdmi->scramb_enabled) > + return; > + > + dev_dbg(hdmi->dev, "disable scrambling\n"); > + > + WRITE_ONCE(hdmi->scramb_enabled, false); > + cancel_delayed_work_sync(&hdmi->scramb_work); > + > + dw_hdmi_qp_write(hdmi, 0, SCRAMB_CONFIG0); > + > + if (hdmi->curr_conn->status == connector_status_connected) { > + drm_scdc_set_scrambling(hdmi->curr_conn, false); > + drm_scdc_set_high_tmds_clock_ratio(hdmi->curr_conn, false); > + } > +} > + > static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, > struct drm_atomic_state *state) > { > struct dw_hdmi_qp *hdmi = bridge->driver_private; > struct drm_connector_state *conn_state; > - struct drm_connector *connector; > unsigned int op_mode; > > - connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); > - if (WARN_ON(!connector)) > + hdmi->curr_conn = drm_atomic_get_new_connector_for_encoder(state, > + bridge->encoder); > + if (WARN_ON(!hdmi->curr_conn)) > return; > > - conn_state = drm_atomic_get_new_connector_state(state, connector); > + conn_state = drm_atomic_get_new_connector_state(state, hdmi->curr_conn); > if (WARN_ON(!conn_state)) > return; > > - if (connector->display_info.is_hdmi) { > + if (hdmi->curr_conn->display_info.is_hdmi) { > dev_dbg(hdmi->dev, "%s mode=HDMI %s rate=%llu bpc=%u\n", __func__, > drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format), > conn_state->hdmi.tmds_char_rate, conn_state->hdmi.output_bpc); > op_mode = 0; > hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate; > + > + if (conn_state->hdmi.tmds_char_rate > HDMI14_MAX_TMDSCLK) > + dw_hdmi_qp_enable_scramb(hdmi); > } else { > dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__); > op_mode = OPMODE_DVI; > @@ -781,7 +887,7 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, > dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); > dw_hdmi_qp_mod(hdmi, op_mode, OPMODE_DVI, LINK_CONFIG0); > > - drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); > + drm_atomic_helper_connector_hdmi_update_infoframes(hdmi->curr_conn, state); > } > > static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, > @@ -791,14 +897,49 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, > > hdmi->tmds_char_rate = 0; > > + dw_hdmi_qp_disable_scramb(hdmi); > + > + hdmi->curr_conn = NULL; > hdmi->phy.ops->disable(hdmi, hdmi->phy.data); > } > > -static enum drm_connector_status > -dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector) > +static int dw_hdmi_qp_reset_crtc(struct dw_hdmi_qp *hdmi, > + struct drm_connector *connector, > + struct drm_modeset_acquire_ctx *ctx) > +{ > + u8 config; > + int ret; > + > + ret = drm_scdc_readb(hdmi->bridge.ddc, SCDC_TMDS_CONFIG, &config); > + if (ret < 0) { > + dev_err(hdmi->dev, "Failed to read TMDS config: %d\n", ret); > + return ret; > + } > + > + if (!!(config & SCDC_SCRAMBLING_ENABLE) == hdmi->scramb_enabled) > + return 0; > + > + drm_atomic_helper_connector_hdmi_hotplug(connector, > + connector_status_connected); > + /* > + * Conform to HDMI 2.0 spec by ensuring scrambled data is not sent > + * before configuring the sink scrambling, as well as suspending any > + * TMDS transmission while changing the TMDS clock rate in the sink. > + */ > + > + dev_dbg(hdmi->dev, "resetting crtc\n"); > + > + return drm_bridge_helper_reset_crtc(&hdmi->bridge, ctx); > +} > + > +static int dw_hdmi_qp_bridge_detect_ctx(struct drm_bridge *bridge, > + struct drm_connector *connector, > + struct drm_modeset_acquire_ctx *ctx) > { > struct dw_hdmi_qp *hdmi = bridge->driver_private; > + enum drm_connector_status status; > const struct drm_edid *drm_edid; > + int ret; > > if (hdmi->no_hpd) { > drm_edid = drm_edid_read_ddc(connector, bridge->ddc); > @@ -808,7 +949,20 @@ dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connec > return connector_status_disconnected; > } > > - return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); > + status = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); > + > + dev_dbg(hdmi->dev, "%s status=%d scramb=%d\n", __func__, > + status, hdmi->scramb_enabled); > + > + if (status == connector_status_connected && hdmi->scramb_enabled) { > + ret = dw_hdmi_qp_reset_crtc(hdmi, connector, ctx); > + if (ret == -EDEADLK) > + return ret; > + if (ret < 0) > + status = connector_status_unknown; > + } > + > + return status; > } > > static const struct drm_edid * > @@ -832,12 +986,12 @@ dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge, > { > struct dw_hdmi_qp *hdmi = bridge->driver_private; > > - /* > - * TODO: when hdmi->no_hpd is 1 we must not support modes that > - * require scrambling, including every mode with a clock above > - * HDMI14_MAX_TMDSCLK. > - */ > - if (rate > HDMI14_MAX_TMDSCLK) { The locally defined HDMI14_MAX_TMDSCLK has been removed, we should now use HDMI_1_3_CHAR_RATE_MAX, and I think we should apply the same change to HDMI20_MAX_TMDSRATE > + if (hdmi->no_hpd && rate > HDMI14_MAX_TMDSCLK) { > + dev_dbg(hdmi->dev, "Unsupported TMDS char rate in no_hpd mode: %lld\n", rate); > + return MODE_CLOCK_HIGH; > + } > + > + if (rate > HDMI20_MAX_TMDSRATE) { > dev_dbg(hdmi->dev, "Unsupported TMDS char rate: %lld\n", rate); > return MODE_CLOCK_HIGH; > } > @@ -1197,7 +1351,7 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = { > .atomic_reset = drm_atomic_helper_bridge_reset, > .atomic_enable = dw_hdmi_qp_bridge_atomic_enable, > .atomic_disable = dw_hdmi_qp_bridge_atomic_disable, > - .detect = dw_hdmi_qp_bridge_detect, > + .detect_ctx = dw_hdmi_qp_bridge_detect_ctx, > .edid_read = dw_hdmi_qp_bridge_edid_read, > .hdmi_tmds_char_rate_valid = dw_hdmi_qp_bridge_tmds_char_rate_valid, > .hdmi_clear_avi_infoframe = dw_hdmi_qp_bridge_clear_avi_infoframe, > @@ -1287,6 +1441,8 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, > if (IS_ERR(hdmi)) > return ERR_CAST(hdmi); > > + INIT_DELAYED_WORK(&hdmi->scramb_work, dw_hdmi_qp_scramb_work); > + > hdmi->dev = dev; > > regs = devm_platform_ioremap_resource(pdev, 0); >