Skip to contentSkip to conversation

drm/bridge: ssd2825: Fix panel enable sequence

PATCH Applied Svyatoslav Ryhel 4 participants 5 messages Oct 25 – Nov 11

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 drm/bridge: ssd2825: Fix panel enable sequence

Currently, in the case of a single bridge and a single panel, the DRM framework calls panel prepare() > bridge pre_enable() > bridge enable() > panel enable(). While this is fine for most cases, the SSD2825 requires the last two steps to be swapped. There is no meaningful way to achieve this using standard bridge chain configuration, so the bridge previously called panel_enable() directly at the end of its pre_enable() hook to gain fine-grained control over the sequence.

Commit 554d36acf5e0 ("drm/bridge: ssd2825: don't create a panel_bridge") removed this call, resulting in a regression where display output is missing because the panel enable function is now called after the bridge completes its configuration.

Since panels are now automatically wrapped into the bridge framework, invoke the next bridge's atomic enable directly, similar to how panel enabling was handled previously.

This restores panel support on the LG Optimus 4X HD (P880) and Optimus Vu (P895).

Fixes 554d36acf5e0 ("drm/bridge: ssd2825: don't create a panel_bridge")
---
 drivers/gpu/drm/bridge/ssd2825.c | 9 +++++++++
 1 file changed, 9 insertions(+)
drivers/gpu/drm/bridge/ssd2825.c +9 -0
index 71877699dfab6..9216cee09115f 100644
@@ -475,6 +475,7 @@ static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge,
 					     struct drm_atomic_commit *state)
 {
 	struct ssd2825_priv *priv = bridge_to_ssd2825(bridge);
+	struct drm_bridge *next_bridge = priv->output.bridge;
 	struct mipi_dsi_device *dsi_dev = priv->output.dev;
 	const struct drm_crtc_state *crtc_state;
 	const struct drm_display_mode *mode;
@@ -569,6 +570,14 @@ static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge,
 	/* Initial DSI configuration register set */
 	ssd2825_write_reg(priv, SSD2825_CONFIGURATION_REG, config);
 	ssd2825_write_reg(priv, SSD2825_VC_CTRL_REG, 0);
+
+	/*
+	 * The SSD2825 requires that the next bridge be enabled first;
+	 * otherwise, the SSD2825 cannot complete its configuration and
+	 * there will be no output.
+	 */
+	if (next_bridge->funcs && next_bridge->funcs->atomic_enable)
+		next_bridge->funcs->atomic_enable(next_bridge, state);
 }
 
 static void ssd2825_bridge_atomic_enable(struct drm_bridge *bridge,
Signature · 1 line
2.53.0
#1 lore raw
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] drm/bridge: ssd2825: duplicate atomic_enable() call on next_bridge --

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] drm/bridge: ssd2825: duplicate atomic_enable() call on next_bridge --

commit 1c5e404afec7538a0e06a870cc1167b8db336814
Author: Svyatoslav Ryhel <[email protected]>

drm/bridge: ssd2825: Fix panel enable sequence

This commit invokes the next bridge's atomic_enable() callback directly at the end of ssd2825_bridge_atomic_pre_enable() to restore panel output after commit 554d36acf5e0 removed direct panel handling.

