From nobody Sun Feb 8 23:42:06 2026 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 65F192857EE for ; Mon, 12 Jan 2026 22:26:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768256799; cv=none; b=QyZ8XwTITHvbqj34HuenEPnZz8nzqaWNDEprwpxw9Qpr0zZCKfWOrBzXEm9O2ndhnAdPbAaKwf1rE9WRiiyo6AVGmu3EFx2QqAMNwGb2SHCfx3fP3MEtvrkCYmgyPzdBRi90wQlNaFUBXjQYhppKmzIzTvCpJ+q+ZBDzuVKfPjE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768256799; c=relaxed/simple; bh=BSp8s1EOO75dmJyosp8j2QYrOgKBYEekmYXUEjCJsHU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YJWrlLFQJH7l8z/AYdcUGhfZzxeNJm7IxMWo/7oBPbwYYyp7sR7lQt+RneR2rNsLy+v/vFW9J7eBXsFCMgXNK4W7e6aG7uCkY9Rr0JbAyxmEufPlwHvRCsBVnJ31egMgGP3uEQQE4YmV4xjFJSkv3GmItlDY4aTMhWP/YiuBbCA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=QEqGiGsI; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="QEqGiGsI" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1768256795; bh=BSp8s1EOO75dmJyosp8j2QYrOgKBYEekmYXUEjCJsHU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=QEqGiGsIgdhQcxFD4AwHM8eHkhY2bGjH9v5rJlaiO/Sc9gJblRYOlTllq/XBu8O+8 gWqqjCHJhMpPJgm1YAXSLjqWtgezq6AdKK8//HMvY0eColjUNcORJUorsyof6+oi5O RlPbXKR7/LWQ9MpJmE7gZwiho7UoIKJ9l1J6aTlWb17zCHUHsqyXuajVQkXBlLvNQf r3oxqk8Rix0fKRtjL5bKIB5u4sZvq5d8b2mQap1bV9Ufw+pfPZxCslTGsfICP3rNTN nN7dTxlMwrxNZAv8H2Ow7z0JKZy1ruf03UYyovvje6w5G9qWK8XbsCZPhyVb61LiUE KAlg++MxWiQMA== Received: from localhost (unknown [82.79.138.145]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (prime256v1) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: cristicc) by bali.collaboradmins.com (Postfix) with ESMTPSA id 5D92B17E1380; Mon, 12 Jan 2026 23:26:35 +0100 (CET) From: Cristian Ciocaltea Date: Tue, 13 Jan 2026 00:26:18 +0200 Subject: [PATCH v2 1/4] drm/bridge: Add ->detect_ctx hook and drm_bridge_detect_ctx() Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260113-dw-hdmi-qp-scramb-v2-1-ae7b2c58d24d@collabora.com> References: <20260113-dw-hdmi-qp-scramb-v2-0-ae7b2c58d24d@collabora.com> In-Reply-To: <20260113-dw-hdmi-qp-scramb-v2-0-ae7b2c58d24d@collabora.com> To: 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?= , Andy Yan 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 X-Mailer: b4 0.14.3 Add an atomic variant of the ->detect callback and a new helper to call the hook while passing an optional drm_modeset_acquire_ctx reference. When both ->detect_ctx and ->detect are defined, the latter is ignored. If acquire_ctx is unset, the function takes care of the locking, while also handling EDEADLK. Tested-by: Diederik de Haas Tested-by: Maud Spierings Signed-off-by: Cristian Ciocaltea --- drivers/gpu/drm/drm_bridge.c | 58 ++++++++++++++++++++++++++++++++++++++++= ++++ include/drm/drm_bridge.h | 30 +++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 6dcf8f6d3ecf..0ef12bf98011 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1344,6 +1344,64 @@ drm_bridge_detect(struct drm_bridge *bridge, struct = drm_connector *connector) } EXPORT_SYMBOL_GPL(drm_bridge_detect); =20 +/** + * drm_bridge_detect_ctx - check if anything is attached to the bridge out= put + * @bridge: bridge control structure + * @connector: attached connector + * @ctx: acquire_ctx, or NULL to let this function handle locking + * + * If the bridge supports output detection, as reported by the + * DRM_BRIDGE_OP_DETECT bridge ops flag, call &drm_bridge_funcs.detect_ctx + * or &drm_bridge_funcs.detect for the bridge and return the connection st= atus. + * Otherwise return connector_status_unknown. + * + * When both @ctx and &drm_bridge_funcs.detect_ctx are not set, this helper + * function is equivalent to drm_bridge_detect() above. + * + * RETURNS: + * The detection status on success, or connector_status_unknown if the bri= dge + * doesn't support output detection. + * If @ctx is set, it might also return -EDEADLK. + */ +int drm_bridge_detect_ctx(struct drm_bridge *bridge, + struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx) +{ + if (!(bridge->ops & DRM_BRIDGE_OP_DETECT)) + return connector_status_unknown; + + if (bridge->funcs->detect_ctx) { + struct drm_modeset_acquire_ctx br_ctx; + int ret; + + if (ctx) + return bridge->funcs->detect_ctx(bridge, connector, ctx); + + drm_modeset_acquire_init(&br_ctx, 0); +retry: + ret =3D drm_modeset_lock(&connector->dev->mode_config.connection_mutex, + &br_ctx); + if (!ret) + ret =3D bridge->funcs->detect_ctx(bridge, connector, &br_ctx); + + if (ret =3D=3D -EDEADLK) { + drm_modeset_backoff(&br_ctx); + goto retry; + } + + if (ret < 0) + ret =3D connector_status_unknown; + + drm_modeset_drop_locks(&br_ctx); + drm_modeset_acquire_fini(&br_ctx); + + return ret; + } + + return bridge->funcs->detect(bridge, connector); +} +EXPORT_SYMBOL_GPL(drm_bridge_detect_ctx); + /** * drm_bridge_get_modes - fill all modes currently valid for the sink into= the * @connector diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 3e6cbfa9dc44..7a38ca37f2eb 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -664,6 +664,33 @@ struct drm_bridge_funcs { enum drm_connector_status (*detect)(struct drm_bridge *bridge, struct drm_connector *connector); =20 + /** + * @detect_ctx: + * + * Check if anything is attached to the bridge output. + * + * This callback is optional, if not implemented the bridge will be + * considered as always having a component attached to its output. + * Bridges that implement this callback shall set the + * DRM_BRIDGE_OP_DETECT flag in their &drm_bridge->ops. + * + * This is the atomic version of &drm_bridge_funcs.detect. + * + * To avoid races against concurrent connector state updates, the + * helper libraries always call this with ctx set to a valid context, + * and &drm_mode_config.connection_mutex will always be locked with + * the ctx parameter set to this ctx. This allows taking additional + * locks as required. + * + * RETURNS: + * + * &drm_connector_status indicating the bridge output status, + * or the error code returned by drm_modeset_lock(), -EDEADLK. + */ + int (*detect_ctx)(struct drm_bridge *bridge, + struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx); + /** * @get_modes: * @@ -1556,6 +1583,9 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm= _bridge *bridge, =20 enum drm_connector_status drm_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connect= or); +int drm_bridge_detect_ctx(struct drm_bridge *bridge, + struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx); int drm_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *connector); const struct drm_edid *drm_bridge_edid_read(struct drm_bridge *bridge, --=20 2.52.0 From nobody Sun Feb 8 23:42:06 2026 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 25D2E28CF49 for ; Mon, 12 Jan 2026 22:26:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768256799; cv=none; b=P/ImAjFLqtwMMLPNczt7a76SmuCW5SvZd/YKoyoIly233NzSDsaje52J7Ed6y0bbyvb/B9dNqX6WpJ/nws5PKivRVdpq3fdJ7AiPGOOoGO+TXFGHLKBk1u3hyoZsiQFhh0evpFKuzVRoybC14bTaof6fodWD7IP+g5I3H7ISVyc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768256799; c=relaxed/simple; bh=q2XOCP26rXDry7FzvFYoqU3pteV9bGFuLefzkF0bFwE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=EDFd3LZEvK1t7zKYHZF1PQCsTb+KgbAz2kL0r11n7IPlYal0kCNKH2f6wecFlJq8bHtrS5YBFU1JlL8mkm/chscLqGZvLsTrwT0FKFcz/8Bwq0OwHhTxClFiXs/Cds+rVPiTgxc9GlrfN+e+QMoIUalRCfxkY003B8nKyDQoYGE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=X2xgoBcU; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="X2xgoBcU" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1768256796; bh=q2XOCP26rXDry7FzvFYoqU3pteV9bGFuLefzkF0bFwE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=X2xgoBcUBWjBwhOqBnkdDA69GV+mqsMpSi4YB9jmY/g4Vx/2vDRVFXFIOgNumhSbS W6LnyG1bEvN69FLm39lmHujJITPlN2A7lFLI4Kewqpe9XheKQSp4+Zl3EzhnrFrFD7 b6m2b8iDoumjAqtkCfAcDnrXU3kswLtgelExWPKjyHvBSbTL3DlXUX3wP3TChU2pKs jI9vXFg8PtKrtrcQNVfL5pbA3a6NzZdynLy/yXM43U9gX2d9YM6kYO0nkpv00U68vr qOfr8ljT1LcFcdfL/h3uG3Cs3HwrAqQyf3Rz5R3Y1fwdfPwCv2hz6/Xi9CTbPHgqU2 oDpyqHPmvDshw== Received: from localhost (unknown [82.79.138.145]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (prime256v1) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: cristicc) by bali.collaboradmins.com (Postfix) with ESMTPSA id 3C11117E1416; Mon, 12 Jan 2026 23:26:36 +0100 (CET) From: Cristian Ciocaltea Date: Tue, 13 Jan 2026 00:26:19 +0200 Subject: [PATCH v2 2/4] drm/bridge-connector: Switch to using ->detect_ctx hook Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260113-dw-hdmi-qp-scramb-v2-2-ae7b2c58d24d@collabora.com> References: <20260113-dw-hdmi-qp-scramb-v2-0-ae7b2c58d24d@collabora.com> In-Reply-To: <20260113-dw-hdmi-qp-scramb-v2-0-ae7b2c58d24d@collabora.com> To: 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?= , Andy Yan 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 X-Mailer: b4 0.14.3 In preparation to allow bridge drivers relying on the HDMI connector framework to provide HDMI 2.0 support, make use of the atomic version of drm_connector_funcs.detect() hook and invoke the newly introduced drm_bridge_detect_ctx() helper. In particular, this is going to be used for triggering an empty modeset in drm_bridge_funcs.detect_ctx() callback, in order to manage SCDC status lost on sink disconnects. Tested-by: Diederik de Haas Tested-by: Maud Spierings Signed-off-by: Cristian Ciocaltea --- drivers/gpu/drm/display/drm_bridge_connector.c | 73 ++++++++++++++--------= ---- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/d= rm/display/drm_bridge_connector.c index 57a0cceabd34..8a1f6afa8c6b 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -200,39 +200,6 @@ static void drm_bridge_connector_disable_hpd(struct dr= m_connector *connector) * Bridge Connector Functions */ =20 -static enum drm_connector_status -drm_bridge_connector_detect(struct drm_connector *connector, bool force) -{ - struct drm_bridge_connector *bridge_connector =3D - to_drm_bridge_connector(connector); - struct drm_bridge *detect =3D bridge_connector->bridge_detect; - struct drm_bridge *hdmi =3D bridge_connector->bridge_hdmi; - enum drm_connector_status status; - - if (detect) { - status =3D detect->funcs->detect(detect, connector); - - if (hdmi) - drm_atomic_helper_connector_hdmi_hotplug(connector, status); - - drm_bridge_connector_hpd_notify(connector, status); - } else { - switch (connector->connector_type) { - case DRM_MODE_CONNECTOR_DPI: - case DRM_MODE_CONNECTOR_LVDS: - case DRM_MODE_CONNECTOR_DSI: - case DRM_MODE_CONNECTOR_eDP: - status =3D connector_status_connected; - break; - default: - status =3D connector_status_unknown; - break; - } - } - - return status; -} - static void drm_bridge_connector_force(struct drm_connector *connector) { struct drm_bridge_connector *bridge_connector =3D @@ -270,7 +237,6 @@ static void drm_bridge_connector_reset(struct drm_conne= ctor *connector) =20 static const struct drm_connector_funcs drm_bridge_connector_funcs =3D { .reset =3D drm_bridge_connector_reset, - .detect =3D drm_bridge_connector_detect, .force =3D drm_bridge_connector_force, .fill_modes =3D drm_helper_probe_single_connector_modes, .atomic_duplicate_state =3D drm_atomic_helper_connector_duplicate_state, @@ -283,6 +249,42 @@ static const struct drm_connector_funcs drm_bridge_con= nector_funcs =3D { * Bridge Connector Helper Functions */ =20 +static int drm_bridge_connector_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct drm_bridge_connector *bridge_connector =3D + to_drm_bridge_connector(connector); + struct drm_bridge *detect =3D bridge_connector->bridge_detect; + struct drm_bridge *hdmi =3D bridge_connector->bridge_hdmi; + int ret; + + if (detect) { + ret =3D drm_bridge_detect_ctx(detect, connector, ctx); + if (ret < 0) + return ret; + + if (hdmi) + drm_atomic_helper_connector_hdmi_hotplug(connector, ret); + + drm_bridge_connector_hpd_notify(connector, ret); + } else { + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DPI: + case DRM_MODE_CONNECTOR_LVDS: + case DRM_MODE_CONNECTOR_DSI: + case DRM_MODE_CONNECTOR_eDP: + ret =3D connector_status_connected; + break; + default: + ret =3D connector_status_unknown; + break; + } + } + + return ret; +} + static int drm_bridge_connector_get_modes_edid(struct drm_connector *conne= ctor, struct drm_bridge *bridge) { @@ -290,7 +292,7 @@ static int drm_bridge_connector_get_modes_edid(struct d= rm_connector *connector, const struct drm_edid *drm_edid; int n; =20 - status =3D drm_bridge_connector_detect(connector, false); + status =3D drm_bridge_connector_detect_ctx(connector, NULL, false); if (status !=3D connector_status_connected) goto no_edid; =20 @@ -376,6 +378,7 @@ static int drm_bridge_connector_atomic_check(struct drm= _connector *connector, =20 static const struct drm_connector_helper_funcs drm_bridge_connector_helper= _funcs =3D { .get_modes =3D drm_bridge_connector_get_modes, + .detect_ctx =3D drm_bridge_connector_detect_ctx, .mode_valid =3D drm_bridge_connector_mode_valid, .enable_hpd =3D drm_bridge_connector_enable_hpd, .disable_hpd =3D drm_bridge_connector_disable_hpd, --=20 2.52.0 From nobody Sun Feb 8 23:42:06 2026 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DC01B284B37 for ; Mon, 12 Jan 2026 22:26:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768256800; cv=none; b=OAvSpAk+4Pm8yWNKdX8FEBp88RLdl96PtQvC6ZBTn2h609ub4x5uC+XGO222iz2vk+rPdvU32efm2xzQlxDod3HbRBP+2Mk2Yox4mdDdBvmxrmvbf/76UaShdrfTN5W6YLRNWQl4YV3ZcH3qjgeaT4Uck0c73aQODFagfTqZAmw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768256800; c=relaxed/simple; bh=y82QMxnSbvE4LP45Yk0BmSOLFP/dEQ91yYpqyXxrPd0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QVFLDvuaDBdzIhOABqjRebpOYUqdMN1sB8Z52kwRwKuu+KIEF413oaeXeqWIbP929eiF28nzZLNZ/BvBdbzyJ3dDM9q+nM3J0KM8C8nqSu1Z7E617dlma9KiR8h2YkKJ6LLRSGcpf0AKybR80cXHxS4A2sgC+PT5iJ53iHJt904= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=n6KPgKgx; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="n6KPgKgx" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1768256797; bh=y82QMxnSbvE4LP45Yk0BmSOLFP/dEQ91yYpqyXxrPd0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=n6KPgKgx3albKGKkimEq/6zZwYfPitKf/W1cNJGohg8o3ECjBt4cd+7rv8e6Xg+E6 biW39ohAVwWPajU+s37hvLp9k4oS65EZA3A58kY+BSG93Ja4Y9mHkMLHKY/Qia7xfc TNH5/NIHkr5T7LUIjpPE41Mm98K6OSHzjZ8UFwNAoPSK5/0Pwj4Ea/pWoVhsHCcLRg WHVNa7ClkIX8tRgqx9cxGkMBbRduRNq/lwB5QGMIuqpQb3tGZM5Z4bseRt07Eu89my EfMlPmODIxjPi2IM4yBPe7c/dPy6v70WDBjDJkjx/JDQTx6kBo3hSrYxLF0Q1P/opg FuL3GmNut8BLg== Received: from localhost (unknown [82.79.138.145]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (prime256v1) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: cristicc) by bali.collaboradmins.com (Postfix) with ESMTPSA id 1D93B17E150E; Mon, 12 Jan 2026 23:26:37 +0100 (CET) From: Cristian Ciocaltea Date: Tue, 13 Jan 2026 00:26:20 +0200 Subject: [PATCH v2 3/4] drm/bridge: dw-hdmi-qp: Add high TMDS clock ratio and scrambling support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260113-dw-hdmi-qp-scramb-v2-3-ae7b2c58d24d@collabora.com> References: <20260113-dw-hdmi-qp-scramb-v2-0-ae7b2c58d24d@collabora.com> In-Reply-To: <20260113-dw-hdmi-qp-scramb-v2-0-ae7b2c58d24d@collabora.com> To: 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?= , Andy Yan 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 X-Mailer: b4 0.14.3 Add support for HDMI 2.0 display modes, e.g. 4K@60Hz, by permitting TMDS character rates above the 340 MHz limit of HDMI 1.4b. Hence, provide the required SCDC management, including the high TMDS clock ratio and scrambling setup, and filter out the HDMI 2.1 modes. Tested-by: Diederik de Haas Tested-by: Maud Spierings Signed-off-by: Cristian Ciocaltea --- drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 184 +++++++++++++++++++++++= +--- 1 file changed, 167 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm= /bridge/synopsys/dw-hdmi-qp.c index 0c7ad06aaca4..de9c8fdb5b55 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 @@ -20,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -37,8 +39,10 @@ #define DDC_SEGMENT_ADDR 0x30 =20 #define HDMI14_MAX_TMDSCLK 340000000 +#define HDMI20_MAX_TMDSRATE 600000000 =20 -#define SCRAMB_POLL_DELAY_MS 3000 +#define SCDC_MIN_SOURCE_VERSION 0x1 +#define SCRAMB_POLL_DELAY_MS 5000 =20 /* * Unless otherwise noted, entries in this table are 100% optimization. @@ -162,6 +166,11 @@ struct dw_hdmi_qp { } phy; =20 unsigned long ref_clk_rate; + + struct drm_connector *curr_conn; + struct delayed_work scramb_work; + bool scramb_enabled; + struct regmap *regm; =20 unsigned long tmds_char_rate; @@ -860,28 +869,98 @@ static int dw_hdmi_qp_config_audio_infoframe(struct d= w_hdmi_qp *hdmi, return 0; } =20 +static bool dw_hdmi_qp_supports_scrambling(struct drm_display_info *displa= y) +{ + if (!display->is_hdmi) + return false; + + return display->hdmi.scdc.supported && + display->hdmi.scdc.scrambling.supported; +} + +static void dw_hdmi_qp_set_scramb(struct dw_hdmi_qp *hdmi) +{ + dev_dbg(hdmi->dev, "set scrambling\n"); + + drm_scdc_set_high_tmds_clock_ratio(hdmi->curr_conn, true); + drm_scdc_set_scrambling(hdmi->curr_conn, true); + + schedule_delayed_work(&hdmi->scramb_work, + msecs_to_jiffies(SCRAMB_POLL_DELAY_MS)); +} + +static void dw_hdmi_qp_scramb_work(struct work_struct *work) +{ + struct dw_hdmi_qp *hdmi =3D container_of(to_delayed_work(work), + struct dw_hdmi_qp, + scramb_work); + if (!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) +{ + u8 ver; + + if (!dw_hdmi_qp_supports_scrambling(&hdmi->curr_conn->display_info)) + return; + + drm_scdc_readb(hdmi->bridge.ddc, SCDC_SINK_VERSION, &ver); + drm_scdc_writeb(hdmi->bridge.ddc, SCDC_SOURCE_VERSION, + min_t(u8, ver, SCDC_MIN_SOURCE_VERSION)); + + dw_hdmi_qp_set_scramb(hdmi); + dw_hdmi_qp_write(hdmi, 1, SCRAMB_CONFIG0); + + hdmi->scramb_enabled =3D true; + + /* 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"); + + hdmi->scramb_enabled =3D false; + cancel_delayed_work_sync(&hdmi->scramb_work); + + dw_hdmi_qp_write(hdmi, 0, SCRAMB_CONFIG0); + + if (hdmi->curr_conn->status =3D=3D 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 =3D bridge->driver_private; struct drm_connector_state *conn_state; - struct drm_connector *connector; unsigned int op_mode; =20 - connector =3D drm_atomic_get_new_connector_for_encoder(state, bridge->enc= oder); - if (WARN_ON(!connector)) + hdmi->curr_conn =3D drm_atomic_get_new_connector_for_encoder(state, + bridge->encoder); + if (WARN_ON(!hdmi->curr_conn)) return; =20 - conn_state =3D drm_atomic_get_new_connector_state(state, connector); + conn_state =3D drm_atomic_get_new_connector_state(state, hdmi->curr_conn); if (WARN_ON(!conn_state)) return; =20 - if (connector->display_info.is_hdmi) { + if (hdmi->curr_conn->display_info.is_hdmi) { dev_dbg(hdmi->dev, "%s mode=3DHDMI %s rate=3D%llu bpc=3D%u\n", __func__, drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_forma= t), conn_state->hdmi.tmds_char_rate, conn_state->hdmi.output_bpc); op_mode =3D 0; hdmi->tmds_char_rate =3D 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=3DDVI\n", __func__); op_mode =3D OPMODE_DVI; @@ -892,7 +971,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); =20 - drm_atomic_helper_connector_hdmi_update_infoframes(connector, state); + drm_atomic_helper_connector_hdmi_update_infoframes(hdmi->curr_conn, state= ); } =20 static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, @@ -902,13 +981,74 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct d= rm_bridge *bridge, =20 hdmi->tmds_char_rate =3D 0; =20 + dw_hdmi_qp_disable_scramb(hdmi); + + hdmi->curr_conn =3D NULL; hdmi->phy.ops->disable(hdmi, hdmi->phy.data); } =20 -static enum drm_connector_status -dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge, struct drm_connector *= connector) +static int dw_hdmi_qp_reset_link(struct dw_hdmi_qp *hdmi, + struct drm_connector *conn, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_crtc *crtc; + u8 config; + int ret; + + if (!conn->state) + return 0; + + crtc =3D conn->state->crtc; + if (!crtc) + return 0; + +retry: + ret =3D drm_modeset_lock(&crtc->mutex, ctx); + if (ret) + goto check_err; + + if (!crtc->state->active) + return 0; + + if (conn->state->commit && + !try_wait_for_completion(&conn->state->commit->hw_done)) + return 0; + + ret =3D 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 0; + } + + if (!!(config & SCDC_SCRAMBLING_ENABLE) =3D=3D hdmi->scramb_enabled) + return 0; + + dev_dbg(hdmi->dev, "%s resetting crtc\n", __func__); + + drm_atomic_helper_connector_hdmi_hotplug(conn, 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. + */ + ret =3D drm_atomic_helper_reset_crtc(crtc, ctx); + +check_err: + if (ret =3D=3D -EDEADLK) { + drm_modeset_backoff(ctx); + goto retry; + } + + return ret; +} + +static int dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge, + struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx) { struct dw_hdmi_qp *hdmi =3D bridge->driver_private; + enum drm_connector_status status; const struct drm_edid *drm_edid; =20 if (hdmi->no_hpd) { @@ -919,7 +1059,15 @@ dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge, s= truct drm_connector *connec return connector_status_disconnected; } =20 - return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); + status =3D hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); + + dev_dbg(hdmi->dev, "%s status=3D%d scramb=3D%d\n", __func__, + status, hdmi->scramb_enabled); + + if (status =3D=3D connector_status_connected && hdmi->scramb_enabled) + dw_hdmi_qp_reset_link(hdmi, connector, ctx); + + return status; } =20 static const struct drm_edid * @@ -943,12 +1091,12 @@ dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct = drm_bridge *bridge, { struct dw_hdmi_qp *hdmi =3D bridge->driver_private; =20 - /* - * 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) { + 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; } @@ -1188,7 +1336,7 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridg= e_funcs =3D { .atomic_reset =3D drm_atomic_helper_bridge_reset, .atomic_enable =3D dw_hdmi_qp_bridge_atomic_enable, .atomic_disable =3D dw_hdmi_qp_bridge_atomic_disable, - .detect =3D dw_hdmi_qp_bridge_detect, + .detect_ctx =3D dw_hdmi_qp_bridge_detect, .edid_read =3D dw_hdmi_qp_bridge_edid_read, .hdmi_tmds_char_rate_valid =3D dw_hdmi_qp_bridge_tmds_char_rate_valid, .hdmi_clear_infoframe =3D dw_hdmi_qp_bridge_clear_infoframe, @@ -1270,6 +1418,8 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_de= vice *pdev, if (IS_ERR(hdmi)) return ERR_CAST(hdmi); =20 + INIT_DELAYED_WORK(&hdmi->scramb_work, dw_hdmi_qp_scramb_work); + hdmi->dev =3D dev; =20 regs =3D devm_platform_ioremap_resource(pdev, 0); --=20 2.52.0 From nobody Sun Feb 8 23:42:06 2026 Received: from bali.collaboradmins.com (bali.collaboradmins.com [148.251.105.195]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9D535296BA9 for ; Mon, 12 Jan 2026 22:26:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.251.105.195 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768256811; cv=none; b=sSwpIVKv8P8t/RFbyStVRzTZJ3Iye/qKN83I34aYRpwtzqkte/7G13FgQhQ7R9Eqfe+Z/GqkUNcVQmJzNkJrmklOvC1nk8H/Sn5PZUTYLUoMFYA/twVcFfo/9BywHrTJq8KL0DBJ1f2NUdEG1WzoHbHeTdlbBDYuxFDG484b1ug= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768256811; c=relaxed/simple; bh=hPdWONUuBZHvs/8qLAlcTuSIfRD9EX7k/uxvPVejFN8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OiqC0VYVNbcvg48V0SlJ4rDS+CBT49MOe/IjklbHBon8hQDLjtQV6Ltsc210KPY+1KWC9yi+0p/hpQF1SohuQMwPZf+IdZpKruf50A/+UyOc8tEY34/5RbOmR8gtTlmjgi3ro1aIq94aygkZcD4ZXEl3nT0z+o9STaeI8OiQIzw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b=SqJsOxj3; arc=none smtp.client-ip=148.251.105.195 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=collabora.com header.i=@collabora.com header.b="SqJsOxj3" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=collabora.com; s=mail; t=1768256798; bh=hPdWONUuBZHvs/8qLAlcTuSIfRD9EX7k/uxvPVejFN8=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=SqJsOxj37Eryt6u1z5Lp1xwAEIilQBm1uUvAMhMyXI4GCvhcdh/Byqgv9ZAOySt4Z +v3jlk11NGLqiVST+FsK4Xa822xaTlf4fPN9XhWXepCZ3kz4qqlvmvcIF8Rc8MBfiM WBDKMDyG9UIyaN8Q9Z1mGLSflGLNgzMmw9PY7iGRIMYBkmob2op85rx+0Z+7oF1w5Y pVR5mkTpH7+J64Mxuir45vpj/njFoa6BN9tao5f4DRnCZm4tTtYiAjhdFiZ3Wg2LtK sbLVYASHE4AnYp84iTsJcs9r9ZqjZDotiX/CMVNg+JsDmDQY/R2YuwevVUJNBxHaYt GEw7bPfozWV1g== Received: from localhost (unknown [82.79.138.145]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (prime256v1) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: cristicc) by bali.collaboradmins.com (Postfix) with ESMTPSA id E6C7517E1513; Mon, 12 Jan 2026 23:26:37 +0100 (CET) From: Cristian Ciocaltea Date: Tue, 13 Jan 2026 00:26:21 +0200 Subject: [PATCH v2 4/4] drm/rockchip: dw_hdmi_qp: Do not send HPD events for all connectors Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260113-dw-hdmi-qp-scramb-v2-4-ae7b2c58d24d@collabora.com> References: <20260113-dw-hdmi-qp-scramb-v2-0-ae7b2c58d24d@collabora.com> In-Reply-To: <20260113-dw-hdmi-qp-scramb-v2-0-ae7b2c58d24d@collabora.com> To: 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?= , Andy Yan 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 X-Mailer: b4 0.14.3 In order to optimize the HPD event handling and run the detect cycle on the affected connector only, make use of drm_connector_helper_hpd_irq_event() instead of drm_helper_hpd_irq_event(). Additionally, move devm_request_threaded_irq() after bridge connector initialization. Tested-by: Diederik de Haas Tested-by: Maud Spierings Signed-off-by: Cristian Ciocaltea --- drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c | 44 ++++++++++++----------= ---- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/d= rm/rockchip/dw_hdmi_qp-rockchip.c index e91caae7e353..4091966a2075 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c @@ -92,6 +92,7 @@ struct rockchip_hdmi_qp { struct regmap *regmap; struct regmap *vo_regmap; struct rockchip_encoder encoder; + struct drm_connector *connector; struct dw_hdmi_qp *hdmi; struct phy *phy; struct gpio_desc *frl_enable_gpio; @@ -251,14 +252,10 @@ static void dw_hdmi_qp_rk3588_hpd_work(struct work_st= ruct *work) struct rockchip_hdmi_qp *hdmi =3D container_of(work, struct rockchip_hdmi_qp, hpd_work.work); - struct drm_device *drm =3D hdmi->encoder.encoder.dev; - bool changed; + bool changed =3D drm_connector_helper_hpd_irq_event(hdmi->connector); =20 - if (drm) { - changed =3D drm_helper_hpd_irq_event(drm); - if (changed) - dev_dbg(hdmi->dev, "connector status changed\n"); - } + if (changed) + dev_dbg(hdmi->dev, "connector status changed\n"); } =20 static irqreturn_t dw_hdmi_qp_rk3576_hardirq(int irq, void *dev_id) @@ -475,13 +472,12 @@ static int dw_hdmi_qp_rockchip_bind(struct device *de= v, struct device *master, struct dw_hdmi_qp_plat_data plat_data =3D {}; const struct rockchip_hdmi_qp_cfg *cfg; struct drm_device *drm =3D data; - struct drm_connector *connector; struct drm_encoder *encoder; struct rockchip_hdmi_qp *hdmi; struct resource *res; struct clk_bulk_data *clks; struct clk *ref_clk; - int ret, irq, i; + int ret, hpd_irq, i; =20 if (!pdev->dev.of_node) return -ENODEV; @@ -582,17 +578,9 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev= , struct device *master, if (plat_data.cec_irq < 0) return plat_data.cec_irq; =20 - irq =3D platform_get_irq_byname(pdev, "hpd"); - if (irq < 0) - return irq; - - ret =3D devm_request_threaded_irq(hdmi->dev, irq, - cfg->ctrl_ops->hardirq_callback, - cfg->ctrl_ops->irq_callback, - IRQF_SHARED, "dw-hdmi-qp-hpd", - hdmi); - if (ret) - return ret; + hpd_irq =3D platform_get_irq_byname(pdev, "hpd"); + if (hpd_irq < 0) + return hpd_irq; =20 drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs= ); drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); @@ -606,12 +594,20 @@ static int dw_hdmi_qp_rockchip_bind(struct device *de= v, struct device *master, "Failed to bind dw-hdmi-qp"); } =20 - connector =3D drm_bridge_connector_init(drm, encoder); - if (IS_ERR(connector)) - return dev_err_probe(hdmi->dev, PTR_ERR(connector), + hdmi->connector =3D drm_bridge_connector_init(drm, encoder); + if (IS_ERR(hdmi->connector)) + return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->connector), "Failed to init bridge connector\n"); =20 - return drm_connector_attach_encoder(connector, encoder); + ret =3D drm_connector_attach_encoder(hdmi->connector, encoder); + if (ret) + return ret; + + return devm_request_threaded_irq(hdmi->dev, hpd_irq, + cfg->ctrl_ops->hardirq_callback, + cfg->ctrl_ops->irq_callback, + IRQF_SHARED, "dw-hdmi-qp-hpd", + hdmi); } =20 static void dw_hdmi_qp_rockchip_unbind(struct device *dev, --=20 2.52.0