All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/11] drm: Add support for bus-format negotiation
@ 2019-12-03 14:15 ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon

This patch series aims at adding support for runtime bus-format
negotiation between all elements of the
'encoder -> bridges -> connector/display' section of the pipeline.

In order to support that, we need drm bridges to fully take part in the
atomic state validation process, which requires adding a
drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
Once those basic building blocks are in place, we can add new hooks to
allow bus format negotiation (those are called just before
->atomic_check()). The bus format selection is done at runtime by
testing all possible combinations across the whole bridge chain until
one is reported to work.

No Major changes in this v4. I think I addressed all comments I got
from Neil and Laurent (thanks for the detailed reviews BTW). Note that
this version only contains core changes. Once those changes are merged
I'll send the imx/panel/lvds-codec specific bits.

A more detailed changelog is provided in each patch.

This patch series is also available here [1].

Thanks,

Boris

[1]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v4

Boris Brezillon (11):
  drm/bridge: Rename bridge helpers targeting a bridge chain
  drm/bridge: Introduce drm_bridge_get_next_bridge()
  drm: Stop accessing encoder->bridge directly
  drm/bridge: Make the bridge chain a double-linked list
  drm/bridge: Add the drm_for_each_bridge_in_chain() helper
  drm/bridge: Add the drm_bridge_get_prev_bridge() helper
  drm/bridge: Clarify the atomic enable/disable hooks semantics
  drm/bridge: Add a drm_bridge_state object
  drm/bridge: Patch atomic hooks to take a drm_bridge_state
  drm/bridge: Add an ->atomic_check() hook
  drm/bridge: Add the necessary bits to support bus format negotiation

 .../drm/bridge/analogix/analogix_dp_core.c    |  41 +-
 drivers/gpu/drm/drm_atomic.c                  |  39 +
 drivers/gpu/drm/drm_atomic_helper.c           |  53 +-
 drivers/gpu/drm/drm_bridge.c                  | 756 +++++++++++++++---
 drivers/gpu/drm/drm_encoder.c                 |  15 +-
 drivers/gpu/drm/drm_probe_helper.c            |   4 +-
 drivers/gpu/drm/exynos/exynos_drm_dsi.c       |  13 +-
 drivers/gpu/drm/mediatek/mtk_hdmi.c           |   8 +-
 drivers/gpu/drm/msm/edp/edp_bridge.c          |  10 +-
 drivers/gpu/drm/omapdrm/omap_drv.c            |   4 +-
 drivers/gpu/drm/omapdrm/omap_encoder.c        |   3 +-
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c        |  10 +-
 drivers/gpu/drm/vc4/vc4_dsi.c                 |  18 +-
 include/drm/drm_atomic.h                      |   3 +
 include/drm/drm_bridge.h                      | 404 +++++++++-
 include/drm/drm_encoder.h                     |   7 +-
 16 files changed, 1174 insertions(+), 214 deletions(-)

-- 
2.23.0


^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v4 00/11] drm: Add support for bus-format negotiation
@ 2019-12-03 14:15 ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

This patch series aims at adding support for runtime bus-format
negotiation between all elements of the
'encoder -> bridges -> connector/display' section of the pipeline.

In order to support that, we need drm bridges to fully take part in the
atomic state validation process, which requires adding a
drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
Once those basic building blocks are in place, we can add new hooks to
allow bus format negotiation (those are called just before
->atomic_check()). The bus format selection is done at runtime by
testing all possible combinations across the whole bridge chain until
one is reported to work.

No Major changes in this v4. I think I addressed all comments I got
from Neil and Laurent (thanks for the detailed reviews BTW). Note that
this version only contains core changes. Once those changes are merged
I'll send the imx/panel/lvds-codec specific bits.

A more detailed changelog is provided in each patch.

This patch series is also available here [1].

Thanks,

Boris

[1]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v4

Boris Brezillon (11):
  drm/bridge: Rename bridge helpers targeting a bridge chain
  drm/bridge: Introduce drm_bridge_get_next_bridge()
  drm: Stop accessing encoder->bridge directly
  drm/bridge: Make the bridge chain a double-linked list
  drm/bridge: Add the drm_for_each_bridge_in_chain() helper
  drm/bridge: Add the drm_bridge_get_prev_bridge() helper
  drm/bridge: Clarify the atomic enable/disable hooks semantics
  drm/bridge: Add a drm_bridge_state object
  drm/bridge: Patch atomic hooks to take a drm_bridge_state
  drm/bridge: Add an ->atomic_check() hook
  drm/bridge: Add the necessary bits to support bus format negotiation

 .../drm/bridge/analogix/analogix_dp_core.c    |  41 +-
 drivers/gpu/drm/drm_atomic.c                  |  39 +
 drivers/gpu/drm/drm_atomic_helper.c           |  53 +-
 drivers/gpu/drm/drm_bridge.c                  | 756 +++++++++++++++---
 drivers/gpu/drm/drm_encoder.c                 |  15 +-
 drivers/gpu/drm/drm_probe_helper.c            |   4 +-
 drivers/gpu/drm/exynos/exynos_drm_dsi.c       |  13 +-
 drivers/gpu/drm/mediatek/mtk_hdmi.c           |   8 +-
 drivers/gpu/drm/msm/edp/edp_bridge.c          |  10 +-
 drivers/gpu/drm/omapdrm/omap_drv.c            |   4 +-
 drivers/gpu/drm/omapdrm/omap_encoder.c        |   3 +-
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c        |  10 +-
 drivers/gpu/drm/vc4/vc4_dsi.c                 |  18 +-
 include/drm/drm_atomic.h                      |   3 +
 include/drm/drm_bridge.h                      | 404 +++++++++-
 include/drm/drm_encoder.h                     |   7 +-
 16 files changed, 1174 insertions(+), 214 deletions(-)

-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* [PATCH v4 01/11] drm/bridge: Rename bridge helpers targeting a bridge chain
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 14:15   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon, Laurent Pinchart

Change the prefix of bridge helpers targeting a bridge chain from
drm_bridge_ to drm_bridge_chain_ to better reflect the fact that
the operation will happen on all elements of chain, starting at the
bridge passed in argument.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes in v4:
* Add A-bs/R-bs

Changes in v3:
* None

Changes in v2:
* Pass te bridge, not the encoder, so we can later act on a sub-chain
  instead of the whole chain
---
 drivers/gpu/drm/drm_atomic_helper.c     |  19 ++--
 drivers/gpu/drm/drm_bridge.c            | 125 ++++++++++++------------
 drivers/gpu/drm/drm_probe_helper.c      |   2 +-
 drivers/gpu/drm/exynos/exynos_drm_dsi.c |   8 +-
 drivers/gpu/drm/mediatek/mtk_hdmi.c     |   4 +-
 drivers/gpu/drm/vc4/vc4_dsi.c           |   8 +-
 include/drm/drm_bridge.h                |  64 ++++++------
 7 files changed, 120 insertions(+), 110 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 587052751b48..cf678be58fa4 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -435,8 +435,9 @@ mode_fixup(struct drm_atomic_state *state)
 		encoder = new_conn_state->best_encoder;
 		funcs = encoder->helper_private;
 
-		ret = drm_bridge_mode_fixup(encoder->bridge, &new_crtc_state->mode,
-				&new_crtc_state->adjusted_mode);
+		ret = drm_bridge_chain_mode_fixup(encoder->bridge,
+					&new_crtc_state->mode,
+					&new_crtc_state->adjusted_mode);
 		if (!ret) {
 			DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
 			return -EINVAL;
@@ -501,7 +502,7 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector,
 		return ret;
 	}
 
-	ret = drm_bridge_mode_valid(encoder->bridge, mode);
+	ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
 	if (ret != MODE_OK) {
 		DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n");
 		return ret;
@@ -1020,7 +1021,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 		 * Each encoder has at most one connector (since we always steal
 		 * it away), so we won't call disable hooks twice.
 		 */
-		drm_atomic_bridge_disable(encoder->bridge, old_state);
+		drm_atomic_bridge_chain_disable(encoder->bridge, old_state);
 
 		/* Right function depends upon target state. */
 		if (funcs) {
@@ -1034,7 +1035,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 				funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
 		}
 
-		drm_atomic_bridge_post_disable(encoder->bridge, old_state);
+		drm_atomic_bridge_chain_post_disable(encoder->bridge,
+						     old_state);
 	}
 
 	for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
@@ -1215,7 +1217,8 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
 			funcs->mode_set(encoder, mode, adjusted_mode);
 		}
 
-		drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode);
+		drm_bridge_chain_mode_set(encoder->bridge, mode,
+					  adjusted_mode);
 	}
 }
 
@@ -1332,7 +1335,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 		 * Each encoder has at most one connector (since we always steal
 		 * it away), so we won't call enable hooks twice.
 		 */
-		drm_atomic_bridge_pre_enable(encoder->bridge, old_state);
+		drm_atomic_bridge_chain_pre_enable(encoder->bridge, old_state);
 
 		if (funcs) {
 			if (funcs->atomic_enable)
@@ -1343,7 +1346,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 				funcs->commit(encoder);
 		}
 
-		drm_atomic_bridge_enable(encoder->bridge, old_state);
+		drm_atomic_bridge_chain_enable(encoder->bridge, old_state);
 	}
 
 	drm_atomic_helper_commit_writebacks(dev, old_state);
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index cba537c99e43..54c874493c57 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -172,8 +172,8 @@ void drm_bridge_detach(struct drm_bridge *bridge)
  */
 
 /**
- * drm_bridge_mode_fixup - fixup proposed mode for all bridges in the
- *			   encoder chain
+ * drm_bridge_chain_mode_fixup - fixup proposed mode for all bridges in the
+ *				 encoder chain
  * @bridge: bridge control structure
  * @mode: desired mode to be set for the bridge
  * @adjusted_mode: updated mode that works for this bridge
@@ -186,9 +186,9 @@ void drm_bridge_detach(struct drm_bridge *bridge)
  * RETURNS:
  * true on success, false on failure
  */
-bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
-			const struct drm_display_mode *mode,
-			struct drm_display_mode *adjusted_mode)
+bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
+				 const struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted_mode)
 {
 	bool ret = true;
 
@@ -198,15 +198,16 @@ bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
 	if (bridge->funcs->mode_fixup)
 		ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode);
 
-	ret = ret && drm_bridge_mode_fixup(bridge->next, mode, adjusted_mode);
+	ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode,
+						 adjusted_mode);
 
 	return ret;
 }
-EXPORT_SYMBOL(drm_bridge_mode_fixup);
+EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
 
 /**
- * drm_bridge_mode_valid - validate the mode against all bridges in the
- * 			   encoder chain.
+ * drm_bridge_chain_mode_valid - validate the mode against all bridges in the
+ *				 encoder chain.
  * @bridge: bridge control structure
  * @mode: desired mode to be validated
  *
@@ -219,8 +220,9 @@ EXPORT_SYMBOL(drm_bridge_mode_fixup);
  * RETURNS:
  * MODE_OK on success, drm_mode_status Enum error code on failure
  */
-enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge,
-					   const struct drm_display_mode *mode)
+enum drm_mode_status
+drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
+			    const struct drm_display_mode *mode)
 {
 	enum drm_mode_status ret = MODE_OK;
 
@@ -233,12 +235,12 @@ enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge,
 	if (ret != MODE_OK)
 		return ret;
 
-	return drm_bridge_mode_valid(bridge->next, mode);
+	return drm_bridge_chain_mode_valid(bridge->next, mode);
 }
-EXPORT_SYMBOL(drm_bridge_mode_valid);
+EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
 
 /**
- * drm_bridge_disable - disables all bridges in the encoder chain
+ * drm_bridge_chain_disable - disables all bridges in the encoder chain
  * @bridge: bridge control structure
  *
  * Calls &drm_bridge_funcs.disable op for all the bridges in the encoder
@@ -247,20 +249,21 @@ EXPORT_SYMBOL(drm_bridge_mode_valid);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_bridge_disable(struct drm_bridge *bridge)
+void drm_bridge_chain_disable(struct drm_bridge *bridge)
 {
 	if (!bridge)
 		return;
 
-	drm_bridge_disable(bridge->next);
+	drm_bridge_chain_disable(bridge->next);
 
 	if (bridge->funcs->disable)
 		bridge->funcs->disable(bridge);
 }
-EXPORT_SYMBOL(drm_bridge_disable);
+EXPORT_SYMBOL(drm_bridge_chain_disable);
 
 /**
- * drm_bridge_post_disable - cleans up after disabling all bridges in the encoder chain
+ * drm_bridge_chain_post_disable - cleans up after disabling all bridges in the
+ *				   encoder chain
  * @bridge: bridge control structure
  *
  * Calls &drm_bridge_funcs.post_disable op for all the bridges in the
@@ -269,7 +272,7 @@ EXPORT_SYMBOL(drm_bridge_disable);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_bridge_post_disable(struct drm_bridge *bridge)
+void drm_bridge_chain_post_disable(struct drm_bridge *bridge)
 {
 	if (!bridge)
 		return;
@@ -277,25 +280,25 @@ void drm_bridge_post_disable(struct drm_bridge *bridge)
 	if (bridge->funcs->post_disable)
 		bridge->funcs->post_disable(bridge);
 
-	drm_bridge_post_disable(bridge->next);
+	drm_bridge_chain_post_disable(bridge->next);
 }
-EXPORT_SYMBOL(drm_bridge_post_disable);
+EXPORT_SYMBOL(drm_bridge_chain_post_disable);
 
 /**
- * drm_bridge_mode_set - set proposed mode for all bridges in the
- *			 encoder chain
+ * drm_bridge_chain_mode_set - set proposed mode for all bridges in the
+ *			       encoder chain
  * @bridge: bridge control structure
- * @mode: desired mode to be set for the bridge
- * @adjusted_mode: updated mode that works for this bridge
+ * @mode: desired mode to be set for the encoder chain
+ * @adjusted_mode: updated mode that works for this encoder chain
  *
  * Calls &drm_bridge_funcs.mode_set op for all the bridges in the
  * encoder chain, starting from the first bridge to the last.
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_bridge_mode_set(struct drm_bridge *bridge,
-			 const struct drm_display_mode *mode,
-			 const struct drm_display_mode *adjusted_mode)
+void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
+			       const struct drm_display_mode *mode,
+			       const struct drm_display_mode *adjusted_mode)
 {
 	if (!bridge)
 		return;
@@ -303,13 +306,13 @@ void drm_bridge_mode_set(struct drm_bridge *bridge,
 	if (bridge->funcs->mode_set)
 		bridge->funcs->mode_set(bridge, mode, adjusted_mode);
 
-	drm_bridge_mode_set(bridge->next, mode, adjusted_mode);
+	drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode);
 }
-EXPORT_SYMBOL(drm_bridge_mode_set);
+EXPORT_SYMBOL(drm_bridge_chain_mode_set);
 
 /**
- * drm_bridge_pre_enable - prepares for enabling all
- *			   bridges in the encoder chain
+ * drm_bridge_chain_pre_enable - prepares for enabling all bridges in the
+ *				 encoder chain
  * @bridge: bridge control structure
  *
  * Calls &drm_bridge_funcs.pre_enable op for all the bridges in the encoder
@@ -318,20 +321,20 @@ EXPORT_SYMBOL(drm_bridge_mode_set);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_bridge_pre_enable(struct drm_bridge *bridge)
+void drm_bridge_chain_pre_enable(struct drm_bridge *bridge)
 {
 	if (!bridge)
 		return;
 
-	drm_bridge_pre_enable(bridge->next);
+	drm_bridge_chain_pre_enable(bridge->next);
 
 	if (bridge->funcs->pre_enable)
 		bridge->funcs->pre_enable(bridge);
 }
-EXPORT_SYMBOL(drm_bridge_pre_enable);
+EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
 
 /**
- * drm_bridge_enable - enables all bridges in the encoder chain
+ * drm_bridge_chain_enable - enables all bridges in the encoder chain
  * @bridge: bridge control structure
  *
  * Calls &drm_bridge_funcs.enable op for all the bridges in the encoder
@@ -340,7 +343,7 @@ EXPORT_SYMBOL(drm_bridge_pre_enable);
  *
  * Note that the bridge passed should be the one closest to the encoder
  */
-void drm_bridge_enable(struct drm_bridge *bridge)
+void drm_bridge_chain_enable(struct drm_bridge *bridge)
 {
 	if (!bridge)
 		return;
@@ -348,12 +351,12 @@ void drm_bridge_enable(struct drm_bridge *bridge)
 	if (bridge->funcs->enable)
 		bridge->funcs->enable(bridge);
 
-	drm_bridge_enable(bridge->next);
+	drm_bridge_chain_enable(bridge->next);
 }
-EXPORT_SYMBOL(drm_bridge_enable);
+EXPORT_SYMBOL(drm_bridge_chain_enable);
 
 /**
- * drm_atomic_bridge_disable - disables all bridges in the encoder chain
+ * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain
  * @bridge: bridge control structure
  * @state: atomic state being committed
  *
@@ -364,24 +367,24 @@ EXPORT_SYMBOL(drm_bridge_enable);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_atomic_bridge_disable(struct drm_bridge *bridge,
-			       struct drm_atomic_state *state)
+void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
+				     struct drm_atomic_state *state)
 {
 	if (!bridge)
 		return;
 
-	drm_atomic_bridge_disable(bridge->next, state);
+	drm_atomic_bridge_chain_disable(bridge->next, state);
 
 	if (bridge->funcs->atomic_disable)
 		bridge->funcs->atomic_disable(bridge, state);
 	else if (bridge->funcs->disable)
 		bridge->funcs->disable(bridge);
 }
-EXPORT_SYMBOL(drm_atomic_bridge_disable);
+EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
 
 /**
- * drm_atomic_bridge_post_disable - cleans up after disabling all bridges in the
- *				    encoder chain
+ * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges
+ *					  in the encoder chain
  * @bridge: bridge control structure
  * @state: atomic state being committed
  *
@@ -392,8 +395,8 @@ EXPORT_SYMBOL(drm_atomic_bridge_disable);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_atomic_bridge_post_disable(struct drm_bridge *bridge,
-				    struct drm_atomic_state *state)
+void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
+					  struct drm_atomic_state *state)
 {
 	if (!bridge)
 		return;
@@ -403,13 +406,13 @@ void drm_atomic_bridge_post_disable(struct drm_bridge *bridge,
 	else if (bridge->funcs->post_disable)
 		bridge->funcs->post_disable(bridge);
 
-	drm_atomic_bridge_post_disable(bridge->next, state);
+	drm_atomic_bridge_chain_post_disable(bridge->next, state);
 }
-EXPORT_SYMBOL(drm_atomic_bridge_post_disable);
+EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
 
 /**
- * drm_atomic_bridge_pre_enable - prepares for enabling all bridges in the
- *				  encoder chain
+ * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
+ *					the encoder chain
  * @bridge: bridge control structure
  * @state: atomic state being committed
  *
@@ -420,23 +423,23 @@ EXPORT_SYMBOL(drm_atomic_bridge_post_disable);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge,
-				  struct drm_atomic_state *state)
+void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
+					struct drm_atomic_state *state)
 {
 	if (!bridge)
 		return;
 
-	drm_atomic_bridge_pre_enable(bridge->next, state);
+	drm_atomic_bridge_chain_pre_enable(bridge->next, state);
 
 	if (bridge->funcs->atomic_pre_enable)
 		bridge->funcs->atomic_pre_enable(bridge, state);
 	else if (bridge->funcs->pre_enable)
 		bridge->funcs->pre_enable(bridge);
 }
-EXPORT_SYMBOL(drm_atomic_bridge_pre_enable);
+EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
 
 /**
- * drm_atomic_bridge_enable - enables all bridges in the encoder chain
+ * drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain
  * @bridge: bridge control structure
  * @state: atomic state being committed
  *
@@ -447,8 +450,8 @@ EXPORT_SYMBOL(drm_atomic_bridge_pre_enable);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_atomic_bridge_enable(struct drm_bridge *bridge,
-			      struct drm_atomic_state *state)
+void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
+				    struct drm_atomic_state *state)
 {
 	if (!bridge)
 		return;
@@ -458,9 +461,9 @@ void drm_atomic_bridge_enable(struct drm_bridge *bridge,
 	else if (bridge->funcs->enable)
 		bridge->funcs->enable(bridge);
 
-	drm_atomic_bridge_enable(bridge->next, state);
+	drm_atomic_bridge_chain_enable(bridge->next, state);
 }
-EXPORT_SYMBOL(drm_atomic_bridge_enable);
+EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
 
 #ifdef CONFIG_OF
 /**
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index a7c87abe88d0..c3ea722065c4 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -112,7 +112,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
 			continue;
 		}
 
-		ret = drm_bridge_mode_valid(encoder->bridge, mode);
+		ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
 		if (ret != MODE_OK) {
 			/* There is also no point in continuing for crtc check
 			 * here. */
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index ceb370864cc4..d984097704b8 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1390,7 +1390,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
 		if (ret < 0)
 			goto err_put_sync;
 	} else {
-		drm_bridge_pre_enable(dsi->out_bridge);
+		drm_bridge_chain_pre_enable(dsi->out_bridge);
 	}
 
 	exynos_dsi_set_display_mode(dsi);
@@ -1401,7 +1401,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
 		if (ret < 0)
 			goto err_display_disable;
 	} else {
-		drm_bridge_enable(dsi->out_bridge);
+		drm_bridge_chain_enable(dsi->out_bridge);
 	}
 
 	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
@@ -1426,10 +1426,10 @@ static void exynos_dsi_disable(struct drm_encoder *encoder)
 	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
 
 	drm_panel_disable(dsi->panel);
-	drm_bridge_disable(dsi->out_bridge);
+	drm_bridge_chain_disable(dsi->out_bridge);
 	exynos_dsi_set_display_enable(dsi, false);
 	drm_panel_unprepare(dsi->panel);
-	drm_bridge_post_disable(dsi->out_bridge);
+	drm_bridge_chain_post_disable(dsi->out_bridge);
 	dsi->state &= ~DSIM_STATE_ENABLED;
 	pm_runtime_put_sync(dsi->dev);
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index f684947c5243..ee3a5e9c10c4 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -1247,8 +1247,8 @@ static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn,
 		struct drm_display_mode adjusted_mode;
 
 		drm_mode_copy(&adjusted_mode, mode);
-		if (!drm_bridge_mode_fixup(hdmi->bridge.next, mode,
-					   &adjusted_mode))
+		if (!drm_bridge_chain_mode_fixup(hdmi->bridge.next, mode,
+						 &adjusted_mode))
 			return MODE_BAD;
 	}
 
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index c9ba83ed49b9..ff81b54ea281 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -753,9 +753,9 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
 	struct vc4_dsi *dsi = vc4_encoder->dsi;
 	struct device *dev = &dsi->pdev->dev;
 
-	drm_bridge_disable(dsi->bridge);
+	drm_bridge_chain_disable(dsi->bridge);
 	vc4_dsi_ulps(dsi, true);
-	drm_bridge_post_disable(dsi->bridge);
+	drm_bridge_chain_post_disable(dsi->bridge);
 
 	clk_disable_unprepare(dsi->pll_phy_clock);
 	clk_disable_unprepare(dsi->escape_clock);
@@ -1055,7 +1055,7 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 
 	vc4_dsi_ulps(dsi, false);
 
-	drm_bridge_pre_enable(dsi->bridge);
+	drm_bridge_chain_pre_enable(dsi->bridge);
 
 	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
 		DSI_PORT_WRITE(DISP0_CTRL,
@@ -1072,7 +1072,7 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 			       DSI_DISP0_ENABLE);
 	}
 
-	drm_bridge_enable(dsi->bridge);
+	drm_bridge_chain_enable(dsi->bridge);
 
 	if (debug_dump_regs) {
 		struct drm_printer p = drm_info_printer(&dsi->pdev->dev);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index c0a2286a81e9..726435baf4ad 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -254,9 +254,10 @@ struct drm_bridge_funcs {
 	 * there is one) when this callback is called.
 	 *
 	 * Note that this function will only be invoked in the context of an
-	 * atomic commit. It will not be invoked from &drm_bridge_pre_enable. It
-	 * would be prudent to also provide an implementation of @pre_enable if
-	 * you are expecting driver calls into &drm_bridge_pre_enable.
+	 * atomic commit. It will not be invoked from
+	 * &drm_bridge_chain_pre_enable. It would be prudent to also provide an
+	 * implementation of @pre_enable if you are expecting driver calls into
+	 * &drm_bridge_chain_pre_enable.
 	 *
 	 * The @atomic_pre_enable callback is optional.
 	 */
@@ -279,9 +280,9 @@ struct drm_bridge_funcs {
 	 * chain if there is one.
 	 *
 	 * Note that this function will only be invoked in the context of an
-	 * atomic commit. It will not be invoked from &drm_bridge_enable. It
-	 * would be prudent to also provide an implementation of @enable if
-	 * you are expecting driver calls into &drm_bridge_enable.
+	 * atomic commit. It will not be invoked from &drm_bridge_chain_enable.
+	 * It would be prudent to also provide an implementation of @enable if
+	 * you are expecting driver calls into &drm_bridge_chain_enable.
 	 *
 	 * The @atomic_enable callback is optional.
 	 */
@@ -301,9 +302,10 @@ struct drm_bridge_funcs {
 	 * signals) feeding it is still running when this callback is called.
 	 *
 	 * Note that this function will only be invoked in the context of an
-	 * atomic commit. It will not be invoked from &drm_bridge_disable. It
-	 * would be prudent to also provide an implementation of @disable if
-	 * you are expecting driver calls into &drm_bridge_disable.
+	 * atomic commit. It will not be invoked from
+	 * &drm_bridge_chain_disable. It would be prudent to also provide an
+	 * implementation of @disable if you are expecting driver calls into
+	 * &drm_bridge_chain_disable.
 	 *
 	 * The @atomic_disable callback is optional.
 	 */
@@ -325,10 +327,11 @@ struct drm_bridge_funcs {
 	 * called.
 	 *
 	 * Note that this function will only be invoked in the context of an
-	 * atomic commit. It will not be invoked from &drm_bridge_post_disable.
+	 * atomic commit. It will not be invoked from
+	 * &drm_bridge_chain_post_disable.
 	 * It would be prudent to also provide an implementation of
 	 * @post_disable if you are expecting driver calls into
-	 * &drm_bridge_post_disable.
+	 * &drm_bridge_chain_post_disable.
 	 *
 	 * The @atomic_post_disable callback is optional.
 	 */
@@ -406,27 +409,28 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np);
 int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 		      struct drm_bridge *previous);
 
-bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
-			   const struct drm_display_mode *mode,
-			   struct drm_display_mode *adjusted_mode);
-enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge,
-					   const struct drm_display_mode *mode);
-void drm_bridge_disable(struct drm_bridge *bridge);
-void drm_bridge_post_disable(struct drm_bridge *bridge);
-void drm_bridge_mode_set(struct drm_bridge *bridge,
-			 const struct drm_display_mode *mode,
-			 const struct drm_display_mode *adjusted_mode);
-void drm_bridge_pre_enable(struct drm_bridge *bridge);
-void drm_bridge_enable(struct drm_bridge *bridge);
+bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
+				 const struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted_mode);
+enum drm_mode_status
+drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
+			    const struct drm_display_mode *mode);
+void drm_bridge_chain_disable(struct drm_bridge *bridge);
+void drm_bridge_chain_post_disable(struct drm_bridge *bridge);
+void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
+			       const struct drm_display_mode *mode,
+			       const struct drm_display_mode *adjusted_mode);
+void drm_bridge_chain_pre_enable(struct drm_bridge *bridge);
+void drm_bridge_chain_enable(struct drm_bridge *bridge);
 
-void drm_atomic_bridge_disable(struct drm_bridge *bridge,
-			       struct drm_atomic_state *state);
-void drm_atomic_bridge_post_disable(struct drm_bridge *bridge,
+void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
+				     struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
+					  struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
+					struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 				    struct drm_atomic_state *state);
-void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge,
-				  struct drm_atomic_state *state);
-void drm_atomic_bridge_enable(struct drm_bridge *bridge,
-			      struct drm_atomic_state *state);
 
 #ifdef CONFIG_DRM_PANEL_BRIDGE
 struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel);
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 01/11] drm/bridge: Rename bridge helpers targeting a bridge chain
@ 2019-12-03 14:15   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

Change the prefix of bridge helpers targeting a bridge chain from
drm_bridge_ to drm_bridge_chain_ to better reflect the fact that
the operation will happen on all elements of chain, starting at the
bridge passed in argument.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes in v4:
* Add A-bs/R-bs

Changes in v3:
* None

Changes in v2:
* Pass te bridge, not the encoder, so we can later act on a sub-chain
  instead of the whole chain
---
 drivers/gpu/drm/drm_atomic_helper.c     |  19 ++--
 drivers/gpu/drm/drm_bridge.c            | 125 ++++++++++++------------
 drivers/gpu/drm/drm_probe_helper.c      |   2 +-
 drivers/gpu/drm/exynos/exynos_drm_dsi.c |   8 +-
 drivers/gpu/drm/mediatek/mtk_hdmi.c     |   4 +-
 drivers/gpu/drm/vc4/vc4_dsi.c           |   8 +-
 include/drm/drm_bridge.h                |  64 ++++++------
 7 files changed, 120 insertions(+), 110 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 587052751b48..cf678be58fa4 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -435,8 +435,9 @@ mode_fixup(struct drm_atomic_state *state)
 		encoder = new_conn_state->best_encoder;
 		funcs = encoder->helper_private;
 
-		ret = drm_bridge_mode_fixup(encoder->bridge, &new_crtc_state->mode,
-				&new_crtc_state->adjusted_mode);
+		ret = drm_bridge_chain_mode_fixup(encoder->bridge,
+					&new_crtc_state->mode,
+					&new_crtc_state->adjusted_mode);
 		if (!ret) {
 			DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
 			return -EINVAL;
@@ -501,7 +502,7 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector,
 		return ret;
 	}
 
-	ret = drm_bridge_mode_valid(encoder->bridge, mode);
+	ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
 	if (ret != MODE_OK) {
 		DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n");
 		return ret;
@@ -1020,7 +1021,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 		 * Each encoder has at most one connector (since we always steal
 		 * it away), so we won't call disable hooks twice.
 		 */
-		drm_atomic_bridge_disable(encoder->bridge, old_state);
+		drm_atomic_bridge_chain_disable(encoder->bridge, old_state);
 
 		/* Right function depends upon target state. */
 		if (funcs) {
@@ -1034,7 +1035,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 				funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
 		}
 
-		drm_atomic_bridge_post_disable(encoder->bridge, old_state);
+		drm_atomic_bridge_chain_post_disable(encoder->bridge,
+						     old_state);
 	}
 
 	for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
@@ -1215,7 +1217,8 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
 			funcs->mode_set(encoder, mode, adjusted_mode);
 		}
 
-		drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode);
+		drm_bridge_chain_mode_set(encoder->bridge, mode,
+					  adjusted_mode);
 	}
 }
 
@@ -1332,7 +1335,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 		 * Each encoder has at most one connector (since we always steal
 		 * it away), so we won't call enable hooks twice.
 		 */
-		drm_atomic_bridge_pre_enable(encoder->bridge, old_state);
+		drm_atomic_bridge_chain_pre_enable(encoder->bridge, old_state);
 
 		if (funcs) {
 			if (funcs->atomic_enable)
@@ -1343,7 +1346,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 				funcs->commit(encoder);
 		}
 
-		drm_atomic_bridge_enable(encoder->bridge, old_state);
+		drm_atomic_bridge_chain_enable(encoder->bridge, old_state);
 	}
 
 	drm_atomic_helper_commit_writebacks(dev, old_state);
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index cba537c99e43..54c874493c57 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -172,8 +172,8 @@ void drm_bridge_detach(struct drm_bridge *bridge)
  */
 
 /**
- * drm_bridge_mode_fixup - fixup proposed mode for all bridges in the
- *			   encoder chain
+ * drm_bridge_chain_mode_fixup - fixup proposed mode for all bridges in the
+ *				 encoder chain
  * @bridge: bridge control structure
  * @mode: desired mode to be set for the bridge
  * @adjusted_mode: updated mode that works for this bridge
@@ -186,9 +186,9 @@ void drm_bridge_detach(struct drm_bridge *bridge)
  * RETURNS:
  * true on success, false on failure
  */
-bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
-			const struct drm_display_mode *mode,
-			struct drm_display_mode *adjusted_mode)
+bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
+				 const struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted_mode)
 {
 	bool ret = true;
 
@@ -198,15 +198,16 @@ bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
 	if (bridge->funcs->mode_fixup)
 		ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode);
 
-	ret = ret && drm_bridge_mode_fixup(bridge->next, mode, adjusted_mode);
+	ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode,
+						 adjusted_mode);
 
 	return ret;
 }
-EXPORT_SYMBOL(drm_bridge_mode_fixup);
+EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
 
 /**
- * drm_bridge_mode_valid - validate the mode against all bridges in the
- * 			   encoder chain.
+ * drm_bridge_chain_mode_valid - validate the mode against all bridges in the
+ *				 encoder chain.
  * @bridge: bridge control structure
  * @mode: desired mode to be validated
  *
@@ -219,8 +220,9 @@ EXPORT_SYMBOL(drm_bridge_mode_fixup);
  * RETURNS:
  * MODE_OK on success, drm_mode_status Enum error code on failure
  */
-enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge,
-					   const struct drm_display_mode *mode)
+enum drm_mode_status
+drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
+			    const struct drm_display_mode *mode)
 {
 	enum drm_mode_status ret = MODE_OK;
 
@@ -233,12 +235,12 @@ enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge,
 	if (ret != MODE_OK)
 		return ret;
 
-	return drm_bridge_mode_valid(bridge->next, mode);
+	return drm_bridge_chain_mode_valid(bridge->next, mode);
 }
-EXPORT_SYMBOL(drm_bridge_mode_valid);
+EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
 
 /**
- * drm_bridge_disable - disables all bridges in the encoder chain
+ * drm_bridge_chain_disable - disables all bridges in the encoder chain
  * @bridge: bridge control structure
  *
  * Calls &drm_bridge_funcs.disable op for all the bridges in the encoder
@@ -247,20 +249,21 @@ EXPORT_SYMBOL(drm_bridge_mode_valid);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_bridge_disable(struct drm_bridge *bridge)
+void drm_bridge_chain_disable(struct drm_bridge *bridge)
 {
 	if (!bridge)
 		return;
 
-	drm_bridge_disable(bridge->next);
+	drm_bridge_chain_disable(bridge->next);
 
 	if (bridge->funcs->disable)
 		bridge->funcs->disable(bridge);
 }
-EXPORT_SYMBOL(drm_bridge_disable);
+EXPORT_SYMBOL(drm_bridge_chain_disable);
 
 /**
- * drm_bridge_post_disable - cleans up after disabling all bridges in the encoder chain
+ * drm_bridge_chain_post_disable - cleans up after disabling all bridges in the
+ *				   encoder chain
  * @bridge: bridge control structure
  *
  * Calls &drm_bridge_funcs.post_disable op for all the bridges in the
@@ -269,7 +272,7 @@ EXPORT_SYMBOL(drm_bridge_disable);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_bridge_post_disable(struct drm_bridge *bridge)
+void drm_bridge_chain_post_disable(struct drm_bridge *bridge)
 {
 	if (!bridge)
 		return;
@@ -277,25 +280,25 @@ void drm_bridge_post_disable(struct drm_bridge *bridge)
 	if (bridge->funcs->post_disable)
 		bridge->funcs->post_disable(bridge);
 
-	drm_bridge_post_disable(bridge->next);
+	drm_bridge_chain_post_disable(bridge->next);
 }
-EXPORT_SYMBOL(drm_bridge_post_disable);
+EXPORT_SYMBOL(drm_bridge_chain_post_disable);
 
 /**
- * drm_bridge_mode_set - set proposed mode for all bridges in the
- *			 encoder chain
+ * drm_bridge_chain_mode_set - set proposed mode for all bridges in the
+ *			       encoder chain
  * @bridge: bridge control structure
- * @mode: desired mode to be set for the bridge
- * @adjusted_mode: updated mode that works for this bridge
+ * @mode: desired mode to be set for the encoder chain
+ * @adjusted_mode: updated mode that works for this encoder chain
  *
  * Calls &drm_bridge_funcs.mode_set op for all the bridges in the
  * encoder chain, starting from the first bridge to the last.
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_bridge_mode_set(struct drm_bridge *bridge,
-			 const struct drm_display_mode *mode,
-			 const struct drm_display_mode *adjusted_mode)
+void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
+			       const struct drm_display_mode *mode,
+			       const struct drm_display_mode *adjusted_mode)
 {
 	if (!bridge)
 		return;
@@ -303,13 +306,13 @@ void drm_bridge_mode_set(struct drm_bridge *bridge,
 	if (bridge->funcs->mode_set)
 		bridge->funcs->mode_set(bridge, mode, adjusted_mode);
 
-	drm_bridge_mode_set(bridge->next, mode, adjusted_mode);
+	drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode);
 }
-EXPORT_SYMBOL(drm_bridge_mode_set);
+EXPORT_SYMBOL(drm_bridge_chain_mode_set);
 
 /**
- * drm_bridge_pre_enable - prepares for enabling all
- *			   bridges in the encoder chain
+ * drm_bridge_chain_pre_enable - prepares for enabling all bridges in the
+ *				 encoder chain
  * @bridge: bridge control structure
  *
  * Calls &drm_bridge_funcs.pre_enable op for all the bridges in the encoder
@@ -318,20 +321,20 @@ EXPORT_SYMBOL(drm_bridge_mode_set);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_bridge_pre_enable(struct drm_bridge *bridge)
+void drm_bridge_chain_pre_enable(struct drm_bridge *bridge)
 {
 	if (!bridge)
 		return;
 
-	drm_bridge_pre_enable(bridge->next);
+	drm_bridge_chain_pre_enable(bridge->next);
 
 	if (bridge->funcs->pre_enable)
 		bridge->funcs->pre_enable(bridge);
 }
-EXPORT_SYMBOL(drm_bridge_pre_enable);
+EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
 
 /**
- * drm_bridge_enable - enables all bridges in the encoder chain
+ * drm_bridge_chain_enable - enables all bridges in the encoder chain
  * @bridge: bridge control structure
  *
  * Calls &drm_bridge_funcs.enable op for all the bridges in the encoder
@@ -340,7 +343,7 @@ EXPORT_SYMBOL(drm_bridge_pre_enable);
  *
  * Note that the bridge passed should be the one closest to the encoder
  */
-void drm_bridge_enable(struct drm_bridge *bridge)
+void drm_bridge_chain_enable(struct drm_bridge *bridge)
 {
 	if (!bridge)
 		return;
@@ -348,12 +351,12 @@ void drm_bridge_enable(struct drm_bridge *bridge)
 	if (bridge->funcs->enable)
 		bridge->funcs->enable(bridge);
 
-	drm_bridge_enable(bridge->next);
+	drm_bridge_chain_enable(bridge->next);
 }
-EXPORT_SYMBOL(drm_bridge_enable);
+EXPORT_SYMBOL(drm_bridge_chain_enable);
 
 /**
- * drm_atomic_bridge_disable - disables all bridges in the encoder chain
+ * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain
  * @bridge: bridge control structure
  * @state: atomic state being committed
  *
@@ -364,24 +367,24 @@ EXPORT_SYMBOL(drm_bridge_enable);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_atomic_bridge_disable(struct drm_bridge *bridge,
-			       struct drm_atomic_state *state)
+void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
+				     struct drm_atomic_state *state)
 {
 	if (!bridge)
 		return;
 
-	drm_atomic_bridge_disable(bridge->next, state);
+	drm_atomic_bridge_chain_disable(bridge->next, state);
 
 	if (bridge->funcs->atomic_disable)
 		bridge->funcs->atomic_disable(bridge, state);
 	else if (bridge->funcs->disable)
 		bridge->funcs->disable(bridge);
 }
-EXPORT_SYMBOL(drm_atomic_bridge_disable);
+EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
 
 /**
- * drm_atomic_bridge_post_disable - cleans up after disabling all bridges in the
- *				    encoder chain
+ * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges
+ *					  in the encoder chain
  * @bridge: bridge control structure
  * @state: atomic state being committed
  *
@@ -392,8 +395,8 @@ EXPORT_SYMBOL(drm_atomic_bridge_disable);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_atomic_bridge_post_disable(struct drm_bridge *bridge,
-				    struct drm_atomic_state *state)
+void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
+					  struct drm_atomic_state *state)
 {
 	if (!bridge)
 		return;
@@ -403,13 +406,13 @@ void drm_atomic_bridge_post_disable(struct drm_bridge *bridge,
 	else if (bridge->funcs->post_disable)
 		bridge->funcs->post_disable(bridge);
 
-	drm_atomic_bridge_post_disable(bridge->next, state);
+	drm_atomic_bridge_chain_post_disable(bridge->next, state);
 }
-EXPORT_SYMBOL(drm_atomic_bridge_post_disable);
+EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
 
 /**
- * drm_atomic_bridge_pre_enable - prepares for enabling all bridges in the
- *				  encoder chain
+ * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
+ *					the encoder chain
  * @bridge: bridge control structure
  * @state: atomic state being committed
  *
@@ -420,23 +423,23 @@ EXPORT_SYMBOL(drm_atomic_bridge_post_disable);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge,
-				  struct drm_atomic_state *state)
+void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
+					struct drm_atomic_state *state)
 {
 	if (!bridge)
 		return;
 
-	drm_atomic_bridge_pre_enable(bridge->next, state);
+	drm_atomic_bridge_chain_pre_enable(bridge->next, state);
 
 	if (bridge->funcs->atomic_pre_enable)
 		bridge->funcs->atomic_pre_enable(bridge, state);
 	else if (bridge->funcs->pre_enable)
 		bridge->funcs->pre_enable(bridge);
 }
-EXPORT_SYMBOL(drm_atomic_bridge_pre_enable);
+EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
 
 /**
- * drm_atomic_bridge_enable - enables all bridges in the encoder chain
+ * drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain
  * @bridge: bridge control structure
  * @state: atomic state being committed
  *
@@ -447,8 +450,8 @@ EXPORT_SYMBOL(drm_atomic_bridge_pre_enable);
  *
  * Note: the bridge passed should be the one closest to the encoder
  */
-void drm_atomic_bridge_enable(struct drm_bridge *bridge,
-			      struct drm_atomic_state *state)
+void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
+				    struct drm_atomic_state *state)
 {
 	if (!bridge)
 		return;
@@ -458,9 +461,9 @@ void drm_atomic_bridge_enable(struct drm_bridge *bridge,
 	else if (bridge->funcs->enable)
 		bridge->funcs->enable(bridge);
 
-	drm_atomic_bridge_enable(bridge->next, state);
+	drm_atomic_bridge_chain_enable(bridge->next, state);
 }
-EXPORT_SYMBOL(drm_atomic_bridge_enable);
+EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
 
 #ifdef CONFIG_OF
 /**
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index a7c87abe88d0..c3ea722065c4 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -112,7 +112,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
 			continue;
 		}
 
-		ret = drm_bridge_mode_valid(encoder->bridge, mode);
+		ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
 		if (ret != MODE_OK) {
 			/* There is also no point in continuing for crtc check
 			 * here. */
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index ceb370864cc4..d984097704b8 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1390,7 +1390,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
 		if (ret < 0)
 			goto err_put_sync;
 	} else {
-		drm_bridge_pre_enable(dsi->out_bridge);
+		drm_bridge_chain_pre_enable(dsi->out_bridge);
 	}
 
 	exynos_dsi_set_display_mode(dsi);
@@ -1401,7 +1401,7 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
 		if (ret < 0)
 			goto err_display_disable;
 	} else {
-		drm_bridge_enable(dsi->out_bridge);
+		drm_bridge_chain_enable(dsi->out_bridge);
 	}
 
 	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
@@ -1426,10 +1426,10 @@ static void exynos_dsi_disable(struct drm_encoder *encoder)
 	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
 
 	drm_panel_disable(dsi->panel);
-	drm_bridge_disable(dsi->out_bridge);
+	drm_bridge_chain_disable(dsi->out_bridge);
 	exynos_dsi_set_display_enable(dsi, false);
 	drm_panel_unprepare(dsi->panel);
-	drm_bridge_post_disable(dsi->out_bridge);
+	drm_bridge_chain_post_disable(dsi->out_bridge);
 	dsi->state &= ~DSIM_STATE_ENABLED;
 	pm_runtime_put_sync(dsi->dev);
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index f684947c5243..ee3a5e9c10c4 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -1247,8 +1247,8 @@ static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn,
 		struct drm_display_mode adjusted_mode;
 
 		drm_mode_copy(&adjusted_mode, mode);
-		if (!drm_bridge_mode_fixup(hdmi->bridge.next, mode,
-					   &adjusted_mode))
+		if (!drm_bridge_chain_mode_fixup(hdmi->bridge.next, mode,
+						 &adjusted_mode))
 			return MODE_BAD;
 	}
 
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index c9ba83ed49b9..ff81b54ea281 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -753,9 +753,9 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
 	struct vc4_dsi *dsi = vc4_encoder->dsi;
 	struct device *dev = &dsi->pdev->dev;
 
-	drm_bridge_disable(dsi->bridge);
+	drm_bridge_chain_disable(dsi->bridge);
 	vc4_dsi_ulps(dsi, true);
-	drm_bridge_post_disable(dsi->bridge);
+	drm_bridge_chain_post_disable(dsi->bridge);
 
 	clk_disable_unprepare(dsi->pll_phy_clock);
 	clk_disable_unprepare(dsi->escape_clock);
@@ -1055,7 +1055,7 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 
 	vc4_dsi_ulps(dsi, false);
 
-	drm_bridge_pre_enable(dsi->bridge);
+	drm_bridge_chain_pre_enable(dsi->bridge);
 
 	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
 		DSI_PORT_WRITE(DISP0_CTRL,
@@ -1072,7 +1072,7 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 			       DSI_DISP0_ENABLE);
 	}
 
-	drm_bridge_enable(dsi->bridge);
+	drm_bridge_chain_enable(dsi->bridge);
 
 	if (debug_dump_regs) {
 		struct drm_printer p = drm_info_printer(&dsi->pdev->dev);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index c0a2286a81e9..726435baf4ad 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -254,9 +254,10 @@ struct drm_bridge_funcs {
 	 * there is one) when this callback is called.
 	 *
 	 * Note that this function will only be invoked in the context of an
-	 * atomic commit. It will not be invoked from &drm_bridge_pre_enable. It
-	 * would be prudent to also provide an implementation of @pre_enable if
-	 * you are expecting driver calls into &drm_bridge_pre_enable.
+	 * atomic commit. It will not be invoked from
+	 * &drm_bridge_chain_pre_enable. It would be prudent to also provide an
+	 * implementation of @pre_enable if you are expecting driver calls into
+	 * &drm_bridge_chain_pre_enable.
 	 *
 	 * The @atomic_pre_enable callback is optional.
 	 */
@@ -279,9 +280,9 @@ struct drm_bridge_funcs {
 	 * chain if there is one.
 	 *
 	 * Note that this function will only be invoked in the context of an
-	 * atomic commit. It will not be invoked from &drm_bridge_enable. It
-	 * would be prudent to also provide an implementation of @enable if
-	 * you are expecting driver calls into &drm_bridge_enable.
+	 * atomic commit. It will not be invoked from &drm_bridge_chain_enable.
+	 * It would be prudent to also provide an implementation of @enable if
+	 * you are expecting driver calls into &drm_bridge_chain_enable.
 	 *
 	 * The @atomic_enable callback is optional.
 	 */
@@ -301,9 +302,10 @@ struct drm_bridge_funcs {
 	 * signals) feeding it is still running when this callback is called.
 	 *
 	 * Note that this function will only be invoked in the context of an
-	 * atomic commit. It will not be invoked from &drm_bridge_disable. It
-	 * would be prudent to also provide an implementation of @disable if
-	 * you are expecting driver calls into &drm_bridge_disable.
+	 * atomic commit. It will not be invoked from
+	 * &drm_bridge_chain_disable. It would be prudent to also provide an
+	 * implementation of @disable if you are expecting driver calls into
+	 * &drm_bridge_chain_disable.
 	 *
 	 * The @atomic_disable callback is optional.
 	 */
@@ -325,10 +327,11 @@ struct drm_bridge_funcs {
 	 * called.
 	 *
 	 * Note that this function will only be invoked in the context of an
-	 * atomic commit. It will not be invoked from &drm_bridge_post_disable.
+	 * atomic commit. It will not be invoked from
+	 * &drm_bridge_chain_post_disable.
 	 * It would be prudent to also provide an implementation of
 	 * @post_disable if you are expecting driver calls into
-	 * &drm_bridge_post_disable.
+	 * &drm_bridge_chain_post_disable.
 	 *
 	 * The @atomic_post_disable callback is optional.
 	 */
@@ -406,27 +409,28 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np);
 int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 		      struct drm_bridge *previous);
 
-bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
-			   const struct drm_display_mode *mode,
-			   struct drm_display_mode *adjusted_mode);
-enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge,
-					   const struct drm_display_mode *mode);
-void drm_bridge_disable(struct drm_bridge *bridge);
-void drm_bridge_post_disable(struct drm_bridge *bridge);
-void drm_bridge_mode_set(struct drm_bridge *bridge,
-			 const struct drm_display_mode *mode,
-			 const struct drm_display_mode *adjusted_mode);
-void drm_bridge_pre_enable(struct drm_bridge *bridge);
-void drm_bridge_enable(struct drm_bridge *bridge);
+bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
+				 const struct drm_display_mode *mode,
+				 struct drm_display_mode *adjusted_mode);
+enum drm_mode_status
+drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
+			    const struct drm_display_mode *mode);
+void drm_bridge_chain_disable(struct drm_bridge *bridge);
+void drm_bridge_chain_post_disable(struct drm_bridge *bridge);
+void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
+			       const struct drm_display_mode *mode,
+			       const struct drm_display_mode *adjusted_mode);
+void drm_bridge_chain_pre_enable(struct drm_bridge *bridge);
+void drm_bridge_chain_enable(struct drm_bridge *bridge);
 
-void drm_atomic_bridge_disable(struct drm_bridge *bridge,
-			       struct drm_atomic_state *state);
-void drm_atomic_bridge_post_disable(struct drm_bridge *bridge,
+void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
+				     struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
+					  struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
+					struct drm_atomic_state *state);
+void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 				    struct drm_atomic_state *state);
-void drm_atomic_bridge_pre_enable(struct drm_bridge *bridge,
-				  struct drm_atomic_state *state);
-void drm_atomic_bridge_enable(struct drm_bridge *bridge,
-			      struct drm_atomic_state *state);
 
 #ifdef CONFIG_DRM_PANEL_BRIDGE
 struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel);
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 02/11] drm/bridge: Introduce drm_bridge_get_next_bridge()
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 14:15   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon, Laurent Pinchart

And use it in drivers accessing the bridge->next field directly.
This is part of our attempt to make the bridge chain a double-linked list
based on the generic list helpers.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes in v4:
* Rename the helper
* Add R-bs

Changes in v3:
* Inline drm_bridge_chain_get_next_bridge() (Suggested by Laurent)

Changes in v2:
* Kill the last/first helpers (they're not really needed)
* Drop the !bridge || !bridge->encoder test
---
 drivers/gpu/drm/drm_encoder.c          |  2 +-
 drivers/gpu/drm/mediatek/mtk_hdmi.c    |  6 ++++--
 drivers/gpu/drm/omapdrm/omap_drv.c     |  4 ++--
 drivers/gpu/drm/omapdrm/omap_encoder.c |  3 ++-
 include/drm/drm_bridge.h               | 13 +++++++++++++
 5 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index 80d88a55302e..fcd3af1ac432 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -171,7 +171,7 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
 		struct drm_bridge *next;
 
 		while (bridge) {
-			next = bridge->next;
+			next = drm_bridge_get_next_bridge(bridge);
 			drm_bridge_detach(bridge);
 			bridge = next;
 		}
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index ee3a5e9c10c4..5e4a4dbda443 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -1238,16 +1238,18 @@ static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn,
 				    struct drm_display_mode *mode)
 {
 	struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
+	struct drm_bridge *next_bridge;
 
 	dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
 		mode->hdisplay, mode->vdisplay, mode->vrefresh,
 		!!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000);
 
-	if (hdmi->bridge.next) {
+	next_bridge = drm_bridge_get_next_bridge(&hdmi->bridge);
+	if (next_bridge) {
 		struct drm_display_mode adjusted_mode;
 
 		drm_mode_copy(&adjusted_mode, mode);
-		if (!drm_bridge_chain_mode_fixup(hdmi->bridge.next, mode,
+		if (!drm_bridge_chain_mode_fixup(next_bridge, mode,
 						 &adjusted_mode))
 			return MODE_BAD;
 	}
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index b3e22c890c51..d2750f60f519 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -217,8 +217,8 @@ static int omap_display_id(struct omap_dss_device *output)
 	} else if (output->bridge) {
 		struct drm_bridge *bridge = output->bridge;
 
-		while (bridge->next)
-			bridge = bridge->next;
+		while (drm_bridge_get_next_bridge(bridge))
+			bridge = drm_bridge_get_next_bridge(bridge);
 
 		node = bridge->of_node;
 	} else if (output->panel) {
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c
index 24bbe9f2a32e..4f2165a37795 100644
--- a/drivers/gpu/drm/omapdrm/omap_encoder.c
+++ b/drivers/gpu/drm/omapdrm/omap_encoder.c
@@ -126,7 +126,8 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
 	for (dssdev = output; dssdev; dssdev = dssdev->next)
 		omap_encoder_update_videomode_flags(&vm, dssdev->bus_flags);
 
-	for (bridge = output->bridge; bridge; bridge = bridge->next) {
+	for (bridge = output->bridge; bridge;
+	     bridge = drm_bridge_get_next_bridge(bridge)) {
 		if (!bridge->timings)
 			continue;
 
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 726435baf4ad..46fc17c63a44 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -409,6 +409,19 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np);
 int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 		      struct drm_bridge *previous);
 
+/**
+ * drm_bridge_get_next_bridge() - Get the next bridge in the chain
+ * @bridge: bridge object
+ *
+ * RETURNS:
+ * the next bridge in the chain after @bridge, or NULL if @bridge is the last.
+ */
+static inline struct drm_bridge *
+drm_bridge_get_next_bridge(struct drm_bridge *bridge)
+{
+	return bridge->next;
+}
+
 bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
 				 const struct drm_display_mode *mode,
 				 struct drm_display_mode *adjusted_mode);
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 02/11] drm/bridge: Introduce drm_bridge_get_next_bridge()
@ 2019-12-03 14:15   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

And use it in drivers accessing the bridge->next field directly.
This is part of our attempt to make the bridge chain a double-linked list
based on the generic list helpers.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes in v4:
* Rename the helper
* Add R-bs

Changes in v3:
* Inline drm_bridge_chain_get_next_bridge() (Suggested by Laurent)

Changes in v2:
* Kill the last/first helpers (they're not really needed)
* Drop the !bridge || !bridge->encoder test
---
 drivers/gpu/drm/drm_encoder.c          |  2 +-
 drivers/gpu/drm/mediatek/mtk_hdmi.c    |  6 ++++--
 drivers/gpu/drm/omapdrm/omap_drv.c     |  4 ++--
 drivers/gpu/drm/omapdrm/omap_encoder.c |  3 ++-
 include/drm/drm_bridge.h               | 13 +++++++++++++
 5 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index 80d88a55302e..fcd3af1ac432 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -171,7 +171,7 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
 		struct drm_bridge *next;
 
 		while (bridge) {
-			next = bridge->next;
+			next = drm_bridge_get_next_bridge(bridge);
 			drm_bridge_detach(bridge);
 			bridge = next;
 		}
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index ee3a5e9c10c4..5e4a4dbda443 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -1238,16 +1238,18 @@ static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn,
 				    struct drm_display_mode *mode)
 {
 	struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
+	struct drm_bridge *next_bridge;
 
 	dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
 		mode->hdisplay, mode->vdisplay, mode->vrefresh,
 		!!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000);
 
-	if (hdmi->bridge.next) {
+	next_bridge = drm_bridge_get_next_bridge(&hdmi->bridge);
+	if (next_bridge) {
 		struct drm_display_mode adjusted_mode;
 
 		drm_mode_copy(&adjusted_mode, mode);
-		if (!drm_bridge_chain_mode_fixup(hdmi->bridge.next, mode,
+		if (!drm_bridge_chain_mode_fixup(next_bridge, mode,
 						 &adjusted_mode))
 			return MODE_BAD;
 	}
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index b3e22c890c51..d2750f60f519 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -217,8 +217,8 @@ static int omap_display_id(struct omap_dss_device *output)
 	} else if (output->bridge) {
 		struct drm_bridge *bridge = output->bridge;
 
-		while (bridge->next)
-			bridge = bridge->next;
+		while (drm_bridge_get_next_bridge(bridge))
+			bridge = drm_bridge_get_next_bridge(bridge);
 
 		node = bridge->of_node;
 	} else if (output->panel) {
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c
index 24bbe9f2a32e..4f2165a37795 100644
--- a/drivers/gpu/drm/omapdrm/omap_encoder.c
+++ b/drivers/gpu/drm/omapdrm/omap_encoder.c
@@ -126,7 +126,8 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
 	for (dssdev = output; dssdev; dssdev = dssdev->next)
 		omap_encoder_update_videomode_flags(&vm, dssdev->bus_flags);
 
-	for (bridge = output->bridge; bridge; bridge = bridge->next) {
+	for (bridge = output->bridge; bridge;
+	     bridge = drm_bridge_get_next_bridge(bridge)) {
 		if (!bridge->timings)
 			continue;
 
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 726435baf4ad..46fc17c63a44 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -409,6 +409,19 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np);
 int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 		      struct drm_bridge *previous);
 
+/**
+ * drm_bridge_get_next_bridge() - Get the next bridge in the chain
+ * @bridge: bridge object
+ *
+ * RETURNS:
+ * the next bridge in the chain after @bridge, or NULL if @bridge is the last.
+ */
+static inline struct drm_bridge *
+drm_bridge_get_next_bridge(struct drm_bridge *bridge)
+{
+	return bridge->next;
+}
+
 bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
 				 const struct drm_display_mode *mode,
 				 struct drm_display_mode *adjusted_mode);
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 03/11] drm: Stop accessing encoder->bridge directly
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 14:15   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon, Laurent Pinchart

We are about to replace the single-linked bridge list by a double-linked
one based on list.h, leading to the suppression of the encoder->bridge
field. But before we can do that we must provide a
drm_bridge_chain_get_first_bridge() bridge helper and patch all drivers
and core helpers to use it instead of directly accessing encoder->bridge.

Note that we still have 2 drivers (VC4 and Exynos) manipulating the
encoder->bridge field directly because they need to cut the bridge chain
in order to control the enable/disable sequence. This is definitely
not something we want to encourage, so let's keep those 2 oddities
around until we find a better solution.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
---
Changes in v4:
* Coding style issues
* Add Rbs
* Adjust the commit message to explain why VC4 and exynos still
  access encoder->bridge directly

Changes in v3:
* None

Changes in v2:
* None
---
 drivers/gpu/drm/drm_atomic_helper.c    | 26 +++++++++++++++++---------
 drivers/gpu/drm/drm_encoder.c          |  3 ++-
 drivers/gpu/drm/drm_probe_helper.c     |  4 +++-
 drivers/gpu/drm/msm/edp/edp_bridge.c   | 10 ++++++++--
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 10 +++++++---
 include/drm/drm_bridge.h               | 15 +++++++++++++++
 6 files changed, 52 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index cf678be58fa4..29c3115bf9e2 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -419,6 +419,7 @@ mode_fixup(struct drm_atomic_state *state)
 	for_each_new_connector_in_state(state, connector, new_conn_state, i) {
 		const struct drm_encoder_helper_funcs *funcs;
 		struct drm_encoder *encoder;
+		struct drm_bridge *bridge;
 
 		WARN_ON(!!new_conn_state->best_encoder != !!new_conn_state->crtc);
 
@@ -435,7 +436,8 @@ mode_fixup(struct drm_atomic_state *state)
 		encoder = new_conn_state->best_encoder;
 		funcs = encoder->helper_private;
 
-		ret = drm_bridge_chain_mode_fixup(encoder->bridge,
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
+		ret = drm_bridge_chain_mode_fixup(bridge,
 					&new_crtc_state->mode,
 					&new_crtc_state->adjusted_mode);
 		if (!ret) {
@@ -493,6 +495,7 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector,
 					    struct drm_crtc *crtc,
 					    const struct drm_display_mode *mode)
 {
+	struct drm_bridge *bridge;
 	enum drm_mode_status ret;
 
 	ret = drm_encoder_mode_valid(encoder, mode);
@@ -502,7 +505,8 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector,
 		return ret;
 	}
 
-	ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
+	bridge = drm_bridge_chain_get_first_bridge(encoder);
+	ret = drm_bridge_chain_mode_valid(bridge, mode);
 	if (ret != MODE_OK) {
 		DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n");
 		return ret;
@@ -985,6 +989,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 	for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) {
 		const struct drm_encoder_helper_funcs *funcs;
 		struct drm_encoder *encoder;
+		struct drm_bridge *bridge;
 
 		/* Shut down everything that's in the changeset and currently
 		 * still on. So need to check the old, saved state. */
@@ -1021,7 +1026,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 		 * Each encoder has at most one connector (since we always steal
 		 * it away), so we won't call disable hooks twice.
 		 */
-		drm_atomic_bridge_chain_disable(encoder->bridge, old_state);
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
+		drm_atomic_bridge_chain_disable(bridge, old_state);
 
 		/* Right function depends upon target state. */
 		if (funcs) {
@@ -1035,8 +1041,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 				funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
 		}
 
-		drm_atomic_bridge_chain_post_disable(encoder->bridge,
-						     old_state);
+		drm_atomic_bridge_chain_post_disable(bridge, old_state);
 	}
 
 	for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
@@ -1190,6 +1195,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
 		const struct drm_encoder_helper_funcs *funcs;
 		struct drm_encoder *encoder;
 		struct drm_display_mode *mode, *adjusted_mode;
+		struct drm_bridge *bridge;
 
 		if (!new_conn_state->best_encoder)
 			continue;
@@ -1217,8 +1223,8 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
 			funcs->mode_set(encoder, mode, adjusted_mode);
 		}
 
-		drm_bridge_chain_mode_set(encoder->bridge, mode,
-					  adjusted_mode);
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
+		drm_bridge_chain_mode_set(bridge, mode, adjusted_mode);
 	}
 }
 
@@ -1317,6 +1323,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 	for_each_new_connector_in_state(old_state, connector, new_conn_state, i) {
 		const struct drm_encoder_helper_funcs *funcs;
 		struct drm_encoder *encoder;
+		struct drm_bridge *bridge;
 
 		if (!new_conn_state->best_encoder)
 			continue;
@@ -1335,7 +1342,8 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 		 * Each encoder has at most one connector (since we always steal
 		 * it away), so we won't call enable hooks twice.
 		 */
-		drm_atomic_bridge_chain_pre_enable(encoder->bridge, old_state);
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
+		drm_atomic_bridge_chain_pre_enable(bridge, old_state);
 
 		if (funcs) {
 			if (funcs->atomic_enable)
@@ -1346,7 +1354,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 				funcs->commit(encoder);
 		}
 
-		drm_atomic_bridge_chain_enable(encoder->bridge, old_state);
+		drm_atomic_bridge_chain_enable(bridge, old_state);
 	}
 
 	drm_atomic_helper_commit_writebacks(dev, old_state);
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index fcd3af1ac432..a2cc7e7241a9 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -167,9 +167,10 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
 	 */
 
 	if (encoder->bridge) {
-		struct drm_bridge *bridge = encoder->bridge;
+		struct drm_bridge *bridge;
 		struct drm_bridge *next;
 
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
 		while (bridge) {
 			next = drm_bridge_get_next_bridge(bridge);
 			drm_bridge_detach(bridge);
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index c3ea722065c4..576b4b7dcd89 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -101,6 +101,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
 
 	/* Step 2: Validate against encoders and crtcs */
 	drm_connector_for_each_possible_encoder(connector, encoder) {
+		struct drm_bridge *bridge;
 		struct drm_crtc *crtc;
 
 		ret = drm_encoder_mode_valid(encoder, mode);
@@ -112,7 +113,8 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
 			continue;
 		}
 
-		ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
+		ret = drm_bridge_chain_mode_valid(bridge, mode);
 		if (ret != MODE_OK) {
 			/* There is also no point in continuing for crtc check
 			 * here. */
diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c
index 2950bba4aca9..b65b5cc2dba2 100644
--- a/drivers/gpu/drm/msm/edp/edp_bridge.c
+++ b/drivers/gpu/drm/msm/edp/edp_bridge.c
@@ -55,8 +55,14 @@ static void edp_bridge_mode_set(struct drm_bridge *bridge,
 	DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));
 
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-		if ((connector->encoder != NULL) &&
-			(connector->encoder->bridge == bridge)) {
+		struct drm_encoder *encoder = connector->encoder;
+		struct drm_bridge *first_bridge;
+
+		if (!connector->encoder)
+			continue;
+
+		first_bridge = drm_bridge_chain_get_first_bridge(encoder);
+		if (bridge == first_bridge) {
 			msm_edp_ctrl_timing_cfg(edp->ctrl,
 				adjusted_mode, &connector->display_info);
 			break;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 2da46e3dc4ae..f2ae4c410244 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -14,6 +14,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
@@ -680,9 +681,10 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
 			rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index];
 		const struct drm_display_mode *mode =
 			&crtc->state->adjusted_mode;
+		struct drm_bridge *bridge;
 
-		rcar_lvds_clk_enable(encoder->base.bridge,
-				     mode->clock * 1000);
+		bridge = drm_bridge_chain_get_first_bridge(&encoder->base);
+		rcar_lvds_clk_enable(bridge, mode->clock * 1000);
 	}
 
 	rcar_du_crtc_start(rcrtc);
@@ -702,12 +704,14 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
 	    rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) {
 		struct rcar_du_encoder *encoder =
 			rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index];
+		struct drm_bridge *bridge;
 
 		/*
 		 * Disable the LVDS clock output, see
 		 * rcar_du_crtc_atomic_enable().
 		 */
-		rcar_lvds_clk_disable(encoder->base.bridge);
+		bridge = drm_bridge_chain_get_first_bridge(&encoder->base);
+		rcar_lvds_clk_disable(bridge);
 	}
 
 	spin_lock_irq(&crtc->dev->event_lock);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 46fc17c63a44..bd78c256b1ed 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -25,6 +25,7 @@
 
 #include <linux/list.h>
 #include <linux/ctype.h>
+#include <drm/drm_encoder.h>
 #include <drm/drm_mode_object.h>
 #include <drm/drm_modes.h>
 
@@ -422,6 +423,20 @@ drm_bridge_get_next_bridge(struct drm_bridge *bridge)
 	return bridge->next;
 }
 
+/**
+ * drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain
+ * @encoder: encoder object
+ *
+ * RETURNS:
+ * the first bridge in the chain, or NULL if @encoder has no bridge attached
+ * to it.
+ */
+static inline struct drm_bridge *
+drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder)
+{
+	return encoder->bridge;
+}
+
 bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
 				 const struct drm_display_mode *mode,
 				 struct drm_display_mode *adjusted_mode);
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 03/11] drm: Stop accessing encoder->bridge directly
@ 2019-12-03 14:15   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

We are about to replace the single-linked bridge list by a double-linked
one based on list.h, leading to the suppression of the encoder->bridge
field. But before we can do that we must provide a
drm_bridge_chain_get_first_bridge() bridge helper and patch all drivers
and core helpers to use it instead of directly accessing encoder->bridge.

Note that we still have 2 drivers (VC4 and Exynos) manipulating the
encoder->bridge field directly because they need to cut the bridge chain
in order to control the enable/disable sequence. This is definitely
not something we want to encourage, so let's keep those 2 oddities
around until we find a better solution.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
---
Changes in v4:
* Coding style issues
* Add Rbs
* Adjust the commit message to explain why VC4 and exynos still
  access encoder->bridge directly

Changes in v3:
* None

Changes in v2:
* None
---
 drivers/gpu/drm/drm_atomic_helper.c    | 26 +++++++++++++++++---------
 drivers/gpu/drm/drm_encoder.c          |  3 ++-
 drivers/gpu/drm/drm_probe_helper.c     |  4 +++-
 drivers/gpu/drm/msm/edp/edp_bridge.c   | 10 ++++++++--
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 10 +++++++---
 include/drm/drm_bridge.h               | 15 +++++++++++++++
 6 files changed, 52 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index cf678be58fa4..29c3115bf9e2 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -419,6 +419,7 @@ mode_fixup(struct drm_atomic_state *state)
 	for_each_new_connector_in_state(state, connector, new_conn_state, i) {
 		const struct drm_encoder_helper_funcs *funcs;
 		struct drm_encoder *encoder;
+		struct drm_bridge *bridge;
 
 		WARN_ON(!!new_conn_state->best_encoder != !!new_conn_state->crtc);
 
@@ -435,7 +436,8 @@ mode_fixup(struct drm_atomic_state *state)
 		encoder = new_conn_state->best_encoder;
 		funcs = encoder->helper_private;
 
-		ret = drm_bridge_chain_mode_fixup(encoder->bridge,
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
+		ret = drm_bridge_chain_mode_fixup(bridge,
 					&new_crtc_state->mode,
 					&new_crtc_state->adjusted_mode);
 		if (!ret) {
@@ -493,6 +495,7 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector,
 					    struct drm_crtc *crtc,
 					    const struct drm_display_mode *mode)
 {
+	struct drm_bridge *bridge;
 	enum drm_mode_status ret;
 
 	ret = drm_encoder_mode_valid(encoder, mode);
@@ -502,7 +505,8 @@ static enum drm_mode_status mode_valid_path(struct drm_connector *connector,
 		return ret;
 	}
 
-	ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
+	bridge = drm_bridge_chain_get_first_bridge(encoder);
+	ret = drm_bridge_chain_mode_valid(bridge, mode);
 	if (ret != MODE_OK) {
 		DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n");
 		return ret;
@@ -985,6 +989,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 	for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) {
 		const struct drm_encoder_helper_funcs *funcs;
 		struct drm_encoder *encoder;
+		struct drm_bridge *bridge;
 
 		/* Shut down everything that's in the changeset and currently
 		 * still on. So need to check the old, saved state. */
@@ -1021,7 +1026,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 		 * Each encoder has at most one connector (since we always steal
 		 * it away), so we won't call disable hooks twice.
 		 */
-		drm_atomic_bridge_chain_disable(encoder->bridge, old_state);
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
+		drm_atomic_bridge_chain_disable(bridge, old_state);
 
 		/* Right function depends upon target state. */
 		if (funcs) {
@@ -1035,8 +1041,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 				funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
 		}
 
-		drm_atomic_bridge_chain_post_disable(encoder->bridge,
-						     old_state);
+		drm_atomic_bridge_chain_post_disable(bridge, old_state);
 	}
 
 	for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) {
@@ -1190,6 +1195,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
 		const struct drm_encoder_helper_funcs *funcs;
 		struct drm_encoder *encoder;
 		struct drm_display_mode *mode, *adjusted_mode;
+		struct drm_bridge *bridge;
 
 		if (!new_conn_state->best_encoder)
 			continue;
@@ -1217,8 +1223,8 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
 			funcs->mode_set(encoder, mode, adjusted_mode);
 		}
 
-		drm_bridge_chain_mode_set(encoder->bridge, mode,
-					  adjusted_mode);
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
+		drm_bridge_chain_mode_set(bridge, mode, adjusted_mode);
 	}
 }
 
@@ -1317,6 +1323,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 	for_each_new_connector_in_state(old_state, connector, new_conn_state, i) {
 		const struct drm_encoder_helper_funcs *funcs;
 		struct drm_encoder *encoder;
+		struct drm_bridge *bridge;
 
 		if (!new_conn_state->best_encoder)
 			continue;
@@ -1335,7 +1342,8 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 		 * Each encoder has at most one connector (since we always steal
 		 * it away), so we won't call enable hooks twice.
 		 */
-		drm_atomic_bridge_chain_pre_enable(encoder->bridge, old_state);
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
+		drm_atomic_bridge_chain_pre_enable(bridge, old_state);
 
 		if (funcs) {
 			if (funcs->atomic_enable)
@@ -1346,7 +1354,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 				funcs->commit(encoder);
 		}
 
-		drm_atomic_bridge_chain_enable(encoder->bridge, old_state);
+		drm_atomic_bridge_chain_enable(bridge, old_state);
 	}
 
 	drm_atomic_helper_commit_writebacks(dev, old_state);
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index fcd3af1ac432..a2cc7e7241a9 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -167,9 +167,10 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
 	 */
 
 	if (encoder->bridge) {
-		struct drm_bridge *bridge = encoder->bridge;
+		struct drm_bridge *bridge;
 		struct drm_bridge *next;
 
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
 		while (bridge) {
 			next = drm_bridge_get_next_bridge(bridge);
 			drm_bridge_detach(bridge);
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index c3ea722065c4..576b4b7dcd89 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -101,6 +101,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
 
 	/* Step 2: Validate against encoders and crtcs */
 	drm_connector_for_each_possible_encoder(connector, encoder) {
+		struct drm_bridge *bridge;
 		struct drm_crtc *crtc;
 
 		ret = drm_encoder_mode_valid(encoder, mode);
@@ -112,7 +113,8 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
 			continue;
 		}
 
-		ret = drm_bridge_chain_mode_valid(encoder->bridge, mode);
+		bridge = drm_bridge_chain_get_first_bridge(encoder);
+		ret = drm_bridge_chain_mode_valid(bridge, mode);
 		if (ret != MODE_OK) {
 			/* There is also no point in continuing for crtc check
 			 * here. */
diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c
index 2950bba4aca9..b65b5cc2dba2 100644
--- a/drivers/gpu/drm/msm/edp/edp_bridge.c
+++ b/drivers/gpu/drm/msm/edp/edp_bridge.c
@@ -55,8 +55,14 @@ static void edp_bridge_mode_set(struct drm_bridge *bridge,
 	DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));
 
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-		if ((connector->encoder != NULL) &&
-			(connector->encoder->bridge == bridge)) {
+		struct drm_encoder *encoder = connector->encoder;
+		struct drm_bridge *first_bridge;
+
+		if (!connector->encoder)
+			continue;
+
+		first_bridge = drm_bridge_chain_get_first_bridge(encoder);
+		if (bridge == first_bridge) {
 			msm_edp_ctrl_timing_cfg(edp->ctrl,
 				adjusted_mode, &connector->display_info);
 			break;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 2da46e3dc4ae..f2ae4c410244 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -14,6 +14,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
@@ -680,9 +681,10 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc,
 			rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index];
 		const struct drm_display_mode *mode =
 			&crtc->state->adjusted_mode;
+		struct drm_bridge *bridge;
 
-		rcar_lvds_clk_enable(encoder->base.bridge,
-				     mode->clock * 1000);
+		bridge = drm_bridge_chain_get_first_bridge(&encoder->base);
+		rcar_lvds_clk_enable(bridge, mode->clock * 1000);
 	}
 
 	rcar_du_crtc_start(rcrtc);
@@ -702,12 +704,14 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc,
 	    rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) {
 		struct rcar_du_encoder *encoder =
 			rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index];
+		struct drm_bridge *bridge;
 
 		/*
 		 * Disable the LVDS clock output, see
 		 * rcar_du_crtc_atomic_enable().
 		 */
-		rcar_lvds_clk_disable(encoder->base.bridge);
+		bridge = drm_bridge_chain_get_first_bridge(&encoder->base);
+		rcar_lvds_clk_disable(bridge);
 	}
 
 	spin_lock_irq(&crtc->dev->event_lock);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 46fc17c63a44..bd78c256b1ed 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -25,6 +25,7 @@
 
 #include <linux/list.h>
 #include <linux/ctype.h>
+#include <drm/drm_encoder.h>
 #include <drm/drm_mode_object.h>
 #include <drm/drm_modes.h>
 
@@ -422,6 +423,20 @@ drm_bridge_get_next_bridge(struct drm_bridge *bridge)
 	return bridge->next;
 }
 
+/**
+ * drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain
+ * @encoder: encoder object
+ *
+ * RETURNS:
+ * the first bridge in the chain, or NULL if @encoder has no bridge attached
+ * to it.
+ */
+static inline struct drm_bridge *
+drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder)
+{
+	return encoder->bridge;
+}
+
 bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
 				 const struct drm_display_mode *mode,
 				 struct drm_display_mode *adjusted_mode);
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 14:15   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon, Laurent Pinchart

So that each element in the chain can easily access its predecessor.
This will be needed to support bus format negotiation between elements
of the bridge chain.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes in v4:
* Simplify the drm_bridge_attach() logic
* Fix list iteration bugs
* Patch VC4 and Exynos DSI drivers to match core changes
* Add R-bs

Changes in v3:
* None

Changes in v2:
* Adjust things to the "dummy encoder bridge" change (patch 2 in this
  series)
---
 drivers/gpu/drm/drm_bridge.c            | 171 +++++++++++++++---------
 drivers/gpu/drm/drm_encoder.c           |  16 +--
 drivers/gpu/drm/exynos/exynos_drm_dsi.c |   5 +-
 drivers/gpu/drm/vc4/vc4_dsi.c           |  10 +-
 include/drm/drm_bridge.h                |  12 +-
 include/drm/drm_encoder.h               |   7 +-
 6 files changed, 143 insertions(+), 78 deletions(-)

diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 54c874493c57..b6517b4fa3d1 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -55,7 +55,7 @@
  * just provide additional hooks to get the desired output at the end of the
  * encoder chain.
  *
- * Bridges can also be chained up using the &drm_bridge.next pointer.
+ * Bridges can also be chained up using the &drm_bridge.chain_node field.
  *
  * Both legacy CRTC helpers and the new atomic modeset helpers support bridges.
  */
@@ -128,20 +128,21 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 	bridge->dev = encoder->dev;
 	bridge->encoder = encoder;
 
+	if (previous)
+		list_add(&bridge->chain_node, &previous->chain_node);
+	else
+		list_add(&bridge->chain_node, &encoder->bridge_chain);
+
 	if (bridge->funcs->attach) {
 		ret = bridge->funcs->attach(bridge);
 		if (ret < 0) {
+			list_del(&bridge->chain_node);
 			bridge->dev = NULL;
 			bridge->encoder = NULL;
 			return ret;
 		}
 	}
 
-	if (previous)
-		previous->next = bridge;
-	else
-		encoder->bridge = bridge;
-
 	return 0;
 }
 EXPORT_SYMBOL(drm_bridge_attach);
@@ -157,6 +158,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
 	if (bridge->funcs->detach)
 		bridge->funcs->detach(bridge);
 
+	list_del(&bridge->chain_node);
 	bridge->dev = NULL;
 }
 
@@ -190,18 +192,21 @@ bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
 				 const struct drm_display_mode *mode,
 				 struct drm_display_mode *adjusted_mode)
 {
-	bool ret = true;
+	struct drm_encoder *encoder;
 
 	if (!bridge)
 		return true;
 
-	if (bridge->funcs->mode_fixup)
-		ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (!bridge->funcs->mode_fixup)
+			continue;
 
-	ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode,
-						 adjusted_mode);
+		if (!bridge->funcs->mode_fixup(bridge, mode, adjusted_mode))
+			return false;
+	}
 
-	return ret;
+	return true;
 }
 EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
 
@@ -224,18 +229,24 @@ enum drm_mode_status
 drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
 			    const struct drm_display_mode *mode)
 {
-	enum drm_mode_status ret = MODE_OK;
+	struct drm_encoder *encoder;
 
 	if (!bridge)
-		return ret;
+		return MODE_OK;
 
-	if (bridge->funcs->mode_valid)
-		ret = bridge->funcs->mode_valid(bridge, mode);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		enum drm_mode_status ret;
+
+		if (!bridge->funcs->mode_valid)
+			continue;
 
-	if (ret != MODE_OK)
-		return ret;
+		ret = bridge->funcs->mode_valid(bridge, mode);
+		if (ret != MODE_OK)
+			return ret;
+	}
 
-	return drm_bridge_chain_mode_valid(bridge->next, mode);
+	return MODE_OK;
 }
 EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
 
@@ -251,13 +262,20 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
  */
 void drm_bridge_chain_disable(struct drm_bridge *bridge)
 {
+	struct drm_encoder *encoder;
+	struct drm_bridge *iter;
+
 	if (!bridge)
 		return;
 
-	drm_bridge_chain_disable(bridge->next);
+	encoder = bridge->encoder;
+	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+		if (iter->funcs->disable)
+			iter->funcs->disable(iter);
 
-	if (bridge->funcs->disable)
-		bridge->funcs->disable(bridge);
+		if (iter == bridge)
+			break;
+	}
 }
 EXPORT_SYMBOL(drm_bridge_chain_disable);
 
@@ -274,13 +292,16 @@ EXPORT_SYMBOL(drm_bridge_chain_disable);
  */
 void drm_bridge_chain_post_disable(struct drm_bridge *bridge)
 {
+	struct drm_encoder *encoder;
+
 	if (!bridge)
 		return;
 
-	if (bridge->funcs->post_disable)
-		bridge->funcs->post_disable(bridge);
-
-	drm_bridge_chain_post_disable(bridge->next);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (bridge->funcs->post_disable)
+			bridge->funcs->post_disable(bridge);
+	}
 }
 EXPORT_SYMBOL(drm_bridge_chain_post_disable);
 
@@ -300,13 +321,16 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
 			       const struct drm_display_mode *mode,
 			       const struct drm_display_mode *adjusted_mode)
 {
+	struct drm_encoder *encoder;
+
 	if (!bridge)
 		return;
 
-	if (bridge->funcs->mode_set)
-		bridge->funcs->mode_set(bridge, mode, adjusted_mode);
-
-	drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (bridge->funcs->mode_set)
+			bridge->funcs->mode_set(bridge, mode, adjusted_mode);
+	}
 }
 EXPORT_SYMBOL(drm_bridge_chain_mode_set);
 
@@ -323,13 +347,17 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set);
  */
 void drm_bridge_chain_pre_enable(struct drm_bridge *bridge)
 {
+	struct drm_encoder *encoder;
+	struct drm_bridge *iter;
+
 	if (!bridge)
 		return;
 
-	drm_bridge_chain_pre_enable(bridge->next);
-
-	if (bridge->funcs->pre_enable)
-		bridge->funcs->pre_enable(bridge);
+	encoder = bridge->encoder;
+	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+		if (iter->funcs->pre_enable)
+			iter->funcs->pre_enable(iter);
+	}
 }
 EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
 
@@ -345,13 +373,16 @@ EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
  */
 void drm_bridge_chain_enable(struct drm_bridge *bridge)
 {
+	struct drm_encoder *encoder;
+
 	if (!bridge)
 		return;
 
-	if (bridge->funcs->enable)
-		bridge->funcs->enable(bridge);
-
-	drm_bridge_chain_enable(bridge->next);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (bridge->funcs->enable)
+			bridge->funcs->enable(bridge);
+	}
 }
 EXPORT_SYMBOL(drm_bridge_chain_enable);
 
@@ -370,15 +401,22 @@ EXPORT_SYMBOL(drm_bridge_chain_enable);
 void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
 				     struct drm_atomic_state *state)
 {
+	struct drm_encoder *encoder;
+	struct drm_bridge *iter;
+
 	if (!bridge)
 		return;
 
-	drm_atomic_bridge_chain_disable(bridge->next, state);
+	encoder = bridge->encoder;
+	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+		if (iter->funcs->atomic_disable)
+			iter->funcs->atomic_disable(iter, state);
+		else if (iter->funcs->disable)
+			iter->funcs->disable(iter);
 
-	if (bridge->funcs->atomic_disable)
-		bridge->funcs->atomic_disable(bridge, state);
-	else if (bridge->funcs->disable)
-		bridge->funcs->disable(bridge);
+		if (iter == bridge)
+			break;
+	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
 
@@ -398,15 +436,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
 void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
 					  struct drm_atomic_state *state)
 {
+	struct drm_encoder *encoder;
+
 	if (!bridge)
 		return;
 
-	if (bridge->funcs->atomic_post_disable)
-		bridge->funcs->atomic_post_disable(bridge, state);
-	else if (bridge->funcs->post_disable)
-		bridge->funcs->post_disable(bridge);
-
-	drm_atomic_bridge_chain_post_disable(bridge->next, state);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (bridge->funcs->atomic_post_disable)
+			bridge->funcs->atomic_post_disable(bridge, state);
+		else if (bridge->funcs->post_disable)
+			bridge->funcs->post_disable(bridge);
+	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
 
@@ -426,15 +467,22 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
 void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
 					struct drm_atomic_state *state)
 {
+	struct drm_encoder *encoder;
+	struct drm_bridge *iter;
+
 	if (!bridge)
 		return;
 
-	drm_atomic_bridge_chain_pre_enable(bridge->next, state);
+	encoder = bridge->encoder;
+	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+		if (iter->funcs->atomic_pre_enable)
+			iter->funcs->atomic_pre_enable(iter, state);
+		else if (iter->funcs->pre_enable)
+			iter->funcs->pre_enable(iter);
 
-	if (bridge->funcs->atomic_pre_enable)
-		bridge->funcs->atomic_pre_enable(bridge, state);
-	else if (bridge->funcs->pre_enable)
-		bridge->funcs->pre_enable(bridge);
+		if (iter == bridge)
+			break;
+	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
 
@@ -453,15 +501,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
 void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 				    struct drm_atomic_state *state)
 {
+	struct drm_encoder *encoder;
+
 	if (!bridge)
 		return;
 
-	if (bridge->funcs->atomic_enable)
-		bridge->funcs->atomic_enable(bridge, state);
-	else if (bridge->funcs->enable)
-		bridge->funcs->enable(bridge);
-
-	drm_atomic_bridge_chain_enable(bridge->next, state);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (bridge->funcs->atomic_enable)
+			bridge->funcs->atomic_enable(bridge, state);
+		else if (bridge->funcs->enable)
+			bridge->funcs->enable(bridge);
+	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
 
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index a2cc7e7241a9..e555281f43d4 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -140,6 +140,7 @@ int drm_encoder_init(struct drm_device *dev,
 		goto out_put;
 	}
 
+	INIT_LIST_HEAD(&encoder->bridge_chain);
 	list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
 	encoder->index = dev->mode_config.num_encoder++;
 
@@ -160,23 +161,16 @@ EXPORT_SYMBOL(drm_encoder_init);
 void drm_encoder_cleanup(struct drm_encoder *encoder)
 {
 	struct drm_device *dev = encoder->dev;
+	struct drm_bridge *bridge, *next;
 
 	/* Note that the encoder_list is considered to be static; should we
 	 * remove the drm_encoder at runtime we would have to decrement all
 	 * the indices on the drm_encoder after us in the encoder_list.
 	 */
 
-	if (encoder->bridge) {
-		struct drm_bridge *bridge;
-		struct drm_bridge *next;
-
-		bridge = drm_bridge_chain_get_first_bridge(encoder);
-		while (bridge) {
-			next = drm_bridge_get_next_bridge(bridge);
-			drm_bridge_detach(bridge);
-			bridge = next;
-		}
-	}
+	list_for_each_entry_safe(bridge, next, &encoder->bridge_chain,
+				 chain_node)
+		drm_bridge_detach(bridge);
 
 	drm_mode_object_unregister(dev, &encoder->base);
 	kfree(encoder->name);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index d984097704b8..7de82e22252a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -255,6 +255,7 @@ struct exynos_dsi {
 	struct mipi_dsi_host dsi_host;
 	struct drm_connector connector;
 	struct drm_panel *panel;
+	struct list_head bridge_chain;
 	struct drm_bridge *out_bridge;
 	struct device *dev;
 
@@ -1522,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
 	if (out_bridge) {
 		drm_bridge_attach(encoder, out_bridge, NULL);
 		dsi->out_bridge = out_bridge;
-		encoder->bridge = NULL;
+		list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
 	} else {
 		int ret = exynos_dsi_create_connector(encoder);
 
@@ -1588,6 +1589,7 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
 		if (dsi->out_bridge->funcs->detach)
 			dsi->out_bridge->funcs->detach(dsi->out_bridge);
 		dsi->out_bridge = NULL;
+		INIT_LIST_HEAD(&dsi->bridge_chain);
 	}
 
 	if (drm->mode_config.poll_enabled)
@@ -1735,6 +1737,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 	init_completion(&dsi->completed);
 	spin_lock_init(&dsi->transfer_lock);
 	INIT_LIST_HEAD(&dsi->transfer_list);
+	INIT_LIST_HEAD(&dsi->bridge_chain);
 
 	dsi->dsi_host.ops = &exynos_dsi_ops;
 	dsi->dsi_host.dev = dev;
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index ff81b54ea281..6c5b80ad6154 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -499,6 +499,7 @@ struct vc4_dsi {
 	struct mipi_dsi_host dsi_host;
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
+	struct list_head bridge_chain;
 
 	void __iomem *regs;
 
@@ -1460,6 +1461,8 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 				       GFP_KERNEL);
 	if (!vc4_dsi_encoder)
 		return -ENOMEM;
+
+	INIT_LIST_HEAD(&dsi->bridge_chain);
 	vc4_dsi_encoder->base.type = VC4_ENCODER_TYPE_DSI1;
 	vc4_dsi_encoder->dsi = dsi;
 	dsi->encoder = &vc4_dsi_encoder->base.base;
@@ -1610,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	 * from our driver, since we need to sequence them within the
 	 * encoder's enable/disable paths.
 	 */
-	dsi->encoder->bridge = NULL;
+	list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
 
 	if (dsi->port == 0)
 		vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
@@ -1632,6 +1635,11 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
 	if (dsi->bridge)
 		pm_runtime_disable(dev);
 
+	/*
+	 * Restore the bridge_chain so the bridge detach procedure can happen
+	 * normally.
+	 */
+	list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
 	vc4_dsi_encoder_destroy(dsi->encoder);
 
 	if (dsi->port == 1)
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index bd78c256b1ed..c118726469ee 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -384,8 +384,8 @@ struct drm_bridge {
 	struct drm_device *dev;
 	/** @encoder: encoder to which this bridge is connected */
 	struct drm_encoder *encoder;
-	/** @next: the next bridge in the encoder chain */
-	struct drm_bridge *next;
+	/** @chain_node: used to form a bridge chain */
+	struct list_head chain_node;
 #ifdef CONFIG_OF
 	/** @of_node: device node pointer to the bridge */
 	struct device_node *of_node;
@@ -420,7 +420,10 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 static inline struct drm_bridge *
 drm_bridge_get_next_bridge(struct drm_bridge *bridge)
 {
-	return bridge->next;
+	if (list_is_last(&bridge->chain_node, &bridge->encoder->bridge_chain))
+		return NULL;
+
+	return list_next_entry(bridge, chain_node);
 }
 
 /**
@@ -434,7 +437,8 @@ drm_bridge_get_next_bridge(struct drm_bridge *bridge)
 static inline struct drm_bridge *
 drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder)
 {
-	return encoder->bridge;
+	return list_first_entry_or_null(&encoder->bridge_chain,
+					struct drm_bridge, chain_node);
 }
 
 bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
index f06164f44efe..5623994b6e9e 100644
--- a/include/drm/drm_encoder.h
+++ b/include/drm/drm_encoder.h
@@ -172,7 +172,12 @@ struct drm_encoder {
 	 * &drm_connector_state.crtc.
 	 */
 	struct drm_crtc *crtc;
-	struct drm_bridge *bridge;
+
+	/**
+	 * @bridge_chain: Bridges attached to this encoder.
+	 */
+	struct list_head bridge_chain;
+
 	const struct drm_encoder_funcs *funcs;
 	const struct drm_encoder_helper_funcs *helper_private;
 };
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-03 14:15   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

So that each element in the chain can easily access its predecessor.
This will be needed to support bus format negotiation between elements
of the bridge chain.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes in v4:
* Simplify the drm_bridge_attach() logic
* Fix list iteration bugs
* Patch VC4 and Exynos DSI drivers to match core changes
* Add R-bs

Changes in v3:
* None

Changes in v2:
* Adjust things to the "dummy encoder bridge" change (patch 2 in this
  series)
---
 drivers/gpu/drm/drm_bridge.c            | 171 +++++++++++++++---------
 drivers/gpu/drm/drm_encoder.c           |  16 +--
 drivers/gpu/drm/exynos/exynos_drm_dsi.c |   5 +-
 drivers/gpu/drm/vc4/vc4_dsi.c           |  10 +-
 include/drm/drm_bridge.h                |  12 +-
 include/drm/drm_encoder.h               |   7 +-
 6 files changed, 143 insertions(+), 78 deletions(-)

diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 54c874493c57..b6517b4fa3d1 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -55,7 +55,7 @@
  * just provide additional hooks to get the desired output at the end of the
  * encoder chain.
  *
- * Bridges can also be chained up using the &drm_bridge.next pointer.
+ * Bridges can also be chained up using the &drm_bridge.chain_node field.
  *
  * Both legacy CRTC helpers and the new atomic modeset helpers support bridges.
  */
@@ -128,20 +128,21 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 	bridge->dev = encoder->dev;
 	bridge->encoder = encoder;
 
+	if (previous)
+		list_add(&bridge->chain_node, &previous->chain_node);
+	else
+		list_add(&bridge->chain_node, &encoder->bridge_chain);
+
 	if (bridge->funcs->attach) {
 		ret = bridge->funcs->attach(bridge);
 		if (ret < 0) {
+			list_del(&bridge->chain_node);
 			bridge->dev = NULL;
 			bridge->encoder = NULL;
 			return ret;
 		}
 	}
 
-	if (previous)
-		previous->next = bridge;
-	else
-		encoder->bridge = bridge;
-
 	return 0;
 }
 EXPORT_SYMBOL(drm_bridge_attach);
@@ -157,6 +158,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
 	if (bridge->funcs->detach)
 		bridge->funcs->detach(bridge);
 
+	list_del(&bridge->chain_node);
 	bridge->dev = NULL;
 }
 
@@ -190,18 +192,21 @@ bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
 				 const struct drm_display_mode *mode,
 				 struct drm_display_mode *adjusted_mode)
 {
-	bool ret = true;
+	struct drm_encoder *encoder;
 
 	if (!bridge)
 		return true;
 
-	if (bridge->funcs->mode_fixup)
-		ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (!bridge->funcs->mode_fixup)
+			continue;
 
-	ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode,
-						 adjusted_mode);
+		if (!bridge->funcs->mode_fixup(bridge, mode, adjusted_mode))
+			return false;
+	}
 
-	return ret;
+	return true;
 }
 EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
 
@@ -224,18 +229,24 @@ enum drm_mode_status
 drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
 			    const struct drm_display_mode *mode)
 {
-	enum drm_mode_status ret = MODE_OK;
+	struct drm_encoder *encoder;
 
 	if (!bridge)
-		return ret;
+		return MODE_OK;
 
-	if (bridge->funcs->mode_valid)
-		ret = bridge->funcs->mode_valid(bridge, mode);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		enum drm_mode_status ret;
+
+		if (!bridge->funcs->mode_valid)
+			continue;
 
-	if (ret != MODE_OK)
-		return ret;
+		ret = bridge->funcs->mode_valid(bridge, mode);
+		if (ret != MODE_OK)
+			return ret;
+	}
 
-	return drm_bridge_chain_mode_valid(bridge->next, mode);
+	return MODE_OK;
 }
 EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
 
@@ -251,13 +262,20 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
  */
 void drm_bridge_chain_disable(struct drm_bridge *bridge)
 {
+	struct drm_encoder *encoder;
+	struct drm_bridge *iter;
+
 	if (!bridge)
 		return;
 
-	drm_bridge_chain_disable(bridge->next);
+	encoder = bridge->encoder;
+	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+		if (iter->funcs->disable)
+			iter->funcs->disable(iter);
 
-	if (bridge->funcs->disable)
-		bridge->funcs->disable(bridge);
+		if (iter == bridge)
+			break;
+	}
 }
 EXPORT_SYMBOL(drm_bridge_chain_disable);
 
@@ -274,13 +292,16 @@ EXPORT_SYMBOL(drm_bridge_chain_disable);
  */
 void drm_bridge_chain_post_disable(struct drm_bridge *bridge)
 {
+	struct drm_encoder *encoder;
+
 	if (!bridge)
 		return;
 
-	if (bridge->funcs->post_disable)
-		bridge->funcs->post_disable(bridge);
-
-	drm_bridge_chain_post_disable(bridge->next);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (bridge->funcs->post_disable)
+			bridge->funcs->post_disable(bridge);
+	}
 }
 EXPORT_SYMBOL(drm_bridge_chain_post_disable);
 
@@ -300,13 +321,16 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
 			       const struct drm_display_mode *mode,
 			       const struct drm_display_mode *adjusted_mode)
 {
+	struct drm_encoder *encoder;
+
 	if (!bridge)
 		return;
 
-	if (bridge->funcs->mode_set)
-		bridge->funcs->mode_set(bridge, mode, adjusted_mode);
-
-	drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (bridge->funcs->mode_set)
+			bridge->funcs->mode_set(bridge, mode, adjusted_mode);
+	}
 }
 EXPORT_SYMBOL(drm_bridge_chain_mode_set);
 
@@ -323,13 +347,17 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set);
  */
 void drm_bridge_chain_pre_enable(struct drm_bridge *bridge)
 {
+	struct drm_encoder *encoder;
+	struct drm_bridge *iter;
+
 	if (!bridge)
 		return;
 
-	drm_bridge_chain_pre_enable(bridge->next);
-
-	if (bridge->funcs->pre_enable)
-		bridge->funcs->pre_enable(bridge);
+	encoder = bridge->encoder;
+	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+		if (iter->funcs->pre_enable)
+			iter->funcs->pre_enable(iter);
+	}
 }
 EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
 
@@ -345,13 +373,16 @@ EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
  */
 void drm_bridge_chain_enable(struct drm_bridge *bridge)
 {
+	struct drm_encoder *encoder;
+
 	if (!bridge)
 		return;
 
-	if (bridge->funcs->enable)
-		bridge->funcs->enable(bridge);
-
-	drm_bridge_chain_enable(bridge->next);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (bridge->funcs->enable)
+			bridge->funcs->enable(bridge);
+	}
 }
 EXPORT_SYMBOL(drm_bridge_chain_enable);
 
@@ -370,15 +401,22 @@ EXPORT_SYMBOL(drm_bridge_chain_enable);
 void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
 				     struct drm_atomic_state *state)
 {
+	struct drm_encoder *encoder;
+	struct drm_bridge *iter;
+
 	if (!bridge)
 		return;
 
-	drm_atomic_bridge_chain_disable(bridge->next, state);
+	encoder = bridge->encoder;
+	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+		if (iter->funcs->atomic_disable)
+			iter->funcs->atomic_disable(iter, state);
+		else if (iter->funcs->disable)
+			iter->funcs->disable(iter);
 
-	if (bridge->funcs->atomic_disable)
-		bridge->funcs->atomic_disable(bridge, state);
-	else if (bridge->funcs->disable)
-		bridge->funcs->disable(bridge);
+		if (iter == bridge)
+			break;
+	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
 
@@ -398,15 +436,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
 void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
 					  struct drm_atomic_state *state)
 {
+	struct drm_encoder *encoder;
+
 	if (!bridge)
 		return;
 
-	if (bridge->funcs->atomic_post_disable)
-		bridge->funcs->atomic_post_disable(bridge, state);
-	else if (bridge->funcs->post_disable)
-		bridge->funcs->post_disable(bridge);
-
-	drm_atomic_bridge_chain_post_disable(bridge->next, state);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (bridge->funcs->atomic_post_disable)
+			bridge->funcs->atomic_post_disable(bridge, state);
+		else if (bridge->funcs->post_disable)
+			bridge->funcs->post_disable(bridge);
+	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
 
@@ -426,15 +467,22 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
 void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
 					struct drm_atomic_state *state)
 {
+	struct drm_encoder *encoder;
+	struct drm_bridge *iter;
+
 	if (!bridge)
 		return;
 
-	drm_atomic_bridge_chain_pre_enable(bridge->next, state);
+	encoder = bridge->encoder;
+	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+		if (iter->funcs->atomic_pre_enable)
+			iter->funcs->atomic_pre_enable(iter, state);
+		else if (iter->funcs->pre_enable)
+			iter->funcs->pre_enable(iter);
 
-	if (bridge->funcs->atomic_pre_enable)
-		bridge->funcs->atomic_pre_enable(bridge, state);
-	else if (bridge->funcs->pre_enable)
-		bridge->funcs->pre_enable(bridge);
+		if (iter == bridge)
+			break;
+	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
 
@@ -453,15 +501,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
 void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 				    struct drm_atomic_state *state)
 {
+	struct drm_encoder *encoder;
+
 	if (!bridge)
 		return;
 
-	if (bridge->funcs->atomic_enable)
-		bridge->funcs->atomic_enable(bridge, state);
-	else if (bridge->funcs->enable)
-		bridge->funcs->enable(bridge);
-
-	drm_atomic_bridge_chain_enable(bridge->next, state);
+	encoder = bridge->encoder;
+	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+		if (bridge->funcs->atomic_enable)
+			bridge->funcs->atomic_enable(bridge, state);
+		else if (bridge->funcs->enable)
+			bridge->funcs->enable(bridge);
+	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
 
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index a2cc7e7241a9..e555281f43d4 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -140,6 +140,7 @@ int drm_encoder_init(struct drm_device *dev,
 		goto out_put;
 	}
 
+	INIT_LIST_HEAD(&encoder->bridge_chain);
 	list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
 	encoder->index = dev->mode_config.num_encoder++;
 
@@ -160,23 +161,16 @@ EXPORT_SYMBOL(drm_encoder_init);
 void drm_encoder_cleanup(struct drm_encoder *encoder)
 {
 	struct drm_device *dev = encoder->dev;
+	struct drm_bridge *bridge, *next;
 
 	/* Note that the encoder_list is considered to be static; should we
 	 * remove the drm_encoder at runtime we would have to decrement all
 	 * the indices on the drm_encoder after us in the encoder_list.
 	 */
 
-	if (encoder->bridge) {
-		struct drm_bridge *bridge;
-		struct drm_bridge *next;
-
-		bridge = drm_bridge_chain_get_first_bridge(encoder);
-		while (bridge) {
-			next = drm_bridge_get_next_bridge(bridge);
-			drm_bridge_detach(bridge);
-			bridge = next;
-		}
-	}
+	list_for_each_entry_safe(bridge, next, &encoder->bridge_chain,
+				 chain_node)
+		drm_bridge_detach(bridge);
 
 	drm_mode_object_unregister(dev, &encoder->base);
 	kfree(encoder->name);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index d984097704b8..7de82e22252a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -255,6 +255,7 @@ struct exynos_dsi {
 	struct mipi_dsi_host dsi_host;
 	struct drm_connector connector;
 	struct drm_panel *panel;
+	struct list_head bridge_chain;
 	struct drm_bridge *out_bridge;
 	struct device *dev;
 
@@ -1522,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
 	if (out_bridge) {
 		drm_bridge_attach(encoder, out_bridge, NULL);
 		dsi->out_bridge = out_bridge;
-		encoder->bridge = NULL;
+		list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
 	} else {
 		int ret = exynos_dsi_create_connector(encoder);
 
@@ -1588,6 +1589,7 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
 		if (dsi->out_bridge->funcs->detach)
 			dsi->out_bridge->funcs->detach(dsi->out_bridge);
 		dsi->out_bridge = NULL;
+		INIT_LIST_HEAD(&dsi->bridge_chain);
 	}
 
 	if (drm->mode_config.poll_enabled)
@@ -1735,6 +1737,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 	init_completion(&dsi->completed);
 	spin_lock_init(&dsi->transfer_lock);
 	INIT_LIST_HEAD(&dsi->transfer_list);
+	INIT_LIST_HEAD(&dsi->bridge_chain);
 
 	dsi->dsi_host.ops = &exynos_dsi_ops;
 	dsi->dsi_host.dev = dev;
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index ff81b54ea281..6c5b80ad6154 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -499,6 +499,7 @@ struct vc4_dsi {
 	struct mipi_dsi_host dsi_host;
 	struct drm_encoder *encoder;
 	struct drm_bridge *bridge;
+	struct list_head bridge_chain;
 
 	void __iomem *regs;
 
@@ -1460,6 +1461,8 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 				       GFP_KERNEL);
 	if (!vc4_dsi_encoder)
 		return -ENOMEM;
+
+	INIT_LIST_HEAD(&dsi->bridge_chain);
 	vc4_dsi_encoder->base.type = VC4_ENCODER_TYPE_DSI1;
 	vc4_dsi_encoder->dsi = dsi;
 	dsi->encoder = &vc4_dsi_encoder->base.base;
@@ -1610,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	 * from our driver, since we need to sequence them within the
 	 * encoder's enable/disable paths.
 	 */
-	dsi->encoder->bridge = NULL;
+	list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
 
 	if (dsi->port == 0)
 		vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
@@ -1632,6 +1635,11 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
 	if (dsi->bridge)
 		pm_runtime_disable(dev);
 
+	/*
+	 * Restore the bridge_chain so the bridge detach procedure can happen
+	 * normally.
+	 */
+	list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
 	vc4_dsi_encoder_destroy(dsi->encoder);
 
 	if (dsi->port == 1)
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index bd78c256b1ed..c118726469ee 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -384,8 +384,8 @@ struct drm_bridge {
 	struct drm_device *dev;
 	/** @encoder: encoder to which this bridge is connected */
 	struct drm_encoder *encoder;
-	/** @next: the next bridge in the encoder chain */
-	struct drm_bridge *next;
+	/** @chain_node: used to form a bridge chain */
+	struct list_head chain_node;
 #ifdef CONFIG_OF
 	/** @of_node: device node pointer to the bridge */
 	struct device_node *of_node;
@@ -420,7 +420,10 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 static inline struct drm_bridge *
 drm_bridge_get_next_bridge(struct drm_bridge *bridge)
 {
-	return bridge->next;
+	if (list_is_last(&bridge->chain_node, &bridge->encoder->bridge_chain))
+		return NULL;
+
+	return list_next_entry(bridge, chain_node);
 }
 
 /**
@@ -434,7 +437,8 @@ drm_bridge_get_next_bridge(struct drm_bridge *bridge)
 static inline struct drm_bridge *
 drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder)
 {
-	return encoder->bridge;
+	return list_first_entry_or_null(&encoder->bridge_chain,
+					struct drm_bridge, chain_node);
 }
 
 bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
index f06164f44efe..5623994b6e9e 100644
--- a/include/drm/drm_encoder.h
+++ b/include/drm/drm_encoder.h
@@ -172,7 +172,12 @@ struct drm_encoder {
 	 * &drm_connector_state.crtc.
 	 */
 	struct drm_crtc *crtc;
-	struct drm_bridge *bridge;
+
+	/**
+	 * @bridge_chain: Bridges attached to this encoder.
+	 */
+	struct list_head bridge_chain;
+
 	const struct drm_encoder_funcs *funcs;
 	const struct drm_encoder_helper_funcs *helper_private;
 };
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 05/11] drm/bridge: Add the drm_for_each_bridge_in_chain() helper
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 14:15   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon, Laurent Pinchart

To iterate over all bridges attached to a specific encoder.

Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
---
Changes in v4:
* Fix the doc header
* Add Rbs

Changes in v3:
* None

Changes in v2:
* New patch
---
 include/drm/drm_bridge.h | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index c118726469ee..1eb854025a20 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -441,6 +441,17 @@ drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder)
 					struct drm_bridge, chain_node);
 }
 
+/**
+ * drm_for_each_bridge_in_chain() - Iterate over all bridges present in a chain
+ * @encoder: the encoder to iterate bridges on
+ * @bridge: a bridge pointer updated to point to the current bridge at each
+ *	    iteration
+ *
+ * Iterate over all bridges present in the bridge chain attached to @encoder.
+ */
+#define drm_for_each_bridge_in_chain(encoder, bridge)			\
+	list_for_each_entry(bridge, &(encoder)->bridge_chain, chain_node)
+
 bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
 				 const struct drm_display_mode *mode,
 				 struct drm_display_mode *adjusted_mode);
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 05/11] drm/bridge: Add the drm_for_each_bridge_in_chain() helper
@ 2019-12-03 14:15   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

To iterate over all bridges attached to a specific encoder.

Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
---
Changes in v4:
* Fix the doc header
* Add Rbs

Changes in v3:
* None

Changes in v2:
* New patch
---
 include/drm/drm_bridge.h | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index c118726469ee..1eb854025a20 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -441,6 +441,17 @@ drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder)
 					struct drm_bridge, chain_node);
 }
 
+/**
+ * drm_for_each_bridge_in_chain() - Iterate over all bridges present in a chain
+ * @encoder: the encoder to iterate bridges on
+ * @bridge: a bridge pointer updated to point to the current bridge at each
+ *	    iteration
+ *
+ * Iterate over all bridges present in the bridge chain attached to @encoder.
+ */
+#define drm_for_each_bridge_in_chain(encoder, bridge)			\
+	list_for_each_entry(bridge, &(encoder)->bridge_chain, chain_node)
+
 bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
 				 const struct drm_display_mode *mode,
 				 struct drm_display_mode *adjusted_mode);
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 06/11] drm/bridge: Add the drm_bridge_get_prev_bridge() helper
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 14:15   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon, Laurent Pinchart

The drm_bridge_get_prev_bridge() helper will be useful for bridge
drivers that want to do bus format negotiation with their neighbours.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
---
Changes in v4:
* Change the helper name to be consistent with the _next_bridge() helper
* Update the commit message
* Add Rbs

Changes in v3:
* Inline drm_bridge_chain_get_prev_bridge()
* Fix the doc

Changes in v2:
* Fix the kerneldoc
* Drop the !bridge || !bridge->encoder check
---
 include/drm/drm_bridge.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 1eb854025a20..bfb0385163f1 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -426,6 +426,22 @@ drm_bridge_get_next_bridge(struct drm_bridge *bridge)
 	return list_next_entry(bridge, chain_node);
 }
 
+/**
+ * drm_bridge_get_prev_bridge() - Get the previous bridge in the chain
+ * @bridge: bridge object
+ *
+ * RETURNS:
+ * the previous bridge in the chain, or NULL if @bridge is the first.
+ */
+static inline struct drm_bridge *
+drm_bridge_get_prev_bridge(struct drm_bridge *bridge)
+{
+	if (list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain))
+		return NULL;
+
+	return list_prev_entry(bridge, chain_node);
+}
+
 /**
  * drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain
  * @encoder: encoder object
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 06/11] drm/bridge: Add the drm_bridge_get_prev_bridge() helper
@ 2019-12-03 14:15   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

The drm_bridge_get_prev_bridge() helper will be useful for bridge
drivers that want to do bus format negotiation with their neighbours.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
---
Changes in v4:
* Change the helper name to be consistent with the _next_bridge() helper
* Update the commit message
* Add Rbs

Changes in v3:
* Inline drm_bridge_chain_get_prev_bridge()
* Fix the doc

Changes in v2:
* Fix the kerneldoc
* Drop the !bridge || !bridge->encoder check
---
 include/drm/drm_bridge.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 1eb854025a20..bfb0385163f1 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -426,6 +426,22 @@ drm_bridge_get_next_bridge(struct drm_bridge *bridge)
 	return list_next_entry(bridge, chain_node);
 }
 
+/**
+ * drm_bridge_get_prev_bridge() - Get the previous bridge in the chain
+ * @bridge: bridge object
+ *
+ * RETURNS:
+ * the previous bridge in the chain, or NULL if @bridge is the first.
+ */
+static inline struct drm_bridge *
+drm_bridge_get_prev_bridge(struct drm_bridge *bridge)
+{
+	if (list_is_first(&bridge->chain_node, &bridge->encoder->bridge_chain))
+		return NULL;
+
+	return list_prev_entry(bridge, chain_node);
+}
+
 /**
  * drm_bridge_chain_get_first_bridge() - Get the first bridge in the chain
  * @encoder: encoder object
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 07/11] drm/bridge: Clarify the atomic enable/disable hooks semantics
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 14:15   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon, Laurent Pinchart

The [pre_]enable/[post_]disable hooks are passed the old atomic state.
Update the doc and rename the arguments to make it clear.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
---
Changes in v4:
* Drop the doc update (Laurent)
* Add Rbs

Changes in v3:
* New patch
---
 include/drm/drm_bridge.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index bfb0385163f1..d7d714023050 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -263,7 +263,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_pre_enable callback is optional.
 	 */
 	void (*atomic_pre_enable)(struct drm_bridge *bridge,
-				  struct drm_atomic_state *state);
+				  struct drm_atomic_state *old_state);
 
 	/**
 	 * @atomic_enable:
@@ -288,7 +288,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_enable callback is optional.
 	 */
 	void (*atomic_enable)(struct drm_bridge *bridge,
-			      struct drm_atomic_state *state);
+			      struct drm_atomic_state *old_state);
 	/**
 	 * @atomic_disable:
 	 *
@@ -311,7 +311,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_disable callback is optional.
 	 */
 	void (*atomic_disable)(struct drm_bridge *bridge,
-			       struct drm_atomic_state *state);
+			       struct drm_atomic_state *old_state);
 
 	/**
 	 * @atomic_post_disable:
@@ -337,7 +337,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_post_disable callback is optional.
 	 */
 	void (*atomic_post_disable)(struct drm_bridge *bridge,
-				    struct drm_atomic_state *state);
+				    struct drm_atomic_state *old_state);
 };
 
 /**
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 07/11] drm/bridge: Clarify the atomic enable/disable hooks semantics
@ 2019-12-03 14:15   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

The [pre_]enable/[post_]disable hooks are passed the old atomic state.
Update the doc and rename the arguments to make it clear.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
---
Changes in v4:
* Drop the doc update (Laurent)
* Add Rbs

Changes in v3:
* New patch
---
 include/drm/drm_bridge.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index bfb0385163f1..d7d714023050 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -263,7 +263,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_pre_enable callback is optional.
 	 */
 	void (*atomic_pre_enable)(struct drm_bridge *bridge,
-				  struct drm_atomic_state *state);
+				  struct drm_atomic_state *old_state);
 
 	/**
 	 * @atomic_enable:
@@ -288,7 +288,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_enable callback is optional.
 	 */
 	void (*atomic_enable)(struct drm_bridge *bridge,
-			      struct drm_atomic_state *state);
+			      struct drm_atomic_state *old_state);
 	/**
 	 * @atomic_disable:
 	 *
@@ -311,7 +311,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_disable callback is optional.
 	 */
 	void (*atomic_disable)(struct drm_bridge *bridge,
-			       struct drm_atomic_state *state);
+			       struct drm_atomic_state *old_state);
 
 	/**
 	 * @atomic_post_disable:
@@ -337,7 +337,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_post_disable callback is optional.
 	 */
 	void (*atomic_post_disable)(struct drm_bridge *bridge,
-				    struct drm_atomic_state *state);
+				    struct drm_atomic_state *old_state);
 };
 
 /**
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 14:15   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon

One of the last remaining objects to not have its atomic state.

This is being motivated by our attempt to support runtime bus-format
negotiation between elements of the bridge chain.
This patch just paves the road for such a feature by adding a new
drm_bridge_state object inheriting from drm_private_obj so we can
re-use some of the existing state initialization/tracking logic.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
---
Changes in v4:
* Fix the doc
* Kill default helpers (inlined)
* Fix drm_atomic_get_bridge_state() to check for an ERR_PTR()
* Add Neil's R-b

Changes in v3:
* None

Changes in v2:
* Use drm_for_each_bridge_in_chain()
* Rename helpers to be more consistent with the rest of the DRM API
* Improve/fix the doc
---
 drivers/gpu/drm/drm_atomic.c        |  39 +++++++++
 drivers/gpu/drm/drm_atomic_helper.c |  20 +++++
 drivers/gpu/drm/drm_bridge.c        | 118 ++++++++++++++++++++++++++--
 include/drm/drm_atomic.h            |   3 +
 include/drm/drm_bridge.h            | 116 +++++++++++++++++++++++++++
 5 files changed, 290 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 14aeaf736321..02f8034f5240 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -30,6 +30,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_bridge.h>
 #include <drm/drm_debugfs.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
@@ -1015,6 +1016,44 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
 		connector->funcs->atomic_print_state(p, state);
 }
 
+/**
+ * drm_atomic_add_encoder_bridges - add bridges attached to an encoder
+ * @state: atomic state
+ * @encoder: DRM encoder
+ *
+ * This function adds all bridges attached to @encoder. This is needed to add
+ * bridge states to @state and make them available when
+ * &bridge_funcs.atomic_{check,pre_enable,enable,disable_post_disable}() are
+ * called
+ *
+ * Returns:
+ * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK
+ * then the w/w mutex code has detected a deadlock and the entire atomic
+ * sequence must be restarted. All other errors are fatal.
+ */
+int
+drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
+			       struct drm_encoder *encoder)
+{
+	struct drm_bridge_state *bridge_state;
+	struct drm_bridge *bridge;
+
+	if (!encoder)
+		return 0;
+
+	DRM_DEBUG_ATOMIC("Adding all bridges for [encoder:%d:%s] to %p\n",
+			 encoder->base.id, encoder->name, state);
+
+	drm_for_each_bridge_in_chain(encoder, bridge) {
+		bridge_state = drm_atomic_get_bridge_state(state, bridge);
+		if (IS_ERR(bridge_state))
+			return PTR_ERR(bridge_state);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_atomic_add_encoder_bridges);
+
 /**
  * drm_atomic_add_affected_connectors - add connectors for crtc
  * @state: atomic state
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 29c3115bf9e2..e2f7f336a2d2 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -730,6 +730,26 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
 			return ret;
 	}
 
+	/*
+	 * Iterate over all connectors again, and add all affected bridges to
+	 * the state.
+	 */
+	for_each_oldnew_connector_in_state(state, connector,
+					   old_connector_state,
+					   new_connector_state, i) {
+		struct drm_encoder *encoder;
+
+		encoder = old_connector_state->best_encoder;
+		ret = drm_atomic_add_encoder_bridges(state, encoder);
+		if (ret)
+			return ret;
+
+		encoder = new_connector_state->best_encoder;
+		ret = drm_atomic_add_encoder_bridges(state, encoder);
+		if (ret)
+			return ret;
+	}
+
 	ret = mode_valid(state);
 	if (ret)
 		return ret;
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index b6517b4fa3d1..bf5a2243a115 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 
+#include <drm/drm_atomic_state_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_encoder.h>
 
@@ -89,6 +90,48 @@ void drm_bridge_remove(struct drm_bridge *bridge)
 }
 EXPORT_SYMBOL(drm_bridge_remove);
 
+static struct drm_private_state *
+drm_bridge_atomic_duplicate_priv_state(struct drm_private_obj *obj)
+{
+	struct drm_bridge *bridge = drm_priv_to_bridge(obj);
+	struct drm_bridge_state *state;
+
+	if (bridge->funcs->atomic_duplicate_state) {
+		state = bridge->funcs->atomic_duplicate_state(bridge);
+
+		return state ? &state->base : NULL;
+	}
+
+	if (WARN_ON(!bridge->base.state))
+		return NULL;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_bridge_duplicate_state(bridge, state);
+
+	return &state->base;
+}
+
+static void
+drm_bridge_atomic_destroy_priv_state(struct drm_private_obj *obj,
+				     struct drm_private_state *s)
+{
+	struct drm_bridge_state *state = drm_priv_to_bridge_state(s);
+	struct drm_bridge *bridge = drm_priv_to_bridge(obj);
+
+	if (bridge->funcs->atomic_destroy_state)
+		bridge->funcs->atomic_destroy_state(bridge, state);
+	else
+		kfree(state);
+}
+
+static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
+	.atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state,
+	.atomic_destroy_state = drm_bridge_atomic_destroy_priv_state,
+};
+
 /**
  * drm_bridge_attach - attach the bridge to an encoder's chain
  *
@@ -114,6 +157,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
 int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 		      struct drm_bridge *previous)
 {
+	struct drm_bridge_state *state;
 	int ret;
 
 	if (!encoder || !bridge)
@@ -135,15 +179,40 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 
 	if (bridge->funcs->attach) {
 		ret = bridge->funcs->attach(bridge);
-		if (ret < 0) {
-			list_del(&bridge->chain_node);
-			bridge->dev = NULL;
-			bridge->encoder = NULL;
-			return ret;
-		}
+		if (ret < 0)
+			goto err_reset_bridge;
 	}
 
+	if (bridge->funcs->atomic_reset) {
+		state = bridge->funcs->atomic_reset(bridge);
+	} else {
+		state = kzalloc(sizeof(*state), GFP_KERNEL);
+		if (state)
+			__drm_atomic_helper_bridge_reset(bridge, state);
+		else
+			state = ERR_PTR(-ENOMEM);
+	}
+
+	if (IS_ERR(state)) {
+		ret = PTR_ERR(state);
+		goto err_detach_bridge;
+	}
+
+	drm_atomic_private_obj_init(bridge->dev, &bridge->base,
+				    &state->base,
+				    &drm_bridge_priv_state_funcs);
+
 	return 0;
+
+err_detach_bridge:
+	if (bridge->funcs->detach)
+		bridge->funcs->detach(bridge);
+
+err_reset_bridge:
+	bridge->dev = NULL;
+	bridge->encoder = NULL;
+	list_del(&bridge->chain_node);
+	return ret;
 }
 EXPORT_SYMBOL(drm_bridge_attach);
 
@@ -155,6 +224,8 @@ void drm_bridge_detach(struct drm_bridge *bridge)
 	if (WARN_ON(!bridge->dev))
 		return;
 
+	drm_atomic_private_obj_fini(&bridge->base);
+
 	if (bridge->funcs->detach)
 		bridge->funcs->detach(bridge);
 
@@ -516,6 +587,41 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
 
+/**
+ * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its
+ *					default
+ * @bridge: the bridge this state is refers to
+ * @state: bridge state to initialize
+ *
+ * Initialize the bridge state to default values. This is meant to be* called
+ * by the bridge &drm_plane_funcs.reset hook for bridges that subclass the
+ * bridge state.
+ */
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
+				      struct drm_bridge_state *state)
+{
+	memset(state, 0, sizeof(*state));
+	state->bridge = bridge;
+}
+EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset);
+
+/**
+ * __drm_atomic_helper_bridge_duplicate_state() - Copy atomic bridge state
+ * @bridge: bridge object
+ * @state: atomic bridge state
+ *
+ * Copies atomic state from a bridge's current state and resets inferred values.
+ * This is useful for drivers that subclass the bridge state.
+ */
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
+						struct drm_bridge_state *state)
+{
+	__drm_atomic_helper_private_obj_duplicate_state(&bridge->base,
+							&state->base);
+	state->bridge = bridge;
+}
+EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_state);
+
 #ifdef CONFIG_OF
 /**
  * of_drm_find_bridge - find the bridge corresponding to the device node in
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index b6c73fd9f55a..e08582773d6c 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -660,6 +660,9 @@ __drm_atomic_get_current_plane_state(struct drm_atomic_state *state,
 	return plane->state;
 }
 
+int __must_check
+drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
+			       struct drm_encoder *encoder);
 int __must_check
 drm_atomic_add_affected_connectors(struct drm_atomic_state *state,
 				   struct drm_crtc *crtc);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index d7d714023050..db5fc18d49bd 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -25,6 +25,8 @@
 
 #include <linux/list.h>
 #include <linux/ctype.h>
+
+#include <drm/drm_atomic.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_mode_object.h>
 #include <drm/drm_modes.h>
@@ -33,6 +35,23 @@ struct drm_bridge;
 struct drm_bridge_timings;
 struct drm_panel;
 
+/**
+ * struct drm_bridge_state - Atomic bridge state object
+ * @base: inherit from &drm_private_state
+ * @bridge: the bridge this state refers to
+ */
+struct drm_bridge_state {
+	struct drm_private_state base;
+
+	struct drm_bridge *bridge;
+};
+
+static inline struct drm_bridge_state *
+drm_priv_to_bridge_state(struct drm_private_state *priv)
+{
+	return container_of(priv, struct drm_bridge_state, base);
+}
+
 /**
  * struct drm_bridge_funcs - drm_bridge control functions
  */
@@ -338,6 +357,49 @@ struct drm_bridge_funcs {
 	 */
 	void (*atomic_post_disable)(struct drm_bridge *bridge,
 				    struct drm_atomic_state *old_state);
+
+	/**
+	 * @atomic_duplicate_state:
+	 *
+	 * Duplicate the current bridge state object (which is guaranteed to be
+	 * non-NULL).
+	 *
+	 * The atomic_duplicate_state() is optional. When not implemented the
+	 * core allocates a drm_bridge_state object and calls
+	 * &__drm_atomic_helper_bridge_duplicate_state() to initialize it.
+	 *
+	 * RETURNS:
+	 * A valid drm_bridge_state object or NULL if the allocation fails.
+	 */
+	struct drm_bridge_state *(*atomic_duplicate_state)(struct drm_bridge *bridge);
+
+	/**
+	 * @atomic_destroy_state:
+	 *
+	 * Destroy a bridge state object previously allocated by
+	 * &drm_bridge_funcs.atomic_duplicate_state().
+	 *
+	 * The atomic_destroy_state hook is optional. When not implemented the
+	 * core calls kfree() on the state.
+	 */
+	void (*atomic_destroy_state)(struct drm_bridge *bridge,
+				     struct drm_bridge_state *state);
+
+	/**
+	 * @atomic_reset:
+	 *
+	 * Reset the bridge to a predefined state (or retrieve its current
+	 * state) and return a &drm_bridge_state object matching this state.
+	 * This function is called at attach time.
+	 *
+	 * The atomic_reset hook is optional. When not implemented the core
+	 * allocates a new state and calls &__drm_atomic_helper_bridge_reset().
+	 *
+	 * RETURNS:
+	 * A valid drm_bridge_state object in case of success, an ERR_PTR()
+	 * giving the reason of the failure otherwise.
+	 */
+	struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
 };
 
 /**
@@ -380,6 +442,8 @@ struct drm_bridge_timings {
  * struct drm_bridge - central DRM bridge control structure
  */
 struct drm_bridge {
+	/** @base: inherit from &drm_private_object */
+	struct drm_private_obj base;
 	/** @dev: DRM device this bridge belongs to */
 	struct drm_device *dev;
 	/** @encoder: encoder to which this bridge is connected */
@@ -404,6 +468,12 @@ struct drm_bridge {
 	void *driver_private;
 };
 
+static inline struct drm_bridge *
+drm_priv_to_bridge(struct drm_private_obj *priv)
+{
+	return container_of(priv, struct drm_bridge, base);
+}
+
 void drm_bridge_add(struct drm_bridge *bridge);
 void drm_bridge_remove(struct drm_bridge *bridge);
 struct drm_bridge *of_drm_find_bridge(struct device_node *np);
@@ -491,6 +561,52 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
 void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 				    struct drm_atomic_state *state);
 
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
+				      struct drm_bridge_state *state);
+struct drm_bridge_state *
+drm_atomic_helper_bridge_reset(struct drm_bridge *bridge);
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
+						struct drm_bridge_state *new);
+
+static inline struct drm_bridge_state *
+drm_atomic_get_bridge_state(struct drm_atomic_state *state,
+			    struct drm_bridge *bridge)
+{
+	struct drm_private_state *obj_state;
+
+	obj_state = drm_atomic_get_private_obj_state(state, &bridge->base);
+	if (IS_ERR(obj_state))
+		return ERR_CAST(obj_state);
+
+	return drm_priv_to_bridge_state(obj_state);
+}
+
+static inline struct drm_bridge_state *
+drm_atomic_get_old_bridge_state(struct drm_atomic_state *state,
+				struct drm_bridge *bridge)
+{
+	struct drm_private_state *obj_state;
+
+	obj_state = drm_atomic_get_old_private_obj_state(state, &bridge->base);
+	if (!obj_state)
+		return NULL;
+
+	return drm_priv_to_bridge_state(obj_state);
+}
+
+static inline struct drm_bridge_state *
+drm_atomic_get_new_bridge_state(struct drm_atomic_state *state,
+				struct drm_bridge *bridge)
+{
+	struct drm_private_state *obj_state;
+
+	obj_state = drm_atomic_get_new_private_obj_state(state, &bridge->base);
+	if (!obj_state)
+		return NULL;
+
+	return drm_priv_to_bridge_state(obj_state);
+}
+
 #ifdef CONFIG_DRM_PANEL_BRIDGE
 struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel);
 struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
@ 2019-12-03 14:15   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

One of the last remaining objects to not have its atomic state.

This is being motivated by our attempt to support runtime bus-format
negotiation between elements of the bridge chain.
This patch just paves the road for such a feature by adding a new
drm_bridge_state object inheriting from drm_private_obj so we can
re-use some of the existing state initialization/tracking logic.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
---
Changes in v4:
* Fix the doc
* Kill default helpers (inlined)
* Fix drm_atomic_get_bridge_state() to check for an ERR_PTR()
* Add Neil's R-b

Changes in v3:
* None

Changes in v2:
* Use drm_for_each_bridge_in_chain()
* Rename helpers to be more consistent with the rest of the DRM API
* Improve/fix the doc
---
 drivers/gpu/drm/drm_atomic.c        |  39 +++++++++
 drivers/gpu/drm/drm_atomic_helper.c |  20 +++++
 drivers/gpu/drm/drm_bridge.c        | 118 ++++++++++++++++++++++++++--
 include/drm/drm_atomic.h            |   3 +
 include/drm/drm_bridge.h            | 116 +++++++++++++++++++++++++++
 5 files changed, 290 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 14aeaf736321..02f8034f5240 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -30,6 +30,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_bridge.h>
 #include <drm/drm_debugfs.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
@@ -1015,6 +1016,44 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
 		connector->funcs->atomic_print_state(p, state);
 }
 
+/**
+ * drm_atomic_add_encoder_bridges - add bridges attached to an encoder
+ * @state: atomic state
+ * @encoder: DRM encoder
+ *
+ * This function adds all bridges attached to @encoder. This is needed to add
+ * bridge states to @state and make them available when
+ * &bridge_funcs.atomic_{check,pre_enable,enable,disable_post_disable}() are
+ * called
+ *
+ * Returns:
+ * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK
+ * then the w/w mutex code has detected a deadlock and the entire atomic
+ * sequence must be restarted. All other errors are fatal.
+ */
+int
+drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
+			       struct drm_encoder *encoder)
+{
+	struct drm_bridge_state *bridge_state;
+	struct drm_bridge *bridge;
+
+	if (!encoder)
+		return 0;
+
+	DRM_DEBUG_ATOMIC("Adding all bridges for [encoder:%d:%s] to %p\n",
+			 encoder->base.id, encoder->name, state);
+
+	drm_for_each_bridge_in_chain(encoder, bridge) {
+		bridge_state = drm_atomic_get_bridge_state(state, bridge);
+		if (IS_ERR(bridge_state))
+			return PTR_ERR(bridge_state);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_atomic_add_encoder_bridges);
+
 /**
  * drm_atomic_add_affected_connectors - add connectors for crtc
  * @state: atomic state
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 29c3115bf9e2..e2f7f336a2d2 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -730,6 +730,26 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
 			return ret;
 	}
 
+	/*
+	 * Iterate over all connectors again, and add all affected bridges to
+	 * the state.
+	 */
+	for_each_oldnew_connector_in_state(state, connector,
+					   old_connector_state,
+					   new_connector_state, i) {
+		struct drm_encoder *encoder;
+
+		encoder = old_connector_state->best_encoder;
+		ret = drm_atomic_add_encoder_bridges(state, encoder);
+		if (ret)
+			return ret;
+
+		encoder = new_connector_state->best_encoder;
+		ret = drm_atomic_add_encoder_bridges(state, encoder);
+		if (ret)
+			return ret;
+	}
+
 	ret = mode_valid(state);
 	if (ret)
 		return ret;
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index b6517b4fa3d1..bf5a2243a115 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -25,6 +25,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 
+#include <drm/drm_atomic_state_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_encoder.h>
 
@@ -89,6 +90,48 @@ void drm_bridge_remove(struct drm_bridge *bridge)
 }
 EXPORT_SYMBOL(drm_bridge_remove);
 
+static struct drm_private_state *
+drm_bridge_atomic_duplicate_priv_state(struct drm_private_obj *obj)
+{
+	struct drm_bridge *bridge = drm_priv_to_bridge(obj);
+	struct drm_bridge_state *state;
+
+	if (bridge->funcs->atomic_duplicate_state) {
+		state = bridge->funcs->atomic_duplicate_state(bridge);
+
+		return state ? &state->base : NULL;
+	}
+
+	if (WARN_ON(!bridge->base.state))
+		return NULL;
+
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return NULL;
+
+	__drm_atomic_helper_bridge_duplicate_state(bridge, state);
+
+	return &state->base;
+}
+
+static void
+drm_bridge_atomic_destroy_priv_state(struct drm_private_obj *obj,
+				     struct drm_private_state *s)
+{
+	struct drm_bridge_state *state = drm_priv_to_bridge_state(s);
+	struct drm_bridge *bridge = drm_priv_to_bridge(obj);
+
+	if (bridge->funcs->atomic_destroy_state)
+		bridge->funcs->atomic_destroy_state(bridge, state);
+	else
+		kfree(state);
+}
+
+static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
+	.atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state,
+	.atomic_destroy_state = drm_bridge_atomic_destroy_priv_state,
+};
+
 /**
  * drm_bridge_attach - attach the bridge to an encoder's chain
  *
@@ -114,6 +157,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
 int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 		      struct drm_bridge *previous)
 {
+	struct drm_bridge_state *state;
 	int ret;
 
 	if (!encoder || !bridge)
@@ -135,15 +179,40 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 
 	if (bridge->funcs->attach) {
 		ret = bridge->funcs->attach(bridge);
-		if (ret < 0) {
-			list_del(&bridge->chain_node);
-			bridge->dev = NULL;
-			bridge->encoder = NULL;
-			return ret;
-		}
+		if (ret < 0)
+			goto err_reset_bridge;
 	}
 
+	if (bridge->funcs->atomic_reset) {
+		state = bridge->funcs->atomic_reset(bridge);
+	} else {
+		state = kzalloc(sizeof(*state), GFP_KERNEL);
+		if (state)
+			__drm_atomic_helper_bridge_reset(bridge, state);
+		else
+			state = ERR_PTR(-ENOMEM);
+	}
+
+	if (IS_ERR(state)) {
+		ret = PTR_ERR(state);
+		goto err_detach_bridge;
+	}
+
+	drm_atomic_private_obj_init(bridge->dev, &bridge->base,
+				    &state->base,
+				    &drm_bridge_priv_state_funcs);
+
 	return 0;
+
+err_detach_bridge:
+	if (bridge->funcs->detach)
+		bridge->funcs->detach(bridge);
+
+err_reset_bridge:
+	bridge->dev = NULL;
+	bridge->encoder = NULL;
+	list_del(&bridge->chain_node);
+	return ret;
 }
 EXPORT_SYMBOL(drm_bridge_attach);
 
@@ -155,6 +224,8 @@ void drm_bridge_detach(struct drm_bridge *bridge)
 	if (WARN_ON(!bridge->dev))
 		return;
 
+	drm_atomic_private_obj_fini(&bridge->base);
+
 	if (bridge->funcs->detach)
 		bridge->funcs->detach(bridge);
 
@@ -516,6 +587,41 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
 
+/**
+ * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its
+ *					default
+ * @bridge: the bridge this state is refers to
+ * @state: bridge state to initialize
+ *
+ * Initialize the bridge state to default values. This is meant to be* called
+ * by the bridge &drm_plane_funcs.reset hook for bridges that subclass the
+ * bridge state.
+ */
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
+				      struct drm_bridge_state *state)
+{
+	memset(state, 0, sizeof(*state));
+	state->bridge = bridge;
+}
+EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset);
+
+/**
+ * __drm_atomic_helper_bridge_duplicate_state() - Copy atomic bridge state
+ * @bridge: bridge object
+ * @state: atomic bridge state
+ *
+ * Copies atomic state from a bridge's current state and resets inferred values.
+ * This is useful for drivers that subclass the bridge state.
+ */
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
+						struct drm_bridge_state *state)
+{
+	__drm_atomic_helper_private_obj_duplicate_state(&bridge->base,
+							&state->base);
+	state->bridge = bridge;
+}
+EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_state);
+
 #ifdef CONFIG_OF
 /**
  * of_drm_find_bridge - find the bridge corresponding to the device node in
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index b6c73fd9f55a..e08582773d6c 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -660,6 +660,9 @@ __drm_atomic_get_current_plane_state(struct drm_atomic_state *state,
 	return plane->state;
 }
 
+int __must_check
+drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
+			       struct drm_encoder *encoder);
 int __must_check
 drm_atomic_add_affected_connectors(struct drm_atomic_state *state,
 				   struct drm_crtc *crtc);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index d7d714023050..db5fc18d49bd 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -25,6 +25,8 @@
 
 #include <linux/list.h>
 #include <linux/ctype.h>
+
+#include <drm/drm_atomic.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_mode_object.h>
 #include <drm/drm_modes.h>
@@ -33,6 +35,23 @@ struct drm_bridge;
 struct drm_bridge_timings;
 struct drm_panel;
 
+/**
+ * struct drm_bridge_state - Atomic bridge state object
+ * @base: inherit from &drm_private_state
+ * @bridge: the bridge this state refers to
+ */
+struct drm_bridge_state {
+	struct drm_private_state base;
+
+	struct drm_bridge *bridge;
+};
+
+static inline struct drm_bridge_state *
+drm_priv_to_bridge_state(struct drm_private_state *priv)
+{
+	return container_of(priv, struct drm_bridge_state, base);
+}
+
 /**
  * struct drm_bridge_funcs - drm_bridge control functions
  */
@@ -338,6 +357,49 @@ struct drm_bridge_funcs {
 	 */
 	void (*atomic_post_disable)(struct drm_bridge *bridge,
 				    struct drm_atomic_state *old_state);
+
+	/**
+	 * @atomic_duplicate_state:
+	 *
+	 * Duplicate the current bridge state object (which is guaranteed to be
+	 * non-NULL).
+	 *
+	 * The atomic_duplicate_state() is optional. When not implemented the
+	 * core allocates a drm_bridge_state object and calls
+	 * &__drm_atomic_helper_bridge_duplicate_state() to initialize it.
+	 *
+	 * RETURNS:
+	 * A valid drm_bridge_state object or NULL if the allocation fails.
+	 */
+	struct drm_bridge_state *(*atomic_duplicate_state)(struct drm_bridge *bridge);
+
+	/**
+	 * @atomic_destroy_state:
+	 *
+	 * Destroy a bridge state object previously allocated by
+	 * &drm_bridge_funcs.atomic_duplicate_state().
+	 *
+	 * The atomic_destroy_state hook is optional. When not implemented the
+	 * core calls kfree() on the state.
+	 */
+	void (*atomic_destroy_state)(struct drm_bridge *bridge,
+				     struct drm_bridge_state *state);
+
+	/**
+	 * @atomic_reset:
+	 *
+	 * Reset the bridge to a predefined state (or retrieve its current
+	 * state) and return a &drm_bridge_state object matching this state.
+	 * This function is called at attach time.
+	 *
+	 * The atomic_reset hook is optional. When not implemented the core
+	 * allocates a new state and calls &__drm_atomic_helper_bridge_reset().
+	 *
+	 * RETURNS:
+	 * A valid drm_bridge_state object in case of success, an ERR_PTR()
+	 * giving the reason of the failure otherwise.
+	 */
+	struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
 };
 
 /**
@@ -380,6 +442,8 @@ struct drm_bridge_timings {
  * struct drm_bridge - central DRM bridge control structure
  */
 struct drm_bridge {
+	/** @base: inherit from &drm_private_object */
+	struct drm_private_obj base;
 	/** @dev: DRM device this bridge belongs to */
 	struct drm_device *dev;
 	/** @encoder: encoder to which this bridge is connected */
@@ -404,6 +468,12 @@ struct drm_bridge {
 	void *driver_private;
 };
 
+static inline struct drm_bridge *
+drm_priv_to_bridge(struct drm_private_obj *priv)
+{
+	return container_of(priv, struct drm_bridge, base);
+}
+
 void drm_bridge_add(struct drm_bridge *bridge);
 void drm_bridge_remove(struct drm_bridge *bridge);
 struct drm_bridge *of_drm_find_bridge(struct device_node *np);
@@ -491,6 +561,52 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
 void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 				    struct drm_atomic_state *state);
 
+void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
+				      struct drm_bridge_state *state);
+struct drm_bridge_state *
+drm_atomic_helper_bridge_reset(struct drm_bridge *bridge);
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
+						struct drm_bridge_state *new);
+
+static inline struct drm_bridge_state *
+drm_atomic_get_bridge_state(struct drm_atomic_state *state,
+			    struct drm_bridge *bridge)
+{
+	struct drm_private_state *obj_state;
+
+	obj_state = drm_atomic_get_private_obj_state(state, &bridge->base);
+	if (IS_ERR(obj_state))
+		return ERR_CAST(obj_state);
+
+	return drm_priv_to_bridge_state(obj_state);
+}
+
+static inline struct drm_bridge_state *
+drm_atomic_get_old_bridge_state(struct drm_atomic_state *state,
+				struct drm_bridge *bridge)
+{
+	struct drm_private_state *obj_state;
+
+	obj_state = drm_atomic_get_old_private_obj_state(state, &bridge->base);
+	if (!obj_state)
+		return NULL;
+
+	return drm_priv_to_bridge_state(obj_state);
+}
+
+static inline struct drm_bridge_state *
+drm_atomic_get_new_bridge_state(struct drm_atomic_state *state,
+				struct drm_bridge *bridge)
+{
+	struct drm_private_state *obj_state;
+
+	obj_state = drm_atomic_get_new_private_obj_state(state, &bridge->base);
+	if (!obj_state)
+		return NULL;
+
+	return drm_priv_to_bridge_state(obj_state);
+}
+
 #ifdef CONFIG_DRM_PANEL_BRIDGE
 struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel);
 struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 09/11] drm/bridge: Patch atomic hooks to take a drm_bridge_state
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 14:15   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon, Laurent Pinchart

This way the drm_bridge_funcs interface is consistent with the rest of
the subsystem.

The only driver implementing those hooks (analogix DP) is patched too.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes in v4:
* Rename func params into old_bridge_state
* Add Laurent's Rb

Changes in v3:
* Old state clarification moved to a separate patch

Changes in v2:
* Pass the old bridge state
---
 .../drm/bridge/analogix/analogix_dp_core.c    | 41 ++++++----
 drivers/gpu/drm/drm_bridge.c                  | 77 ++++++++++++++-----
 include/drm/drm_bridge.h                      |  8 +-
 3 files changed, 85 insertions(+), 41 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index bb411fe52ae8..c8b77b6a8fb0 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -1289,19 +1289,21 @@ struct drm_crtc *analogix_dp_get_new_crtc(struct analogix_dp_device *dp,
 	return conn_state->crtc;
 }
 
-static void analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
-						 struct drm_atomic_state *state)
+static void
+analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
+				     struct drm_bridge_state *old_bridge_state)
 {
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
 	int ret;
 
-	crtc = analogix_dp_get_new_crtc(dp, state);
+	crtc = analogix_dp_get_new_crtc(dp, old_state);
 	if (!crtc)
 		return;
 
-	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
 	/* Don't touch the panel if we're coming back from PSR */
 	if (old_crtc_state && old_crtc_state->self_refresh_active)
 		return;
@@ -1366,20 +1368,22 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
 	return ret;
 }
 
-static void analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
-					     struct drm_atomic_state *state)
+static void
+analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
+				 struct drm_bridge_state *old_bridge_state)
 {
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
 	int timeout_loop = 0;
 	int ret;
 
-	crtc = analogix_dp_get_new_crtc(dp, state);
+	crtc = analogix_dp_get_new_crtc(dp, old_state);
 	if (!crtc)
 		return;
 
-	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
 	/* Not a full enable, just disable PSR and continue */
 	if (old_crtc_state && old_crtc_state->self_refresh_active) {
 		ret = analogix_dp_disable_psr(dp);
@@ -1440,18 +1444,20 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
 }
 
-static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
-					      struct drm_atomic_state *state)
+static void
+analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
+				  struct drm_bridge_state *old_bridge_state)
 {
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state = NULL;
 
-	crtc = analogix_dp_get_new_crtc(dp, state);
+	crtc = analogix_dp_get_new_crtc(dp, old_state);
 	if (!crtc)
 		goto out;
 
-	new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
 	if (!new_crtc_state)
 		goto out;
 
@@ -1463,20 +1469,21 @@ static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
 	analogix_dp_bridge_disable(bridge);
 }
 
-static
-void analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge,
-					    struct drm_atomic_state *state)
+static void
+analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge,
+				struct drm_bridge_state *old_bridge_state)
 {
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state;
 	int ret;
 
-	crtc = analogix_dp_get_new_crtc(dp, state);
+	crtc = analogix_dp_get_new_crtc(dp, old_state);
 	if (!crtc)
 		return;
 
-	new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
 	if (!new_crtc_state || !new_crtc_state->self_refresh_active)
 		return;
 
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index bf5a2243a115..15b21ff6f1fe 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -460,7 +460,7 @@ EXPORT_SYMBOL(drm_bridge_chain_enable);
 /**
  * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain
  * @bridge: bridge control structure
- * @state: atomic state being committed
+ * @old_state: old atomic state
  *
  * Calls &drm_bridge_funcs.atomic_disable (falls back on
  * &drm_bridge_funcs.disable) op for all the bridges in the encoder chain,
@@ -470,7 +470,7 @@ EXPORT_SYMBOL(drm_bridge_chain_enable);
  * Note: the bridge passed should be the one closest to the encoder
  */
 void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
-				     struct drm_atomic_state *state)
+				     struct drm_atomic_state *old_state)
 {
 	struct drm_encoder *encoder;
 	struct drm_bridge *iter;
@@ -480,10 +480,19 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
 
 	encoder = bridge->encoder;
 	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
-		if (iter->funcs->atomic_disable)
-			iter->funcs->atomic_disable(iter, state);
-		else if (iter->funcs->disable)
+		if (iter->funcs->atomic_disable) {
+			struct drm_bridge_state *old_bridge_state;
+
+			old_bridge_state =
+				drm_atomic_get_old_bridge_state(old_state,
+								iter);
+			if (WARN_ON(!old_bridge_state))
+				return;
+
+			iter->funcs->atomic_disable(iter, old_bridge_state);
+		} else if (iter->funcs->disable) {
 			iter->funcs->disable(iter);
+		}
 
 		if (iter == bridge)
 			break;
@@ -495,7 +504,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
  * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges
  *					  in the encoder chain
  * @bridge: bridge control structure
- * @state: atomic state being committed
+ * @old_state: old atomic state
  *
  * Calls &drm_bridge_funcs.atomic_post_disable (falls back on
  * &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain,
@@ -505,7 +514,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
  * Note: the bridge passed should be the one closest to the encoder
  */
 void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
-					  struct drm_atomic_state *state)
+					  struct drm_atomic_state *old_state)
 {
 	struct drm_encoder *encoder;
 
@@ -514,10 +523,20 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
 
 	encoder = bridge->encoder;
 	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
-		if (bridge->funcs->atomic_post_disable)
-			bridge->funcs->atomic_post_disable(bridge, state);
-		else if (bridge->funcs->post_disable)
+		if (bridge->funcs->atomic_post_disable) {
+			struct drm_bridge_state *old_bridge_state;
+
+			old_bridge_state =
+				drm_atomic_get_old_bridge_state(old_state,
+								bridge);
+			if (WARN_ON(!old_bridge_state))
+				return;
+
+			bridge->funcs->atomic_post_disable(bridge,
+							   old_bridge_state);
+		} else if (bridge->funcs->post_disable) {
 			bridge->funcs->post_disable(bridge);
+		}
 	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
@@ -526,7 +545,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
  * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
  *					the encoder chain
  * @bridge: bridge control structure
- * @state: atomic state being committed
+ * @old_state: old atomic state
  *
  * Calls &drm_bridge_funcs.atomic_pre_enable (falls back on
  * &drm_bridge_funcs.pre_enable) op for all the bridges in the encoder chain,
@@ -536,7 +555,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
  * Note: the bridge passed should be the one closest to the encoder
  */
 void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
-					struct drm_atomic_state *state)
+					struct drm_atomic_state *old_state)
 {
 	struct drm_encoder *encoder;
 	struct drm_bridge *iter;
@@ -546,10 +565,19 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
 
 	encoder = bridge->encoder;
 	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
-		if (iter->funcs->atomic_pre_enable)
-			iter->funcs->atomic_pre_enable(iter, state);
-		else if (iter->funcs->pre_enable)
+		if (iter->funcs->atomic_pre_enable) {
+			struct drm_bridge_state *old_bridge_state;
+
+			old_bridge_state =
+				drm_atomic_get_old_bridge_state(old_state,
+								iter);
+			if (WARN_ON(!old_bridge_state))
+				return;
+
+			iter->funcs->atomic_pre_enable(iter, old_bridge_state);
+		} else if (iter->funcs->pre_enable) {
 			iter->funcs->pre_enable(iter);
+		}
 
 		if (iter == bridge)
 			break;
@@ -560,7 +588,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
 /**
  * drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain
  * @bridge: bridge control structure
- * @state: atomic state being committed
+ * @old_state: old atomic state
  *
  * Calls &drm_bridge_funcs.atomic_enable (falls back on
  * &drm_bridge_funcs.enable) op for all the bridges in the encoder chain,
@@ -570,7 +598,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
  * Note: the bridge passed should be the one closest to the encoder
  */
 void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
-				    struct drm_atomic_state *state)
+				    struct drm_atomic_state *old_state)
 {
 	struct drm_encoder *encoder;
 
@@ -579,10 +607,19 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 
 	encoder = bridge->encoder;
 	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
-		if (bridge->funcs->atomic_enable)
-			bridge->funcs->atomic_enable(bridge, state);
-		else if (bridge->funcs->enable)
+		if (bridge->funcs->atomic_enable) {
+			struct drm_bridge_state *old_bridge_state;
+
+			old_bridge_state =
+				drm_atomic_get_old_bridge_state(old_state,
+								bridge);
+			if (WARN_ON(!old_bridge_state))
+				return;
+
+			bridge->funcs->atomic_enable(bridge, old_bridge_state);
+		} else if (bridge->funcs->enable) {
 			bridge->funcs->enable(bridge);
+		}
 	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index db5fc18d49bd..b80c3cac3203 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -282,7 +282,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_pre_enable callback is optional.
 	 */
 	void (*atomic_pre_enable)(struct drm_bridge *bridge,
-				  struct drm_atomic_state *old_state);
+				  struct drm_bridge_state *old_bridge_state);
 
 	/**
 	 * @atomic_enable:
@@ -307,7 +307,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_enable callback is optional.
 	 */
 	void (*atomic_enable)(struct drm_bridge *bridge,
-			      struct drm_atomic_state *old_state);
+			      struct drm_bridge_state *old_bridge_state);
 	/**
 	 * @atomic_disable:
 	 *
@@ -330,7 +330,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_disable callback is optional.
 	 */
 	void (*atomic_disable)(struct drm_bridge *bridge,
-			       struct drm_atomic_state *old_state);
+			       struct drm_bridge_state *old_bridge_state);
 
 	/**
 	 * @atomic_post_disable:
@@ -356,7 +356,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_post_disable callback is optional.
 	 */
 	void (*atomic_post_disable)(struct drm_bridge *bridge,
-				    struct drm_atomic_state *old_state);
+				    struct drm_bridge_state *old_bridge_state);
 
 	/**
 	 * @atomic_duplicate_state:
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 09/11] drm/bridge: Patch atomic hooks to take a drm_bridge_state
@ 2019-12-03 14:15   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

This way the drm_bridge_funcs interface is consistent with the rest of
the subsystem.

The only driver implementing those hooks (analogix DP) is patched too.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes in v4:
* Rename func params into old_bridge_state
* Add Laurent's Rb

Changes in v3:
* Old state clarification moved to a separate patch

Changes in v2:
* Pass the old bridge state
---
 .../drm/bridge/analogix/analogix_dp_core.c    | 41 ++++++----
 drivers/gpu/drm/drm_bridge.c                  | 77 ++++++++++++++-----
 include/drm/drm_bridge.h                      |  8 +-
 3 files changed, 85 insertions(+), 41 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index bb411fe52ae8..c8b77b6a8fb0 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -1289,19 +1289,21 @@ struct drm_crtc *analogix_dp_get_new_crtc(struct analogix_dp_device *dp,
 	return conn_state->crtc;
 }
 
-static void analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
-						 struct drm_atomic_state *state)
+static void
+analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
+				     struct drm_bridge_state *old_bridge_state)
 {
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
 	int ret;
 
-	crtc = analogix_dp_get_new_crtc(dp, state);
+	crtc = analogix_dp_get_new_crtc(dp, old_state);
 	if (!crtc)
 		return;
 
-	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
 	/* Don't touch the panel if we're coming back from PSR */
 	if (old_crtc_state && old_crtc_state->self_refresh_active)
 		return;
@@ -1366,20 +1368,22 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
 	return ret;
 }
 
-static void analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
-					     struct drm_atomic_state *state)
+static void
+analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
+				 struct drm_bridge_state *old_bridge_state)
 {
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
 	int timeout_loop = 0;
 	int ret;
 
-	crtc = analogix_dp_get_new_crtc(dp, state);
+	crtc = analogix_dp_get_new_crtc(dp, old_state);
 	if (!crtc)
 		return;
 
-	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
+	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
 	/* Not a full enable, just disable PSR and continue */
 	if (old_crtc_state && old_crtc_state->self_refresh_active) {
 		ret = analogix_dp_disable_psr(dp);
@@ -1440,18 +1444,20 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
 }
 
-static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
-					      struct drm_atomic_state *state)
+static void
+analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
+				  struct drm_bridge_state *old_bridge_state)
 {
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state = NULL;
 
-	crtc = analogix_dp_get_new_crtc(dp, state);
+	crtc = analogix_dp_get_new_crtc(dp, old_state);
 	if (!crtc)
 		goto out;
 
-	new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
 	if (!new_crtc_state)
 		goto out;
 
@@ -1463,20 +1469,21 @@ static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
 	analogix_dp_bridge_disable(bridge);
 }
 
-static
-void analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge,
-					    struct drm_atomic_state *state)
+static void
+analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge,
+				struct drm_bridge_state *old_bridge_state)
 {
+	struct drm_atomic_state *old_state = old_bridge_state->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state;
 	int ret;
 
-	crtc = analogix_dp_get_new_crtc(dp, state);
+	crtc = analogix_dp_get_new_crtc(dp, old_state);
 	if (!crtc)
 		return;
 
-	new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
 	if (!new_crtc_state || !new_crtc_state->self_refresh_active)
 		return;
 
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index bf5a2243a115..15b21ff6f1fe 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -460,7 +460,7 @@ EXPORT_SYMBOL(drm_bridge_chain_enable);
 /**
  * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain
  * @bridge: bridge control structure
- * @state: atomic state being committed
+ * @old_state: old atomic state
  *
  * Calls &drm_bridge_funcs.atomic_disable (falls back on
  * &drm_bridge_funcs.disable) op for all the bridges in the encoder chain,
@@ -470,7 +470,7 @@ EXPORT_SYMBOL(drm_bridge_chain_enable);
  * Note: the bridge passed should be the one closest to the encoder
  */
 void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
-				     struct drm_atomic_state *state)
+				     struct drm_atomic_state *old_state)
 {
 	struct drm_encoder *encoder;
 	struct drm_bridge *iter;
@@ -480,10 +480,19 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
 
 	encoder = bridge->encoder;
 	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
-		if (iter->funcs->atomic_disable)
-			iter->funcs->atomic_disable(iter, state);
-		else if (iter->funcs->disable)
+		if (iter->funcs->atomic_disable) {
+			struct drm_bridge_state *old_bridge_state;
+
+			old_bridge_state =
+				drm_atomic_get_old_bridge_state(old_state,
+								iter);
+			if (WARN_ON(!old_bridge_state))
+				return;
+
+			iter->funcs->atomic_disable(iter, old_bridge_state);
+		} else if (iter->funcs->disable) {
 			iter->funcs->disable(iter);
+		}
 
 		if (iter == bridge)
 			break;
@@ -495,7 +504,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
  * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges
  *					  in the encoder chain
  * @bridge: bridge control structure
- * @state: atomic state being committed
+ * @old_state: old atomic state
  *
  * Calls &drm_bridge_funcs.atomic_post_disable (falls back on
  * &drm_bridge_funcs.post_disable) op for all the bridges in the encoder chain,
@@ -505,7 +514,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
  * Note: the bridge passed should be the one closest to the encoder
  */
 void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
-					  struct drm_atomic_state *state)
+					  struct drm_atomic_state *old_state)
 {
 	struct drm_encoder *encoder;
 
@@ -514,10 +523,20 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
 
 	encoder = bridge->encoder;
 	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
-		if (bridge->funcs->atomic_post_disable)
-			bridge->funcs->atomic_post_disable(bridge, state);
-		else if (bridge->funcs->post_disable)
+		if (bridge->funcs->atomic_post_disable) {
+			struct drm_bridge_state *old_bridge_state;
+
+			old_bridge_state =
+				drm_atomic_get_old_bridge_state(old_state,
+								bridge);
+			if (WARN_ON(!old_bridge_state))
+				return;
+
+			bridge->funcs->atomic_post_disable(bridge,
+							   old_bridge_state);
+		} else if (bridge->funcs->post_disable) {
 			bridge->funcs->post_disable(bridge);
+		}
 	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
@@ -526,7 +545,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
  * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
  *					the encoder chain
  * @bridge: bridge control structure
- * @state: atomic state being committed
+ * @old_state: old atomic state
  *
  * Calls &drm_bridge_funcs.atomic_pre_enable (falls back on
  * &drm_bridge_funcs.pre_enable) op for all the bridges in the encoder chain,
@@ -536,7 +555,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
  * Note: the bridge passed should be the one closest to the encoder
  */
 void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
-					struct drm_atomic_state *state)
+					struct drm_atomic_state *old_state)
 {
 	struct drm_encoder *encoder;
 	struct drm_bridge *iter;
@@ -546,10 +565,19 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
 
 	encoder = bridge->encoder;
 	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
-		if (iter->funcs->atomic_pre_enable)
-			iter->funcs->atomic_pre_enable(iter, state);
-		else if (iter->funcs->pre_enable)
+		if (iter->funcs->atomic_pre_enable) {
+			struct drm_bridge_state *old_bridge_state;
+
+			old_bridge_state =
+				drm_atomic_get_old_bridge_state(old_state,
+								iter);
+			if (WARN_ON(!old_bridge_state))
+				return;
+
+			iter->funcs->atomic_pre_enable(iter, old_bridge_state);
+		} else if (iter->funcs->pre_enable) {
 			iter->funcs->pre_enable(iter);
+		}
 
 		if (iter == bridge)
 			break;
@@ -560,7 +588,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
 /**
  * drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain
  * @bridge: bridge control structure
- * @state: atomic state being committed
+ * @old_state: old atomic state
  *
  * Calls &drm_bridge_funcs.atomic_enable (falls back on
  * &drm_bridge_funcs.enable) op for all the bridges in the encoder chain,
@@ -570,7 +598,7 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
  * Note: the bridge passed should be the one closest to the encoder
  */
 void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
-				    struct drm_atomic_state *state)
+				    struct drm_atomic_state *old_state)
 {
 	struct drm_encoder *encoder;
 
@@ -579,10 +607,19 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 
 	encoder = bridge->encoder;
 	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
-		if (bridge->funcs->atomic_enable)
-			bridge->funcs->atomic_enable(bridge, state);
-		else if (bridge->funcs->enable)
+		if (bridge->funcs->atomic_enable) {
+			struct drm_bridge_state *old_bridge_state;
+
+			old_bridge_state =
+				drm_atomic_get_old_bridge_state(old_state,
+								bridge);
+			if (WARN_ON(!old_bridge_state))
+				return;
+
+			bridge->funcs->atomic_enable(bridge, old_bridge_state);
+		} else if (bridge->funcs->enable) {
 			bridge->funcs->enable(bridge);
+		}
 	}
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index db5fc18d49bd..b80c3cac3203 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -282,7 +282,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_pre_enable callback is optional.
 	 */
 	void (*atomic_pre_enable)(struct drm_bridge *bridge,
-				  struct drm_atomic_state *old_state);
+				  struct drm_bridge_state *old_bridge_state);
 
 	/**
 	 * @atomic_enable:
@@ -307,7 +307,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_enable callback is optional.
 	 */
 	void (*atomic_enable)(struct drm_bridge *bridge,
-			      struct drm_atomic_state *old_state);
+			      struct drm_bridge_state *old_bridge_state);
 	/**
 	 * @atomic_disable:
 	 *
@@ -330,7 +330,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_disable callback is optional.
 	 */
 	void (*atomic_disable)(struct drm_bridge *bridge,
-			       struct drm_atomic_state *old_state);
+			       struct drm_bridge_state *old_bridge_state);
 
 	/**
 	 * @atomic_post_disable:
@@ -356,7 +356,7 @@ struct drm_bridge_funcs {
 	 * The @atomic_post_disable callback is optional.
 	 */
 	void (*atomic_post_disable)(struct drm_bridge *bridge,
-				    struct drm_atomic_state *old_state);
+				    struct drm_bridge_state *old_bridge_state);
 
 	/**
 	 * @atomic_duplicate_state:
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 10/11] drm/bridge: Add an ->atomic_check() hook
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 14:15   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon, Laurent Pinchart

So that bridge drivers have a way to check/reject an atomic operation.
The drm_atomic_bridge_chain_check() (which is just a wrapper around
the ->atomic_check() hook) is called in place of
drm_bridge_chain_mode_fixup() (when ->atomic_check() is not implemented,
the core falls back on ->mode_fixup(), so the behavior should stay
the same for existing bridge drivers).

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes in v4:
* Add R-bs

Changes in v3:
* None

Changes in v2:
* Clarify the fact that ->atomic_check() is replacing ->mode_fixup()
---
 drivers/gpu/drm/drm_atomic_helper.c | 12 +++---
 drivers/gpu/drm/drm_bridge.c        | 62 +++++++++++++++++++++++++++++
 include/drm/drm_bridge.h            | 29 +++++++++++++-
 3 files changed, 96 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index e2f7f336a2d2..a358f81670e9 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -437,12 +437,12 @@ mode_fixup(struct drm_atomic_state *state)
 		funcs = encoder->helper_private;
 
 		bridge = drm_bridge_chain_get_first_bridge(encoder);
-		ret = drm_bridge_chain_mode_fixup(bridge,
-					&new_crtc_state->mode,
-					&new_crtc_state->adjusted_mode);
-		if (!ret) {
-			DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
-			return -EINVAL;
+		ret = drm_atomic_bridge_chain_check(bridge,
+						    new_crtc_state,
+						    new_conn_state);
+		if (ret) {
+			DRM_DEBUG_ATOMIC("Bridge atomic check failed\n");
+			return ret;
 		}
 
 		if (funcs && funcs->atomic_check) {
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 15b21ff6f1fe..557c29cae51b 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -624,6 +624,68 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
 
+static int drm_atomic_bridge_check(struct drm_bridge *bridge,
+				   struct drm_crtc_state *crtc_state,
+				   struct drm_connector_state *conn_state)
+{
+	if (bridge->funcs->atomic_check) {
+		struct drm_bridge_state *bridge_state;
+		int ret;
+
+		bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
+							       bridge);
+		if (WARN_ON(!bridge_state))
+			return -EINVAL;
+
+		ret = bridge->funcs->atomic_check(bridge, bridge_state,
+						  crtc_state, conn_state);
+		if (ret)
+			return ret;
+	} else if (bridge->funcs->mode_fixup) {
+		if (!bridge->funcs->mode_fixup(bridge, &crtc_state->mode,
+					       &crtc_state->adjusted_mode))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * drm_atomic_bridge_chain_check() - Do an atomic check on the bridge chain
+ * @bridge: bridge control structure
+ * @crtc_state: new CRTC state
+ * @conn_state: new connector state
+ *
+ * Calls &drm_bridge_funcs.atomic_check() (falls back on
+ * &drm_bridge_funcs.mode_fixup()) op for all the bridges in the encoder chain,
+ * starting from the last bridge to the first. These are called before calling
+ * &drm_encoder_helper_funcs.atomic_check()
+ *
+ * RETURNS:
+ * 0 on success, a negative error code on failure
+ */
+int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
+				  struct drm_crtc_state *crtc_state,
+				  struct drm_connector_state *conn_state)
+{
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_bridge *iter;
+
+	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+		int ret;
+
+		ret = drm_atomic_bridge_check(iter, crtc_state, conn_state);
+		if (ret)
+			return ret;
+
+		if (iter == bridge)
+			break;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_atomic_bridge_chain_check);
+
 /**
  * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its
  *					default
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index b80c3cac3203..8206ad90ad2d 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -128,7 +128,9 @@ struct drm_bridge_funcs {
 	 * this function passes all other callbacks must succeed for this
 	 * configuration.
 	 *
-	 * The @mode_fixup callback is optional.
+	 * The mode_fixup callback is optional. &drm_bridge_funcs.mode_fixup()
+	 * is not called when &drm_bridge_funcs.atomic_check() is implemented,
+	 * so only one of them should be provided.
 	 *
 	 * NOTE:
 	 *
@@ -385,6 +387,28 @@ struct drm_bridge_funcs {
 	void (*atomic_destroy_state)(struct drm_bridge *bridge,
 				     struct drm_bridge_state *state);
 
+	/**
+	 * @atomic_check:
+	 *
+	 * This method is responsible for checking bridge state correctness.
+	 * It can also check the state of the surrounding components in chain
+	 * to make sure the whole pipeline can work properly.
+	 *
+	 * &drm_bridge_funcs.atomic_check() hooks are called in reverse
+	 * order (from the last to the first bridge).
+	 *
+	 * This method is optional. &drm_bridge_funcs.mode_fixup() is not
+	 * called when &drm_bridge_funcs.atomic_check() is implemented, so only
+	 * one of them should be provided.
+	 *
+	 * RETURNS:
+	 * zero if the check passed, a negative error code otherwise.
+	 */
+	int (*atomic_check)(struct drm_bridge *bridge,
+			    struct drm_bridge_state *bridge_state,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state);
+
 	/**
 	 * @atomic_reset:
 	 *
@@ -552,6 +576,9 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
 void drm_bridge_chain_pre_enable(struct drm_bridge *bridge);
 void drm_bridge_chain_enable(struct drm_bridge *bridge);
 
+int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
+				  struct drm_crtc_state *crtc_state,
+				  struct drm_connector_state *conn_state);
 void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
 				     struct drm_atomic_state *state);
 void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 10/11] drm/bridge: Add an ->atomic_check() hook
@ 2019-12-03 14:15   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

So that bridge drivers have a way to check/reject an atomic operation.
The drm_atomic_bridge_chain_check() (which is just a wrapper around
the ->atomic_check() hook) is called in place of
drm_bridge_chain_mode_fixup() (when ->atomic_check() is not implemented,
the core falls back on ->mode_fixup(), so the behavior should stay
the same for existing bridge drivers).

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes in v4:
* Add R-bs

Changes in v3:
* None

Changes in v2:
* Clarify the fact that ->atomic_check() is replacing ->mode_fixup()
---
 drivers/gpu/drm/drm_atomic_helper.c | 12 +++---
 drivers/gpu/drm/drm_bridge.c        | 62 +++++++++++++++++++++++++++++
 include/drm/drm_bridge.h            | 29 +++++++++++++-
 3 files changed, 96 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index e2f7f336a2d2..a358f81670e9 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -437,12 +437,12 @@ mode_fixup(struct drm_atomic_state *state)
 		funcs = encoder->helper_private;
 
 		bridge = drm_bridge_chain_get_first_bridge(encoder);
-		ret = drm_bridge_chain_mode_fixup(bridge,
-					&new_crtc_state->mode,
-					&new_crtc_state->adjusted_mode);
-		if (!ret) {
-			DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
-			return -EINVAL;
+		ret = drm_atomic_bridge_chain_check(bridge,
+						    new_crtc_state,
+						    new_conn_state);
+		if (ret) {
+			DRM_DEBUG_ATOMIC("Bridge atomic check failed\n");
+			return ret;
 		}
 
 		if (funcs && funcs->atomic_check) {
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 15b21ff6f1fe..557c29cae51b 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -624,6 +624,68 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
 
+static int drm_atomic_bridge_check(struct drm_bridge *bridge,
+				   struct drm_crtc_state *crtc_state,
+				   struct drm_connector_state *conn_state)
+{
+	if (bridge->funcs->atomic_check) {
+		struct drm_bridge_state *bridge_state;
+		int ret;
+
+		bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
+							       bridge);
+		if (WARN_ON(!bridge_state))
+			return -EINVAL;
+
+		ret = bridge->funcs->atomic_check(bridge, bridge_state,
+						  crtc_state, conn_state);
+		if (ret)
+			return ret;
+	} else if (bridge->funcs->mode_fixup) {
+		if (!bridge->funcs->mode_fixup(bridge, &crtc_state->mode,
+					       &crtc_state->adjusted_mode))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * drm_atomic_bridge_chain_check() - Do an atomic check on the bridge chain
+ * @bridge: bridge control structure
+ * @crtc_state: new CRTC state
+ * @conn_state: new connector state
+ *
+ * Calls &drm_bridge_funcs.atomic_check() (falls back on
+ * &drm_bridge_funcs.mode_fixup()) op for all the bridges in the encoder chain,
+ * starting from the last bridge to the first. These are called before calling
+ * &drm_encoder_helper_funcs.atomic_check()
+ *
+ * RETURNS:
+ * 0 on success, a negative error code on failure
+ */
+int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
+				  struct drm_crtc_state *crtc_state,
+				  struct drm_connector_state *conn_state)
+{
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_bridge *iter;
+
+	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+		int ret;
+
+		ret = drm_atomic_bridge_check(iter, crtc_state, conn_state);
+		if (ret)
+			return ret;
+
+		if (iter == bridge)
+			break;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_atomic_bridge_chain_check);
+
 /**
  * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its
  *					default
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index b80c3cac3203..8206ad90ad2d 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -128,7 +128,9 @@ struct drm_bridge_funcs {
 	 * this function passes all other callbacks must succeed for this
 	 * configuration.
 	 *
-	 * The @mode_fixup callback is optional.
+	 * The mode_fixup callback is optional. &drm_bridge_funcs.mode_fixup()
+	 * is not called when &drm_bridge_funcs.atomic_check() is implemented,
+	 * so only one of them should be provided.
 	 *
 	 * NOTE:
 	 *
@@ -385,6 +387,28 @@ struct drm_bridge_funcs {
 	void (*atomic_destroy_state)(struct drm_bridge *bridge,
 				     struct drm_bridge_state *state);
 
+	/**
+	 * @atomic_check:
+	 *
+	 * This method is responsible for checking bridge state correctness.
+	 * It can also check the state of the surrounding components in chain
+	 * to make sure the whole pipeline can work properly.
+	 *
+	 * &drm_bridge_funcs.atomic_check() hooks are called in reverse
+	 * order (from the last to the first bridge).
+	 *
+	 * This method is optional. &drm_bridge_funcs.mode_fixup() is not
+	 * called when &drm_bridge_funcs.atomic_check() is implemented, so only
+	 * one of them should be provided.
+	 *
+	 * RETURNS:
+	 * zero if the check passed, a negative error code otherwise.
+	 */
+	int (*atomic_check)(struct drm_bridge *bridge,
+			    struct drm_bridge_state *bridge_state,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state);
+
 	/**
 	 * @atomic_reset:
 	 *
@@ -552,6 +576,9 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
 void drm_bridge_chain_pre_enable(struct drm_bridge *bridge);
 void drm_bridge_chain_enable(struct drm_bridge *bridge);
 
+int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
+				  struct drm_crtc_state *crtc_state,
+				  struct drm_connector_state *conn_state);
 void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
 				     struct drm_atomic_state *state);
 void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 11/11] drm/bridge: Add the necessary bits to support bus format negotiation
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 14:15   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Lucas Stach, Chris Healy, Andrey Smirnov, Nikita Yushchenko,
	kernel, Daniel Vetter, Inki Dae, Joonyoung Shim, Seung-Woo Kim,
	Kyungmin Park, Thierry Reding, Sam Ravnborg, Philipp Zabel,
	Rob Clark, Andrzej Hajda, Neil Armstrong, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt, Boris Brezillon

drm_bridge_state is extended to describe the input and output bus
configurations. These bus configurations are exposed through the
drm_bus_cfg struct which encodes the configuration of a physical
bus between two components in an output pipeline, usually between
two bridges, an encoder and a bridge, or a bridge and a connector.

The bus configuration is stored in drm_bridge_state separately for
the input and output buses, as seen from the point of view of each
bridge. The bus configuration of a bridge output is usually identical
to the configuration of the next bridge's input, but may differ if
the signals are modified between the two bridges, for instance by an
inverter on the board. The input and output configurations of a
bridge may differ if the bridge modifies the signals internally,
for instance by performing format conversion, or*modifying signals
polarities.

Bus format negotiation is automated by the core, drivers just have
to implement the ->atomic_get_{output,input}_bus_fmts() hooks if they
want to take part to this negotiation. Negotiation happens in reverse
order, starting from the last element of the chain (the one directly
connected to the display) up to the first element of the chain (the one
connected to the encoder).
During this negotiation all supported formats are tested until we find
one that works, meaning that the formats array should be in decreasing
preference order (assuming the driver has a preference order).

Note that the bus format negotiation works even if some elements in the
chain don't implement the ->atomic_get_{output,input}_bus_fmts() hooks.
In that case, the core advertises only MEDIA_BUS_FMT_FIXED and lets
the previous bridge element decide what to do (most of the time, bridge
drivers will pick a default bus format or extract this piece of
information from somewhere else, like a FW property).

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
Changes in v4:
* Enhance the doc
* Fix typos
* Rename some parameters/fields
* Reword the commit message

Changes in v3:
* Fix the commit message (Reported by Laurent)
* Document the fact that bus formats should not be directly modified by
  drivers (Suggested by Laurent)
* Document the fact that format order matters (Suggested by Laurent)
* Propagate bus flags by default
* Document the fact that drivers can tweak bus flags if needed
* Let ->atomic_get_{output,input}_bus_fmts() allocate the bus format
  array (Suggested by Laurent)
* Add a drm_atomic_helper_bridge_propagate_bus_fmt()
* Mandate that bridge drivers return accurate input_fmts even if they
  are known to be the first element in the bridge chain

Changes in v2:
* Rework things to support more complex use cases
---
 drivers/gpu/drm/drm_bridge.c | 267 ++++++++++++++++++++++++++++++++++-
 include/drm/drm_bridge.h     | 124 ++++++++++++++++
 2 files changed, 390 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 557c29cae51b..9f987c71adef 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -650,13 +650,261 @@ static int drm_atomic_bridge_check(struct drm_bridge *bridge,
 	return 0;
 }
 
+/**
+ * drm_atomic_helper_bridge_propagate_bus_fmt() - Propagate output format to
+ *						  the input end of a bridge
+ * @bridge: bridge control structure
+ * @bridge_state: new bridge state
+ * @crtc_state: new CRTC state
+ * @conn_state: new connector state
+ * @output_fmt: tested output bus format
+ * @num_input_fmts: will contain the size of the returned array
+ *
+ * This helper is a pluggable implementation of the
+ * &drm_bridge_funcs.atomic_get_input_bus_fmts operation for bridges that don't
+ * modify the bus configuration between their input and their output. It
+ * returns an array of input formats with a single element set to @output_fmt.
+ *
+ * RETURNS:
+ * a valid format array of size @num_input_fmts, or NULL if the allocation
+ * failed
+ */
+u32 *
+drm_atomic_helper_bridge_propagate_bus_fmt(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)
+{
+	u32 *input_fmts;
+
+	input_fmts = kzalloc(sizeof(*input_fmts), GFP_KERNEL);
+	if (!input_fmts) {
+		*num_input_fmts = 0;
+		return NULL;
+	}
+
+	*num_input_fmts = 1;
+	input_fmts[0] = output_fmt;
+	return input_fmts;
+}
+EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt);
+
+static int select_bus_fmt_recursive(struct drm_bridge *first_bridge,
+				    struct drm_bridge *cur_bridge,
+				    struct drm_crtc_state *crtc_state,
+				    struct drm_connector_state *conn_state,
+				    u32 out_bus_fmt)
+{
+	struct drm_bridge_state *cur_state;
+	unsigned int num_in_bus_fmts, i;
+	struct drm_bridge *prev_bridge;
+	u32 *in_bus_fmts;
+	int ret;
+
+	prev_bridge = drm_bridge_get_prev_bridge(cur_bridge);
+	cur_state = drm_atomic_get_new_bridge_state(crtc_state->state,
+						    cur_bridge);
+	if (WARN_ON(!cur_state))
+		return -EINVAL;
+
+	/*
+	 * If bus format negotiation is not supported by this bridge, let's
+	 * pass MEDIA_BUS_FMT_FIXED to the previous bridge in the chain and
+	 * hope that it can handle this situation gracefully (by providing
+	 * appropriate default values).
+	 */
+	if (!cur_bridge->funcs->atomic_get_input_bus_fmts) {
+		if (cur_bridge != first_bridge) {
+			ret = select_bus_fmt_recursive(first_bridge,
+						       prev_bridge, crtc_state,
+						       conn_state,
+						       MEDIA_BUS_FMT_FIXED);
+			if (ret)
+				return ret;
+		}
+
+		cur_state->input_bus_cfg.format = MEDIA_BUS_FMT_FIXED;
+		cur_state->output_bus_cfg.format = out_bus_fmt;
+		return 0;
+	}
+
+	in_bus_fmts = cur_bridge->funcs->atomic_get_input_bus_fmts(cur_bridge,
+							cur_state,
+							crtc_state,
+							conn_state,
+							out_bus_fmt,
+							&num_in_bus_fmts);
+	if (!num_in_bus_fmts)
+		return -ENOTSUPP;
+	else if (!in_bus_fmts)
+		return -ENOMEM;
+
+	if (first_bridge == cur_bridge) {
+		cur_state->input_bus_cfg.format = in_bus_fmts[0];
+		cur_state->output_bus_cfg.format = out_bus_fmt;
+		kfree(in_bus_fmts);
+		return 0;
+	}
+
+	for (i = 0; i < num_in_bus_fmts; i++) {
+		ret = select_bus_fmt_recursive(first_bridge, prev_bridge,
+					       crtc_state, conn_state,
+					       in_bus_fmts[i]);
+		if (ret != -ENOTSUPP)
+			break;
+	}
+
+	if (!ret) {
+		cur_state->input_bus_cfg.format = in_bus_fmts[i];
+		cur_state->output_bus_cfg.format = out_bus_fmt;
+	}
+
+	kfree(in_bus_fmts);
+	return ret;
+}
+
+/*
+ * This function is called by &drm_atomic_bridge_chain_check() just before
+ * calling &drm_bridge_funcs.atomic_check() on all elements of the chain.
+ * It performs bus format negotiation between bridge elements. The negotiation
+ * happens in reverse order, starting from the last element in the chain up to
+ * @bridge.
+ *
+ * Negotiation starts by retrieving supported output bus formats on the last
+ * bridge element and testing them one by one. The test is recursive, meaning
+ * that for each tested output format, the whole chain will be walked backward,
+ * and each element will have to choose an input bus format that can be
+ * transcoded to the requested output format. When a bridge element does not
+ * support transcoding into a specific output format -ENOTSUPP is returned and
+ * the next bridge element will have to try a different format. If none of the
+ * combinations worked, -ENOTSUPP is returned and the atomic modeset will fail.
+ *
+ * This implementation is relying on
+ * &drm_bridge_funcs.atomic_get_output_bus_fmts() and
+ * &drm_bridge_funcs.atomic_get_input_bus_fmts() to gather supported
+ * input/output formats.
+ *
+ * When &drm_bridge_funcs.atomic_get_output_bus_fmts() is not implemented by
+ * the last element of the chain, &drm_atomic_bridge_chain_select_bus_fmts()
+ * tries a single format: &drm_connector.display_info.bus_formats[0] if
+ * available, MEDIA_BUS_FMT_FIXED otherwise.
+ *
+ * When &drm_bridge_funcs.atomic_get_input_bus_fmts() is not implemented,
+ * &drm_atomic_bridge_chain_select_bus_fmts() skips the negotiation on the
+ * bridge element that lacks this hook and asks the previous element in the
+ * chain to try MEDIA_BUS_FMT_FIXED. It's up to bridge drivers to decide what
+ * to do in that case (fail if they want to enforce bus format negotiation, or
+ * provide a reasonable default if they need to support pipelines where not
+ * all elements support bus format negotiation).
+ */
+static int
+drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge,
+					struct drm_crtc_state *crtc_state,
+					struct drm_connector_state *conn_state)
+{
+	struct drm_connector *conn = conn_state->connector;
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_bridge_state *last_bridge_state;
+	unsigned int i, num_out_bus_fmts;
+	struct drm_bridge *last_bridge;
+	u32 *out_bus_fmts;
+	int ret = 0;
+
+	last_bridge = list_last_entry(&encoder->bridge_chain,
+				      struct drm_bridge, chain_node);
+	last_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
+							    last_bridge);
+	if (WARN_ON(!last_bridge_state))
+		return -EINVAL;
+
+	if (last_bridge->funcs->atomic_get_output_bus_fmts) {
+		const struct drm_bridge_funcs *funcs = last_bridge->funcs;
+
+		out_bus_fmts = funcs->atomic_get_output_bus_fmts(last_bridge,
+							last_bridge_state,
+							crtc_state,
+							conn_state,
+							&num_out_bus_fmts);
+		if (!num_out_bus_fmts)
+			return -ENOTSUPP;
+		else if (!out_bus_fmts)
+			return -ENOMEM;
+	} else {
+		num_out_bus_fmts = 1;
+		out_bus_fmts = kmalloc(sizeof(*out_bus_fmts), GFP_KERNEL);
+		if (!out_bus_fmts)
+			return -ENOMEM;
+
+		if (conn->display_info.num_bus_formats &&
+		    conn->display_info.bus_formats)
+			out_bus_fmts[0] = conn->display_info.bus_formats[0];
+		else
+			out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED;
+	}
+
+	for (i = 0; i < num_out_bus_fmts; i++) {
+		ret = select_bus_fmt_recursive(bridge, last_bridge, crtc_state,
+					       conn_state, out_bus_fmts[i]);
+		if (ret != -ENOTSUPP)
+			break;
+	}
+
+	kfree(out_bus_fmts);
+
+	return ret;
+}
+
+static void
+drm_atomic_bridge_propagate_bus_flags(struct drm_bridge *bridge,
+				      struct drm_connector *conn,
+				      struct drm_atomic_state *state)
+{
+	struct drm_bridge_state *bridge_state, *next_bridge_state;
+	struct drm_bridge *next_bridge;
+	u32 output_flags;
+
+	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
+	next_bridge = drm_bridge_get_next_bridge(bridge);
+
+	/*
+	 * Let's try to apply the most common case here, that is, propagate
+	 * display_info flags for the last bridge, and propagate the input
+	 * flags of the next bridge element to the output end of the current
+	 * bridge when the bridge is not the last one.
+	 * There are exceptions to this rule, like when signal inversion is
+	 * happening at the board level, but that's something drivers can deal
+	 * with from their &drm_bridge_funcs.atomic_check() implementation by
+	 * simply overriding the flags value we've set here.
+	 */
+	if (!next_bridge) {
+		output_flags = conn->display_info.bus_flags;
+	} else {
+		next_bridge_state = drm_atomic_get_new_bridge_state(state,
+								next_bridge);
+		output_flags = next_bridge_state->input_bus_cfg.flags;
+	}
+
+	bridge_state->output_bus_cfg.flags = output_flags;
+
+	/*
+	 * Propage the output flags to the input end of the bridge. Again, it's
+	 * not necessarily what all bridges want, but that's what most of them
+	 * do, and by doing that by default we avoid forcing drivers to
+	 * duplicate the "dummy propagation" logic.
+	 */
+	bridge_state->input_bus_cfg.flags = output_flags;
+}
+
 /**
  * drm_atomic_bridge_chain_check() - Do an atomic check on the bridge chain
  * @bridge: bridge control structure
  * @crtc_state: new CRTC state
  * @conn_state: new connector state
  *
- * Calls &drm_bridge_funcs.atomic_check() (falls back on
+ * First trigger a bus format negotiation before calling
+ * &drm_bridge_funcs.atomic_check() (falls back on
  * &drm_bridge_funcs.mode_fixup()) op for all the bridges in the encoder chain,
  * starting from the last bridge to the first. These are called before calling
  * &drm_encoder_helper_funcs.atomic_check()
@@ -668,12 +916,29 @@ int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
 				  struct drm_crtc_state *crtc_state,
 				  struct drm_connector_state *conn_state)
 {
+	struct drm_connector *conn = conn_state->connector;
 	struct drm_encoder *encoder = bridge->encoder;
 	struct drm_bridge *iter;
+	int ret;
+
+	ret = drm_atomic_bridge_chain_select_bus_fmts(bridge, crtc_state,
+						      conn_state);
+	if (ret)
+		return ret;
 
 	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
 		int ret;
 
+		/*
+		 * Bus flags are propagated by default. If a bridge needs to
+		 * tweak the input bus flags for any reason, it should happen
+		 * in its &drm_bridge_funcs.atomic_check() implementation such
+		 * that preceding bridges in the chain can propagate the new
+		 * bus flags.
+		 */
+		drm_atomic_bridge_propagate_bus_flags(iter, conn,
+						      crtc_state->state);
+
 		ret = drm_atomic_bridge_check(iter, crtc_state, conn_state);
 		if (ret)
 			return ret;
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 8206ad90ad2d..6217a31af8d8 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -35,6 +35,38 @@ struct drm_bridge;
 struct drm_bridge_timings;
 struct drm_panel;
 
+/**
+ * struct drm_bus_cfg - bus configuration
+ *
+ * This structure stores the configuration of a physical bus between two
+ * components in an output pipeline, usually between two bridges, an encoder
+ * and a bridge, or a bridge and a connector.
+ *
+ * The bus configuration is stored in &drm_bridge_state separately for the
+ * input and output buses, as seen from the point of view of each bridge. The
+ * bus configuration of a bridge output is usually identical to the
+ * configuration of the next bridge's input, but may differ if the signals are
+ * modified between the two bridges, for instance by an inverter on the board.
+ * The input and output configurations of a bridge may differ if the bridge
+ * modifies the signals internally, for instance by performing format
+ * conversion, or modifying signals polarities.
+ */
+struct drm_bus_cfg {
+	/**
+	 * @fmt: format used on this bus (one of the MEDIA_BUS_FMT_* format)
+	 *
+	 * This field should not be directly modified by drivers
+	 * (&drm_atomic_bridge_chain_select_bus_fmts() takes care of the bus
+	 * format negotiation).
+	 */
+	u32 format;
+
+	/**
+	 * @flags: DRM_BUS_* flags used on this bus
+	 */
+	u32 flags;
+};
+
 /**
  * struct drm_bridge_state - Atomic bridge state object
  * @base: inherit from &drm_private_state
@@ -44,6 +76,16 @@ struct drm_bridge_state {
 	struct drm_private_state base;
 
 	struct drm_bridge *bridge;
+
+	/**
+	 * @input_bus_cfg: input bus configuration
+	 */
+	struct drm_bus_cfg input_bus_cfg;
+
+	/**
+	 * @output_bus_cfg: input bus configuration
+	 */
+	struct drm_bus_cfg output_bus_cfg;
 };
 
 static inline struct drm_bridge_state *
@@ -387,6 +429,72 @@ struct drm_bridge_funcs {
 	void (*atomic_destroy_state)(struct drm_bridge *bridge,
 				     struct drm_bridge_state *state);
 
+	/**
+	 * @atomic_get_output_bus_fmts:
+	 *
+	 * Return the supported bus formats on the output end of a bridge.
+	 * The returned array must be allocated with kmalloc() and will be
+	 * freed by the caller. If the allocation fails, NULL should be
+	 * returned. num_output_fmts must be set to the returned array size.
+	 * Formats listed in the returned array should be listed in decreasing
+	 * preference order (the core will try all formats until it finds one
+	 * that works).
+	 *
+	 * This method is only called on the last element of the bridge chain
+	 * as part of the bus format negotiation process that happens in
+	 * &drm_atomic_bridge_chain_select_bus_fmts().
+	 * This method is optional. When not implemented, the core will
+	 * fall back to &drm_connector.display_info.bus_formats[0] if
+	 * &drm_connector.display_info.num_bus_formats > 0,
+	 * or to MEDIA_BUS_FMT_FIXED otherwise.
+	 */
+	u32 *(*atomic_get_output_bus_fmts)(struct drm_bridge *bridge,
+					   struct drm_bridge_state *bridge_state,
+					   struct drm_crtc_state *crtc_state,
+					   struct drm_connector_state *conn_state,
+					   unsigned int *num_output_fmts);
+
+	/**
+	 * @atomic_get_input_bus_fmts:
+	 *
+	 * Return the supported bus formats on the input end of a bridge for
+	 * a specific output bus format.
+	 *
+	 * The returned array must be allocated with kmalloc() and will be
+	 * freed by the caller. If the allocation fails, NULL should be
+	 * returned. num_output_fmts must be set to the returned array size.
+	 * Formats listed in the returned array should be listed in decreasing
+	 * preference order (the core will try all formats until it finds one
+	 * that works). When the format is not supported NULL should be
+	 * returned and *num_output_fmts should be set to 0.
+	 *
+	 * This method is called on all elements of the bridge chain as part of
+	 * the bus format negotiation process that happens in
+	 * &drm_atomic_bridge_chain_select_bus_fmts().
+	 * This method is optional. When not implemented, the core will bypass
+	 * bus format negotiation on this element of the bridge without
+	 * failing, and the previous element in the chain will be passed
+	 * MEDIA_BUS_FMT_FIXED as its output bus format.
+	 *
+	 * Bridge drivers that need to support being linked to bridges that are
+	 * not supporting bus format negotiation should handle the
+	 * output_fmt == MEDIA_BUS_FMT_FIXED case appropriately, by selecting a
+	 * sensible default value or extracting this information from somewhere
+	 * else (FW property, &drm_display_mode, &drm_display_info, ...)
+	 *
+	 * Note: Even if input format selection on the first bridge has no
+	 * impact on the negotiation process (bus format negotiation stops once
+	 * we reach the first element of the chain), drivers are expected to
+	 * return accurate input formats as the input format may be used to
+	 * configure the CRTC output appropriately.
+	 */
+	u32 *(*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);
+
 	/**
 	 * @atomic_check:
 	 *
@@ -401,6 +509,14 @@ struct drm_bridge_funcs {
 	 * called when &drm_bridge_funcs.atomic_check() is implemented, so only
 	 * one of them should be provided.
 	 *
+	 * If drivers need to tweak &drm_bridge_state.input_bus_cfg.flags or
+	 * &drm_bridge_state.output_bus_cfg.flags it should should happen in
+	 * this function. By default the &drm_bridge_state.output_bus_cfg.flags
+	 * field is set to the next bridge
+	 * &drm_bridge_state.input_bus_cfg.flags value or
+	 * &drm_connector.display_info.bus_flags if the bridge is the last
+	 * element in the chain.
+	 *
 	 * RETURNS:
 	 * zero if the check passed, a negative error code otherwise.
 	 */
@@ -588,6 +704,14 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
 void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 				    struct drm_atomic_state *state);
 
+u32 *
+drm_atomic_helper_bridge_propagate_bus_fmt(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);
+
 void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
 				      struct drm_bridge_state *state);
 struct drm_bridge_state *
-- 
2.23.0


^ permalink raw reply related	[flat|nested] 84+ messages in thread

* [PATCH v4 11/11] drm/bridge: Add the necessary bits to support bus format negotiation
@ 2019-12-03 14:15   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-03 14:15 UTC (permalink / raw)
  To: dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Boris Brezillon

drm_bridge_state is extended to describe the input and output bus
configurations. These bus configurations are exposed through the
drm_bus_cfg struct which encodes the configuration of a physical
bus between two components in an output pipeline, usually between
two bridges, an encoder and a bridge, or a bridge and a connector.

The bus configuration is stored in drm_bridge_state separately for
the input and output buses, as seen from the point of view of each
bridge. The bus configuration of a bridge output is usually identical
to the configuration of the next bridge's input, but may differ if
the signals are modified between the two bridges, for instance by an
inverter on the board. The input and output configurations of a
bridge may differ if the bridge modifies the signals internally,
for instance by performing format conversion, or*modifying signals
polarities.

Bus format negotiation is automated by the core, drivers just have
to implement the ->atomic_get_{output,input}_bus_fmts() hooks if they
want to take part to this negotiation. Negotiation happens in reverse
order, starting from the last element of the chain (the one directly
connected to the display) up to the first element of the chain (the one
connected to the encoder).
During this negotiation all supported formats are tested until we find
one that works, meaning that the formats array should be in decreasing
preference order (assuming the driver has a preference order).

Note that the bus format negotiation works even if some elements in the
chain don't implement the ->atomic_get_{output,input}_bus_fmts() hooks.
In that case, the core advertises only MEDIA_BUS_FMT_FIXED and lets
the previous bridge element decide what to do (most of the time, bridge
drivers will pick a default bus format or extract this piece of
information from somewhere else, like a FW property).

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
Changes in v4:
* Enhance the doc
* Fix typos
* Rename some parameters/fields
* Reword the commit message

Changes in v3:
* Fix the commit message (Reported by Laurent)
* Document the fact that bus formats should not be directly modified by
  drivers (Suggested by Laurent)
* Document the fact that format order matters (Suggested by Laurent)
* Propagate bus flags by default
* Document the fact that drivers can tweak bus flags if needed
* Let ->atomic_get_{output,input}_bus_fmts() allocate the bus format
  array (Suggested by Laurent)
* Add a drm_atomic_helper_bridge_propagate_bus_fmt()
* Mandate that bridge drivers return accurate input_fmts even if they
  are known to be the first element in the bridge chain

Changes in v2:
* Rework things to support more complex use cases
---
 drivers/gpu/drm/drm_bridge.c | 267 ++++++++++++++++++++++++++++++++++-
 include/drm/drm_bridge.h     | 124 ++++++++++++++++
 2 files changed, 390 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 557c29cae51b..9f987c71adef 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -650,13 +650,261 @@ static int drm_atomic_bridge_check(struct drm_bridge *bridge,
 	return 0;
 }
 
+/**
+ * drm_atomic_helper_bridge_propagate_bus_fmt() - Propagate output format to
+ *						  the input end of a bridge
+ * @bridge: bridge control structure
+ * @bridge_state: new bridge state
+ * @crtc_state: new CRTC state
+ * @conn_state: new connector state
+ * @output_fmt: tested output bus format
+ * @num_input_fmts: will contain the size of the returned array
+ *
+ * This helper is a pluggable implementation of the
+ * &drm_bridge_funcs.atomic_get_input_bus_fmts operation for bridges that don't
+ * modify the bus configuration between their input and their output. It
+ * returns an array of input formats with a single element set to @output_fmt.
+ *
+ * RETURNS:
+ * a valid format array of size @num_input_fmts, or NULL if the allocation
+ * failed
+ */
+u32 *
+drm_atomic_helper_bridge_propagate_bus_fmt(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)
+{
+	u32 *input_fmts;
+
+	input_fmts = kzalloc(sizeof(*input_fmts), GFP_KERNEL);
+	if (!input_fmts) {
+		*num_input_fmts = 0;
+		return NULL;
+	}
+
+	*num_input_fmts = 1;
+	input_fmts[0] = output_fmt;
+	return input_fmts;
+}
+EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt);
+
+static int select_bus_fmt_recursive(struct drm_bridge *first_bridge,
+				    struct drm_bridge *cur_bridge,
+				    struct drm_crtc_state *crtc_state,
+				    struct drm_connector_state *conn_state,
+				    u32 out_bus_fmt)
+{
+	struct drm_bridge_state *cur_state;
+	unsigned int num_in_bus_fmts, i;
+	struct drm_bridge *prev_bridge;
+	u32 *in_bus_fmts;
+	int ret;
+
+	prev_bridge = drm_bridge_get_prev_bridge(cur_bridge);
+	cur_state = drm_atomic_get_new_bridge_state(crtc_state->state,
+						    cur_bridge);
+	if (WARN_ON(!cur_state))
+		return -EINVAL;
+
+	/*
+	 * If bus format negotiation is not supported by this bridge, let's
+	 * pass MEDIA_BUS_FMT_FIXED to the previous bridge in the chain and
+	 * hope that it can handle this situation gracefully (by providing
+	 * appropriate default values).
+	 */
+	if (!cur_bridge->funcs->atomic_get_input_bus_fmts) {
+		if (cur_bridge != first_bridge) {
+			ret = select_bus_fmt_recursive(first_bridge,
+						       prev_bridge, crtc_state,
+						       conn_state,
+						       MEDIA_BUS_FMT_FIXED);
+			if (ret)
+				return ret;
+		}
+
+		cur_state->input_bus_cfg.format = MEDIA_BUS_FMT_FIXED;
+		cur_state->output_bus_cfg.format = out_bus_fmt;
+		return 0;
+	}
+
+	in_bus_fmts = cur_bridge->funcs->atomic_get_input_bus_fmts(cur_bridge,
+							cur_state,
+							crtc_state,
+							conn_state,
+							out_bus_fmt,
+							&num_in_bus_fmts);
+	if (!num_in_bus_fmts)
+		return -ENOTSUPP;
+	else if (!in_bus_fmts)
+		return -ENOMEM;
+
+	if (first_bridge == cur_bridge) {
+		cur_state->input_bus_cfg.format = in_bus_fmts[0];
+		cur_state->output_bus_cfg.format = out_bus_fmt;
+		kfree(in_bus_fmts);
+		return 0;
+	}
+
+	for (i = 0; i < num_in_bus_fmts; i++) {
+		ret = select_bus_fmt_recursive(first_bridge, prev_bridge,
+					       crtc_state, conn_state,
+					       in_bus_fmts[i]);
+		if (ret != -ENOTSUPP)
+			break;
+	}
+
+	if (!ret) {
+		cur_state->input_bus_cfg.format = in_bus_fmts[i];
+		cur_state->output_bus_cfg.format = out_bus_fmt;
+	}
+
+	kfree(in_bus_fmts);
+	return ret;
+}
+
+/*
+ * This function is called by &drm_atomic_bridge_chain_check() just before
+ * calling &drm_bridge_funcs.atomic_check() on all elements of the chain.
+ * It performs bus format negotiation between bridge elements. The negotiation
+ * happens in reverse order, starting from the last element in the chain up to
+ * @bridge.
+ *
+ * Negotiation starts by retrieving supported output bus formats on the last
+ * bridge element and testing them one by one. The test is recursive, meaning
+ * that for each tested output format, the whole chain will be walked backward,
+ * and each element will have to choose an input bus format that can be
+ * transcoded to the requested output format. When a bridge element does not
+ * support transcoding into a specific output format -ENOTSUPP is returned and
+ * the next bridge element will have to try a different format. If none of the
+ * combinations worked, -ENOTSUPP is returned and the atomic modeset will fail.
+ *
+ * This implementation is relying on
+ * &drm_bridge_funcs.atomic_get_output_bus_fmts() and
+ * &drm_bridge_funcs.atomic_get_input_bus_fmts() to gather supported
+ * input/output formats.
+ *
+ * When &drm_bridge_funcs.atomic_get_output_bus_fmts() is not implemented by
+ * the last element of the chain, &drm_atomic_bridge_chain_select_bus_fmts()
+ * tries a single format: &drm_connector.display_info.bus_formats[0] if
+ * available, MEDIA_BUS_FMT_FIXED otherwise.
+ *
+ * When &drm_bridge_funcs.atomic_get_input_bus_fmts() is not implemented,
+ * &drm_atomic_bridge_chain_select_bus_fmts() skips the negotiation on the
+ * bridge element that lacks this hook and asks the previous element in the
+ * chain to try MEDIA_BUS_FMT_FIXED. It's up to bridge drivers to decide what
+ * to do in that case (fail if they want to enforce bus format negotiation, or
+ * provide a reasonable default if they need to support pipelines where not
+ * all elements support bus format negotiation).
+ */
+static int
+drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge,
+					struct drm_crtc_state *crtc_state,
+					struct drm_connector_state *conn_state)
+{
+	struct drm_connector *conn = conn_state->connector;
+	struct drm_encoder *encoder = bridge->encoder;
+	struct drm_bridge_state *last_bridge_state;
+	unsigned int i, num_out_bus_fmts;
+	struct drm_bridge *last_bridge;
+	u32 *out_bus_fmts;
+	int ret = 0;
+
+	last_bridge = list_last_entry(&encoder->bridge_chain,
+				      struct drm_bridge, chain_node);
+	last_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
+							    last_bridge);
+	if (WARN_ON(!last_bridge_state))
+		return -EINVAL;
+
+	if (last_bridge->funcs->atomic_get_output_bus_fmts) {
+		const struct drm_bridge_funcs *funcs = last_bridge->funcs;
+
+		out_bus_fmts = funcs->atomic_get_output_bus_fmts(last_bridge,
+							last_bridge_state,
+							crtc_state,
+							conn_state,
+							&num_out_bus_fmts);
+		if (!num_out_bus_fmts)
+			return -ENOTSUPP;
+		else if (!out_bus_fmts)
+			return -ENOMEM;
+	} else {
+		num_out_bus_fmts = 1;
+		out_bus_fmts = kmalloc(sizeof(*out_bus_fmts), GFP_KERNEL);
+		if (!out_bus_fmts)
+			return -ENOMEM;
+
+		if (conn->display_info.num_bus_formats &&
+		    conn->display_info.bus_formats)
+			out_bus_fmts[0] = conn->display_info.bus_formats[0];
+		else
+			out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED;
+	}
+
+	for (i = 0; i < num_out_bus_fmts; i++) {
+		ret = select_bus_fmt_recursive(bridge, last_bridge, crtc_state,
+					       conn_state, out_bus_fmts[i]);
+		if (ret != -ENOTSUPP)
+			break;
+	}
+
+	kfree(out_bus_fmts);
+
+	return ret;
+}
+
+static void
+drm_atomic_bridge_propagate_bus_flags(struct drm_bridge *bridge,
+				      struct drm_connector *conn,
+				      struct drm_atomic_state *state)
+{
+	struct drm_bridge_state *bridge_state, *next_bridge_state;
+	struct drm_bridge *next_bridge;
+	u32 output_flags;
+
+	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
+	next_bridge = drm_bridge_get_next_bridge(bridge);
+
+	/*
+	 * Let's try to apply the most common case here, that is, propagate
+	 * display_info flags for the last bridge, and propagate the input
+	 * flags of the next bridge element to the output end of the current
+	 * bridge when the bridge is not the last one.
+	 * There are exceptions to this rule, like when signal inversion is
+	 * happening at the board level, but that's something drivers can deal
+	 * with from their &drm_bridge_funcs.atomic_check() implementation by
+	 * simply overriding the flags value we've set here.
+	 */
+	if (!next_bridge) {
+		output_flags = conn->display_info.bus_flags;
+	} else {
+		next_bridge_state = drm_atomic_get_new_bridge_state(state,
+								next_bridge);
+		output_flags = next_bridge_state->input_bus_cfg.flags;
+	}
+
+	bridge_state->output_bus_cfg.flags = output_flags;
+
+	/*
+	 * Propage the output flags to the input end of the bridge. Again, it's
+	 * not necessarily what all bridges want, but that's what most of them
+	 * do, and by doing that by default we avoid forcing drivers to
+	 * duplicate the "dummy propagation" logic.
+	 */
+	bridge_state->input_bus_cfg.flags = output_flags;
+}
+
 /**
  * drm_atomic_bridge_chain_check() - Do an atomic check on the bridge chain
  * @bridge: bridge control structure
  * @crtc_state: new CRTC state
  * @conn_state: new connector state
  *
- * Calls &drm_bridge_funcs.atomic_check() (falls back on
+ * First trigger a bus format negotiation before calling
+ * &drm_bridge_funcs.atomic_check() (falls back on
  * &drm_bridge_funcs.mode_fixup()) op for all the bridges in the encoder chain,
  * starting from the last bridge to the first. These are called before calling
  * &drm_encoder_helper_funcs.atomic_check()
@@ -668,12 +916,29 @@ int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
 				  struct drm_crtc_state *crtc_state,
 				  struct drm_connector_state *conn_state)
 {
+	struct drm_connector *conn = conn_state->connector;
 	struct drm_encoder *encoder = bridge->encoder;
 	struct drm_bridge *iter;
+	int ret;
+
+	ret = drm_atomic_bridge_chain_select_bus_fmts(bridge, crtc_state,
+						      conn_state);
+	if (ret)
+		return ret;
 
 	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
 		int ret;
 
+		/*
+		 * Bus flags are propagated by default. If a bridge needs to
+		 * tweak the input bus flags for any reason, it should happen
+		 * in its &drm_bridge_funcs.atomic_check() implementation such
+		 * that preceding bridges in the chain can propagate the new
+		 * bus flags.
+		 */
+		drm_atomic_bridge_propagate_bus_flags(iter, conn,
+						      crtc_state->state);
+
 		ret = drm_atomic_bridge_check(iter, crtc_state, conn_state);
 		if (ret)
 			return ret;
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 8206ad90ad2d..6217a31af8d8 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -35,6 +35,38 @@ struct drm_bridge;
 struct drm_bridge_timings;
 struct drm_panel;
 
+/**
+ * struct drm_bus_cfg - bus configuration
+ *
+ * This structure stores the configuration of a physical bus between two
+ * components in an output pipeline, usually between two bridges, an encoder
+ * and a bridge, or a bridge and a connector.
+ *
+ * The bus configuration is stored in &drm_bridge_state separately for the
+ * input and output buses, as seen from the point of view of each bridge. The
+ * bus configuration of a bridge output is usually identical to the
+ * configuration of the next bridge's input, but may differ if the signals are
+ * modified between the two bridges, for instance by an inverter on the board.
+ * The input and output configurations of a bridge may differ if the bridge
+ * modifies the signals internally, for instance by performing format
+ * conversion, or modifying signals polarities.
+ */
+struct drm_bus_cfg {
+	/**
+	 * @fmt: format used on this bus (one of the MEDIA_BUS_FMT_* format)
+	 *
+	 * This field should not be directly modified by drivers
+	 * (&drm_atomic_bridge_chain_select_bus_fmts() takes care of the bus
+	 * format negotiation).
+	 */
+	u32 format;
+
+	/**
+	 * @flags: DRM_BUS_* flags used on this bus
+	 */
+	u32 flags;
+};
+
 /**
  * struct drm_bridge_state - Atomic bridge state object
  * @base: inherit from &drm_private_state
@@ -44,6 +76,16 @@ struct drm_bridge_state {
 	struct drm_private_state base;
 
 	struct drm_bridge *bridge;
+
+	/**
+	 * @input_bus_cfg: input bus configuration
+	 */
+	struct drm_bus_cfg input_bus_cfg;
+
+	/**
+	 * @output_bus_cfg: input bus configuration
+	 */
+	struct drm_bus_cfg output_bus_cfg;
 };
 
 static inline struct drm_bridge_state *
@@ -387,6 +429,72 @@ struct drm_bridge_funcs {
 	void (*atomic_destroy_state)(struct drm_bridge *bridge,
 				     struct drm_bridge_state *state);
 
+	/**
+	 * @atomic_get_output_bus_fmts:
+	 *
+	 * Return the supported bus formats on the output end of a bridge.
+	 * The returned array must be allocated with kmalloc() and will be
+	 * freed by the caller. If the allocation fails, NULL should be
+	 * returned. num_output_fmts must be set to the returned array size.
+	 * Formats listed in the returned array should be listed in decreasing
+	 * preference order (the core will try all formats until it finds one
+	 * that works).
+	 *
+	 * This method is only called on the last element of the bridge chain
+	 * as part of the bus format negotiation process that happens in
+	 * &drm_atomic_bridge_chain_select_bus_fmts().
+	 * This method is optional. When not implemented, the core will
+	 * fall back to &drm_connector.display_info.bus_formats[0] if
+	 * &drm_connector.display_info.num_bus_formats > 0,
+	 * or to MEDIA_BUS_FMT_FIXED otherwise.
+	 */
+	u32 *(*atomic_get_output_bus_fmts)(struct drm_bridge *bridge,
+					   struct drm_bridge_state *bridge_state,
+					   struct drm_crtc_state *crtc_state,
+					   struct drm_connector_state *conn_state,
+					   unsigned int *num_output_fmts);
+
+	/**
+	 * @atomic_get_input_bus_fmts:
+	 *
+	 * Return the supported bus formats on the input end of a bridge for
+	 * a specific output bus format.
+	 *
+	 * The returned array must be allocated with kmalloc() and will be
+	 * freed by the caller. If the allocation fails, NULL should be
+	 * returned. num_output_fmts must be set to the returned array size.
+	 * Formats listed in the returned array should be listed in decreasing
+	 * preference order (the core will try all formats until it finds one
+	 * that works). When the format is not supported NULL should be
+	 * returned and *num_output_fmts should be set to 0.
+	 *
+	 * This method is called on all elements of the bridge chain as part of
+	 * the bus format negotiation process that happens in
+	 * &drm_atomic_bridge_chain_select_bus_fmts().
+	 * This method is optional. When not implemented, the core will bypass
+	 * bus format negotiation on this element of the bridge without
+	 * failing, and the previous element in the chain will be passed
+	 * MEDIA_BUS_FMT_FIXED as its output bus format.
+	 *
+	 * Bridge drivers that need to support being linked to bridges that are
+	 * not supporting bus format negotiation should handle the
+	 * output_fmt == MEDIA_BUS_FMT_FIXED case appropriately, by selecting a
+	 * sensible default value or extracting this information from somewhere
+	 * else (FW property, &drm_display_mode, &drm_display_info, ...)
+	 *
+	 * Note: Even if input format selection on the first bridge has no
+	 * impact on the negotiation process (bus format negotiation stops once
+	 * we reach the first element of the chain), drivers are expected to
+	 * return accurate input formats as the input format may be used to
+	 * configure the CRTC output appropriately.
+	 */
+	u32 *(*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);
+
 	/**
 	 * @atomic_check:
 	 *
@@ -401,6 +509,14 @@ struct drm_bridge_funcs {
 	 * called when &drm_bridge_funcs.atomic_check() is implemented, so only
 	 * one of them should be provided.
 	 *
+	 * If drivers need to tweak &drm_bridge_state.input_bus_cfg.flags or
+	 * &drm_bridge_state.output_bus_cfg.flags it should should happen in
+	 * this function. By default the &drm_bridge_state.output_bus_cfg.flags
+	 * field is set to the next bridge
+	 * &drm_bridge_state.input_bus_cfg.flags value or
+	 * &drm_connector.display_info.bus_flags if the bridge is the last
+	 * element in the chain.
+	 *
 	 * RETURNS:
 	 * zero if the check passed, a negative error code otherwise.
 	 */
@@ -588,6 +704,14 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
 void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 				    struct drm_atomic_state *state);
 
+u32 *
+drm_atomic_helper_bridge_propagate_bus_fmt(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);
+
 void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
 				      struct drm_bridge_state *state);
 struct drm_bridge_state *
-- 
2.23.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 07/11] drm/bridge: Clarify the atomic enable/disable hooks semantics
  2019-12-03 14:15   ` Boris Brezillon
@ 2019-12-03 18:02     ` Laurent Pinchart
  -1 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-03 18:02 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Neil Armstrong, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree, Eric Anholt

Hi Boris,

Thanks for the patch.

On Tue, Dec 03, 2019 at 03:15:11PM +0100, Boris Brezillon wrote:
> The [pre_]enable/[post_]disable hooks are passed the old atomic state.
> Update the doc and rename the arguments to make it clear.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
> Changes in v4:
> * Drop the doc update (Laurent)

I was referring to the doc updates in drm_bridge.h only. Is there a
reason you dropped the changes from drm_bridge.c ? I think those were
good.

> * Add Rbs
> 
> Changes in v3:
> * New patch
> ---
>  include/drm/drm_bridge.h | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index bfb0385163f1..d7d714023050 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -263,7 +263,7 @@ struct drm_bridge_funcs {
>  	 * The @atomic_pre_enable callback is optional.
>  	 */
>  	void (*atomic_pre_enable)(struct drm_bridge *bridge,
> -				  struct drm_atomic_state *state);
> +				  struct drm_atomic_state *old_state);
>  
>  	/**
>  	 * @atomic_enable:
> @@ -288,7 +288,7 @@ struct drm_bridge_funcs {
>  	 * The @atomic_enable callback is optional.
>  	 */
>  	void (*atomic_enable)(struct drm_bridge *bridge,
> -			      struct drm_atomic_state *state);
> +			      struct drm_atomic_state *old_state);
>  	/**
>  	 * @atomic_disable:
>  	 *
> @@ -311,7 +311,7 @@ struct drm_bridge_funcs {
>  	 * The @atomic_disable callback is optional.
>  	 */
>  	void (*atomic_disable)(struct drm_bridge *bridge,
> -			       struct drm_atomic_state *state);
> +			       struct drm_atomic_state *old_state);
>  
>  	/**
>  	 * @atomic_post_disable:
> @@ -337,7 +337,7 @@ struct drm_bridge_funcs {
>  	 * The @atomic_post_disable callback is optional.
>  	 */
>  	void (*atomic_post_disable)(struct drm_bridge *bridge,
> -				    struct drm_atomic_state *state);
> +				    struct drm_atomic_state *old_state);
>  };
>  
>  /**

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 07/11] drm/bridge: Clarify the atomic enable/disable hooks semantics
@ 2019-12-03 18:02     ` Laurent Pinchart
  0 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-03 18:02 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Mark Rutland, Neil Armstrong, dri-devel, Thierry Reding, kernel,
	Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, devicetree, Jonas Karlman, Rob Herring,
	Jernej Skrabec, Seung-Woo Kim

Hi Boris,

Thanks for the patch.

On Tue, Dec 03, 2019 at 03:15:11PM +0100, Boris Brezillon wrote:
> The [pre_]enable/[post_]disable hooks are passed the old atomic state.
> Update the doc and rename the arguments to make it clear.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
> Changes in v4:
> * Drop the doc update (Laurent)

I was referring to the doc updates in drm_bridge.h only. Is there a
reason you dropped the changes from drm_bridge.c ? I think those were
good.

> * Add Rbs
> 
> Changes in v3:
> * New patch
> ---
>  include/drm/drm_bridge.h | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index bfb0385163f1..d7d714023050 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -263,7 +263,7 @@ struct drm_bridge_funcs {
>  	 * The @atomic_pre_enable callback is optional.
>  	 */
>  	void (*atomic_pre_enable)(struct drm_bridge *bridge,
> -				  struct drm_atomic_state *state);
> +				  struct drm_atomic_state *old_state);
>  
>  	/**
>  	 * @atomic_enable:
> @@ -288,7 +288,7 @@ struct drm_bridge_funcs {
>  	 * The @atomic_enable callback is optional.
>  	 */
>  	void (*atomic_enable)(struct drm_bridge *bridge,
> -			      struct drm_atomic_state *state);
> +			      struct drm_atomic_state *old_state);
>  	/**
>  	 * @atomic_disable:
>  	 *
> @@ -311,7 +311,7 @@ struct drm_bridge_funcs {
>  	 * The @atomic_disable callback is optional.
>  	 */
>  	void (*atomic_disable)(struct drm_bridge *bridge,
> -			       struct drm_atomic_state *state);
> +			       struct drm_atomic_state *old_state);
>  
>  	/**
>  	 * @atomic_post_disable:
> @@ -337,7 +337,7 @@ struct drm_bridge_funcs {
>  	 * The @atomic_post_disable callback is optional.
>  	 */
>  	void (*atomic_post_disable)(struct drm_bridge *bridge,
> -				    struct drm_atomic_state *state);
> +				    struct drm_atomic_state *old_state);
>  };
>  
>  /**

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
  2019-12-03 14:15   ` Boris Brezillon
@ 2019-12-03 18:17     ` Laurent Pinchart
  -1 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-03 18:17 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Neil Armstrong, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree, Eric Anholt

Hi Boris,

Thank you for the patch.

On Tue, Dec 03, 2019 at 03:15:12PM +0100, Boris Brezillon wrote:
> One of the last remaining objects to not have its atomic state.
> 
> This is being motivated by our attempt to support runtime bus-format
> negotiation between elements of the bridge chain.
> This patch just paves the road for such a feature by adding a new
> drm_bridge_state object inheriting from drm_private_obj so we can
> re-use some of the existing state initialization/tracking logic.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
> Changes in v4:
> * Fix the doc
> * Kill default helpers (inlined)

I liked the default helpers, inlining their content makes the code more
difficult to follow in my opinion. I will however not push back if you
prefer not to have them.

> * Fix drm_atomic_get_bridge_state() to check for an ERR_PTR()
> * Add Neil's R-b
> 
> Changes in v3:
> * None
> 
> Changes in v2:
> * Use drm_for_each_bridge_in_chain()
> * Rename helpers to be more consistent with the rest of the DRM API
> * Improve/fix the doc
> ---
>  drivers/gpu/drm/drm_atomic.c        |  39 +++++++++
>  drivers/gpu/drm/drm_atomic_helper.c |  20 +++++
>  drivers/gpu/drm/drm_bridge.c        | 118 ++++++++++++++++++++++++++--
>  include/drm/drm_atomic.h            |   3 +
>  include/drm/drm_bridge.h            | 116 +++++++++++++++++++++++++++
>  5 files changed, 290 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 14aeaf736321..02f8034f5240 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -30,6 +30,7 @@
>  
>  #include <drm/drm_atomic.h>
>  #include <drm/drm_atomic_uapi.h>
> +#include <drm/drm_bridge.h>
>  #include <drm/drm_debugfs.h>
>  #include <drm/drm_device.h>
>  #include <drm/drm_drv.h>
> @@ -1015,6 +1016,44 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
>  		connector->funcs->atomic_print_state(p, state);
>  }
>  
> +/**
> + * drm_atomic_add_encoder_bridges - add bridges attached to an encoder
> + * @state: atomic state
> + * @encoder: DRM encoder
> + *
> + * This function adds all bridges attached to @encoder. This is needed to add
> + * bridge states to @state and make them available when
> + * &bridge_funcs.atomic_{check,pre_enable,enable,disable_post_disable}() are
> + * called
> + *
> + * Returns:
> + * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK
> + * then the w/w mutex code has detected a deadlock and the entire atomic
> + * sequence must be restarted. All other errors are fatal.
> + */
> +int
> +drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
> +			       struct drm_encoder *encoder)
> +{
> +	struct drm_bridge_state *bridge_state;
> +	struct drm_bridge *bridge;
> +
> +	if (!encoder)
> +		return 0;
> +
> +	DRM_DEBUG_ATOMIC("Adding all bridges for [encoder:%d:%s] to %p\n",
> +			 encoder->base.id, encoder->name, state);
> +
> +	drm_for_each_bridge_in_chain(encoder, bridge) {
> +		bridge_state = drm_atomic_get_bridge_state(state, bridge);
> +		if (IS_ERR(bridge_state))
> +			return PTR_ERR(bridge_state);
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_atomic_add_encoder_bridges);
> +
>  /**
>   * drm_atomic_add_affected_connectors - add connectors for crtc
>   * @state: atomic state
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 29c3115bf9e2..e2f7f336a2d2 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -730,6 +730,26 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
>  			return ret;
>  	}
>  
> +	/*
> +	 * Iterate over all connectors again, and add all affected bridges to
> +	 * the state.
> +	 */
> +	for_each_oldnew_connector_in_state(state, connector,
> +					   old_connector_state,
> +					   new_connector_state, i) {
> +		struct drm_encoder *encoder;
> +
> +		encoder = old_connector_state->best_encoder;
> +		ret = drm_atomic_add_encoder_bridges(state, encoder);
> +		if (ret)
> +			return ret;
> +
> +		encoder = new_connector_state->best_encoder;
> +		ret = drm_atomic_add_encoder_bridges(state, encoder);
> +		if (ret)
> +			return ret;
> +	}
> +
>  	ret = mode_valid(state);
>  	if (ret)
>  		return ret;
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index b6517b4fa3d1..bf5a2243a115 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -25,6 +25,7 @@
>  #include <linux/module.h>
>  #include <linux/mutex.h>
>  
> +#include <drm/drm_atomic_state_helper.h>
>  #include <drm/drm_bridge.h>
>  #include <drm/drm_encoder.h>
>  
> @@ -89,6 +90,48 @@ void drm_bridge_remove(struct drm_bridge *bridge)
>  }
>  EXPORT_SYMBOL(drm_bridge_remove);
>  
> +static struct drm_private_state *
> +drm_bridge_atomic_duplicate_priv_state(struct drm_private_obj *obj)
> +{
> +	struct drm_bridge *bridge = drm_priv_to_bridge(obj);
> +	struct drm_bridge_state *state;
> +
> +	if (bridge->funcs->atomic_duplicate_state) {
> +		state = bridge->funcs->atomic_duplicate_state(bridge);
> +
> +		return state ? &state->base : NULL;
> +	}
> +
> +	if (WARN_ON(!bridge->base.state))
> +		return NULL;
> +
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_bridge_duplicate_state(bridge, state);
> +
> +	return &state->base;
> +}
> +
> +static void
> +drm_bridge_atomic_destroy_priv_state(struct drm_private_obj *obj,
> +				     struct drm_private_state *s)
> +{
> +	struct drm_bridge_state *state = drm_priv_to_bridge_state(s);
> +	struct drm_bridge *bridge = drm_priv_to_bridge(obj);
> +
> +	if (bridge->funcs->atomic_destroy_state)
> +		bridge->funcs->atomic_destroy_state(bridge, state);
> +	else
> +		kfree(state);
> +}
> +
> +static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
> +	.atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state,
> +	.atomic_destroy_state = drm_bridge_atomic_destroy_priv_state,
> +};
> +
>  /**
>   * drm_bridge_attach - attach the bridge to an encoder's chain
>   *
> @@ -114,6 +157,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
>  int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  		      struct drm_bridge *previous)
>  {
> +	struct drm_bridge_state *state;
>  	int ret;
>  
>  	if (!encoder || !bridge)
> @@ -135,15 +179,40 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  
>  	if (bridge->funcs->attach) {
>  		ret = bridge->funcs->attach(bridge);
> -		if (ret < 0) {
> -			list_del(&bridge->chain_node);
> -			bridge->dev = NULL;
> -			bridge->encoder = NULL;
> -			return ret;
> -		}
> +		if (ret < 0)
> +			goto err_reset_bridge;
>  	}
>  
> +	if (bridge->funcs->atomic_reset) {
> +		state = bridge->funcs->atomic_reset(bridge);
> +	} else {
> +		state = kzalloc(sizeof(*state), GFP_KERNEL);
> +		if (state)
> +			__drm_atomic_helper_bridge_reset(bridge, state);
> +		else
> +			state = ERR_PTR(-ENOMEM);
> +	}
> +
> +	if (IS_ERR(state)) {
> +		ret = PTR_ERR(state);
> +		goto err_detach_bridge;
> +	}
> +
> +	drm_atomic_private_obj_init(bridge->dev, &bridge->base,
> +				    &state->base,
> +				    &drm_bridge_priv_state_funcs);
> +
>  	return 0;
> +
> +err_detach_bridge:
> +	if (bridge->funcs->detach)
> +		bridge->funcs->detach(bridge);
> +
> +err_reset_bridge:
> +	bridge->dev = NULL;
> +	bridge->encoder = NULL;
> +	list_del(&bridge->chain_node);
> +	return ret;
>  }
>  EXPORT_SYMBOL(drm_bridge_attach);
>  
> @@ -155,6 +224,8 @@ void drm_bridge_detach(struct drm_bridge *bridge)
>  	if (WARN_ON(!bridge->dev))
>  		return;
>  
> +	drm_atomic_private_obj_fini(&bridge->base);
> +
>  	if (bridge->funcs->detach)
>  		bridge->funcs->detach(bridge);
>  
> @@ -516,6 +587,41 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
>  }
>  EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
>  
> +/**
> + * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its
> + *					default
> + * @bridge: the bridge this state is refers to
> + * @state: bridge state to initialize
> + *
> + * Initialize the bridge state to default values. This is meant to be* called

s/be\*/be/

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> + * by the bridge &drm_plane_funcs.reset hook for bridges that subclass the
> + * bridge state.
> + */
> +void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
> +				      struct drm_bridge_state *state)
> +{
> +	memset(state, 0, sizeof(*state));
> +	state->bridge = bridge;
> +}
> +EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset);
> +
> +/**
> + * __drm_atomic_helper_bridge_duplicate_state() - Copy atomic bridge state
> + * @bridge: bridge object
> + * @state: atomic bridge state
> + *
> + * Copies atomic state from a bridge's current state and resets inferred values.
> + * This is useful for drivers that subclass the bridge state.
> + */
> +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
> +						struct drm_bridge_state *state)
> +{
> +	__drm_atomic_helper_private_obj_duplicate_state(&bridge->base,
> +							&state->base);
> +	state->bridge = bridge;
> +}
> +EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_state);
> +
>  #ifdef CONFIG_OF
>  /**
>   * of_drm_find_bridge - find the bridge corresponding to the device node in
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> index b6c73fd9f55a..e08582773d6c 100644
> --- a/include/drm/drm_atomic.h
> +++ b/include/drm/drm_atomic.h
> @@ -660,6 +660,9 @@ __drm_atomic_get_current_plane_state(struct drm_atomic_state *state,
>  	return plane->state;
>  }
>  
> +int __must_check
> +drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
> +			       struct drm_encoder *encoder);
>  int __must_check
>  drm_atomic_add_affected_connectors(struct drm_atomic_state *state,
>  				   struct drm_crtc *crtc);
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index d7d714023050..db5fc18d49bd 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -25,6 +25,8 @@
>  
>  #include <linux/list.h>
>  #include <linux/ctype.h>
> +
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_encoder.h>
>  #include <drm/drm_mode_object.h>
>  #include <drm/drm_modes.h>
> @@ -33,6 +35,23 @@ struct drm_bridge;
>  struct drm_bridge_timings;
>  struct drm_panel;
>  
> +/**
> + * struct drm_bridge_state - Atomic bridge state object
> + * @base: inherit from &drm_private_state
> + * @bridge: the bridge this state refers to
> + */
> +struct drm_bridge_state {
> +	struct drm_private_state base;
> +
> +	struct drm_bridge *bridge;
> +};
> +
> +static inline struct drm_bridge_state *
> +drm_priv_to_bridge_state(struct drm_private_state *priv)
> +{
> +	return container_of(priv, struct drm_bridge_state, base);
> +}
> +
>  /**
>   * struct drm_bridge_funcs - drm_bridge control functions
>   */
> @@ -338,6 +357,49 @@ struct drm_bridge_funcs {
>  	 */
>  	void (*atomic_post_disable)(struct drm_bridge *bridge,
>  				    struct drm_atomic_state *old_state);
> +
> +	/**
> +	 * @atomic_duplicate_state:
> +	 *
> +	 * Duplicate the current bridge state object (which is guaranteed to be
> +	 * non-NULL).
> +	 *
> +	 * The atomic_duplicate_state() is optional. When not implemented the
> +	 * core allocates a drm_bridge_state object and calls
> +	 * &__drm_atomic_helper_bridge_duplicate_state() to initialize it.
> +	 *
> +	 * RETURNS:
> +	 * A valid drm_bridge_state object or NULL if the allocation fails.
> +	 */
> +	struct drm_bridge_state *(*atomic_duplicate_state)(struct drm_bridge *bridge);
> +
> +	/**
> +	 * @atomic_destroy_state:
> +	 *
> +	 * Destroy a bridge state object previously allocated by
> +	 * &drm_bridge_funcs.atomic_duplicate_state().
> +	 *
> +	 * The atomic_destroy_state hook is optional. When not implemented the
> +	 * core calls kfree() on the state.
> +	 */
> +	void (*atomic_destroy_state)(struct drm_bridge *bridge,
> +				     struct drm_bridge_state *state);
> +
> +	/**
> +	 * @atomic_reset:
> +	 *
> +	 * Reset the bridge to a predefined state (or retrieve its current
> +	 * state) and return a &drm_bridge_state object matching this state.
> +	 * This function is called at attach time.
> +	 *
> +	 * The atomic_reset hook is optional. When not implemented the core
> +	 * allocates a new state and calls &__drm_atomic_helper_bridge_reset().
> +	 *
> +	 * RETURNS:
> +	 * A valid drm_bridge_state object in case of success, an ERR_PTR()
> +	 * giving the reason of the failure otherwise.
> +	 */
> +	struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
>  };
>  
>  /**
> @@ -380,6 +442,8 @@ struct drm_bridge_timings {
>   * struct drm_bridge - central DRM bridge control structure
>   */
>  struct drm_bridge {
> +	/** @base: inherit from &drm_private_object */
> +	struct drm_private_obj base;
>  	/** @dev: DRM device this bridge belongs to */
>  	struct drm_device *dev;
>  	/** @encoder: encoder to which this bridge is connected */
> @@ -404,6 +468,12 @@ struct drm_bridge {
>  	void *driver_private;
>  };
>  
> +static inline struct drm_bridge *
> +drm_priv_to_bridge(struct drm_private_obj *priv)
> +{
> +	return container_of(priv, struct drm_bridge, base);
> +}
> +
>  void drm_bridge_add(struct drm_bridge *bridge);
>  void drm_bridge_remove(struct drm_bridge *bridge);
>  struct drm_bridge *of_drm_find_bridge(struct device_node *np);
> @@ -491,6 +561,52 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
>  void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
>  				    struct drm_atomic_state *state);
>  
> +void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
> +				      struct drm_bridge_state *state);
> +struct drm_bridge_state *
> +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge);
> +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
> +						struct drm_bridge_state *new);
> +
> +static inline struct drm_bridge_state *
> +drm_atomic_get_bridge_state(struct drm_atomic_state *state,
> +			    struct drm_bridge *bridge)
> +{
> +	struct drm_private_state *obj_state;
> +
> +	obj_state = drm_atomic_get_private_obj_state(state, &bridge->base);
> +	if (IS_ERR(obj_state))
> +		return ERR_CAST(obj_state);
> +
> +	return drm_priv_to_bridge_state(obj_state);
> +}
> +
> +static inline struct drm_bridge_state *
> +drm_atomic_get_old_bridge_state(struct drm_atomic_state *state,
> +				struct drm_bridge *bridge)
> +{
> +	struct drm_private_state *obj_state;
> +
> +	obj_state = drm_atomic_get_old_private_obj_state(state, &bridge->base);
> +	if (!obj_state)
> +		return NULL;
> +
> +	return drm_priv_to_bridge_state(obj_state);
> +}
> +
> +static inline struct drm_bridge_state *
> +drm_atomic_get_new_bridge_state(struct drm_atomic_state *state,
> +				struct drm_bridge *bridge)
> +{
> +	struct drm_private_state *obj_state;
> +
> +	obj_state = drm_atomic_get_new_private_obj_state(state, &bridge->base);
> +	if (!obj_state)
> +		return NULL;
> +
> +	return drm_priv_to_bridge_state(obj_state);
> +}
> +
>  #ifdef CONFIG_DRM_PANEL_BRIDGE
>  struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel);
>  struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
> -- 
> 2.23.0
> 

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
@ 2019-12-03 18:17     ` Laurent Pinchart
  0 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-03 18:17 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Mark Rutland, Neil Armstrong, dri-devel, Thierry Reding, kernel,
	Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, devicetree, Jonas Karlman, Rob Herring,
	Jernej Skrabec, Seung-Woo Kim

Hi Boris,

Thank you for the patch.

On Tue, Dec 03, 2019 at 03:15:12PM +0100, Boris Brezillon wrote:
> One of the last remaining objects to not have its atomic state.
> 
> This is being motivated by our attempt to support runtime bus-format
> negotiation between elements of the bridge chain.
> This patch just paves the road for such a feature by adding a new
> drm_bridge_state object inheriting from drm_private_obj so we can
> re-use some of the existing state initialization/tracking logic.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
> Changes in v4:
> * Fix the doc
> * Kill default helpers (inlined)

I liked the default helpers, inlining their content makes the code more
difficult to follow in my opinion. I will however not push back if you
prefer not to have them.

> * Fix drm_atomic_get_bridge_state() to check for an ERR_PTR()
> * Add Neil's R-b
> 
> Changes in v3:
> * None
> 
> Changes in v2:
> * Use drm_for_each_bridge_in_chain()
> * Rename helpers to be more consistent with the rest of the DRM API
> * Improve/fix the doc
> ---
>  drivers/gpu/drm/drm_atomic.c        |  39 +++++++++
>  drivers/gpu/drm/drm_atomic_helper.c |  20 +++++
>  drivers/gpu/drm/drm_bridge.c        | 118 ++++++++++++++++++++++++++--
>  include/drm/drm_atomic.h            |   3 +
>  include/drm/drm_bridge.h            | 116 +++++++++++++++++++++++++++
>  5 files changed, 290 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 14aeaf736321..02f8034f5240 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -30,6 +30,7 @@
>  
>  #include <drm/drm_atomic.h>
>  #include <drm/drm_atomic_uapi.h>
> +#include <drm/drm_bridge.h>
>  #include <drm/drm_debugfs.h>
>  #include <drm/drm_device.h>
>  #include <drm/drm_drv.h>
> @@ -1015,6 +1016,44 @@ static void drm_atomic_connector_print_state(struct drm_printer *p,
>  		connector->funcs->atomic_print_state(p, state);
>  }
>  
> +/**
> + * drm_atomic_add_encoder_bridges - add bridges attached to an encoder
> + * @state: atomic state
> + * @encoder: DRM encoder
> + *
> + * This function adds all bridges attached to @encoder. This is needed to add
> + * bridge states to @state and make them available when
> + * &bridge_funcs.atomic_{check,pre_enable,enable,disable_post_disable}() are
> + * called
> + *
> + * Returns:
> + * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK
> + * then the w/w mutex code has detected a deadlock and the entire atomic
> + * sequence must be restarted. All other errors are fatal.
> + */
> +int
> +drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
> +			       struct drm_encoder *encoder)
> +{
> +	struct drm_bridge_state *bridge_state;
> +	struct drm_bridge *bridge;
> +
> +	if (!encoder)
> +		return 0;
> +
> +	DRM_DEBUG_ATOMIC("Adding all bridges for [encoder:%d:%s] to %p\n",
> +			 encoder->base.id, encoder->name, state);
> +
> +	drm_for_each_bridge_in_chain(encoder, bridge) {
> +		bridge_state = drm_atomic_get_bridge_state(state, bridge);
> +		if (IS_ERR(bridge_state))
> +			return PTR_ERR(bridge_state);
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_atomic_add_encoder_bridges);
> +
>  /**
>   * drm_atomic_add_affected_connectors - add connectors for crtc
>   * @state: atomic state
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index 29c3115bf9e2..e2f7f336a2d2 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -730,6 +730,26 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
>  			return ret;
>  	}
>  
> +	/*
> +	 * Iterate over all connectors again, and add all affected bridges to
> +	 * the state.
> +	 */
> +	for_each_oldnew_connector_in_state(state, connector,
> +					   old_connector_state,
> +					   new_connector_state, i) {
> +		struct drm_encoder *encoder;
> +
> +		encoder = old_connector_state->best_encoder;
> +		ret = drm_atomic_add_encoder_bridges(state, encoder);
> +		if (ret)
> +			return ret;
> +
> +		encoder = new_connector_state->best_encoder;
> +		ret = drm_atomic_add_encoder_bridges(state, encoder);
> +		if (ret)
> +			return ret;
> +	}
> +
>  	ret = mode_valid(state);
>  	if (ret)
>  		return ret;
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index b6517b4fa3d1..bf5a2243a115 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -25,6 +25,7 @@
>  #include <linux/module.h>
>  #include <linux/mutex.h>
>  
> +#include <drm/drm_atomic_state_helper.h>
>  #include <drm/drm_bridge.h>
>  #include <drm/drm_encoder.h>
>  
> @@ -89,6 +90,48 @@ void drm_bridge_remove(struct drm_bridge *bridge)
>  }
>  EXPORT_SYMBOL(drm_bridge_remove);
>  
> +static struct drm_private_state *
> +drm_bridge_atomic_duplicate_priv_state(struct drm_private_obj *obj)
> +{
> +	struct drm_bridge *bridge = drm_priv_to_bridge(obj);
> +	struct drm_bridge_state *state;
> +
> +	if (bridge->funcs->atomic_duplicate_state) {
> +		state = bridge->funcs->atomic_duplicate_state(bridge);
> +
> +		return state ? &state->base : NULL;
> +	}
> +
> +	if (WARN_ON(!bridge->base.state))
> +		return NULL;
> +
> +	state = kzalloc(sizeof(*state), GFP_KERNEL);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_bridge_duplicate_state(bridge, state);
> +
> +	return &state->base;
> +}
> +
> +static void
> +drm_bridge_atomic_destroy_priv_state(struct drm_private_obj *obj,
> +				     struct drm_private_state *s)
> +{
> +	struct drm_bridge_state *state = drm_priv_to_bridge_state(s);
> +	struct drm_bridge *bridge = drm_priv_to_bridge(obj);
> +
> +	if (bridge->funcs->atomic_destroy_state)
> +		bridge->funcs->atomic_destroy_state(bridge, state);
> +	else
> +		kfree(state);
> +}
> +
> +static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
> +	.atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state,
> +	.atomic_destroy_state = drm_bridge_atomic_destroy_priv_state,
> +};
> +
>  /**
>   * drm_bridge_attach - attach the bridge to an encoder's chain
>   *
> @@ -114,6 +157,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
>  int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  		      struct drm_bridge *previous)
>  {
> +	struct drm_bridge_state *state;
>  	int ret;
>  
>  	if (!encoder || !bridge)
> @@ -135,15 +179,40 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  
>  	if (bridge->funcs->attach) {
>  		ret = bridge->funcs->attach(bridge);
> -		if (ret < 0) {
> -			list_del(&bridge->chain_node);
> -			bridge->dev = NULL;
> -			bridge->encoder = NULL;
> -			return ret;
> -		}
> +		if (ret < 0)
> +			goto err_reset_bridge;
>  	}
>  
> +	if (bridge->funcs->atomic_reset) {
> +		state = bridge->funcs->atomic_reset(bridge);
> +	} else {
> +		state = kzalloc(sizeof(*state), GFP_KERNEL);
> +		if (state)
> +			__drm_atomic_helper_bridge_reset(bridge, state);
> +		else
> +			state = ERR_PTR(-ENOMEM);
> +	}
> +
> +	if (IS_ERR(state)) {
> +		ret = PTR_ERR(state);
> +		goto err_detach_bridge;
> +	}
> +
> +	drm_atomic_private_obj_init(bridge->dev, &bridge->base,
> +				    &state->base,
> +				    &drm_bridge_priv_state_funcs);
> +
>  	return 0;
> +
> +err_detach_bridge:
> +	if (bridge->funcs->detach)
> +		bridge->funcs->detach(bridge);
> +
> +err_reset_bridge:
> +	bridge->dev = NULL;
> +	bridge->encoder = NULL;
> +	list_del(&bridge->chain_node);
> +	return ret;
>  }
>  EXPORT_SYMBOL(drm_bridge_attach);
>  
> @@ -155,6 +224,8 @@ void drm_bridge_detach(struct drm_bridge *bridge)
>  	if (WARN_ON(!bridge->dev))
>  		return;
>  
> +	drm_atomic_private_obj_fini(&bridge->base);
> +
>  	if (bridge->funcs->detach)
>  		bridge->funcs->detach(bridge);
>  
> @@ -516,6 +587,41 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
>  }
>  EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
>  
> +/**
> + * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its
> + *					default
> + * @bridge: the bridge this state is refers to
> + * @state: bridge state to initialize
> + *
> + * Initialize the bridge state to default values. This is meant to be* called

s/be\*/be/

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> + * by the bridge &drm_plane_funcs.reset hook for bridges that subclass the
> + * bridge state.
> + */
> +void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
> +				      struct drm_bridge_state *state)
> +{
> +	memset(state, 0, sizeof(*state));
> +	state->bridge = bridge;
> +}
> +EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset);
> +
> +/**
> + * __drm_atomic_helper_bridge_duplicate_state() - Copy atomic bridge state
> + * @bridge: bridge object
> + * @state: atomic bridge state
> + *
> + * Copies atomic state from a bridge's current state and resets inferred values.
> + * This is useful for drivers that subclass the bridge state.
> + */
> +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
> +						struct drm_bridge_state *state)
> +{
> +	__drm_atomic_helper_private_obj_duplicate_state(&bridge->base,
> +							&state->base);
> +	state->bridge = bridge;
> +}
> +EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_state);
> +
>  #ifdef CONFIG_OF
>  /**
>   * of_drm_find_bridge - find the bridge corresponding to the device node in
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> index b6c73fd9f55a..e08582773d6c 100644
> --- a/include/drm/drm_atomic.h
> +++ b/include/drm/drm_atomic.h
> @@ -660,6 +660,9 @@ __drm_atomic_get_current_plane_state(struct drm_atomic_state *state,
>  	return plane->state;
>  }
>  
> +int __must_check
> +drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
> +			       struct drm_encoder *encoder);
>  int __must_check
>  drm_atomic_add_affected_connectors(struct drm_atomic_state *state,
>  				   struct drm_crtc *crtc);
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index d7d714023050..db5fc18d49bd 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -25,6 +25,8 @@
>  
>  #include <linux/list.h>
>  #include <linux/ctype.h>
> +
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_encoder.h>
>  #include <drm/drm_mode_object.h>
>  #include <drm/drm_modes.h>
> @@ -33,6 +35,23 @@ struct drm_bridge;
>  struct drm_bridge_timings;
>  struct drm_panel;
>  
> +/**
> + * struct drm_bridge_state - Atomic bridge state object
> + * @base: inherit from &drm_private_state
> + * @bridge: the bridge this state refers to
> + */
> +struct drm_bridge_state {
> +	struct drm_private_state base;
> +
> +	struct drm_bridge *bridge;
> +};
> +
> +static inline struct drm_bridge_state *
> +drm_priv_to_bridge_state(struct drm_private_state *priv)
> +{
> +	return container_of(priv, struct drm_bridge_state, base);
> +}
> +
>  /**
>   * struct drm_bridge_funcs - drm_bridge control functions
>   */
> @@ -338,6 +357,49 @@ struct drm_bridge_funcs {
>  	 */
>  	void (*atomic_post_disable)(struct drm_bridge *bridge,
>  				    struct drm_atomic_state *old_state);
> +
> +	/**
> +	 * @atomic_duplicate_state:
> +	 *
> +	 * Duplicate the current bridge state object (which is guaranteed to be
> +	 * non-NULL).
> +	 *
> +	 * The atomic_duplicate_state() is optional. When not implemented the
> +	 * core allocates a drm_bridge_state object and calls
> +	 * &__drm_atomic_helper_bridge_duplicate_state() to initialize it.
> +	 *
> +	 * RETURNS:
> +	 * A valid drm_bridge_state object or NULL if the allocation fails.
> +	 */
> +	struct drm_bridge_state *(*atomic_duplicate_state)(struct drm_bridge *bridge);
> +
> +	/**
> +	 * @atomic_destroy_state:
> +	 *
> +	 * Destroy a bridge state object previously allocated by
> +	 * &drm_bridge_funcs.atomic_duplicate_state().
> +	 *
> +	 * The atomic_destroy_state hook is optional. When not implemented the
> +	 * core calls kfree() on the state.
> +	 */
> +	void (*atomic_destroy_state)(struct drm_bridge *bridge,
> +				     struct drm_bridge_state *state);
> +
> +	/**
> +	 * @atomic_reset:
> +	 *
> +	 * Reset the bridge to a predefined state (or retrieve its current
> +	 * state) and return a &drm_bridge_state object matching this state.
> +	 * This function is called at attach time.
> +	 *
> +	 * The atomic_reset hook is optional. When not implemented the core
> +	 * allocates a new state and calls &__drm_atomic_helper_bridge_reset().
> +	 *
> +	 * RETURNS:
> +	 * A valid drm_bridge_state object in case of success, an ERR_PTR()
> +	 * giving the reason of the failure otherwise.
> +	 */
> +	struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
>  };
>  
>  /**
> @@ -380,6 +442,8 @@ struct drm_bridge_timings {
>   * struct drm_bridge - central DRM bridge control structure
>   */
>  struct drm_bridge {
> +	/** @base: inherit from &drm_private_object */
> +	struct drm_private_obj base;
>  	/** @dev: DRM device this bridge belongs to */
>  	struct drm_device *dev;
>  	/** @encoder: encoder to which this bridge is connected */
> @@ -404,6 +468,12 @@ struct drm_bridge {
>  	void *driver_private;
>  };
>  
> +static inline struct drm_bridge *
> +drm_priv_to_bridge(struct drm_private_obj *priv)
> +{
> +	return container_of(priv, struct drm_bridge, base);
> +}
> +
>  void drm_bridge_add(struct drm_bridge *bridge);
>  void drm_bridge_remove(struct drm_bridge *bridge);
>  struct drm_bridge *of_drm_find_bridge(struct device_node *np);
> @@ -491,6 +561,52 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
>  void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
>  				    struct drm_atomic_state *state);
>  
> +void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge,
> +				      struct drm_bridge_state *state);
> +struct drm_bridge_state *
> +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge);
> +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
> +						struct drm_bridge_state *new);
> +
> +static inline struct drm_bridge_state *
> +drm_atomic_get_bridge_state(struct drm_atomic_state *state,
> +			    struct drm_bridge *bridge)
> +{
> +	struct drm_private_state *obj_state;
> +
> +	obj_state = drm_atomic_get_private_obj_state(state, &bridge->base);
> +	if (IS_ERR(obj_state))
> +		return ERR_CAST(obj_state);
> +
> +	return drm_priv_to_bridge_state(obj_state);
> +}
> +
> +static inline struct drm_bridge_state *
> +drm_atomic_get_old_bridge_state(struct drm_atomic_state *state,
> +				struct drm_bridge *bridge)
> +{
> +	struct drm_private_state *obj_state;
> +
> +	obj_state = drm_atomic_get_old_private_obj_state(state, &bridge->base);
> +	if (!obj_state)
> +		return NULL;
> +
> +	return drm_priv_to_bridge_state(obj_state);
> +}
> +
> +static inline struct drm_bridge_state *
> +drm_atomic_get_new_bridge_state(struct drm_atomic_state *state,
> +				struct drm_bridge *bridge)
> +{
> +	struct drm_private_state *obj_state;
> +
> +	obj_state = drm_atomic_get_new_private_obj_state(state, &bridge->base);
> +	if (!obj_state)
> +		return NULL;
> +
> +	return drm_priv_to_bridge_state(obj_state);
> +}
> +
>  #ifdef CONFIG_DRM_PANEL_BRIDGE
>  struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel);
>  struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
> -- 
> 2.23.0
> 

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 00/11] drm: Add support for bus-format negotiation
  2019-12-03 14:15 ` Boris Brezillon
@ 2019-12-03 18:19   ` Laurent Pinchart
  -1 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-03 18:19 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Neil Armstrong, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree, Eric Anholt

Hi Boris,

On Tue, Dec 03, 2019 at 03:15:04PM +0100, Boris Brezillon wrote:
> This patch series aims at adding support for runtime bus-format
> negotiation between all elements of the
> 'encoder -> bridges -> connector/display' section of the pipeline.
> 
> In order to support that, we need drm bridges to fully take part in the
> atomic state validation process, which requires adding a
> drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
> Once those basic building blocks are in place, we can add new hooks to
> allow bus format negotiation (those are called just before
> ->atomic_check()). The bus format selection is done at runtime by
> testing all possible combinations across the whole bridge chain until
> one is reported to work.
> 
> No Major changes in this v4. I think I addressed all comments I got
> from Neil and Laurent (thanks for the detailed reviews BTW). Note that
> this version only contains core changes. Once those changes are merged
> I'll send the imx/panel/lvds-codec specific bits.

I think it would make sense to fast-track patches 01 to 07 (a bit
selfishly as I would have a need for them in omapdrm :-)), but starting
from 08 I wonder if it wouldn't make more sense to merge them with the
imx/panel/lvds-codec bits to have a user.

> A more detailed changelog is provided in each patch.
> 
> This patch series is also available here [1].
> 
> Thanks,
> 
> Boris
> 
> [1]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v4
> 
> Boris Brezillon (11):
>   drm/bridge: Rename bridge helpers targeting a bridge chain
>   drm/bridge: Introduce drm_bridge_get_next_bridge()
>   drm: Stop accessing encoder->bridge directly
>   drm/bridge: Make the bridge chain a double-linked list
>   drm/bridge: Add the drm_for_each_bridge_in_chain() helper
>   drm/bridge: Add the drm_bridge_get_prev_bridge() helper
>   drm/bridge: Clarify the atomic enable/disable hooks semantics
>   drm/bridge: Add a drm_bridge_state object
>   drm/bridge: Patch atomic hooks to take a drm_bridge_state
>   drm/bridge: Add an ->atomic_check() hook
>   drm/bridge: Add the necessary bits to support bus format negotiation
> 
>  .../drm/bridge/analogix/analogix_dp_core.c    |  41 +-
>  drivers/gpu/drm/drm_atomic.c                  |  39 +
>  drivers/gpu/drm/drm_atomic_helper.c           |  53 +-
>  drivers/gpu/drm/drm_bridge.c                  | 756 +++++++++++++++---
>  drivers/gpu/drm/drm_encoder.c                 |  15 +-
>  drivers/gpu/drm/drm_probe_helper.c            |   4 +-
>  drivers/gpu/drm/exynos/exynos_drm_dsi.c       |  13 +-
>  drivers/gpu/drm/mediatek/mtk_hdmi.c           |   8 +-
>  drivers/gpu/drm/msm/edp/edp_bridge.c          |  10 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c            |   4 +-
>  drivers/gpu/drm/omapdrm/omap_encoder.c        |   3 +-
>  drivers/gpu/drm/rcar-du/rcar_du_crtc.c        |  10 +-
>  drivers/gpu/drm/vc4/vc4_dsi.c                 |  18 +-
>  include/drm/drm_atomic.h                      |   3 +
>  include/drm/drm_bridge.h                      | 404 +++++++++-
>  include/drm/drm_encoder.h                     |   7 +-
>  16 files changed, 1174 insertions(+), 214 deletions(-)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 00/11] drm: Add support for bus-format negotiation
@ 2019-12-03 18:19   ` Laurent Pinchart
  0 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-03 18:19 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Mark Rutland, Neil Armstrong, dri-devel, Thierry Reding, kernel,
	Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, devicetree, Jonas Karlman, Rob Herring,
	Jernej Skrabec, Seung-Woo Kim

Hi Boris,

On Tue, Dec 03, 2019 at 03:15:04PM +0100, Boris Brezillon wrote:
> This patch series aims at adding support for runtime bus-format
> negotiation between all elements of the
> 'encoder -> bridges -> connector/display' section of the pipeline.
> 
> In order to support that, we need drm bridges to fully take part in the
> atomic state validation process, which requires adding a
> drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
> Once those basic building blocks are in place, we can add new hooks to
> allow bus format negotiation (those are called just before
> ->atomic_check()). The bus format selection is done at runtime by
> testing all possible combinations across the whole bridge chain until
> one is reported to work.
> 
> No Major changes in this v4. I think I addressed all comments I got
> from Neil and Laurent (thanks for the detailed reviews BTW). Note that
> this version only contains core changes. Once those changes are merged
> I'll send the imx/panel/lvds-codec specific bits.

I think it would make sense to fast-track patches 01 to 07 (a bit
selfishly as I would have a need for them in omapdrm :-)), but starting
from 08 I wonder if it wouldn't make more sense to merge them with the
imx/panel/lvds-codec bits to have a user.

> A more detailed changelog is provided in each patch.
> 
> This patch series is also available here [1].
> 
> Thanks,
> 
> Boris
> 
> [1]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v4
> 
> Boris Brezillon (11):
>   drm/bridge: Rename bridge helpers targeting a bridge chain
>   drm/bridge: Introduce drm_bridge_get_next_bridge()
>   drm: Stop accessing encoder->bridge directly
>   drm/bridge: Make the bridge chain a double-linked list
>   drm/bridge: Add the drm_for_each_bridge_in_chain() helper
>   drm/bridge: Add the drm_bridge_get_prev_bridge() helper
>   drm/bridge: Clarify the atomic enable/disable hooks semantics
>   drm/bridge: Add a drm_bridge_state object
>   drm/bridge: Patch atomic hooks to take a drm_bridge_state
>   drm/bridge: Add an ->atomic_check() hook
>   drm/bridge: Add the necessary bits to support bus format negotiation
> 
>  .../drm/bridge/analogix/analogix_dp_core.c    |  41 +-
>  drivers/gpu/drm/drm_atomic.c                  |  39 +
>  drivers/gpu/drm/drm_atomic_helper.c           |  53 +-
>  drivers/gpu/drm/drm_bridge.c                  | 756 +++++++++++++++---
>  drivers/gpu/drm/drm_encoder.c                 |  15 +-
>  drivers/gpu/drm/drm_probe_helper.c            |   4 +-
>  drivers/gpu/drm/exynos/exynos_drm_dsi.c       |  13 +-
>  drivers/gpu/drm/mediatek/mtk_hdmi.c           |   8 +-
>  drivers/gpu/drm/msm/edp/edp_bridge.c          |  10 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c            |   4 +-
>  drivers/gpu/drm/omapdrm/omap_encoder.c        |   3 +-
>  drivers/gpu/drm/rcar-du/rcar_du_crtc.c        |  10 +-
>  drivers/gpu/drm/vc4/vc4_dsi.c                 |  18 +-
>  include/drm/drm_atomic.h                      |   3 +
>  include/drm/drm_bridge.h                      | 404 +++++++++-
>  include/drm/drm_encoder.h                     |   7 +-
>  16 files changed, 1174 insertions(+), 214 deletions(-)

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 07/11] drm/bridge: Clarify the atomic enable/disable hooks semantics
  2019-12-03 18:02     ` Laurent Pinchart
@ 2019-12-04  9:00       ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-04  9:00 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Neil Armstrong, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree, Eric Anholt

On Tue, 3 Dec 2019 20:02:23 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> Thanks for the patch.
> 
> On Tue, Dec 03, 2019 at 03:15:11PM +0100, Boris Brezillon wrote:
> > The [pre_]enable/[post_]disable hooks are passed the old atomic state.
> > Update the doc and rename the arguments to make it clear.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > ---
> > Changes in v4:
> > * Drop the doc update (Laurent)  
> 
> I was referring to the doc updates in drm_bridge.h only. Is there a
> reason you dropped the changes from drm_bridge.c ? I think those were
> good.

Oops, that's a mistake. I'll fix it before applying.

> 
> > * Add Rbs
> > 
> > Changes in v3:
> > * New patch
> > ---
> >  include/drm/drm_bridge.h | 8 ++++----
> >  1 file changed, 4 insertions(+), 4 deletions(-)
> > 
> > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > index bfb0385163f1..d7d714023050 100644
> > --- a/include/drm/drm_bridge.h
> > +++ b/include/drm/drm_bridge.h
> > @@ -263,7 +263,7 @@ struct drm_bridge_funcs {
> >  	 * The @atomic_pre_enable callback is optional.
> >  	 */
> >  	void (*atomic_pre_enable)(struct drm_bridge *bridge,
> > -				  struct drm_atomic_state *state);
> > +				  struct drm_atomic_state *old_state);
> >  
> >  	/**
> >  	 * @atomic_enable:
> > @@ -288,7 +288,7 @@ struct drm_bridge_funcs {
> >  	 * The @atomic_enable callback is optional.
> >  	 */
> >  	void (*atomic_enable)(struct drm_bridge *bridge,
> > -			      struct drm_atomic_state *state);
> > +			      struct drm_atomic_state *old_state);
> >  	/**
> >  	 * @atomic_disable:
> >  	 *
> > @@ -311,7 +311,7 @@ struct drm_bridge_funcs {
> >  	 * The @atomic_disable callback is optional.
> >  	 */
> >  	void (*atomic_disable)(struct drm_bridge *bridge,
> > -			       struct drm_atomic_state *state);
> > +			       struct drm_atomic_state *old_state);
> >  
> >  	/**
> >  	 * @atomic_post_disable:
> > @@ -337,7 +337,7 @@ struct drm_bridge_funcs {
> >  	 * The @atomic_post_disable callback is optional.
> >  	 */
> >  	void (*atomic_post_disable)(struct drm_bridge *bridge,
> > -				    struct drm_atomic_state *state);
> > +				    struct drm_atomic_state *old_state);
> >  };
> >  
> >  /**  
> 


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 07/11] drm/bridge: Clarify the atomic enable/disable hooks semantics
@ 2019-12-04  9:00       ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-04  9:00 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Mark Rutland, Neil Armstrong, dri-devel, Thierry Reding, kernel,
	Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, devicetree, Jonas Karlman, Rob Herring,
	Jernej Skrabec, Seung-Woo Kim

On Tue, 3 Dec 2019 20:02:23 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> Thanks for the patch.
> 
> On Tue, Dec 03, 2019 at 03:15:11PM +0100, Boris Brezillon wrote:
> > The [pre_]enable/[post_]disable hooks are passed the old atomic state.
> > Update the doc and rename the arguments to make it clear.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > ---
> > Changes in v4:
> > * Drop the doc update (Laurent)  
> 
> I was referring to the doc updates in drm_bridge.h only. Is there a
> reason you dropped the changes from drm_bridge.c ? I think those were
> good.

Oops, that's a mistake. I'll fix it before applying.

> 
> > * Add Rbs
> > 
> > Changes in v3:
> > * New patch
> > ---
> >  include/drm/drm_bridge.h | 8 ++++----
> >  1 file changed, 4 insertions(+), 4 deletions(-)
> > 
> > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > index bfb0385163f1..d7d714023050 100644
> > --- a/include/drm/drm_bridge.h
> > +++ b/include/drm/drm_bridge.h
> > @@ -263,7 +263,7 @@ struct drm_bridge_funcs {
> >  	 * The @atomic_pre_enable callback is optional.
> >  	 */
> >  	void (*atomic_pre_enable)(struct drm_bridge *bridge,
> > -				  struct drm_atomic_state *state);
> > +				  struct drm_atomic_state *old_state);
> >  
> >  	/**
> >  	 * @atomic_enable:
> > @@ -288,7 +288,7 @@ struct drm_bridge_funcs {
> >  	 * The @atomic_enable callback is optional.
> >  	 */
> >  	void (*atomic_enable)(struct drm_bridge *bridge,
> > -			      struct drm_atomic_state *state);
> > +			      struct drm_atomic_state *old_state);
> >  	/**
> >  	 * @atomic_disable:
> >  	 *
> > @@ -311,7 +311,7 @@ struct drm_bridge_funcs {
> >  	 * The @atomic_disable callback is optional.
> >  	 */
> >  	void (*atomic_disable)(struct drm_bridge *bridge,
> > -			       struct drm_atomic_state *state);
> > +			       struct drm_atomic_state *old_state);
> >  
> >  	/**
> >  	 * @atomic_post_disable:
> > @@ -337,7 +337,7 @@ struct drm_bridge_funcs {
> >  	 * The @atomic_post_disable callback is optional.
> >  	 */
> >  	void (*atomic_post_disable)(struct drm_bridge *bridge,
> > -				    struct drm_atomic_state *state);
> > +				    struct drm_atomic_state *old_state);
> >  };
> >  
> >  /**  
> 

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
  2019-12-03 18:17     ` Laurent Pinchart
@ 2019-12-04  9:03       ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-04  9:03 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Neil Armstrong, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree, Eric Anholt

On Tue, 3 Dec 2019 20:17:05 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> Thank you for the patch.
> 
> On Tue, Dec 03, 2019 at 03:15:12PM +0100, Boris Brezillon wrote:
> > One of the last remaining objects to not have its atomic state.
> > 
> > This is being motivated by our attempt to support runtime bus-format
> > negotiation between elements of the bridge chain.
> > This patch just paves the road for such a feature by adding a new
> > drm_bridge_state object inheriting from drm_private_obj so we can
> > re-use some of the existing state initialization/tracking logic.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > ---
> > Changes in v4:
> > * Fix the doc
> > * Kill default helpers (inlined)  
> 
> I liked the default helpers, inlining their content makes the code more
> difficult to follow in my opinion.

I'll go back to this approach then. Should I keep the original helper
names even though they're not globally visible (and should probably
never be)?


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
@ 2019-12-04  9:03       ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-04  9:03 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Mark Rutland, Neil Armstrong, dri-devel, Thierry Reding, kernel,
	Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, devicetree, Jonas Karlman, Rob Herring,
	Jernej Skrabec, Seung-Woo Kim

On Tue, 3 Dec 2019 20:17:05 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> Thank you for the patch.
> 
> On Tue, Dec 03, 2019 at 03:15:12PM +0100, Boris Brezillon wrote:
> > One of the last remaining objects to not have its atomic state.
> > 
> > This is being motivated by our attempt to support runtime bus-format
> > negotiation between elements of the bridge chain.
> > This patch just paves the road for such a feature by adding a new
> > drm_bridge_state object inheriting from drm_private_obj so we can
> > re-use some of the existing state initialization/tracking logic.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > ---
> > Changes in v4:
> > * Fix the doc
> > * Kill default helpers (inlined)  
> 
> I liked the default helpers, inlining their content makes the code more
> difficult to follow in my opinion.

I'll go back to this approach then. Should I keep the original helper
names even though they're not globally visible (and should probably
never be)?

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 00/11] drm: Add support for bus-format negotiation
  2019-12-03 18:19   ` Laurent Pinchart
@ 2019-12-04  9:09     ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-04  9:09 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Neil Armstrong, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree, Eric Anholt

On Tue, 3 Dec 2019 20:19:24 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> On Tue, Dec 03, 2019 at 03:15:04PM +0100, Boris Brezillon wrote:
> > This patch series aims at adding support for runtime bus-format
> > negotiation between all elements of the
> > 'encoder -> bridges -> connector/display' section of the pipeline.
> > 
> > In order to support that, we need drm bridges to fully take part in the
> > atomic state validation process, which requires adding a
> > drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
> > Once those basic building blocks are in place, we can add new hooks to
> > allow bus format negotiation (those are called just before  
> > ->atomic_check()). The bus format selection is done at runtime by  
> > testing all possible combinations across the whole bridge chain until
> > one is reported to work.
> > 
> > No Major changes in this v4. I think I addressed all comments I got
> > from Neil and Laurent (thanks for the detailed reviews BTW). Note that
> > this version only contains core changes. Once those changes are merged
> > I'll send the imx/panel/lvds-codec specific bits.  
> 
> I think it would make sense to fast-track patches 01 to 07 (a bit
> selfishly as I would have a need for them in omapdrm :-))

Will do that end of this week.

>, but starting
> from 08 I wonder if it wouldn't make more sense to merge them with the
> imx/panel/lvds-codec bits to have a user.

I'd really like to see that happen early in this release cycle so we
can catch bugs before those commits reach Linus' tree, and the
lvds-codec changes depend on your PR. Can we make sure this one is
merged just after the MW has closed and the drm-next -> drm-misc-next
back merge done just after that?

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 00/11] drm: Add support for bus-format negotiation
@ 2019-12-04  9:09     ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-04  9:09 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Mark Rutland, Neil Armstrong, dri-devel, Thierry Reding, kernel,
	Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, devicetree, Jonas Karlman, Rob Herring,
	Jernej Skrabec, Seung-Woo Kim

On Tue, 3 Dec 2019 20:19:24 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> On Tue, Dec 03, 2019 at 03:15:04PM +0100, Boris Brezillon wrote:
> > This patch series aims at adding support for runtime bus-format
> > negotiation between all elements of the
> > 'encoder -> bridges -> connector/display' section of the pipeline.
> > 
> > In order to support that, we need drm bridges to fully take part in the
> > atomic state validation process, which requires adding a
> > drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
> > Once those basic building blocks are in place, we can add new hooks to
> > allow bus format negotiation (those are called just before  
> > ->atomic_check()). The bus format selection is done at runtime by  
> > testing all possible combinations across the whole bridge chain until
> > one is reported to work.
> > 
> > No Major changes in this v4. I think I addressed all comments I got
> > from Neil and Laurent (thanks for the detailed reviews BTW). Note that
> > this version only contains core changes. Once those changes are merged
> > I'll send the imx/panel/lvds-codec specific bits.  
> 
> I think it would make sense to fast-track patches 01 to 07 (a bit
> selfishly as I would have a need for them in omapdrm :-))

Will do that end of this week.

>, but starting
> from 08 I wonder if it wouldn't make more sense to merge them with the
> imx/panel/lvds-codec bits to have a user.

I'd really like to see that happen early in this release cycle so we
can catch bugs before those commits reach Linus' tree, and the
lvds-codec changes depend on your PR. Can we make sure this one is
merged just after the MW has closed and the drm-next -> drm-misc-next
back merge done just after that?
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
  2019-12-04  9:03       ` Boris Brezillon
@ 2019-12-04  9:12         ` Laurent Pinchart
  -1 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-04  9:12 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Neil Armstrong, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree, Eric Anholt

Hi Boris,

On Wed, Dec 04, 2019 at 10:03:02AM +0100, Boris Brezillon wrote:
> On Tue, 3 Dec 2019 20:17:05 +0200 Laurent Pinchart wrote:
> > On Tue, Dec 03, 2019 at 03:15:12PM +0100, Boris Brezillon wrote:
> > > One of the last remaining objects to not have its atomic state.
> > > 
> > > This is being motivated by our attempt to support runtime bus-format
> > > negotiation between elements of the bridge chain.
> > > This patch just paves the road for such a feature by adding a new
> > > drm_bridge_state object inheriting from drm_private_obj so we can
> > > re-use some of the existing state initialization/tracking logic.
> > > 
> > > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > > ---
> > > Changes in v4:
> > > * Fix the doc
> > > * Kill default helpers (inlined)  
> > 
> > I liked the default helpers, inlining their content makes the code more
> > difficult to follow in my opinion.
> 
> I'll go back to this approach then. Should I keep the original helper
> names even though they're not globally visible (and should probably
> never be)?

I agree they should probably never be visible, and I trust your
judgement on naming. Please double-check the documentation though, to
ensure that it matches the implementation.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
@ 2019-12-04  9:12         ` Laurent Pinchart
  0 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-04  9:12 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Mark Rutland, Neil Armstrong, dri-devel, Thierry Reding, kernel,
	Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, devicetree, Jonas Karlman, Rob Herring,
	Jernej Skrabec, Seung-Woo Kim

Hi Boris,

On Wed, Dec 04, 2019 at 10:03:02AM +0100, Boris Brezillon wrote:
> On Tue, 3 Dec 2019 20:17:05 +0200 Laurent Pinchart wrote:
> > On Tue, Dec 03, 2019 at 03:15:12PM +0100, Boris Brezillon wrote:
> > > One of the last remaining objects to not have its atomic state.
> > > 
> > > This is being motivated by our attempt to support runtime bus-format
> > > negotiation between elements of the bridge chain.
> > > This patch just paves the road for such a feature by adding a new
> > > drm_bridge_state object inheriting from drm_private_obj so we can
> > > re-use some of the existing state initialization/tracking logic.
> > > 
> > > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > > ---
> > > Changes in v4:
> > > * Fix the doc
> > > * Kill default helpers (inlined)  
> > 
> > I liked the default helpers, inlining their content makes the code more
> > difficult to follow in my opinion.
> 
> I'll go back to this approach then. Should I keep the original helper
> names even though they're not globally visible (and should probably
> never be)?

I agree they should probably never be visible, and I trust your
judgement on naming. Please double-check the documentation though, to
ensure that it matches the implementation.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 00/11] drm: Add support for bus-format negotiation
  2019-12-04  9:09     ` Boris Brezillon
@ 2019-12-04  9:15       ` Laurent Pinchart
  -1 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-04  9:15 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Neil Armstrong, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree, Eric Anholt

Hi Boris,

On Wed, Dec 04, 2019 at 10:09:57AM +0100, Boris Brezillon wrote:
> On Tue, 3 Dec 2019 20:19:24 +0200 Laurent Pinchart wrote:
> > On Tue, Dec 03, 2019 at 03:15:04PM +0100, Boris Brezillon wrote:
> > > This patch series aims at adding support for runtime bus-format
> > > negotiation between all elements of the
> > > 'encoder -> bridges -> connector/display' section of the pipeline.
> > > 
> > > In order to support that, we need drm bridges to fully take part in the
> > > atomic state validation process, which requires adding a
> > > drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
> > > Once those basic building blocks are in place, we can add new hooks to
> > > allow bus format negotiation (those are called just before  
> > > ->atomic_check()). The bus format selection is done at runtime by  
> > > testing all possible combinations across the whole bridge chain until
> > > one is reported to work.
> > > 
> > > No Major changes in this v4. I think I addressed all comments I got
> > > from Neil and Laurent (thanks for the detailed reviews BTW). Note that
> > > this version only contains core changes. Once those changes are merged
> > > I'll send the imx/panel/lvds-codec specific bits.  
> > 
> > I think it would make sense to fast-track patches 01 to 07 (a bit
> > selfishly as I would have a need for them in omapdrm :-))
> 
> Will do that end of this week.

Thank you.

> >, but starting
> > from 08 I wonder if it wouldn't make more sense to merge them with the
> > imx/panel/lvds-codec bits to have a user.
> 
> I'd really like to see that happen early in this release cycle so we
> can catch bugs before those commits reach Linus' tree,

I have no issue merging all your patches early in the release cycle,
including the imx/panel/lvds-codec bits. Given the rebase required for
lvds-codec it may take a bit of time though, so I'm OK if you prefer
merging the core first, but I'm not sure it will help catching bugs if
it's unused :-)

> and the
> lvds-codec changes depend on your PR. Can we make sure this one is
> merged just after the MW has closed and the drm-next -> drm-misc-next
> back merge done just after that?

I've sent the pull request already, as soon as Dave or Daniel handles
it, you'll have the necessary dependencies.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 00/11] drm: Add support for bus-format negotiation
@ 2019-12-04  9:15       ` Laurent Pinchart
  0 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-04  9:15 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Mark Rutland, Neil Armstrong, dri-devel, Thierry Reding, kernel,
	Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, devicetree, Jonas Karlman, Rob Herring,
	Jernej Skrabec, Seung-Woo Kim

Hi Boris,

On Wed, Dec 04, 2019 at 10:09:57AM +0100, Boris Brezillon wrote:
> On Tue, 3 Dec 2019 20:19:24 +0200 Laurent Pinchart wrote:
> > On Tue, Dec 03, 2019 at 03:15:04PM +0100, Boris Brezillon wrote:
> > > This patch series aims at adding support for runtime bus-format
> > > negotiation between all elements of the
> > > 'encoder -> bridges -> connector/display' section of the pipeline.
> > > 
> > > In order to support that, we need drm bridges to fully take part in the
> > > atomic state validation process, which requires adding a
> > > drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
> > > Once those basic building blocks are in place, we can add new hooks to
> > > allow bus format negotiation (those are called just before  
> > > ->atomic_check()). The bus format selection is done at runtime by  
> > > testing all possible combinations across the whole bridge chain until
> > > one is reported to work.
> > > 
> > > No Major changes in this v4. I think I addressed all comments I got
> > > from Neil and Laurent (thanks for the detailed reviews BTW). Note that
> > > this version only contains core changes. Once those changes are merged
> > > I'll send the imx/panel/lvds-codec specific bits.  
> > 
> > I think it would make sense to fast-track patches 01 to 07 (a bit
> > selfishly as I would have a need for them in omapdrm :-))
> 
> Will do that end of this week.

Thank you.

> >, but starting
> > from 08 I wonder if it wouldn't make more sense to merge them with the
> > imx/panel/lvds-codec bits to have a user.
> 
> I'd really like to see that happen early in this release cycle so we
> can catch bugs before those commits reach Linus' tree,

I have no issue merging all your patches early in the release cycle,
including the imx/panel/lvds-codec bits. Given the rebase required for
lvds-codec it may take a bit of time though, so I'm OK if you prefer
merging the core first, but I'm not sure it will help catching bugs if
it's unused :-)

> and the
> lvds-codec changes depend on your PR. Can we make sure this one is
> merged just after the MW has closed and the drm-next -> drm-misc-next
> back merge done just after that?

I've sent the pull request already, as soon as Dave or Daniel handles
it, you'll have the necessary dependencies.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
  2019-12-04  9:12         ` Laurent Pinchart
@ 2019-12-04  9:42           ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-04  9:42 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Neil Armstrong, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree, Eric Anholt

On Wed, 4 Dec 2019 11:12:55 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> On Wed, Dec 04, 2019 at 10:03:02AM +0100, Boris Brezillon wrote:
> > On Tue, 3 Dec 2019 20:17:05 +0200 Laurent Pinchart wrote:  
> > > On Tue, Dec 03, 2019 at 03:15:12PM +0100, Boris Brezillon wrote:  
> > > > One of the last remaining objects to not have its atomic state.
> > > > 
> > > > This is being motivated by our attempt to support runtime bus-format
> > > > negotiation between elements of the bridge chain.
> > > > This patch just paves the road for such a feature by adding a new
> > > > drm_bridge_state object inheriting from drm_private_obj so we can
> > > > re-use some of the existing state initialization/tracking logic.
> > > > 
> > > > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > > > ---
> > > > Changes in v4:
> > > > * Fix the doc
> > > > * Kill default helpers (inlined)    
> > > 
> > > I liked the default helpers, inlining their content makes the code more
> > > difficult to follow in my opinion.  
> > 
> > I'll go back to this approach then. Should I keep the original helper
> > names even though they're not globally visible (and should probably
> > never be)?  
> 
> I agree they should probably never be visible, and I trust your
> judgement on naming. Please double-check the documentation though, to
> ensure that it matches the implementation.
> 

Is there any point keeping the documentation if they're not exposed?

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
@ 2019-12-04  9:42           ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-04  9:42 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Mark Rutland, Neil Armstrong, dri-devel, Thierry Reding, kernel,
	Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, devicetree, Jonas Karlman, Rob Herring,
	Jernej Skrabec, Seung-Woo Kim

On Wed, 4 Dec 2019 11:12:55 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> On Wed, Dec 04, 2019 at 10:03:02AM +0100, Boris Brezillon wrote:
> > On Tue, 3 Dec 2019 20:17:05 +0200 Laurent Pinchart wrote:  
> > > On Tue, Dec 03, 2019 at 03:15:12PM +0100, Boris Brezillon wrote:  
> > > > One of the last remaining objects to not have its atomic state.
> > > > 
> > > > This is being motivated by our attempt to support runtime bus-format
> > > > negotiation between elements of the bridge chain.
> > > > This patch just paves the road for such a feature by adding a new
> > > > drm_bridge_state object inheriting from drm_private_obj so we can
> > > > re-use some of the existing state initialization/tracking logic.
> > > > 
> > > > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > > > ---
> > > > Changes in v4:
> > > > * Fix the doc
> > > > * Kill default helpers (inlined)    
> > > 
> > > I liked the default helpers, inlining their content makes the code more
> > > difficult to follow in my opinion.  
> > 
> > I'll go back to this approach then. Should I keep the original helper
> > names even though they're not globally visible (and should probably
> > never be)?  
> 
> I agree they should probably never be visible, and I trust your
> judgement on naming. Please double-check the documentation though, to
> ensure that it matches the implementation.
> 

Is there any point keeping the documentation if they're not exposed?
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
  2019-12-04  9:42           ` Boris Brezillon
@ 2019-12-04 10:38             ` Laurent Pinchart
  -1 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-04 10:38 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Neil Armstrong, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree, Eric Anholt

Hi Boris,

On Wed, Dec 04, 2019 at 10:42:07AM +0100, Boris Brezillon wrote:
> On Wed, 4 Dec 2019 11:12:55 +0200 Laurent Pinchart wrote:
> > On Wed, Dec 04, 2019 at 10:03:02AM +0100, Boris Brezillon wrote:
> > > On Tue, 3 Dec 2019 20:17:05 +0200 Laurent Pinchart wrote:  
> > > > On Tue, Dec 03, 2019 at 03:15:12PM +0100, Boris Brezillon wrote:  
> > > > > One of the last remaining objects to not have its atomic state.
> > > > > 
> > > > > This is being motivated by our attempt to support runtime bus-format
> > > > > negotiation between elements of the bridge chain.
> > > > > This patch just paves the road for such a feature by adding a new
> > > > > drm_bridge_state object inheriting from drm_private_obj so we can
> > > > > re-use some of the existing state initialization/tracking logic.
> > > > > 
> > > > > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > > > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > > > > ---
> > > > > Changes in v4:
> > > > > * Fix the doc
> > > > > * Kill default helpers (inlined)    
> > > > 
> > > > I liked the default helpers, inlining their content makes the code more
> > > > difficult to follow in my opinion.  
> > > 
> > > I'll go back to this approach then. Should I keep the original helper
> > > names even though they're not globally visible (and should probably
> > > never be)?  
> > 
> > I agree they should probably never be visible, and I trust your
> > judgement on naming. Please double-check the documentation though, to
> > ensure that it matches the implementation.
> 
> Is there any point keeping the documentation if they're not exposed?

I'll let you decide on that, depending on if the documentation could
bring value or if the functions would be so trivial that it would be
overkill.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object
@ 2019-12-04 10:38             ` Laurent Pinchart
  0 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-04 10:38 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Mark Rutland, Neil Armstrong, dri-devel, Thierry Reding, kernel,
	Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, devicetree, Jonas Karlman, Rob Herring,
	Jernej Skrabec, Seung-Woo Kim

Hi Boris,

On Wed, Dec 04, 2019 at 10:42:07AM +0100, Boris Brezillon wrote:
> On Wed, 4 Dec 2019 11:12:55 +0200 Laurent Pinchart wrote:
> > On Wed, Dec 04, 2019 at 10:03:02AM +0100, Boris Brezillon wrote:
> > > On Tue, 3 Dec 2019 20:17:05 +0200 Laurent Pinchart wrote:  
> > > > On Tue, Dec 03, 2019 at 03:15:12PM +0100, Boris Brezillon wrote:  
> > > > > One of the last remaining objects to not have its atomic state.
> > > > > 
> > > > > This is being motivated by our attempt to support runtime bus-format
> > > > > negotiation between elements of the bridge chain.
> > > > > This patch just paves the road for such a feature by adding a new
> > > > > drm_bridge_state object inheriting from drm_private_obj so we can
> > > > > re-use some of the existing state initialization/tracking logic.
> > > > > 
> > > > > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > > > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > > > > ---
> > > > > Changes in v4:
> > > > > * Fix the doc
> > > > > * Kill default helpers (inlined)    
> > > > 
> > > > I liked the default helpers, inlining their content makes the code more
> > > > difficult to follow in my opinion.  
> > > 
> > > I'll go back to this approach then. Should I keep the original helper
> > > names even though they're not globally visible (and should probably
> > > never be)?  
> > 
> > I agree they should probably never be visible, and I trust your
> > judgement on naming. Please double-check the documentation though, to
> > ensure that it matches the implementation.
> 
> Is there any point keeping the documentation if they're not exposed?

I'll let you decide on that, depending on if the documentation could
bring value or if the functions would be so trivial that it would be
overkill.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 00/11] drm: Add support for bus-format negotiation
  2019-12-03 18:19   ` Laurent Pinchart
@ 2019-12-04 13:43     ` Neil Armstrong
  -1 siblings, 0 replies; 84+ messages in thread
From: Neil Armstrong @ 2019-12-04 13:43 UTC (permalink / raw)
  To: Laurent Pinchart, Boris Brezillon
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Jonas Karlman, Jernej Skrabec, Rob Herring, Mark Rutland,
	devicetree, Eric Anholt

On 03/12/2019 19:19, Laurent Pinchart wrote:
> Hi Boris,
> 
> On Tue, Dec 03, 2019 at 03:15:04PM +0100, Boris Brezillon wrote:
>> This patch series aims at adding support for runtime bus-format
>> negotiation between all elements of the
>> 'encoder -> bridges -> connector/display' section of the pipeline.
>>
>> In order to support that, we need drm bridges to fully take part in the
>> atomic state validation process, which requires adding a
>> drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
>> Once those basic building blocks are in place, we can add new hooks to
>> allow bus format negotiation (those are called just before
>> ->atomic_check()). The bus format selection is done at runtime by
>> testing all possible combinations across the whole bridge chain until
>> one is reported to work.
>>
>> No Major changes in this v4. I think I addressed all comments I got
>> from Neil and Laurent (thanks for the detailed reviews BTW). Note that
>> this version only contains core changes. Once those changes are merged
>> I'll send the imx/panel/lvds-codec specific bits.
> 
> I think it would make sense to fast-track patches 01 to 07 (a bit
> selfishly as I would have a need for them in omapdrm :-)), but starting
> from 08 I wonder if it wouldn't make more sense to merge them with the
> imx/panel/lvds-codec bits to have a user.

@Boris you can push when Laurent's PR has been merged and backmerged to drm-misc-next

I have a patchset being an user of patches 8-11, so the lvds-codec stuff won't be the
only user of this !
I'll post a version ASAP to validate the v4 serie on the dw-hdmi usercase.

Thanks !

Neil

> 
>> A more detailed changelog is provided in each patch.
>>
>> This patch series is also available here [1].
>>
>> Thanks,
>>
>> Boris
>>
>> [1]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v4
>>
>> Boris Brezillon (11):
>>   drm/bridge: Rename bridge helpers targeting a bridge chain
>>   drm/bridge: Introduce drm_bridge_get_next_bridge()
>>   drm: Stop accessing encoder->bridge directly
>>   drm/bridge: Make the bridge chain a double-linked list
>>   drm/bridge: Add the drm_for_each_bridge_in_chain() helper
>>   drm/bridge: Add the drm_bridge_get_prev_bridge() helper
>>   drm/bridge: Clarify the atomic enable/disable hooks semantics
>>   drm/bridge: Add a drm_bridge_state object
>>   drm/bridge: Patch atomic hooks to take a drm_bridge_state
>>   drm/bridge: Add an ->atomic_check() hook
>>   drm/bridge: Add the necessary bits to support bus format negotiation
>>
>>  .../drm/bridge/analogix/analogix_dp_core.c    |  41 +-
>>  drivers/gpu/drm/drm_atomic.c                  |  39 +
>>  drivers/gpu/drm/drm_atomic_helper.c           |  53 +-
>>  drivers/gpu/drm/drm_bridge.c                  | 756 +++++++++++++++---
>>  drivers/gpu/drm/drm_encoder.c                 |  15 +-
>>  drivers/gpu/drm/drm_probe_helper.c            |   4 +-
>>  drivers/gpu/drm/exynos/exynos_drm_dsi.c       |  13 +-
>>  drivers/gpu/drm/mediatek/mtk_hdmi.c           |   8 +-
>>  drivers/gpu/drm/msm/edp/edp_bridge.c          |  10 +-
>>  drivers/gpu/drm/omapdrm/omap_drv.c            |   4 +-
>>  drivers/gpu/drm/omapdrm/omap_encoder.c        |   3 +-
>>  drivers/gpu/drm/rcar-du/rcar_du_crtc.c        |  10 +-
>>  drivers/gpu/drm/vc4/vc4_dsi.c                 |  18 +-
>>  include/drm/drm_atomic.h                      |   3 +
>>  include/drm/drm_bridge.h                      | 404 +++++++++-
>>  include/drm/drm_encoder.h                     |   7 +-
>>  16 files changed, 1174 insertions(+), 214 deletions(-)
> 


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 00/11] drm: Add support for bus-format negotiation
@ 2019-12-04 13:43     ` Neil Armstrong
  0 siblings, 0 replies; 84+ messages in thread
From: Neil Armstrong @ 2019-12-04 13:43 UTC (permalink / raw)
  To: Laurent Pinchart, Boris Brezillon
  Cc: Mark Rutland, dri-devel, Thierry Reding, kernel, Sam Ravnborg,
	Nikita Yushchenko, Andrey Smirnov, Kyungmin Park, Chris Healy,
	devicetree, Jonas Karlman, Rob Herring, Jernej Skrabec,
	Seung-Woo Kim

On 03/12/2019 19:19, Laurent Pinchart wrote:
> Hi Boris,
> 
> On Tue, Dec 03, 2019 at 03:15:04PM +0100, Boris Brezillon wrote:
>> This patch series aims at adding support for runtime bus-format
>> negotiation between all elements of the
>> 'encoder -> bridges -> connector/display' section of the pipeline.
>>
>> In order to support that, we need drm bridges to fully take part in the
>> atomic state validation process, which requires adding a
>> drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
>> Once those basic building blocks are in place, we can add new hooks to
>> allow bus format negotiation (those are called just before
>> ->atomic_check()). The bus format selection is done at runtime by
>> testing all possible combinations across the whole bridge chain until
>> one is reported to work.
>>
>> No Major changes in this v4. I think I addressed all comments I got
>> from Neil and Laurent (thanks for the detailed reviews BTW). Note that
>> this version only contains core changes. Once those changes are merged
>> I'll send the imx/panel/lvds-codec specific bits.
> 
> I think it would make sense to fast-track patches 01 to 07 (a bit
> selfishly as I would have a need for them in omapdrm :-)), but starting
> from 08 I wonder if it wouldn't make more sense to merge them with the
> imx/panel/lvds-codec bits to have a user.

@Boris you can push when Laurent's PR has been merged and backmerged to drm-misc-next

I have a patchset being an user of patches 8-11, so the lvds-codec stuff won't be the
only user of this !
I'll post a version ASAP to validate the v4 serie on the dw-hdmi usercase.

Thanks !

Neil

> 
>> A more detailed changelog is provided in each patch.
>>
>> This patch series is also available here [1].
>>
>> Thanks,
>>
>> Boris
>>
>> [1]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v4
>>
>> Boris Brezillon (11):
>>   drm/bridge: Rename bridge helpers targeting a bridge chain
>>   drm/bridge: Introduce drm_bridge_get_next_bridge()
>>   drm: Stop accessing encoder->bridge directly
>>   drm/bridge: Make the bridge chain a double-linked list
>>   drm/bridge: Add the drm_for_each_bridge_in_chain() helper
>>   drm/bridge: Add the drm_bridge_get_prev_bridge() helper
>>   drm/bridge: Clarify the atomic enable/disable hooks semantics
>>   drm/bridge: Add a drm_bridge_state object
>>   drm/bridge: Patch atomic hooks to take a drm_bridge_state
>>   drm/bridge: Add an ->atomic_check() hook
>>   drm/bridge: Add the necessary bits to support bus format negotiation
>>
>>  .../drm/bridge/analogix/analogix_dp_core.c    |  41 +-
>>  drivers/gpu/drm/drm_atomic.c                  |  39 +
>>  drivers/gpu/drm/drm_atomic_helper.c           |  53 +-
>>  drivers/gpu/drm/drm_bridge.c                  | 756 +++++++++++++++---
>>  drivers/gpu/drm/drm_encoder.c                 |  15 +-
>>  drivers/gpu/drm/drm_probe_helper.c            |   4 +-
>>  drivers/gpu/drm/exynos/exynos_drm_dsi.c       |  13 +-
>>  drivers/gpu/drm/mediatek/mtk_hdmi.c           |   8 +-
>>  drivers/gpu/drm/msm/edp/edp_bridge.c          |  10 +-
>>  drivers/gpu/drm/omapdrm/omap_drv.c            |   4 +-
>>  drivers/gpu/drm/omapdrm/omap_encoder.c        |   3 +-
>>  drivers/gpu/drm/rcar-du/rcar_du_crtc.c        |  10 +-
>>  drivers/gpu/drm/vc4/vc4_dsi.c                 |  18 +-
>>  include/drm/drm_atomic.h                      |   3 +
>>  include/drm/drm_bridge.h                      | 404 +++++++++-
>>  include/drm/drm_encoder.h                     |   7 +-
>>  16 files changed, 1174 insertions(+), 214 deletions(-)
> 

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 00/11] drm: Add support for bus-format negotiation
  2019-12-03 18:19   ` Laurent Pinchart
@ 2019-12-09  9:43     ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-09  9:43 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel, Lucas Stach, Chris Healy, Andrey Smirnov,
	Nikita Yushchenko, kernel, Daniel Vetter, Inki Dae,
	Joonyoung Shim, Seung-Woo Kim, Kyungmin Park, Thierry Reding,
	Sam Ravnborg, Philipp Zabel, Rob Clark, Andrzej Hajda,
	Neil Armstrong, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, Eric Anholt

On Tue, 3 Dec 2019 20:19:24 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> On Tue, Dec 03, 2019 at 03:15:04PM +0100, Boris Brezillon wrote:
> > This patch series aims at adding support for runtime bus-format
> > negotiation between all elements of the
> > 'encoder -> bridges -> connector/display' section of the pipeline.
> > 
> > In order to support that, we need drm bridges to fully take part in the
> > atomic state validation process, which requires adding a
> > drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
> > Once those basic building blocks are in place, we can add new hooks to
> > allow bus format negotiation (those are called just before  
> > ->atomic_check()). The bus format selection is done at runtime by  
> > testing all possible combinations across the whole bridge chain until
> > one is reported to work.
> > 
> > No Major changes in this v4. I think I addressed all comments I got
> > from Neil and Laurent (thanks for the detailed reviews BTW). Note that
> > this version only contains core changes. Once those changes are merged
> > I'll send the imx/panel/lvds-codec specific bits.  
> 
> I think it would make sense to fast-track patches 01 to 07 (a bit
> selfishly as I would have a need for them in omapdrm :-)),

I just queued patches 1 to 7 to drm-misc-next.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 00/11] drm: Add support for bus-format negotiation
@ 2019-12-09  9:43     ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-09  9:43 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Mark Rutland, Neil Armstrong, dri-devel, Thierry Reding, kernel,
	Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, Jonas Karlman, Rob Herring, Jernej Skrabec,
	Seung-Woo Kim

On Tue, 3 Dec 2019 20:19:24 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> On Tue, Dec 03, 2019 at 03:15:04PM +0100, Boris Brezillon wrote:
> > This patch series aims at adding support for runtime bus-format
> > negotiation between all elements of the
> > 'encoder -> bridges -> connector/display' section of the pipeline.
> > 
> > In order to support that, we need drm bridges to fully take part in the
> > atomic state validation process, which requires adding a
> > drm_bridge_state and a new drm_bridge_funcs.atomic_check() hook.
> > Once those basic building blocks are in place, we can add new hooks to
> > allow bus format negotiation (those are called just before  
> > ->atomic_check()). The bus format selection is done at runtime by  
> > testing all possible combinations across the whole bridge chain until
> > one is reported to work.
> > 
> > No Major changes in this v4. I think I addressed all comments I got
> > from Neil and Laurent (thanks for the detailed reviews BTW). Note that
> > this version only contains core changes. Once those changes are merged
> > I'll send the imx/panel/lvds-codec specific bits.  
> 
> I think it would make sense to fast-track patches 01 to 07 (a bit
> selfishly as I would have a need for them in omapdrm :-)),

I just queued patches 1 to 7 to drm-misc-next.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-03 14:15   ` Boris Brezillon
@ 2019-12-16 13:54     ` Marek Szyprowski
  -1 siblings, 0 replies; 84+ messages in thread
From: Marek Szyprowski @ 2019-12-16 13:54 UTC (permalink / raw)
  To: Boris Brezillon, dri-devel
  Cc: Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Nikita Yushchenko, Andrey Smirnov,
	Kyungmin Park, Chris Healy, devicetree, Jonas Karlman,
	Rob Herring, Jernej Skrabec, Seung-Woo Kim, Andrzej Hajda

Hi All,

On 03.12.2019 15:15, Boris Brezillon wrote:
> So that each element in the chain can easily access its predecessor.
> This will be needed to support bus format negotiation between elements
> of the bridge chain.
>
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

I've noticed that this patch got merged to linux-next as commit 
05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of 
Samsung Exynos5250-based Arndale board. Booting stops after following 
messages:

[drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
[drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
[drm] No driver support for vblank timestamp query.
[drm] Cannot find any crtc or sizes
[drm] Cannot find any crtc or sizes
[drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0

I will try to debug this and provide more information soon.

> ---
> Changes in v4:
> * Simplify the drm_bridge_attach() logic
> * Fix list iteration bugs
> * Patch VC4 and Exynos DSI drivers to match core changes
> * Add R-bs
>
> Changes in v3:
> * None
>
> Changes in v2:
> * Adjust things to the "dummy encoder bridge" change (patch 2 in this
>    series)
> ---
>   drivers/gpu/drm/drm_bridge.c            | 171 +++++++++++++++---------
>   drivers/gpu/drm/drm_encoder.c           |  16 +--
>   drivers/gpu/drm/exynos/exynos_drm_dsi.c |   5 +-
>   drivers/gpu/drm/vc4/vc4_dsi.c           |  10 +-
>   include/drm/drm_bridge.h                |  12 +-
>   include/drm/drm_encoder.h               |   7 +-
>   6 files changed, 143 insertions(+), 78 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index 54c874493c57..b6517b4fa3d1 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -55,7 +55,7 @@
>    * just provide additional hooks to get the desired output at the end of the
>    * encoder chain.
>    *
> - * Bridges can also be chained up using the &drm_bridge.next pointer.
> + * Bridges can also be chained up using the &drm_bridge.chain_node field.
>    *
>    * Both legacy CRTC helpers and the new atomic modeset helpers support bridges.
>    */
> @@ -128,20 +128,21 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>   	bridge->dev = encoder->dev;
>   	bridge->encoder = encoder;
>   
> +	if (previous)
> +		list_add(&bridge->chain_node, &previous->chain_node);
> +	else
> +		list_add(&bridge->chain_node, &encoder->bridge_chain);
> +
>   	if (bridge->funcs->attach) {
>   		ret = bridge->funcs->attach(bridge);
>   		if (ret < 0) {
> +			list_del(&bridge->chain_node);
>   			bridge->dev = NULL;
>   			bridge->encoder = NULL;
>   			return ret;
>   		}
>   	}
>   
> -	if (previous)
> -		previous->next = bridge;
> -	else
> -		encoder->bridge = bridge;
> -
>   	return 0;
>   }
>   EXPORT_SYMBOL(drm_bridge_attach);
> @@ -157,6 +158,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
>   	if (bridge->funcs->detach)
>   		bridge->funcs->detach(bridge);
>   
> +	list_del(&bridge->chain_node);
>   	bridge->dev = NULL;
>   }
>   
> @@ -190,18 +192,21 @@ bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
>   				 const struct drm_display_mode *mode,
>   				 struct drm_display_mode *adjusted_mode)
>   {
> -	bool ret = true;
> +	struct drm_encoder *encoder;
>   
>   	if (!bridge)
>   		return true;
>   
> -	if (bridge->funcs->mode_fixup)
> -		ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (!bridge->funcs->mode_fixup)
> +			continue;
>   
> -	ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode,
> -						 adjusted_mode);
> +		if (!bridge->funcs->mode_fixup(bridge, mode, adjusted_mode))
> +			return false;
> +	}
>   
> -	return ret;
> +	return true;
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
>   
> @@ -224,18 +229,24 @@ enum drm_mode_status
>   drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
>   			    const struct drm_display_mode *mode)
>   {
> -	enum drm_mode_status ret = MODE_OK;
> +	struct drm_encoder *encoder;
>   
>   	if (!bridge)
> -		return ret;
> +		return MODE_OK;
>   
> -	if (bridge->funcs->mode_valid)
> -		ret = bridge->funcs->mode_valid(bridge, mode);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		enum drm_mode_status ret;
> +
> +		if (!bridge->funcs->mode_valid)
> +			continue;
>   
> -	if (ret != MODE_OK)
> -		return ret;
> +		ret = bridge->funcs->mode_valid(bridge, mode);
> +		if (ret != MODE_OK)
> +			return ret;
> +	}
>   
> -	return drm_bridge_chain_mode_valid(bridge->next, mode);
> +	return MODE_OK;
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
>   
> @@ -251,13 +262,20 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
>    */
>   void drm_bridge_chain_disable(struct drm_bridge *bridge)
>   {
> +	struct drm_encoder *encoder;
> +	struct drm_bridge *iter;
> +
>   	if (!bridge)
>   		return;
>   
> -	drm_bridge_chain_disable(bridge->next);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
> +		if (iter->funcs->disable)
> +			iter->funcs->disable(iter);
>   
> -	if (bridge->funcs->disable)
> -		bridge->funcs->disable(bridge);
> +		if (iter == bridge)
> +			break;
> +	}
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_disable);
>   
> @@ -274,13 +292,16 @@ EXPORT_SYMBOL(drm_bridge_chain_disable);
>    */
>   void drm_bridge_chain_post_disable(struct drm_bridge *bridge)
>   {
> +	struct drm_encoder *encoder;
> +
>   	if (!bridge)
>   		return;
>   
> -	if (bridge->funcs->post_disable)
> -		bridge->funcs->post_disable(bridge);
> -
> -	drm_bridge_chain_post_disable(bridge->next);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (bridge->funcs->post_disable)
> +			bridge->funcs->post_disable(bridge);
> +	}
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_post_disable);
>   
> @@ -300,13 +321,16 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
>   			       const struct drm_display_mode *mode,
>   			       const struct drm_display_mode *adjusted_mode)
>   {
> +	struct drm_encoder *encoder;
> +
>   	if (!bridge)
>   		return;
>   
> -	if (bridge->funcs->mode_set)
> -		bridge->funcs->mode_set(bridge, mode, adjusted_mode);
> -
> -	drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (bridge->funcs->mode_set)
> +			bridge->funcs->mode_set(bridge, mode, adjusted_mode);
> +	}
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_mode_set);
>   
> @@ -323,13 +347,17 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set);
>    */
>   void drm_bridge_chain_pre_enable(struct drm_bridge *bridge)
>   {
> +	struct drm_encoder *encoder;
> +	struct drm_bridge *iter;
> +
>   	if (!bridge)
>   		return;
>   
> -	drm_bridge_chain_pre_enable(bridge->next);
> -
> -	if (bridge->funcs->pre_enable)
> -		bridge->funcs->pre_enable(bridge);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
> +		if (iter->funcs->pre_enable)
> +			iter->funcs->pre_enable(iter);
> +	}
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
>   
> @@ -345,13 +373,16 @@ EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
>    */
>   void drm_bridge_chain_enable(struct drm_bridge *bridge)
>   {
> +	struct drm_encoder *encoder;
> +
>   	if (!bridge)
>   		return;
>   
> -	if (bridge->funcs->enable)
> -		bridge->funcs->enable(bridge);
> -
> -	drm_bridge_chain_enable(bridge->next);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (bridge->funcs->enable)
> +			bridge->funcs->enable(bridge);
> +	}
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_enable);
>   
> @@ -370,15 +401,22 @@ EXPORT_SYMBOL(drm_bridge_chain_enable);
>   void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
>   				     struct drm_atomic_state *state)
>   {
> +	struct drm_encoder *encoder;
> +	struct drm_bridge *iter;
> +
>   	if (!bridge)
>   		return;
>   
> -	drm_atomic_bridge_chain_disable(bridge->next, state);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
> +		if (iter->funcs->atomic_disable)
> +			iter->funcs->atomic_disable(iter, state);
> +		else if (iter->funcs->disable)
> +			iter->funcs->disable(iter);
>   
> -	if (bridge->funcs->atomic_disable)
> -		bridge->funcs->atomic_disable(bridge, state);
> -	else if (bridge->funcs->disable)
> -		bridge->funcs->disable(bridge);
> +		if (iter == bridge)
> +			break;
> +	}
>   }
>   EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
>   
> @@ -398,15 +436,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
>   void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
>   					  struct drm_atomic_state *state)
>   {
> +	struct drm_encoder *encoder;
> +
>   	if (!bridge)
>   		return;
>   
> -	if (bridge->funcs->atomic_post_disable)
> -		bridge->funcs->atomic_post_disable(bridge, state);
> -	else if (bridge->funcs->post_disable)
> -		bridge->funcs->post_disable(bridge);
> -
> -	drm_atomic_bridge_chain_post_disable(bridge->next, state);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (bridge->funcs->atomic_post_disable)
> +			bridge->funcs->atomic_post_disable(bridge, state);
> +		else if (bridge->funcs->post_disable)
> +			bridge->funcs->post_disable(bridge);
> +	}
>   }
>   EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
>   
> @@ -426,15 +467,22 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
>   void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
>   					struct drm_atomic_state *state)
>   {
> +	struct drm_encoder *encoder;
> +	struct drm_bridge *iter;
> +
>   	if (!bridge)
>   		return;
>   
> -	drm_atomic_bridge_chain_pre_enable(bridge->next, state);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
> +		if (iter->funcs->atomic_pre_enable)
> +			iter->funcs->atomic_pre_enable(iter, state);
> +		else if (iter->funcs->pre_enable)
> +			iter->funcs->pre_enable(iter);
>   
> -	if (bridge->funcs->atomic_pre_enable)
> -		bridge->funcs->atomic_pre_enable(bridge, state);
> -	else if (bridge->funcs->pre_enable)
> -		bridge->funcs->pre_enable(bridge);
> +		if (iter == bridge)
> +			break;
> +	}
>   }
>   EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
>   
> @@ -453,15 +501,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
>   void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
>   				    struct drm_atomic_state *state)
>   {
> +	struct drm_encoder *encoder;
> +
>   	if (!bridge)
>   		return;
>   
> -	if (bridge->funcs->atomic_enable)
> -		bridge->funcs->atomic_enable(bridge, state);
> -	else if (bridge->funcs->enable)
> -		bridge->funcs->enable(bridge);
> -
> -	drm_atomic_bridge_chain_enable(bridge->next, state);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (bridge->funcs->atomic_enable)
> +			bridge->funcs->atomic_enable(bridge, state);
> +		else if (bridge->funcs->enable)
> +			bridge->funcs->enable(bridge);
> +	}
>   }
>   EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
>   
> diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
> index a2cc7e7241a9..e555281f43d4 100644
> --- a/drivers/gpu/drm/drm_encoder.c
> +++ b/drivers/gpu/drm/drm_encoder.c
> @@ -140,6 +140,7 @@ int drm_encoder_init(struct drm_device *dev,
>   		goto out_put;
>   	}
>   
> +	INIT_LIST_HEAD(&encoder->bridge_chain);
>   	list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
>   	encoder->index = dev->mode_config.num_encoder++;
>   
> @@ -160,23 +161,16 @@ EXPORT_SYMBOL(drm_encoder_init);
>   void drm_encoder_cleanup(struct drm_encoder *encoder)
>   {
>   	struct drm_device *dev = encoder->dev;
> +	struct drm_bridge *bridge, *next;
>   
>   	/* Note that the encoder_list is considered to be static; should we
>   	 * remove the drm_encoder at runtime we would have to decrement all
>   	 * the indices on the drm_encoder after us in the encoder_list.
>   	 */
>   
> -	if (encoder->bridge) {
> -		struct drm_bridge *bridge;
> -		struct drm_bridge *next;
> -
> -		bridge = drm_bridge_chain_get_first_bridge(encoder);
> -		while (bridge) {
> -			next = drm_bridge_get_next_bridge(bridge);
> -			drm_bridge_detach(bridge);
> -			bridge = next;
> -		}
> -	}
> +	list_for_each_entry_safe(bridge, next, &encoder->bridge_chain,
> +				 chain_node)
> +		drm_bridge_detach(bridge);
>   
>   	drm_mode_object_unregister(dev, &encoder->base);
>   	kfree(encoder->name);
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> index d984097704b8..7de82e22252a 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> @@ -255,6 +255,7 @@ struct exynos_dsi {
>   	struct mipi_dsi_host dsi_host;
>   	struct drm_connector connector;
>   	struct drm_panel *panel;
> +	struct list_head bridge_chain;
>   	struct drm_bridge *out_bridge;
>   	struct device *dev;
>   
> @@ -1522,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>   	if (out_bridge) {
>   		drm_bridge_attach(encoder, out_bridge, NULL);
>   		dsi->out_bridge = out_bridge;
> -		encoder->bridge = NULL;
> +		list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
>   	} else {
>   		int ret = exynos_dsi_create_connector(encoder);
>   
> @@ -1588,6 +1589,7 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
>   		if (dsi->out_bridge->funcs->detach)
>   			dsi->out_bridge->funcs->detach(dsi->out_bridge);
>   		dsi->out_bridge = NULL;
> +		INIT_LIST_HEAD(&dsi->bridge_chain);
>   	}
>   
>   	if (drm->mode_config.poll_enabled)
> @@ -1735,6 +1737,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
>   	init_completion(&dsi->completed);
>   	spin_lock_init(&dsi->transfer_lock);
>   	INIT_LIST_HEAD(&dsi->transfer_list);
> +	INIT_LIST_HEAD(&dsi->bridge_chain);
>   
>   	dsi->dsi_host.ops = &exynos_dsi_ops;
>   	dsi->dsi_host.dev = dev;
> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index ff81b54ea281..6c5b80ad6154 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> @@ -499,6 +499,7 @@ struct vc4_dsi {
>   	struct mipi_dsi_host dsi_host;
>   	struct drm_encoder *encoder;
>   	struct drm_bridge *bridge;
> +	struct list_head bridge_chain;
>   
>   	void __iomem *regs;
>   
> @@ -1460,6 +1461,8 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>   				       GFP_KERNEL);
>   	if (!vc4_dsi_encoder)
>   		return -ENOMEM;
> +
> +	INIT_LIST_HEAD(&dsi->bridge_chain);
>   	vc4_dsi_encoder->base.type = VC4_ENCODER_TYPE_DSI1;
>   	vc4_dsi_encoder->dsi = dsi;
>   	dsi->encoder = &vc4_dsi_encoder->base.base;
> @@ -1610,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>   	 * from our driver, since we need to sequence them within the
>   	 * encoder's enable/disable paths.
>   	 */
> -	dsi->encoder->bridge = NULL;
> +	list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>   
>   	if (dsi->port == 0)
>   		vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
> @@ -1632,6 +1635,11 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>   	if (dsi->bridge)
>   		pm_runtime_disable(dev);
>   
> +	/*
> +	 * Restore the bridge_chain so the bridge detach procedure can happen
> +	 * normally.
> +	 */
> +	list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>   	vc4_dsi_encoder_destroy(dsi->encoder);
>   
>   	if (dsi->port == 1)
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index bd78c256b1ed..c118726469ee 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -384,8 +384,8 @@ struct drm_bridge {
>   	struct drm_device *dev;
>   	/** @encoder: encoder to which this bridge is connected */
>   	struct drm_encoder *encoder;
> -	/** @next: the next bridge in the encoder chain */
> -	struct drm_bridge *next;
> +	/** @chain_node: used to form a bridge chain */
> +	struct list_head chain_node;
>   #ifdef CONFIG_OF
>   	/** @of_node: device node pointer to the bridge */
>   	struct device_node *of_node;
> @@ -420,7 +420,10 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>   static inline struct drm_bridge *
>   drm_bridge_get_next_bridge(struct drm_bridge *bridge)
>   {
> -	return bridge->next;
> +	if (list_is_last(&bridge->chain_node, &bridge->encoder->bridge_chain))
> +		return NULL;
> +
> +	return list_next_entry(bridge, chain_node);
>   }
>   
>   /**
> @@ -434,7 +437,8 @@ drm_bridge_get_next_bridge(struct drm_bridge *bridge)
>   static inline struct drm_bridge *
>   drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder)
>   {
> -	return encoder->bridge;
> +	return list_first_entry_or_null(&encoder->bridge_chain,
> +					struct drm_bridge, chain_node);
>   }
>   
>   bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
> diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
> index f06164f44efe..5623994b6e9e 100644
> --- a/include/drm/drm_encoder.h
> +++ b/include/drm/drm_encoder.h
> @@ -172,7 +172,12 @@ struct drm_encoder {
>   	 * &drm_connector_state.crtc.
>   	 */
>   	struct drm_crtc *crtc;
> -	struct drm_bridge *bridge;
> +
> +	/**
> +	 * @bridge_chain: Bridges attached to this encoder.
> +	 */
> +	struct list_head bridge_chain;
> +
>   	const struct drm_encoder_funcs *funcs;
>   	const struct drm_encoder_helper_funcs *helper_private;
>   };

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-16 13:54     ` Marek Szyprowski
  0 siblings, 0 replies; 84+ messages in thread
From: Marek Szyprowski @ 2019-12-16 13:54 UTC (permalink / raw)
  To: Boris Brezillon, dri-devel
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, Jernej Skrabec,
	Rob Herring, Kyungmin Park, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Chris Healy

Hi All,

On 03.12.2019 15:15, Boris Brezillon wrote:
> So that each element in the chain can easily access its predecessor.
> This will be needed to support bus format negotiation between elements
> of the bridge chain.
>
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

I've noticed that this patch got merged to linux-next as commit 
05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of 
Samsung Exynos5250-based Arndale board. Booting stops after following 
messages:

[drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
[drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
[drm] No driver support for vblank timestamp query.
[drm] Cannot find any crtc or sizes
[drm] Cannot find any crtc or sizes
[drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0

I will try to debug this and provide more information soon.

> ---
> Changes in v4:
> * Simplify the drm_bridge_attach() logic
> * Fix list iteration bugs
> * Patch VC4 and Exynos DSI drivers to match core changes
> * Add R-bs
>
> Changes in v3:
> * None
>
> Changes in v2:
> * Adjust things to the "dummy encoder bridge" change (patch 2 in this
>    series)
> ---
>   drivers/gpu/drm/drm_bridge.c            | 171 +++++++++++++++---------
>   drivers/gpu/drm/drm_encoder.c           |  16 +--
>   drivers/gpu/drm/exynos/exynos_drm_dsi.c |   5 +-
>   drivers/gpu/drm/vc4/vc4_dsi.c           |  10 +-
>   include/drm/drm_bridge.h                |  12 +-
>   include/drm/drm_encoder.h               |   7 +-
>   6 files changed, 143 insertions(+), 78 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index 54c874493c57..b6517b4fa3d1 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -55,7 +55,7 @@
>    * just provide additional hooks to get the desired output at the end of the
>    * encoder chain.
>    *
> - * Bridges can also be chained up using the &drm_bridge.next pointer.
> + * Bridges can also be chained up using the &drm_bridge.chain_node field.
>    *
>    * Both legacy CRTC helpers and the new atomic modeset helpers support bridges.
>    */
> @@ -128,20 +128,21 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>   	bridge->dev = encoder->dev;
>   	bridge->encoder = encoder;
>   
> +	if (previous)
> +		list_add(&bridge->chain_node, &previous->chain_node);
> +	else
> +		list_add(&bridge->chain_node, &encoder->bridge_chain);
> +
>   	if (bridge->funcs->attach) {
>   		ret = bridge->funcs->attach(bridge);
>   		if (ret < 0) {
> +			list_del(&bridge->chain_node);
>   			bridge->dev = NULL;
>   			bridge->encoder = NULL;
>   			return ret;
>   		}
>   	}
>   
> -	if (previous)
> -		previous->next = bridge;
> -	else
> -		encoder->bridge = bridge;
> -
>   	return 0;
>   }
>   EXPORT_SYMBOL(drm_bridge_attach);
> @@ -157,6 +158,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
>   	if (bridge->funcs->detach)
>   		bridge->funcs->detach(bridge);
>   
> +	list_del(&bridge->chain_node);
>   	bridge->dev = NULL;
>   }
>   
> @@ -190,18 +192,21 @@ bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
>   				 const struct drm_display_mode *mode,
>   				 struct drm_display_mode *adjusted_mode)
>   {
> -	bool ret = true;
> +	struct drm_encoder *encoder;
>   
>   	if (!bridge)
>   		return true;
>   
> -	if (bridge->funcs->mode_fixup)
> -		ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (!bridge->funcs->mode_fixup)
> +			continue;
>   
> -	ret = ret && drm_bridge_chain_mode_fixup(bridge->next, mode,
> -						 adjusted_mode);
> +		if (!bridge->funcs->mode_fixup(bridge, mode, adjusted_mode))
> +			return false;
> +	}
>   
> -	return ret;
> +	return true;
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_mode_fixup);
>   
> @@ -224,18 +229,24 @@ enum drm_mode_status
>   drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
>   			    const struct drm_display_mode *mode)
>   {
> -	enum drm_mode_status ret = MODE_OK;
> +	struct drm_encoder *encoder;
>   
>   	if (!bridge)
> -		return ret;
> +		return MODE_OK;
>   
> -	if (bridge->funcs->mode_valid)
> -		ret = bridge->funcs->mode_valid(bridge, mode);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		enum drm_mode_status ret;
> +
> +		if (!bridge->funcs->mode_valid)
> +			continue;
>   
> -	if (ret != MODE_OK)
> -		return ret;
> +		ret = bridge->funcs->mode_valid(bridge, mode);
> +		if (ret != MODE_OK)
> +			return ret;
> +	}
>   
> -	return drm_bridge_chain_mode_valid(bridge->next, mode);
> +	return MODE_OK;
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
>   
> @@ -251,13 +262,20 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
>    */
>   void drm_bridge_chain_disable(struct drm_bridge *bridge)
>   {
> +	struct drm_encoder *encoder;
> +	struct drm_bridge *iter;
> +
>   	if (!bridge)
>   		return;
>   
> -	drm_bridge_chain_disable(bridge->next);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
> +		if (iter->funcs->disable)
> +			iter->funcs->disable(iter);
>   
> -	if (bridge->funcs->disable)
> -		bridge->funcs->disable(bridge);
> +		if (iter == bridge)
> +			break;
> +	}
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_disable);
>   
> @@ -274,13 +292,16 @@ EXPORT_SYMBOL(drm_bridge_chain_disable);
>    */
>   void drm_bridge_chain_post_disable(struct drm_bridge *bridge)
>   {
> +	struct drm_encoder *encoder;
> +
>   	if (!bridge)
>   		return;
>   
> -	if (bridge->funcs->post_disable)
> -		bridge->funcs->post_disable(bridge);
> -
> -	drm_bridge_chain_post_disable(bridge->next);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (bridge->funcs->post_disable)
> +			bridge->funcs->post_disable(bridge);
> +	}
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_post_disable);
>   
> @@ -300,13 +321,16 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
>   			       const struct drm_display_mode *mode,
>   			       const struct drm_display_mode *adjusted_mode)
>   {
> +	struct drm_encoder *encoder;
> +
>   	if (!bridge)
>   		return;
>   
> -	if (bridge->funcs->mode_set)
> -		bridge->funcs->mode_set(bridge, mode, adjusted_mode);
> -
> -	drm_bridge_chain_mode_set(bridge->next, mode, adjusted_mode);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (bridge->funcs->mode_set)
> +			bridge->funcs->mode_set(bridge, mode, adjusted_mode);
> +	}
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_mode_set);
>   
> @@ -323,13 +347,17 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_set);
>    */
>   void drm_bridge_chain_pre_enable(struct drm_bridge *bridge)
>   {
> +	struct drm_encoder *encoder;
> +	struct drm_bridge *iter;
> +
>   	if (!bridge)
>   		return;
>   
> -	drm_bridge_chain_pre_enable(bridge->next);
> -
> -	if (bridge->funcs->pre_enable)
> -		bridge->funcs->pre_enable(bridge);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
> +		if (iter->funcs->pre_enable)
> +			iter->funcs->pre_enable(iter);
> +	}
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
>   
> @@ -345,13 +373,16 @@ EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
>    */
>   void drm_bridge_chain_enable(struct drm_bridge *bridge)
>   {
> +	struct drm_encoder *encoder;
> +
>   	if (!bridge)
>   		return;
>   
> -	if (bridge->funcs->enable)
> -		bridge->funcs->enable(bridge);
> -
> -	drm_bridge_chain_enable(bridge->next);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (bridge->funcs->enable)
> +			bridge->funcs->enable(bridge);
> +	}
>   }
>   EXPORT_SYMBOL(drm_bridge_chain_enable);
>   
> @@ -370,15 +401,22 @@ EXPORT_SYMBOL(drm_bridge_chain_enable);
>   void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
>   				     struct drm_atomic_state *state)
>   {
> +	struct drm_encoder *encoder;
> +	struct drm_bridge *iter;
> +
>   	if (!bridge)
>   		return;
>   
> -	drm_atomic_bridge_chain_disable(bridge->next, state);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
> +		if (iter->funcs->atomic_disable)
> +			iter->funcs->atomic_disable(iter, state);
> +		else if (iter->funcs->disable)
> +			iter->funcs->disable(iter);
>   
> -	if (bridge->funcs->atomic_disable)
> -		bridge->funcs->atomic_disable(bridge, state);
> -	else if (bridge->funcs->disable)
> -		bridge->funcs->disable(bridge);
> +		if (iter == bridge)
> +			break;
> +	}
>   }
>   EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
>   
> @@ -398,15 +436,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
>   void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
>   					  struct drm_atomic_state *state)
>   {
> +	struct drm_encoder *encoder;
> +
>   	if (!bridge)
>   		return;
>   
> -	if (bridge->funcs->atomic_post_disable)
> -		bridge->funcs->atomic_post_disable(bridge, state);
> -	else if (bridge->funcs->post_disable)
> -		bridge->funcs->post_disable(bridge);
> -
> -	drm_atomic_bridge_chain_post_disable(bridge->next, state);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (bridge->funcs->atomic_post_disable)
> +			bridge->funcs->atomic_post_disable(bridge, state);
> +		else if (bridge->funcs->post_disable)
> +			bridge->funcs->post_disable(bridge);
> +	}
>   }
>   EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
>   
> @@ -426,15 +467,22 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
>   void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
>   					struct drm_atomic_state *state)
>   {
> +	struct drm_encoder *encoder;
> +	struct drm_bridge *iter;
> +
>   	if (!bridge)
>   		return;
>   
> -	drm_atomic_bridge_chain_pre_enable(bridge->next, state);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
> +		if (iter->funcs->atomic_pre_enable)
> +			iter->funcs->atomic_pre_enable(iter, state);
> +		else if (iter->funcs->pre_enable)
> +			iter->funcs->pre_enable(iter);
>   
> -	if (bridge->funcs->atomic_pre_enable)
> -		bridge->funcs->atomic_pre_enable(bridge, state);
> -	else if (bridge->funcs->pre_enable)
> -		bridge->funcs->pre_enable(bridge);
> +		if (iter == bridge)
> +			break;
> +	}
>   }
>   EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
>   
> @@ -453,15 +501,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
>   void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
>   				    struct drm_atomic_state *state)
>   {
> +	struct drm_encoder *encoder;
> +
>   	if (!bridge)
>   		return;
>   
> -	if (bridge->funcs->atomic_enable)
> -		bridge->funcs->atomic_enable(bridge, state);
> -	else if (bridge->funcs->enable)
> -		bridge->funcs->enable(bridge);
> -
> -	drm_atomic_bridge_chain_enable(bridge->next, state);
> +	encoder = bridge->encoder;
> +	list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
> +		if (bridge->funcs->atomic_enable)
> +			bridge->funcs->atomic_enable(bridge, state);
> +		else if (bridge->funcs->enable)
> +			bridge->funcs->enable(bridge);
> +	}
>   }
>   EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
>   
> diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
> index a2cc7e7241a9..e555281f43d4 100644
> --- a/drivers/gpu/drm/drm_encoder.c
> +++ b/drivers/gpu/drm/drm_encoder.c
> @@ -140,6 +140,7 @@ int drm_encoder_init(struct drm_device *dev,
>   		goto out_put;
>   	}
>   
> +	INIT_LIST_HEAD(&encoder->bridge_chain);
>   	list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
>   	encoder->index = dev->mode_config.num_encoder++;
>   
> @@ -160,23 +161,16 @@ EXPORT_SYMBOL(drm_encoder_init);
>   void drm_encoder_cleanup(struct drm_encoder *encoder)
>   {
>   	struct drm_device *dev = encoder->dev;
> +	struct drm_bridge *bridge, *next;
>   
>   	/* Note that the encoder_list is considered to be static; should we
>   	 * remove the drm_encoder at runtime we would have to decrement all
>   	 * the indices on the drm_encoder after us in the encoder_list.
>   	 */
>   
> -	if (encoder->bridge) {
> -		struct drm_bridge *bridge;
> -		struct drm_bridge *next;
> -
> -		bridge = drm_bridge_chain_get_first_bridge(encoder);
> -		while (bridge) {
> -			next = drm_bridge_get_next_bridge(bridge);
> -			drm_bridge_detach(bridge);
> -			bridge = next;
> -		}
> -	}
> +	list_for_each_entry_safe(bridge, next, &encoder->bridge_chain,
> +				 chain_node)
> +		drm_bridge_detach(bridge);
>   
>   	drm_mode_object_unregister(dev, &encoder->base);
>   	kfree(encoder->name);
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> index d984097704b8..7de82e22252a 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> @@ -255,6 +255,7 @@ struct exynos_dsi {
>   	struct mipi_dsi_host dsi_host;
>   	struct drm_connector connector;
>   	struct drm_panel *panel;
> +	struct list_head bridge_chain;
>   	struct drm_bridge *out_bridge;
>   	struct device *dev;
>   
> @@ -1522,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>   	if (out_bridge) {
>   		drm_bridge_attach(encoder, out_bridge, NULL);
>   		dsi->out_bridge = out_bridge;
> -		encoder->bridge = NULL;
> +		list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
>   	} else {
>   		int ret = exynos_dsi_create_connector(encoder);
>   
> @@ -1588,6 +1589,7 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
>   		if (dsi->out_bridge->funcs->detach)
>   			dsi->out_bridge->funcs->detach(dsi->out_bridge);
>   		dsi->out_bridge = NULL;
> +		INIT_LIST_HEAD(&dsi->bridge_chain);
>   	}
>   
>   	if (drm->mode_config.poll_enabled)
> @@ -1735,6 +1737,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
>   	init_completion(&dsi->completed);
>   	spin_lock_init(&dsi->transfer_lock);
>   	INIT_LIST_HEAD(&dsi->transfer_list);
> +	INIT_LIST_HEAD(&dsi->bridge_chain);
>   
>   	dsi->dsi_host.ops = &exynos_dsi_ops;
>   	dsi->dsi_host.dev = dev;
> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index ff81b54ea281..6c5b80ad6154 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> @@ -499,6 +499,7 @@ struct vc4_dsi {
>   	struct mipi_dsi_host dsi_host;
>   	struct drm_encoder *encoder;
>   	struct drm_bridge *bridge;
> +	struct list_head bridge_chain;
>   
>   	void __iomem *regs;
>   
> @@ -1460,6 +1461,8 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>   				       GFP_KERNEL);
>   	if (!vc4_dsi_encoder)
>   		return -ENOMEM;
> +
> +	INIT_LIST_HEAD(&dsi->bridge_chain);
>   	vc4_dsi_encoder->base.type = VC4_ENCODER_TYPE_DSI1;
>   	vc4_dsi_encoder->dsi = dsi;
>   	dsi->encoder = &vc4_dsi_encoder->base.base;
> @@ -1610,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>   	 * from our driver, since we need to sequence them within the
>   	 * encoder's enable/disable paths.
>   	 */
> -	dsi->encoder->bridge = NULL;
> +	list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>   
>   	if (dsi->port == 0)
>   		vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
> @@ -1632,6 +1635,11 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>   	if (dsi->bridge)
>   		pm_runtime_disable(dev);
>   
> +	/*
> +	 * Restore the bridge_chain so the bridge detach procedure can happen
> +	 * normally.
> +	 */
> +	list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>   	vc4_dsi_encoder_destroy(dsi->encoder);
>   
>   	if (dsi->port == 1)
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index bd78c256b1ed..c118726469ee 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -384,8 +384,8 @@ struct drm_bridge {
>   	struct drm_device *dev;
>   	/** @encoder: encoder to which this bridge is connected */
>   	struct drm_encoder *encoder;
> -	/** @next: the next bridge in the encoder chain */
> -	struct drm_bridge *next;
> +	/** @chain_node: used to form a bridge chain */
> +	struct list_head chain_node;
>   #ifdef CONFIG_OF
>   	/** @of_node: device node pointer to the bridge */
>   	struct device_node *of_node;
> @@ -420,7 +420,10 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>   static inline struct drm_bridge *
>   drm_bridge_get_next_bridge(struct drm_bridge *bridge)
>   {
> -	return bridge->next;
> +	if (list_is_last(&bridge->chain_node, &bridge->encoder->bridge_chain))
> +		return NULL;
> +
> +	return list_next_entry(bridge, chain_node);
>   }
>   
>   /**
> @@ -434,7 +437,8 @@ drm_bridge_get_next_bridge(struct drm_bridge *bridge)
>   static inline struct drm_bridge *
>   drm_bridge_chain_get_first_bridge(struct drm_encoder *encoder)
>   {
> -	return encoder->bridge;
> +	return list_first_entry_or_null(&encoder->bridge_chain,
> +					struct drm_bridge, chain_node);
>   }
>   
>   bool drm_bridge_chain_mode_fixup(struct drm_bridge *bridge,
> diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
> index f06164f44efe..5623994b6e9e 100644
> --- a/include/drm/drm_encoder.h
> +++ b/include/drm/drm_encoder.h
> @@ -172,7 +172,12 @@ struct drm_encoder {
>   	 * &drm_connector_state.crtc.
>   	 */
>   	struct drm_crtc *crtc;
> -	struct drm_bridge *bridge;
> +
> +	/**
> +	 * @bridge_chain: Bridges attached to this encoder.
> +	 */
> +	struct list_head bridge_chain;
> +
>   	const struct drm_encoder_funcs *funcs;
>   	const struct drm_encoder_helper_funcs *helper_private;
>   };

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-16 13:54     ` Marek Szyprowski
@ 2019-12-16 14:55       ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-16 14:55 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: dri-devel, Mark Rutland, Neil Armstrong, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Nikita Yushchenko,
	Andrey Smirnov, Kyungmin Park, Chris Healy, devicetree,
	Jonas Karlman, Rob Herring, Jernej Skrabec, Seung-Woo Kim,
	Andrzej Hajda

Hello Marek,

On Mon, 16 Dec 2019 14:54:25 +0100
Marek Szyprowski <m.szyprowski@samsung.com> wrote:

> Hi All,
> 
> On 03.12.2019 15:15, Boris Brezillon wrote:
> > So that each element in the chain can easily access its predecessor.
> > This will be needed to support bus format negotiation between elements
> > of the bridge chain.
> >
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>  
> 
> I've noticed that this patch got merged to linux-next as commit 
> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of 
> Samsung Exynos5250-based Arndale board. Booting stops after following 
> messages:
> 
> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> [drm] No driver support for vblank timestamp query.
> [drm] Cannot find any crtc or sizes
> [drm] Cannot find any crtc or sizes
> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> 
> I will try to debug this and provide more information soon.
> 

Can you try with this diff applied?

--->8---
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 3955f84dc893..118ecedc7621 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1523,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
        if (out_bridge) {
                drm_bridge_attach(encoder, out_bridge, NULL);
                dsi->out_bridge = out_bridge;
-               list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
+               list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
        } else {
                int ret = exynos_dsi_create_connector(encoder);
 
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 6c5b80ad6154..e1378d48210f 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1613,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
         * from our driver, since we need to sequence them within the
         * encoder's enable/disable paths.
         */
-       list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
+       list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
 
        if (dsi->port == 0)
                vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
@@ -1639,7 +1639,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
         * Restore the bridge_chain so the bridge detach procedure can happen
         * normally.
         */
-       list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
+       list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
        vc4_dsi_encoder_destroy(dsi->encoder);
 
        if (dsi->port == 1)

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-16 14:55       ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-16 14:55 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, Jernej Skrabec,
	dri-devel, Rob Herring, Kyungmin Park, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy

Hello Marek,

On Mon, 16 Dec 2019 14:54:25 +0100
Marek Szyprowski <m.szyprowski@samsung.com> wrote:

> Hi All,
> 
> On 03.12.2019 15:15, Boris Brezillon wrote:
> > So that each element in the chain can easily access its predecessor.
> > This will be needed to support bus format negotiation between elements
> > of the bridge chain.
> >
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>  
> 
> I've noticed that this patch got merged to linux-next as commit 
> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of 
> Samsung Exynos5250-based Arndale board. Booting stops after following 
> messages:
> 
> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> [drm] No driver support for vblank timestamp query.
> [drm] Cannot find any crtc or sizes
> [drm] Cannot find any crtc or sizes
> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> 
> I will try to debug this and provide more information soon.
> 

Can you try with this diff applied?

--->8---
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 3955f84dc893..118ecedc7621 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1523,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
        if (out_bridge) {
                drm_bridge_attach(encoder, out_bridge, NULL);
                dsi->out_bridge = out_bridge;
-               list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
+               list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
        } else {
                int ret = exynos_dsi_create_connector(encoder);
 
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 6c5b80ad6154..e1378d48210f 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1613,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
         * from our driver, since we need to sequence them within the
         * encoder's enable/disable paths.
         */
-       list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
+       list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
 
        if (dsi->port == 0)
                vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
@@ -1639,7 +1639,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
         * Restore the bridge_chain so the bridge detach procedure can happen
         * normally.
         */
-       list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
+       list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
        vc4_dsi_encoder_destroy(dsi->encoder);
 
        if (dsi->port == 1)
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-16 14:55       ` Boris Brezillon
@ 2019-12-16 15:02         ` Marek Szyprowski
  -1 siblings, 0 replies; 84+ messages in thread
From: Marek Szyprowski @ 2019-12-16 15:02 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: dri-devel, Mark Rutland, Neil Armstrong, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Nikita Yushchenko,
	Andrey Smirnov, Kyungmin Park, Chris Healy, devicetree,
	Jonas Karlman, Rob Herring, Jernej Skrabec, Seung-Woo Kim,
	Andrzej Hajda

Hi Boris,

On 16.12.2019 15:55, Boris Brezillon wrote:
> On Mon, 16 Dec 2019 14:54:25 +0100
> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>> On 03.12.2019 15:15, Boris Brezillon wrote:
>>> So that each element in the chain can easily access its predecessor.
>>> This will be needed to support bus format negotiation between elements
>>> of the bridge chain.
>>>
>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>> I've noticed that this patch got merged to linux-next as commit
>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
>> Samsung Exynos5250-based Arndale board. Booting stops after following
>> messages:
>>
>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>> [drm] No driver support for vblank timestamp query.
>> [drm] Cannot find any crtc or sizes
>> [drm] Cannot find any crtc or sizes
>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
>>
>> I will try to debug this and provide more information soon.
>>
> Can you try with this diff applied?

This patch doesn't change anything.

> --->8---
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> index 3955f84dc893..118ecedc7621 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> @@ -1523,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>          if (out_bridge) {
>                  drm_bridge_attach(encoder, out_bridge, NULL);
>                  dsi->out_bridge = out_bridge;
> -               list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
> +               list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
>          } else {
>                  int ret = exynos_dsi_create_connector(encoder);
>   
> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index 6c5b80ad6154..e1378d48210f 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> @@ -1613,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>           * from our driver, since we need to sequence them within the
>           * encoder's enable/disable paths.
>           */
> -       list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
> +       list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>   
>          if (dsi->port == 0)
>                  vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
> @@ -1639,7 +1639,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>           * Restore the bridge_chain so the bridge detach procedure can happen
>           * normally.
>           */
> -       list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
> +       list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>          vc4_dsi_encoder_destroy(dsi->encoder);
>   
>          if (dsi->port == 1)
>
>
Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-16 15:02         ` Marek Szyprowski
  0 siblings, 0 replies; 84+ messages in thread
From: Marek Szyprowski @ 2019-12-16 15:02 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, Jernej Skrabec,
	dri-devel, Rob Herring, Kyungmin Park, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy

Hi Boris,

On 16.12.2019 15:55, Boris Brezillon wrote:
> On Mon, 16 Dec 2019 14:54:25 +0100
> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>> On 03.12.2019 15:15, Boris Brezillon wrote:
>>> So that each element in the chain can easily access its predecessor.
>>> This will be needed to support bus format negotiation between elements
>>> of the bridge chain.
>>>
>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>> I've noticed that this patch got merged to linux-next as commit
>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
>> Samsung Exynos5250-based Arndale board. Booting stops after following
>> messages:
>>
>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>> [drm] No driver support for vblank timestamp query.
>> [drm] Cannot find any crtc or sizes
>> [drm] Cannot find any crtc or sizes
>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
>>
>> I will try to debug this and provide more information soon.
>>
> Can you try with this diff applied?

This patch doesn't change anything.

> --->8---
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> index 3955f84dc893..118ecedc7621 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> @@ -1523,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>          if (out_bridge) {
>                  drm_bridge_attach(encoder, out_bridge, NULL);
>                  dsi->out_bridge = out_bridge;
> -               list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
> +               list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
>          } else {
>                  int ret = exynos_dsi_create_connector(encoder);
>   
> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index 6c5b80ad6154..e1378d48210f 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> @@ -1613,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>           * from our driver, since we need to sequence them within the
>           * encoder's enable/disable paths.
>           */
> -       list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
> +       list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>   
>          if (dsi->port == 0)
>                  vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
> @@ -1639,7 +1639,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>           * Restore the bridge_chain so the bridge detach procedure can happen
>           * normally.
>           */
> -       list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
> +       list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>          vc4_dsi_encoder_destroy(dsi->encoder);
>   
>          if (dsi->port == 1)
>
>
Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-16 15:02         ` Marek Szyprowski
@ 2019-12-16 15:25           ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-16 15:25 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: dri-devel, Mark Rutland, Neil Armstrong, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Nikita Yushchenko,
	Andrey Smirnov, Kyungmin Park, Chris Healy, devicetree,
	Jonas Karlman, Rob Herring, Jernej Skrabec, Seung-Woo Kim,
	Andrzej Hajda

On Mon, 16 Dec 2019 16:02:36 +0100
Marek Szyprowski <m.szyprowski@samsung.com> wrote:

> Hi Boris,
> 
> On 16.12.2019 15:55, Boris Brezillon wrote:
> > On Mon, 16 Dec 2019 14:54:25 +0100
> > Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
> >> On 03.12.2019 15:15, Boris Brezillon wrote:  
> >>> So that each element in the chain can easily access its predecessor.
> >>> This will be needed to support bus format negotiation between elements
> >>> of the bridge chain.
> >>>
> >>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> >>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> >>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>  
> >> I've noticed that this patch got merged to linux-next as commit
> >> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> >> Samsung Exynos5250-based Arndale board. Booting stops after following
> >> messages:
> >>
> >> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> >> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> >> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> >> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> >> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> >> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> >> [drm] No driver support for vblank timestamp query.
> >> [drm] Cannot find any crtc or sizes
> >> [drm] Cannot find any crtc or sizes
> >> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> >>
> >> I will try to debug this and provide more information soon.
> >>  
> > Can you try with this diff applied?  
> 
> This patch doesn't change anything.

Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
after the list_splice_init() call?

> 
> > --->8---  
> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > index 3955f84dc893..118ecedc7621 100644
> > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > @@ -1523,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
> >          if (out_bridge) {
> >                  drm_bridge_attach(encoder, out_bridge, NULL);
> >                  dsi->out_bridge = out_bridge;
> > -               list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
> > +               list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
> >          } else {
> >                  int ret = exynos_dsi_create_connector(encoder);
> >   
> > diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> > index 6c5b80ad6154..e1378d48210f 100644
> > --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> > +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> > @@ -1613,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
> >           * from our driver, since we need to sequence them within the
> >           * encoder's enable/disable paths.
> >           */
> > -       list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
> > +       list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
> >   
> >          if (dsi->port == 0)
> >                  vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
> > @@ -1639,7 +1639,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
> >           * Restore the bridge_chain so the bridge detach procedure can happen
> >           * normally.
> >           */
> > -       list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
> > +       list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
> >          vc4_dsi_encoder_destroy(dsi->encoder);
> >   
> >          if (dsi->port == 1)
> >
> >  
> Best regards


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-16 15:25           ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-16 15:25 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, Jernej Skrabec,
	dri-devel, Rob Herring, Kyungmin Park, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy

On Mon, 16 Dec 2019 16:02:36 +0100
Marek Szyprowski <m.szyprowski@samsung.com> wrote:

> Hi Boris,
> 
> On 16.12.2019 15:55, Boris Brezillon wrote:
> > On Mon, 16 Dec 2019 14:54:25 +0100
> > Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
> >> On 03.12.2019 15:15, Boris Brezillon wrote:  
> >>> So that each element in the chain can easily access its predecessor.
> >>> This will be needed to support bus format negotiation between elements
> >>> of the bridge chain.
> >>>
> >>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> >>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> >>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>  
> >> I've noticed that this patch got merged to linux-next as commit
> >> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> >> Samsung Exynos5250-based Arndale board. Booting stops after following
> >> messages:
> >>
> >> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> >> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> >> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> >> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> >> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> >> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> >> [drm] No driver support for vblank timestamp query.
> >> [drm] Cannot find any crtc or sizes
> >> [drm] Cannot find any crtc or sizes
> >> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> >>
> >> I will try to debug this and provide more information soon.
> >>  
> > Can you try with this diff applied?  
> 
> This patch doesn't change anything.

Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
after the list_splice_init() call?

> 
> > --->8---  
> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > index 3955f84dc893..118ecedc7621 100644
> > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > @@ -1523,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
> >          if (out_bridge) {
> >                  drm_bridge_attach(encoder, out_bridge, NULL);
> >                  dsi->out_bridge = out_bridge;
> > -               list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
> > +               list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
> >          } else {
> >                  int ret = exynos_dsi_create_connector(encoder);
> >   
> > diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> > index 6c5b80ad6154..e1378d48210f 100644
> > --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> > +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> > @@ -1613,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
> >           * from our driver, since we need to sequence them within the
> >           * encoder's enable/disable paths.
> >           */
> > -       list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
> > +       list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
> >   
> >          if (dsi->port == 0)
> >                  vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
> > @@ -1639,7 +1639,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
> >           * Restore the bridge_chain so the bridge detach procedure can happen
> >           * normally.
> >           */
> > -       list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
> > +       list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
> >          vc4_dsi_encoder_destroy(dsi->encoder);
> >   
> >          if (dsi->port == 1)
> >
> >  
> Best regards

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-16 15:25           ` Boris Brezillon
@ 2019-12-23  9:55             ` Marek Szyprowski
  -1 siblings, 0 replies; 84+ messages in thread
From: Marek Szyprowski @ 2019-12-23  9:55 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: dri-devel, Mark Rutland, Neil Armstrong, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Nikita Yushchenko,
	Andrey Smirnov, Kyungmin Park, Chris Healy, devicetree,
	Jonas Karlman, Rob Herring, Jernej Skrabec, Seung-Woo Kim,
	Andrzej Hajda

Hi Boris,

On 16.12.2019 16:25, Boris Brezillon wrote:
> On Mon, 16 Dec 2019 16:02:36 +0100
> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>> Hi Boris,
>>
>> On 16.12.2019 15:55, Boris Brezillon wrote:
>>> On Mon, 16 Dec 2019 14:54:25 +0100
>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>> On 03.12.2019 15:15, Boris Brezillon wrote:
>>>>> So that each element in the chain can easily access its predecessor.
>>>>> This will be needed to support bus format negotiation between elements
>>>>> of the bridge chain.
>>>>>
>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>>> I've noticed that this patch got merged to linux-next as commit
>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
>>>> messages:
>>>>
>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>>>> [drm] No driver support for vblank timestamp query.
>>>> [drm] Cannot find any crtc or sizes
>>>> [drm] Cannot find any crtc or sizes
>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
>>>>
>>>> I will try to debug this and provide more information soon.
>>>>   
>>> Can you try with this diff applied?
>> This patch doesn't change anything.
> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> after the list_splice_init() call?

encoder->bridge_chain contains only one element. dsi->drive_chain is empty.

Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
fixed the boot issue. It looks that this is related with the way the 
Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
will give a bit more detailed comment and spread some light on this.

I can send a formal patch fixing this if You want.

>>> --->8---
>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>> index 3955f84dc893..118ecedc7621 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>> @@ -1523,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>>>           if (out_bridge) {
>>>                   drm_bridge_attach(encoder, out_bridge, NULL);
>>>                   dsi->out_bridge = out_bridge;
>>> -               list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
>>> +               list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
>>>           } else {
>>>                   int ret = exynos_dsi_create_connector(encoder);
>>>    
>>> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
>>> index 6c5b80ad6154..e1378d48210f 100644
>>> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
>>> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
>>> @@ -1613,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>>>            * from our driver, since we need to sequence them within the
>>>            * encoder's enable/disable paths.
>>>            */
>>> -       list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>>> +       list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>>>    
>>>           if (dsi->port == 0)
>>>                   vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
>>> @@ -1639,7 +1639,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>>>            * Restore the bridge_chain so the bridge detach procedure can happen
>>>            * normally.
>>>            */
>>> -       list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>>> +       list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>>>           vc4_dsi_encoder_destroy(dsi->encoder);
>>>    
>>>           if (dsi->port == 1)
>>>
>>>
Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-23  9:55             ` Marek Szyprowski
  0 siblings, 0 replies; 84+ messages in thread
From: Marek Szyprowski @ 2019-12-23  9:55 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, Jernej Skrabec,
	dri-devel, Rob Herring, Kyungmin Park, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy

Hi Boris,

On 16.12.2019 16:25, Boris Brezillon wrote:
> On Mon, 16 Dec 2019 16:02:36 +0100
> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>> Hi Boris,
>>
>> On 16.12.2019 15:55, Boris Brezillon wrote:
>>> On Mon, 16 Dec 2019 14:54:25 +0100
>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>> On 03.12.2019 15:15, Boris Brezillon wrote:
>>>>> So that each element in the chain can easily access its predecessor.
>>>>> This will be needed to support bus format negotiation between elements
>>>>> of the bridge chain.
>>>>>
>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>>> I've noticed that this patch got merged to linux-next as commit
>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
>>>> messages:
>>>>
>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>>>> [drm] No driver support for vblank timestamp query.
>>>> [drm] Cannot find any crtc or sizes
>>>> [drm] Cannot find any crtc or sizes
>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
>>>>
>>>> I will try to debug this and provide more information soon.
>>>>   
>>> Can you try with this diff applied?
>> This patch doesn't change anything.
> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> after the list_splice_init() call?

encoder->bridge_chain contains only one element. dsi->drive_chain is empty.

Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
fixed the boot issue. It looks that this is related with the way the 
Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
will give a bit more detailed comment and spread some light on this.

I can send a formal patch fixing this if You want.

>>> --->8---
>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>> index 3955f84dc893..118ecedc7621 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>> @@ -1523,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>>>           if (out_bridge) {
>>>                   drm_bridge_attach(encoder, out_bridge, NULL);
>>>                   dsi->out_bridge = out_bridge;
>>> -               list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
>>> +               list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
>>>           } else {
>>>                   int ret = exynos_dsi_create_connector(encoder);
>>>    
>>> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
>>> index 6c5b80ad6154..e1378d48210f 100644
>>> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
>>> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
>>> @@ -1613,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>>>            * from our driver, since we need to sequence them within the
>>>            * encoder's enable/disable paths.
>>>            */
>>> -       list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>>> +       list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>>>    
>>>           if (dsi->port == 0)
>>>                   vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
>>> @@ -1639,7 +1639,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>>>            * Restore the bridge_chain so the bridge detach procedure can happen
>>>            * normally.
>>>            */
>>> -       list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>>> +       list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>>>           vc4_dsi_encoder_destroy(dsi->encoder);
>>>    
>>>           if (dsi->port == 1)
>>>
>>>
Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-23  9:55             ` Marek Szyprowski
@ 2019-12-24  9:16               ` Andrzej Hajda
  -1 siblings, 0 replies; 84+ messages in thread
From: Andrzej Hajda @ 2019-12-24  9:16 UTC (permalink / raw)
  To: Marek Szyprowski, Boris Brezillon
  Cc: dri-devel, Mark Rutland, Neil Armstrong, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Nikita Yushchenko,
	Andrey Smirnov, Kyungmin Park, Chris Healy, devicetree,
	Jonas Karlman, Rob Herring, Jernej Skrabec, Seung-Woo Kim

On 23.12.2019 10:55, Marek Szyprowski wrote:
> Hi Boris,
>
> On 16.12.2019 16:25, Boris Brezillon wrote:
>> On Mon, 16 Dec 2019 16:02:36 +0100
>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>> Hi Boris,
>>>
>>> On 16.12.2019 15:55, Boris Brezillon wrote:
>>>> On Mon, 16 Dec 2019 14:54:25 +0100
>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:
>>>>>> So that each element in the chain can easily access its predecessor.
>>>>>> This will be needed to support bus format negotiation between elements
>>>>>> of the bridge chain.
>>>>>>
>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>>>> I've noticed that this patch got merged to linux-next as commit
>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
>>>>> messages:
>>>>>
>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>>>>> [drm] No driver support for vblank timestamp query.
>>>>> [drm] Cannot find any crtc or sizes
>>>>> [drm] Cannot find any crtc or sizes
>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
>>>>>
>>>>> I will try to debug this and provide more information soon.
>>>>>   
>>>> Can you try with this diff applied?
>>> This patch doesn't change anything.
>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
>> after the list_splice_init() call?
> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
>
> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> fixed the boot issue. It looks that this is related with the way the 
> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> will give a bit more detailed comment and spread some light on this.


Hi Marek, Boris,


I have not followed latest patches due to high work load, my bad. Marek
thanks from pointing

About ExynosDSI bridge handling:

The order of calling encoder, bridge (and consequently panel) ops
enforced by DRM core (bridge->pre_enable, encoder->enable,
bridge->enable) does not fit to ExynosDSI hardware initialization
sequence, if I remember correctly it does not fit to whole MIPI DSI
standard (I think similar situation is with eDP). As a result DSI
drivers must use some ugly workarounds, rely on HW properly coping with
incorrect sequences, or, as in case of ExynosDSI driver, just avoid
using encoder->bridge chaining and call bridge ops by itself when suitable.

So proper patch converting to double-linked list should not try to
splice ExynosDSI private bridge list with with encoder's, encoder's list
should be always empty, as Marek suggested.


Regards

Andrzej


>
> I can send a formal patch fixing this if You want.
>
>>>> --->8---
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>>> index 3955f84dc893..118ecedc7621 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>>> @@ -1523,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>>>>           if (out_bridge) {
>>>>                   drm_bridge_attach(encoder, out_bridge, NULL);
>>>>                   dsi->out_bridge = out_bridge;
>>>> -               list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
>>>> +               list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
>>>>           } else {
>>>>                   int ret = exynos_dsi_create_connector(encoder);
>>>>    
>>>> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
>>>> index 6c5b80ad6154..e1378d48210f 100644
>>>> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
>>>> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
>>>> @@ -1613,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>>>>            * from our driver, since we need to sequence them within the
>>>>            * encoder's enable/disable paths.
>>>>            */
>>>> -       list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>>>> +       list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>>>>    
>>>>           if (dsi->port == 0)
>>>>                   vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
>>>> @@ -1639,7 +1639,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>>>>            * Restore the bridge_chain so the bridge detach procedure can happen
>>>>            * normally.
>>>>            */
>>>> -       list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>>>> +       list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>>>>           vc4_dsi_encoder_destroy(dsi->encoder);
>>>>    
>>>>           if (dsi->port == 1)
>>>>
>>>>
> Best regards



^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-24  9:16               ` Andrzej Hajda
  0 siblings, 0 replies; 84+ messages in thread
From: Andrzej Hajda @ 2019-12-24  9:16 UTC (permalink / raw)
  To: Marek Szyprowski, Boris Brezillon
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, Jernej Skrabec,
	dri-devel, Rob Herring, Kyungmin Park, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy

On 23.12.2019 10:55, Marek Szyprowski wrote:
> Hi Boris,
>
> On 16.12.2019 16:25, Boris Brezillon wrote:
>> On Mon, 16 Dec 2019 16:02:36 +0100
>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>> Hi Boris,
>>>
>>> On 16.12.2019 15:55, Boris Brezillon wrote:
>>>> On Mon, 16 Dec 2019 14:54:25 +0100
>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:
>>>>>> So that each element in the chain can easily access its predecessor.
>>>>>> This will be needed to support bus format negotiation between elements
>>>>>> of the bridge chain.
>>>>>>
>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>>>> I've noticed that this patch got merged to linux-next as commit
>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
>>>>> messages:
>>>>>
>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>>>>> [drm] No driver support for vblank timestamp query.
>>>>> [drm] Cannot find any crtc or sizes
>>>>> [drm] Cannot find any crtc or sizes
>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
>>>>>
>>>>> I will try to debug this and provide more information soon.
>>>>>   
>>>> Can you try with this diff applied?
>>> This patch doesn't change anything.
>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
>> after the list_splice_init() call?
> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
>
> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> fixed the boot issue. It looks that this is related with the way the 
> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> will give a bit more detailed comment and spread some light on this.


Hi Marek, Boris,


I have not followed latest patches due to high work load, my bad. Marek
thanks from pointing

About ExynosDSI bridge handling:

The order of calling encoder, bridge (and consequently panel) ops
enforced by DRM core (bridge->pre_enable, encoder->enable,
bridge->enable) does not fit to ExynosDSI hardware initialization
sequence, if I remember correctly it does not fit to whole MIPI DSI
standard (I think similar situation is with eDP). As a result DSI
drivers must use some ugly workarounds, rely on HW properly coping with
incorrect sequences, or, as in case of ExynosDSI driver, just avoid
using encoder->bridge chaining and call bridge ops by itself when suitable.

So proper patch converting to double-linked list should not try to
splice ExynosDSI private bridge list with with encoder's, encoder's list
should be always empty, as Marek suggested.


Regards

Andrzej


>
> I can send a formal patch fixing this if You want.
>
>>>> --->8---
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>>> index 3955f84dc893..118ecedc7621 100644
>>>> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>>> @@ -1523,7 +1523,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>>>>           if (out_bridge) {
>>>>                   drm_bridge_attach(encoder, out_bridge, NULL);
>>>>                   dsi->out_bridge = out_bridge;
>>>> -               list_splice(&encoder->bridge_chain, &dsi->bridge_chain);
>>>> +               list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
>>>>           } else {
>>>>                   int ret = exynos_dsi_create_connector(encoder);
>>>>    
>>>> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
>>>> index 6c5b80ad6154..e1378d48210f 100644
>>>> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
>>>> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
>>>> @@ -1613,7 +1613,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>>>>            * from our driver, since we need to sequence them within the
>>>>            * encoder's enable/disable paths.
>>>>            */
>>>> -       list_splice(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>>>> +       list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
>>>>    
>>>>           if (dsi->port == 0)
>>>>                   vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
>>>> @@ -1639,7 +1639,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>>>>            * Restore the bridge_chain so the bridge detach procedure can happen
>>>>            * normally.
>>>>            */
>>>> -       list_splice(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>>>> +       list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
>>>>           vc4_dsi_encoder_destroy(dsi->encoder);
>>>>    
>>>>           if (dsi->port == 1)
>>>>
>>>>
> Best regards


_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-24  9:16               ` Andrzej Hajda
@ 2019-12-24  9:44                 ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-24  9:44 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Marek Szyprowski, dri-devel, Mark Rutland, Neil Armstrong,
	Thierry Reding, Laurent Pinchart, kernel, Sam Ravnborg,
	Nikita Yushchenko, Andrey Smirnov, Kyungmin Park, Chris Healy,
	devicetree, Jonas Karlman, Rob Herring, Jernej Skrabec,
	Seung-Woo Kim

On Tue, 24 Dec 2019 10:16:49 +0100
Andrzej Hajda <a.hajda@samsung.com> wrote:

> On 23.12.2019 10:55, Marek Szyprowski wrote:
> > Hi Boris,
> >
> > On 16.12.2019 16:25, Boris Brezillon wrote:  
> >> On Mon, 16 Dec 2019 16:02:36 +0100
> >> Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
> >>> Hi Boris,
> >>>
> >>> On 16.12.2019 15:55, Boris Brezillon wrote:  
> >>>> On Mon, 16 Dec 2019 14:54:25 +0100
> >>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
> >>>>> On 03.12.2019 15:15, Boris Brezillon wrote:  
> >>>>>> So that each element in the chain can easily access its predecessor.
> >>>>>> This will be needed to support bus format negotiation between elements
> >>>>>> of the bridge chain.
> >>>>>>
> >>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> >>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> >>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>  
> >>>>> I've noticed that this patch got merged to linux-next as commit
> >>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> >>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> >>>>> messages:
> >>>>>
> >>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> >>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> >>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> >>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> >>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> >>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> >>>>> [drm] No driver support for vblank timestamp query.
> >>>>> [drm] Cannot find any crtc or sizes
> >>>>> [drm] Cannot find any crtc or sizes
> >>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> >>>>>
> >>>>> I will try to debug this and provide more information soon.
> >>>>>     
> >>>> Can you try with this diff applied?  
> >>> This patch doesn't change anything.  
> >> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> >> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> >> after the list_splice_init() call?  
> > encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> >
> > Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> > fixed the boot issue.

If INIT_LIST_HEAD() worked, I don't understand why replacing the
list_splice() call by a list_splice_init() (which doing a list_splice()
+ INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
list_splice_init() version doesn't work?

> > It looks that this is related with the way the 
> > Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> > will give a bit more detailed comment and spread some light on this.  
> 
> 
> Hi Marek, Boris,
> 
> 
> I have not followed latest patches due to high work load, my bad. Marek
> thanks from pointing
> 
> About ExynosDSI bridge handling:
> 
> The order of calling encoder, bridge (and consequently panel) ops
> enforced by DRM core (bridge->pre_enable, encoder->enable,
> bridge->enable) does not fit to ExynosDSI hardware initialization
> sequence, if I remember correctly it does not fit to whole MIPI DSI
> standard (I think similar situation is with eDP). As a result DSI
> drivers must use some ugly workarounds, rely on HW properly coping with
> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> using encoder->bridge chaining and call bridge ops by itself when suitable.

Yes, that's definitely hack-ish, and I proposed 2 solutions to address
that in previous versions of this patchset, unfortunately I didn't get
any feedback so I went for the less invasive option (keep the hack but
adapt it to the double-linked list changes), which still lead to
regressions :-/.

Just a reminder of my 2 proposals:

1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
   split your enable/disable logic in 2 parts and make sure things are
   ready when the panel/next bridge tries to send DSI commands
2/ move everything that's needed to send DSI commands out of the
   ->enable() path (maybe in runtime PM resume/suspend hooks) so you
   can call that in the DSI transfer path too

As pointed out by Laurent, #1 doesn't work because some panel drivers
send DSI commands in their ->prepare() hook, and ->pre_enable() methods
are called in reverse order, meaning that the DRM panel bridge driver
would try to issue DSI commands before the DSI host controllers is ready
to send them. I still thing #2 is a good option.

> 
> So proper patch converting to double-linked list should not try to
> splice ExynosDSI private bridge list with with encoder's, encoder's list
> should be always empty, as Marek suggested.

That's exactly what I wanted to do: make the encoder's list empty after
attach() and restore it to its initial state before unregistering
the bridge, except I forgot that list_splice() doesn't call
INIT_LIST_HEAD(). It's still not clear to me why replacing the
list_splice() call by a list_splice_init() didn't work.
Also note that calling INIT_LIST_HEAD() only works if you have one
bridge in the chain, so if we go for that option we need a comment
explaining the limitations of this approach.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-24  9:44                 ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-24  9:44 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, Jernej Skrabec,
	dri-devel, Rob Herring, Kyungmin Park, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy,
	Marek Szyprowski

On Tue, 24 Dec 2019 10:16:49 +0100
Andrzej Hajda <a.hajda@samsung.com> wrote:

> On 23.12.2019 10:55, Marek Szyprowski wrote:
> > Hi Boris,
> >
> > On 16.12.2019 16:25, Boris Brezillon wrote:  
> >> On Mon, 16 Dec 2019 16:02:36 +0100
> >> Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
> >>> Hi Boris,
> >>>
> >>> On 16.12.2019 15:55, Boris Brezillon wrote:  
> >>>> On Mon, 16 Dec 2019 14:54:25 +0100
> >>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
> >>>>> On 03.12.2019 15:15, Boris Brezillon wrote:  
> >>>>>> So that each element in the chain can easily access its predecessor.
> >>>>>> This will be needed to support bus format negotiation between elements
> >>>>>> of the bridge chain.
> >>>>>>
> >>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> >>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> >>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>  
> >>>>> I've noticed that this patch got merged to linux-next as commit
> >>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> >>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> >>>>> messages:
> >>>>>
> >>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> >>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> >>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> >>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> >>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> >>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> >>>>> [drm] No driver support for vblank timestamp query.
> >>>>> [drm] Cannot find any crtc or sizes
> >>>>> [drm] Cannot find any crtc or sizes
> >>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> >>>>>
> >>>>> I will try to debug this and provide more information soon.
> >>>>>     
> >>>> Can you try with this diff applied?  
> >>> This patch doesn't change anything.  
> >> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> >> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> >> after the list_splice_init() call?  
> > encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> >
> > Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> > fixed the boot issue.

If INIT_LIST_HEAD() worked, I don't understand why replacing the
list_splice() call by a list_splice_init() (which doing a list_splice()
+ INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
list_splice_init() version doesn't work?

> > It looks that this is related with the way the 
> > Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> > will give a bit more detailed comment and spread some light on this.  
> 
> 
> Hi Marek, Boris,
> 
> 
> I have not followed latest patches due to high work load, my bad. Marek
> thanks from pointing
> 
> About ExynosDSI bridge handling:
> 
> The order of calling encoder, bridge (and consequently panel) ops
> enforced by DRM core (bridge->pre_enable, encoder->enable,
> bridge->enable) does not fit to ExynosDSI hardware initialization
> sequence, if I remember correctly it does not fit to whole MIPI DSI
> standard (I think similar situation is with eDP). As a result DSI
> drivers must use some ugly workarounds, rely on HW properly coping with
> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> using encoder->bridge chaining and call bridge ops by itself when suitable.

Yes, that's definitely hack-ish, and I proposed 2 solutions to address
that in previous versions of this patchset, unfortunately I didn't get
any feedback so I went for the less invasive option (keep the hack but
adapt it to the double-linked list changes), which still lead to
regressions :-/.

Just a reminder of my 2 proposals:

1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
   split your enable/disable logic in 2 parts and make sure things are
   ready when the panel/next bridge tries to send DSI commands
2/ move everything that's needed to send DSI commands out of the
   ->enable() path (maybe in runtime PM resume/suspend hooks) so you
   can call that in the DSI transfer path too

As pointed out by Laurent, #1 doesn't work because some panel drivers
send DSI commands in their ->prepare() hook, and ->pre_enable() methods
are called in reverse order, meaning that the DRM panel bridge driver
would try to issue DSI commands before the DSI host controllers is ready
to send them. I still thing #2 is a good option.

> 
> So proper patch converting to double-linked list should not try to
> splice ExynosDSI private bridge list with with encoder's, encoder's list
> should be always empty, as Marek suggested.

That's exactly what I wanted to do: make the encoder's list empty after
attach() and restore it to its initial state before unregistering
the bridge, except I forgot that list_splice() doesn't call
INIT_LIST_HEAD(). It's still not clear to me why replacing the
list_splice() call by a list_splice_init() didn't work.
Also note that calling INIT_LIST_HEAD() only works if you have one
bridge in the chain, so if we go for that option we need a comment
explaining the limitations of this approach.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-24  9:44                 ` Boris Brezillon
@ 2019-12-24  9:49                   ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-24  9:49 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, Jernej Skrabec,
	dri-devel, Rob Herring, Kyungmin Park, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy,
	Marek Szyprowski

On Tue, 24 Dec 2019 10:44:22 +0100
Boris Brezillon <boris.brezillon@collabora.com> wrote:

> On Tue, 24 Dec 2019 10:16:49 +0100
> Andrzej Hajda <a.hajda@samsung.com> wrote:
> 
> > On 23.12.2019 10:55, Marek Szyprowski wrote:  
> > > Hi Boris,
> > >
> > > On 16.12.2019 16:25, Boris Brezillon wrote:    
> > >> On Mon, 16 Dec 2019 16:02:36 +0100
> > >> Marek Szyprowski <m.szyprowski@samsung.com> wrote:    
> > >>> Hi Boris,
> > >>>
> > >>> On 16.12.2019 15:55, Boris Brezillon wrote:    
> > >>>> On Mon, 16 Dec 2019 14:54:25 +0100
> > >>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:    
> > >>>>> On 03.12.2019 15:15, Boris Brezillon wrote:    
> > >>>>>> So that each element in the chain can easily access its predecessor.
> > >>>>>> This will be needed to support bus format negotiation between elements
> > >>>>>> of the bridge chain.
> > >>>>>>
> > >>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > >>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > >>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>    
> > >>>>> I've noticed that this patch got merged to linux-next as commit
> > >>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> > >>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> > >>>>> messages:
> > >>>>>
> > >>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> > >>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> > >>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> > >>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> > >>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> > >>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> > >>>>> [drm] No driver support for vblank timestamp query.
> > >>>>> [drm] Cannot find any crtc or sizes
> > >>>>> [drm] Cannot find any crtc or sizes
> > >>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> > >>>>>
> > >>>>> I will try to debug this and provide more information soon.
> > >>>>>       
> > >>>> Can you try with this diff applied?    
> > >>> This patch doesn't change anything.    
> > >> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> > >> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> > >> after the list_splice_init() call?    
> > > encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> > >
> > > Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> > > fixed the boot issue.  
> 
> If INIT_LIST_HEAD() worked, I don't understand why replacing the
> list_splice() call by a list_splice_init() (which doing a list_splice()
> + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> list_splice_init() version doesn't work?
> 
> > > It looks that this is related with the way the 
> > > Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> > > will give a bit more detailed comment and spread some light on this.    
> > 
> > 
> > Hi Marek, Boris,
> > 
> > 
> > I have not followed latest patches due to high work load, my bad. Marek
> > thanks from pointing
> > 
> > About ExynosDSI bridge handling:
> > 
> > The order of calling encoder, bridge (and consequently panel) ops
> > enforced by DRM core (bridge->pre_enable, encoder->enable,
> > bridge->enable) does not fit to ExynosDSI hardware initialization
> > sequence, if I remember correctly it does not fit to whole MIPI DSI
> > standard (I think similar situation is with eDP). As a result DSI
> > drivers must use some ugly workarounds, rely on HW properly coping with
> > incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> > using encoder->bridge chaining and call bridge ops by itself when suitable.  
> 
> Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> that in previous versions of this patchset, unfortunately I didn't get
> any feedback so I went for the less invasive option (keep the hack but
> adapt it to the double-linked list changes), which still lead to
> regressions :-/.
> 
> Just a reminder of my 2 proposals:
> 
> 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
>    split your enable/disable logic in 2 parts and make sure things are
>    ready when the panel/next bridge tries to send DSI commands
> 2/ move everything that's needed to send DSI commands out of the
>    ->enable() path (maybe in runtime PM resume/suspend hooks) so you  
>    can call that in the DSI transfer path too
> 
> As pointed out by Laurent, #1 doesn't work because some panel drivers
> send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> are called in reverse order, meaning that the DRM panel bridge driver
> would try to issue DSI commands before the DSI host controllers is ready
> to send them. I still thing #2 is a good option.
> 
> > 
> > So proper patch converting to double-linked list should not try to
> > splice ExynosDSI private bridge list with with encoder's, encoder's list
> > should be always empty, as Marek suggested.  
> 
> That's exactly what I wanted to do: make the encoder's list empty after
> attach() and restore it to its initial state before unregistering
> the bridge, except I forgot that list_splice() doesn't call
> INIT_LIST_HEAD(). It's still not clear to me why replacing the
> list_splice() call by a list_splice_init() didn't work.

Okay, I think I figured it out: drm_bridge_chain_xx() helpers use
encoder->bridge_chain as their list head, and you'll never hit the 'elem
is list head' condition since we moved all elems from
encoder->bridge_chain to exynos_dsi->bridge_chain. The only way this
can work is if we stop using the helpers and implement our own list
iterators.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-24  9:49                   ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-24  9:49 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Mark Rutland, Nikita Yushchenko, Jernej Skrabec, Jonas Karlman,
	Andrey Smirnov, Neil Armstrong, Seung-Woo Kim, Kyungmin Park,
	dri-devel, devicetree, Rob Herring, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy,
	Marek Szyprowski

On Tue, 24 Dec 2019 10:44:22 +0100
Boris Brezillon <boris.brezillon@collabora.com> wrote:

> On Tue, 24 Dec 2019 10:16:49 +0100
> Andrzej Hajda <a.hajda@samsung.com> wrote:
> 
> > On 23.12.2019 10:55, Marek Szyprowski wrote:  
> > > Hi Boris,
> > >
> > > On 16.12.2019 16:25, Boris Brezillon wrote:    
> > >> On Mon, 16 Dec 2019 16:02:36 +0100
> > >> Marek Szyprowski <m.szyprowski@samsung.com> wrote:    
> > >>> Hi Boris,
> > >>>
> > >>> On 16.12.2019 15:55, Boris Brezillon wrote:    
> > >>>> On Mon, 16 Dec 2019 14:54:25 +0100
> > >>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:    
> > >>>>> On 03.12.2019 15:15, Boris Brezillon wrote:    
> > >>>>>> So that each element in the chain can easily access its predecessor.
> > >>>>>> This will be needed to support bus format negotiation between elements
> > >>>>>> of the bridge chain.
> > >>>>>>
> > >>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > >>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > >>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>    
> > >>>>> I've noticed that this patch got merged to linux-next as commit
> > >>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> > >>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> > >>>>> messages:
> > >>>>>
> > >>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> > >>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> > >>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> > >>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> > >>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> > >>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> > >>>>> [drm] No driver support for vblank timestamp query.
> > >>>>> [drm] Cannot find any crtc or sizes
> > >>>>> [drm] Cannot find any crtc or sizes
> > >>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> > >>>>>
> > >>>>> I will try to debug this and provide more information soon.
> > >>>>>       
> > >>>> Can you try with this diff applied?    
> > >>> This patch doesn't change anything.    
> > >> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> > >> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> > >> after the list_splice_init() call?    
> > > encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> > >
> > > Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> > > fixed the boot issue.  
> 
> If INIT_LIST_HEAD() worked, I don't understand why replacing the
> list_splice() call by a list_splice_init() (which doing a list_splice()
> + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> list_splice_init() version doesn't work?
> 
> > > It looks that this is related with the way the 
> > > Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> > > will give a bit more detailed comment and spread some light on this.    
> > 
> > 
> > Hi Marek, Boris,
> > 
> > 
> > I have not followed latest patches due to high work load, my bad. Marek
> > thanks from pointing
> > 
> > About ExynosDSI bridge handling:
> > 
> > The order of calling encoder, bridge (and consequently panel) ops
> > enforced by DRM core (bridge->pre_enable, encoder->enable,
> > bridge->enable) does not fit to ExynosDSI hardware initialization
> > sequence, if I remember correctly it does not fit to whole MIPI DSI
> > standard (I think similar situation is with eDP). As a result DSI
> > drivers must use some ugly workarounds, rely on HW properly coping with
> > incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> > using encoder->bridge chaining and call bridge ops by itself when suitable.  
> 
> Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> that in previous versions of this patchset, unfortunately I didn't get
> any feedback so I went for the less invasive option (keep the hack but
> adapt it to the double-linked list changes), which still lead to
> regressions :-/.
> 
> Just a reminder of my 2 proposals:
> 
> 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
>    split your enable/disable logic in 2 parts and make sure things are
>    ready when the panel/next bridge tries to send DSI commands
> 2/ move everything that's needed to send DSI commands out of the
>    ->enable() path (maybe in runtime PM resume/suspend hooks) so you  
>    can call that in the DSI transfer path too
> 
> As pointed out by Laurent, #1 doesn't work because some panel drivers
> send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> are called in reverse order, meaning that the DRM panel bridge driver
> would try to issue DSI commands before the DSI host controllers is ready
> to send them. I still thing #2 is a good option.
> 
> > 
> > So proper patch converting to double-linked list should not try to
> > splice ExynosDSI private bridge list with with encoder's, encoder's list
> > should be always empty, as Marek suggested.  
> 
> That's exactly what I wanted to do: make the encoder's list empty after
> attach() and restore it to its initial state before unregistering
> the bridge, except I forgot that list_splice() doesn't call
> INIT_LIST_HEAD(). It's still not clear to me why replacing the
> list_splice() call by a list_splice_init() didn't work.

Okay, I think I figured it out: drm_bridge_chain_xx() helpers use
encoder->bridge_chain as their list head, and you'll never hit the 'elem
is list head' condition since we moved all elems from
encoder->bridge_chain to exynos_dsi->bridge_chain. The only way this
can work is if we stop using the helpers and implement our own list
iterators.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-24  9:49                   ` Boris Brezillon
@ 2019-12-24 10:03                     ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-24 10:03 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Mark Rutland, Nikita Yushchenko, Jernej Skrabec, Jonas Karlman,
	Andrey Smirnov, Neil Armstrong, Seung-Woo Kim, Kyungmin Park,
	dri-devel, devicetree, Rob Herring, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy,
	Marek Szyprowski

On Tue, 24 Dec 2019 10:49:36 +0100
Boris Brezillon <boris.brezillon@collabora.com> wrote:

> On Tue, 24 Dec 2019 10:44:22 +0100
> Boris Brezillon <boris.brezillon@collabora.com> wrote:
> 
> > On Tue, 24 Dec 2019 10:16:49 +0100
> > Andrzej Hajda <a.hajda@samsung.com> wrote:
> >   
> > > On 23.12.2019 10:55, Marek Szyprowski wrote:    
> > > > Hi Boris,
> > > >
> > > > On 16.12.2019 16:25, Boris Brezillon wrote:      
> > > >> On Mon, 16 Dec 2019 16:02:36 +0100
> > > >> Marek Szyprowski <m.szyprowski@samsung.com> wrote:      
> > > >>> Hi Boris,
> > > >>>
> > > >>> On 16.12.2019 15:55, Boris Brezillon wrote:      
> > > >>>> On Mon, 16 Dec 2019 14:54:25 +0100
> > > >>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:      
> > > >>>>> On 03.12.2019 15:15, Boris Brezillon wrote:      
> > > >>>>>> So that each element in the chain can easily access its predecessor.
> > > >>>>>> This will be needed to support bus format negotiation between elements
> > > >>>>>> of the bridge chain.
> > > >>>>>>
> > > >>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > >>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > > >>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>      
> > > >>>>> I've noticed that this patch got merged to linux-next as commit
> > > >>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> > > >>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> > > >>>>> messages:
> > > >>>>>
> > > >>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> > > >>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> > > >>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> > > >>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> > > >>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> > > >>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> > > >>>>> [drm] No driver support for vblank timestamp query.
> > > >>>>> [drm] Cannot find any crtc or sizes
> > > >>>>> [drm] Cannot find any crtc or sizes
> > > >>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> > > >>>>>
> > > >>>>> I will try to debug this and provide more information soon.
> > > >>>>>         
> > > >>>> Can you try with this diff applied?      
> > > >>> This patch doesn't change anything.      
> > > >> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> > > >> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> > > >> after the list_splice_init() call?      
> > > > encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> > > >
> > > > Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> > > > fixed the boot issue.    
> > 
> > If INIT_LIST_HEAD() worked, I don't understand why replacing the
> > list_splice() call by a list_splice_init() (which doing a list_splice()
> > + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> > list_splice_init() version doesn't work?
> >   
> > > > It looks that this is related with the way the 
> > > > Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> > > > will give a bit more detailed comment and spread some light on this.      
> > > 
> > > 
> > > Hi Marek, Boris,
> > > 
> > > 
> > > I have not followed latest patches due to high work load, my bad. Marek
> > > thanks from pointing
> > > 
> > > About ExynosDSI bridge handling:
> > > 
> > > The order of calling encoder, bridge (and consequently panel) ops
> > > enforced by DRM core (bridge->pre_enable, encoder->enable,
> > > bridge->enable) does not fit to ExynosDSI hardware initialization
> > > sequence, if I remember correctly it does not fit to whole MIPI DSI
> > > standard (I think similar situation is with eDP). As a result DSI
> > > drivers must use some ugly workarounds, rely on HW properly coping with
> > > incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> > > using encoder->bridge chaining and call bridge ops by itself when suitable.    
> > 
> > Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> > that in previous versions of this patchset, unfortunately I didn't get
> > any feedback so I went for the less invasive option (keep the hack but
> > adapt it to the double-linked list changes), which still lead to
> > regressions :-/.
> > 
> > Just a reminder of my 2 proposals:
> > 
> > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> >    split your enable/disable logic in 2 parts and make sure things are
> >    ready when the panel/next bridge tries to send DSI commands
> > 2/ move everything that's needed to send DSI commands out of the  
> >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you    
> >    can call that in the DSI transfer path too
> > 
> > As pointed out by Laurent, #1 doesn't work because some panel drivers
> > send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> > are called in reverse order, meaning that the DRM panel bridge driver
> > would try to issue DSI commands before the DSI host controllers is ready
> > to send them. I still thing #2 is a good option.
> >   
> > > 
> > > So proper patch converting to double-linked list should not try to
> > > splice ExynosDSI private bridge list with with encoder's, encoder's list
> > > should be always empty, as Marek suggested.    
> > 
> > That's exactly what I wanted to do: make the encoder's list empty after
> > attach() and restore it to its initial state before unregistering
> > the bridge, except I forgot that list_splice() doesn't call
> > INIT_LIST_HEAD(). It's still not clear to me why replacing the
> > list_splice() call by a list_splice_init() didn't work.  
> 
> Okay, I think I figured it out: drm_bridge_chain_xx() helpers use
> encoder->bridge_chain as their list head, and you'll never hit the 'elem
> is list head' condition since we moved all elems from
> encoder->bridge_chain to exynos_dsi->bridge_chain. The only way this
> can work is if we stop using the helpers and implement our own list
> iterators.

Just to make it clear, calling INIT_LIST_HEAD(encoder->bridge_chain)
doesn't really fix the bug, it just prevents the hang (infinite loop)
and turn all drm_bridge_chain_xx() calls into NOPs.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-24 10:03                     ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-24 10:03 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Mark Rutland, Nikita Yushchenko, Jernej Skrabec, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, dri-devel,
	Kyungmin Park, Rob Herring, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Chris Healy, devicetree, Marek Szyprowski

On Tue, 24 Dec 2019 10:49:36 +0100
Boris Brezillon <boris.brezillon@collabora.com> wrote:

> On Tue, 24 Dec 2019 10:44:22 +0100
> Boris Brezillon <boris.brezillon@collabora.com> wrote:
> 
> > On Tue, 24 Dec 2019 10:16:49 +0100
> > Andrzej Hajda <a.hajda@samsung.com> wrote:
> >   
> > > On 23.12.2019 10:55, Marek Szyprowski wrote:    
> > > > Hi Boris,
> > > >
> > > > On 16.12.2019 16:25, Boris Brezillon wrote:      
> > > >> On Mon, 16 Dec 2019 16:02:36 +0100
> > > >> Marek Szyprowski <m.szyprowski@samsung.com> wrote:      
> > > >>> Hi Boris,
> > > >>>
> > > >>> On 16.12.2019 15:55, Boris Brezillon wrote:      
> > > >>>> On Mon, 16 Dec 2019 14:54:25 +0100
> > > >>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:      
> > > >>>>> On 03.12.2019 15:15, Boris Brezillon wrote:      
> > > >>>>>> So that each element in the chain can easily access its predecessor.
> > > >>>>>> This will be needed to support bus format negotiation between elements
> > > >>>>>> of the bridge chain.
> > > >>>>>>
> > > >>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > >>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > > >>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>      
> > > >>>>> I've noticed that this patch got merged to linux-next as commit
> > > >>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> > > >>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> > > >>>>> messages:
> > > >>>>>
> > > >>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> > > >>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> > > >>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> > > >>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> > > >>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> > > >>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> > > >>>>> [drm] No driver support for vblank timestamp query.
> > > >>>>> [drm] Cannot find any crtc or sizes
> > > >>>>> [drm] Cannot find any crtc or sizes
> > > >>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> > > >>>>>
> > > >>>>> I will try to debug this and provide more information soon.
> > > >>>>>         
> > > >>>> Can you try with this diff applied?      
> > > >>> This patch doesn't change anything.      
> > > >> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> > > >> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> > > >> after the list_splice_init() call?      
> > > > encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> > > >
> > > > Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> > > > fixed the boot issue.    
> > 
> > If INIT_LIST_HEAD() worked, I don't understand why replacing the
> > list_splice() call by a list_splice_init() (which doing a list_splice()
> > + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> > list_splice_init() version doesn't work?
> >   
> > > > It looks that this is related with the way the 
> > > > Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> > > > will give a bit more detailed comment and spread some light on this.      
> > > 
> > > 
> > > Hi Marek, Boris,
> > > 
> > > 
> > > I have not followed latest patches due to high work load, my bad. Marek
> > > thanks from pointing
> > > 
> > > About ExynosDSI bridge handling:
> > > 
> > > The order of calling encoder, bridge (and consequently panel) ops
> > > enforced by DRM core (bridge->pre_enable, encoder->enable,
> > > bridge->enable) does not fit to ExynosDSI hardware initialization
> > > sequence, if I remember correctly it does not fit to whole MIPI DSI
> > > standard (I think similar situation is with eDP). As a result DSI
> > > drivers must use some ugly workarounds, rely on HW properly coping with
> > > incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> > > using encoder->bridge chaining and call bridge ops by itself when suitable.    
> > 
> > Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> > that in previous versions of this patchset, unfortunately I didn't get
> > any feedback so I went for the less invasive option (keep the hack but
> > adapt it to the double-linked list changes), which still lead to
> > regressions :-/.
> > 
> > Just a reminder of my 2 proposals:
> > 
> > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> >    split your enable/disable logic in 2 parts and make sure things are
> >    ready when the panel/next bridge tries to send DSI commands
> > 2/ move everything that's needed to send DSI commands out of the  
> >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you    
> >    can call that in the DSI transfer path too
> > 
> > As pointed out by Laurent, #1 doesn't work because some panel drivers
> > send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> > are called in reverse order, meaning that the DRM panel bridge driver
> > would try to issue DSI commands before the DSI host controllers is ready
> > to send them. I still thing #2 is a good option.
> >   
> > > 
> > > So proper patch converting to double-linked list should not try to
> > > splice ExynosDSI private bridge list with with encoder's, encoder's list
> > > should be always empty, as Marek suggested.    
> > 
> > That's exactly what I wanted to do: make the encoder's list empty after
> > attach() and restore it to its initial state before unregistering
> > the bridge, except I forgot that list_splice() doesn't call
> > INIT_LIST_HEAD(). It's still not clear to me why replacing the
> > list_splice() call by a list_splice_init() didn't work.  
> 
> Okay, I think I figured it out: drm_bridge_chain_xx() helpers use
> encoder->bridge_chain as their list head, and you'll never hit the 'elem
> is list head' condition since we moved all elems from
> encoder->bridge_chain to exynos_dsi->bridge_chain. The only way this
> can work is if we stop using the helpers and implement our own list
> iterators.

Just to make it clear, calling INIT_LIST_HEAD(encoder->bridge_chain)
doesn't really fix the bug, it just prevents the hang (infinite loop)
and turn all drm_bridge_chain_xx() calls into NOPs.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-24  9:44                 ` Boris Brezillon
@ 2019-12-24 11:31                   ` Sam Ravnborg
  -1 siblings, 0 replies; 84+ messages in thread
From: Sam Ravnborg @ 2019-12-24 11:31 UTC (permalink / raw)
  To: Boris Brezillon, Jitao Shi
  Cc: Andrzej Hajda, Marek Szyprowski, dri-devel, Mark Rutland,
	Neil Armstrong, Thierry Reding, Laurent Pinchart, kernel,
	Nikita Yushchenko, Andrey Smirnov, Kyungmin Park, Chris Healy,
	devicetree, Jonas Karlman, Rob Herring, Jernej Skrabec,
	Seung-Woo Kim

Hi Boris.

> Just a reminder of my 2 proposals:
> 
> 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
>    split your enable/disable logic in 2 parts and make sure things are
>    ready when the panel/next bridge tries to send DSI commands
> 2/ move everything that's needed to send DSI commands out of the
>    ->enable() path (maybe in runtime PM resume/suspend hooks) so you
>    can call that in the DSI transfer path too
> 
> As pointed out by Laurent, #1 doesn't work because some panel drivers
> send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> are called in reverse order, meaning that the DRM panel bridge driver
> would try to issue DSI commands before the DSI host controllers is ready
> to send them. I still thing #2 is a good option.

Jitao Shi suggested to extend panels so we had a sequence of:

  prepare_power()  <= new callback,
                   here one should NOT be allowed to send
                   DSI commands
  prepare()
  enable()

   #
   # panel is now ready to show your favourite christmas movie
   #

  disable()
  unprepare()
  unprepare_power()  <= new callback


Would this help implement what you suggest above?
Relevant panels would then have to be updated - but this
is doable.

	Sam

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-24 11:31                   ` Sam Ravnborg
  0 siblings, 0 replies; 84+ messages in thread
From: Sam Ravnborg @ 2019-12-24 11:31 UTC (permalink / raw)
  To: Boris Brezillon, Jitao Shi
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Rob Herring,
	Neil Armstrong, Andrey Smirnov, Jonas Karlman, Seung-Woo Kim,
	Jernej Skrabec, dri-devel, Kyungmin Park, Thierry Reding,
	Laurent Pinchart, kernel, Chris Healy, Marek Szyprowski

Hi Boris.

> Just a reminder of my 2 proposals:
> 
> 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
>    split your enable/disable logic in 2 parts and make sure things are
>    ready when the panel/next bridge tries to send DSI commands
> 2/ move everything that's needed to send DSI commands out of the
>    ->enable() path (maybe in runtime PM resume/suspend hooks) so you
>    can call that in the DSI transfer path too
> 
> As pointed out by Laurent, #1 doesn't work because some panel drivers
> send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> are called in reverse order, meaning that the DRM panel bridge driver
> would try to issue DSI commands before the DSI host controllers is ready
> to send them. I still thing #2 is a good option.

Jitao Shi suggested to extend panels so we had a sequence of:

  prepare_power()  <= new callback,
                   here one should NOT be allowed to send
                   DSI commands
  prepare()
  enable()

   #
   # panel is now ready to show your favourite christmas movie
   #

  disable()
  unprepare()
  unprepare_power()  <= new callback


Would this help implement what you suggest above?
Relevant panels would then have to be updated - but this
is doable.

	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-24 11:31                   ` Sam Ravnborg
@ 2019-12-25  1:36                     ` Laurent Pinchart
  -1 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-25  1:36 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Boris Brezillon, Jitao Shi, Andrzej Hajda, Marek Szyprowski,
	dri-devel, Mark Rutland, Neil Armstrong, Thierry Reding, kernel,
	Nikita Yushchenko, Andrey Smirnov, Kyungmin Park, Chris Healy,
	devicetree, Jonas Karlman, Rob Herring, Jernej Skrabec,
	Seung-Woo Kim

Hi Sam,

On Tue, Dec 24, 2019 at 12:31:11PM +0100, Sam Ravnborg wrote:
> > Just a reminder of my 2 proposals:
> > 
> > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> >    split your enable/disable logic in 2 parts and make sure things are
> >    ready when the panel/next bridge tries to send DSI commands
> > 2/ move everything that's needed to send DSI commands out of the
> >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you
> >    can call that in the DSI transfer path too
> > 
> > As pointed out by Laurent, #1 doesn't work because some panel drivers
> > send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> > are called in reverse order, meaning that the DRM panel bridge driver
> > would try to issue DSI commands before the DSI host controllers is ready
> > to send them. I still thing #2 is a good option.
> 
> Jitao Shi suggested to extend panels so we had a sequence of:
> 
>   prepare_power()  <= new callback,
>                    here one should NOT be allowed to send
>                    DSI commands
>   prepare()
>   enable()
> 
>    #
>    # panel is now ready to show your favourite christmas movie
>    #
> 
>   disable()
>   unprepare()
>   unprepare_power()  <= new callback

Please note that you will need corresponding bridge operations in order
to implement the DRM panel bridge.

> Would this help implement what you suggest above?
> Relevant panels would then have to be updated - but this
> is doable.

The fundamental issue is that the enable/disable sequence requirements
are inherently driven by sinks, while the bridge API goes from source to
sink. A DSI panel could for instance require the following enable
sequence.

0. DSI bus disabled (LP-00 state)
1. Power up the panel
2. Enable the DSI bus (LP-11 state)
3. Configure the panel (through DCS LP mode writes, SPI, GPIOs, ...)
4. Transition the source to HS mode, sending sync packets, but no data
5. Perform additional configuration for the panel
6. Enable transmission of video from the source

For a given bus type there are thus well-specified states for a source
(in this case disabled, enabled in LP mode, in HS mode, sending sync
packets, sending video packets).

As our API operates from source to sink, fine-grained control of bridges
is difficult. We can of course always make bridge and panel operations
more fine-grained, but that's more of a ad-hoc solution that may lead to
abuse. We would at the very least document very explicitly what each
operation would be allowed to do for every bus type.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-25  1:36                     ` Laurent Pinchart
  0 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-25  1:36 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Jitao Shi,
	Rob Herring, Neil Armstrong, Andrey Smirnov, Jonas Karlman,
	Seung-Woo Kim, Jernej Skrabec, dri-devel, Kyungmin Park,
	Boris Brezillon, Thierry Reding, kernel, Chris Healy,
	Marek Szyprowski

Hi Sam,

On Tue, Dec 24, 2019 at 12:31:11PM +0100, Sam Ravnborg wrote:
> > Just a reminder of my 2 proposals:
> > 
> > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> >    split your enable/disable logic in 2 parts and make sure things are
> >    ready when the panel/next bridge tries to send DSI commands
> > 2/ move everything that's needed to send DSI commands out of the
> >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you
> >    can call that in the DSI transfer path too
> > 
> > As pointed out by Laurent, #1 doesn't work because some panel drivers
> > send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> > are called in reverse order, meaning that the DRM panel bridge driver
> > would try to issue DSI commands before the DSI host controllers is ready
> > to send them. I still thing #2 is a good option.
> 
> Jitao Shi suggested to extend panels so we had a sequence of:
> 
>   prepare_power()  <= new callback,
>                    here one should NOT be allowed to send
>                    DSI commands
>   prepare()
>   enable()
> 
>    #
>    # panel is now ready to show your favourite christmas movie
>    #
> 
>   disable()
>   unprepare()
>   unprepare_power()  <= new callback

Please note that you will need corresponding bridge operations in order
to implement the DRM panel bridge.

> Would this help implement what you suggest above?
> Relevant panels would then have to be updated - but this
> is doable.

The fundamental issue is that the enable/disable sequence requirements
are inherently driven by sinks, while the bridge API goes from source to
sink. A DSI panel could for instance require the following enable
sequence.

0. DSI bus disabled (LP-00 state)
1. Power up the panel
2. Enable the DSI bus (LP-11 state)
3. Configure the panel (through DCS LP mode writes, SPI, GPIOs, ...)
4. Transition the source to HS mode, sending sync packets, but no data
5. Perform additional configuration for the panel
6. Enable transmission of video from the source

For a given bus type there are thus well-specified states for a source
(in this case disabled, enabled in LP mode, in HS mode, sending sync
packets, sending video packets).

As our API operates from source to sink, fine-grained control of bridges
is difficult. We can of course always make bridge and panel operations
more fine-grained, but that's more of a ad-hoc solution that may lead to
abuse. We would at the very least document very explicitly what each
operation would be allowed to do for every bus type.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-24  9:44                 ` Boris Brezillon
@ 2019-12-27  9:42                   ` Andrzej Hajda
  -1 siblings, 0 replies; 84+ messages in thread
From: Andrzej Hajda @ 2019-12-27  9:42 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Marek Szyprowski, dri-devel, Mark Rutland, Neil Armstrong,
	Thierry Reding, Laurent Pinchart, kernel, Sam Ravnborg,
	Nikita Yushchenko, Andrey Smirnov, Kyungmin Park, Chris Healy,
	devicetree, Jonas Karlman, Rob Herring, Jernej Skrabec,
	Seung-Woo Kim

On 24.12.2019 10:44, Boris Brezillon wrote:
> On Tue, 24 Dec 2019 10:16:49 +0100
> Andrzej Hajda <a.hajda@samsung.com> wrote:
>
>> On 23.12.2019 10:55, Marek Szyprowski wrote:
>>> Hi Boris,
>>>
>>> On 16.12.2019 16:25, Boris Brezillon wrote:  
>>>> On Mon, 16 Dec 2019 16:02:36 +0100
>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
>>>>> Hi Boris,
>>>>>
>>>>> On 16.12.2019 15:55, Boris Brezillon wrote:  
>>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
>>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
>>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:  
>>>>>>>> So that each element in the chain can easily access its predecessor.
>>>>>>>> This will be needed to support bus format negotiation between elements
>>>>>>>> of the bridge chain.
>>>>>>>>
>>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>  
>>>>>>> I've noticed that this patch got merged to linux-next as commit
>>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
>>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
>>>>>>> messages:
>>>>>>>
>>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
>>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
>>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
>>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
>>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
>>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>>>>>>> [drm] No driver support for vblank timestamp query.
>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
>>>>>>>
>>>>>>> I will try to debug this and provide more information soon.
>>>>>>>     
>>>>>> Can you try with this diff applied?  
>>>>> This patch doesn't change anything.  
>>>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
>>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
>>>> after the list_splice_init() call?  
>>> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
>>>
>>> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
>>> fixed the boot issue.
> If INIT_LIST_HEAD() worked, I don't understand why replacing the
> list_splice() call by a list_splice_init() (which doing a list_splice()
> + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> list_splice_init() version doesn't work?
>
>>> It looks that this is related with the way the 
>>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
>>> will give a bit more detailed comment and spread some light on this.  
>>
>> Hi Marek, Boris,
>>
>>
>> I have not followed latest patches due to high work load, my bad. Marek
>> thanks from pointing
>>
>> About ExynosDSI bridge handling:
>>
>> The order of calling encoder, bridge (and consequently panel) ops
>> enforced by DRM core (bridge->pre_enable, encoder->enable,
>> bridge->enable) does not fit to ExynosDSI hardware initialization
>> sequence, if I remember correctly it does not fit to whole MIPI DSI
>> standard (I think similar situation is with eDP). As a result DSI
>> drivers must use some ugly workarounds, rely on HW properly coping with
>> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
>> using encoder->bridge chaining and call bridge ops by itself when suitable.
> Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> that in previous versions of this patchset, unfortunately I didn't get
> any feedback so I went for the less invasive option (keep the hack but
> adapt it to the double-linked list changes), which still lead to
> regressions :-/.
>
> Just a reminder of my 2 proposals:
>
> 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
>    split your enable/disable logic in 2 parts and make sure things are
>    ready when the panel/next bridge tries to send DSI commands


If it means 'convert exynos_dsi to bridge' I do not think it will help -

- pre_enable op will be still called after pre_enable op of downstream
bridge - and this is the main reason why exynos_dsi do not use encoder
bridge chain - it needs to perform some operations BEFORE (pre)enabling
downstream devices.


> 2/ move everything that's needed to send DSI commands out of the
>    ->enable() path (maybe in runtime PM resume/suspend hooks) so you
>    can call that in the DSI transfer path too


It looks like a solution for DSI protocol, where control bus is shared
with data bus, but the problem is more general - we have source and sink
connected with some local bus, which has some negotiation/enable/disable
protocol/requirements. And drm_core/bridge framework enforces us to fit
every such protocol to 'drm_bridge protocol' with few opses called in
fixed order, without clearly defined purpose of each ops. That does not
sound generic and results in multiple issues:

- different drivers uses different opses to perform the same thing,

- different drivers assumes different things about their sinks/sources
in their opses,

- more complicated sequences does not fit at all to this model.

All this results in incompatibilities between drivers which become
visible with devices used in different configurations/platforms.


Regards

Andrzej


>
> As pointed out by Laurent, #1 doesn't work because some panel drivers
> send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> are called in reverse order, meaning that the DRM panel bridge driver
> would try to issue DSI commands before the DSI host controllers is ready
> to send them. I still thing #2 is a good option.
>
>> So proper patch converting to double-linked list should not try to
>> splice ExynosDSI private bridge list with with encoder's, encoder's list
>> should be always empty, as Marek suggested.
> That's exactly what I wanted to do: make the encoder's list empty after
> attach() and restore it to its initial state before unregistering
> the bridge, except I forgot that list_splice() doesn't call
> INIT_LIST_HEAD(). It's still not clear to me why replacing the
> list_splice() call by a list_splice_init() didn't work.
> Also note that calling INIT_LIST_HEAD() only works if you have one
> bridge in the chain, so if we go for that option we need a comment
> explaining the limitations of this approach.
>


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-27  9:42                   ` Andrzej Hajda
  0 siblings, 0 replies; 84+ messages in thread
From: Andrzej Hajda @ 2019-12-27  9:42 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, Jernej Skrabec,
	dri-devel, Rob Herring, Kyungmin Park, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy,
	Marek Szyprowski

On 24.12.2019 10:44, Boris Brezillon wrote:
> On Tue, 24 Dec 2019 10:16:49 +0100
> Andrzej Hajda <a.hajda@samsung.com> wrote:
>
>> On 23.12.2019 10:55, Marek Szyprowski wrote:
>>> Hi Boris,
>>>
>>> On 16.12.2019 16:25, Boris Brezillon wrote:  
>>>> On Mon, 16 Dec 2019 16:02:36 +0100
>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
>>>>> Hi Boris,
>>>>>
>>>>> On 16.12.2019 15:55, Boris Brezillon wrote:  
>>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
>>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
>>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:  
>>>>>>>> So that each element in the chain can easily access its predecessor.
>>>>>>>> This will be needed to support bus format negotiation between elements
>>>>>>>> of the bridge chain.
>>>>>>>>
>>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>  
>>>>>>> I've noticed that this patch got merged to linux-next as commit
>>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
>>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
>>>>>>> messages:
>>>>>>>
>>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
>>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
>>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
>>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
>>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
>>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>>>>>>> [drm] No driver support for vblank timestamp query.
>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
>>>>>>>
>>>>>>> I will try to debug this and provide more information soon.
>>>>>>>     
>>>>>> Can you try with this diff applied?  
>>>>> This patch doesn't change anything.  
>>>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
>>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
>>>> after the list_splice_init() call?  
>>> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
>>>
>>> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
>>> fixed the boot issue.
> If INIT_LIST_HEAD() worked, I don't understand why replacing the
> list_splice() call by a list_splice_init() (which doing a list_splice()
> + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> list_splice_init() version doesn't work?
>
>>> It looks that this is related with the way the 
>>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
>>> will give a bit more detailed comment and spread some light on this.  
>>
>> Hi Marek, Boris,
>>
>>
>> I have not followed latest patches due to high work load, my bad. Marek
>> thanks from pointing
>>
>> About ExynosDSI bridge handling:
>>
>> The order of calling encoder, bridge (and consequently panel) ops
>> enforced by DRM core (bridge->pre_enable, encoder->enable,
>> bridge->enable) does not fit to ExynosDSI hardware initialization
>> sequence, if I remember correctly it does not fit to whole MIPI DSI
>> standard (I think similar situation is with eDP). As a result DSI
>> drivers must use some ugly workarounds, rely on HW properly coping with
>> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
>> using encoder->bridge chaining and call bridge ops by itself when suitable.
> Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> that in previous versions of this patchset, unfortunately I didn't get
> any feedback so I went for the less invasive option (keep the hack but
> adapt it to the double-linked list changes), which still lead to
> regressions :-/.
>
> Just a reminder of my 2 proposals:
>
> 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
>    split your enable/disable logic in 2 parts and make sure things are
>    ready when the panel/next bridge tries to send DSI commands


If it means 'convert exynos_dsi to bridge' I do not think it will help -

- pre_enable op will be still called after pre_enable op of downstream
bridge - and this is the main reason why exynos_dsi do not use encoder
bridge chain - it needs to perform some operations BEFORE (pre)enabling
downstream devices.


> 2/ move everything that's needed to send DSI commands out of the
>    ->enable() path (maybe in runtime PM resume/suspend hooks) so you
>    can call that in the DSI transfer path too


It looks like a solution for DSI protocol, where control bus is shared
with data bus, but the problem is more general - we have source and sink
connected with some local bus, which has some negotiation/enable/disable
protocol/requirements. And drm_core/bridge framework enforces us to fit
every such protocol to 'drm_bridge protocol' with few opses called in
fixed order, without clearly defined purpose of each ops. That does not
sound generic and results in multiple issues:

- different drivers uses different opses to perform the same thing,

- different drivers assumes different things about their sinks/sources
in their opses,

- more complicated sequences does not fit at all to this model.

All this results in incompatibilities between drivers which become
visible with devices used in different configurations/platforms.


Regards

Andrzej


>
> As pointed out by Laurent, #1 doesn't work because some panel drivers
> send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> are called in reverse order, meaning that the DRM panel bridge driver
> would try to issue DSI commands before the DSI host controllers is ready
> to send them. I still thing #2 is a good option.
>
>> So proper patch converting to double-linked list should not try to
>> splice ExynosDSI private bridge list with with encoder's, encoder's list
>> should be always empty, as Marek suggested.
> That's exactly what I wanted to do: make the encoder's list empty after
> attach() and restore it to its initial state before unregistering
> the bridge, except I forgot that list_splice() doesn't call
> INIT_LIST_HEAD(). It's still not clear to me why replacing the
> list_splice() call by a list_splice_init() didn't work.
> Also note that calling INIT_LIST_HEAD() only works if you have one
> bridge in the chain, so if we go for that option we need a comment
> explaining the limitations of this approach.
>

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-24 10:03                     ` Boris Brezillon
@ 2019-12-27 10:25                       ` Marek Szyprowski
  -1 siblings, 0 replies; 84+ messages in thread
From: Marek Szyprowski @ 2019-12-27 10:25 UTC (permalink / raw)
  To: Boris Brezillon, Andrzej Hajda
  Cc: Mark Rutland, Nikita Yushchenko, Jernej Skrabec, Jonas Karlman,
	Andrey Smirnov, Neil Armstrong, Seung-Woo Kim, Kyungmin Park,
	dri-devel, devicetree, Rob Herring, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy

Hi Boris,

On 24.12.2019 11:03, Boris Brezillon wrote:
> On Tue, 24 Dec 2019 10:49:36 +0100
> Boris Brezillon <boris.brezillon@collabora.com> wrote:
>> On Tue, 24 Dec 2019 10:44:22 +0100
>> Boris Brezillon <boris.brezillon@collabora.com> wrote:
>>> On Tue, 24 Dec 2019 10:16:49 +0100
>>> Andrzej Hajda <a.hajda@samsung.com> wrote:
>>>> On 23.12.2019 10:55, Marek Szyprowski wrote:
>>>>> On 16.12.2019 16:25, Boris Brezillon wrote:
>>>>>> On Mon, 16 Dec 2019 16:02:36 +0100
>>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>>>>> On 16.12.2019 15:55, Boris Brezillon wrote:
>>>>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
>>>>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:
>>>>>>>>>> So that each element in the chain can easily access its predecessor.
>>>>>>>>>> This will be needed to support bus format negotiation between elements
>>>>>>>>>> of the bridge chain.
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>>>>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>>>>>>>> I've noticed that this patch got merged to linux-next as commit
>>>>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
>>>>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
>>>>>>>>> messages:
>>>>>>>>>
>>>>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
>>>>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
>>>>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
>>>>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
>>>>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
>>>>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>>>>>>>>> [drm] No driver support for vblank timestamp query.
>>>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
>>>>>>>>>
>>>>>>>>> I will try to debug this and provide more information soon.
>>>>>>>>>          
>>>>>>>> Can you try with this diff applied?
>>>>>>> This patch doesn't change anything.
>>>>>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
>>>>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
>>>>>> after the list_splice_init() call?
>>>>> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
>>>>>
>>>>> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain)
>>>>> fixed the boot issue.
>>> If INIT_LIST_HEAD() worked, I don't understand why replacing the
>>> list_splice() call by a list_splice_init() (which doing a list_splice()
>>> + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
>>> list_splice_init() version doesn't work?
>>>    
>>>>> It looks that this is related with the way the
>>>>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej
>>>>> will give a bit more detailed comment and spread some light on this.
>>>>
>>>> Hi Marek, Boris,
>>>>
>>>>
>>>> I have not followed latest patches due to high work load, my bad. Marek
>>>> thanks from pointing
>>>>
>>>> About ExynosDSI bridge handling:
>>>>
>>>> The order of calling encoder, bridge (and consequently panel) ops
>>>> enforced by DRM core (bridge->pre_enable, encoder->enable,
>>>> bridge->enable) does not fit to ExynosDSI hardware initialization
>>>> sequence, if I remember correctly it does not fit to whole MIPI DSI
>>>> standard (I think similar situation is with eDP). As a result DSI
>>>> drivers must use some ugly workarounds, rely on HW properly coping with
>>>> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
>>>> using encoder->bridge chaining and call bridge ops by itself when suitable.
>>> Yes, that's definitely hack-ish, and I proposed 2 solutions to address
>>> that in previous versions of this patchset, unfortunately I didn't get
>>> any feedback so I went for the less invasive option (keep the hack but
>>> adapt it to the double-linked list changes), which still lead to
>>> regressions :-/.
>>>
>>> Just a reminder of my 2 proposals:
>>>
>>> 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
>>>     split your enable/disable logic in 2 parts and make sure things are
>>>     ready when the panel/next bridge tries to send DSI commands
>>> 2/ move everything that's needed to send DSI commands out of the
>>>     ->enable() path (maybe in runtime PM resume/suspend hooks) so you
>>>     can call that in the DSI transfer path too
>>>
>>> As pointed out by Laurent, #1 doesn't work because some panel drivers
>>> send DSI commands in their ->prepare() hook, and ->pre_enable() methods
>>> are called in reverse order, meaning that the DRM panel bridge driver
>>> would try to issue DSI commands before the DSI host controllers is ready
>>> to send them. I still thing #2 is a good option.
>>>    
>>>> So proper patch converting to double-linked list should not try to
>>>> splice ExynosDSI private bridge list with with encoder's, encoder's list
>>>> should be always empty, as Marek suggested.
>>> That's exactly what I wanted to do: make the encoder's list empty after
>>> attach() and restore it to its initial state before unregistering
>>> the bridge, except I forgot that list_splice() doesn't call
>>> INIT_LIST_HEAD(). It's still not clear to me why replacing the
>>> list_splice() call by a list_splice_init() didn't work.
>> Okay, I think I figured it out: drm_bridge_chain_xx() helpers use
>> encoder->bridge_chain as their list head, and you'll never hit the 'elem
>> is list head' condition since we moved all elems from
>> encoder->bridge_chain to exynos_dsi->bridge_chain. The only way this
>> can work is if we stop using the helpers and implement our own list
>> iterators.
> Just to make it clear, calling INIT_LIST_HEAD(encoder->bridge_chain)
> doesn't really fix the bug, it just prevents the hang (infinite loop)
> and turn all drm_bridge_chain_xx() calls into NOPs.

Right, I've just checked it and indeed the display chain outputs nothing 
after such 'fix'. To get it finally working I've replaced 
drm_bridge_chain_*() operations for exynos_dsi 'out_bridge' by a direct 
calls. I will submit a patch in a few minutes. I hope that such 
workaround can be used now to fix the regression until a better solution 
is agreed.

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-27 10:25                       ` Marek Szyprowski
  0 siblings, 0 replies; 84+ messages in thread
From: Marek Szyprowski @ 2019-12-27 10:25 UTC (permalink / raw)
  To: Boris Brezillon, Andrzej Hajda
  Cc: Mark Rutland, Nikita Yushchenko, Jernej Skrabec, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, dri-devel,
	Kyungmin Park, Rob Herring, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Chris Healy, devicetree

Hi Boris,

On 24.12.2019 11:03, Boris Brezillon wrote:
> On Tue, 24 Dec 2019 10:49:36 +0100
> Boris Brezillon <boris.brezillon@collabora.com> wrote:
>> On Tue, 24 Dec 2019 10:44:22 +0100
>> Boris Brezillon <boris.brezillon@collabora.com> wrote:
>>> On Tue, 24 Dec 2019 10:16:49 +0100
>>> Andrzej Hajda <a.hajda@samsung.com> wrote:
>>>> On 23.12.2019 10:55, Marek Szyprowski wrote:
>>>>> On 16.12.2019 16:25, Boris Brezillon wrote:
>>>>>> On Mon, 16 Dec 2019 16:02:36 +0100
>>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>>>>> On 16.12.2019 15:55, Boris Brezillon wrote:
>>>>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
>>>>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:
>>>>>>>>>> So that each element in the chain can easily access its predecessor.
>>>>>>>>>> This will be needed to support bus format negotiation between elements
>>>>>>>>>> of the bridge chain.
>>>>>>>>>>
>>>>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>>>>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>>>>>>>> I've noticed that this patch got merged to linux-next as commit
>>>>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
>>>>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
>>>>>>>>> messages:
>>>>>>>>>
>>>>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
>>>>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
>>>>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
>>>>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
>>>>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
>>>>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>>>>>>>>> [drm] No driver support for vblank timestamp query.
>>>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
>>>>>>>>>
>>>>>>>>> I will try to debug this and provide more information soon.
>>>>>>>>>          
>>>>>>>> Can you try with this diff applied?
>>>>>>> This patch doesn't change anything.
>>>>>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
>>>>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
>>>>>> after the list_splice_init() call?
>>>>> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
>>>>>
>>>>> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain)
>>>>> fixed the boot issue.
>>> If INIT_LIST_HEAD() worked, I don't understand why replacing the
>>> list_splice() call by a list_splice_init() (which doing a list_splice()
>>> + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
>>> list_splice_init() version doesn't work?
>>>    
>>>>> It looks that this is related with the way the
>>>>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej
>>>>> will give a bit more detailed comment and spread some light on this.
>>>>
>>>> Hi Marek, Boris,
>>>>
>>>>
>>>> I have not followed latest patches due to high work load, my bad. Marek
>>>> thanks from pointing
>>>>
>>>> About ExynosDSI bridge handling:
>>>>
>>>> The order of calling encoder, bridge (and consequently panel) ops
>>>> enforced by DRM core (bridge->pre_enable, encoder->enable,
>>>> bridge->enable) does not fit to ExynosDSI hardware initialization
>>>> sequence, if I remember correctly it does not fit to whole MIPI DSI
>>>> standard (I think similar situation is with eDP). As a result DSI
>>>> drivers must use some ugly workarounds, rely on HW properly coping with
>>>> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
>>>> using encoder->bridge chaining and call bridge ops by itself when suitable.
>>> Yes, that's definitely hack-ish, and I proposed 2 solutions to address
>>> that in previous versions of this patchset, unfortunately I didn't get
>>> any feedback so I went for the less invasive option (keep the hack but
>>> adapt it to the double-linked list changes), which still lead to
>>> regressions :-/.
>>>
>>> Just a reminder of my 2 proposals:
>>>
>>> 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
>>>     split your enable/disable logic in 2 parts and make sure things are
>>>     ready when the panel/next bridge tries to send DSI commands
>>> 2/ move everything that's needed to send DSI commands out of the
>>>     ->enable() path (maybe in runtime PM resume/suspend hooks) so you
>>>     can call that in the DSI transfer path too
>>>
>>> As pointed out by Laurent, #1 doesn't work because some panel drivers
>>> send DSI commands in their ->prepare() hook, and ->pre_enable() methods
>>> are called in reverse order, meaning that the DRM panel bridge driver
>>> would try to issue DSI commands before the DSI host controllers is ready
>>> to send them. I still thing #2 is a good option.
>>>    
>>>> So proper patch converting to double-linked list should not try to
>>>> splice ExynosDSI private bridge list with with encoder's, encoder's list
>>>> should be always empty, as Marek suggested.
>>> That's exactly what I wanted to do: make the encoder's list empty after
>>> attach() and restore it to its initial state before unregistering
>>> the bridge, except I forgot that list_splice() doesn't call
>>> INIT_LIST_HEAD(). It's still not clear to me why replacing the
>>> list_splice() call by a list_splice_init() didn't work.
>> Okay, I think I figured it out: drm_bridge_chain_xx() helpers use
>> encoder->bridge_chain as their list head, and you'll never hit the 'elem
>> is list head' condition since we moved all elems from
>> encoder->bridge_chain to exynos_dsi->bridge_chain. The only way this
>> can work is if we stop using the helpers and implement our own list
>> iterators.
> Just to make it clear, calling INIT_LIST_HEAD(encoder->bridge_chain)
> doesn't really fix the bug, it just prevents the hang (infinite loop)
> and turn all drm_bridge_chain_xx() calls into NOPs.

Right, I've just checked it and indeed the display chain outputs nothing 
after such 'fix'. To get it finally working I've replaced 
drm_bridge_chain_*() operations for exynos_dsi 'out_bridge' by a direct 
calls. I will submit a patch in a few minutes. I hope that such 
workaround can be used now to fix the regression until a better solution 
is agreed.

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-27  9:42                   ` Andrzej Hajda
@ 2019-12-27 10:51                     ` Laurent Pinchart
  -1 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-27 10:51 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Boris Brezillon, Marek Szyprowski, dri-devel, Mark Rutland,
	Neil Armstrong, Thierry Reding, kernel, Sam Ravnborg,
	Nikita Yushchenko, Andrey Smirnov, Kyungmin Park, Chris Healy,
	devicetree, Jonas Karlman, Rob Herring, Jernej Skrabec,
	Seung-Woo Kim

Hi Andrzej,

On Fri, Dec 27, 2019 at 10:42:25AM +0100, Andrzej Hajda wrote:
> On 24.12.2019 10:44, Boris Brezillon wrote:
> > On Tue, 24 Dec 2019 10:16:49 +0100 Andrzej Hajda wrote:
> >> On 23.12.2019 10:55, Marek Szyprowski wrote:
> >>> On 16.12.2019 16:25, Boris Brezillon wrote:  
> >>>> On Mon, 16 Dec 2019 16:02:36 +0100 Marek Szyprowski wrote:  
> >>>>> On 16.12.2019 15:55, Boris Brezillon wrote:  
> >>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
> >>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
> >>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:  
> >>>>>>>> So that each element in the chain can easily access its predecessor.
> >>>>>>>> This will be needed to support bus format negotiation between elements
> >>>>>>>> of the bridge chain.
> >>>>>>>>
> >>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> >>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> >>>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>  
> >>>>>>>
> >>>>>>> I've noticed that this patch got merged to linux-next as commit
> >>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> >>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> >>>>>>> messages:
> >>>>>>>
> >>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> >>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> >>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> >>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> >>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> >>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> >>>>>>> [drm] No driver support for vblank timestamp query.
> >>>>>>> [drm] Cannot find any crtc or sizes
> >>>>>>> [drm] Cannot find any crtc or sizes
> >>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> >>>>>>>
> >>>>>>> I will try to debug this and provide more information soon.
> >>>>>>
> >>>>>> Can you try with this diff applied?  
> >>>>>
> >>>>> This patch doesn't change anything.  
> >>>>
> >>>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> >>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> >>>> after the list_splice_init() call?  
> >>>
> >>> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> >>>
> >>> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> >>> fixed the boot issue.
> >
> > If INIT_LIST_HEAD() worked, I don't understand why replacing the
> > list_splice() call by a list_splice_init() (which doing a list_splice()
> > + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> > list_splice_init() version doesn't work?
> >
> >>> It looks that this is related with the way the 
> >>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> >>> will give a bit more detailed comment and spread some light on this.  
> >>
> >> Hi Marek, Boris,
> >>
> >> I have not followed latest patches due to high work load, my bad. Marek
> >> thanks from pointing
> >>
> >> About ExynosDSI bridge handling:
> >>
> >> The order of calling encoder, bridge (and consequently panel) ops
> >> enforced by DRM core (bridge->pre_enable, encoder->enable,
> >> bridge->enable) does not fit to ExynosDSI hardware initialization
> >> sequence, if I remember correctly it does not fit to whole MIPI DSI
> >> standard (I think similar situation is with eDP). As a result DSI
> >> drivers must use some ugly workarounds, rely on HW properly coping with
> >> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> >> using encoder->bridge chaining and call bridge ops by itself when suitable.
> >
> > Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> > that in previous versions of this patchset, unfortunately I didn't get
> > any feedback so I went for the less invasive option (keep the hack but
> > adapt it to the double-linked list changes), which still lead to
> > regressions :-/.
> >
> > Just a reminder of my 2 proposals:
> >
> > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> >    split your enable/disable logic in 2 parts and make sure things are
> >    ready when the panel/next bridge tries to send DSI commands
> 
> If it means 'convert exynos_dsi to bridge' I do not think it will help -
> 
> - pre_enable op will be still called after pre_enable op of downstream
> bridge - and this is the main reason why exynos_dsi do not use encoder
> bridge chain - it needs to perform some operations BEFORE (pre)enabling
> downstream devices.
> 
> > 2/ move everything that's needed to send DSI commands out of the
> >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you
> >    can call that in the DSI transfer path too
> 
> It looks like a solution for DSI protocol, where control bus is shared
> with data bus, but the problem is more general - we have source and sink
> connected with some local bus, which has some negotiation/enable/disable
> protocol/requirements. And drm_core/bridge framework enforces us to fit
> every such protocol to 'drm_bridge protocol' with few opses called in
> fixed order, without clearly defined purpose of each ops. That does not
> sound generic and results in multiple issues:
> 
> - different drivers uses different opses to perform the same thing,
> 
> - different drivers assumes different things about their sinks/sources
> in their opses,
> 
> - more complicated sequences does not fit at all to this model.
> 
> All this results in incompatibilities between drivers which become
> visible with devices used in different configurations/platforms.

I fully agree with you, not defining the semantics of the bridge
operations precisely was I believe a mistake, and we're paying the price
now. That's OK, we "just" need to fix it :-)

> > As pointed out by Laurent, #1 doesn't work because some panel drivers
> > send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> > are called in reverse order, meaning that the DRM panel bridge driver
> > would try to issue DSI commands before the DSI host controllers is ready
> > to send them. I still thing #2 is a good option.
> >
> >> So proper patch converting to double-linked list should not try to
> >> splice ExynosDSI private bridge list with with encoder's, encoder's list
> >> should be always empty, as Marek suggested.
> >
> > That's exactly what I wanted to do: make the encoder's list empty after
> > attach() and restore it to its initial state before unregistering
> > the bridge, except I forgot that list_splice() doesn't call
> > INIT_LIST_HEAD(). It's still not clear to me why replacing the
> > list_splice() call by a list_splice_init() didn't work.
> > Also note that calling INIT_LIST_HEAD() only works if you have one
> > bridge in the chain, so if we go for that option we need a comment
> > explaining the limitations of this approach.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-27 10:51                     ` Laurent Pinchart
  0 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2019-12-27 10:51 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, Jernej Skrabec,
	dri-devel, Rob Herring, Kyungmin Park, Boris Brezillon,
	Thierry Reding, kernel, Sam Ravnborg, Chris Healy,
	Marek Szyprowski

Hi Andrzej,

On Fri, Dec 27, 2019 at 10:42:25AM +0100, Andrzej Hajda wrote:
> On 24.12.2019 10:44, Boris Brezillon wrote:
> > On Tue, 24 Dec 2019 10:16:49 +0100 Andrzej Hajda wrote:
> >> On 23.12.2019 10:55, Marek Szyprowski wrote:
> >>> On 16.12.2019 16:25, Boris Brezillon wrote:  
> >>>> On Mon, 16 Dec 2019 16:02:36 +0100 Marek Szyprowski wrote:  
> >>>>> On 16.12.2019 15:55, Boris Brezillon wrote:  
> >>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
> >>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:  
> >>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:  
> >>>>>>>> So that each element in the chain can easily access its predecessor.
> >>>>>>>> This will be needed to support bus format negotiation between elements
> >>>>>>>> of the bridge chain.
> >>>>>>>>
> >>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> >>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> >>>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>  
> >>>>>>>
> >>>>>>> I've noticed that this patch got merged to linux-next as commit
> >>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> >>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> >>>>>>> messages:
> >>>>>>>
> >>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> >>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> >>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> >>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> >>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> >>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> >>>>>>> [drm] No driver support for vblank timestamp query.
> >>>>>>> [drm] Cannot find any crtc or sizes
> >>>>>>> [drm] Cannot find any crtc or sizes
> >>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> >>>>>>>
> >>>>>>> I will try to debug this and provide more information soon.
> >>>>>>
> >>>>>> Can you try with this diff applied?  
> >>>>>
> >>>>> This patch doesn't change anything.  
> >>>>
> >>>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> >>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> >>>> after the list_splice_init() call?  
> >>>
> >>> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> >>>
> >>> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> >>> fixed the boot issue.
> >
> > If INIT_LIST_HEAD() worked, I don't understand why replacing the
> > list_splice() call by a list_splice_init() (which doing a list_splice()
> > + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> > list_splice_init() version doesn't work?
> >
> >>> It looks that this is related with the way the 
> >>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> >>> will give a bit more detailed comment and spread some light on this.  
> >>
> >> Hi Marek, Boris,
> >>
> >> I have not followed latest patches due to high work load, my bad. Marek
> >> thanks from pointing
> >>
> >> About ExynosDSI bridge handling:
> >>
> >> The order of calling encoder, bridge (and consequently panel) ops
> >> enforced by DRM core (bridge->pre_enable, encoder->enable,
> >> bridge->enable) does not fit to ExynosDSI hardware initialization
> >> sequence, if I remember correctly it does not fit to whole MIPI DSI
> >> standard (I think similar situation is with eDP). As a result DSI
> >> drivers must use some ugly workarounds, rely on HW properly coping with
> >> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> >> using encoder->bridge chaining and call bridge ops by itself when suitable.
> >
> > Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> > that in previous versions of this patchset, unfortunately I didn't get
> > any feedback so I went for the less invasive option (keep the hack but
> > adapt it to the double-linked list changes), which still lead to
> > regressions :-/.
> >
> > Just a reminder of my 2 proposals:
> >
> > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> >    split your enable/disable logic in 2 parts and make sure things are
> >    ready when the panel/next bridge tries to send DSI commands
> 
> If it means 'convert exynos_dsi to bridge' I do not think it will help -
> 
> - pre_enable op will be still called after pre_enable op of downstream
> bridge - and this is the main reason why exynos_dsi do not use encoder
> bridge chain - it needs to perform some operations BEFORE (pre)enabling
> downstream devices.
> 
> > 2/ move everything that's needed to send DSI commands out of the
> >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you
> >    can call that in the DSI transfer path too
> 
> It looks like a solution for DSI protocol, where control bus is shared
> with data bus, but the problem is more general - we have source and sink
> connected with some local bus, which has some negotiation/enable/disable
> protocol/requirements. And drm_core/bridge framework enforces us to fit
> every such protocol to 'drm_bridge protocol' with few opses called in
> fixed order, without clearly defined purpose of each ops. That does not
> sound generic and results in multiple issues:
> 
> - different drivers uses different opses to perform the same thing,
> 
> - different drivers assumes different things about their sinks/sources
> in their opses,
> 
> - more complicated sequences does not fit at all to this model.
> 
> All this results in incompatibilities between drivers which become
> visible with devices used in different configurations/platforms.

I fully agree with you, not defining the semantics of the bridge
operations precisely was I believe a mistake, and we're paying the price
now. That's OK, we "just" need to fix it :-)

> > As pointed out by Laurent, #1 doesn't work because some panel drivers
> > send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> > are called in reverse order, meaning that the DRM panel bridge driver
> > would try to issue DSI commands before the DSI host controllers is ready
> > to send them. I still thing #2 is a good option.
> >
> >> So proper patch converting to double-linked list should not try to
> >> splice ExynosDSI private bridge list with with encoder's, encoder's list
> >> should be always empty, as Marek suggested.
> >
> > That's exactly what I wanted to do: make the encoder's list empty after
> > attach() and restore it to its initial state before unregistering
> > the bridge, except I forgot that list_splice() doesn't call
> > INIT_LIST_HEAD(). It's still not clear to me why replacing the
> > list_splice() call by a list_splice_init() didn't work.
> > Also note that calling INIT_LIST_HEAD() only works if you have one
> > bridge in the chain, so if we go for that option we need a comment
> > explaining the limitations of this approach.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-27 10:25                       ` Marek Szyprowski
@ 2019-12-27 11:03                         ` Marek Szyprowski
  -1 siblings, 0 replies; 84+ messages in thread
From: Marek Szyprowski @ 2019-12-27 11:03 UTC (permalink / raw)
  To: Boris Brezillon, Andrzej Hajda
  Cc: Mark Rutland, Nikita Yushchenko, Jernej Skrabec, Jonas Karlman,
	Andrey Smirnov, Neil Armstrong, Seung-Woo Kim, Kyungmin Park,
	dri-devel, devicetree, Rob Herring, Thierry Reding,
	Laurent Pinchart, kernel, Sam Ravnborg, Chris Healy

Hi All,

On 27.12.2019 11:25, Marek Szyprowski wrote:
> On 24.12.2019 11:03, Boris Brezillon wrote:
>> On Tue, 24 Dec 2019 10:49:36 +0100
>> Boris Brezillon <boris.brezillon@collabora.com> wrote:
>>> On Tue, 24 Dec 2019 10:44:22 +0100
>>> Boris Brezillon <boris.brezillon@collabora.com> wrote:
>>>> On Tue, 24 Dec 2019 10:16:49 +0100
>>>> Andrzej Hajda <a.hajda@samsung.com> wrote:
>>>>> On 23.12.2019 10:55, Marek Szyprowski wrote:
>>>>>> On 16.12.2019 16:25, Boris Brezillon wrote:
>>>>>>> On Mon, 16 Dec 2019 16:02:36 +0100
>>>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>>>>>> On 16.12.2019 15:55, Boris Brezillon wrote:
>>>>>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
>>>>>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>>>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:
>>>>>>>>>>> So that each element in the chain can easily access its 
>>>>>>>>>>> predecessor.
>>>>>>>>>>> This will be needed to support bus format negotiation 
>>>>>>>>>>> between elements
>>>>>>>>>>> of the bridge chain.
>>>>>>>>>>>
>>>>>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>>>>>>>>>> Reviewed-by: Laurent Pinchart 
>>>>>>>>>>> <laurent.pinchart@ideasonboard.com>
>>>>>>>>>> I've noticed that this patch got merged to linux-next as commit
>>>>>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks 
>>>>>>>>>> booting of
>>>>>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after 
>>>>>>>>>> following
>>>>>>>>>> messages:
>>>>>>>>>>
>>>>>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping 
>>>>>>>>>> operations
>>>>>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops 
>>>>>>>>>> fimd_component_ops)
>>>>>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops 
>>>>>>>>>> mixer_component_ops)
>>>>>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops 
>>>>>>>>>> exynos_dsi_component_ops)
>>>>>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops 
>>>>>>>>>> hdmi_component_ops)
>>>>>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>>>>>>>>>> [drm] No driver support for vblank timestamp query.
>>>>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on 
>>>>>>>>>> minor 0
>>>>>>>>>>
>>>>>>>>>> I will try to debug this and provide more information soon.
>>>>>>>>> Can you try with this diff applied?
>>>>>>>> This patch doesn't change anything.
>>>>>>> Okay. Can you do a list_for_each_entry() on both 
>>>>>>> encoder->bridge_chain
>>>>>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) 
>>>>>>> before and
>>>>>>> after the list_splice_init() call?
>>>>>> encoder->bridge_chain contains only one element. dsi->drive_chain 
>>>>>> is empty.
>>>>>>
>>>>>> Replacing that list_splice() with 
>>>>>> INIT_LIST_HEAD(&encoder->bridge_chain)
>>>>>> fixed the boot issue.
>>>> If INIT_LIST_HEAD() worked, I don't understand why replacing the
>>>> list_splice() call by a list_splice_init() (which doing a 
>>>> list_splice()
>>>> + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
>>>> list_splice_init() version doesn't work?
>>>>>> It looks that this is related with the way the
>>>>>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej
>>>>>> will give a bit more detailed comment and spread some light on this.
>>>>>
>>>>> Hi Marek, Boris,
>>>>>
>>>>>
>>>>> I have not followed latest patches due to high work load, my bad. 
>>>>> Marek
>>>>> thanks from pointing
>>>>>
>>>>> About ExynosDSI bridge handling:
>>>>>
>>>>> The order of calling encoder, bridge (and consequently panel) ops
>>>>> enforced by DRM core (bridge->pre_enable, encoder->enable,
>>>>> bridge->enable) does not fit to ExynosDSI hardware initialization
>>>>> sequence, if I remember correctly it does not fit to whole MIPI DSI
>>>>> standard (I think similar situation is with eDP). As a result DSI
>>>>> drivers must use some ugly workarounds, rely on HW properly coping 
>>>>> with
>>>>> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
>>>>> using encoder->bridge chaining and call bridge ops by itself when 
>>>>> suitable.
>>>> Yes, that's definitely hack-ish, and I proposed 2 solutions to address
>>>> that in previous versions of this patchset, unfortunately I didn't get
>>>> any feedback so I went for the less invasive option (keep the hack but
>>>> adapt it to the double-linked list changes), which still lead to
>>>> regressions :-/.
>>>>
>>>> Just a reminder of my 2 proposals:
>>>>
>>>> 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you 
>>>> can
>>>>     split your enable/disable logic in 2 parts and make sure things 
>>>> are
>>>>     ready when the panel/next bridge tries to send DSI commands
>>>> 2/ move everything that's needed to send DSI commands out of the
>>>>     ->enable() path (maybe in runtime PM resume/suspend hooks) so you
>>>>     can call that in the DSI transfer path too
>>>>
>>>> As pointed out by Laurent, #1 doesn't work because some panel drivers
>>>> send DSI commands in their ->prepare() hook, and ->pre_enable() 
>>>> methods
>>>> are called in reverse order, meaning that the DRM panel bridge driver
>>>> would try to issue DSI commands before the DSI host controllers is 
>>>> ready
>>>> to send them. I still thing #2 is a good option.
>>>>> So proper patch converting to double-linked list should not try to
>>>>> splice ExynosDSI private bridge list with with encoder's, 
>>>>> encoder's list
>>>>> should be always empty, as Marek suggested.
>>>> That's exactly what I wanted to do: make the encoder's list empty 
>>>> after
>>>> attach() and restore it to its initial state before unregistering
>>>> the bridge, except I forgot that list_splice() doesn't call
>>>> INIT_LIST_HEAD(). It's still not clear to me why replacing the
>>>> list_splice() call by a list_splice_init() didn't work.
>>> Okay, I think I figured it out: drm_bridge_chain_xx() helpers use
>>> encoder->bridge_chain as their list head, and you'll never hit the 
>>> 'elem
>>> is list head' condition since we moved all elems from
>>> encoder->bridge_chain to exynos_dsi->bridge_chain. The only way this
>>> can work is if we stop using the helpers and implement our own list
>>> iterators.
>> Just to make it clear, calling INIT_LIST_HEAD(encoder->bridge_chain)
>> doesn't really fix the bug, it just prevents the hang (infinite loop)
>> and turn all drm_bridge_chain_xx() calls into NOPs.
>
> Right, I've just checked it and indeed the display chain outputs 
> nothing after such 'fix'. To get it finally working I've replaced 
> drm_bridge_chain_*() operations for exynos_dsi 'out_bridge' by a 
> direct calls. I will submit a patch in a few minutes. I hope that such 
> workaround can be used now to fix the regression until a better 
> solution is agreed.

I've posted a 'quick & working' fix for Exynos DRM DSI driver:

https://lkml.org/lkml/2019/12/27/121

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland


^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-27 11:03                         ` Marek Szyprowski
  0 siblings, 0 replies; 84+ messages in thread
From: Marek Szyprowski @ 2019-12-27 11:03 UTC (permalink / raw)
  To: Boris Brezillon, Andrzej Hajda
  Cc: Mark Rutland, Nikita Yushchenko, Jernej Skrabec, Neil Armstrong,
	Andrey Smirnov, Jonas Karlman, Seung-Woo Kim, dri-devel,
	Kyungmin Park, Rob Herring, Thierry Reding, Laurent Pinchart,
	kernel, Sam Ravnborg, Chris Healy, devicetree

Hi All,

On 27.12.2019 11:25, Marek Szyprowski wrote:
> On 24.12.2019 11:03, Boris Brezillon wrote:
>> On Tue, 24 Dec 2019 10:49:36 +0100
>> Boris Brezillon <boris.brezillon@collabora.com> wrote:
>>> On Tue, 24 Dec 2019 10:44:22 +0100
>>> Boris Brezillon <boris.brezillon@collabora.com> wrote:
>>>> On Tue, 24 Dec 2019 10:16:49 +0100
>>>> Andrzej Hajda <a.hajda@samsung.com> wrote:
>>>>> On 23.12.2019 10:55, Marek Szyprowski wrote:
>>>>>> On 16.12.2019 16:25, Boris Brezillon wrote:
>>>>>>> On Mon, 16 Dec 2019 16:02:36 +0100
>>>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>>>>>> On 16.12.2019 15:55, Boris Brezillon wrote:
>>>>>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
>>>>>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>>>>>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:
>>>>>>>>>>> So that each element in the chain can easily access its 
>>>>>>>>>>> predecessor.
>>>>>>>>>>> This will be needed to support bus format negotiation 
>>>>>>>>>>> between elements
>>>>>>>>>>> of the bridge chain.
>>>>>>>>>>>
>>>>>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
>>>>>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
>>>>>>>>>>> Reviewed-by: Laurent Pinchart 
>>>>>>>>>>> <laurent.pinchart@ideasonboard.com>
>>>>>>>>>> I've noticed that this patch got merged to linux-next as commit
>>>>>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks 
>>>>>>>>>> booting of
>>>>>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after 
>>>>>>>>>> following
>>>>>>>>>> messages:
>>>>>>>>>>
>>>>>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping 
>>>>>>>>>> operations
>>>>>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops 
>>>>>>>>>> fimd_component_ops)
>>>>>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops 
>>>>>>>>>> mixer_component_ops)
>>>>>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops 
>>>>>>>>>> exynos_dsi_component_ops)
>>>>>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops 
>>>>>>>>>> hdmi_component_ops)
>>>>>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
>>>>>>>>>> [drm] No driver support for vblank timestamp query.
>>>>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>>>>> [drm] Cannot find any crtc or sizes
>>>>>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on 
>>>>>>>>>> minor 0
>>>>>>>>>>
>>>>>>>>>> I will try to debug this and provide more information soon.
>>>>>>>>> Can you try with this diff applied?
>>>>>>>> This patch doesn't change anything.
>>>>>>> Okay. Can you do a list_for_each_entry() on both 
>>>>>>> encoder->bridge_chain
>>>>>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) 
>>>>>>> before and
>>>>>>> after the list_splice_init() call?
>>>>>> encoder->bridge_chain contains only one element. dsi->drive_chain 
>>>>>> is empty.
>>>>>>
>>>>>> Replacing that list_splice() with 
>>>>>> INIT_LIST_HEAD(&encoder->bridge_chain)
>>>>>> fixed the boot issue.
>>>> If INIT_LIST_HEAD() worked, I don't understand why replacing the
>>>> list_splice() call by a list_splice_init() (which doing a 
>>>> list_splice()
>>>> + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
>>>> list_splice_init() version doesn't work?
>>>>>> It looks that this is related with the way the
>>>>>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej
>>>>>> will give a bit more detailed comment and spread some light on this.
>>>>>
>>>>> Hi Marek, Boris,
>>>>>
>>>>>
>>>>> I have not followed latest patches due to high work load, my bad. 
>>>>> Marek
>>>>> thanks from pointing
>>>>>
>>>>> About ExynosDSI bridge handling:
>>>>>
>>>>> The order of calling encoder, bridge (and consequently panel) ops
>>>>> enforced by DRM core (bridge->pre_enable, encoder->enable,
>>>>> bridge->enable) does not fit to ExynosDSI hardware initialization
>>>>> sequence, if I remember correctly it does not fit to whole MIPI DSI
>>>>> standard (I think similar situation is with eDP). As a result DSI
>>>>> drivers must use some ugly workarounds, rely on HW properly coping 
>>>>> with
>>>>> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
>>>>> using encoder->bridge chaining and call bridge ops by itself when 
>>>>> suitable.
>>>> Yes, that's definitely hack-ish, and I proposed 2 solutions to address
>>>> that in previous versions of this patchset, unfortunately I didn't get
>>>> any feedback so I went for the less invasive option (keep the hack but
>>>> adapt it to the double-linked list changes), which still lead to
>>>> regressions :-/.
>>>>
>>>> Just a reminder of my 2 proposals:
>>>>
>>>> 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you 
>>>> can
>>>>     split your enable/disable logic in 2 parts and make sure things 
>>>> are
>>>>     ready when the panel/next bridge tries to send DSI commands
>>>> 2/ move everything that's needed to send DSI commands out of the
>>>>     ->enable() path (maybe in runtime PM resume/suspend hooks) so you
>>>>     can call that in the DSI transfer path too
>>>>
>>>> As pointed out by Laurent, #1 doesn't work because some panel drivers
>>>> send DSI commands in their ->prepare() hook, and ->pre_enable() 
>>>> methods
>>>> are called in reverse order, meaning that the DRM panel bridge driver
>>>> would try to issue DSI commands before the DSI host controllers is 
>>>> ready
>>>> to send them. I still thing #2 is a good option.
>>>>> So proper patch converting to double-linked list should not try to
>>>>> splice ExynosDSI private bridge list with with encoder's, 
>>>>> encoder's list
>>>>> should be always empty, as Marek suggested.
>>>> That's exactly what I wanted to do: make the encoder's list empty 
>>>> after
>>>> attach() and restore it to its initial state before unregistering
>>>> the bridge, except I forgot that list_splice() doesn't call
>>>> INIT_LIST_HEAD(). It's still not clear to me why replacing the
>>>> list_splice() call by a list_splice_init() didn't work.
>>> Okay, I think I figured it out: drm_bridge_chain_xx() helpers use
>>> encoder->bridge_chain as their list head, and you'll never hit the 
>>> 'elem
>>> is list head' condition since we moved all elems from
>>> encoder->bridge_chain to exynos_dsi->bridge_chain. The only way this
>>> can work is if we stop using the helpers and implement our own list
>>> iterators.
>> Just to make it clear, calling INIT_LIST_HEAD(encoder->bridge_chain)
>> doesn't really fix the bug, it just prevents the hang (infinite loop)
>> and turn all drm_bridge_chain_xx() calls into NOPs.
>
> Right, I've just checked it and indeed the display chain outputs 
> nothing after such 'fix'. To get it finally working I've replaced 
> drm_bridge_chain_*() operations for exynos_dsi 'out_bridge' by a 
> direct calls. I will submit a patch in a few minutes. I hope that such 
> workaround can be used now to fix the regression until a better 
> solution is agreed.

I've posted a 'quick & working' fix for Exynos DRM DSI driver:

https://lkml.org/lkml/2019/12/27/121

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-27 10:51                     ` Laurent Pinchart
@ 2019-12-27 12:21                       ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-27 12:21 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Andrzej Hajda, Marek Szyprowski, dri-devel, Mark Rutland,
	Neil Armstrong, Thierry Reding, kernel, Sam Ravnborg,
	Nikita Yushchenko, Andrey Smirnov, Kyungmin Park, Chris Healy,
	devicetree, Jonas Karlman, Rob Herring, Jernej Skrabec,
	Seung-Woo Kim

On Fri, 27 Dec 2019 12:51:54 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Andrzej,
> 
> On Fri, Dec 27, 2019 at 10:42:25AM +0100, Andrzej Hajda wrote:
> > On 24.12.2019 10:44, Boris Brezillon wrote:  
> > > On Tue, 24 Dec 2019 10:16:49 +0100 Andrzej Hajda wrote:  
> > >> On 23.12.2019 10:55, Marek Szyprowski wrote:  
> > >>> On 16.12.2019 16:25, Boris Brezillon wrote:    
> > >>>> On Mon, 16 Dec 2019 16:02:36 +0100 Marek Szyprowski wrote:    
> > >>>>> On 16.12.2019 15:55, Boris Brezillon wrote:    
> > >>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
> > >>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:    
> > >>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:    
> > >>>>>>>> So that each element in the chain can easily access its predecessor.
> > >>>>>>>> This will be needed to support bus format negotiation between elements
> > >>>>>>>> of the bridge chain.
> > >>>>>>>>
> > >>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > >>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > >>>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>    
> > >>>>>>>
> > >>>>>>> I've noticed that this patch got merged to linux-next as commit
> > >>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> > >>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> > >>>>>>> messages:
> > >>>>>>>
> > >>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> > >>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> > >>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> > >>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> > >>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> > >>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> > >>>>>>> [drm] No driver support for vblank timestamp query.
> > >>>>>>> [drm] Cannot find any crtc or sizes
> > >>>>>>> [drm] Cannot find any crtc or sizes
> > >>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> > >>>>>>>
> > >>>>>>> I will try to debug this and provide more information soon.  
> > >>>>>>
> > >>>>>> Can you try with this diff applied?    
> > >>>>>
> > >>>>> This patch doesn't change anything.    
> > >>>>
> > >>>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> > >>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> > >>>> after the list_splice_init() call?    
> > >>>
> > >>> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> > >>>
> > >>> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> > >>> fixed the boot issue.  
> > >
> > > If INIT_LIST_HEAD() worked, I don't understand why replacing the
> > > list_splice() call by a list_splice_init() (which doing a list_splice()
> > > + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> > > list_splice_init() version doesn't work?
> > >  
> > >>> It looks that this is related with the way the 
> > >>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> > >>> will give a bit more detailed comment and spread some light on this.    
> > >>
> > >> Hi Marek, Boris,
> > >>
> > >> I have not followed latest patches due to high work load, my bad. Marek
> > >> thanks from pointing
> > >>
> > >> About ExynosDSI bridge handling:
> > >>
> > >> The order of calling encoder, bridge (and consequently panel) ops
> > >> enforced by DRM core (bridge->pre_enable, encoder->enable,
> > >> bridge->enable) does not fit to ExynosDSI hardware initialization
> > >> sequence, if I remember correctly it does not fit to whole MIPI DSI
> > >> standard (I think similar situation is with eDP). As a result DSI
> > >> drivers must use some ugly workarounds, rely on HW properly coping with
> > >> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> > >> using encoder->bridge chaining and call bridge ops by itself when suitable.  
> > >
> > > Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> > > that in previous versions of this patchset, unfortunately I didn't get
> > > any feedback so I went for the less invasive option (keep the hack but
> > > adapt it to the double-linked list changes), which still lead to
> > > regressions :-/.
> > >
> > > Just a reminder of my 2 proposals:
> > >
> > > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> > >    split your enable/disable logic in 2 parts and make sure things are
> > >    ready when the panel/next bridge tries to send DSI commands  
> > 
> > If it means 'convert exynos_dsi to bridge' I do not think it will help -
> > 
> > - pre_enable op will be still called after pre_enable op of downstream
> > bridge - and this is the main reason why exynos_dsi do not use encoder
> > bridge chain - it needs to perform some operations BEFORE (pre)enabling
> > downstream devices.

Yep, I figured that after Laurent's review.

> >   
> > > 2/ move everything that's needed to send DSI commands out of the  
> > >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you  
> > >    can call that in the DSI transfer path too  
> > 
> > It looks like a solution for DSI protocol, where control bus is shared
> > with data bus, but the problem is more general - we have source and sink
> > connected with some local bus, which has some negotiation/enable/disable
> > protocol/requirements. And drm_core/bridge framework enforces us to fit
> > every such protocol to 'drm_bridge protocol' with few opses called in
> > fixed order, without clearly defined purpose of each ops. That does not
> > sound generic and results in multiple issues:
> > 
> > - different drivers uses different opses to perform the same thing,
> > 
> > - different drivers assumes different things about their sinks/sources
> > in their opses,
> > 
> > - more complicated sequences does not fit at all to this model.
> > 
> > All this results in incompatibilities between drivers which become
> > visible with devices used in different configurations/platforms.

That's true, drm_bridge_funcs semantics is rather vague and probably
doesn't fit all needs, but that's not the only problem we have when it
comes to DSI IMHO. I mean, I couldn't find any doc in drm_mipi_dsi.h
explaining when panel/bridge drivers are allowed to send DCS commands.
I was personally assuming that we were allowed to send such commands as
soon as mipi_dsi_attach() was called, but that's not clearly not
possible with VC4 and Exynos. This part should be clarified too.

> 
> I fully agree with you, not defining the semantics of the bridge
> operations precisely was I believe a mistake, and we're paying the price
> now. That's OK, we "just" need to fix it :-)

Okay, so how do we fix that? :-)

I'm not a big fan of specializing the drm_bridge_funcs interface to fit
protocol X or protocol Y needs. Sounds like a never ending story,
protocol Z might require something slightly different, and we're likely
to end up with an interface that's not generic at all.

Maybe I'm wrong, but it sounds like all DSI ordering issues could be
addressed at the mipi_dsi_host_ops level if we define a new
->enter_power_state() hook and have the DSI framework keep track of the
host power state (LP,HS,PD,...). The framework would then make sure we
are in a valid state (LP or HS) before calling dsi_host->transfer(). If
we have that in place, I don't think we need new hooks at the bridge
level, at least not for the DSI case. Please let me know if I'm missing
something.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-27 12:21                       ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-27 12:21 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Rob Herring,
	Neil Armstrong, Andrey Smirnov, Jonas Karlman, Seung-Woo Kim,
	Jernej Skrabec, dri-devel, Kyungmin Park, Thierry Reding, kernel,
	Sam Ravnborg, Chris Healy, Marek Szyprowski

On Fri, 27 Dec 2019 12:51:54 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Andrzej,
> 
> On Fri, Dec 27, 2019 at 10:42:25AM +0100, Andrzej Hajda wrote:
> > On 24.12.2019 10:44, Boris Brezillon wrote:  
> > > On Tue, 24 Dec 2019 10:16:49 +0100 Andrzej Hajda wrote:  
> > >> On 23.12.2019 10:55, Marek Szyprowski wrote:  
> > >>> On 16.12.2019 16:25, Boris Brezillon wrote:    
> > >>>> On Mon, 16 Dec 2019 16:02:36 +0100 Marek Szyprowski wrote:    
> > >>>>> On 16.12.2019 15:55, Boris Brezillon wrote:    
> > >>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
> > >>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:    
> > >>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:    
> > >>>>>>>> So that each element in the chain can easily access its predecessor.
> > >>>>>>>> This will be needed to support bus format negotiation between elements
> > >>>>>>>> of the bridge chain.
> > >>>>>>>>
> > >>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > >>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > >>>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>    
> > >>>>>>>
> > >>>>>>> I've noticed that this patch got merged to linux-next as commit
> > >>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> > >>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> > >>>>>>> messages:
> > >>>>>>>
> > >>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> > >>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> > >>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> > >>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> > >>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> > >>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> > >>>>>>> [drm] No driver support for vblank timestamp query.
> > >>>>>>> [drm] Cannot find any crtc or sizes
> > >>>>>>> [drm] Cannot find any crtc or sizes
> > >>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> > >>>>>>>
> > >>>>>>> I will try to debug this and provide more information soon.  
> > >>>>>>
> > >>>>>> Can you try with this diff applied?    
> > >>>>>
> > >>>>> This patch doesn't change anything.    
> > >>>>
> > >>>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> > >>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> > >>>> after the list_splice_init() call?    
> > >>>
> > >>> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> > >>>
> > >>> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> > >>> fixed the boot issue.  
> > >
> > > If INIT_LIST_HEAD() worked, I don't understand why replacing the
> > > list_splice() call by a list_splice_init() (which doing a list_splice()
> > > + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> > > list_splice_init() version doesn't work?
> > >  
> > >>> It looks that this is related with the way the 
> > >>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> > >>> will give a bit more detailed comment and spread some light on this.    
> > >>
> > >> Hi Marek, Boris,
> > >>
> > >> I have not followed latest patches due to high work load, my bad. Marek
> > >> thanks from pointing
> > >>
> > >> About ExynosDSI bridge handling:
> > >>
> > >> The order of calling encoder, bridge (and consequently panel) ops
> > >> enforced by DRM core (bridge->pre_enable, encoder->enable,
> > >> bridge->enable) does not fit to ExynosDSI hardware initialization
> > >> sequence, if I remember correctly it does not fit to whole MIPI DSI
> > >> standard (I think similar situation is with eDP). As a result DSI
> > >> drivers must use some ugly workarounds, rely on HW properly coping with
> > >> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> > >> using encoder->bridge chaining and call bridge ops by itself when suitable.  
> > >
> > > Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> > > that in previous versions of this patchset, unfortunately I didn't get
> > > any feedback so I went for the less invasive option (keep the hack but
> > > adapt it to the double-linked list changes), which still lead to
> > > regressions :-/.
> > >
> > > Just a reminder of my 2 proposals:
> > >
> > > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> > >    split your enable/disable logic in 2 parts and make sure things are
> > >    ready when the panel/next bridge tries to send DSI commands  
> > 
> > If it means 'convert exynos_dsi to bridge' I do not think it will help -
> > 
> > - pre_enable op will be still called after pre_enable op of downstream
> > bridge - and this is the main reason why exynos_dsi do not use encoder
> > bridge chain - it needs to perform some operations BEFORE (pre)enabling
> > downstream devices.

Yep, I figured that after Laurent's review.

> >   
> > > 2/ move everything that's needed to send DSI commands out of the  
> > >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you  
> > >    can call that in the DSI transfer path too  
> > 
> > It looks like a solution for DSI protocol, where control bus is shared
> > with data bus, but the problem is more general - we have source and sink
> > connected with some local bus, which has some negotiation/enable/disable
> > protocol/requirements. And drm_core/bridge framework enforces us to fit
> > every such protocol to 'drm_bridge protocol' with few opses called in
> > fixed order, without clearly defined purpose of each ops. That does not
> > sound generic and results in multiple issues:
> > 
> > - different drivers uses different opses to perform the same thing,
> > 
> > - different drivers assumes different things about their sinks/sources
> > in their opses,
> > 
> > - more complicated sequences does not fit at all to this model.
> > 
> > All this results in incompatibilities between drivers which become
> > visible with devices used in different configurations/platforms.

That's true, drm_bridge_funcs semantics is rather vague and probably
doesn't fit all needs, but that's not the only problem we have when it
comes to DSI IMHO. I mean, I couldn't find any doc in drm_mipi_dsi.h
explaining when panel/bridge drivers are allowed to send DCS commands.
I was personally assuming that we were allowed to send such commands as
soon as mipi_dsi_attach() was called, but that's not clearly not
possible with VC4 and Exynos. This part should be clarified too.

> 
> I fully agree with you, not defining the semantics of the bridge
> operations precisely was I believe a mistake, and we're paying the price
> now. That's OK, we "just" need to fix it :-)

Okay, so how do we fix that? :-)

I'm not a big fan of specializing the drm_bridge_funcs interface to fit
protocol X or protocol Y needs. Sounds like a never ending story,
protocol Z might require something slightly different, and we're likely
to end up with an interface that's not generic at all.

Maybe I'm wrong, but it sounds like all DSI ordering issues could be
addressed at the mipi_dsi_host_ops level if we define a new
->enter_power_state() hook and have the DSI framework keep track of the
host power state (LP,HS,PD,...). The framework would then make sure we
are in a valid state (LP or HS) before calling dsi_host->transfer(). If
we have that in place, I don't think we need new hooks at the bridge
level, at least not for the DSI case. Please let me know if I'm missing
something.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-24 11:31                   ` Sam Ravnborg
@ 2019-12-27 12:39                     ` Boris Brezillon
  -1 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-27 12:39 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Jitao Shi, Andrzej Hajda, Marek Szyprowski, dri-devel,
	Mark Rutland, Neil Armstrong, Thierry Reding, Laurent Pinchart,
	kernel, Nikita Yushchenko, Andrey Smirnov, Kyungmin Park,
	Chris Healy, devicetree, Jonas Karlman, Rob Herring,
	Jernej Skrabec, Seung-Woo Kim

On Tue, 24 Dec 2019 12:31:11 +0100
Sam Ravnborg <sam@ravnborg.org> wrote:

> Hi Boris.
> 
> > Just a reminder of my 2 proposals:
> > 
> > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> >    split your enable/disable logic in 2 parts and make sure things are
> >    ready when the panel/next bridge tries to send DSI commands
> > 2/ move everything that's needed to send DSI commands out of the  
> >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you  
> >    can call that in the DSI transfer path too
> > 
> > As pointed out by Laurent, #1 doesn't work because some panel drivers
> > send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> > are called in reverse order, meaning that the DRM panel bridge driver
> > would try to issue DSI commands before the DSI host controllers is ready
> > to send them. I still thing #2 is a good option.  
> 
> Jitao Shi suggested to extend panels so we had a sequence of:
> 
>   prepare_power()  <= new callback,
>                    here one should NOT be allowed to send
>                    DSI commands
>   prepare()
>   enable()
> 
>    #
>    # panel is now ready to show your favourite christmas movie
>    #
> 
>   disable()
>   unprepare()
>   unprepare_power()  <= new callback
> 
> 
> Would this help implement what you suggest above?
> Relevant panels would then have to be updated - but this
> is doable.

I didn't look at Jitao's proposal but it looks like it's addressing a
similar issue on the DSI slave/device side: the device probably needs
to be powered before the host can interact with it through the DSI+DPHY
bus. I'm not entirely sure why we'd need another hook to do that since
we already have the ->prepare() one.

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2019-12-27 12:39                     ` Boris Brezillon
  0 siblings, 0 replies; 84+ messages in thread
From: Boris Brezillon @ 2019-12-27 12:39 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Jitao Shi,
	Rob Herring, Neil Armstrong, Andrey Smirnov, Jonas Karlman,
	Seung-Woo Kim, Jernej Skrabec, dri-devel, Kyungmin Park,
	Thierry Reding, Laurent Pinchart, kernel, Chris Healy,
	Marek Szyprowski

On Tue, 24 Dec 2019 12:31:11 +0100
Sam Ravnborg <sam@ravnborg.org> wrote:

> Hi Boris.
> 
> > Just a reminder of my 2 proposals:
> > 
> > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> >    split your enable/disable logic in 2 parts and make sure things are
> >    ready when the panel/next bridge tries to send DSI commands
> > 2/ move everything that's needed to send DSI commands out of the  
> >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you  
> >    can call that in the DSI transfer path too
> > 
> > As pointed out by Laurent, #1 doesn't work because some panel drivers
> > send DSI commands in their ->prepare() hook, and ->pre_enable() methods
> > are called in reverse order, meaning that the DRM panel bridge driver
> > would try to issue DSI commands before the DSI host controllers is ready
> > to send them. I still thing #2 is a good option.  
> 
> Jitao Shi suggested to extend panels so we had a sequence of:
> 
>   prepare_power()  <= new callback,
>                    here one should NOT be allowed to send
>                    DSI commands
>   prepare()
>   enable()
> 
>    #
>    # panel is now ready to show your favourite christmas movie
>    #
> 
>   disable()
>   unprepare()
>   unprepare_power()  <= new callback
> 
> 
> Would this help implement what you suggest above?
> Relevant panels would then have to be updated - but this
> is doable.

I didn't look at Jitao's proposal but it looks like it's addressing a
similar issue on the DSI slave/device side: the device probably needs
to be powered before the host can interact with it through the DSI+DPHY
bus. I'm not entirely sure why we'd need another hook to do that since
we already have the ->prepare() one.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
  2019-12-27 12:21                       ` Boris Brezillon
@ 2020-01-01 17:13                         ` Laurent Pinchart
  -1 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2020-01-01 17:13 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Andrzej Hajda, Marek Szyprowski, dri-devel, Mark Rutland,
	Neil Armstrong, Thierry Reding, kernel, Sam Ravnborg,
	Nikita Yushchenko, Andrey Smirnov, Kyungmin Park, Chris Healy,
	devicetree, Jonas Karlman, Rob Herring, Jernej Skrabec,
	Seung-Woo Kim

Hi Boris,

On Fri, Dec 27, 2019 at 01:21:31PM +0100, Boris Brezillon wrote:
> On Fri, 27 Dec 2019 12:51:54 +0200 Laurent Pinchart wrote:
> > On Fri, Dec 27, 2019 at 10:42:25AM +0100, Andrzej Hajda wrote:
> > > On 24.12.2019 10:44, Boris Brezillon wrote:  
> > > > On Tue, 24 Dec 2019 10:16:49 +0100 Andrzej Hajda wrote:  
> > > >> On 23.12.2019 10:55, Marek Szyprowski wrote:  
> > > >>> On 16.12.2019 16:25, Boris Brezillon wrote:    
> > > >>>> On Mon, 16 Dec 2019 16:02:36 +0100 Marek Szyprowski wrote:    
> > > >>>>> On 16.12.2019 15:55, Boris Brezillon wrote:    
> > > >>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
> > > >>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:    
> > > >>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:    
> > > >>>>>>>> So that each element in the chain can easily access its predecessor.
> > > >>>>>>>> This will be needed to support bus format negotiation between elements
> > > >>>>>>>> of the bridge chain.
> > > >>>>>>>>
> > > >>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > >>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > > >>>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>    
> > > >>>>>>>
> > > >>>>>>> I've noticed that this patch got merged to linux-next as commit
> > > >>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> > > >>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> > > >>>>>>> messages:
> > > >>>>>>>
> > > >>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> > > >>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> > > >>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> > > >>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> > > >>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> > > >>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> > > >>>>>>> [drm] No driver support for vblank timestamp query.
> > > >>>>>>> [drm] Cannot find any crtc or sizes
> > > >>>>>>> [drm] Cannot find any crtc or sizes
> > > >>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> > > >>>>>>>
> > > >>>>>>> I will try to debug this and provide more information soon.  
> > > >>>>>>
> > > >>>>>> Can you try with this diff applied?    
> > > >>>>>
> > > >>>>> This patch doesn't change anything.    
> > > >>>>
> > > >>>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> > > >>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> > > >>>> after the list_splice_init() call?    
> > > >>>
> > > >>> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> > > >>>
> > > >>> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> > > >>> fixed the boot issue.  
> > > >
> > > > If INIT_LIST_HEAD() worked, I don't understand why replacing the
> > > > list_splice() call by a list_splice_init() (which doing a list_splice()
> > > > + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> > > > list_splice_init() version doesn't work?
> > > >  
> > > >>> It looks that this is related with the way the 
> > > >>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> > > >>> will give a bit more detailed comment and spread some light on this.    
> > > >>
> > > >> Hi Marek, Boris,
> > > >>
> > > >> I have not followed latest patches due to high work load, my bad. Marek
> > > >> thanks from pointing
> > > >>
> > > >> About ExynosDSI bridge handling:
> > > >>
> > > >> The order of calling encoder, bridge (and consequently panel) ops
> > > >> enforced by DRM core (bridge->pre_enable, encoder->enable,
> > > >> bridge->enable) does not fit to ExynosDSI hardware initialization
> > > >> sequence, if I remember correctly it does not fit to whole MIPI DSI
> > > >> standard (I think similar situation is with eDP). As a result DSI
> > > >> drivers must use some ugly workarounds, rely on HW properly coping with
> > > >> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> > > >> using encoder->bridge chaining and call bridge ops by itself when suitable.  
> > > >
> > > > Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> > > > that in previous versions of this patchset, unfortunately I didn't get
> > > > any feedback so I went for the less invasive option (keep the hack but
> > > > adapt it to the double-linked list changes), which still lead to
> > > > regressions :-/.
> > > >
> > > > Just a reminder of my 2 proposals:
> > > >
> > > > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> > > >    split your enable/disable logic in 2 parts and make sure things are
> > > >    ready when the panel/next bridge tries to send DSI commands  
> > > 
> > > If it means 'convert exynos_dsi to bridge' I do not think it will help -
> > > 
> > > - pre_enable op will be still called after pre_enable op of downstream
> > > bridge - and this is the main reason why exynos_dsi do not use encoder
> > > bridge chain - it needs to perform some operations BEFORE (pre)enabling
> > > downstream devices.
> 
> Yep, I figured that after Laurent's review.
> 
> > > > 2/ move everything that's needed to send DSI commands out of the  
> > > >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you  
> > > >    can call that in the DSI transfer path too  
> > > 
> > > It looks like a solution for DSI protocol, where control bus is shared
> > > with data bus, but the problem is more general - we have source and sink
> > > connected with some local bus, which has some negotiation/enable/disable
> > > protocol/requirements. And drm_core/bridge framework enforces us to fit
> > > every such protocol to 'drm_bridge protocol' with few opses called in
> > > fixed order, without clearly defined purpose of each ops. That does not
> > > sound generic and results in multiple issues:
> > > 
> > > - different drivers uses different opses to perform the same thing,
> > > 
> > > - different drivers assumes different things about their sinks/sources
> > > in their opses,
> > > 
> > > - more complicated sequences does not fit at all to this model.
> > > 
> > > All this results in incompatibilities between drivers which become
> > > visible with devices used in different configurations/platforms.
> 
> That's true, drm_bridge_funcs semantics is rather vague and probably
> doesn't fit all needs, but that's not the only problem we have when it
> comes to DSI IMHO. I mean, I couldn't find any doc in drm_mipi_dsi.h
> explaining when panel/bridge drivers are allowed to send DCS commands.
> I was personally assuming that we were allowed to send such commands as
> soon as mipi_dsi_attach() was called, but that's not clearly not
> possible with VC4 and Exynos. This part should be clarified too.
> 
> > I fully agree with you, not defining the semantics of the bridge
> > operations precisely was I believe a mistake, and we're paying the price
> > now. That's OK, we "just" need to fix it :-)
> 
> Okay, so how do we fix that? :-)
> 
> I'm not a big fan of specializing the drm_bridge_funcs interface to fit
> protocol X or protocol Y needs. Sounds like a never ending story,
> protocol Z might require something slightly different, and we're likely
> to end up with an interface that's not generic at all.
> 
> Maybe I'm wrong, but it sounds like all DSI ordering issues could be
> addressed at the mipi_dsi_host_ops level if we define a new
> ->enter_power_state() hook and have the DSI framework keep track of the
> host power state (LP,HS,PD,...). The framework would then make sure we
> are in a valid state (LP or HS) before calling dsi_host->transfer(). If
> we have that in place, I don't think we need new hooks at the bridge
> level, at least not for the DSI case. Please let me know if I'm missing
> something.

I'll have to review the mipi_dsi_host_ops in details to answer this, but
I think there will be at least two potential issues.

- The complexity on the DSI host side will increase due to the need to
  handle both the DSI host and bridge operations. We will have to define
  precise semantics for the bridge operations anyway to ensure they work
  properly with the DSI host operations, and that may just defeat the
  purpose of your proposal altogether.

- The issue isn't limited to ->transfer(), and DSI sink needs finer
  control of the source for the purpose of video transfer as well. As
  explained in another e-mail in this thread, it's not uncommon for DSI
  sinks to first require the source to start sending H/V sync packets
  without sending any video data, then get configured (through MIPI
  commands or an out-of-band channel such as I2C or SPI), and finally
  require the source to send video data.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 84+ messages in thread

* Re: [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list
@ 2020-01-01 17:13                         ` Laurent Pinchart
  0 siblings, 0 replies; 84+ messages in thread
From: Laurent Pinchart @ 2020-01-01 17:13 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: Mark Rutland, Nikita Yushchenko, devicetree, Rob Herring,
	Neil Armstrong, Andrey Smirnov, Jonas Karlman, Seung-Woo Kim,
	Jernej Skrabec, dri-devel, Kyungmin Park, Thierry Reding, kernel,
	Sam Ravnborg, Chris Healy, Marek Szyprowski

Hi Boris,

On Fri, Dec 27, 2019 at 01:21:31PM +0100, Boris Brezillon wrote:
> On Fri, 27 Dec 2019 12:51:54 +0200 Laurent Pinchart wrote:
> > On Fri, Dec 27, 2019 at 10:42:25AM +0100, Andrzej Hajda wrote:
> > > On 24.12.2019 10:44, Boris Brezillon wrote:  
> > > > On Tue, 24 Dec 2019 10:16:49 +0100 Andrzej Hajda wrote:  
> > > >> On 23.12.2019 10:55, Marek Szyprowski wrote:  
> > > >>> On 16.12.2019 16:25, Boris Brezillon wrote:    
> > > >>>> On Mon, 16 Dec 2019 16:02:36 +0100 Marek Szyprowski wrote:    
> > > >>>>> On 16.12.2019 15:55, Boris Brezillon wrote:    
> > > >>>>>> On Mon, 16 Dec 2019 14:54:25 +0100
> > > >>>>>> Marek Szyprowski <m.szyprowski@samsung.com> wrote:    
> > > >>>>>>> On 03.12.2019 15:15, Boris Brezillon wrote:    
> > > >>>>>>>> So that each element in the chain can easily access its predecessor.
> > > >>>>>>>> This will be needed to support bus format negotiation between elements
> > > >>>>>>>> of the bridge chain.
> > > >>>>>>>>
> > > >>>>>>>> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > >>>>>>>> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> > > >>>>>>>> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>    
> > > >>>>>>>
> > > >>>>>>> I've noticed that this patch got merged to linux-next as commit
> > > >>>>>>> 05193dc38197021894b17239fafbd2eb1afe5a45. Sadly it breaks booting of
> > > >>>>>>> Samsung Exynos5250-based Arndale board. Booting stops after following
> > > >>>>>>> messages:
> > > >>>>>>>
> > > >>>>>>> [drm] Exynos DRM: using 14400000.fimd device for DMA mapping operations
> > > >>>>>>> exynos-drm exynos-drm: bound 14400000.fimd (ops fimd_component_ops)
> > > >>>>>>> exynos-drm exynos-drm: bound 14450000.mixer (ops mixer_component_ops)
> > > >>>>>>> exynos-drm exynos-drm: bound 14500000.dsi (ops exynos_dsi_component_ops)
> > > >>>>>>> exynos-drm exynos-drm: bound 14530000.hdmi (ops hdmi_component_ops)
> > > >>>>>>> [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
> > > >>>>>>> [drm] No driver support for vblank timestamp query.
> > > >>>>>>> [drm] Cannot find any crtc or sizes
> > > >>>>>>> [drm] Cannot find any crtc or sizes
> > > >>>>>>> [drm] Initialized exynos 1.1.0 20180330 for exynos-drm on minor 0
> > > >>>>>>>
> > > >>>>>>> I will try to debug this and provide more information soon.  
> > > >>>>>>
> > > >>>>>> Can you try with this diff applied?    
> > > >>>>>
> > > >>>>> This patch doesn't change anything.    
> > > >>>>
> > > >>>> Okay. Can you do a list_for_each_entry() on both encoder->bridge_chain
> > > >>>> and dsi->bridge_chain (dump bridge pointers in a pr_info()) before and
> > > >>>> after the list_splice_init() call?    
> > > >>>
> > > >>> encoder->bridge_chain contains only one element. dsi->drive_chain is empty.
> > > >>>
> > > >>> Replacing that list_splice() with INIT_LIST_HEAD(&encoder->bridge_chain) 
> > > >>> fixed the boot issue.  
> > > >
> > > > If INIT_LIST_HEAD() worked, I don't understand why replacing the
> > > > list_splice() call by a list_splice_init() (which doing a list_splice()
> > > > + INIT_LIST_HEAD()) didn't fix the problem. Are you sure the
> > > > list_splice_init() version doesn't work?
> > > >  
> > > >>> It looks that this is related with the way the 
> > > >>> Exynos DSI handles bridges (in bridge and out brige?). Maybe Andrzej 
> > > >>> will give a bit more detailed comment and spread some light on this.    
> > > >>
> > > >> Hi Marek, Boris,
> > > >>
> > > >> I have not followed latest patches due to high work load, my bad. Marek
> > > >> thanks from pointing
> > > >>
> > > >> About ExynosDSI bridge handling:
> > > >>
> > > >> The order of calling encoder, bridge (and consequently panel) ops
> > > >> enforced by DRM core (bridge->pre_enable, encoder->enable,
> > > >> bridge->enable) does not fit to ExynosDSI hardware initialization
> > > >> sequence, if I remember correctly it does not fit to whole MIPI DSI
> > > >> standard (I think similar situation is with eDP). As a result DSI
> > > >> drivers must use some ugly workarounds, rely on HW properly coping with
> > > >> incorrect sequences, or, as in case of ExynosDSI driver, just avoid
> > > >> using encoder->bridge chaining and call bridge ops by itself when suitable.  
> > > >
> > > > Yes, that's definitely hack-ish, and I proposed 2 solutions to address
> > > > that in previous versions of this patchset, unfortunately I didn't get
> > > > any feedback so I went for the less invasive option (keep the hack but
> > > > adapt it to the double-linked list changes), which still lead to
> > > > regressions :-/.
> > > >
> > > > Just a reminder of my 2 proposals:
> > > >
> > > > 1/ implement the bridge_ops->pre_enable/post_disable() hooks so you can
> > > >    split your enable/disable logic in 2 parts and make sure things are
> > > >    ready when the panel/next bridge tries to send DSI commands  
> > > 
> > > If it means 'convert exynos_dsi to bridge' I do not think it will help -
> > > 
> > > - pre_enable op will be still called after pre_enable op of downstream
> > > bridge - and this is the main reason why exynos_dsi do not use encoder
> > > bridge chain - it needs to perform some operations BEFORE (pre)enabling
> > > downstream devices.
> 
> Yep, I figured that after Laurent's review.
> 
> > > > 2/ move everything that's needed to send DSI commands out of the  
> > > >    ->enable() path (maybe in runtime PM resume/suspend hooks) so you  
> > > >    can call that in the DSI transfer path too  
> > > 
> > > It looks like a solution for DSI protocol, where control bus is shared
> > > with data bus, but the problem is more general - we have source and sink
> > > connected with some local bus, which has some negotiation/enable/disable
> > > protocol/requirements. And drm_core/bridge framework enforces us to fit
> > > every such protocol to 'drm_bridge protocol' with few opses called in
> > > fixed order, without clearly defined purpose of each ops. That does not
> > > sound generic and results in multiple issues:
> > > 
> > > - different drivers uses different opses to perform the same thing,
> > > 
> > > - different drivers assumes different things about their sinks/sources
> > > in their opses,
> > > 
> > > - more complicated sequences does not fit at all to this model.
> > > 
> > > All this results in incompatibilities between drivers which become
> > > visible with devices used in different configurations/platforms.
> 
> That's true, drm_bridge_funcs semantics is rather vague and probably
> doesn't fit all needs, but that's not the only problem we have when it
> comes to DSI IMHO. I mean, I couldn't find any doc in drm_mipi_dsi.h
> explaining when panel/bridge drivers are allowed to send DCS commands.
> I was personally assuming that we were allowed to send such commands as
> soon as mipi_dsi_attach() was called, but that's not clearly not
> possible with VC4 and Exynos. This part should be clarified too.
> 
> > I fully agree with you, not defining the semantics of the bridge
> > operations precisely was I believe a mistake, and we're paying the price
> > now. That's OK, we "just" need to fix it :-)
> 
> Okay, so how do we fix that? :-)
> 
> I'm not a big fan of specializing the drm_bridge_funcs interface to fit
> protocol X or protocol Y needs. Sounds like a never ending story,
> protocol Z might require something slightly different, and we're likely
> to end up with an interface that's not generic at all.
> 
> Maybe I'm wrong, but it sounds like all DSI ordering issues could be
> addressed at the mipi_dsi_host_ops level if we define a new
> ->enter_power_state() hook and have the DSI framework keep track of the
> host power state (LP,HS,PD,...). The framework would then make sure we
> are in a valid state (LP or HS) before calling dsi_host->transfer(). If
> we have that in place, I don't think we need new hooks at the bridge
> level, at least not for the DSI case. Please let me know if I'm missing
> something.

I'll have to review the mipi_dsi_host_ops in details to answer this, but
I think there will be at least two potential issues.

- The complexity on the DSI host side will increase due to the need to
  handle both the DSI host and bridge operations. We will have to define
  precise semantics for the bridge operations anyway to ensure they work
  properly with the DSI host operations, and that may just defeat the
  purpose of your proposal altogether.

- The issue isn't limited to ->transfer(), and DSI sink needs finer
  control of the source for the purpose of video transfer as well. As
  explained in another e-mail in this thread, it's not uncommon for DSI
  sinks to first require the source to start sending H/V sync packets
  without sending any video data, then get configured (through MIPI
  commands or an out-of-band channel such as I2C or SPI), and finally
  require the source to send video data.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 84+ messages in thread

end of thread, other threads:[~2020-01-01 17:13 UTC | newest]

Thread overview: 84+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-03 14:15 [PATCH v4 00/11] drm: Add support for bus-format negotiation Boris Brezillon
2019-12-03 14:15 ` Boris Brezillon
2019-12-03 14:15 ` [PATCH v4 01/11] drm/bridge: Rename bridge helpers targeting a bridge chain Boris Brezillon
2019-12-03 14:15   ` Boris Brezillon
2019-12-03 14:15 ` [PATCH v4 02/11] drm/bridge: Introduce drm_bridge_get_next_bridge() Boris Brezillon
2019-12-03 14:15   ` Boris Brezillon
2019-12-03 14:15 ` [PATCH v4 03/11] drm: Stop accessing encoder->bridge directly Boris Brezillon
2019-12-03 14:15   ` Boris Brezillon
2019-12-03 14:15 ` [PATCH v4 04/11] drm/bridge: Make the bridge chain a double-linked list Boris Brezillon
2019-12-03 14:15   ` Boris Brezillon
2019-12-16 13:54   ` Marek Szyprowski
2019-12-16 13:54     ` Marek Szyprowski
2019-12-16 14:55     ` Boris Brezillon
2019-12-16 14:55       ` Boris Brezillon
2019-12-16 15:02       ` Marek Szyprowski
2019-12-16 15:02         ` Marek Szyprowski
2019-12-16 15:25         ` Boris Brezillon
2019-12-16 15:25           ` Boris Brezillon
2019-12-23  9:55           ` Marek Szyprowski
2019-12-23  9:55             ` Marek Szyprowski
2019-12-24  9:16             ` Andrzej Hajda
2019-12-24  9:16               ` Andrzej Hajda
2019-12-24  9:44               ` Boris Brezillon
2019-12-24  9:44                 ` Boris Brezillon
2019-12-24  9:49                 ` Boris Brezillon
2019-12-24  9:49                   ` Boris Brezillon
2019-12-24 10:03                   ` Boris Brezillon
2019-12-24 10:03                     ` Boris Brezillon
2019-12-27 10:25                     ` Marek Szyprowski
2019-12-27 10:25                       ` Marek Szyprowski
2019-12-27 11:03                       ` Marek Szyprowski
2019-12-27 11:03                         ` Marek Szyprowski
2019-12-24 11:31                 ` Sam Ravnborg
2019-12-24 11:31                   ` Sam Ravnborg
2019-12-25  1:36                   ` Laurent Pinchart
2019-12-25  1:36                     ` Laurent Pinchart
2019-12-27 12:39                   ` Boris Brezillon
2019-12-27 12:39                     ` Boris Brezillon
2019-12-27  9:42                 ` Andrzej Hajda
2019-12-27  9:42                   ` Andrzej Hajda
2019-12-27 10:51                   ` Laurent Pinchart
2019-12-27 10:51                     ` Laurent Pinchart
2019-12-27 12:21                     ` Boris Brezillon
2019-12-27 12:21                       ` Boris Brezillon
2020-01-01 17:13                       ` Laurent Pinchart
2020-01-01 17:13                         ` Laurent Pinchart
2019-12-03 14:15 ` [PATCH v4 05/11] drm/bridge: Add the drm_for_each_bridge_in_chain() helper Boris Brezillon
2019-12-03 14:15   ` Boris Brezillon
2019-12-03 14:15 ` [PATCH v4 06/11] drm/bridge: Add the drm_bridge_get_prev_bridge() helper Boris Brezillon
2019-12-03 14:15   ` Boris Brezillon
2019-12-03 14:15 ` [PATCH v4 07/11] drm/bridge: Clarify the atomic enable/disable hooks semantics Boris Brezillon
2019-12-03 14:15   ` Boris Brezillon
2019-12-03 18:02   ` Laurent Pinchart
2019-12-03 18:02     ` Laurent Pinchart
2019-12-04  9:00     ` Boris Brezillon
2019-12-04  9:00       ` Boris Brezillon
2019-12-03 14:15 ` [PATCH v4 08/11] drm/bridge: Add a drm_bridge_state object Boris Brezillon
2019-12-03 14:15   ` Boris Brezillon
2019-12-03 18:17   ` Laurent Pinchart
2019-12-03 18:17     ` Laurent Pinchart
2019-12-04  9:03     ` Boris Brezillon
2019-12-04  9:03       ` Boris Brezillon
2019-12-04  9:12       ` Laurent Pinchart
2019-12-04  9:12         ` Laurent Pinchart
2019-12-04  9:42         ` Boris Brezillon
2019-12-04  9:42           ` Boris Brezillon
2019-12-04 10:38           ` Laurent Pinchart
2019-12-04 10:38             ` Laurent Pinchart
2019-12-03 14:15 ` [PATCH v4 09/11] drm/bridge: Patch atomic hooks to take a drm_bridge_state Boris Brezillon
2019-12-03 14:15   ` Boris Brezillon
2019-12-03 14:15 ` [PATCH v4 10/11] drm/bridge: Add an ->atomic_check() hook Boris Brezillon
2019-12-03 14:15   ` Boris Brezillon
2019-12-03 14:15 ` [PATCH v4 11/11] drm/bridge: Add the necessary bits to support bus format negotiation Boris Brezillon
2019-12-03 14:15   ` Boris Brezillon
2019-12-03 18:19 ` [PATCH v4 00/11] drm: Add support for bus-format negotiation Laurent Pinchart
2019-12-03 18:19   ` Laurent Pinchart
2019-12-04  9:09   ` Boris Brezillon
2019-12-04  9:09     ` Boris Brezillon
2019-12-04  9:15     ` Laurent Pinchart
2019-12-04  9:15       ` Laurent Pinchart
2019-12-04 13:43   ` Neil Armstrong
2019-12-04 13:43     ` Neil Armstrong
2019-12-09  9:43   ` Boris Brezillon
2019-12-09  9:43     ` Boris Brezillon

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.