Quoted 14 lines
drivers/gpu/drm/bridge/ssd2825.c +1 -0
index 71877699dfab6..9216cee09115f 100644
@@ -475,6 +475,7 @@ static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge,
 					     struct drm_atomic_commit *state)
 {
 	struct ssd2825_priv *priv = bridge_to_ssd2825(bridge);
+	struct drm_bridge *next_bridge = priv->output.bridge;
 	struct mipi_dsi_device *dsi_dev = priv->output.dev;
 	const struct drm_crtc_state *crtc_state;
 	const struct drm_display_mode *mode;
@@ -569,6 +570,14 @@ static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge,
 	/* Initial DSI configuration register set */

drivers/gpu/drm/bridge/ssd2825.c static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge, · line 580

Quoted 10 lines
drivers/gpu/drm/bridge/ssd2825.c +8 -0
 	ssd2825_write_reg(priv, SSD2825_CONFIGURATION_REG, config);
 	ssd2825_write_reg(priv, SSD2825_VC_CTRL_REG, 0);
+
+	/*
+	 * The SSD2825 requires that the next bridge be enabled first;
+	 * otherwise, the SSD2825 cannot complete its configuration and
+	 * there will be no output.
+	 */
+	if (next_bridge->funcs && next_bridge->funcs->atomic_enable)
+		next_bridge->funcs->atomic_enable(next_bridge, state);

[Severity: Medium]
Does calling next_bridge->funcs->atomic_enable() directly in ssd2825_bridge_atomic_pre_enable() cause next_bridge->funcs->atomic_enable() to be invoked twice on every modeset enable?

In drivers/gpu/drm/bridge/ssd2825.c, ssd2825_bridge_attach() attaches priv->output.bridge to the encoder's bridge chain:

	return drm_bridge_attach(bridge->encoder, priv->output.bridge, bridge,
				 flags);

During an atomic modeset enable, drm_atomic_helper_commit_modeset_enables() runs drm_atomic_bridge_chain_pre_enable(), which calls ssd2825_bridge_atomic_pre_enable() and now invokes next_bridge->funcs->atomic_enable() for the first time.

Immediately afterwards, drm_atomic_helper_commit_encoder_bridge_enable() calls drm_atomic_bridge_chain_enable() in drivers/gpu/drm/drm_bridge.c:

	drm_for_each_bridge_in_chain_from(first_bridge, bridge)
		if (bridge->funcs->atomic_enable)
			bridge->funcs->atomic_enable(bridge, state);

This calls next_bridge->funcs->atomic_enable() a second time while drm_atomic_bridge_chain_disable() only calls atomic_disable() once.

When next_bridge is a panel bridge, panel_bridge_atomic_enable() calls drm_panel_enable() in drivers/gpu/drm/drm_panel.c, where the second call hits the already-enabled check and emits a warning on every modeset:

	if (panel->enabled) {
		dev_warn(panel->dev, "Skipping enable of already enabled panel\n");
		return;
	}

When next_bridge is a non-panel drm_bridge, there is no enabled-state guard in the DRM bridge core, so next_bridge->funcs->atomic_enable() runs twice without a matching second disable.

Additionally, if next_bridge sets pre_enable_prev_first, drm_atomic_bridge_chain_pre_enable() runs ssd2825_bridge_atomic_pre_enable() before next_bridge->funcs->atomic_pre_enable(), which would cause next_bridge->funcs->atomic_enable() to run before next_bridge has been prepared in atomic_pre_enable().

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
maintainer Reviewed-by on v1 1/1
#2 lore raw
In reply to Svyatoslav Ryhel
Quoted 43 lines · Svyatoslav Ryhel

Currently, in the case of a single bridge and a single panel, the DRM framework calls panel prepare() > bridge pre_enable() > bridge enable() > panel enable(). While this is fine for most cases, the SSD2825 requires the last two steps to be swapped. There is no meaningful way to achieve this using standard bridge chain configuration, so the bridge previously called panel_enable() directly at the end of its pre_enable() hook to gain fine-grained control over the sequence.

Commit 554d36acf5e0 ("drm/bridge: ssd2825: don't create a panel_bridge") removed this call, resulting in a regression where display output is missing because the panel enable function is now called after the bridge completes its configuration.

Since panels are now automatically wrapped into the bridge framework, invoke the next bridge's atomic enable directly, similar to how panel enabling was handled previously.

This restores panel support on the LG Optimus 4X HD (P880) and Optimus Vu (P895).

Fixes 554d36acf5e0 ("drm/bridge: ssd2825: don't create a panel_bridge")
---
  drivers/gpu/drm/bridge/ssd2825.c | 9 +++++++++
  1 file changed, 9 insertions(+)
drivers/gpu/drm/bridge/ssd2825.c +2 -0
index 71877699dfab6..9216cee09115f 100644
@@ -475,6 +475,7 @@ static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge,
  					     struct drm_atomic_commit *state)
  {
  	struct ssd2825_priv *priv = bridge_to_ssd2825(bridge);
+	struct drm_bridge *next_bridge = priv->output.bridge;
  	struct mipi_dsi_device *dsi_dev = priv->output.dev;
  	const struct drm_crtc_state *crtc_state;
  	const struct drm_display_mode *mode;
@@ -569,6 +570,14 @@ static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge,
  	/* Initial DSI configuration register set */
  	ssd2825_write_reg(priv, SSD2825_CONFIGURATION_REG, config);
  	ssd2825_write_reg(priv, SSD2825_VC_CTRL_REG, 0);
+

drivers/gpu/drm/bridge/ssd2825.c static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge, · line 583

Quoted 10 lines
drivers/gpu/drm/bridge/ssd2825.c +7 -0
+	/*
+	 * The SSD2825 requires that the next bridge be enabled first;
+	 * otherwise, the SSD2825 cannot complete its configuration and
+	 * there will be no output.
+	 */
+	if (next_bridge->funcs && next_bridge->funcs->atomic_enable)
+		next_bridge->funcs->atomic_enable(next_bridge, state);
  }
 
  static void ssd2825_bridge_atomic_enable(struct drm_bridge *bridge,

Thanks,
Neil

reviewer Reviewed-by on v1 1/1
#3 lore raw
In reply to Svyatoslav Ryhel
Quoted 41 lines · Svyatoslav Ryhel

Currently, in the case of a single bridge and a single panel, the DRM framework calls panel prepare() > bridge pre_enable() > bridge enable() > panel enable(). While this is fine for most cases, the SSD2825 requires the last two steps to be swapped. There is no meaningful way to achieve this using standard bridge chain configuration, so the bridge previously called panel_enable() directly at the end of its pre_enable() hook to gain fine-grained control over the sequence.

Commit 554d36acf5e0 ("drm/bridge: ssd2825: don't create a panel_bridge") removed this call, resulting in a regression where display output is missing because the panel enable function is now called after the bridge completes its configuration.

Since panels are now automatically wrapped into the bridge framework, invoke the next bridge's atomic enable directly, similar to how panel enabling was handled previously.

This restores panel support on the LG Optimus 4X HD (P880) and Optimus Vu (P895).

Fixes 554d36acf5e0 ("drm/bridge: ssd2825: don't create a panel_bridge")
---
 drivers/gpu/drm/bridge/ssd2825.c | 9 +++++++++
 1 file changed, 9 insertions(+)
drivers/gpu/drm/bridge/ssd2825.c +1 -0
index 71877699dfab6..9216cee09115f 100644
@@ -475,6 +475,7 @@ static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge,
 					     struct drm_atomic_commit *state)
 {
 	struct ssd2825_priv *priv = bridge_to_ssd2825(bridge);
+	struct drm_bridge *next_bridge = priv->output.bridge;
 	struct mipi_dsi_device *dsi_dev = priv->output.dev;
 	const struct drm_crtc_state *crtc_state;
 	const struct drm_display_mode *mode;
@@ -569,6 +570,14 @@ static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge,
 	/* Initial DSI configuration register set */
 	ssd2825_write_reg(priv, SSD2825_CONFIGURATION_REG, config);

drivers/gpu/drm/bridge/ssd2825.c static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge, · line 581

Quoted 10 lines
drivers/gpu/drm/bridge/ssd2825.c +8 -0
 	ssd2825_write_reg(priv, SSD2825_VC_CTRL_REG, 0);
+
+	/*
+	 * The SSD2825 requires that the next bridge be enabled first;
+	 * otherwise, the SSD2825 cannot complete its configuration and
+	 * there will be no output.
+	 */
+	if (next_bridge->funcs && next_bridge->funcs->atomic_enable)
+		next_bridge->funcs->atomic_enable(next_bridge, state);
 }

I wonder whether this logic could be moved to the drm_bridge core, based on a flag somewhat like pre_enable_prev_first. I think it would be tricky to implement though.

Anyway, this fix seems to correctly re-instate the behaviour that was in place before 554d36acf5e0, so:

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

reviewer Applied
#4 lore raw
In reply to Svyatoslav Ryhel
Quoted 9 lines · Svyatoslav Ryhel

Currently, in the case of a single bridge and a single panel, the DRM framework calls panel prepare() > bridge pre_enable() > bridge enable() > panel enable(). While this is fine for most cases, the SSD2825 requires the last two steps to be swapped. There is no meaningful way to achieve this using standard bridge chain configuration, so the bridge previously called panel_enable() directly at the end of its pre_enable() hook to gain fine-grained control over the sequence.

[...]

Applied, thanks!

[1/1] drm/bridge: ssd2825: Fix panel enable sequence

      commit: 8bf8cda54c08ee3693f4dd1e2b7607fe5e466c79

Best regards,

Signature · 3 lines
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com