* [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms
@ 2026-07-23 22:17 Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 01/15] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback Sebastian Reichel
` (14 more replies)
0 siblings, 15 replies; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel, Sashiko
This patch series updates the Synopsys Designware DisplayPort bridge
together with the only existing user: The Rockchip RK3576/RK3588:
1. Follow-up bridges (PHY, USB-C connector)
This is needed to get USB-C DP AltMode working; I've followed
the Qualcomm driver as reference
2. Runtime PM
The initial driver has been upstreamed without RPM; add it to
avoid wasting power when nothing is plugged
3. Audio
The initial driver has been upstreamed without audio support;
this adds all missing bits for audio with single stream transport
To properly make use of the bridge code the following USBDP PHY series
is also needed:
https://lore.kernel.org/all/20260714-rockchip-usbdp-cleanup-v13-0-6cb3e769d4c5@collabora.com/
This series can be applied without the USBDP side. In that case USB-C
support won't work as the mainline USBDP side does not register as a
DRM bridge. As upstream DT binding for USBDP does not yet support
proper ports, no such board should be upstream.
Boards using the DP controller for native DP connectors (or HDMI
bridges), currently directly link to the connector/hdmi bridge. These
are not affected by the USBDP changes and should keep working in any
case. The difference is, that this series the DRM side is aware of the
link. I don't have any of those boards, so this code path is untested.
I added a dedicated bridge callback for out-of-band hotplug events,
which is separate from the hotplug_notify. I have a feeling, that
there might be a better solution, but haven't found it. Please comment.
Changes in v5:
- Link to v4: https://lore.kernel.org/r/20260721-synopsys-dw-dp-improvements-v4-0-f0f4a4ede712@collabora.com
- Add new patch to cancel pending HPD work on unbind (Sashiko)
- Add new patch to add missing mutex cleanups on module removal (Sashiko)
- Add new patch moving dw_dp_bridge_atomic_get_output_bus_fmts
- Update MEDIA_BUS_FMT_FIXED patch to avoid fixed RGB888 format
and instead use whatever works for the negotiated bandwidth (Sashiko)
- Add more error checking for runtime PM patches (Sashiko)
- Ensure runtime PM is held in interrupt handler (Sashiko)
- Fix usbc_mode logic inversion for runtime PM in unbind (Sashiko)
- Move sleep for HPD_HOT_PLUG from hotplug handler to runtime
resume function; no need to sleep when runtime PM is kept
active and runtime PM resume is a slow operation anyways. (Sashiko)
- Add new patch to protect sdp_reg_bank from concurrent access (Sashiko)
- Add new patch to use regmap_set_bits in dw_dp_send_sdp()
- Update DT binding patch to be all about the selected solution and
mention the ABI break
- Clear SDP in the audio patch on unprepare (Sashiko)
- Add a comment, that only 1,2 or 8 channels are supported (Sashiko)
- Error out on unsupported sample_width (Sashiko)
- Fix missing error check for clock enable (Sashiko)
Changes in PATCHv4:
- Link to v3: https://lore.kernel.org/all/20260612-synopsys-dw-dp-improvements-v3-0-dc61e6352508@collabora.com/
- Drop "Simplify driver data setting" (effectively reverted later on)
- Add "Fix incorrect resource lifetimes in bind callback", which
affects all following patches quite a bit (Sashiko)
- Update "Add follow-up bridge support" to mention that fatal errors
are fine (Sashiko)
- Update "Add out-of-band HPD notify handler" to use
drm_for_each_bridge_in_chain() instead of
drm_for_each_bridge_in_chain_scoped() required after rebase to
latest drm-misc-next
- Add 100ms sleep in hpd detect functions after runtime resume in the
"Add Runtime PM support" patch (Andy Yan)
- Keep runtime PM enabled if DP is used without USB-C to avoid missing
HPD signals in "Add Runtime PM support" patch (Andy Yan)
- Update patch description of DT binding patch and drop RFC tag
- Use pm_runtime_resume_and_get() in the audio startup function, which
allows easy error handling (Sashiko)
- Avoid updating audio_interface in the audio prepare function when
an error related early exit happens (Sashiko)
- Add error handling for clock prepare calls in audio prepare function
(Sashiko)
Changes in PATCHv3:
- Link to v2: https://lore.kernel.org/r/20260501-synopsys-dw-dp-improvements-v2-0-d7e7f6bac77f@collabora.com
- Collect Reviewed-by on "Simplify driver data setting" patch (Andy Yan)
- Use of_drm_get_bridge_by_endpoint instead of devm_drm_of_get_bridge (Luca Ceresoli)
- Use FIELD_PREP_WM16 instead of FIELD_PREP_WM16_CONST (Chaoyi Chen)
- Rebase to latest drm-misc-next (Dropped Cristian's patches)
Changes in PATCHv2:
* Link to v1: https://lore.kernel.org/r/20260326-synopsys-dw-dp-improvements-v1-0-501849162290@collabora.com
* rebased to latest drm-misc-next
* prepended the Patches from Cristian's cleanup series, as they also
needed a rebase and should be merged first
https://lore.kernel.org/dri-devel/20260327-drm-rk-fixes-v3-0-fd2e6900c08c@collabora.com/
* fix issue with the audio stream (un)prepare handling in last patch
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
Sebastian Reichel (15):
drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback
drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind
drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal
drm/bridge: synopsys: dw-dp: Move dw_dp_bridge_atomic_get_output_bus_fmts()
drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED
drm/bridge: synopsys: dw-dp: Add follow-up bridge support
drm/bridge: Add out-of-band HPD notify handler
drm/bridge: synopsys: dw-dp: Support software triggered OOB HPD
drm/rockchip: dw_dp: Implement out-of-band HPD handling
drm/bridge: synopsys: dw-dp: Add Runtime PM support
drm/rockchip: dw_dp: Add runtime PM support
drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access
drm/bridge: synopsys: dw-dp: Use regmap_set_bits in dw_dp_send_sdp
dt-bindings: display: rockchip: dw-dp: Fix sound DAI cells
drm/bridge: synopsys: dw-dp: Add audio support
.../bindings/display/rockchip/rockchip,dw-dp.yaml | 9 +-
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 659 +++++++++++++++++----
drivers/gpu/drm/display/drm_bridge_connector.c | 6 +
drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 235 +++++++-
include/drm/bridge/dw_dp.h | 11 +-
include/drm/drm_bridge.h | 14 +
6 files changed, 789 insertions(+), 145 deletions(-)
---
base-commit: c182a2ae13886463f7ae36e034449477306d182b
change-id: 20260325-synopsys-dw-dp-improvements-7da2e98df1dd
Best regards,
--
Sebastian Reichel <sebastian.reichel@collabora.com>
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v5 01/15] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:34 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 02/15] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind Sebastian Reichel
` (13 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel, Sashiko
Currently the Synopsys DesignWare DP controller driver's bind function
requests lots of resources using device managed functions. These are
free'd on driver removal instead of at unbind time. Fix this discrepancy
by introducing a new probe helper function and moving over most of the
early resource acquisitions.
Fixes: 86eecc3a9c2e ("drm/bridge: synopsys: Add DW DPTX Controller support library")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 128 +++++++++++++++---------------
drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 53 ++++++++-----
include/drm/bridge/dw_dp.h | 5 +-
3 files changed, 99 insertions(+), 87 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 3445c82e6f50..368e1f85e625 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1954,20 +1954,72 @@ static const struct regmap_config dw_dp_regmap_config = {
.rd_table = &dw_dp_readable_table,
};
-static void dw_dp_phy_exit(void *data)
+int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder)
{
- struct dw_dp *dp = data;
+ struct device *dev = dp->dev;
+ struct drm_bridge *bridge;
+ int ret;
+
+ bridge = &dp->bridge;
+ bridge->of_node = dev->of_node;
+ bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
+ bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
+ bridge->ycbcr_420_allowed = true;
+
+ drm_bridge_add(bridge);
+
+ dp->aux.dev = dev;
+ dp->aux.drm_dev = encoder->dev;
+ dp->aux.name = dev_name(dev);
+ dp->aux.transfer = dw_dp_aux_transfer;
+ ret = drm_dp_aux_register(&dp->aux);
+ if (ret) {
+ dev_err_probe(dev, ret, "Aux register failed\n");
+ goto unregister_bridge;
+ }
+
+ ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to attach bridge\n");
+ goto unregister_aux;
+ }
+
+ dw_dp_init_hw(dp);
+ ret = phy_init(dp->phy);
+ if (ret) {
+ dev_err_probe(dev, ret, "phy init failed\n");
+ goto unregister_aux;
+ }
+
+ enable_irq(dp->irq);
+
+ return 0;
+
+unregister_aux:
+ drm_dp_aux_unregister(&dp->aux);
+
+unregister_bridge:
+ drm_bridge_remove(bridge);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dw_dp_bind);
+
+void dw_dp_unbind(struct dw_dp *dp)
+{
+ disable_irq(dp->irq);
phy_exit(dp->phy);
+ drm_dp_aux_unregister(&dp->aux);
+ drm_bridge_remove(&dp->bridge);
}
+EXPORT_SYMBOL_GPL(dw_dp_unbind);
-struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
- const struct dw_dp_plat_data *plat_data)
+struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_data *plat_data)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct dw_dp *dp;
- struct drm_bridge *bridge;
+ struct device *dev = &pdev->dev;
void __iomem *res;
+ struct dw_dp *dp;
int ret;
dp = devm_drm_bridge_alloc(dev, struct dw_dp, bridge, &dw_dp_bridge_funcs);
@@ -1976,9 +2028,8 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
dp->dev = dev;
dp->pixel_mode = plat_data->pixel_mode;
-
dp->plat_data.max_link_rate = plat_data->max_link_rate;
- bridge = &dp->bridge;
+
mutex_init(&dp->irq_lock);
INIT_WORK(&dp->hpd_work, dw_dp_hpd_work);
init_completion(&dp->complete);
@@ -2035,69 +2086,20 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
return ERR_CAST(dp->rstc);
}
- bridge->of_node = dev->of_node;
- bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
- bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
- bridge->ycbcr_420_allowed = true;
-
- ret = devm_drm_bridge_add(dev, bridge);
- if (ret)
- return ERR_PTR(ret);
-
- dp->aux.dev = dev;
- dp->aux.drm_dev = encoder->dev;
- dp->aux.name = dev_name(dev);
- dp->aux.transfer = dw_dp_aux_transfer;
- ret = drm_dp_aux_register(&dp->aux);
- if (ret) {
- dev_err_probe(dev, ret, "Aux register failed\n");
- return ERR_PTR(ret);
- }
-
- ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
- if (ret) {
- dev_err_probe(dev, ret, "Failed to attach bridge\n");
- goto unregister_aux;
- }
-
- dw_dp_init_hw(dp);
-
- ret = phy_init(dp->phy);
- if (ret) {
- dev_err_probe(dev, ret, "phy init failed\n");
- goto unregister_aux;
- }
-
- ret = devm_add_action_or_reset(dev, dw_dp_phy_exit, dp);
- if (ret)
- goto unregister_aux;
-
dp->irq = platform_get_irq(pdev, 0);
- if (dp->irq < 0) {
- ret = dp->irq;
- goto unregister_aux;
- }
+ if (dp->irq < 0)
+ return ERR_PTR(dp->irq);
ret = devm_request_threaded_irq(dev, dp->irq, NULL, dw_dp_irq,
- IRQF_ONESHOT, dev_name(dev), dp);
+ IRQF_ONESHOT | IRQF_NO_AUTOEN, dev_name(dev), dp);
if (ret) {
dev_err_probe(dev, ret, "failed to request irq\n");
- goto unregister_aux;
+ return ERR_PTR(ret);
}
return dp;
-
-unregister_aux:
- drm_dp_aux_unregister(&dp->aux);
- return ERR_PTR(ret);
}
-EXPORT_SYMBOL_GPL(dw_dp_bind);
-
-void dw_dp_unbind(struct dw_dp *dp)
-{
- drm_dp_aux_unregister(&dp->aux);
-}
-EXPORT_SYMBOL_GPL(dw_dp_unbind);
+EXPORT_SYMBOL_GPL(dw_dp_probe);
MODULE_AUTHOR("Andy Yan <andyshrk@163.com>");
MODULE_DESCRIPTION("DW DP Core Library");
diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
index b23efb153c9e..005938dc66c9 100644
--- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
@@ -26,7 +26,7 @@
struct rockchip_dw_dp {
struct dw_dp *base;
struct device *dev;
- struct rockchip_encoder encoder;
+ struct rockchip_encoder *encoder;
};
static int dw_dp_encoder_atomic_check(struct drm_encoder *encoder,
@@ -73,37 +73,28 @@ static const struct drm_encoder_helper_funcs dw_dp_encoder_helper_funcs = {
static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *data)
{
- struct platform_device *pdev = to_platform_device(dev);
- const struct dw_dp_plat_data *plat_data;
+ struct rockchip_dw_dp *dp = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
- struct rockchip_dw_dp *dp;
struct drm_encoder *encoder;
struct drm_connector *connector;
int ret;
- dp = drmm_kzalloc(drm_dev, sizeof(*dp), GFP_KERNEL);
- if (!dp)
+ dp->encoder = drmm_kzalloc(drm_dev, sizeof(*dp->encoder), GFP_KERNEL);
+ if (!dp->encoder)
return -ENOMEM;
- dp->dev = dev;
- platform_set_drvdata(pdev, dp);
-
- plat_data = of_device_get_match_data(dev);
- if (!plat_data)
- return -ENODEV;
-
- encoder = &dp->encoder.encoder;
+ encoder = &dp->encoder->encoder;
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, dev->of_node);
- rockchip_drm_encoder_set_crtc_endpoint_id(&dp->encoder, dev->of_node, 0, 0);
+ rockchip_drm_encoder_set_crtc_endpoint_id(dp->encoder, dev->of_node, 0, 0);
ret = drmm_encoder_init(drm_dev, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL);
if (ret)
return ret;
drm_encoder_helper_add(encoder, &dw_dp_encoder_helper_funcs);
- dp->base = dw_dp_bind(dev, encoder, plat_data);
- if (IS_ERR(dp->base))
- return PTR_ERR(dp->base);
+ ret = dw_dp_bind(dp->base, encoder);
+ if (ret)
+ return ret;
connector = drm_bridge_connector_init(drm_dev, encoder);
if (IS_ERR(connector)) {
@@ -128,12 +119,30 @@ static const struct component_ops dw_dp_rockchip_component_ops = {
.unbind = dw_dp_rockchip_unbind,
};
-static int dw_dp_probe(struct platform_device *pdev)
+static int dw_dp_rockchip_probe(struct platform_device *pdev)
{
+ const struct dw_dp_plat_data *plat_data;
+ struct device *dev = &pdev->dev;
+ struct rockchip_dw_dp *dp;
+
+ plat_data = of_device_get_match_data(dev);
+ if (!plat_data)
+ return -ENODEV;
+
+ dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+ if (!dp)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, dp);
+ dp->dev = dev;
+
+ dp->base = dw_dp_probe(pdev, plat_data);
+ if (IS_ERR(dp->base))
+ return PTR_ERR(dp->base);
+
return component_add(&pdev->dev, &dw_dp_rockchip_component_ops);
}
-static void dw_dp_remove(struct platform_device *pdev)
+static void dw_dp_rockchip_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &dw_dp_rockchip_component_ops);
}
@@ -161,8 +170,8 @@ static const struct of_device_id dw_dp_of_match[] = {
MODULE_DEVICE_TABLE(of, dw_dp_of_match);
struct platform_driver dw_dp_driver = {
- .probe = dw_dp_probe,
- .remove = dw_dp_remove,
+ .probe = dw_dp_rockchip_probe,
+ .remove = dw_dp_rockchip_remove,
.driver = {
.name = "dw-dp",
.of_match_table = dw_dp_of_match,
diff --git a/include/drm/bridge/dw_dp.h b/include/drm/bridge/dw_dp.h
index 22105c3e8e4d..a82412a9e769 100644
--- a/include/drm/bridge/dw_dp.h
+++ b/include/drm/bridge/dw_dp.h
@@ -22,7 +22,8 @@ struct dw_dp_plat_data {
u8 pixel_mode;
};
-struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
- const struct dw_dp_plat_data *plat_data);
+int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder);
void dw_dp_unbind(struct dw_dp *dp);
+
+struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_data *plat_data);
#endif /* __DW_DP__ */
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 02/15] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 01/15] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:32 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 03/15] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal Sebastian Reichel
` (12 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel, Sashiko
There is a race condition during device unbind: If the HPD interrupt
started an HPD worker exactly when the unbind happens, the worker
thread might access invalid resources. Avoid this by stopping any
pending work immediately after disabling the interrupt.
Fixes: 86eecc3a9c2e ("drm/bridge: synopsys: Add DW DPTX Controller support library")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 368e1f85e625..21bd08571dcf 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -2009,6 +2009,7 @@ EXPORT_SYMBOL_GPL(dw_dp_bind);
void dw_dp_unbind(struct dw_dp *dp)
{
disable_irq(dp->irq);
+ cancel_work_sync(&dp->hpd_work);
phy_exit(dp->phy);
drm_dp_aux_unregister(&dp->aux);
drm_bridge_remove(&dp->bridge);
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 03/15] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 01/15] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 02/15] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:48 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 04/15] drm/bridge: synopsys: dw-dp: Move dw_dp_bridge_atomic_get_output_bus_fmts() Sebastian Reichel
` (11 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel, Sashiko
The driver is currently missing to fully clean up after itself. Ensure
that the mutex is cleaned up. This is only relevant when CONFIG_DEBUG_MUTEXES
is set, so not worth a Fixes tag.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 21bd08571dcf..b28040d69fab 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -2031,10 +2031,13 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_
dp->pixel_mode = plat_data->pixel_mode;
dp->plat_data.max_link_rate = plat_data->max_link_rate;
- mutex_init(&dp->irq_lock);
INIT_WORK(&dp->hpd_work, dw_dp_hpd_work);
init_completion(&dp->complete);
+ ret = devm_mutex_init(dev, &dp->irq_lock);
+ if (ret)
+ return ERR_PTR(ret);
+
res = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(res))
return ERR_CAST(res);
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 04/15] drm/bridge: synopsys: dw-dp: Move dw_dp_bridge_atomic_get_output_bus_fmts()
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (2 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 03/15] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:28 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 05/15] drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED Sebastian Reichel
` (10 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
Move dw_dp_bridge_atomic_get_output_bus_fmts() before
dw_dp_bridge_atomic_check() in preparation for reusing it.
No functional change intended.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 96 ++++++++++++++++-----------------
1 file changed, 48 insertions(+), 48 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index b28040d69fab..3dec7777fb28 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1522,6 +1522,54 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux,
* 2. the minimum hsync should be 9 pixel;
* 3. the minimum hbp should be 16 pixel;
*/
+static u32 *dw_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ unsigned int *num_output_fmts)
+{
+ struct dw_dp *dp = bridge_to_dp(bridge);
+ struct dw_dp_link *link = &dp->link;
+ struct drm_display_info *di = &conn_state->connector->display_info;
+ struct drm_display_mode mode = crtc_state->mode;
+ const struct dw_dp_output_format *fmt;
+ u32 i, j = 0;
+ u32 *output_fmts;
+
+ *num_output_fmts = 0;
+
+ output_fmts = kcalloc(ARRAY_SIZE(dw_dp_output_formats), sizeof(*output_fmts), GFP_KERNEL);
+ if (!output_fmts)
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(dw_dp_output_formats); i++) {
+ fmt = &dw_dp_output_formats[i];
+
+ if (fmt->bpc > conn_state->max_bpc)
+ continue;
+
+ if (!(BIT(fmt->color_format) & di->color_formats))
+ continue;
+
+ if (fmt->color_format == DRM_OUTPUT_COLOR_FORMAT_YCBCR420 &&
+ !link->vsc_sdp_supported)
+ continue;
+
+ if (fmt->color_format != DRM_OUTPUT_COLOR_FORMAT_YCBCR420 &&
+ drm_mode_is_420_only(di, &mode))
+ continue;
+
+ if (!dw_dp_bandwidth_ok(dp, &mode, fmt->bpp, link->lanes, link->rate))
+ continue;
+
+ output_fmts[j++] = fmt->bus_format;
+ }
+
+ *num_output_fmts = j;
+
+ return output_fmts;
+}
+
static int dw_dp_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
@@ -1752,54 +1800,6 @@ static const struct drm_edid *dw_dp_bridge_edid_read(struct drm_bridge *bridge,
return edid;
}
-static u32 *dw_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
- struct drm_bridge_state *bridge_state,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state,
- unsigned int *num_output_fmts)
-{
- struct dw_dp *dp = bridge_to_dp(bridge);
- struct dw_dp_link *link = &dp->link;
- struct drm_display_info *di = &conn_state->connector->display_info;
- struct drm_display_mode mode = crtc_state->mode;
- const struct dw_dp_output_format *fmt;
- u32 i, j = 0;
- u32 *output_fmts;
-
- *num_output_fmts = 0;
-
- output_fmts = kcalloc(ARRAY_SIZE(dw_dp_output_formats), sizeof(*output_fmts), GFP_KERNEL);
- if (!output_fmts)
- return NULL;
-
- for (i = 0; i < ARRAY_SIZE(dw_dp_output_formats); i++) {
- fmt = &dw_dp_output_formats[i];
-
- if (fmt->bpc > conn_state->max_bpc)
- continue;
-
- if (!(BIT(fmt->color_format) & di->color_formats))
- continue;
-
- if (fmt->color_format == DRM_OUTPUT_COLOR_FORMAT_YCBCR420 &&
- !link->vsc_sdp_supported)
- continue;
-
- if (fmt->color_format != DRM_OUTPUT_COLOR_FORMAT_YCBCR420 &&
- drm_mode_is_420_only(di, &mode))
- continue;
-
- if (!dw_dp_bandwidth_ok(dp, &mode, fmt->bpp, link->lanes, link->rate))
- continue;
-
- output_fmts[j++] = fmt->bus_format;
- }
-
- *num_output_fmts = j;
-
- return output_fmts;
-}
-
static struct drm_bridge_state *dw_dp_bridge_atomic_duplicate_state(struct drm_bridge *bridge)
{
struct dw_dp_bridge_state *state;
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 05/15] drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (3 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 04/15] drm/bridge: synopsys: dw-dp: Move dw_dp_bridge_atomic_get_output_bus_fmts() Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:31 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 06/15] drm/bridge: synopsys: dw-dp: Add follow-up bridge support Sebastian Reichel
` (9 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
Add support for MEDIA_BUS_FMT_FIXED, which is e.g. requested for USB-C
DP chains as the last bridge in the chain (aux-hpd-bridge) does not
implement atomic_get_output_bus_fmts(), which results in the generic
drm_atomic_bridge_chain_select_bus_fmts() code using MEDIA_BUS_FMT_FIXED
instead.
For decent support of this, two areas are changed:
1. In atomic_check, resolving MEDIA_BUS_FMT_FIXED by selecting the
highest-quality format that fits the DP link (first entry from
the bandwidth-filtered output format list).
2. Implementing a custom .atomic_get_input_bus_fmts hook that, on
MEDIA_BUS_FMT_FIXED, advertises all bandwidth-validated formats
from dw_dp_bridge_atomic_get_output_bus_fmts(). This lets the
upstream encoder negotiate the best mutually supported format.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 49 +++++++++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 3dec7777fb28..812c67fdfb6b 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1576,6 +1576,7 @@ static int dw_dp_bridge_atomic_check(struct drm_bridge *bridge,
struct drm_connector_state *conn_state)
{
struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+ unsigned int out_bus_format = bridge_state->output_bus_cfg.format;
struct dw_dp *dp = bridge_to_dp(bridge);
struct dw_dp_bridge_state *state;
const struct dw_dp_output_format *fmt;
@@ -1586,7 +1587,25 @@ static int dw_dp_bridge_atomic_check(struct drm_bridge *bridge,
state = to_dw_dp_bridge_state(bridge_state);
mode = &state->mode;
- fmt = dw_dp_get_output_format(bridge_state->output_bus_cfg.format);
+ if (out_bus_format == MEDIA_BUS_FMT_FIXED) {
+ unsigned int num_fmts = 0;
+ u32 *fmts;
+
+ fmts = dw_dp_bridge_atomic_get_output_bus_fmts(bridge,
+ bridge_state,
+ crtc_state,
+ conn_state,
+ &num_fmts);
+ if (!num_fmts) {
+ kfree(fmts);
+ return -EINVAL;
+ }
+
+ out_bus_format = fmts[0];
+ kfree(fmts);
+ }
+
+ fmt = dw_dp_get_output_format(out_bus_format);
if (!fmt)
return -EINVAL;
@@ -1800,6 +1819,32 @@ static const struct drm_edid *dw_dp_bridge_edid_read(struct drm_bridge *bridge,
return edid;
}
+static u32 *
+dw_dp_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ u32 output_fmt,
+ unsigned int *num_input_fmts)
+{
+ /*
+ * MEDIA_BUS_FMT_FIXED means the downstream bridge does not constrain
+ * the bus format. In that case, advertise all formats supported by the
+ * DP link so the upstream encoder can negotiate the best match.
+ */
+ if (output_fmt == MEDIA_BUS_FMT_FIXED)
+ return dw_dp_bridge_atomic_get_output_bus_fmts(bridge,
+ bridge_state,
+ crtc_state,
+ conn_state,
+ num_input_fmts);
+
+ return drm_atomic_helper_bridge_propagate_bus_fmt(bridge, bridge_state,
+ crtc_state, conn_state,
+ output_fmt,
+ num_input_fmts);
+}
+
static struct drm_bridge_state *dw_dp_bridge_atomic_duplicate_state(struct drm_bridge *bridge)
{
struct dw_dp_bridge_state *state;
@@ -1817,7 +1862,7 @@ static const struct drm_bridge_funcs dw_dp_bridge_funcs = {
.atomic_duplicate_state = dw_dp_bridge_atomic_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_create_state = drm_atomic_helper_bridge_create_state,
- .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
+ .atomic_get_input_bus_fmts = dw_dp_bridge_atomic_get_input_bus_fmts,
.atomic_get_output_bus_fmts = dw_dp_bridge_atomic_get_output_bus_fmts,
.atomic_check = dw_dp_bridge_atomic_check,
.mode_valid = dw_dp_bridge_mode_valid,
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 06/15] drm/bridge: synopsys: dw-dp: Add follow-up bridge support
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (4 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 05/15] drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 07/15] drm/bridge: Add out-of-band HPD notify handler Sebastian Reichel
` (8 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
Add support to use USB-C connectors with the DP altmode helper code on
devicetree based platforms. To get this working there must be a DRM
bridge chain from the DisplayPort controller to the USB-C connector.
E.g. on Rockchip RK3576:
root@rk3576 # cat /sys/kernel/debug/dri/0/encoder-0/bridges
bridge[0]: dw_dp_bridge_funcs
refcount: 7
type: [10] DP
OF: /soc/dp@27e40000:rockchip,rk3576-dp
ops: [0x47] detect edid hpd
bridge[1]: drm_aux_bridge_funcs
refcount: 4
type: [0] Unknown
OF: /soc/phy@2b010000:rockchip,rk3576-usbdp-phy
ops: [0x0]
bridge[2]: drm_aux_hpd_bridge_funcs
refcount: 5
type: [10] DP
OF: /soc/i2c@2ac50000/typec-portc@22/connector:usb-c-connector
ops: [0x4] hpd
It's fine to fatally error out when there is no follow-up bridge
as the Rockchip Designware Displayport controller is the only
user of the bridge helper and has the port marked as required
in its binding.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 812c67fdfb6b..b9d6dff9e0af 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -329,6 +329,8 @@ struct dw_dp {
struct dw_dp_plat_data plat_data;
u8 pixel_mode;
+ struct drm_bridge *next_bridge;
+
DECLARE_BITMAP(sdp_reg_bank, SDP_REG_BANK_SIZE);
};
@@ -2029,18 +2031,35 @@ int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder)
goto unregister_aux;
}
+ dp->next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 1, 0);
+ if (IS_ERR(dp->next_bridge)) {
+ ret = PTR_ERR(dp->next_bridge);
+ dev_err_probe(dev, ret, "failed to get follow-up bridge.\n");
+ goto unregister_aux;
+ }
+
+ ret = drm_bridge_attach(encoder, dp->next_bridge, bridge,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to attach next bridge\n");
+ goto put_next_bridge;
+ }
+
dw_dp_init_hw(dp);
ret = phy_init(dp->phy);
if (ret) {
dev_err_probe(dev, ret, "phy init failed\n");
- goto unregister_aux;
+ goto put_next_bridge;
}
enable_irq(dp->irq);
return 0;
+put_next_bridge:
+ drm_bridge_put(dp->next_bridge);
+
unregister_aux:
drm_dp_aux_unregister(&dp->aux);
@@ -2056,6 +2075,7 @@ void dw_dp_unbind(struct dw_dp *dp)
disable_irq(dp->irq);
cancel_work_sync(&dp->hpd_work);
phy_exit(dp->phy);
+ drm_bridge_put(dp->next_bridge);
drm_dp_aux_unregister(&dp->aux);
drm_bridge_remove(&dp->bridge);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 07/15] drm/bridge: Add out-of-band HPD notify handler
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (5 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 06/15] drm/bridge: synopsys: dw-dp: Add follow-up bridge support Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 08/15] drm/bridge: synopsys: dw-dp: Support software triggered OOB HPD Sebastian Reichel
` (7 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
For DP bridges, that can be used for DP AltMode, it might be necessary
to enforce HPD status. There is an existing ->oob_hotplug_event() on
the DRM connector, but it currently just calls into hpd_notify().
As DP bridge drivers usually also implement .detect and that also
generates calls into hpd_notify, this is a bad place to force the
HPD status as the follow-up detect call might force it off again
resulting in all follow-up calls to the detection routine also
failing.
Avoid this by having a dedicated function for OOB events.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 6 ++++++
include/drm/drm_bridge.h | 14 ++++++++++++++
2 files changed, 20 insertions(+)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 8b54069fa53a..632cc3ae3b54 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -180,6 +180,12 @@ static void drm_bridge_connector_oob_hotplug_event(struct drm_connector *connect
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
+ /* Notify all bridges in the pipeline of hotplug events. */
+ drm_for_each_bridge_in_chain(bridge_connector->encoder, bridge) {
+ if (bridge->funcs->oob_notify)
+ bridge->funcs->oob_notify(bridge, connector, status);
+ }
+
drm_bridge_connector_handle_hpd(bridge_connector, status);
}
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 3ac84ed57ab2..f38010d2edd1 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -540,6 +540,20 @@ struct drm_bridge_funcs {
*/
void (*hpd_disable)(struct drm_bridge *bridge);
+ /**
+ * @oob_notify:
+ *
+ * Notify the bridge of out of band hot plug detection.
+ *
+ * This callback is optional, it may be implemented by bridges that
+ * need to be notified of display connection or disconnection for
+ * internal reasons. One use case is to force the DP controllers HPD
+ * signal for USB-C DP AltMode.
+ */
+ void (*oob_notify)(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ enum drm_connector_status status);
+
/**
* @hdmi_tmds_char_rate_valid:
*
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 08/15] drm/bridge: synopsys: dw-dp: Support software triggered OOB HPD
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (6 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 07/15] drm/bridge: Add out-of-band HPD notify handler Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 09/15] drm/rockchip: dw_dp: Implement out-of-band HPD handling Sebastian Reichel
` (6 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
Add support for USB-C DP AltMode out-of-band hotplug handling. The
handling itself is implemented in the platform specific driver as the
registers to force HPD state are not part of the Designware DisplayPort
IP itself. Instead the platform integration might provide the necessary
functionality to mux the HPD signal.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 38 +++++++++++++++++++++++++++++++++
include/drm/bridge/dw_dp.h | 3 +++
2 files changed, 41 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index b9d6dff9e0af..9975df8ea37e 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1860,6 +1860,19 @@ static struct drm_bridge_state *dw_dp_bridge_atomic_duplicate_state(struct drm_b
return &state->base;
}
+static void dw_dp_bridge_oob_notify(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ enum drm_connector_status status)
+{
+ bool hpd_high = status != connector_status_disconnected;
+ struct dw_dp *dp = bridge_to_dp(bridge);
+
+ if (dp->plat_data.hpd_sw_cfg)
+ dp->plat_data.hpd_sw_cfg(dp->plat_data.data, hpd_high);
+ else
+ dev_err_once(dp->dev, "Missing platform handler for OOB HPD handling\n");
+}
+
static const struct drm_bridge_funcs dw_dp_bridge_funcs = {
.atomic_duplicate_state = dw_dp_bridge_atomic_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
@@ -1872,6 +1885,7 @@ static const struct drm_bridge_funcs dw_dp_bridge_funcs = {
.atomic_disable = dw_dp_bridge_atomic_disable,
.detect = dw_dp_bridge_detect,
.edid_read = dw_dp_bridge_edid_read,
+ .oob_notify = dw_dp_bridge_oob_notify,
};
static int dw_dp_link_retrain(struct dw_dp *dp)
@@ -2001,6 +2015,19 @@ static const struct regmap_config dw_dp_regmap_config = {
.rd_table = &dw_dp_readable_table,
};
+static bool dw_dp_is_routed_to_usb_c(struct drm_encoder *encoder)
+{
+ struct drm_bridge *last_bridge __free(drm_bridge_put) = NULL;
+ struct fwnode_handle *fwnode;
+
+ last_bridge = drm_bridge_chain_get_last_bridge(encoder);
+ if (!last_bridge)
+ return false;
+
+ fwnode = of_fwnode_handle(last_bridge->of_node);
+ return fwnode_device_is_compatible(fwnode, "usb-c-connector");
+}
+
int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder)
{
struct device *dev = dp->dev;
@@ -2045,6 +2072,13 @@ int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder)
goto put_next_bridge;
}
+ if (dw_dp_is_routed_to_usb_c(encoder)) {
+ dev_dbg(dev, "USB-C mode\n");
+
+ if (dp->plat_data.hpd_sw_sel)
+ dp->plat_data.hpd_sw_sel(dp->plat_data.data, 1);
+ }
+
dw_dp_init_hw(dp);
ret = phy_init(dp->phy);
@@ -2094,6 +2128,10 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_
dp->dev = dev;
dp->pixel_mode = plat_data->pixel_mode;
+
+ dp->plat_data.hpd_sw_sel = plat_data->hpd_sw_sel;
+ dp->plat_data.hpd_sw_cfg = plat_data->hpd_sw_cfg;
+ dp->plat_data.data = plat_data->data;
dp->plat_data.max_link_rate = plat_data->max_link_rate;
INIT_WORK(&dp->hpd_work, dw_dp_hpd_work);
diff --git a/include/drm/bridge/dw_dp.h b/include/drm/bridge/dw_dp.h
index a82412a9e769..79b2cdf0df99 100644
--- a/include/drm/bridge/dw_dp.h
+++ b/include/drm/bridge/dw_dp.h
@@ -20,6 +20,9 @@ enum {
struct dw_dp_plat_data {
u32 max_link_rate;
u8 pixel_mode;
+ void *data;
+ void (*hpd_sw_sel)(void *data, bool hpd);
+ void (*hpd_sw_cfg)(void *data, bool hpd);
};
int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder);
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 09/15] drm/rockchip: dw_dp: Implement out-of-band HPD handling
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (7 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 08/15] drm/bridge: synopsys: dw-dp: Support software triggered OOB HPD Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:34 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 10/15] drm/bridge: synopsys: dw-dp: Add Runtime PM support Sebastian Reichel
` (5 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
Implement out-of-band hotplug handling, which will be used to receive
external hotplug information from the USB-C state machine. This is
currently handled by the USBDP PHY, which brings quite some trouble
as the register being accessed requires the power-domain from the DP
controller and also requires custom TypeC HPD info parsing in the
USBDP PHY driver.
In contrast to the USBDP PHY this does not just enable the hotplug
signal when a DP AltMode capable adapter is plugged in, but instead
properly detects if a cable is plugged in for things like USB-C to
HDMI adapters.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 123 ++++++++++++++++++++++++++++--
1 file changed, 118 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
index 005938dc66c9..0ecbad24dd4e 100644
--- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
@@ -7,9 +7,12 @@
*/
#include <linux/component.h>
+#include <linux/hw_bitfield.h>
#include <linux/media-bus-format.h>
+#include <linux/mfd/syscon.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/regmap.h>
#include <linux/videodev2.h>
#include <drm/bridge/dw_dp.h>
@@ -23,12 +26,54 @@
#include "rockchip_drm_drv.h"
+#define ROCKCHIP_MAX_CTRLS 2
+
+#define ROCKCHIP_VO_GRF_DP_SINK_HPD_SEL BIT(10)
+#define ROCKCHIP_VO_GRF_DP_SINK_HPD_CFG BIT(11)
+
+struct rockchip_dw_dp_plat_data {
+ u8 num_ctrls;
+ u32 ctrl_ids[ROCKCHIP_MAX_CTRLS];
+ u32 max_link_rate;
+ u8 pixel_mode;
+ u32 hpd_reg[ROCKCHIP_MAX_CTRLS];
+};
+
struct rockchip_dw_dp {
struct dw_dp *base;
struct device *dev;
+ const struct rockchip_dw_dp_plat_data *pdata;
+ struct regmap *vo_grf;
struct rockchip_encoder *encoder;
+ int id;
+ bool hpd_sel;
+ bool hpd_cfg;
};
+static void dw_dp_rockchip_hpd_sw_sel(void *data, bool force_hpd_from_sw)
+{
+ struct rockchip_dw_dp *dp = data;
+ u32 hpd_reg = dp->pdata->hpd_reg[dp->id];
+
+ dp->hpd_sel = force_hpd_from_sw;
+
+ regmap_write(dp->vo_grf, hpd_reg,
+ FIELD_PREP_WM16(ROCKCHIP_VO_GRF_DP_SINK_HPD_SEL, dp->hpd_sel));
+}
+
+static void dw_dp_rockchip_hpd_sw_cfg(void *data, bool hpd)
+{
+ struct rockchip_dw_dp *dp = data;
+ u32 hpd_reg = dp->pdata->hpd_reg[dp->id];
+
+ dev_dbg(dp->dev, "Force HPD connected=%s\n", str_yes_no(hpd));
+
+ dp->hpd_cfg = hpd;
+
+ regmap_write(dp->vo_grf, hpd_reg,
+ FIELD_PREP_WM16(ROCKCHIP_VO_GRF_DP_SINK_HPD_CFG, dp->hpd_cfg));
+}
+
static int dw_dp_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
@@ -71,6 +116,34 @@ static const struct drm_encoder_helper_funcs dw_dp_encoder_helper_funcs = {
.atomic_check = dw_dp_encoder_atomic_check,
};
+static struct regmap *dp_dp_rockchip_get_vo_grf(struct rockchip_dw_dp *dp)
+{
+ struct device_node *np = dev_of_node(dp->dev);
+ struct of_phandle_args args;
+ struct regmap *regmap;
+ int ret;
+
+ ret = of_parse_phandle_with_args(np, "phys", "#phy-cells", 0, &args);
+ if (ret)
+ return ERR_PTR(-ENODEV);
+
+ /*
+ * Limit this workaround to RK3576 and RK3588, new platforms should
+ * add a VO GRF phandle in the DisplayPort DT node.
+ */
+ if (!of_device_is_compatible(args.np, "rockchip,rk3576-usbdp-phy") &&
+ !of_device_is_compatible(args.np, "rockchip,rk3588-usbdp-phy")) {
+ regmap = ERR_PTR(-ENODEV);
+ goto out_put_node;
+ }
+
+ regmap = syscon_regmap_lookup_by_phandle(args.np, "rockchip,vo-grf");
+
+out_put_node:
+ of_node_put(args.np);
+ return regmap;
+}
+
static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *data)
{
struct rockchip_dw_dp *dp = dev_get_drvdata(dev);
@@ -121,19 +194,53 @@ static const struct component_ops dw_dp_rockchip_component_ops = {
static int dw_dp_rockchip_probe(struct platform_device *pdev)
{
- const struct dw_dp_plat_data *plat_data;
+ const struct rockchip_dw_dp_plat_data *plat_data_const;
+ struct dw_dp_plat_data *plat_data;
struct device *dev = &pdev->dev;
struct rockchip_dw_dp *dp;
+ struct resource *res;
+ int id;
- plat_data = of_device_get_match_data(dev);
- if (!plat_data)
+ plat_data_const = device_get_match_data(dev);
+ if (!plat_data_const)
return -ENODEV;
+ plat_data = devm_kzalloc(dev, sizeof(*plat_data), GFP_KERNEL);
+ if (!plat_data)
+ return -ENOMEM;
+
dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
if (!dp)
return -ENOMEM;
platform_set_drvdata(pdev, dp);
dp->dev = dev;
+ dp->pdata = plat_data_const;
+
+ res = platform_get_mem_or_io(pdev, 0);
+ if (!res)
+ return -ENODEV;
+
+ /* find the DisplayPort ID from the io address */
+ dp->id = -ENODEV;
+ for (id = 0; id < plat_data_const->num_ctrls; id++) {
+ if (res->start == plat_data_const->ctrl_ids[id]) {
+ dp->id = id;
+ break;
+ }
+ }
+
+ if (dp->id < 0)
+ return dp->id;
+
+ dp->vo_grf = dp_dp_rockchip_get_vo_grf(dp);
+ if (IS_ERR(dp->vo_grf))
+ return PTR_ERR(dp->vo_grf);
+
+ plat_data->max_link_rate = plat_data_const->max_link_rate;
+ plat_data->pixel_mode = plat_data_const->pixel_mode;
+ plat_data->hpd_sw_sel = dw_dp_rockchip_hpd_sw_sel;
+ plat_data->hpd_sw_cfg = dw_dp_rockchip_hpd_sw_cfg;
+ plat_data->data = dp;
dp->base = dw_dp_probe(pdev, plat_data);
if (IS_ERR(dp->base))
@@ -147,14 +254,20 @@ static void dw_dp_rockchip_remove(struct platform_device *pdev)
component_del(&pdev->dev, &dw_dp_rockchip_component_ops);
}
-static const struct dw_dp_plat_data rk3588_dp_plat_data = {
+static const struct rockchip_dw_dp_plat_data rk3588_dp_plat_data = {
+ .num_ctrls = 2,
+ .ctrl_ids = {0xfde50000, 0xfde60000},
.max_link_rate = 810000,
.pixel_mode = DW_DP_MP_QUAD_PIXEL,
+ .hpd_reg = {0x0000, 0x0008},
};
-static const struct dw_dp_plat_data rk3576_dp_plat_data = {
+static const struct rockchip_dw_dp_plat_data rk3576_dp_plat_data = {
+ .num_ctrls = 1,
+ .ctrl_ids = {0x27e40000},
.max_link_rate = 810000,
.pixel_mode = DW_DP_MP_DUAL_PIXEL,
+ .hpd_reg = {0x0000},
};
static const struct of_device_id dw_dp_of_match[] = {
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 10/15] drm/bridge: synopsys: dw-dp: Add Runtime PM support
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (8 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 09/15] drm/rockchip: dw_dp: Implement out-of-band HPD handling Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:37 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 11/15] drm/rockchip: dw_dp: Add runtime " Sebastian Reichel
` (4 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
Add runtime PM stubs to the Synopsys DesignWare DisplayPort bridge
driver. Support is not enabled automatically and must be hooked up
in the vendor specific glue code.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 71 ++++++++++++++++++++++++++++++++-
include/drm/bridge/dw_dp.h | 3 ++
2 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 9975df8ea37e..bcf7ad84f217 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -328,6 +328,7 @@ struct dw_dp {
struct dw_dp_link link;
struct dw_dp_plat_data plat_data;
u8 pixel_mode;
+ bool usbc_mode;
struct drm_bridge *next_bridge;
@@ -1467,6 +1468,11 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux,
if (WARN_ON(msg->size > 16))
return -E2BIG;
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dp->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
switch (msg->request & ~DP_AUX_I2C_MOT) {
case DP_AUX_NATIVE_WRITE:
case DP_AUX_I2C_WRITE:
@@ -1720,6 +1726,8 @@ static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_connector_state *conn_state;
int ret;
+ pm_runtime_get_sync(dp->dev);
+
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
if (!connector) {
dev_err(dp->dev, "failed to get connector\n");
@@ -1774,6 +1782,7 @@ static void dw_dp_bridge_atomic_disable(struct drm_bridge *bridge,
dw_dp_link_disable(dp);
bitmap_zero(dp->sdp_reg_bank, SDP_REG_BANK_SIZE);
dw_dp_reset(dp);
+ pm_runtime_put_autosuspend(dp->dev);
}
static bool dw_dp_hpd_detect_link(struct dw_dp *dp, struct drm_connector *connector)
@@ -1794,6 +1803,10 @@ static enum drm_connector_status dw_dp_bridge_detect(struct drm_bridge *bridge,
{
struct dw_dp *dp = bridge_to_dp(bridge);
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dp->dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return connector_status_disconnected;
+
if (!dw_dp_hpd_detect(dp))
return connector_status_disconnected;
@@ -1973,10 +1986,17 @@ static irqreturn_t dw_dp_irq(int irq, void *data)
{
struct dw_dp *dp = data;
u32 value;
+ int ret;
+
+ ret = pm_runtime_get_if_active(dp->dev);
+ if (ret <= 0)
+ return IRQ_NONE;
regmap_read(dp->regmap, DW_DP_GENERAL_INTERRUPT, &value);
- if (!value)
+ if (!value) {
+ pm_runtime_put_autosuspend(dp->dev);
return IRQ_NONE;
+ }
if (value & HPD_EVENT)
dw_dp_handle_hpd_event(dp);
@@ -1986,6 +2006,7 @@ static irqreturn_t dw_dp_irq(int irq, void *data)
complete(&dp->complete);
}
+ pm_runtime_put_autosuspend(dp->dev);
return IRQ_HANDLED;
}
@@ -2073,10 +2094,15 @@ int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder)
}
if (dw_dp_is_routed_to_usb_c(encoder)) {
+ dp->usbc_mode = true;
dev_dbg(dev, "USB-C mode\n");
if (dp->plat_data.hpd_sw_sel)
dp->plat_data.hpd_sw_sel(dp->plat_data.data, 1);
+ } else {
+ dp->usbc_mode = false;
+ /* Keep runtime PM enabled to have working native HPD IRQ */
+ pm_runtime_get_sync(dp->dev);
}
dw_dp_init_hw(dp);
@@ -2084,13 +2110,17 @@ int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder)
ret = phy_init(dp->phy);
if (ret) {
dev_err_probe(dev, ret, "phy init failed\n");
- goto put_next_bridge;
+ goto put_runtime_pm;
}
enable_irq(dp->irq);
return 0;
+put_runtime_pm:
+ if (!dp->usbc_mode)
+ pm_runtime_put_sync(dp->dev);
+
put_next_bridge:
drm_bridge_put(dp->next_bridge);
@@ -2106,6 +2136,8 @@ EXPORT_SYMBOL_GPL(dw_dp_bind);
void dw_dp_unbind(struct dw_dp *dp)
{
+ if (!dp->usbc_mode)
+ pm_runtime_put_sync(dp->dev);
disable_irq(dp->irq);
cancel_work_sync(&dp->hpd_work);
phy_exit(dp->phy);
@@ -2208,6 +2240,41 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_
}
EXPORT_SYMBOL_GPL(dw_dp_probe);
+int dw_dp_runtime_suspend(struct dw_dp *dp)
+{
+ clk_disable_unprepare(dp->aux_clk);
+ clk_disable_unprepare(dp->apb_clk);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dw_dp_runtime_suspend);
+
+int dw_dp_runtime_resume(struct dw_dp *dp)
+{
+ int ret;
+
+ ret = clk_prepare_enable(dp->apb_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(dp->aux_clk);
+ if (ret) {
+ clk_disable_unprepare(dp->apb_clk);
+ return ret;
+ }
+
+ dw_dp_init_hw(dp);
+
+ /*
+ * HPD_HOT_PLUG bit is asserted only after the sink holds HPD
+ * high for at least 100ms.
+ */
+ msleep(110);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dw_dp_runtime_resume);
+
MODULE_AUTHOR("Andy Yan <andyshrk@163.com>");
MODULE_DESCRIPTION("DW DP Core Library");
MODULE_LICENSE("GPL");
diff --git a/include/drm/bridge/dw_dp.h b/include/drm/bridge/dw_dp.h
index 79b2cdf0df99..f20d2c2c40ca 100644
--- a/include/drm/bridge/dw_dp.h
+++ b/include/drm/bridge/dw_dp.h
@@ -29,4 +29,7 @@ int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder);
void dw_dp_unbind(struct dw_dp *dp);
struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_data *plat_data);
+
+int dw_dp_runtime_suspend(struct dw_dp *dp);
+int dw_dp_runtime_resume(struct dw_dp *dp);
#endif /* __DW_DP__ */
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 11/15] drm/rockchip: dw_dp: Add runtime PM support
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (9 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 10/15] drm/bridge: synopsys: dw-dp: Add Runtime PM support Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:40 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 12/15] drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access Sebastian Reichel
` (3 subsequent siblings)
14 siblings, 1 reply; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
Add support for runtime PM to the Rockchip RK3576/3588 Synopsys
DesignWare DisplayPort driver.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 65 +++++++++++++++++++++++++++++--
1 file changed, 62 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
index 0ecbad24dd4e..b1a69d8821a2 100644
--- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
@@ -12,6 +12,7 @@
#include <linux/mfd/syscon.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/videodev2.h>
@@ -54,9 +55,15 @@ static void dw_dp_rockchip_hpd_sw_sel(void *data, bool force_hpd_from_sw)
{
struct rockchip_dw_dp *dp = data;
u32 hpd_reg = dp->pdata->hpd_reg[dp->id];
+ int ret;
dp->hpd_sel = force_hpd_from_sw;
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dp->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return;
+
regmap_write(dp->vo_grf, hpd_reg,
FIELD_PREP_WM16(ROCKCHIP_VO_GRF_DP_SINK_HPD_SEL, dp->hpd_sel));
}
@@ -65,11 +72,17 @@ static void dw_dp_rockchip_hpd_sw_cfg(void *data, bool hpd)
{
struct rockchip_dw_dp *dp = data;
u32 hpd_reg = dp->pdata->hpd_reg[dp->id];
+ int ret;
dev_dbg(dp->dev, "Force HPD connected=%s\n", str_yes_no(hpd));
dp->hpd_cfg = hpd;
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dp->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return;
+
regmap_write(dp->vo_grf, hpd_reg,
FIELD_PREP_WM16(ROCKCHIP_VO_GRF_DP_SINK_HPD_CFG, dp->hpd_cfg));
}
@@ -165,18 +178,32 @@ static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *
return ret;
drm_encoder_helper_add(encoder, &dw_dp_encoder_helper_funcs);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, 500);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
ret = dw_dp_bind(dp->base, encoder);
if (ret)
return ret;
connector = drm_bridge_connector_init(drm_dev, encoder);
if (IS_ERR(connector)) {
- dw_dp_unbind(dp->base);
- return dev_err_probe(dev, PTR_ERR(connector),
- "Failed to init bridge connector\n");
+ ret = PTR_ERR(connector);
+ dev_err_probe(dev, ret, "Failed to init bridge connector\n");
+ goto error;
}
+ pm_request_idle(dev);
+
return 0;
+
+error:
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_resume(dev);
+ pm_runtime_disable(dev);
+ dw_dp_unbind(dp->base);
+ return ret;
}
static void dw_dp_rockchip_unbind(struct device *dev, struct device *master,
@@ -185,6 +212,9 @@ static void dw_dp_rockchip_unbind(struct device *dev, struct device *master,
struct rockchip_dw_dp *dp = dev_get_drvdata(dev);
dw_dp_unbind(dp->base);
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_resume(dev);
+ pm_runtime_disable(dev);
}
static const struct component_ops dw_dp_rockchip_component_ops = {
@@ -254,6 +284,34 @@ static void dw_dp_rockchip_remove(struct platform_device *pdev)
component_del(&pdev->dev, &dw_dp_rockchip_component_ops);
}
+static int dw_dp_rockchip_runtime_suspend(struct device *dev)
+{
+ struct rockchip_dw_dp *dp = dev_get_drvdata(dev);
+
+ return dw_dp_runtime_suspend(dp->base);
+}
+
+static int dw_dp_rockchip_runtime_resume(struct device *dev)
+{
+ struct rockchip_dw_dp *dp = dev_get_drvdata(dev);
+ u32 hpd_reg = dp->pdata->hpd_reg[dp->id];
+ int ret;
+
+ ret = dw_dp_runtime_resume(dp->base);
+ if (ret)
+ return ret;
+
+ regmap_write(dp->vo_grf, hpd_reg,
+ FIELD_PREP_WM16(ROCKCHIP_VO_GRF_DP_SINK_HPD_SEL, dp->hpd_sel) |
+ FIELD_PREP_WM16(ROCKCHIP_VO_GRF_DP_SINK_HPD_CFG, dp->hpd_cfg));
+
+ return 0;
+}
+
+static const struct dev_pm_ops dw_dp_pm_ops = {
+ RUNTIME_PM_OPS(dw_dp_rockchip_runtime_suspend, dw_dp_rockchip_runtime_resume, NULL)
+};
+
static const struct rockchip_dw_dp_plat_data rk3588_dp_plat_data = {
.num_ctrls = 2,
.ctrl_ids = {0xfde50000, 0xfde60000},
@@ -288,5 +346,6 @@ struct platform_driver dw_dp_driver = {
.driver = {
.name = "dw-dp",
.of_match_table = dw_dp_of_match,
+ .pm = pm_ptr(&dw_dp_pm_ops),
},
};
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 12/15] drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (10 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 11/15] drm/rockchip: dw_dp: Add runtime " Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 13/15] drm/bridge: synopsys: dw-dp: Use regmap_set_bits in dw_dp_send_sdp Sebastian Reichel
` (2 subsequent siblings)
14 siblings, 0 replies; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
Right now sdp_reg_bank is only used during atomic enable/disable and
thus there is no risk of two threads accidently claiming the same bit.
This changes once more SDP users (like audio support) are added, so
introduce a mutex to protect concurrent access to the bitmap.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 24 +++++++++++++++++-------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index bcf7ad84f217..435f23fe391e 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -322,6 +322,8 @@ struct dw_dp {
struct dw_dp_hotplug hotplug;
/* Serialize hpd status access */
struct mutex irq_lock;
+ /* Serialize sdp_reg_bank access */
+ struct mutex sdp_lock;
struct drm_dp_aux aux;
@@ -1045,11 +1047,13 @@ static int dw_dp_send_sdp(struct dw_dp *dp, struct dw_dp_sdp *sdp)
u32 reg;
int i, nr;
- nr = find_first_zero_bit(dp->sdp_reg_bank, SDP_REG_BANK_SIZE);
- if (nr < SDP_REG_BANK_SIZE)
- set_bit(nr, dp->sdp_reg_bank);
- else
- return -EBUSY;
+ scoped_guard(mutex, &dp->sdp_lock) {
+ nr = find_first_zero_bit(dp->sdp_reg_bank, SDP_REG_BANK_SIZE);
+ if (nr < SDP_REG_BANK_SIZE)
+ set_bit(nr, dp->sdp_reg_bank);
+ else
+ return -EBUSY;
+ }
reg = DW_DP_SDP_REGISTER_BANK + nr * 9 * 4;
@@ -1740,7 +1744,8 @@ static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge,
return;
}
- set_bit(0, dp->sdp_reg_bank);
+ scoped_guard(mutex, &dp->sdp_lock)
+ set_bit(0, dp->sdp_reg_bank);
ret = dw_dp_link_enable(dp);
if (ret < 0) {
@@ -1780,7 +1785,8 @@ static void dw_dp_bridge_atomic_disable(struct drm_bridge *bridge,
dw_dp_video_disable(dp);
dw_dp_link_disable(dp);
- bitmap_zero(dp->sdp_reg_bank, SDP_REG_BANK_SIZE);
+ scoped_guard(mutex, &dp->sdp_lock)
+ bitmap_zero(dp->sdp_reg_bank, SDP_REG_BANK_SIZE);
dw_dp_reset(dp);
pm_runtime_put_autosuspend(dp->dev);
}
@@ -2173,6 +2179,10 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_
if (ret)
return ERR_PTR(ret);
+ ret = devm_mutex_init(dev, &dp->sdp_lock);
+ if (ret)
+ return ERR_PTR(ret);
+
res = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(res))
return ERR_CAST(res);
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 13/15] drm/bridge: synopsys: dw-dp: Use regmap_set_bits in dw_dp_send_sdp
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (11 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 12/15] drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 14/15] dt-bindings: display: rockchip: dw-dp: Fix sound DAI cells Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 15/15] drm/bridge: synopsys: dw-dp: Add audio support Sebastian Reichel
14 siblings, 0 replies; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
Simplify dw_dp_send_sdp() a little bit by making use of
regmap_set_bits.
No functional change intended.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 435f23fe391e..2bd37e5c36da 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1066,14 +1066,12 @@ static int dw_dp_send_sdp(struct dw_dp *dp, struct dw_dp_sdp *sdp)
FIELD_PREP(SDP_REGS, get_unaligned_le32(payload)));
if (sdp->flags & DW_DP_SDP_VERTICAL_INTERVAL)
- regmap_update_bits(dp->regmap, DW_DP_SDP_VERTICAL_CTRL,
- EN_VERTICAL_SDP << nr,
- EN_VERTICAL_SDP << nr);
+ regmap_set_bits(dp->regmap, DW_DP_SDP_VERTICAL_CTRL,
+ EN_VERTICAL_SDP << nr);
if (sdp->flags & DW_DP_SDP_HORIZONTAL_INTERVAL)
- regmap_update_bits(dp->regmap, DW_DP_SDP_HORIZONTAL_CTRL,
- EN_HORIZONTAL_SDP << nr,
- EN_HORIZONTAL_SDP << nr);
+ regmap_set_bits(dp->regmap, DW_DP_SDP_HORIZONTAL_CTRL,
+ EN_HORIZONTAL_SDP << nr);
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 14/15] dt-bindings: display: rockchip: dw-dp: Fix sound DAI cells
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (12 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 13/15] drm/bridge: synopsys: dw-dp: Use regmap_set_bits in dw_dp_send_sdp Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 15/15] drm/bridge: synopsys: dw-dp: Add audio support Sebastian Reichel
14 siblings, 0 replies; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
The RK3588 and RK3576 DesignWare DisplayPort controllers both have two
possible DAI interfaces: I2S and S/PDIF. Thus an argument is needed to
to select the right interface.
In addition to that the RK3576 DisplayPort controller is configured
with Multi Stream Transport (MST) enabled for up to 3 displays and
thus has a total of 6 DAI interfaces (I2S and S/PDIF for each possible
stream). Meanwhile the RK3588 does not support MST and thus has only 2
DAI interfaces.
The binding update from this patch has only been tested with the simple
single stream transport (SST) setup as the Linux driver does not yet
support MST. Once MST support is added, the plan is to simply add more
numbers to the argument, so that it looks like this for RK3576:
0 = I2S on stream 0,
1 = S/PDIF on stream 0
2 = I2S on stream 1,
3 = S/PDIF on stream 1
4 = I2S on stream 2,
5 = S/PDIF on stream 2
As the arguments are not part of the binding itself the audio side
is also ready for MST after this change.
Switching '#sound-dai-cells' from 0 to 1 without keeping compatibility
is an ABI break. The rationale for going that way is, that there is not
a single known driver implementation for the current binding. It's also
unclear how the current binding would be used (only support I2S or
S/PDIF for stream 0?). The mainline rk3588 DTS include sets it to 0, but
does not have any soundcard using the DAI. This will be fixed up
separately. The RK3576 does not set it at all.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
.../devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml
index 2b0d9e23e943..c4f8959dd65d 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml
@@ -25,7 +25,7 @@ description: |
* Supports up to 8/10 bits per color component
* Supports RBG, YCbCr4:4:4, YCbCr4:2:2, YCbCr4:2:0
* Pixel clock up to 594MHz
- * I2S, SPDIF audio interface
+ * I2S, S/PDIF audio interface
properties:
compatible:
@@ -46,7 +46,7 @@ properties:
- description: DisplayPort AUX clock
- description: HDCP clock
- description: I2S interface clock
- - description: SPDIF interfce clock
+ - description: S/PDIF interfce clock
clock-names:
minItems: 3
@@ -83,7 +83,8 @@ properties:
maxItems: 1
"#sound-dai-cells":
- const: 0
+ const: 1
+ description: 0 for I2S, 1 for S/PDIF
required:
- compatible
@@ -144,7 +145,7 @@ examples:
resets = <&cru SRST_DP0>;
phys = <&usbdp_phy0 PHY_TYPE_DP>;
power-domains = <&power RK3588_PD_VO0>;
- #sound-dai-cells = <0>;
+ #sound-dai-cells = <1>;
ports {
#address-cells = <1>;
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v5 15/15] drm/bridge: synopsys: dw-dp: Add audio support
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (13 preceding siblings ...)
2026-07-23 22:17 ` [PATCH v5 14/15] dt-bindings: display: rockchip: dw-dp: Fix sound DAI cells Sebastian Reichel
@ 2026-07-23 22:17 ` Sebastian Reichel
2026-07-23 22:38 ` sashiko-bot
14 siblings, 1 reply; 25+ messages in thread
From: Sebastian Reichel @ 2026-07-23 22:17 UTC (permalink / raw)
To: Sandy Huang, Heiko Stübner, Andy Yan, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, Andrzej Hajda, Neil Armstrong,
Robert Foss, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, David Airlie,
Simona Vetter, Dmitry Baryshkov, Luca Ceresoli
Cc: Cristian Ciocaltea, Damon Ding, Dmitry Baryshkov, Alexey Charkov,
dri-devel, linux-rockchip, linux-kernel, devicetree, kernel,
linux-arm-kernel, Sebastian Reichel
Implement audio support for the Synopsys DesignWare DisplayPort
controller.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 255 +++++++++++++++++++++++++++++++-
1 file changed, 253 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 2bd37e5c36da..a9e04cee002e 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -23,17 +23,21 @@
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/display/drm_dp_helper.h>
+#include <drm/display/drm_hdmi_audio_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
+#include <sound/hdmi-codec.h>
+
#define DW_DP_VERSION_NUMBER 0x0000
#define DW_DP_VERSION_TYPE 0x0004
#define DW_DP_ID 0x0008
#define DW_DP_CONFIG_REG1 0x0100
+#define AUDIO_SELECT GENMASK(2, 1)
#define DW_DP_CONFIG_REG2 0x0104
#define DW_DP_CONFIG_REG3 0x0108
@@ -110,6 +114,10 @@
#define HBR_MODE_ENABLE BIT(10)
#define AUDIO_DATA_WIDTH GENMASK(9, 5)
#define AUDIO_DATA_IN_EN GENMASK(4, 1)
+#define AUDIO_DATA_IN_EN_CHANNEL12 BIT(0)
+#define AUDIO_DATA_IN_EN_CHANNEL34 BIT(1)
+#define AUDIO_DATA_IN_EN_CHANNEL56 BIT(2)
+#define AUDIO_DATA_IN_EN_CHANNEL78 BIT(3)
#define AUDIO_INF_SELECT BIT(0)
#define DW_DP_SDP_VERTICAL_CTRL 0x0500
@@ -253,6 +261,8 @@
#define SDP_REG_BANK_SIZE 16
+#define DW_DP_SDP_VERSION 0x12
+
struct dw_dp_link_caps {
bool enhanced_framing;
bool tps3_supported;
@@ -305,6 +315,19 @@ struct dw_dp_hotplug {
bool long_hpd;
};
+enum dw_dp_audio_interface_support {
+ DW_DP_AUDIO_I2S_ONLY = 0,
+ DW_DP_AUDIO_SPDIF_ONLY = 1,
+ DW_DP_AUDIO_I2S_AND_SPDIF = 2,
+ DW_DP_AUDIO_NONE = 3,
+};
+
+enum dw_dp_audio_interface {
+ DW_DP_AUDIO_I2S = 0,
+ DW_DP_AUDIO_SPDIF = 1,
+ DW_DP_AUDIO_UNUSED,
+};
+
struct dw_dp {
struct drm_bridge bridge;
struct device *dev;
@@ -320,6 +343,9 @@ struct dw_dp {
int irq;
struct work_struct hpd_work;
struct dw_dp_hotplug hotplug;
+ enum dw_dp_audio_interface audio_interface;
+ int audio_channels;
+ int audio_sdp_nr;
/* Serialize hpd status access */
struct mutex irq_lock;
/* Serialize sdp_reg_bank access */
@@ -1073,7 +1099,19 @@ static int dw_dp_send_sdp(struct dw_dp *dp, struct dw_dp_sdp *sdp)
regmap_set_bits(dp->regmap, DW_DP_SDP_HORIZONTAL_CTRL,
EN_HORIZONTAL_SDP << nr);
- return 0;
+ return nr;
+}
+
+static void dw_dp_clear_sdp(struct dw_dp *dp, int nr)
+{
+ regmap_clear_bits(dp->regmap, DW_DP_SDP_VERTICAL_CTRL,
+ EN_VERTICAL_SDP << nr);
+
+ regmap_clear_bits(dp->regmap, DW_DP_SDP_HORIZONTAL_CTRL,
+ EN_HORIZONTAL_SDP << nr);
+
+ scoped_guard(mutex, &dp->sdp_lock)
+ clear_bit(nr, dp->sdp_reg_bank);
}
static int dw_dp_send_vsc_sdp(struct dw_dp *dp)
@@ -1890,6 +1928,205 @@ static void dw_dp_bridge_oob_notify(struct drm_bridge *bridge,
dev_err_once(dp->dev, "Missing platform handler for OOB HPD handling\n");
}
+static int dw_dp_audio_infoframe_send(struct dw_dp *dp)
+{
+ struct hdmi_audio_infoframe frame;
+ struct dw_dp_sdp sdp;
+ int ret;
+
+ ret = hdmi_audio_infoframe_init(&frame);
+ if (ret < 0)
+ return ret;
+
+ frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+ frame.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+ frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+ frame.channels = dp->audio_channels;
+
+ ret = hdmi_audio_infoframe_pack_for_dp(&frame, &sdp.base, DW_DP_SDP_VERSION);
+ if (ret < 0)
+ return ret;
+
+ sdp.flags = DW_DP_SDP_VERTICAL_INTERVAL;
+
+ return dw_dp_send_sdp(dp, &sdp);
+}
+
+static int dw_dp_audio_startup(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct dw_dp *dp = bridge_to_dp(bridge);
+
+ dev_dbg(dp->dev, "audio startup\n");
+
+ return pm_runtime_resume_and_get(dp->dev);
+}
+
+static void dw_dp_audio_unprepare(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct dw_dp *dp = bridge_to_dp(bridge);
+
+ /* Disable all audio streams */
+ regmap_update_bits(dp->regmap, DW_DP_AUD_CONFIG1, AUDIO_DATA_IN_EN,
+ FIELD_PREP(AUDIO_DATA_IN_EN, 0));
+
+ if (dp->audio_interface == DW_DP_AUDIO_SPDIF)
+ clk_disable_unprepare(dp->spdif_clk);
+ else if (dp->audio_interface == DW_DP_AUDIO_I2S)
+ clk_disable_unprepare(dp->i2s_clk);
+
+ dp->audio_interface = DW_DP_AUDIO_UNUSED;
+
+ dw_dp_clear_sdp(dp, dp->audio_sdp_nr);
+}
+
+static int dw_dp_audio_prepare(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
+{
+ struct dw_dp *dp = bridge_to_dp(bridge);
+ u8 audio_data_in_en, supported_audio_interfaces;
+ enum dw_dp_audio_interface audio_interface;
+ u32 cfg1;
+ int ret;
+
+ /*
+ * prepare might be called multiple times, so release the clocks
+ * from previous calls to keep the calls in balance.
+ */
+ if (dp->audio_interface != DW_DP_AUDIO_UNUSED)
+ dw_dp_audio_unprepare(bridge, connector);
+
+ /* The hardware is limited to 1,2 or 8 channels */
+ dp->audio_channels = params->cea.channels;
+ switch (params->cea.channels) {
+ case 1:
+ case 2:
+ audio_data_in_en = AUDIO_DATA_IN_EN_CHANNEL12;
+ break;
+ case 8:
+ audio_data_in_en = AUDIO_DATA_IN_EN_CHANNEL12 |
+ AUDIO_DATA_IN_EN_CHANNEL34 |
+ AUDIO_DATA_IN_EN_CHANNEL56 |
+ AUDIO_DATA_IN_EN_CHANNEL78;
+ break;
+ default:
+ dev_err(dp->dev, "invalid audio channels %d\n", dp->audio_channels);
+ return -EINVAL;
+ }
+
+ if (params->sample_width < 16 || params->sample_width >> 24) {
+ dev_err(dp->dev, "invalid data sample width %d\n", params->sample_width);
+ return -EINVAL;
+ }
+
+ switch (daifmt->fmt) {
+ case HDMI_SPDIF:
+ audio_interface = DW_DP_AUDIO_SPDIF;
+ break;
+ case HDMI_I2S:
+ /*
+ * It is recommended to use SPDIF instead of I2S, since I2S mode requires
+ * manually inserting PCUV control bits from userspace and this is done
+ * automatically in hardware for SPDIF mode.
+ */
+ audio_interface = DW_DP_AUDIO_I2S;
+ break;
+ default:
+ dev_err(dp->dev, "invalid DAI format %d\n", daifmt->fmt);
+ return -EINVAL;
+ }
+
+ regmap_read(dp->regmap, DW_DP_CONFIG_REG1, &cfg1);
+ supported_audio_interfaces = FIELD_GET(AUDIO_SELECT, cfg1);
+
+ if (supported_audio_interfaces != DW_DP_AUDIO_I2S_AND_SPDIF &&
+ supported_audio_interfaces != audio_interface) {
+ dev_err(dp->dev, "unsupported DAI %d\n", daifmt->fmt);
+ return -EINVAL;
+ }
+
+ ret = clk_prepare_enable(dp->spdif_clk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(dp->i2s_clk);
+ if (ret) {
+ clk_disable_unprepare(dp->spdif_clk);
+ return ret;
+ }
+
+ regmap_update_bits(dp->regmap, DW_DP_AUD_CONFIG1,
+ AUDIO_DATA_IN_EN | NUM_CHANNELS | AUDIO_DATA_WIDTH |
+ AUDIO_INF_SELECT | HBR_MODE_ENABLE,
+ FIELD_PREP(AUDIO_DATA_IN_EN, audio_data_in_en) |
+ FIELD_PREP(NUM_CHANNELS, dp->audio_channels - 1) |
+ FIELD_PREP(AUDIO_DATA_WIDTH, params->sample_width) |
+ FIELD_PREP(AUDIO_INF_SELECT, audio_interface) |
+ FIELD_PREP(HBR_MODE_ENABLE, 0));
+
+ /* Wait for inf switch */
+ usleep_range(20, 40);
+
+ if (audio_interface == DW_DP_AUDIO_I2S)
+ clk_disable_unprepare(dp->spdif_clk);
+ else if (audio_interface == DW_DP_AUDIO_SPDIF)
+ clk_disable_unprepare(dp->i2s_clk);
+
+ dp->audio_interface = audio_interface;
+
+ /*
+ * Send audio stream during vertical and horizontal blanking periods.
+ * Send out audio timestamp SDP once per video frame during the vertical
+ * blanking period
+ */
+ regmap_update_bits(dp->regmap, DW_DP_SDP_VERTICAL_CTRL,
+ EN_AUDIO_STREAM_SDP | EN_AUDIO_TIMESTAMP_SDP,
+ FIELD_PREP(EN_AUDIO_STREAM_SDP, 1) |
+ FIELD_PREP(EN_AUDIO_TIMESTAMP_SDP, 1));
+ regmap_update_bits(dp->regmap, DW_DP_SDP_HORIZONTAL_CTRL,
+ EN_AUDIO_STREAM_SDP,
+ FIELD_PREP(EN_AUDIO_STREAM_SDP, 1));
+
+ ret = dw_dp_audio_infoframe_send(dp);
+ if (ret < 0)
+ dev_err(dp->dev, "failed to send audio infoframe\n");
+
+ dp->audio_sdp_nr = ret;
+
+ dev_dbg(dp->dev, "audio prepare with %d channels using DAI=%d\n",
+ dp->audio_channels, dp->audio_interface);
+
+ return 0;
+}
+
+static void dw_dp_audio_shutdown(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct dw_dp *dp = bridge_to_dp(bridge);
+
+ dev_dbg(dp->dev, "audio shutdown\n");
+
+ dw_dp_audio_unprepare(bridge, connector);
+ pm_runtime_put_autosuspend(dp->dev);
+}
+
+static int dw_dp_audio_mute_stream(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ bool enable, int direction)
+{
+ struct dw_dp *dp = bridge_to_dp(bridge);
+
+ dev_dbg(dp->dev, "audio %smute\n", enable ? "" : "un");
+
+ regmap_update_bits(dp->regmap, DW_DP_AUD_CONFIG1, AUDIO_MUTE,
+ FIELD_PREP(AUDIO_MUTE, enable));
+
+ return 0;
+}
+
static const struct drm_bridge_funcs dw_dp_bridge_funcs = {
.atomic_duplicate_state = dw_dp_bridge_atomic_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
@@ -1903,6 +2140,11 @@ static const struct drm_bridge_funcs dw_dp_bridge_funcs = {
.detect = dw_dp_bridge_detect,
.edid_read = dw_dp_bridge_edid_read,
.oob_notify = dw_dp_bridge_oob_notify,
+
+ .dp_audio_startup = dw_dp_audio_startup,
+ .dp_audio_prepare = dw_dp_audio_prepare,
+ .dp_audio_shutdown = dw_dp_audio_shutdown,
+ .dp_audio_mute_stream = dw_dp_audio_mute_stream,
};
static int dw_dp_link_retrain(struct dw_dp *dp)
@@ -2061,10 +2303,19 @@ int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder)
bridge = &dp->bridge;
bridge->of_node = dev->of_node;
- bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
+ bridge->ops = DRM_BRIDGE_OP_DP_AUDIO |
+ DRM_BRIDGE_OP_DETECT |
+ DRM_BRIDGE_OP_EDID |
+ DRM_BRIDGE_OP_HPD;
bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
bridge->ycbcr_420_allowed = true;
+ dp->audio_interface = DW_DP_AUDIO_UNUSED;
+ bridge->hdmi_audio_dev = dev;
+ bridge->hdmi_audio_max_i2s_playback_channels = 8;
+ bridge->hdmi_audio_dai_port = 1;
+ bridge->hdmi_audio_spdif_playback = true;
+
drm_bridge_add(bridge);
dp->aux.dev = dev;
--
2.53.0
^ permalink raw reply related [flat|nested] 25+ messages in thread
* Re: [PATCH v5 04/15] drm/bridge: synopsys: dw-dp: Move dw_dp_bridge_atomic_get_output_bus_fmts()
2026-07-23 22:17 ` [PATCH v5 04/15] drm/bridge: synopsys: dw-dp: Move dw_dp_bridge_atomic_get_output_bus_fmts() Sebastian Reichel
@ 2026-07-23 22:28 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-23 22:28 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: robh, conor+dt, dri-devel, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The dynamically allocated `output_fmts` array is leaked when no matching bus formats are found.
--
--- Patch [4]: [PATCH v5 04/15] drm/bridge: synopsys: dw-dp: Move dw_dp_bridge_atomic_get_output_bus_fmts() ---
commit c989bc992dcfd432abf52123b03475b9fb318ca0
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Move dw_dp_bridge_atomic_get_output_bus_fmts()
This commit moves dw_dp_bridge_atomic_get_output_bus_fmts() before
dw_dp_bridge_atomic_check() in preparation for reusing it. No functional
changes are intended.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index b28040d69fabf..3dec7777fb285 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
[ ... ]
> @@ -1522,6 +1522,54 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux,
> * 2. the minimum hsync should be 9 pixel;
> * 3. the minimum hbp should be 16 pixel;
> */
> +static u32 *dw_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
> + struct drm_bridge_state *bridge_state,
> + struct drm_crtc_state *crtc_state,
> + struct drm_connector_state *conn_state,
> + unsigned int *num_output_fmts)
> +{
[ ... ]
> + output_fmts = kcalloc(ARRAY_SIZE(dw_dp_output_formats), sizeof(*output_fmts), GFP_KERNEL);
> + if (!output_fmts)
> + return NULL;
> +
> + for (i = 0; i < ARRAY_SIZE(dw_dp_output_formats); i++) {
[ ... ]
> + if (!dw_dp_bandwidth_ok(dp, &mode, fmt->bpp, link->lanes, link->rate))
> + continue;
> +
> + output_fmts[j++] = fmt->bus_format;
> + }
> +
> + *num_output_fmts = j;
> +
> + return output_fmts;
[Severity: High]
This isn't a bug introduced by this patch, but does this code leak the
dynamically allocated output_fmts array when no formats match?
If a display mode has a very high pixel clock, dw_dp_bandwidth_ok() might
return false for all formats, leaving j as 0. The function then sets
*num_output_fmts = 0 and returns the allocated output_fmts array.
In the DRM core, if drm_atomic_bridge_chain_select_bus_fmts() receives 0 in
num_out_bus_fmts, it immediately returns -ENOTSUPP without calling kfree()
on the returned pointer. Since this can be repeatedly triggered via the
atomic test ioctl by a process with DRM Master privileges, could this be
exploited to exhaust memory?
> +}
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724-synopsys-dw-dp-improvements-v5-0-9445c2e87441@collabora.com?part=4
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v5 05/15] drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED
2026-07-23 22:17 ` [PATCH v5 05/15] drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED Sebastian Reichel
@ 2026-07-23 22:31 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-23 22:31 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: dri-devel, conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] dw_dp_bridge_atomic_check ignores the negotiated input bus format when the output format is MEDIA_BUS_FMT_FIXED, forcing the highest-bandwidth format instead.
- [Low] OOM condition masked as -EINVAL instead of -ENOMEM in dw_dp_bridge_atomic_check.
--
--- Patch [5]: [PATCH v5 05/15] drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED ---
commit 22a7a7c58c5ed8233a59740b01dc41f49ab3cdc8
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED
This patch adds support for MEDIA_BUS_FMT_FIXED to the Synopsys DW-DP bridge.
It updates atomic_check to resolve MEDIA_BUS_FMT_FIXED by selecting the first
bandwidth-filtered output format, and implements a custom
atomic_get_input_bus_fmts hook to advertise all supported formats for upstream
negotiation.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index 3dec7777fb285..812c67fdfb6b8 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> @@ -1586,7 +1587,25 @@ static int dw_dp_bridge_atomic_check(struct drm_bridge *bridge,
> state = to_dw_dp_bridge_state(bridge_state);
> mode = &state->mode;
>
> - fmt = dw_dp_get_output_format(bridge_state->output_bus_cfg.format);
> + if (out_bus_format == MEDIA_BUS_FMT_FIXED) {
> + unsigned int num_fmts = 0;
> + u32 *fmts;
> +
> + fmts = dw_dp_bridge_atomic_get_output_bus_fmts(bridge,
> + bridge_state,
> + crtc_state,
> + conn_state,
> + &num_fmts);
> + if (!num_fmts) {
> + kfree(fmts);
> + return -EINVAL;
[Severity: Low]
Does this mask an out-of-memory condition?
If dw_dp_bridge_atomic_get_output_bus_fmts() fails to allocate memory via
kcalloc(), it returns NULL and sets num_fmts to 0. Returning -EINVAL here
obscures the actual -ENOMEM error.
> + }
> +
> + out_bus_format = fmts[0];
[Severity: High]
Does this unconditionally override the successfully negotiated input format?
If the upstream element negotiated a format other than fmts[0] (which would
be stored in bridge_state->input_bus_cfg.format), won't forcing fmts[0] here
cause a format mismatch between the hardware and the upstream encoder?
> + kfree(fmts);
> + }
> +
> + fmt = dw_dp_get_output_format(out_bus_format);
> if (!fmt)
> return -EINVAL;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724-synopsys-dw-dp-improvements-v5-0-9445c2e87441@collabora.com?part=5
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v5 02/15] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind
2026-07-23 22:17 ` [PATCH v5 02/15] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind Sebastian Reichel
@ 2026-07-23 22:32 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-23 22:32 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: devicetree, robh, conor+dt, dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] The completion `dp->complete` is not re-initialized before starting a new AUX transfer, causing subsequent transfers to falsely complete immediately if a previous transfer timed out.
- [High] The AUX read length calculation incorrectly subtracts 1 instead of adding 1, causing all DP AUX reads to fail with `-EBUSY`.
--
--- Patch [2]: [PATCH v5 02/15] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind ---
commit 7fd4acc667a3adf3c285f78c69b39dea1318c0a1
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
Subject: drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind
This commit fixes a race condition during device unbind where the HPD worker
thread might access invalid resources if started exactly when unbind happens.
It avoids this by canceling any pending HPD work immediately after disabling
the interrupt.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index 368e1f85e6253..21bd08571dcfe 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> @@ -2009,6 +2009,7 @@ EXPORT_SYMBOL_GPL(dw_dp_bind);
> void dw_dp_unbind(struct dw_dp *dp)
> {
> disable_irq(dp->irq);
> + cancel_work_sync(&dp->hpd_work);
> phy_exit(dp->phy);
> drm_dp_aux_unregister(&dp->aux);
> drm_bridge_remove(&dp->bridge);
[Severity: High]
This isn't a bug introduced by this patch, but looking at dw_dp_aux_transfer()
in drivers/gpu/drm/bridge/synopsys/dw-dp.c, it seems dp->complete is not
re-initialized before waiting for completion:
drivers/gpu/drm/bridge/synopsys/dw-dp.c:dw_dp_aux_transfer() {
...
if (msg->size > 0)
value = FIELD_PREP(AUX_LEN_REQ, msg->size - 1);
...
regmap_write(dp->regmap, DW_DP_AUX_CMD, value);
status = wait_for_completion_timeout(&dp->complete, timeout);
...
}
If a previous transfer timed out, the completion count would remain 0.
If the hardware eventually finishes and fires a late interrupt, the interrupt
handler could call complete(&dp->complete).
Could a subsequent call to dw_dp_aux_transfer() then falsely complete
immediately if wait_for_completion_timeout() consumes this stale completion?
Would adding a call to reinit_completion(&dp->complete) before writing the
command resolve this?
[Severity: High]
This is a pre-existing issue, but further down in dw_dp_aux_transfer(), is there
a logic error in the AUX read length calculation?
drivers/gpu/drm/bridge/synopsys/dw-dp.c:dw_dp_aux_transfer() {
...
if (msg->size > 0 && msg->reply == DP_AUX_NATIVE_REPLY_ACK) {
if (msg->request & DP_AUX_I2C_READ) {
size_t count = FIELD_GET(AUX_BYTES_READ, value) - 1;
if (count != msg->size)
return -EBUSY;
...
}
Because 1 is subtracted from FIELD_GET(AUX_BYTES_READ, value) instead of being
added, count can never equal msg->size.
For example, if the hardware reports msg->size or msg->size - 1, subtracting 1
guarantees count evaluates to either msg->size - 1 or msg->size - 2.
Does this cause all DP AUX reads to unconditionally fail with -EBUSY, breaking
EDID reading and link training?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724-synopsys-dw-dp-improvements-v5-0-9445c2e87441@collabora.com?part=2
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v5 01/15] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback
2026-07-23 22:17 ` [PATCH v5 01/15] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback Sebastian Reichel
@ 2026-07-23 22:34 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-23 22:34 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: conor+dt, robh, dri-devel, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing cancel_work_sync(&dp->hpd_work) in dw_dp_unbind leads to use-after-free
- [High] Missing reinit_completion() in AUX transfer function causes stale completion state
--
--- Patch [1]: [PATCH v5 01/15] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback ---
commit f51b82393e0acdef468182d74c204f63a5e111ae
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback
Currently the Synopsys DesignWare DP controller driver's bind function
requests lots of resources using device managed functions. These are
free'd on driver removal instead of at unbind time. Fix this discrepancy
by introducing a new probe helper function and moving over most of the
early resource acquisitions.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index 3445c82e6f50e..368e1f85e6253 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
[ ... ]
> + dp->aux.transfer = dw_dp_aux_transfer;
[Severity: High]
This isn't a bug introduced by this patch, but does the AUX transfer function
need a call to reinit_completion()?
Looking at dw_dp_aux_transfer() in drivers/gpu/drm/bridge/synopsys/dw-dp.c,
it waits for completion like this:
regmap_write(dp->regmap, DW_DP_AUX_CMD, value);
status = wait_for_completion_timeout(&dp->complete, timeout);
If a previous transfer times out and the hardware interrupt eventually arrives
late, it can leave dp->complete.done greater than zero. When the next transfer
is initiated, wait_for_completion_timeout() could consume the leftover
completion and return immediately without waiting for the new transfer to
finish, causing it to read garbage DPCD data.
> + ret = drm_dp_aux_register(&dp->aux);
[ ... ]
> +void dw_dp_unbind(struct dw_dp *dp)
> +{
> + disable_irq(dp->irq);
> phy_exit(dp->phy);
> + drm_dp_aux_unregister(&dp->aux);
> + drm_bridge_remove(&dp->bridge);
> }
[Severity: High]
This is a pre-existing issue, but does dw_dp_unbind() in
drivers/gpu/drm/bridge/synopsys/dw-dp.c need to call
cancel_work_sync(&dp->hpd_work) to prevent a use-after-free?
If a hotplug event occurs when the driver is being unbound, the IRQ handler
might schedule dp->hpd_work. The disable_irq() call here waits for the IRQ
handler to finish but does not cancel pending workqueue items. After the
driver unbinds and the devm-managed dp structure is freed, the workqueue
could execute dw_dp_hpd_work() and access the freed dp structure.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724-synopsys-dw-dp-improvements-v5-0-9445c2e87441@collabora.com?part=1
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v5 09/15] drm/rockchip: dw_dp: Implement out-of-band HPD handling
2026-07-23 22:17 ` [PATCH v5 09/15] drm/rockchip: dw_dp: Implement out-of-band HPD handling Sebastian Reichel
@ 2026-07-23 22:34 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-23 22:34 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: conor+dt, dri-devel, robh, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Out-of-band HPD callbacks write to hardware registers without ensuring the DP controller's power domain is active, risking system crashes.
- [Medium] The VO GRF lookup unconditionally searches the PHY node, entirely failing to support new platforms that provide the VO GRF phandle directly in the DP node.
--
--- Patch [9]: [PATCH v5 09/15] drm/rockchip: dw_dp: Implement out-of-band HPD handling ---
commit d5d5b2e640507cef02b65193fd2c17e492af4958
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/rockchip: dw_dp: Implement out-of-band HPD handling
This commit implements out-of-band hotplug handling for the Rockchip
DesignWare DisplayPort driver. It moves the HPD register access from the
USBDP PHY driver to this driver to better detect cable plug events from the
USB-C state machine.
> diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> index 005938dc66c9f..0ecbad24dd4e8 100644
> --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
[ ... ]
> @@ -23,12 +26,54 @@
>
> #include "rockchip_drm_drv.h"
>
> +#define ROCKCHIP_MAX_CTRLS 2
> +
> +#define ROCKCHIP_VO_GRF_DP_SINK_HPD_SEL BIT(10)
> +#define ROCKCHIP_VO_GRF_DP_SINK_HPD_CFG BIT(11)
> +
> +struct rockchip_dw_dp_plat_data {
> + u8 num_ctrls;
> + u32 ctrl_ids[ROCKCHIP_MAX_CTRLS];
> + u32 max_link_rate;
> + u8 pixel_mode;
> + u32 hpd_reg[ROCKCHIP_MAX_CTRLS];
> +};
> +
> struct rockchip_dw_dp {
> struct dw_dp *base;
> struct device *dev;
> + const struct rockchip_dw_dp_plat_data *pdata;
> + struct regmap *vo_grf;
> struct rockchip_encoder *encoder;
> + int id;
> + bool hpd_sel;
> + bool hpd_cfg;
> };
>
> +static void dw_dp_rockchip_hpd_sw_sel(void *data, bool force_hpd_from_sw)
> +{
> + struct rockchip_dw_dp *dp = data;
> + u32 hpd_reg = dp->pdata->hpd_reg[dp->id];
> +
> + dp->hpd_sel = force_hpd_from_sw;
> +
> + regmap_write(dp->vo_grf, hpd_reg,
> + FIELD_PREP_WM16(ROCKCHIP_VO_GRF_DP_SINK_HPD_SEL, dp->hpd_sel));
[Severity: High]
Does this out-of-band hotplug callback require power management before writing
to the VO GRF register?
The commit message notes that this register requires the power-domain from the
DP controller, but dw_dp_rockchip_hpd_sw_sel() executes without first calling
pm_runtime_get_sync() or similar power management functions.
If the display pipeline is suspended when the USB-C state machine triggers
an event, could this unclocked register write lead to a bus hang or
synchronous external abort?
> +}
> +
> +static void dw_dp_rockchip_hpd_sw_cfg(void *data, bool hpd)
> +{
> + struct rockchip_dw_dp *dp = data;
> + u32 hpd_reg = dp->pdata->hpd_reg[dp->id];
> +
> + dev_dbg(dp->dev, "Force HPD connected=%s\n", str_yes_no(hpd));
> +
> + dp->hpd_cfg = hpd;
> +
> + regmap_write(dp->vo_grf, hpd_reg,
> + FIELD_PREP_WM16(ROCKCHIP_VO_GRF_DP_SINK_HPD_CFG, dp->hpd_cfg));
[Severity: High]
Similar to the concern in dw_dp_rockchip_hpd_sw_sel(), does this register
write also require the DP controller's power domain to be active?
> +}
> +
> static int dw_dp_encoder_atomic_check(struct drm_encoder *encoder,
[ ... ]
> @@ -71,6 +116,34 @@ static const struct drm_encoder_helper_funcs dw_dp_encoder_helper_funcs = {
> .atomic_check = dw_dp_encoder_atomic_check,
> };
>
> +static struct regmap *dp_dp_rockchip_get_vo_grf(struct rockchip_dw_dp *dp)
> +{
> + struct device_node *np = dev_of_node(dp->dev);
> + struct of_phandle_args args;
> + struct regmap *regmap;
> + int ret;
> +
> + ret = of_parse_phandle_with_args(np, "phys", "#phy-cells", 0, &args);
> + if (ret)
> + return ERR_PTR(-ENODEV);
> +
> + /*
> + * Limit this workaround to RK3576 and RK3588, new platforms should
> + * add a VO GRF phandle in the DisplayPort DT node.
> + */
> + if (!of_device_is_compatible(args.np, "rockchip,rk3576-usbdp-phy") &&
> + !of_device_is_compatible(args.np, "rockchip,rk3588-usbdp-phy")) {
> + regmap = ERR_PTR(-ENODEV);
> + goto out_put_node;
> + }
> +
> + regmap = syscon_regmap_lookup_by_phandle(args.np, "rockchip,vo-grf");
[Severity: Medium]
Will this function fail to support the new platforms mentioned in the comment?
If a new platform does not have the "phys" property, of_parse_phandle_with_args()
will fail early and return ERR_PTR(-ENODEV). It appears there is no fallback
to check for the VO GRF phandle directly in the DP node, which would prevent
new platforms from probing successfully.
> +
> +out_put_node:
> + of_node_put(args.np);
> + return regmap;
> +}
> +
> static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *data)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724-synopsys-dw-dp-improvements-v5-0-9445c2e87441@collabora.com?part=9
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v5 10/15] drm/bridge: synopsys: dw-dp: Add Runtime PM support
2026-07-23 22:17 ` [PATCH v5 10/15] drm/bridge: synopsys: dw-dp: Add Runtime PM support Sebastian Reichel
@ 2026-07-23 22:37 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-23 22:37 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: devicetree, robh, dri-devel, conor+dt
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Optional runtime PM support breaks the driver when runtime PM is not enabled by the vendor glue code.
- [High] Using devm_clk_get_enabled() alongside manual clock disable in runtime suspend causes a clock enable count imbalance.
- [High] Missing error check for pm_runtime_get_sync() in dw_dp_bridge_atomic_enable() leads to unclocked register access.
- [High] Hardware registers are accessed without acquiring a PM reference in USB-C mode during bind.
- [High] Inverted teardown order in dw_dp_unbind() drops the runtime PM reference before disabling IRQs and pending work.
- [High] Calling pm_runtime_get_sync() inside dw_dp_bridge_atomic_enable() introduces a sleeping function into an atomic context.
- [High] dw_dp_hpd_work accesses hardware registers without a PM reference, risking a crash if the device is suspended.
--
--- Patch [10]: [PATCH v5 10/15] drm/bridge: synopsys: dw-dp: Add Runtime PM support ---
commit 68cdca52c26d611b36038053d2dc6f02f519e36d
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Add Runtime PM support
This commit adds runtime PM stubs to the Synopsys DesignWare DisplayPort
bridge driver. Support is conditionally enabled based on the vendor specific
glue code hooking it up.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index 9975df8ea37e1..bcf7ad84f2178 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
[ ... ]
> @@ -1467,6 +1468,11 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux,
> if (WARN_ON(msg->size > 16))
> return -E2BIG;
>
> + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dp->dev, pm);
> + ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
> + if (ret)
> + return ret;
> +
[Severity: High]
Does this cause the AUX transfer to unconditionally fail on platforms that do
not enable runtime PM?
The commit message states that runtime PM "must be hooked up in the vendor
specific glue code". However, PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND
internally uses pm_runtime_resume_and_get(), which returns -EACCES when
runtime PM is disabled. Could this cause dw_dp_aux_transfer() to abort
early on platforms where the vendor glue code does not enable PM?
> switch (msg->request & ~DP_AUX_I2C_MOT) {
> case DP_AUX_NATIVE_WRITE:
> case DP_AUX_I2C_WRITE:
[ ... ]
> @@ -1720,6 +1726,8 @@ static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> struct drm_connector_state *conn_state;
> int ret;
>
> + pm_runtime_get_sync(dp->dev);
> +
[Severity: High]
Should the return value of pm_runtime_get_sync() be checked here?
If the device fails to resume, execution proceeds into dw_dp_link_enable()
and dw_dp_video_enable(). Could this lead to asynchronous external aborts
from accessing unclocked hardware registers?
[Severity: High]
Is it safe to call pm_runtime_get_sync() from this atomic context?
DRM guidelines indicate that encoder and bridge atomic enable callbacks run
in atomic context during non-blocking commits where sleeping is forbidden.
Since pm_runtime_get_sync() can sleep, and the newly added
dw_dp_runtime_resume() explicitly calls msleep(), could this cause kernel
warnings or deadlocks during atomic commits?
> connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
> if (!connector) {
> dev_err(dp->dev, "failed to get connector\n");
[ ... ]
> @@ -1973,10 +1986,17 @@ static irqreturn_t dw_dp_irq(int irq, void *data)
> {
> struct dw_dp *dp = data;
> u32 value;
> + int ret;
> +
> + ret = pm_runtime_get_if_active(dp->dev);
> + if (ret <= 0)
> + return IRQ_NONE;
[Severity: High]
Could this silently drop all valid interrupts when runtime PM is disabled?
When runtime PM is not enabled by the vendor glue code,
pm_runtime_get_if_active() returns -EINVAL (which is <= 0). Does this cause
the IRQ handler to always return IRQ_NONE without processing the interrupt?
[Severity: High]
Is a PM reference required for the HPD work scheduled by the IRQ handler?
When the HPD event schedules dw_dp_hpd_work(), the work executes
dw_dp_link_retrain() and accesses hardware registers like DW_DP_CCTL.
Since the work item doesn't acquire a PM reference itself, could it crash by
accessing unclocked registers if the device autosuspends before or during the
work execution?
>
> regmap_read(dp->regmap, DW_DP_GENERAL_INTERRUPT, &value);
> - if (!value)
> + if (!value) {
> + pm_runtime_put_autosuspend(dp->dev);
> return IRQ_NONE;
> + }
>
> if (value & HPD_EVENT)
> dw_dp_handle_hpd_event(dp);
[ ... ]
> @@ -2073,10 +2094,15 @@ int dw_dp_bind(struct dw_dp *dp, struct drm_encoder *encoder)
> }
>
> if (dw_dp_is_routed_to_usb_c(encoder)) {
> + dp->usbc_mode = true;
> dev_dbg(dev, "USB-C mode\n");
>
> if (dp->plat_data.hpd_sw_sel)
> dp->plat_data.hpd_sw_sel(dp->plat_data.data, 1);
> + } else {
> + dp->usbc_mode = false;
> + /* Keep runtime PM enabled to have working native HPD IRQ */
> + pm_runtime_get_sync(dp->dev);
> }
>
> dw_dp_init_hw(dp);
[Severity: High]
Does dw_dp_init_hw() need a PM reference when in USB-C mode?
If usbc_mode is true, pm_runtime_get_sync() is skipped. However,
dw_dp_init_hw() is called immediately after, which writes to hardware
registers. If the device happens to be runtime suspended, could this
unclocked hardware access crash the system?
[ ... ]
> @@ -2106,6 +2136,8 @@ EXPORT_SYMBOL_GPL(dw_dp_bind);
>
> void dw_dp_unbind(struct dw_dp *dp)
> {
> + if (!dp->usbc_mode)
> + pm_runtime_put_sync(dp->dev);
> disable_irq(dp->irq);
> cancel_work_sync(&dp->hpd_work);
> phy_exit(dp->phy);
[Severity: High]
Should the runtime PM reference be held until the teardown is complete?
By calling pm_runtime_put_sync() before disable_irq(), cancel_work_sync(),
and phy_exit(), the device is suspended and its clocks are turned off.
If an HPD interrupt fires before disable_irq(), could it cause an IRQ storm
since the suspended hardware won't allow the interrupt to be cleared?
Additionally, could phy_exit() crash if executed on a suspended device?
[ ... ]
> @@ -2208,6 +2240,41 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_
> }
> EXPORT_SYMBOL_GPL(dw_dp_probe);
>
> +int dw_dp_runtime_suspend(struct dw_dp *dp)
> +{
> + clk_disable_unprepare(dp->aux_clk);
> + clk_disable_unprepare(dp->apb_clk);
> +
[Severity: High]
Does manually disabling these clocks conflict with devres?
The apb_clk and aux_clk are acquired using devm_clk_get_enabled() during
probe, which registers a cleanup action to automatically disable them on
device removal. If dw_dp_runtime_suspend() manually disables them, could
the devres cleanup attempt to disable them a second time on driver unbind,
causing a clock enable count underflow?
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(dw_dp_runtime_suspend);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724-synopsys-dw-dp-improvements-v5-0-9445c2e87441@collabora.com?part=10
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v5 15/15] drm/bridge: synopsys: dw-dp: Add audio support
2026-07-23 22:17 ` [PATCH v5 15/15] drm/bridge: synopsys: dw-dp: Add audio support Sebastian Reichel
@ 2026-07-23 22:38 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-23 22:38 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: robh, conor+dt, devicetree, dri-devel
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Unhandled error from `dw_dp_audio_infoframe_send()` results in a negative SDP index, leading to out-of-bounds memory write and undefined bitwise shifts.
- [High] Typo in sample width validation allows 32-bit audio to bypass checks and silently overflow the hardware register mask.
- [High] `dw_dp_audio_unprepare()` unconditionally clears `audio_sdp_nr` (which defaults to 0), potentially corrupting the active video stream's VSC SDP configuration.
- [Medium] `dw_dp_audio_unprepare()` fails to clear the audio stream and timestamp SDP enablement bits, leaving the hardware in an inconsistent state.
--
--- Patch [15]: [PATCH v5 15/15] drm/bridge: synopsys: dw-dp: Add audio support ---
commit 8d3e8e3c7ab668f3eaf614e21b09c813acc45d46
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Add audio support
This commit adds audio support to the Synopsys DesignWare DisplayPort
controller. It implements the necessary DRM bridge audio callbacks and
configures the hardware to transmit I2S or SPDIF audio streams.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index 2bd37e5c36da8..a9e04cee002e8 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
[ ... ]
> @@ -1890,6 +1928,205 @@ static void dw_dp_bridge_oob_notify(struct drm_bridge *bridge,
[ ... ]
> +static void dw_dp_audio_unprepare(struct drm_bridge *bridge,
> + struct drm_connector *connector)
> +{
> + struct dw_dp *dp = bridge_to_dp(bridge);
> +
> + /* Disable all audio streams */
> + regmap_update_bits(dp->regmap, DW_DP_AUD_CONFIG1, AUDIO_DATA_IN_EN,
> + FIELD_PREP(AUDIO_DATA_IN_EN, 0));
> +
[Severity: Medium]
Does this leave the hardware attempting to send stale audio SDPs?
The setup logic in dw_dp_audio_prepare() enables EN_AUDIO_STREAM_SDP and
EN_AUDIO_TIMESTAMP_SDP in the DW_DP_SDP_VERTICAL_CTRL and
DW_DP_SDP_HORIZONTAL_CTRL registers.
However, this cleanup path disables AUDIO_DATA_IN_EN and clears the
infoframe SDP slot, but does not appear to clear the stream and timestamp
SDP bits.
> + if (dp->audio_interface == DW_DP_AUDIO_SPDIF)
> + clk_disable_unprepare(dp->spdif_clk);
> + else if (dp->audio_interface == DW_DP_AUDIO_I2S)
> + clk_disable_unprepare(dp->i2s_clk);
> +
> + dp->audio_interface = DW_DP_AUDIO_UNUSED;
> +
> + dw_dp_clear_sdp(dp, dp->audio_sdp_nr);
[Severity: High]
Can this unintentionally clear the video stream configuration?
When the dw_dp structure is allocated, dp->audio_sdp_nr is initialized to 0.
If a userspace application opens the audio device but closes it before
hardware parameters are configured, dw_dp_audio_shutdown() will still be
called, leading to an unconditional call to dw_dp_clear_sdp(dp, 0).
Since slot 0 is typically already in use by the video stream (allocated by
dw_dp_send_vsc_sdp), this could cause the display hardware to lose its VSC
SDP configuration and break the display.
> +}
> +
> +static int dw_dp_audio_prepare(struct drm_bridge *bridge,
> + struct drm_connector *connector,
> + struct hdmi_codec_daifmt *daifmt,
> + struct hdmi_codec_params *params)
> +{
[ ... ]
> + if (params->sample_width < 16 || params->sample_width >> 24) {
> + dev_err(dp->dev, "invalid data sample width %d\n", params->sample_width);
> + return -EINVAL;
> + }
[Severity: High]
Is this a typo where >> 24 was intended to be > 24?
If an ALSA client configures a stream with a 32-bit sample width,
params->sample_width >> 24 evaluates to 0, which bypasses this check.
Later in this function, the 32 value is passed to
FIELD_PREP(AUDIO_DATA_WIDTH, 32). Since AUDIO_DATA_WIDTH is GENMASK(9, 5),
this will silently overflow the 5-bit mask and truncate to 0,
misconfiguring the hardware.
[ ... ]
> + ret = dw_dp_audio_infoframe_send(dp);
> + if (ret < 0)
> + dev_err(dp->dev, "failed to send audio infoframe\n");
> +
> + dp->audio_sdp_nr = ret;
[Severity: Critical]
Could this lead to an out-of-bounds memory write if sending the infoframe
fails?
If dw_dp_audio_infoframe_send() returns a negative error code (for example,
if dw_dp_send_sdp() returns -EBUSY because the SDP bank is full), that
negative value is assigned to dp->audio_sdp_nr while the function proceeds
without returning the error.
During teardown, dw_dp_audio_unprepare() will then call:
dw_dp_clear_sdp(dp, dp->audio_sdp_nr);
In dw_dp_clear_sdp(), this results in negative bit shifts and an
out-of-bounds write:
EN_VERTICAL_SDP << nr
clear_bit(nr, dp->sdp_reg_bank);
Would it be safer to return the error code here instead of continuing?
> +
> + dev_dbg(dp->dev, "audio prepare with %d channels using DAI=%d\n",
> + dp->audio_channels, dp->audio_interface);
> +
> + return 0;
> +}
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724-synopsys-dw-dp-improvements-v5-0-9445c2e87441@collabora.com?part=15
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v5 11/15] drm/rockchip: dw_dp: Add runtime PM support
2026-07-23 22:17 ` [PATCH v5 11/15] drm/rockchip: dw_dp: Add runtime " Sebastian Reichel
@ 2026-07-23 22:40 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-23 22:40 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: robh, conor+dt, dri-devel, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Incorrect teardown sequence in dw_dp_rockchip_unbind() causes invalid hardware access.
- [Medium] Missing PM runtime cleanup when dw_dp_bind() fails.
--
--- Patch [11]: [PATCH v5 11/15] drm/rockchip: dw_dp: Add runtime PM support ---
commit b12de6118be77a7ac8babe1d82f46a8db5857b90
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/rockchip: dw_dp: Add runtime PM support
Add support for runtime PM to the Rockchip RK3576/3588 Synopsys
DesignWare DisplayPort driver.
> diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> index 0ecbad24dd4e8..b1a69d8821a2c 100644
> --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
[ ... ]
> @@ -165,18 +178,32 @@ static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *
> return ret;
> drm_encoder_helper_add(encoder, &dw_dp_encoder_helper_funcs);
>
> + pm_runtime_use_autosuspend(dev);
> + pm_runtime_set_autosuspend_delay(dev, 500);
> + pm_runtime_set_active(dev);
> + pm_runtime_enable(dev);
> +
> ret = dw_dp_bind(dp->base, encoder);
> if (ret)
> return ret;
[Severity: Medium]
If dw_dp_bind() returns an error, does this early return bypass the PM runtime
cleanup sequence?
This leaves the device's PM state enabled and unbalanced. Should this jump to
the error path to properly disable PM runtime on failure?
>
> connector = drm_bridge_connector_init(drm_dev, encoder);
> if (IS_ERR(connector)) {
> - dw_dp_unbind(dp->base);
> - return dev_err_probe(dev, PTR_ERR(connector),
> - "Failed to init bridge connector\n");
> + ret = PTR_ERR(connector);
> + dev_err_probe(dev, ret, "Failed to init bridge connector\n");
> + goto error;
> }
>
> + pm_request_idle(dev);
> +
> return 0;
> +
> +error:
> + pm_runtime_dont_use_autosuspend(dev);
> + pm_runtime_resume(dev);
> + pm_runtime_disable(dev);
> + dw_dp_unbind(dp->base);
> + return ret;
> }
>
> static void dw_dp_rockchip_unbind(struct device *dev, struct device *master,
> @@ -185,6 +212,9 @@ static void dw_dp_rockchip_unbind(struct device *dev, struct device *master,
> struct rockchip_dw_dp *dp = dev_get_drvdata(dev);
>
> dw_dp_unbind(dp->base);
> + pm_runtime_dont_use_autosuspend(dev);
> + pm_runtime_resume(dev);
> + pm_runtime_disable(dev);
> }
[Severity: High]
Does this sequence cause invalid hardware access if the device is suspended?
dw_dp_unbind() is called before disabling PM runtime. dw_dp_unbind() calls
phy_exit(), shutting down the PHY. If the device was suspended prior to unbind,
pm_runtime_resume() forces a wake-up, which calls dw_dp_runtime_resume() then
dw_dp_init_hw().
This would perform hardware register writes on a device whose PHY has already
been exited. Should PM runtime be disabled before calling dw_dp_unbind()?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724-synopsys-dw-dp-improvements-v5-0-9445c2e87441@collabora.com?part=11
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v5 03/15] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal
2026-07-23 22:17 ` [PATCH v5 03/15] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal Sebastian Reichel
@ 2026-07-23 22:48 ` sashiko-bot
0 siblings, 0 replies; 25+ messages in thread
From: sashiko-bot @ 2026-07-23 22:48 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: conor+dt, dri-devel, robh, devicetree
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing `reinit_completion` in `dw_dp_aux_transfer` allows stale completion signals to falsely succeed future transfers.
- [High] The AUX device is registered and exposed to userspace before the hardware is initialized and the IRQ is enabled.
- [High] Concurrent HPD interrupts can overwrite `dp->hotplug.long_hpd`, causing dropped hotplug events.
--
--- Patch [3]: [PATCH v5 03/15] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal ---
commit bc7168d1bf0fe1e3fae642d5cb72e8aa27942948
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal
The driver is currently missing to fully clean up after itself. Ensure
that the mutex is cleaned up.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index 21bd08571dcfe..b28040d69fabf 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> @@ -2031,10 +2031,13 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_
> dp->pixel_mode = plat_data->pixel_mode;
> dp->plat_data.max_link_rate = plat_data->max_link_rate;
>
> - mutex_init(&dp->irq_lock);
> INIT_WORK(&dp->hpd_work, dw_dp_hpd_work);
[Severity: High]
This is a pre-existing issue, but could concurrent HPD interrupts overwrite
dp->hotplug.long_hpd, causing dropped hotplug events?
In drivers/gpu/drm/bridge/synopsys/dw-dp.c:dw_dp_handle_hpd_event(), the flag
is updated based on interrupt status:
if (value & HPD_IRQ) {
...
dp->hotplug.long_hpd = false;
...
}
if (value & HPD_HOT_PLUG) {
...
dp->hotplug.long_hpd = true;
...
}
Since schedule_work() ensures only a single instance of the work runs,
multiple interrupts firing in rapid succession (e.g., a hot plug followed
immediately by an IRQ/short HPD from the sink requesting link training)
could overwrite the long_hpd flag before the worker can process the first
event.
The worker would only see the final state (e.g., false for short HPD),
permanently dropping the hotplug notification.
> init_completion(&dp->complete);
[Severity: High]
This is a pre-existing issue, but is there a missing reinit_completion() in
dw_dp_aux_transfer() that could allow stale completion signals to falsely
succeed future transfers?
In drivers/gpu/drm/bridge/synopsys/dw-dp.c:dw_dp_aux_transfer(), the driver
waits for an AUX reply:
regmap_write(dp->regmap, DW_DP_AUX_CMD, value);
status = wait_for_completion_timeout(&dp->complete, timeout);
if (!status) {
dev_err(dp->dev, "timeout waiting for AUX reply\n");
return -ETIMEDOUT;
}
If an AUX transfer times out, the completion's internal done counter is not
consumed. If the hardware later fires a delayed AUX_REPLY_EVENT interrupt,
the IRQ handler will call complete(&dp->complete), incrementing the counter.
Subsequent calls to dw_dp_aux_transfer() would then immediately return from
wait_for_completion_timeout() without waiting for the actual hardware reply.
[Severity: High]
This is another pre-existing issue, but is the AUX device registered and
exposed to userspace before the hardware is initialized and the IRQ is
enabled?
In drivers/gpu/drm/bridge/synopsys/dw-dp.c:dw_dp_bind():
ret = drm_dp_aux_register(&dp->aux);
if (ret) {
...
}
...
dw_dp_init_hw(dp);
ret = phy_init(dp->phy);
...
enable_irq(dp->irq);
If a userspace process opens the /dev/drm_dp_auxN device and issues a read
during the window after drm_dp_aux_register() but before enable_irq(), the
transfer will time out. This initial timeout would permanently corrupt the
completion state as described above, causing future legitimate EDID reads and
link training to fail.
>
> + ret = devm_mutex_init(dev, &dp->irq_lock);
> + if (ret)
> + return ERR_PTR(ret);
> +
> res = devm_platform_ioremap_resource(pdev, 0);
> if (IS_ERR(res))
> return ERR_CAST(res);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260724-synopsys-dw-dp-improvements-v5-0-9445c2e87441@collabora.com?part=3
^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2026-07-23 22:48 UTC | newest]
Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-23 22:17 [PATCH v5 00/15] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 01/15] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback Sebastian Reichel
2026-07-23 22:34 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 02/15] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind Sebastian Reichel
2026-07-23 22:32 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 03/15] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal Sebastian Reichel
2026-07-23 22:48 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 04/15] drm/bridge: synopsys: dw-dp: Move dw_dp_bridge_atomic_get_output_bus_fmts() Sebastian Reichel
2026-07-23 22:28 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 05/15] drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED Sebastian Reichel
2026-07-23 22:31 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 06/15] drm/bridge: synopsys: dw-dp: Add follow-up bridge support Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 07/15] drm/bridge: Add out-of-band HPD notify handler Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 08/15] drm/bridge: synopsys: dw-dp: Support software triggered OOB HPD Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 09/15] drm/rockchip: dw_dp: Implement out-of-band HPD handling Sebastian Reichel
2026-07-23 22:34 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 10/15] drm/bridge: synopsys: dw-dp: Add Runtime PM support Sebastian Reichel
2026-07-23 22:37 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 11/15] drm/rockchip: dw_dp: Add runtime " Sebastian Reichel
2026-07-23 22:40 ` sashiko-bot
2026-07-23 22:17 ` [PATCH v5 12/15] drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 13/15] drm/bridge: synopsys: dw-dp: Use regmap_set_bits in dw_dp_send_sdp Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 14/15] dt-bindings: display: rockchip: dw-dp: Fix sound DAI cells Sebastian Reichel
2026-07-23 22:17 ` [PATCH v5 15/15] drm/bridge: synopsys: dw-dp: Add audio support Sebastian Reichel
2026-07-23 22:38 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox