* [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms
@ 2026-07-28 17:40 Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 01/18] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback Sebastian Reichel
` (17 more replies)
0 siblings, 18 replies; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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, Krzysztof Kozlowski
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 v7:
- Link to v6: https://lore.kernel.org/r/20260724-synopsys-dw-dp-improvements-v6-0-041d99a19c4e@collabora.com
- Move drm_bridge_add() to probe and only attach it in the bind
function, as the object is re-used and not fully cleared by
drm_bridge_remove (Sashiko)
- Add a new patch resetting the DP AUX sub-controller on message
timeout to have decent error recovery (Sashiko)
- Fix dp_dp_rockchip_get_vo_grf -> dw_dp_rockchip_get_vo_grf (Sashiko)
- Introduce pm_active in the PM runtime patch, to early exit
on atomic_disable when atomic_enable failed to get runtime
PM enabled (Sashiko)
- Introduce drm_bound in the PM runtime patch, which is (un)set
when the driver is bound to a DRM encoder and used to enable
IRQ enabling in the runtime PM handlers. This in turn protects
the system of potential spurious interrupts. (Sashiko)
- In the audio patch, fix support for hotplug with a running
audio stream. (Sashiko)
- To avoid further blowing up the series, I've decided not to
act on these "pre-existing" issues reported by Sashiko. I
think it makes sense to handle them separately:
* Short HPD and Long HPD events share a single boolean state
variable, causing critical hot plug events to be lost if
interrupts arrive in quick succession.
* DP AUX lifetime complains (registered too early / unregistered
too late); reported multiple times
* The driver fails to support DP AUX short reads, incorrectly
rejecting them with -EBUSY instead of returning the number
of bytes successfully transferred, which breaks I2C-over-AUX.
- Collected Reviewed-by from Krzysztof Kozlowski on the
DT binding patch
Changes in v6:
- Link to v5: https://lore.kernel.org/r/20260724-synopsys-dw-dp-improvements-v5-0-9445c2e87441@collabora.com
- Fix overlong commit message in mutex patch
- Update MEDIA_BUS_FMT_FIXED patch, to use input format as output format
when output format is MEDIA_BUS_FMT_FIXED (Sashiko)
- Drop patch moving dw_dp_bridge_atomic_get_output_bus_fmts (no longer
needed)
- Reword the comment about platforms other than RK3576 and RK3588
shouldn't get the VO GRF from the USBDP to make it more obvious
that no such platform exist at the moment and thus the alternative
path will not be implemented at this point in time (Sashiko)
- Update the runtime PM code to have much better error handling
as well as support for platforms not using runtime PM (Sashiko)
- Update the runtime PM code to keep the device suspended when
probed, but unbound as a nice side effect
- Fix error handling for dw_dp_audio_infoframe_send() in audio
patch (Sashiko)
- Fix typo in sample width check in audio patch (Sashiko)
- Improve cleanup of SDP in audio patch (Sashiko)
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 (18):
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: Add missing reinit_completion
drm/bridge: synopsys: dw-dp: Reset AUX channel on transfer timeout
drm/bridge: synopsys: dw-dp: Free output_fmts when none are valid
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: Clear only enabled SDPs on atomic disable
drm/bridge: synopsys: dw-dp: Add audio support
.../bindings/display/rockchip/rockchip,dw-dp.yaml | 9 +-
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 684 ++++++++++++++++++---
drivers/gpu/drm/display/drm_bridge_connector.c | 6 +
drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 219 ++++++-
include/drm/bridge/dw_dp.h | 11 +-
include/drm/drm_bridge.h | 14 +
6 files changed, 840 insertions(+), 103 deletions(-)
---
base-commit: df0311845915442ab75d03ee89fb443b444c1370
change-id: 20260325-synopsys-dw-dp-improvements-7da2e98df1dd
Best regards,
--
Sebastian Reichel <sebastian.reichel@collabora.com>
^ permalink raw reply [flat|nested] 30+ messages in thread
* [PATCH v7 01/18] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 17:59 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 02/18] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind Sebastian Reichel
` (16 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 | 121 +++++++++++++++---------------
drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 53 +++++++------
include/drm/bridge/dw_dp.h | 5 +-
3 files changed, 94 insertions(+), 85 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 3445c82e6f50..67d9cd0bf5fd 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1954,20 +1954,59 @@ 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 = &dp->bridge;
+ int 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)
+ return dev_err_probe(dev, ret, "Aux register failed\n");
+
+ 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);
+
+ 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);
}
+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 device *dev = &pdev->dev;
struct drm_bridge *bridge;
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 +2015,18 @@ 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;
+ 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);
+
mutex_init(&dp->irq_lock);
INIT_WORK(&dp->hpd_work, dw_dp_hpd_work);
init_completion(&dp->complete);
@@ -2035,69 +2083,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] 30+ messages in thread
* [PATCH v7 02/18] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 01/18] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 18:09 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 03/18] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal Sebastian Reichel
` (15 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 67d9cd0bf5fd..8393f9c9b0f0 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1996,6 +1996,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);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v7 03/18] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 01/18] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 02/18] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 18:20 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 04/18] drm/bridge: synopsys: dw-dp: Add missing reinit_completion Sebastian Reichel
` (14 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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.
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 | 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 8393f9c9b0f0..ca7ff544d243 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -2028,10 +2028,13 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_
if (ret)
return ERR_PTR(ret);
- 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] 30+ messages in thread
* [PATCH v7 04/18] drm/bridge: synopsys: dw-dp: Add missing reinit_completion
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (2 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 03/18] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 18:34 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 05/18] drm/bridge: synopsys: dw-dp: Reset AUX channel on transfer timeout Sebastian Reichel
` (13 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 DP AUX transfer method uses a completion triggered by an interrupt,
which can timeout. If the function runs into the timeout and the
interrupt fires afterwards, the following DP aux transfer completion
would trigger immediately without waiting for the interrupt. This in
turn means the next one would also be broken and so on.
Fix this potential issue by re-initializing the completion before
sending the AUX command.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: 86eecc3a9c2e ("drm/bridge: synopsys: Add DW DPTX Controller support library")
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index ca7ff544d243..951f1e280e47 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1465,6 +1465,8 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux,
if (WARN_ON(msg->size > 16))
return -E2BIG;
+ reinit_completion(&dp->complete);
+
switch (msg->request & ~DP_AUX_I2C_MOT) {
case DP_AUX_NATIVE_WRITE:
case DP_AUX_I2C_WRITE:
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v7 05/18] drm/bridge: synopsys: dw-dp: Reset AUX channel on transfer timeout
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (3 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 04/18] drm/bridge: synopsys: dw-dp: Add missing reinit_completion Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 18:43 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 06/18] drm/bridge: synopsys: dw-dp: Free output_fmts when none are valid Sebastian Reichel
` (12 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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
Re-initializing the completion before each transfer discards a stale
completion that has already been signalled, but it does not protect
against a reply interrupt that fires after reinit_completion(): the
delayed complete() then satisfies the wait of the following transfer,
which reads back the status and data of the aborted transaction.
Avoid this by resetting the AUX controller when a transfer times out,
so no delayed reply interrupt can leak into a subsequent transfer.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Fixes: 86eecc3a9c2e ("drm/bridge: synopsys: Add DW DPTX Controller support library")
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 951f1e280e47..1281fe28b0f9 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1493,6 +1493,11 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux,
status = wait_for_completion_timeout(&dp->complete, timeout);
if (!status) {
dev_err(dp->dev, "timeout waiting for AUX reply\n");
+ regmap_update_bits(dp->regmap, DW_DP_SOFT_RESET_CTRL,
+ AUX_RESET, FIELD_PREP(AUX_RESET, 1));
+ usleep_range(10, 20);
+ regmap_update_bits(dp->regmap, DW_DP_SOFT_RESET_CTRL,
+ AUX_RESET, FIELD_PREP(AUX_RESET, 0));
return -ETIMEDOUT;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v7 06/18] drm/bridge: synopsys: dw-dp: Free output_fmts when none are valid
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (4 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 05/18] drm/bridge: synopsys: dw-dp: Reset AUX channel on transfer timeout Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 07/18] drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED Sebastian Reichel
` (11 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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
If dw_dp_bandwidth_ok() returns false for all formats, *num_output_fmts
might end up becoming 0. In this case functions calling it assume that
nothing needs to be free'd, so free output_fmts within the function to
avoid leaking memory.
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 | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 1281fe28b0f9..ebb7280ad84c 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1802,6 +1802,11 @@ static u32 *dw_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
output_fmts[j++] = fmt->bus_format;
}
+ if (j == 0) {
+ kfree(output_fmts);
+ output_fmts = NULL;
+ }
+
*num_output_fmts = j;
return output_fmts;
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v7 07/18] drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (5 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 06/18] drm/bridge: synopsys: dw-dp: Free output_fmts when none are valid Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 08/18] drm/bridge: synopsys: dw-dp: Add follow-up bridge support Sebastian Reichel
` (10 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 output format by
using the negotiated input format.
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 | 34 +++++++++++++++++++++++++++++++--
1 file changed, 32 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index ebb7280ad84c..2a1ed27a19fe 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1535,6 +1535,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;
@@ -1545,7 +1546,10 @@ 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)
+ out_bus_format = bridge_state->input_bus_cfg.format;
+
+ fmt = dw_dp_get_output_format(out_bus_format);
if (!fmt)
return -EINVAL;
@@ -1812,6 +1816,32 @@ static u32 *dw_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
return output_fmts;
}
+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;
@@ -1829,7 +1859,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] 30+ messages in thread
* [PATCH v7 08/18] drm/bridge: synopsys: dw-dp: Add follow-up bridge support
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (6 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 07/18] drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 09/18] drm/bridge: Add out-of-band HPD notify handler Sebastian Reichel
` (9 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 2a1ed27a19fe..719f22578ed6 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);
};
@@ -2016,18 +2018,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);
@@ -2040,6 +2059,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);
}
EXPORT_SYMBOL_GPL(dw_dp_unbind);
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v7 09/18] drm/bridge: Add out-of-band HPD notify handler
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (7 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 08/18] drm/bridge: synopsys: dw-dp: Add follow-up bridge support Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 19:19 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 10/18] drm/bridge: synopsys: dw-dp: Support software triggered OOB HPD Sebastian Reichel
` (8 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 58fff047f43b..713652fca033 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] 30+ messages in thread
* [PATCH v7 10/18] drm/bridge: synopsys: dw-dp: Support software triggered OOB HPD
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (8 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 09/18] drm/bridge: Add out-of-band HPD notify handler Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 11/18] drm/rockchip: dw_dp: Implement out-of-band HPD handling Sebastian Reichel
` (7 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 719f22578ed6..d4f75b7f85d0 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1857,6 +1857,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,
@@ -1869,6 +1882,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)
@@ -1998,6 +2012,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;
@@ -2032,6 +2059,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);
@@ -2078,6 +2112,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;
bridge = &dp->bridge;
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] 30+ messages in thread
* [PATCH v7 11/18] drm/rockchip: dw_dp: Implement out-of-band HPD handling
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (9 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 10/18] drm/bridge: synopsys: dw-dp: Support software triggered OOB HPD Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 19:41 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 12/18] drm/bridge: synopsys: dw-dp: Add Runtime PM support Sebastian Reichel
` (6 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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. Thus this patch prevents massive SError problems once
runtime PM is enabled. Apart from that it avoids 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 | 124 ++++++++++++++++++++++++++++--
1 file changed, 119 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..b2e817044c94 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,35 @@ static const struct drm_encoder_helper_funcs dw_dp_encoder_helper_funcs = {
.atomic_check = dw_dp_encoder_atomic_check,
};
+static struct regmap *dw_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, potential future platforms
+ * reusing the driver should just add a VO GRF phandle in the DisplayPort
+ * controller 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 +195,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 = dw_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 +255,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] 30+ messages in thread
* [PATCH v7 12/18] drm/bridge: synopsys: dw-dp: Add Runtime PM support
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (10 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 11/18] drm/rockchip: dw_dp: Implement out-of-band HPD handling Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 19:58 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 13/18] drm/rockchip: dw_dp: Add runtime " Sebastian Reichel
` (5 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 | 126 ++++++++++++++++++++++++++++++--
include/drm/bridge/dw_dp.h | 3 +
2 files changed, 121 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index d4f75b7f85d0..8b650f904575 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -328,6 +328,9 @@ struct dw_dp {
struct dw_dp_link link;
struct dw_dp_plat_data plat_data;
u8 pixel_mode;
+ bool usbc_mode;
+ bool pm_active;
+ bool drm_bound;
struct drm_bridge *next_bridge;
@@ -1467,6 +1470,11 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux,
if (WARN_ON(msg->size > 16))
return -E2BIG;
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dp->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
reinit_completion(&dp->complete);
switch (msg->request & ~DP_AUX_I2C_MOT) {
@@ -1664,6 +1672,13 @@ static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_connector_state *conn_state;
int ret;
+ ret = pm_runtime_get_active(dp->dev, RPM_TRANSPARENT);
+ if (ret) {
+ dev_err(dp->dev, "runtime PM failure\n");
+ return;
+ }
+ dp->pm_active = true;
+
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
if (!connector) {
dev_err(dp->dev, "failed to get connector\n");
@@ -1714,10 +1729,15 @@ static void dw_dp_bridge_atomic_disable(struct drm_bridge *bridge,
{
struct dw_dp *dp = bridge_to_dp(bridge);
+ if (!dp->pm_active)
+ return;
+ dp->pm_active = false;
+
dw_dp_video_disable(dp);
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)
@@ -1738,6 +1758,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_AUTOSUSPEND(dp->dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return connector_status_disconnected;
+
if (!dw_dp_hpd_detect(dp))
return connector_status_disconnected;
@@ -1920,6 +1944,11 @@ static void dw_dp_hpd_work(struct work_struct *work)
bool long_hpd;
int ret;
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dp->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return;
+
mutex_lock(&dp->irq_lock);
long_hpd = dp->hotplug.long_hpd;
mutex_unlock(&dp->irq_lock);
@@ -2059,25 +2088,52 @@ 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");
+ dp->usbc_mode = dw_dp_is_routed_to_usb_c(encoder);
- if (dp->plat_data.hpd_sw_sel)
- dp->plat_data.hpd_sw_sel(dp->plat_data.data, 1);
+ ret = pm_runtime_get_active(dev, RPM_TRANSPARENT);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to get runtime PM\n");
+ goto put_next_bridge;
}
- dw_dp_init_hw(dp);
+ /* resume once if runtime PM is disabled */
+ if (!pm_runtime_enabled(dev)) {
+ ret = dw_dp_runtime_resume(dp);
+ if (ret)
+ goto put_runtime_pm;
+ }
+
+ if (dp->plat_data.hpd_sw_sel)
+ dp->plat_data.hpd_sw_sel(dp->plat_data.data, dp->usbc_mode);
ret = phy_init(dp->phy);
if (ret) {
dev_err_probe(dev, ret, "phy init failed\n");
- goto put_next_bridge;
+ goto put_manual_pm;
}
+ dp->drm_bound = true;
enable_irq(dp->irq);
+ /*
+ * USB-C has out-of-band hotplug detection, so device may enter
+ * runtime suspend. Native mode needs to be resumed for working
+ * hotplug detection.
+ */
+ if (dp->usbc_mode) {
+ dev_dbg(dev, "USB-C mode\n");
+ pm_runtime_put_autosuspend(dp->dev);
+ }
+
return 0;
+put_manual_pm:
+ if (!pm_runtime_enabled(dev))
+ dw_dp_runtime_suspend(dp);
+
+put_runtime_pm:
+ pm_runtime_put_sync(dp->dev);
+
put_next_bridge:
drm_bridge_put(dp->next_bridge);
@@ -2090,9 +2146,22 @@ EXPORT_SYMBOL_GPL(dw_dp_bind);
void dw_dp_unbind(struct dw_dp *dp)
{
+ if (dp->usbc_mode)
+ pm_runtime_get_sync(dp->dev);
+
+ dp->drm_bound = false;
disable_irq(dp->irq);
+
cancel_work_sync(&dp->hpd_work);
phy_exit(dp->phy);
+
+ /* Last PM runtime suspend must be done with drm_bound = false */
+ pm_runtime_put_sync(dp->dev);
+
+ /* suspend once if runtime PM is disabled */
+ if (!pm_runtime_enabled(dp->dev))
+ dw_dp_runtime_suspend(dp);
+
drm_bridge_put(dp->next_bridge);
drm_dp_aux_unregister(&dp->aux);
}
@@ -2151,13 +2220,13 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_
return ERR_CAST(dp->phy);
}
- dp->apb_clk = devm_clk_get_enabled(dev, "apb");
+ dp->apb_clk = devm_clk_get(dev, "apb");
if (IS_ERR(dp->apb_clk)) {
dev_err_probe(dev, PTR_ERR(dp->apb_clk), "failed to get apb clock\n");
return ERR_CAST(dp->apb_clk);
}
- dp->aux_clk = devm_clk_get_enabled(dev, "aux");
+ dp->aux_clk = devm_clk_get(dev, "aux");
if (IS_ERR(dp->aux_clk)) {
dev_err_probe(dev, PTR_ERR(dp->aux_clk), "failed to get aux clock\n");
return ERR_CAST(dp->aux_clk);
@@ -2202,6 +2271,47 @@ 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)
+{
+ if (dp->drm_bound)
+ disable_irq(dp->irq);
+
+ 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);
+
+ if (dp->drm_bound)
+ enable_irq(dp->irq);
+
+ 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] 30+ messages in thread
* [PATCH v7 13/18] drm/rockchip: dw_dp: Add runtime PM support
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (11 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 12/18] drm/bridge: synopsys: dw-dp: Add Runtime PM support Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 20:14 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 14/18] drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access Sebastian Reichel
` (4 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 | 50 ++++++++++++++++++++++++++++++-
1 file changed, 49 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
index b2e817044c94..01915f7b24d5 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_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_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));
}
@@ -200,7 +213,7 @@ static int dw_dp_rockchip_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct rockchip_dw_dp *dp;
struct resource *res;
- int id;
+ int id, ret;
plat_data_const = device_get_match_data(dev);
if (!plat_data_const)
@@ -247,6 +260,12 @@ static int dw_dp_rockchip_probe(struct platform_device *pdev)
if (IS_ERR(dp->base))
return PTR_ERR(dp->base);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, 500);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
return component_add(&pdev->dev, &dw_dp_rockchip_component_ops);
}
@@ -255,6 +274,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},
@@ -289,5 +336,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] 30+ messages in thread
* [PATCH v7 14/18] drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (12 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 13/18] drm/rockchip: dw_dp: Add runtime " Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 20:26 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 15/18] drm/bridge: synopsys: dw-dp: Use regmap_set_bits in dw_dp_send_sdp Sebastian Reichel
` (3 subsequent siblings)
17 siblings, 1 reply; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 8b650f904575..50eb7a467932 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;
@@ -1047,11 +1049,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;
@@ -1691,7 +1695,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) {
@@ -1735,7 +1740,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);
}
@@ -2204,6 +2210,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] 30+ messages in thread
* [PATCH v7 15/18] drm/bridge: synopsys: dw-dp: Use regmap_set_bits in dw_dp_send_sdp
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (13 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 14/18] drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 16/18] dt-bindings: display: rockchip: dw-dp: Fix sound DAI cells Sebastian Reichel
` (2 subsequent siblings)
17 siblings, 0 replies; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 50eb7a467932..fcee47c6716a 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1068,14 +1068,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] 30+ messages in thread
* [PATCH v7 16/18] dt-bindings: display: rockchip: dw-dp: Fix sound DAI cells
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (14 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 15/18] drm/bridge: synopsys: dw-dp: Use regmap_set_bits in dw_dp_send_sdp Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 17/18] drm/bridge: synopsys: dw-dp: Clear only enabled SDPs on atomic disable Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 18/18] drm/bridge: synopsys: dw-dp: Add audio support Sebastian Reichel
17 siblings, 0 replies; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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, Krzysztof Kozlowski
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.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
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] 30+ messages in thread
* [PATCH v7 17/18] drm/bridge: synopsys: dw-dp: Clear only enabled SDPs on atomic disable
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (15 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 16/18] dt-bindings: display: rockchip: dw-dp: Fix sound DAI cells Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
2026-07-28 20:50 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 18/18] drm/bridge: synopsys: dw-dp: Add audio support Sebastian Reichel
17 siblings, 1 reply; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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
dw_dp_bridge_atomic_disable() bulk-cleared the whole SDP register bank
allocation bitmap via bitmap_zero() resulting in the loss of all
tracking information. This results in a slot potentially being handed
out again by dw_dp_send_sdp(), which is still considered to be held
by the previous owner. Then the previous owner might free up the wrong
SDP later on.
Instead of bulk clearing the tracking information, the new
implementation only clears the SDPs actually configured during
dw_dp_bridge_atomic_enable() instead of the entire bank. The
introduced functionality for that will also be used by the to-be-added
audio infrastructure.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-dp.c | 27 ++++++++++++++++++++++++---
1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index fcee47c6716a..c0026bd4c7b5 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -336,6 +336,7 @@ struct dw_dp {
struct drm_bridge *next_bridge;
+ int vsc_sdp_nr;
DECLARE_BITMAP(sdp_reg_bank, SDP_REG_BANK_SIZE);
};
@@ -1075,7 +1076,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)
@@ -1393,7 +1406,7 @@ static int dw_dp_video_enable(struct dw_dp *dp)
FIELD_PREP(VIDEO_STREAM_ENABLE, 1));
if (dw_dp_video_need_vsc_sdp(dp))
- dw_dp_send_vsc_sdp(dp);
+ dp->vsc_sdp_nr = dw_dp_send_vsc_sdp(dp);
return 0;
}
@@ -1738,8 +1751,14 @@ static void dw_dp_bridge_atomic_disable(struct drm_bridge *bridge,
dw_dp_video_disable(dp);
dw_dp_link_disable(dp);
+
+ if (dp->vsc_sdp_nr >= 0) {
+ dw_dp_clear_sdp(dp, dp->vsc_sdp_nr);
+ dp->vsc_sdp_nr = -1;
+ }
scoped_guard(mutex, &dp->sdp_lock)
- bitmap_zero(dp->sdp_reg_bank, SDP_REG_BANK_SIZE);
+ clear_bit(0, dp->sdp_reg_bank);
+
dw_dp_reset(dp);
pm_runtime_put_autosuspend(dp->dev);
}
@@ -2191,6 +2210,8 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_
dp->plat_data.data = plat_data->data;
dp->plat_data.max_link_rate = plat_data->max_link_rate;
+ dp->vsc_sdp_nr = -1;
+
bridge = &dp->bridge;
bridge->of_node = dev->of_node;
bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
--
2.53.0
^ permalink raw reply related [flat|nested] 30+ messages in thread
* [PATCH v7 18/18] drm/bridge: synopsys: dw-dp: Add audio support
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
` (16 preceding siblings ...)
2026-07-28 17:40 ` [PATCH v7 17/18] drm/bridge: synopsys: dw-dp: Clear only enabled SDPs on atomic disable Sebastian Reichel
@ 2026-07-28 17:40 ` Sebastian Reichel
17 siblings, 0 replies; 30+ messages in thread
From: Sebastian Reichel @ 2026-07-28 17:40 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 | 298 +++++++++++++++++++++++++++++++-
1 file changed, 297 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index c0026bd4c7b5..3b73e822478d 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,10 +343,16 @@ 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_sample_width;
+ int audio_sdp_nr;
/* Serialize hpd status access */
struct mutex irq_lock;
/* Serialize sdp_reg_bank access */
struct mutex sdp_lock;
+ /* Serialize audio state */
+ struct mutex audio_lock;
struct drm_dp_aux aux;
@@ -1679,6 +1708,247 @@ static int dw_dp_link_enable(struct dw_dp *dp)
return ret;
}
+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 void __dw_dp_audio_disable(struct dw_dp *dp)
+{
+ if (dp->audio_sdp_nr >= 0) {
+ dw_dp_clear_sdp(dp, dp->audio_sdp_nr);
+ dp->audio_sdp_nr = -1;
+ }
+
+ regmap_clear_bits(dp->regmap, DW_DP_SDP_VERTICAL_CTRL,
+ EN_AUDIO_STREAM_SDP | EN_AUDIO_TIMESTAMP_SDP);
+ regmap_clear_bits(dp->regmap, DW_DP_SDP_HORIZONTAL_CTRL,
+ EN_AUDIO_STREAM_SDP);
+
+ regmap_clear_bits(dp->regmap, DW_DP_AUD_CONFIG1, AUDIO_DATA_IN_EN);
+
+ 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;
+}
+
+static int __dw_dp_audio_enable(struct dw_dp *dp)
+{
+ u8 audio_data_in_en;
+
+ switch (dp->audio_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:
+ return -EINVAL;
+ }
+
+ 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, dp->audio_sample_width) |
+ FIELD_PREP(AUDIO_INF_SELECT, dp->audio_interface) |
+ FIELD_PREP(HBR_MODE_ENABLE, 0));
+
+ /* Wait for inf switch */
+ usleep_range(20, 40);
+
+ /*
+ * 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));
+
+ if (dp->audio_sdp_nr >= 0) {
+ dw_dp_clear_sdp(dp, dp->audio_sdp_nr);
+ dp->audio_sdp_nr = -1;
+ }
+
+ dp->audio_sdp_nr = dw_dp_audio_infoframe_send(dp);
+ if (dp->audio_sdp_nr < 0)
+ return dp->audio_sdp_nr;
+
+ return 0;
+}
+
+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);
+
+ guard(mutex)(&dp->audio_lock);
+
+ __dw_dp_audio_disable(dp);
+}
+
+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 supported_audio_interfaces;
+ enum dw_dp_audio_interface audio_interface;
+ u32 cfg1;
+ int ret;
+
+ guard(mutex)(&dp->audio_lock);
+
+ /*
+ * 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_disable(dp);
+
+ /* The hardware is limited to 1,2 or 8 channels */
+ switch (params->cea.channels) {
+ case 1:
+ case 2:
+ case 8:
+ break;
+ default:
+ dev_err(dp->dev, "invalid audio channels %d\n", params->cea.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;
+ }
+
+ 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_channels = params->cea.channels;
+ dp->audio_sample_width = params->sample_width;
+ dp->audio_interface = audio_interface;
+
+ ret = __dw_dp_audio_enable(dp);
+ if (ret < 0) {
+ dev_err(dp->dev, "failed to enable audio\n");
+ __dw_dp_audio_disable(dp);
+ return 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 void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *state)
{
@@ -1720,6 +1990,14 @@ static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge,
dev_err(dp->dev, "failed to enable video: %d\n", ret);
return;
}
+
+ scoped_guard(mutex, &dp->audio_lock) {
+ if (dp->audio_interface != DW_DP_AUDIO_UNUSED) {
+ ret = __dw_dp_audio_enable(dp);
+ if (ret < 0)
+ dev_err(dp->dev, "failed to restore audio: %d\n", ret);
+ }
+ }
}
static void dw_dp_reset(struct dw_dp *dp)
@@ -1930,6 +2208,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)
@@ -2211,12 +2494,21 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_
dp->plat_data.max_link_rate = plat_data->max_link_rate;
dp->vsc_sdp_nr = -1;
+ dp->audio_interface = DW_DP_AUDIO_UNUSED;
+ dp->audio_sdp_nr = -1;
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;
+ 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;
ret = devm_drm_bridge_add(dev, bridge);
if (ret)
@@ -2233,6 +2525,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->audio_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] 30+ messages in thread
* Re: [PATCH v7 01/18] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback
2026-07-28 17:40 ` [PATCH v7 01/18] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback Sebastian Reichel
@ 2026-07-28 17:59 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-28 17:59 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: devicetree, dri-devel, conor+dt, robh
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Premature global registration of the DRM bridge before resource initialization.
- [High] Incorrect teardown order in `dw_dp_unbind()` causes hardware access after PHY shutdown.
Pre-existing issues:
- [High] Missing cancellation of asynchronous hotplug work leads to Use-After-Free.
--
--- Patch [1]: [PATCH v7 01/18] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback ---
commit d953a018bc82561ebc8b90aad3a536efae9649c9
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
Date: Tue Jul 28 17:40:37 2026 +0000
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..67d9cd0bf5fd4 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
[ ... ]
> +void dw_dp_unbind(struct dw_dp *dp)
> +{
> + disable_irq(dp->irq);
> phy_exit(dp->phy);
> + drm_dp_aux_unregister(&dp->aux);
> }
[Severity: High]
Is this teardown order correct? Calling phy_exit() before
drm_dp_aux_unregister() powers down the PHY while the AUX device is still
registered.
If an active AUX transfer is waiting for completion in dw_dp_aux_transfer(),
disable_irq() will prevent the completion from firing, causing a timeout. When
it wakes up, it will attempt to read hardware registers. Because the PHY has
already been powered down, wouldn't accessing these registers trigger a bus
error?
> +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)
> {
[ ... ]
> dp->dev = dev;
> dp->pixel_mode = plat_data->pixel_mode;
> -
> dp->plat_data.max_link_rate = plat_data->max_link_rate;
> +
> 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;
> +
> + ret = devm_drm_bridge_add(dev, bridge);
> + if (ret)
> + return ERR_PTR(ret);
> +
[Severity: High]
Is it safe to register the bridge here?
By calling devm_drm_bridge_add() early in dw_dp_probe() before resources like
dp->regmap, dp->phy, and the dp->aux adapter are initialized, this makes the
bridge globally visible to other drivers prematurely.
If a concurrent driver discovers the bridge and initiates operations like
drm_bridge_edid_read(), could it attempt to use the uninitialized dp->aux
adapter and crash?
> mutex_init(&dp->irq_lock);
> INIT_WORK(&dp->hpd_work, dw_dp_hpd_work);
[Severity: High]
This is a pre-existing issue, but is dp->hpd_work cancelled anywhere during
driver teardown?
The work struct is initialized and tied to the lifetime of the dp object, but
neither dw_dp_unbind() nor the remove function seem to call cancel_work_sync().
Could this lead to a use-after-free if the workqueue continues executing and
accesses the freed dp memory after the device is unbound?
> init_completion(&dp->complete);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-synopsys-dw-dp-improvements-v7-0-b7640fa8cf48@collabora.com?part=1
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v7 02/18] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind
2026-07-28 17:40 ` [PATCH v7 02/18] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind Sebastian Reichel
@ 2026-07-28 18:09 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-28 18:09 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: robh, devicetree, conor+dt, dri-devel
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 causes immediate return on subsequent transfers if a previous one timed out.
- [High] The driver requests a hardware reset control but never deasserts it, potentially leaving the hardware in a reset state.
- [Medium] The DP AUX I2C adapter is exposed to userspace before the hardware is initialized, and unregistered after the hardware is powered down.
--
--- Patch [2]: [PATCH v7 02/18] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind ---
commit 9df51b0b37728e172be280b5d55126b4cd203eb4
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind
This commit fixes a race condition during device unbind by canceling any
pending HPD work after disabling the interrupt, preventing the worker thread
from accessing invalid resources.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index 67d9cd0bf5fd4..8393f9c9b0f00 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> @@ -1996,6 +1996,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);
[Severity: Medium]
This is a pre-existing issue, but the AUX adapter is unregistered after the
PHY is shut down via phy_exit().
Can this allow in-flight transactions to access unpowered hardware? A
userspace process could initiate an I2C transaction during device unbind after
phy_exit() is called but before drm_dp_aux_unregister(), causing I2C
transfers to timeout or fail.
Similarly, is the AUX adapter registered before hardware initialization in
dw_dp_bind()? It appears drm_dp_aux_register() is called before
dw_dp_init_hw() and phy_init() complete, exposing uninitialized hardware to
potential userspace access.
> }
While reviewing this file, I also noticed a couple of other pre-existing
issues outside of this patch's scope:
[Severity: High]
This isn't a bug introduced by this patch, but does dw_dp_aux_transfer() fail
to reinitialize the completion before waiting?
drivers/gpu/drm/bridge/synopsys/dw-dp.c:dw_dp_aux_transfer() {
...
status = wait_for_completion_timeout(&dp->complete, timeout);
...
}
If an AUX reply interrupt fires after wait_for_completion_timeout() has timed
out, the completion structure's done count is incremented.
On the next transfer, will wait_for_completion_timeout() return immediately
without waiting for the new hardware reply? Consecutive I2C/AUX transfers
could fail prematurely, potentially breaking display detection.
[Severity: High]
This is also a pre-existing issue, but the driver requests a hardware reset
control without ever deasserting it:
drivers/gpu/drm/bridge/synopsys/dw-dp.c:dw_dp_probe() {
...
dp->rstc = devm_reset_control_get(dev, NULL);
...
}
Can this leave the DisplayPort hardware in a reset state? There are no
corresponding calls to reset_control_deassert() anywhere in the driver.
If the bootloader has asserted the reset line, the driver might fail to
communicate with the hardware and potentially cause bus hangs.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-synopsys-dw-dp-improvements-v7-0-b7640fa8cf48@collabora.com?part=2
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v7 03/18] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal
2026-07-28 17:40 ` [PATCH v7 03/18] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal Sebastian Reichel
@ 2026-07-28 18:20 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-28 18:20 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 DRM bridge is exposed to the system before its backing resources are fully initialized, leading to a reverse-order teardown Use-After-Free (UAF) vulnerability.
--
--- Patch [3]: [PATCH v7 03/18] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal ---
commit 374a35fd683724436ba90569775efd9aa551c376
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 8393f9c9b0f00..ca7ff544d243a 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> @@ -2028,10 +2028,13 @@ struct dw_dp *dw_dp_probe(struct platform_device *pdev, const struct dw_dp_plat_
> if (ret)
> return ERR_PTR(ret);
>
[Severity: High]
This is a pre-existing issue, but since this patch focuses on teardown
cleanups, could the early bridge registration cause a use-after-free
during driver unbind?
Because devres executes cleanup callbacks in reverse order of initialization,
the bridge remains registered globally while its backing resources, such
as dp->regmap, are freed.
If an administrator triggers a driver unbind, a concurrent DRM operation
(like a DRM_IOCTL_MODE_GETCONNECTOR ioctl) querying the connector state
could invoke bridge->funcs->detect(), leading to a dereference of the freed
regmap:
dw_dp_bridge_detect()
dw_dp_hpd_detect(dp)
regmap_read(dp->regmap, ...)
Should the bridge be registered later in the probe function, after all
hardware resources are allocated, to avoid this reverse-order teardown race?
> - 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);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-synopsys-dw-dp-improvements-v7-0-b7640fa8cf48@collabora.com?part=3
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v7 04/18] drm/bridge: synopsys: dw-dp: Add missing reinit_completion
2026-07-28 17:40 ` [PATCH v7 04/18] drm/bridge: synopsys: dw-dp: Add missing reinit_completion Sebastian Reichel
@ 2026-07-28 18:34 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-28 18:34 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: robh, conor+dt, devicetree, dri-devel
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [High] The addition of `reinit_completion()` introduces a data race on `complete.done` and incorrectly mitigates spurious IRQs, causing a TOCTOU window due to threaded IRQ scheduling delays.
Pre-existing issues:
- [High] Lifecycle ordering bug: the AUX I2C interface is exposed to userspace before hardware is initialized, and unregistered after hardware is torn down.
- [High] A divide-by-zero vulnerability exists in dw_dp_video_enable when processing a display mode with a clock value of 1.
- [High] I2C-over-AUX reply status (NACK/DEFER) is silently discarded, causing deferred I2C transactions to falsely succeed and return garbage data.
- [High] dw_dp_aux_transfer improperly rejects short AUX reads by returning -EBUSY.
- [High] dw_dp_aux_transfer accesses the AUX hardware without ensuring the PHY is powered on.
--
--- Patch [4]: [PATCH v7 04/18] drm/bridge: synopsys: dw-dp: Add missing reinit_completion ---
commit e066df83b1559151eb45281e1a32fbe600503fc1
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Add missing reinit_completion
The DP AUX transfer method uses a completion triggered by an interrupt,
which can timeout. If the function runs into the timeout and the
interrupt fires afterwards, the following DP aux transfer completion
would trigger immediately without waiting for the interrupt. This in
turn means the next one would also be broken and so on.
Fix this potential issue by re-initializing the completion before
sending the AUX command.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index ca7ff544d243a..951f1e280e478 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> @@ -1465,6 +1465,8 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux,
> if (WARN_ON(msg->size > 16))
> return -E2BIG;
>
> + reinit_completion(&dp->complete);
> +
[Severity: High]
Does this lockless modification of the completion state race with the
threaded interrupt handler?
If an AUX transfer times out, an AUX_REPLY_EVENT interrupt follows, and
Linux schedules the threaded IRQ handler. If a new AUX transfer is initiated
concurrently, it will call reinit_completion() and start the hardware.
The delayed IRQ handler might then execute:
dw_dp_irq() {
...
complete(&dp->complete);
}
Could this immediately satisfy the wait for the new transfer before the
hardware finishes, causing the new transfer to proceed with a stale or
intermediate status? Should we drain or synchronize with the IRQ thread
before issuing a new hardware command?
[Severity: High]
This is a pre-existing issue, but does dw_dp_aux_transfer() access the AUX
hardware without ensuring the PHY is powered on?
Later in dw_dp_aux_transfer():
dw_dp_aux_transfer() {
...
regmap_write(dp->regmap, DW_DP_AUX_CMD, value);
status = wait_for_completion_timeout(&dp->complete, timeout);
}
If userspace tools query an inactive monitor while the PHY is powered off,
could these hardware register writes cause timeouts or bus hangs? Should
there be a phy_power_on() block surrounding this transfer?
[Severity: High]
This is a pre-existing issue, but are we silently discarding the
I2C-over-AUX reply status?
Also in dw_dp_aux_transfer():
dw_dp_aux_transfer() {
...
msg->reply = FIELD_GET(AUX_STATUS, value);
}
Because AUX_STATUS masks bits 7..4 and shifts them down, the resulting
value lacks the original bits 4 and 5. The DRM core mask
DP_AUX_I2C_REPLY_MASK expects these bits in their original positions.
Since the value will be at most 0xF, applying the 0x30 mask will extract 0
(DP_AUX_I2C_REPLY_ACK). Does this cause deferred I2C transactions or NACKs
to falsely succeed and return garbage data?
[Severity: High]
This is a pre-existing issue, but does dw_dp_aux_transfer() improperly
reject short reads?
dw_dp_aux_transfer() {
...
if (count != msg->size)
return -EBUSY;
}
If a DP sink returns fewer bytes than requested, the DRM DP helper expects
the actual number of bytes transferred so it can adjust the offset. By
returning -EBUSY, drm_dp_i2c_do_msg() will retry the exact same sized
transfer instead. Could this cause a complete failure to read EDID data
from sinks that restrict reply lengths?
[Severity: High]
This is a pre-existing issue, but does the driver expose the AUX I2C
interface to userspace before the hardware is fully initialized?
Looking at dw_dp_bind(), it registers the interface:
dw_dp_bind() {
...
ret = drm_dp_aux_register(&dp->aux);
}
If userspace opens the device node and issues an ioctl before phy_init() is
called, could this lead to bus hangs or synchronous aborts when accessing
uninitialized DP registers?
Similarly, dw_dp_unbind() unregisters the AUX interface after the hardware
is torn down:
dw_dp_unbind() {
...
phy_exit(dp->phy);
drm_dp_aux_unregister(&dp->aux);
}
[Severity: High]
This is a pre-existing issue, but is there a potential divide-by-zero here?
In dw_dp_video_enable(), when configuring the threshold:
dw_dp_video_enable() {
...
if (color_format == DRM_OUTPUT_COLOR_FORMAT_YCBCR420)
t2 = (link->rate / 4) * 1000 / (mode->clock / 2);
}
If a local user configures a custom DRM mode with a clock value of 1,
(mode->clock / 2) evaluates to 0. Since drm_mode_convert_umode() does not
reject clock values of 1, could this trigger a kernel panic?
> switch (msg->request & ~DP_AUX_I2C_MOT) {
> case DP_AUX_NATIVE_WRITE:
> case DP_AUX_I2C_WRITE:
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-synopsys-dw-dp-improvements-v7-0-b7640fa8cf48@collabora.com?part=4
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v7 05/18] drm/bridge: synopsys: dw-dp: Reset AUX channel on transfer timeout
2026-07-28 17:40 ` [PATCH v7 05/18] drm/bridge: synopsys: dw-dp: Reset AUX channel on transfer timeout Sebastian Reichel
@ 2026-07-28 18:43 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-28 18:43 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: dri-devel, conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The soft reset mechanism intended to prevent delayed completions from leaking into subsequent transfers is incomplete due to a software race condition with the threaded IRQ handler. `dw_dp_irq()` is registered via `devm_request_threaded_irq()` with no hard-IRQ handler, meaning it executes in process context. If the hardware asserts the AUX reply interrupt just before the transfer timeout expires, the IRQ thread may wake up and read the interrupt status (`value & AUX_REPLY_EVENT`), but get preempted before calling `complete(&dp->complete)`. The timeout handler will then reset the hardware and return `-ETIMEDOUT`. When a subsequent transfer starts, it calls `reinit_completion(&dp->complete)`. If the delayed IRQ thread resumes and calls `complete()` at this point, it will erroneously satisfy the new transfer's wait, causing it to read aborted or reset hardware status. To fully close this race, the timeout path must synchronize with the threaded IRQ handler (e.g., using `synchronize_irq()`) before returning.
--
--- Patch [5]: [PATCH v7 05/18] drm/bridge: synopsys: dw-dp: Reset AUX channel on transfer timeout ---
commit fd706e1cca6ddcdd0e07e034b13e849a9aa5e4b5
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Reset AUX channel on transfer timeout
This patch resets the AUX controller when a transfer times out to prevent a
delayed reply interrupt from leaking into a subsequent transfer.
Re-initializing the completion before each transfer does not protect against
a reply interrupt that fires after the initialization.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> @@ -1493,6 +1493,11 @@ static ssize_t dw_dp_aux_transfer(struct drm_dp_aux *aux,
> status = wait_for_completion_timeout(&dp->complete, timeout);
> if (!status) {
> dev_err(dp->dev, "timeout waiting for AUX reply\n");
> + regmap_update_bits(dp->regmap, DW_DP_SOFT_RESET_CTRL,
> + AUX_RESET, FIELD_PREP(AUX_RESET, 1));
> + usleep_range(10, 20);
> + regmap_update_bits(dp->regmap, DW_DP_SOFT_RESET_CTRL,
> + AUX_RESET, FIELD_PREP(AUX_RESET, 0));
> return -ETIMEDOUT;
[Severity: High]
This is a pre-existing issue, but does this soft reset fully close the race
with the threaded IRQ handler?
If the hardware asserts the AUX reply interrupt exactly as the timeout
expires, the threaded IRQ handler dw_dp_irq() could wake up and read the
interrupt status:
dw_dp_irq() {
...
if (value & AUX_REPLY_EVENT) {
regmap_write(dp->regmap, DW_DP_GENERAL_INTERRUPT, AUX_REPLY_EVENT);
If the thread is preempted right here, the timeout handling above executes,
the hardware is reset, and the function returns -ETIMEDOUT.
When a subsequent transfer begins and calls reinit_completion(&dp->complete),
could the delayed IRQ thread resume and execute the completion?
complete(&dp->complete);
}
}
This would erroneously satisfy the new transfer's wait and cause it to read
aborted hardware status. Would calling synchronize_irq() in this timeout path
prevent the completion from leaking?
> }
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-synopsys-dw-dp-improvements-v7-0-b7640fa8cf48@collabora.com?part=5
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v7 09/18] drm/bridge: Add out-of-band HPD notify handler
2026-07-28 17:40 ` [PATCH v7 09/18] drm/bridge: Add out-of-band HPD notify handler Sebastian Reichel
@ 2026-07-28 19:19 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-28 19:19 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: robh, devicetree, conor+dt, dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Potential Use-After-Free of the connector and encoder due to missing DRM device reference in OOB hotplug event handling.
--
--- Patch [9]: [PATCH v7 09/18] drm/bridge: Add out-of-band HPD notify handler ---
commit 60a32b50437d6f06ebb30ed7d8a1cc757f684526
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
Subject: drm/bridge: Add out-of-band HPD notify handler
This commit adds an out-of-band hotplug detection notify handler to the DRM
bridge structure. It allows notifying all bridges in a pipeline of hotplug
events, which is useful for forcing DP controllers HPD signals for USB-C DP
AltMode.
> diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
> index 8b54069fa53a4..632cc3ae3b544 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) {
[Severity: High]
This is a pre-existing issue, but I have a question about the use of
bridge_connector->encoder and connector here.
The Type-C subsystem calls drm_connector_oob_hotplug_event() in
drivers/gpu/drm/drm_connector.c, which acquires a reference to the
connector via drm_connector_find_by_fwnode().
However, drm_connector_get() only increments the connector's internal
kref, not the refcount of the parent drm_device.
If the DRM driver is concurrently unbound (e.g., via sysfs) during a
hotplug event, the drm_device refcount can drop to 0.
This triggers drmm_ cleanup handlers that free the drmm_kzalloc'ed
connector memory while the Type-C subsystem is still executing
connector->funcs->oob_hotplug_event().
Could this lead to accessing a freed bridge_connector->encoder and
passing the freed connector object down the stack to the newly added
oob_notify callbacks?
> + if (bridge->funcs->oob_notify)
> + bridge->funcs->oob_notify(bridge, connector, status);
> + }
> +
> drm_bridge_connector_handle_hpd(bridge_connector, status);
> }
>
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-synopsys-dw-dp-improvements-v7-0-b7640fa8cf48@collabora.com?part=9
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v7 11/18] drm/rockchip: dw_dp: Implement out-of-band HPD handling
2026-07-28 17:40 ` [PATCH v7 11/18] drm/rockchip: dw_dp: Implement out-of-band HPD handling Sebastian Reichel
@ 2026-07-28 19:41 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-28 19:41 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:
- [Medium] Missing runtime PM acquisition in out-of-band HPD callbacks leads to SError crashes once runtime PM is enabled.
--
--- Patch [11]: [PATCH v7 11/18] drm/rockchip: dw_dp: Implement out-of-band HPD handling ---
commit 38a0d76cc721376e1ac94e73ba9bcc954b4597fd
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/rockchip: dw_dp: Implement out-of-band HPD handling
This patch implements out-of-band hotplug handling to receive external hotplug
information from the USB-C state machine. This moves the logic from the USBDP
PHY driver to avoid SError crashes once runtime PM is enabled.
> diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> index 005938dc66c9f..b2e817044c942 100644
> --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
[ ... ]
> +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: Medium]
Will this regmap_write() trigger an SError if the USB-C state machine
triggers an out-of-band hotplug event while the DP controller is runtime
suspended?
The commit message notes this patch is intended to prevent SError problems
once runtime PM is enabled. Since the register access requires the DP
controller's power domain, should there be a runtime PM acquisition before
writing to the vo_grf regmap?
> +}
> +
> +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: Medium]
Similarly, could this asynchronous write cause an SError crash if it fires
without first ensuring the DP controller's power domain is active via runtime
PM?
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-synopsys-dw-dp-improvements-v7-0-b7640fa8cf48@collabora.com?part=11
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v7 12/18] drm/bridge: synopsys: dw-dp: Add Runtime PM support
2026-07-28 17:40 ` [PATCH v7 12/18] drm/bridge: synopsys: dw-dp: Add Runtime PM support Sebastian Reichel
@ 2026-07-28 19:58 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-28 19:58 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: devicetree, conor+dt, dri-devel, robh
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Critical] Incorrect teardown order in dw_dp_unbind() allows AUX transfers to cause a kernel panic or hang.
- [High] Calling a sleeping function (msleep) from a runtime PM resume callback that is reachable from an atomic commit path.
Pre-existing issues:
- [High] Unbalanced phy_power_off() in dw_dp_bridge_atomic_disable() corrupts the PHY power count when atomic_enable() fails early.
--
--- Patch [12]: [PATCH v7 12/18] drm/bridge: synopsys: dw-dp: Add Runtime PM support ---
commit 6fa63f17bf7f03d90ce981c3f9cd86d3e8761c32
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Add Runtime PM support
This commit adds runtime PM support to the Synopsys DesignWare DisplayPort
bridge driver. The support is not enabled automatically and must be hooked
up in the vendor specific glue code.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index d4f75b7f85d0a..8b650f904575d 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
[ ... ]
> @@ -1664,6 +1672,13 @@ static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> struct drm_connector_state *conn_state;
> int ret;
>
> + ret = pm_runtime_get_active(dp->dev, RPM_TRANSPARENT);
> + if (ret) {
> + dev_err(dp->dev, "runtime PM failure\n");
> + return;
> + }
> + dp->pm_active = true;
> +
> connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
> if (!connector) {
> dev_err(dp->dev, "failed to get connector\n");
> return;
> }
[Severity: High]
This is a pre-existing issue, but does this early return cause an unbalanced
phy_power_off() later?
If dw_dp_bridge_atomic_enable() returns early here, it skips a successful
phy_power_on(). However, the subsequent dw_dp_bridge_atomic_disable()
unconditionally calls dw_dp_link_disable(), which executes phy_power_off().
Would calling phy_power_off() without a preceding successful phy_power_on()
decrease the PHY power count below 0 and permanently prevent the PHY from
being powered on again?
[ ... ]
> @@ -2090,9 +2146,22 @@ EXPORT_SYMBOL_GPL(dw_dp_bind);
>
> void dw_dp_unbind(struct dw_dp *dp)
> {
> + if (dp->usbc_mode)
> + pm_runtime_get_sync(dp->dev);
> +
> + dp->drm_bound = false;
> disable_irq(dp->irq);
> +
> cancel_work_sync(&dp->hpd_work);
> phy_exit(dp->phy);
> +
> + /* Last PM runtime suspend must be done with drm_bound = false */
> + pm_runtime_put_sync(dp->dev);
> +
> + /* suspend once if runtime PM is disabled */
> + if (!pm_runtime_enabled(dp->dev))
> + dw_dp_runtime_suspend(dp);
> +
> drm_bridge_put(dp->next_bridge);
> drm_dp_aux_unregister(&dp->aux);
> }
[Severity: Critical]
Does this teardown order in dw_dp_unbind() introduce a race condition with
concurrent AUX transfers?
Because drm_dp_aux_unregister() is called at the very end, the AUX interface
remains active while the underlying hardware is shut down.
If a concurrent dw_dp_aux_transfer() occurs from userspace while runtime PM
is disabled, dw_dp_unbind() will manually call dw_dp_runtime_suspend()
disabling the APB clock. The AUX transfer will succeed its PM guard and
its subsequent regmap_write() will access unclocked hardware, triggering a
synchronous bus abort.
[ ... ]
> @@ -2202,6 +2271,47 @@ 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)
> +{
> + if (dp->drm_bound)
> + disable_irq(dp->irq);
> +
> + 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);
> +
> + if (dp->drm_bound)
> + enable_irq(dp->irq);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(dw_dp_runtime_resume);
[Severity: High]
Does this introduce a sleeping delay in an atomic commit path?
The drm_atomic_helper_commit_tail() path is an atomic context in DRM display
drivers. During a commit, it calls dw_dp_bridge_atomic_enable(), which now
makes a synchronous pm_runtime_get_active() call.
That will invoke this dw_dp_runtime_resume() callback which executes
msleep(110). Calling a sleeping function from an atomic commit path violates
DRM subsystem requirements and can cause system instability and warnings.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-synopsys-dw-dp-improvements-v7-0-b7640fa8cf48@collabora.com?part=12
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v7 13/18] drm/rockchip: dw_dp: Add runtime PM support
2026-07-28 17:40 ` [PATCH v7 13/18] drm/rockchip: dw_dp: Add runtime " Sebastian Reichel
@ 2026-07-28 20:14 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-28 20:14 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: dri-devel, robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Incorrect ordering of GRF (Video Output General Register File) restoration in `dw_dp_rockchip_runtime_resume` delays HPD signal visibility to the DP hardware, breaking link training.
- [Medium] Missing system sleep callbacks prevent the device and its power domain from suspending during system sleep for native DP configurations.
--
--- Patch [13]: [PATCH v7 13/18] drm/rockchip: dw_dp: Add runtime PM support ---
commit ea12bfaf9ea353b204570fe194d520aa064c1270
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 b2e817044c942..01915f7b24d59 100644
> --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
[ ... ]
> @@ -255,6 +274,34 @@ static void dw_dp_rockchip_remove(struct platform_device *pdev)
[ ... ]
> +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));
[Severity: High]
Does restoring the GRF state after dw_dp_runtime_resume() break link training?
Looking at dw_dp_runtime_resume(), it initializes the hardware and sleeps
for 110ms to debounce the HPD signal. Because the power domain was off, the
VO GRF is in its reset state, so during this 110ms wait, the DP controller
will see HPD as LOW.
Once the GRF is finally written here, the controller sees HPD go HIGH and
starts a new hardware debounce. Any immediate attempt to access the sink
by the display core might fail because the DP controller still believes
HPD is disconnected.
Should the GRF state be restored before calling dw_dp_runtime_resume() so
the hardware debounce sees the correct HPD signal?
> +
> + 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)
> +};
[Severity: Medium]
Will this prevent the system from entering deeper sleep states during system
suspend?
For native DP configurations, the core dw_dp driver acquires a runtime PM
reference in dw_dp_bind() and holds it indefinitely for Hot-Plug Detect
purposes. During system suspend (S3), since dw_dp_pm_ops is defined using
only RUNTIME_PM_OPS without system sleep callbacks, the PM core won't force
the device into a suspended state.
This means the generic PM domain might see the device as active and refuse
to power off the domain. Should system sleep callbacks be added to handle
this?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-synopsys-dw-dp-improvements-v7-0-b7640fa8cf48@collabora.com?part=13
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v7 14/18] drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access
2026-07-28 17:40 ` [PATCH v7 14/18] drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access Sebastian Reichel
@ 2026-07-28 20:26 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-28 20:26 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: robh, dri-devel, devicetree, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] The atomic_disable path indiscriminately wipes the entire shared SDP bitmap, breaking the concurrent access model this patch aims to introduce.
--
--- Patch [14]: [PATCH v7 14/18] drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access ---
commit 20478fb4c1990e713fd92f6e28d07f388653d106
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access
This patch introduces a mutex to protect sdp_reg_bank from concurrent access,
preparing the driver for additional SDP users like audio support.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
[ ... ]
> @@ -1691,7 +1695,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);
[Severity: High]
This isn't a bug introduced by this patch, but since the goal is to support
concurrent users, is it safe to unconditionally claim bit 0 here?
If video is disabled and a concurrent user like audio allocates an SDP, it
might be assigned bit 0. When video re-enables, unconditionally setting bit 0
could overwrite the other subsystem's reservation.
[ ... ]
> @@ -1735,7 +1740,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);
[Severity: High]
This is also a pre-existing issue, but will clearing the entire bitmap here
break the concurrent access model this patch aims to protect?
If audio or another subsystem holds an SDP reservation when video is disabled,
bitmap_zero() will incorrectly mark their bits as free. If a new user then
requests an SDP, it could be allocated the same bit, causing multiple
subsystems to write to the same hardware register bank concurrently.
Should dw_dp_bridge_atomic_disable() only clear the specific bits reserved by
the video path rather than zeroing out the entire shared state?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-synopsys-dw-dp-improvements-v7-0-b7640fa8cf48@collabora.com?part=14
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [PATCH v7 17/18] drm/bridge: synopsys: dw-dp: Clear only enabled SDPs on atomic disable
2026-07-28 17:40 ` [PATCH v7 17/18] drm/bridge: synopsys: dw-dp: Clear only enabled SDPs on atomic disable Sebastian Reichel
@ 2026-07-28 20:50 ` sashiko-bot
0 siblings, 0 replies; 30+ messages in thread
From: sashiko-bot @ 2026-07-28 20:50 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: conor+dt, dri-devel, devicetree, robh
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Unbalanced and unconditional clearing of SDP slot 0 defeats tracking and risks concurrent stream corruption.
- [High] Sleeping functions are called from an atomic commit callback path in violation of DRM rules.
--
--- Patch [17]: [PATCH v7 17/18] drm/bridge: synopsys: dw-dp: Clear only enabled SDPs on atomic disable ---
commit 7e0bb4945c91158df2f4360bed9dbdb18041404b
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
drm/bridge: synopsys: dw-dp: Clear only enabled SDPs on atomic disable
This commit updates dw_dp_bridge_atomic_disable() to selectively clear only
the previously configured SDPs (like VSC SDP) instead of bulk clearing the
entire SDP allocation bitmap. This aims to prevent handing out in-use SDPs
to other systems like the upcoming audio infrastructure.
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> index fcee47c6716a7..c0026bd4c7b53 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
[ ... ]
> +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);
> +}
[ ... ]
> @@ -1738,8 +1751,14 @@ static void dw_dp_bridge_atomic_disable(struct drm_bridge *bridge,
>
> dw_dp_video_disable(dp);
> dw_dp_link_disable(dp);
> +
> + if (dp->vsc_sdp_nr >= 0) {
> + dw_dp_clear_sdp(dp, dp->vsc_sdp_nr);
[Severity: High]
This isn't a bug introduced by this patch, but does this code call sleeping
functions in an atomic commit path?
The DRM subsystem calls dw_dp_bridge_atomic_disable() during non-blocking
commits via drm_atomic_helper_commit_tail(), which runs in atomic context.
This patch adds a call to dw_dp_clear_sdp(), which acquires a mutex using
scoped_guard(mutex, &dp->sdp_lock).
While the function already acquired this mutex and called sleeping functions
on this path previously, adding new mutex acquisitions here can lead to
system instability or deadlocks when the display mode is disabled.
> + dp->vsc_sdp_nr = -1;
> + }
> scoped_guard(mutex, &dp->sdp_lock)
> - bitmap_zero(dp->sdp_reg_bank, SDP_REG_BANK_SIZE);
> + clear_bit(0, dp->sdp_reg_bank);
[Severity: High]
This is a pre-existing issue, but does unconditionally clearing bit 0 here
defeat the tracking mechanism?
If userspace opens the ALSA PCM device while the DRM bridge is disabled,
ASoC might allocate slot 0 for audio. When the display is later enabled
and disabled, dw_dp_bridge_atomic_disable() unconditionally executes
clear_bit(0, dp->sdp_reg_bank).
This silently revokes the active audio allocation, allowing another
allocation to claim it while the audio subsystem still believes it owns
the slot.
While the previous code wiped the entire bank via bitmap_zero(),
explicitly clearing bit 0 seems to leave the audio tracking vulnerable.
Should this unconditional clear_bit() be removed entirely since
vsc_sdp_nr is now being handled explicitly?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-synopsys-dw-dp-improvements-v7-0-b7640fa8cf48@collabora.com?part=17
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2026-07-28 20:50 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-28 17:40 [PATCH v7 00/18] Synopsys DisplayPort Controller improvements for Rockchip platforms Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 01/18] drm/bridge: synopsys: dw-dp: Fix incorrect resource lifetimes in bind callback Sebastian Reichel
2026-07-28 17:59 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 02/18] drm/bridge: synopsys: dw-dp: Cancel pending HPD work on unbind Sebastian Reichel
2026-07-28 18:09 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 03/18] drm/bridge: synopsys: dw-dp: Add missing mutex cleanups on module removal Sebastian Reichel
2026-07-28 18:20 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 04/18] drm/bridge: synopsys: dw-dp: Add missing reinit_completion Sebastian Reichel
2026-07-28 18:34 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 05/18] drm/bridge: synopsys: dw-dp: Reset AUX channel on transfer timeout Sebastian Reichel
2026-07-28 18:43 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 06/18] drm/bridge: synopsys: dw-dp: Free output_fmts when none are valid Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 07/18] drm/bridge: synopsys: dw-dp: Support MEDIA_BUS_FMT_FIXED Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 08/18] drm/bridge: synopsys: dw-dp: Add follow-up bridge support Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 09/18] drm/bridge: Add out-of-band HPD notify handler Sebastian Reichel
2026-07-28 19:19 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 10/18] drm/bridge: synopsys: dw-dp: Support software triggered OOB HPD Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 11/18] drm/rockchip: dw_dp: Implement out-of-band HPD handling Sebastian Reichel
2026-07-28 19:41 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 12/18] drm/bridge: synopsys: dw-dp: Add Runtime PM support Sebastian Reichel
2026-07-28 19:58 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 13/18] drm/rockchip: dw_dp: Add runtime " Sebastian Reichel
2026-07-28 20:14 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 14/18] drm/bridge: synopsys: dw-dp: Protect sdp_reg_bank from concurrent access Sebastian Reichel
2026-07-28 20:26 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 15/18] drm/bridge: synopsys: dw-dp: Use regmap_set_bits in dw_dp_send_sdp Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 16/18] dt-bindings: display: rockchip: dw-dp: Fix sound DAI cells Sebastian Reichel
2026-07-28 17:40 ` [PATCH v7 17/18] drm/bridge: synopsys: dw-dp: Clear only enabled SDPs on atomic disable Sebastian Reichel
2026-07-28 20:50 ` sashiko-bot
2026-07-28 17:40 ` [PATCH v7 18/18] drm/bridge: synopsys: dw-dp: Add audio support Sebastian Reichel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox