Linux-Devicetree Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v3 00/21] drm: Add support for bus-format negotiation
@ 2019-10-23 15:44 Boris Brezillon
  2019-10-23 15:44 ` [PATCH v3 01/21] drm/vc4: Declare the DSI encoder as a bridge element Boris Brezillon
                   ` (22 more replies)
  0 siblings, 23 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:44 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, 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.

Major changes since v2:
* Get rid of the dummy bridge embedded in drm_encoder and let encoder
  drivers provide their own bridge element
* Clarify APIs and improve the doc
* Propagate bus flags by default

Major changes since the RFC:

* Add a dummy bridge to the drm_encoder object so that vc4 and exynos
  DSI drivers can implement the pre_enable/post_disable hooks instead
  of manually setting encoder->bridge to NULL to control the
  enable/disable sequence. This change is also a first step towards
  drm_bridge/drm_encoder unification. New encoder drivers should
  stop implementing drm_encoder_helper_funcs and switch to
  drm_bridge_funcs. Existing drivers can be converted progressively
  (already have a branch where I started converting some of them [1])
* rework the bus format negotiation to give more control to bridge
  drivers in the selection process (driver can select at runtime which
  input bus format they support for a specific output bus format based
  on any information available in the connector, crtc and bridge state.

A more detailed changelog is provided in each patch.

This patch series is also available here [2].

Thanks,

Boris

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

*** BLURB HERE ***

Boris Brezillon (21):
  drm/vc4: Declare the DSI encoder as a bridge element
  drm/exynos: Don't reset bridge->next
  drm/exynos: Declare the DSI encoder as a bridge element
  drm/bridge: Rename bridge helpers targeting a bridge chain
  drm/bridge: Introduce drm_bridge_chain_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 a drm_bridge_state object
  drm/bridge: Clarify the atomic enable/disable hooks semantics
  drm/bridge: Patch atomic hooks to take a drm_bridge_state
  drm/bridge: Add an ->atomic_check() hook
  drm/bridge: Add the drm_bridge_chain_get_prev_bridge() helper
  drm/bridge: Add the necessary bits to support bus format negotiation
  drm/imx: pd: Use bus format/flags provided by the bridge when
    available
  drm/bridge: lvds-encoder: Implement basic bus format negotiation
  dt-bindings: display: bridge: lvds-transmitter: Add new props
  drm/bridge: panel: Propage bus format/flags
  drm/panel: simple: Add support for Toshiba LTA089AC29000 panel
  dt-bindings: display: panel: Add the LTA089AC29000 variant
  ARM: dts: imx: imx51-zii-rdu1: Fix the display pipeline definition

 .../display/bridge/lvds-transmitter.txt       |  13 +
 .../display/panel/toshiba,lt089ac29000.txt    |   5 +-
 arch/arm/boot/dts/imx51-zii-rdu1.dts          |  24 +-
 .../drm/bridge/analogix/analogix_dp_core.c    |  12 +-
 drivers/gpu/drm/bridge/lvds-encoder.c         |  72 ++
 drivers/gpu/drm/bridge/panel.c                |   1 +
 drivers/gpu/drm/drm_atomic.c                  |  39 +
 drivers/gpu/drm/drm_atomic_helper.c           |  54 +-
 drivers/gpu/drm/drm_bridge.c                  | 800 +++++++++++++++---
 drivers/gpu/drm/drm_encoder.c                 |  15 +-
 drivers/gpu/drm/drm_probe_helper.c            |   4 +-
 drivers/gpu/drm/exynos/exynos_dp.c            |   1 -
 drivers/gpu/drm/exynos/exynos_drm_dsi.c       |  90 +-
 drivers/gpu/drm/imx/parallel-display.c        | 174 +++-
 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/panel/panel-simple.c          |  36 +
 drivers/gpu/drm/rcar-du/rcar_du_crtc.c        |  11 +-
 drivers/gpu/drm/vc4/vc4_dsi.c                 |  90 +-
 include/drm/drm_atomic.h                      |   3 +
 include/drm/drm_bridge.h                      | 396 ++++++++-
 include/drm/drm_encoder.h                     |   9 +-
 24 files changed, 1588 insertions(+), 286 deletions(-)

-- 
2.21.0


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

* [PATCH v3 01/21] drm/vc4: Declare the DSI encoder as a bridge element
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
@ 2019-10-23 15:44 ` Boris Brezillon
  2019-11-24 10:01   ` Laurent Pinchart
  2019-10-23 15:44 ` [PATCH v3 02/21] drm/exynos: Don't reset bridge->next Boris Brezillon
                   ` (21 subsequent siblings)
  22 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:44 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, Boris Brezillon

Encoder drivers will progressively transition to the drm_bridge
interface in place of the drm_encoder one.

Let's start with the VC4 driver, and use the ->pre_{enable,disable}()
hooks to get rid of the hack resetting encoder->bridge.next (which was
needed to control the encoder/bridge enable/disable sequence).

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
Changes in v3:
* Embed a drm_bridge object in vc4_dsi since drm_encoder no longer has
  a dummy bridge

Changes in v2:
* New patch (replaces "drm/vc4: Get rid of the dsi->bridge field")
---
 drivers/gpu/drm/vc4/vc4_dsi.c | 88 +++++++++++++++++++++--------------
 1 file changed, 52 insertions(+), 36 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index c9ba83ed49b9..49f8a313e759 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -498,7 +498,11 @@ struct vc4_dsi {
 
 	struct mipi_dsi_host dsi_host;
 	struct drm_encoder *encoder;
-	struct drm_bridge *bridge;
+
+	/* Embed a bridge object so we can implement bridge funcs instead of
+	 * encoder ones.
+	 */
+	struct drm_bridge bridge;
 
 	void __iomem *regs;
 
@@ -543,6 +547,11 @@ struct vc4_dsi {
 	struct debugfs_regset32 regset;
 };
 
+static inline struct vc4_dsi *bridge_to_vc4_dsi(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct vc4_dsi, bridge);
+}
+
 #define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host)
 
 static inline void
@@ -747,16 +756,11 @@ dsi_esc_timing(u32 ns)
 	return DIV_ROUND_UP(ns, ESC_TIME_NS);
 }
 
-static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
+static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge)
 {
-	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
-	struct vc4_dsi *dsi = vc4_encoder->dsi;
+	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
 	struct device *dev = &dsi->pdev->dev;
 
-	drm_bridge_disable(dsi->bridge);
-	vc4_dsi_ulps(dsi, true);
-	drm_bridge_post_disable(dsi->bridge);
-
 	clk_disable_unprepare(dsi->pll_phy_clock);
 	clk_disable_unprepare(dsi->escape_clock);
 	clk_disable_unprepare(dsi->pixel_clock);
@@ -764,6 +768,13 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
 	pm_runtime_put(dev);
 }
 
+static void vc4_dsi_bridge_disable(struct drm_bridge *bridge)
+{
+	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
+
+	vc4_dsi_ulps(dsi, true);
+}
+
 /* Extends the mode's blank intervals to handle BCM2835's integer-only
  * DSI PLL divider.
  *
@@ -777,12 +788,11 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
  * higher-than-expected clock rate to the panel, but that's what the
  * firmware does too.
  */
-static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
-				       const struct drm_display_mode *mode,
-				       struct drm_display_mode *adjusted_mode)
+static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
+				      const struct drm_display_mode *mode,
+				      struct drm_display_mode *adjusted_mode)
 {
-	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
-	struct vc4_dsi *dsi = vc4_encoder->dsi;
+	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
 	struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock);
 	unsigned long parent_rate = clk_get_rate(phy_parent);
 	unsigned long pixel_clock_hz = mode->clock * 1000;
@@ -816,11 +826,11 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
 	return true;
 }
 
-static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
+static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge)
 {
+	struct drm_encoder *encoder = bridge->encoder;
 	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
-	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
-	struct vc4_dsi *dsi = vc4_encoder->dsi;
+	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
 	struct device *dev = &dsi->pdev->dev;
 	bool debug_dump_regs = false;
 	unsigned long hs_clock;
@@ -1054,8 +1064,12 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 	}
 
 	vc4_dsi_ulps(dsi, false);
+}
 
-	drm_bridge_pre_enable(dsi->bridge);
+static void vc4_dsi_bridge_enable(struct drm_bridge *bridge)
+{
+	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
+	bool debug_dump_regs = false;
 
 	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
 		DSI_PORT_WRITE(DISP0_CTRL,
@@ -1072,8 +1086,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 			       DSI_DISP0_ENABLE);
 	}
 
-	drm_bridge_enable(dsi->bridge);
-
 	if (debug_dump_regs) {
 		struct drm_printer p = drm_info_printer(&dsi->pdev->dev);
 		dev_info(&dsi->pdev->dev, "DSI regs after:\n");
@@ -1290,10 +1302,12 @@ static const struct mipi_dsi_host_ops vc4_dsi_host_ops = {
 	.transfer = vc4_dsi_host_transfer,
 };
 
-static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = {
-	.disable = vc4_dsi_encoder_disable,
-	.enable = vc4_dsi_encoder_enable,
-	.mode_fixup = vc4_dsi_encoder_mode_fixup,
+static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = {
+	.pre_enable = vc4_dsi_bridge_pre_enable,
+	.enable = vc4_dsi_bridge_enable,
+	.disable = vc4_dsi_bridge_disable,
+	.post_disable = vc4_dsi_bridge_post_disable,
+	.mode_fixup = vc4_dsi_bridge_mode_fixup,
 };
 
 static const struct of_device_id vc4_dsi_dt_match[] = {
@@ -1445,6 +1459,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_dsi *dsi = dev_get_drvdata(dev);
 	struct vc4_dsi_encoder *vc4_dsi_encoder;
+	struct drm_bridge *next_bridge;
 	struct drm_panel *panel;
 	const struct of_device_id *match;
 	dma_cap_mask_t dma_mask;
@@ -1561,7 +1576,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	}
 
 	ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
-					  &panel, &dsi->bridge);
+					  &panel, &next_bridge);
 	if (ret) {
 		/* If the bridge or panel pointed by dev->of_node is not
 		 * enabled, just return 0 here so that we don't prevent the DRM
@@ -1576,10 +1591,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	}
 
 	if (panel) {
-		dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel,
+		next_bridge = devm_drm_panel_bridge_add_typed(dev, panel,
 							      DRM_MODE_CONNECTOR_DSI);
-		if (IS_ERR(dsi->bridge))
-			return PTR_ERR(dsi->bridge);
+		if (IS_ERR(next_bridge))
+			return PTR_ERR(next_bridge);
 	}
 
 	/* The esc clock rate is supposed to always be 100Mhz. */
@@ -1598,19 +1613,20 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 
 	drm_encoder_init(drm, dsi->encoder, &vc4_dsi_encoder_funcs,
 			 DRM_MODE_ENCODER_DSI, NULL);
-	drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs);
 
-	ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL);
+	/* Declare ourself as the first bridge element. */
+	dsi->bridge.funcs = &vc4_dsi_bridge_funcs;
+	ret = drm_bridge_attach(dsi->encoder, &dsi->bridge, NULL);
+	if (ret) {
+		dev_err(dev, "bridge attach failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = drm_bridge_attach(dsi->encoder, next_bridge, &dsi->bridge);
 	if (ret) {
 		dev_err(dev, "bridge attach failed: %d\n", ret);
 		return ret;
 	}
-	/* Disable the atomic helper calls into the bridge.  We
-	 * manually call the bridge pre_enable / enable / etc. calls
-	 * from our driver, since we need to sequence them within the
-	 * encoder's enable/disable paths.
-	 */
-	dsi->encoder->bridge = NULL;
 
 	if (dsi->port == 0)
 		vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
@@ -1629,7 +1645,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
 	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_dsi *dsi = dev_get_drvdata(dev);
 
-	if (dsi->bridge)
+	if (dsi->bridge.next)
 		pm_runtime_disable(dev);
 
 	vc4_dsi_encoder_destroy(dsi->encoder);
-- 
2.21.0


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

* [PATCH v3 02/21] drm/exynos: Don't reset bridge->next
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
  2019-10-23 15:44 ` [PATCH v3 01/21] drm/vc4: Declare the DSI encoder as a bridge element Boris Brezillon
@ 2019-10-23 15:44 ` Boris Brezillon
  2019-10-28 12:19   ` Inki Dae
  2019-10-23 15:44 ` [PATCH v3 03/21] drm/exynos: Declare the DSI encoder as a bridge element Boris Brezillon
                   ` (20 subsequent siblings)
  22 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:44 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, Boris Brezillon, Laurent Pinchart

bridge->next is only points to the new bridge if drm_bridge_attach()
succeeds. No need to reset it manually here.

Note that this change is part of the attempt to make the bridge chain
a double-linked list. In order to do that we must patch all drivers
manipulating the bridge->next field.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes in v2:
* Add Laurent's R-b (I'd like to have a R-b from the DRM exynos
  maintainers before applying that one)
---
 drivers/gpu/drm/exynos/exynos_dp.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c
index 1e6aa24bf45e..4785885c0f4f 100644
--- a/drivers/gpu/drm/exynos/exynos_dp.c
+++ b/drivers/gpu/drm/exynos/exynos_dp.c
@@ -110,7 +110,6 @@ static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data,
 		if (ret) {
 			DRM_DEV_ERROR(dp->dev,
 				      "Failed to attach bridge to drm\n");
-			bridge->next = NULL;
 			return ret;
 		}
 	}
-- 
2.21.0


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

* [PATCH v3 03/21] drm/exynos: Declare the DSI encoder as a bridge element
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
  2019-10-23 15:44 ` [PATCH v3 01/21] drm/vc4: Declare the DSI encoder as a bridge element Boris Brezillon
  2019-10-23 15:44 ` [PATCH v3 02/21] drm/exynos: Don't reset bridge->next Boris Brezillon
@ 2019-10-23 15:44 ` Boris Brezillon
  2019-11-24 10:24   ` Laurent Pinchart
  2019-10-23 15:44 ` [PATCH v3 04/21] drm/bridge: Rename bridge helpers targeting a bridge chain Boris Brezillon
                   ` (19 subsequent siblings)
  22 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:44 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, Boris Brezillon

Encoder drivers will progressively transition to the drm_bridge
interface in place of the drm_encoder one.

Converting the Exynos DSI encoder driver to this approach allows us to
use the ->pre_{enable,disable}()  hooks and get rid of the hack
resetting encoder->bridge.next (which was needed to control the
encoder/bridge enable/disable sequence).

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
Changes in v3:
* Embed a drm_bridge object in exynos_dsi since drm_encoder no longer
  has a dummy bridge

Changes in v2:
* New patch (replacement for "drm/exynos: Get rid of exynos_dsi->out_bridge")
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 89 +++++++++++++++----------
 1 file changed, 55 insertions(+), 34 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 72726f2c7a9f..3915f50b005e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -252,10 +252,10 @@ struct exynos_dsi_driver_data {
 
 struct exynos_dsi {
 	struct drm_encoder encoder;
+	struct drm_bridge bridge;
 	struct mipi_dsi_host dsi_host;
 	struct drm_connector connector;
 	struct drm_panel *panel;
-	struct drm_bridge *out_bridge;
 	struct device *dev;
 
 	void __iomem *reg_base;
@@ -291,6 +291,11 @@ static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e)
 	return container_of(e, struct exynos_dsi, encoder);
 }
 
+static inline struct exynos_dsi *bridge_to_dsi(struct drm_bridge *b)
+{
+	return container_of(b, struct exynos_dsi, bridge);
+}
+
 enum reg_idx {
 	DSIM_STATUS_REG,	/* Status register */
 	DSIM_SWRST_REG,		/* Software reset register */
@@ -1374,25 +1379,38 @@ static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
 	}
 }
 
-static void exynos_dsi_enable(struct drm_encoder *encoder)
+static void exynos_dsi_pre_enable(struct drm_bridge *bridge)
 {
-	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
 	int ret;
 
 	if (dsi->state & DSIM_STATE_ENABLED)
 		return;
 
 	pm_runtime_get_sync(dsi->dev);
-	dsi->state |= DSIM_STATE_ENABLED;
 
 	if (dsi->panel) {
 		ret = drm_panel_prepare(dsi->panel);
 		if (ret < 0)
 			goto err_put_sync;
-	} else {
-		drm_bridge_pre_enable(dsi->out_bridge);
 	}
 
+	dsi->state |= DSIM_STATE_ENABLED;
+	return;
+
+err_put_sync:
+	pm_runtime_put(dsi->dev);
+}
+
+static void exynos_dsi_enable(struct drm_bridge *bridge)
+{
+	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
+	int ret;
+
+	if (!(dsi->state & DSIM_STATE_ENABLED) ||
+	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
+		return;
+
 	exynos_dsi_set_display_mode(dsi);
 	exynos_dsi_set_display_enable(dsi, true);
 
@@ -1400,8 +1418,6 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
 		ret = drm_panel_enable(dsi->panel);
 		if (ret < 0)
 			goto err_display_disable;
-	} else {
-		drm_bridge_enable(dsi->out_bridge);
 	}
 
 	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
@@ -1410,28 +1426,30 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
 err_display_disable:
 	exynos_dsi_set_display_enable(dsi, false);
 	drm_panel_unprepare(dsi->panel);
-
-err_put_sync:
-	dsi->state &= ~DSIM_STATE_ENABLED;
-	pm_runtime_put(dsi->dev);
 }
 
-static void exynos_dsi_disable(struct drm_encoder *encoder)
+static void exynos_dsi_disable(struct drm_bridge *bridge)
 {
-	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
+
+	if (!(dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
+		return;
+
+	drm_panel_disable(dsi->panel);
+	exynos_dsi_set_display_enable(dsi, false);
+	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
+}
+
+static void exynos_dsi_post_disable(struct drm_bridge *bridge)
+{
+	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
 
 	if (!(dsi->state & DSIM_STATE_ENABLED))
 		return;
 
-	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
-
-	drm_panel_disable(dsi->panel);
-	drm_bridge_disable(dsi->out_bridge);
-	exynos_dsi_set_display_enable(dsi, false);
 	drm_panel_unprepare(dsi->panel);
-	drm_bridge_post_disable(dsi->out_bridge);
-	dsi->state &= ~DSIM_STATE_ENABLED;
 	pm_runtime_put_sync(dsi->dev);
+	dsi->state &= ~DSIM_STATE_ENABLED;
 }
 
 static enum drm_connector_status
@@ -1499,9 +1517,11 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder)
 	return 0;
 }
 
-static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = {
+static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
+	.pre_enable = exynos_dsi_pre_enable,
 	.enable = exynos_dsi_enable,
 	.disable = exynos_dsi_disable,
+	.post_disable = exynos_dsi_post_disable,
 };
 
 static const struct drm_encoder_funcs exynos_dsi_encoder_funcs = {
@@ -1520,9 +1540,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
 
 	out_bridge  = of_drm_find_bridge(device->dev.of_node);
 	if (out_bridge) {
-		drm_bridge_attach(encoder, out_bridge, NULL);
-		dsi->out_bridge = out_bridge;
-		encoder->bridge = NULL;
+		drm_bridge_attach(encoder, out_bridge, &dsi->bridge);
 	} else {
 		int ret = exynos_dsi_create_connector(encoder);
 
@@ -1575,19 +1593,19 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
 				  struct mipi_dsi_device *device)
 {
 	struct exynos_dsi *dsi = host_to_dsi(host);
+	struct drm_bridge *out_bridge = dsi->bridge.next;
 	struct drm_device *drm = dsi->encoder.dev;
 
 	if (dsi->panel) {
 		mutex_lock(&drm->mode_config.mutex);
-		exynos_dsi_disable(&dsi->encoder);
+		exynos_dsi_disable(&dsi->bridge);
+		exynos_dsi_post_disable(&dsi->bridge);
 		drm_panel_detach(dsi->panel);
 		dsi->panel = NULL;
 		dsi->connector.status = connector_status_disconnected;
 		mutex_unlock(&drm->mode_config.mutex);
-	} else {
-		if (dsi->out_bridge->funcs->detach)
-			dsi->out_bridge->funcs->detach(dsi->out_bridge);
-		dsi->out_bridge = NULL;
+	} else if (out_bridge && out_bridge->funcs->detach) {
+		out_bridge->funcs->detach(out_bridge);
 	}
 
 	if (drm->mode_config.poll_enabled)
@@ -1687,16 +1705,18 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
 	drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs,
 			 DRM_MODE_ENCODER_TMDS, NULL);
 
-	drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
-
 	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
 	if (ret < 0)
 		return ret;
 
+	/* Declare ourself as the first bridge element. */
+	dsi->bridge.funcs = &exynos_dsi_bridge_funcs;
+	drm_bridge_attach(encoder, &dsi->bridge, NULL);
+
 	if (dsi->in_bridge_node) {
 		in_bridge = of_drm_find_bridge(dsi->in_bridge_node);
 		if (in_bridge)
-			drm_bridge_attach(encoder, in_bridge, NULL);
+			drm_bridge_attach(encoder, in_bridge, &dsi->bridge);
 	}
 
 	return mipi_dsi_host_register(&dsi->dsi_host);
@@ -1708,7 +1728,8 @@ static void exynos_dsi_unbind(struct device *dev, struct device *master,
 	struct drm_encoder *encoder = dev_get_drvdata(dev);
 	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
 
-	exynos_dsi_disable(encoder);
+	exynos_dsi_disable(&dsi->bridge);
+	exynos_dsi_post_disable(&dsi->bridge);
 
 	mipi_dsi_host_unregister(&dsi->dsi_host);
 }
-- 
2.21.0


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

* [PATCH v3 04/21] drm/bridge: Rename bridge helpers targeting a bridge chain
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (2 preceding siblings ...)
  2019-10-23 15:44 ` [PATCH v3 03/21] drm/exynos: Declare the DSI encoder as a bridge element Boris Brezillon
@ 2019-10-23 15:44 ` Boris Brezillon
  2019-10-25 13:26   ` Neil Armstrong
  2019-11-24 10:28   ` Laurent Pinchart
  2019-10-23 15:44 ` [PATCH v3 05/21] drm/bridge: Introduce drm_bridge_chain_get_next_bridge() Boris Brezillon
                   ` (18 subsequent siblings)
  22 siblings, 2 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:44 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, 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>
---
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/mediatek/mtk_hdmi.c |   4 +-
 include/drm/drm_bridge.h            |  64 +++++++-------
 5 files changed, 112 insertions(+), 102 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/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index c79b1f855d89..ea68b5adccbe 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/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.21.0


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

* [PATCH v3 05/21] drm/bridge: Introduce drm_bridge_chain_get_next_bridge()
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (3 preceding siblings ...)
  2019-10-23 15:44 ` [PATCH v3 04/21] drm/bridge: Rename bridge helpers targeting a bridge chain Boris Brezillon
@ 2019-10-23 15:44 ` Boris Brezillon
  2019-10-25 13:27   ` Neil Armstrong
  2019-11-24 10:33   ` Laurent Pinchart
  2019-10-23 15:44 ` [PATCH v3 06/21] drm: Stop accessing encoder->bridge directly Boris Brezillon
                   ` (17 subsequent siblings)
  22 siblings, 2 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:44 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, 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>
---
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/exynos/exynos_drm_dsi.c |  3 ++-
 drivers/gpu/drm/mediatek/mtk_hdmi.c     |  6 ++++--
 drivers/gpu/drm/omapdrm/omap_drv.c      |  4 ++--
 drivers/gpu/drm/omapdrm/omap_encoder.c  |  3 ++-
 drivers/gpu/drm/vc4/vc4_dsi.c           |  4 +++-
 include/drm/drm_bridge.h                | 13 +++++++++++++
 6 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 3915f50b005e..005c67894b78 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1593,9 +1593,10 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
 				  struct mipi_dsi_device *device)
 {
 	struct exynos_dsi *dsi = host_to_dsi(host);
-	struct drm_bridge *out_bridge = dsi->bridge.next;
 	struct drm_device *drm = dsi->encoder.dev;
+	struct drm_bridge *out_bridge;
 
+	out_bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
 	if (dsi->panel) {
 		mutex_lock(&drm->mode_config.mutex);
 		exynos_dsi_disable(&dsi->bridge);
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index ea68b5adccbe..cfaa5aab8876 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_chain_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..865164fe28dc 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_chain_get_next_bridge(bridge))
+			bridge = drm_bridge_chain_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..8ca54081997e 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_chain_get_next_bridge(bridge)) {
 		if (!bridge->timings)
 			continue;
 
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 49f8a313e759..49c47185aff0 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1644,8 +1644,10 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
 	struct drm_device *drm = dev_get_drvdata(master);
 	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_dsi *dsi = dev_get_drvdata(dev);
+	struct drm_bridge *bridge;
 
-	if (dsi->bridge.next)
+	bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
+	if (bridge)
 		pm_runtime_disable(dev);
 
 	vc4_dsi_encoder_destroy(dsi->encoder);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 726435baf4ad..8aeba83fcf31 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_chain_get_next_bridge() - Get the next bridge in the chain
+ * @bridge: bridge object
+ *
+ * RETURNS:
+ * the next bridge in the chain, or NULL if @bridge is the last.
+ */
+static inline struct drm_bridge *
+drm_bridge_chain_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.21.0


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

* [PATCH v3 06/21] drm: Stop accessing encoder->bridge directly
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (4 preceding siblings ...)
  2019-10-23 15:44 ` [PATCH v3 05/21] drm/bridge: Introduce drm_bridge_chain_get_next_bridge() Boris Brezillon
@ 2019-10-23 15:44 ` Boris Brezillon
  2019-10-25 13:28   ` Neil Armstrong
  2019-11-24 10:39   ` Laurent Pinchart
  2019-10-23 15:44 ` [PATCH v3 07/21] drm/bridge: Make the bridge chain a double-linked list Boris Brezillon
                   ` (16 subsequent siblings)
  22 siblings, 2 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:44 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, 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.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
 drivers/gpu/drm/drm_atomic_helper.c    | 25 +++++++++++++++++--------
 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 | 11 ++++++++---
 include/drm/drm_bridge.h               | 15 +++++++++++++++
 6 files changed, 53 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index cf678be58fa4..f02ddffd4960 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,7 +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,
+		drm_atomic_bridge_chain_post_disable(bridge,
 						     old_state);
 	}
 
@@ -1190,6 +1196,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 +1224,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 +1324,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 +1343,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 +1355,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 80d88a55302e..4fe9e723e227 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 = bridge->next;
 			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..7a1f1e5f0326 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,15 @@ 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 8aeba83fcf31..27eef63ce0ff 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_chain_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.21.0


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

* [PATCH v3 07/21] drm/bridge: Make the bridge chain a double-linked list
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (5 preceding siblings ...)
  2019-10-23 15:44 ` [PATCH v3 06/21] drm: Stop accessing encoder->bridge directly Boris Brezillon
@ 2019-10-23 15:44 ` Boris Brezillon
  2019-10-25 13:29   ` Neil Armstrong
  2019-11-24 13:57   ` Laurent Pinchart
  2019-10-23 15:44 ` [PATCH v3 08/21] drm/bridge: Add the drm_for_each_bridge_in_chain() helper Boris Brezillon
                   ` (15 subsequent siblings)
  22 siblings, 2 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:44 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, 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>
---
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 +---
 include/drm/drm_bridge.h      |  12 ++-
 include/drm/drm_encoder.h     |   9 +-
 4 files changed, 135 insertions(+), 73 deletions(-)

diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 54c874493c57..c5cf8a9c4237 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.
  */
@@ -114,6 +114,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
 int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 		      struct drm_bridge *previous)
 {
+	LIST_HEAD(tmp_list);
 	int ret;
 
 	if (!encoder || !bridge)
@@ -127,6 +128,7 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 
 	bridge->dev = encoder->dev;
 	bridge->encoder = encoder;
+	list_add_tail(&bridge->chain_node, &tmp_list);
 
 	if (bridge->funcs->attach) {
 		ret = bridge->funcs->attach(bridge);
@@ -138,9 +140,9 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 	}
 
 	if (previous)
-		previous->next = bridge;
+		list_splice(&tmp_list, &previous->chain_node);
 	else
-		encoder->bridge = bridge;
+		list_splice(&tmp_list, &encoder->bridge_chain);
 
 	return 0;
 }
@@ -157,6 +159,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 +193,22 @@ 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 +231,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;
+
+	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 (bridge->funcs->mode_valid)
 		ret = bridge->funcs->mode_valid(bridge, mode);
+		if (ret != MODE_OK)
+			return ret;
+	}
 
-	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 +264,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 +294,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 +323,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 +349,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 +375,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 +403,23 @@ 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 +439,19 @@ 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 +471,23 @@ 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, &bridge->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 +506,19 @@ 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, &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 4fe9e723e227..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 = bridge->next;
-			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/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 27eef63ce0ff..3ab16c95e59e 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_chain_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_chain_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..9b3dde177c81 100644
--- a/include/drm/drm_encoder.h
+++ b/include/drm/drm_encoder.h
@@ -172,7 +172,14 @@ struct drm_encoder {
 	 * &drm_connector_state.crtc.
 	 */
 	struct drm_crtc *crtc;
-	struct drm_bridge *bridge;
+
+	/**
+	 * @bridge_chain: Bridges attached to this encoder. The first entry of
+	 * this list is always &drm_encoder.bridge. It may be followed by other
+	 * bridge entities.
+	 */
+	struct list_head bridge_chain;
+
 	const struct drm_encoder_funcs *funcs;
 	const struct drm_encoder_helper_funcs *helper_private;
 };
-- 
2.21.0


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

* [PATCH v3 08/21] drm/bridge: Add the drm_for_each_bridge_in_chain() helper
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (6 preceding siblings ...)
  2019-10-23 15:44 ` [PATCH v3 07/21] drm/bridge: Make the bridge chain a double-linked list Boris Brezillon
@ 2019-10-23 15:44 ` Boris Brezillon
  2019-10-25 13:30   ` Neil Armstrong
  2019-11-24 14:07   ` Laurent Pinchart
  2019-10-23 15:45 ` [PATCH v3 09/21] drm/bridge: Add a drm_bridge_state object Boris Brezillon
                   ` (14 subsequent siblings)
  22 siblings, 2 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:44 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, 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>
---
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 3ab16c95e59e..238e84ab63a3 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);
 }
 
+/**
+ * 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.21.0


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

* [PATCH v3 09/21] drm/bridge: Add a drm_bridge_state object
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (7 preceding siblings ...)
  2019-10-23 15:44 ` [PATCH v3 08/21] drm/bridge: Add the drm_for_each_bridge_in_chain() helper Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-10-25 14:35   ` Neil Armstrong
                     ` (2 more replies)
  2019-10-23 15:45 ` [PATCH v3 10/21] drm/bridge: Clarify the atomic enable/disable hooks semantics Boris Brezillon
                   ` (13 subsequent siblings)
  22 siblings, 3 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, 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>
---
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        | 168 +++++++++++++++++++++++++++-
 include/drm/drm_atomic.h            |   3 +
 include/drm/drm_bridge.h            | 118 +++++++++++++++++++
 5 files changed, 343 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 419381abbdd1..6c249ce39380 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>
@@ -1010,6 +1011,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 f02ddffd4960..de985ba7ce2d 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 c5cf8a9c4237..c53966767782 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,38 @@ 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);
+	else
+		state = drm_atomic_helper_bridge_duplicate_state(bridge);
+
+	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
+		drm_atomic_helper_bridge_destroy_state(bridge, 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 +147,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;
 	LIST_HEAD(tmp_list);
 	int ret;
 
@@ -132,19 +166,39 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 
 	if (bridge->funcs->attach) {
 		ret = bridge->funcs->attach(bridge);
-		if (ret < 0) {
-			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 = drm_atomic_helper_bridge_reset(bridge);
+
+	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);
+
 	if (previous)
 		list_splice(&tmp_list, &previous->chain_node);
 	else
 		list_splice(&tmp_list, &encoder->bridge_chain);
 
 	return 0;
+
+err_detach_bridge:
+	if (bridge->funcs->detach)
+		bridge->funcs->detach(bridge);
+
+err_reset_bridge:
+	bridge->dev = NULL;
+	bridge->encoder = NULL;
+	return ret;
 }
 EXPORT_SYMBOL(drm_bridge_attach);
 
@@ -156,6 +210,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);
 
@@ -522,6 +578,108 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 }
 EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
 
+/**
+ * drm_atomic_helper_bridge_destroy_state() - Default destroy state helper
+ * @bridge: the bridge this state refers to
+ * @state: state object to destroy
+ *
+ * Just a simple kfree() for now.
+ */
+void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge,
+					    struct drm_bridge_state *state)
+{
+	kfree(state);
+}
+EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state);
+
+/**
+ * __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
+ *
+ * For now it's just a memset(0) plus a state->bridge assignment. Might
+ * be extended in the future.
+ */
+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_reset() - default &drm_plane_funcs.reset hook for
+ *				      bridges
+ * @bridge: the bridge to reset state on
+ *
+ * Resets the atomic state for @bridge by freeing the state pointer (which
+ * might be NULL, e.g. at driver load time) and allocating a new empty state
+ * object.
+ *
+ * RETURNS:
+ * A valid drm_bridge_state object in case of success, an ERR_PTR()
+ * giving the reaon of the failure otherwise.
+ */
+struct drm_bridge_state *
+drm_atomic_helper_bridge_reset(struct drm_bridge *bridge)
+{
+	struct drm_bridge_state *bridge_state;
+
+	bridge_state = kzalloc(sizeof(*bridge_state), GFP_KERNEL);
+	if (!bridge_state)
+		return ERR_PTR(-ENOMEM);
+
+	__drm_atomic_helper_bridge_reset(bridge, bridge_state);
+	return bridge_state;
+}
+EXPORT_SYMBOL(drm_atomic_helper_bridge_reset);
+
+/**
+ * __drm_atomic_helper_bridge_duplicate_state() - Copy the content of the
+ *						  current bridge state into a
+ *						  new one
+ * @bridge: bridge element the old and new states are referring to
+ * @new: new bridge state to copy to
+ *
+ * Should be used by custom &drm_bridge_funcs.atomic_duplicate() implementation
+ * to copy the previous state into the new object.
+ */
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
+						struct drm_bridge_state *new)
+{
+	__drm_atomic_helper_private_obj_duplicate_state(&bridge->base,
+							&new->base);
+	new->bridge = bridge;
+}
+EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_state);
+
+/**
+ * drm_atomic_helper_duplicate_bridge_state() - Default duplicate state helper
+ * @bridge: bridge containing the state to duplicate
+ *
+ * Default implementation of &drm_bridge_funcs.atomic_duplicate().
+ *
+ * RETURNS:
+ * a valid state object or NULL if the allocation fails.
+ */
+struct drm_bridge_state *
+drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge)
+{
+	struct drm_bridge_state *new;
+
+	if (WARN_ON(!bridge->base.state))
+		return NULL;
+
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	if (new)
+		__drm_atomic_helper_bridge_duplicate_state(bridge, new);
+
+	return new;
+}
+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 927e1205d7aa..1c0a08217712 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 238e84ab63a3..a608c47d1de5 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_atomic.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_mode_object.h>
 #include <drm/drm_modes.h>
@@ -33,6 +34,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 +356,48 @@ struct drm_bridge_funcs {
 	 */
 	void (*atomic_post_disable)(struct drm_bridge *bridge,
 				    struct drm_atomic_state *state);
+
+	/**
+	 * @atomic_duplicate_state:
+	 *
+	 * Duplicate the current bridge state object (which is guaranteed to be
+	 * non-NULL).
+	 *
+	 * The atomic_duplicate_state() is optional, the core falls back on
+	 * &drm_atomic_helper_bridge_duplicate_state() when not implemented.
+	 *
+	 * 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, the coref falls back on
+	 * &drm_atomic_helper_bridge_destroy_state() when not implemented.
+	 */
+	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, the core falls back on
+	 * &drm_atomic_helper_bridge_reset() when not implemented.
+	 *
+	 * RETURNS:
+	 * A valid drm_bridge_state object in case of success, an ERR_PTR()
+	 * giving the reaon of the failure otherwise.
+	 */
+	struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
 };
 
 /**
@@ -380,6 +440,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 +466,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);
@@ -475,6 +543,56 @@ 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_destroy_state(struct drm_bridge *bridge,
+					    struct drm_bridge_state *state);
+void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
+						struct drm_bridge_state *new);
+struct drm_bridge_state *
+drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge);
+
+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 (!obj_state)
+		return NULL;
+
+	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.21.0


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

* [PATCH v3 10/21] drm/bridge: Clarify the atomic enable/disable hooks semantics
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (8 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 09/21] drm/bridge: Add a drm_bridge_state object Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-10-25 14:33   ` Neil Armstrong
  2019-12-02 16:50   ` Laurent Pinchart
  2019-10-23 15:45 ` [PATCH v3 11/21] drm/bridge: Patch atomic hooks to take a drm_bridge_state Boris Brezillon
                   ` (12 subsequent siblings)
  22 siblings, 2 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, 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>
---
Changes in v3:
* New patch
---
 drivers/gpu/drm/drm_bridge.c | 24 ++++++++++++------------
 include/drm/drm_bridge.h     | 16 ++++++++++++----
 2 files changed, 24 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index c53966767782..ca74bfe028c9 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -447,7 +447,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,
@@ -457,7 +457,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;
@@ -469,7 +469,7 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
 	list_for_each_entry_reverse(iter, &encoder->bridge_chain,
 				    chain_node) {
 		if (iter->funcs->atomic_disable)
-			iter->funcs->atomic_disable(iter, state);
+			iter->funcs->atomic_disable(iter, old_state);
 		else if (iter->funcs->disable)
 			iter->funcs->disable(iter);
 
@@ -483,7 +483,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,
@@ -493,7 +493,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;
 
@@ -504,7 +504,7 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
 	list_for_each_entry_from(bridge, &encoder->bridge_chain,
 				 chain_node) {
 		if (bridge->funcs->atomic_post_disable)
-			bridge->funcs->atomic_post_disable(bridge, state);
+			bridge->funcs->atomic_post_disable(bridge, old_state);
 		else if (bridge->funcs->post_disable)
 			bridge->funcs->post_disable(bridge);
 	}
@@ -515,7 +515,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,
@@ -525,7 +525,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;
@@ -537,7 +537,7 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
 	list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain,
 				    chain_node) {
 		if (iter->funcs->atomic_pre_enable)
-			iter->funcs->atomic_pre_enable(iter, state);
+			iter->funcs->atomic_pre_enable(iter, old_state);
 		else if (iter->funcs->pre_enable)
 			iter->funcs->pre_enable(iter);
 
@@ -550,7 +550,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,
@@ -560,7 +560,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;
 
@@ -571,7 +571,7 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 	list_for_each_entry_from(bridge, &bridge->encoder->bridge_chain,
 				 chain_node) {
 		if (bridge->funcs->atomic_enable)
-			bridge->funcs->atomic_enable(bridge, state);
+			bridge->funcs->atomic_enable(bridge, old_state);
 		else if (bridge->funcs->enable)
 			bridge->funcs->enable(bridge);
 	}
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index a608c47d1de5..e814e6d6e7c2 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -266,6 +266,8 @@ struct drm_bridge_funcs {
 	 * bridge's @atomic_pre_enable or @pre_enable function. If the preceding
 	 * element is a &drm_encoder it's called right before the encoder's
 	 * &drm_encoder_helper_funcs.atomic_enable hook.
+	 * This hook is passed the old atomic state (atomic state after new/old
+	 * states have been swapped).
 	 *
 	 * The display pipe (i.e. clocks and timing signals) feeding this bridge
 	 * will not yet be running when this callback is called. The bridge must
@@ -281,7 +283,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:
@@ -292,6 +294,8 @@ struct drm_bridge_funcs {
 	 * bridge's @atomic_enable or @enable function. If the preceding element
 	 * is a &drm_encoder it's called right after the encoder's
 	 * &drm_encoder_helper_funcs.atomic_enable hook.
+	 * This hook is passed the old atomic state (atomic state after new/old
+	 * states have been swapped).
 	 *
 	 * The bridge can assume that the display pipe (i.e. clocks and timing
 	 * signals) feeding it is running when this callback is called. This
@@ -306,7 +310,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:
 	 *
@@ -316,6 +320,8 @@ struct drm_bridge_funcs {
 	 * bridge's @atomic_disable or @disable vfunc. If the preceding element
 	 * is a &drm_encoder it's called right before the
 	 * &drm_encoder_helper_funcs.atomic_disable hook.
+	 * This hook is passed the old atomic state (atomic state after new/old
+	 * states have been swapped).
 	 *
 	 * The bridge can assume that the display pipe (i.e. clocks and timing
 	 * signals) feeding it is still running when this callback is called.
@@ -329,7 +335,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:
@@ -340,6 +346,8 @@ struct drm_bridge_funcs {
 	 * @atomic_post_disable or @post_disable function. If the preceding
 	 * element is a &drm_encoder it's called right after the encoder's
 	 * &drm_encoder_helper_funcs.atomic_disable hook.
+	 * This hook is passed the old atomic state (atomic state after new/old
+	 * states have been swapped).
 	 *
 	 * The bridge must assume that the display pipe (i.e. clocks and timing
 	 * signals) feeding it is no longer running when this callback is
@@ -355,7 +363,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);
 
 	/**
 	 * @atomic_duplicate_state:
-- 
2.21.0


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

* [PATCH v3 11/21] drm/bridge: Patch atomic hooks to take a drm_bridge_state
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (9 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 10/21] drm/bridge: Clarify the atomic enable/disable hooks semantics Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-12-02 16:57   ` Laurent Pinchart
  2019-10-23 15:45 ` [PATCH v3 12/21] drm/bridge: Add an ->atomic_check() hook Boris Brezillon
                   ` (11 subsequent siblings)
  22 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, 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>
---
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    | 12 ++--
 drivers/gpu/drm/drm_bridge.c                  | 61 +++++++++++++++----
 include/drm/drm_bridge.h                      | 24 ++++----
 3 files changed, 69 insertions(+), 28 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..e438e757f2ce 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -1290,8 +1290,9 @@ struct drm_crtc *analogix_dp_get_new_crtc(struct analogix_dp_device *dp,
 }
 
 static void analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
-						 struct drm_atomic_state *state)
+						 struct drm_bridge_state *bstate)
 {
+	struct drm_atomic_state *state = bstate->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
@@ -1367,8 +1368,9 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
 }
 
 static void analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
-					     struct drm_atomic_state *state)
+					     struct drm_bridge_state *bstate)
 {
+	struct drm_atomic_state *state = bstate->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
@@ -1441,8 +1443,9 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 }
 
 static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
-					      struct drm_atomic_state *state)
+					      struct drm_bridge_state *bstate)
 {
+	struct drm_atomic_state *state = bstate->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state = NULL;
@@ -1465,8 +1468,9 @@ static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
 
 static
 void analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge,
-					    struct drm_atomic_state *state)
+					    struct drm_bridge_state *bstate)
 {
+	struct drm_atomic_state *state = bstate->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state;
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index ca74bfe028c9..377866e3214f 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -468,10 +468,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, old_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;
@@ -503,10 +512,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, old_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);
@@ -536,10 +555,19 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
 	encoder = bridge->encoder;
 	list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain,
 				    chain_node) {
-		if (iter->funcs->atomic_pre_enable)
-			iter->funcs->atomic_pre_enable(iter, old_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;
@@ -570,10 +598,19 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
 	encoder = bridge->encoder;
 	list_for_each_entry_from(bridge, &bridge->encoder->bridge_chain,
 				 chain_node) {
-		if (bridge->funcs->atomic_enable)
-			bridge->funcs->atomic_enable(bridge, old_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 e814e6d6e7c2..b1f557d8dba9 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -266,8 +266,8 @@ struct drm_bridge_funcs {
 	 * bridge's @atomic_pre_enable or @pre_enable function. If the preceding
 	 * element is a &drm_encoder it's called right before the encoder's
 	 * &drm_encoder_helper_funcs.atomic_enable hook.
-	 * This hook is passed the old atomic state (atomic state after new/old
-	 * states have been swapped).
+	 * This hook is passed the old bridge state (the new one can be
+	 * retrieved from bridge->state).
 	 *
 	 * The display pipe (i.e. clocks and timing signals) feeding this bridge
 	 * will not yet be running when this callback is called. The bridge must
@@ -283,7 +283,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:
@@ -294,8 +294,8 @@ struct drm_bridge_funcs {
 	 * bridge's @atomic_enable or @enable function. If the preceding element
 	 * is a &drm_encoder it's called right after the encoder's
 	 * &drm_encoder_helper_funcs.atomic_enable hook.
-	 * This hook is passed the old atomic state (atomic state after new/old
-	 * states have been swapped).
+	 * This hook is passed the old bridge state (the new one can be
+	 * retrieved from bridge->state).
 	 *
 	 * The bridge can assume that the display pipe (i.e. clocks and timing
 	 * signals) feeding it is running when this callback is called. This
@@ -310,7 +310,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:
 	 *
@@ -320,8 +320,8 @@ struct drm_bridge_funcs {
 	 * bridge's @atomic_disable or @disable vfunc. If the preceding element
 	 * is a &drm_encoder it's called right before the
 	 * &drm_encoder_helper_funcs.atomic_disable hook.
-	 * This hook is passed the old atomic state (atomic state after new/old
-	 * states have been swapped).
+	 * This hook is passed the old bridge state (the new one can be
+	 * retrieved from bridge->state).
 	 *
 	 * The bridge can assume that the display pipe (i.e. clocks and timing
 	 * signals) feeding it is still running when this callback is called.
@@ -335,7 +335,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:
@@ -346,8 +346,8 @@ struct drm_bridge_funcs {
 	 * @atomic_post_disable or @post_disable function. If the preceding
 	 * element is a &drm_encoder it's called right after the encoder's
 	 * &drm_encoder_helper_funcs.atomic_disable hook.
-	 * This hook is passed the old atomic state (atomic state after new/old
-	 * states have been swapped).
+	 * This hook is passed the old bridge state (the new one can be
+	 * retrieved from bridge->state).
 	 *
 	 * The bridge must assume that the display pipe (i.e. clocks and timing
 	 * signals) feeding it is no longer running when this callback is
@@ -363,7 +363,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.21.0


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

* [PATCH v3 12/21] drm/bridge: Add an ->atomic_check() hook
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (10 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 11/21] drm/bridge: Patch atomic hooks to take a drm_bridge_state Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-10-25 14:35   ` Neil Armstrong
  2019-12-02 17:03   ` Laurent Pinchart
  2019-10-23 15:45 ` [PATCH v3 13/21] drm/bridge: Add the drm_bridge_chain_get_prev_bridge() helper Boris Brezillon
                   ` (10 subsequent siblings)
  22 siblings, 2 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, 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>
---
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 de985ba7ce2d..1d0a19511a0d 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 377866e3214f..990e056296bd 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -615,6 +615,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_destroy_state() - Default destroy state helper
  * @bridge: the bridge this state refers to
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index b1f557d8dba9..2beb1ef9a733 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -127,7 +127,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:
 	 *
@@ -391,6 +393,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:
 	 *
@@ -542,6 +566,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.21.0


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

* [PATCH v3 13/21] drm/bridge: Add the drm_bridge_chain_get_prev_bridge() helper
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (11 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 12/21] drm/bridge: Add an ->atomic_check() hook Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-10-25 14:34   ` Neil Armstrong
  2019-12-02 17:05   ` Laurent Pinchart
  2019-10-23 15:45 ` [PATCH v3 14/21] drm/bridge: Add the necessary bits to support bus format negotiation Boris Brezillon
                   ` (9 subsequent siblings)
  22 siblings, 2 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, Boris Brezillon

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>
---
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 2beb1ef9a733..3fb518494640 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -526,6 +526,22 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge)
 	return list_next_entry(bridge, chain_node);
 }
 
+/**
+ * drm_bridge_chain_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_chain_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.21.0


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

* [PATCH v3 14/21] drm/bridge: Add the necessary bits to support bus format negotiation
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (12 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 13/21] drm/bridge: Add the drm_bridge_chain_get_prev_bridge() helper Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-12-03 10:03   ` Laurent Pinchart
  2019-10-23 15:45 ` [PATCH v3 15/21] drm/imx: pd: Use bus format/flags provided by the bridge when available Boris Brezillon
                   ` (8 subsequent siblings)
  22 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, Boris Brezillon

drm_bridge_state is extended to describe the input and output bus
configuration. This bus configuration is exposed through the
drm_bus_cfg struct which contains 2 properties: the bus format and
the bus flags.

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 reserve
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 let
the previous bridge element decide what to do (most of the time, bridge
drivers will pick a default bus format of extract this piece of
information from somewhere else, like a FW property).

Bus flags negotiation is left to drivers which can simply propagate the
flags from the input of the next bridge element if there's no conversion
done inside the bridge, or tweak them if the bridge does some kind of
signal inversion.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
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 | 257 ++++++++++++++++++++++++++++++++++-
 include/drm/drm_bridge.h     | 106 +++++++++++++++
 2 files changed, 362 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 990e056296bd..6022fb3d406a 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -641,13 +641,251 @@ static int drm_atomic_bridge_check(struct drm_bridge *bridge,
 	return 0;
 }
 
+static int select_bus_fmt_recursive(struct drm_bridge *first,
+				    struct drm_bridge *cur,
+				    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;
+	u32 *in_bus_fmts;
+	int ret;
+
+	prev = drm_bridge_chain_get_prev_bridge(cur);
+	cur_state = drm_atomic_get_new_bridge_state(crtc_state->state, cur);
+	if (WARN_ON(!cur_state))
+		return -EINVAL;
+
+	/*
+	 * 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->funcs->atomic_get_input_bus_fmts) {
+		if (cur != first) {
+			ret = select_bus_fmt_recursive(first, prev, crtc_state,
+						       conn_state,
+						       MEDIA_BUS_FMT_FIXED);
+			if (ret)
+				return ret;
+		}
+
+		cur_state->input_bus_cfg.fmt = MEDIA_BUS_FMT_FIXED;
+		cur_state->output_bus_cfg.fmt = out_bus_fmt;
+		return 0;
+	}
+
+	in_bus_fmts = cur->funcs->atomic_get_input_bus_fmts(cur, 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 == cur) {
+		cur_state->input_bus_cfg.fmt = in_bus_fmts[0];
+		cur_state->output_bus_cfg.fmt = out_bus_fmt;
+		kfree(in_bus_fmts);
+		return 0;
+	}
+
+	for (i = 0; i < num_in_bus_fmts; i++) {
+		ret = select_bus_fmt_recursive(first, prev, crtc_state,
+					       conn_state, in_bus_fmts[i]);
+		if (ret != -ENOTSUPP)
+			break;
+	}
+
+	if (!ret) {
+		cur_state->input_bus_cfg.fmt = in_bus_fmts[i];
+		cur_state->output_bus_cfg.fmt = 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's providing 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 = kzalloc(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_chain_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_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
+ *
+ * Helper that propagate the output format to the input end of bridge.
+ * Particularly useful for dummy bridge elements like the panel_bridge.
+ *
+ * RETURNS:
+ * a valid format array of size @num_input_fmts, or NULL if the allocation
+ * failed or if @num_input_fmts is set to 0
+ */
+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;
+
+	*num_input_fmts = 1;
+	input_fmts = kzalloc(sizeof(*input_fmts), GFP_KERNEL);
+	if (!input_fmts)
+		return NULL;
+
+	input_fmts[0] = output_fmt;
+	return input_fmts;
+}
+EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt);
+
 /**
  * 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()
@@ -659,12 +897,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 3fb518494640..d37aaf34b882 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -34,15 +34,43 @@ struct drm_bridge;
 struct drm_bridge_timings;
 struct drm_panel;
 
+/**
+ * struct drm_bus_cfg - bus configuration
+ * @fmt: format used on this bus. This field should not be directly modified
+ *	 by drivers (&drm_atomic_bridge_chain_select_bus_fmts() takes care of
+ *	 the bus format negotiation).
+ * @flags: DRM_BUS_ flags used on this bus. Drivers should set the output bus
+ *	   flags to the input bus flags of the next bridge element (unless they
+ *	   have a good reason not to, like a passive signal inversion between
+ *	   those 2 elements). Input bus flags can be different from the output
+ *	   ones if there's some kind of conversion happening inside the bridge,
+ *	   but most of the times it's safe to just propagate the output bus
+ *	   flags to the input end.
+ *	   This bus flags selection/propagation should be done in the driver
+ *	   &drm_bridge_funcs.atomic_check() hook.
+ *
+ * Encodes the bus format and bus flags used by one end of the bridge or
+ * by the encoder output.
+ */
+struct drm_bus_cfg {
+	u32 fmt;
+	u32 flags;
+};
+
 /**
  * struct drm_bridge_state - Atomic bridge state object
  * @base: inherit from &drm_private_state
  * @bridge: the bridge this state refers to
+ * @input_bus_info: input bus information
+ * @output_bus_info: output bus information
  */
 struct drm_bridge_state {
 	struct drm_private_state base;
 
 	struct drm_bridge *bridge;
+
+	struct drm_bus_cfg input_bus_cfg;
+	struct drm_bus_cfg output_bus_cfg;
 };
 
 static inline struct drm_bridge_state *
@@ -393,6 +421,69 @@ 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
+	 * fallback to &drm_connector.display_info.bus_formats[0] if
+	 * &drm_connector.display_info.num_bus_formats > 0,
+	 * 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).
+	 *
+	 * This method is called on all 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 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.
+	 */
+	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:
 	 *
@@ -407,6 +498,13 @@ 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.input_bus_cfg.flags it should should happen in
+	 * this function. By default those fields are 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.
 	 */
@@ -594,6 +692,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.21.0


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

* [PATCH v3 15/21] drm/imx: pd: Use bus format/flags provided by the bridge when available
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (13 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 14/21] drm/bridge: Add the necessary bits to support bus format negotiation Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-12-03 13:50   ` Philipp Zabel
  2019-10-23 15:45 ` [PATCH v3 16/21] drm/bridge: lvds-encoder: Implement basic bus format negotiation Boris Brezillon
                   ` (7 subsequent siblings)
  22 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, Boris Brezillon

Now that bridges can expose the bus format/flags they expect, we can
use those instead of the relying on the display_info provided by the
connector (which is only valid if the encoder is directly connected
to bridge element driving the panel/display).

We also explicitly expose the bus formats supported by our encoder by
filling encoder->output_bus_caps with proper info.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
Hi Philipp,

I think I addressed all your comments except the addition of
SYNC_DRIVE_POSEDGE/NEGEDGE flags, which would require changing
ipu_crtc_mode_set_nofb() to take those flags into account or
turning those flags into their PIXDATA counterpart. If you don't mind,
I'd like to leave that for later.

Regards,

Boris

Changes in v3 (all suggested by Philipp):
* Adjust to match core changes
* Propagate output format to input format
* Pick a default value when output_fmt = _FIXED
* Add missing BGR888 and GBR888 fmts to imx_pd_bus_fmts[]

Changes in v2:
* Adjust things to match the new bus-format negotiation infra
---
 drivers/gpu/drm/imx/parallel-display.c | 174 +++++++++++++++++++++----
 1 file changed, 150 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c
index 35518e5de356..28ae083df3ca 100644
--- a/drivers/gpu/drm/imx/parallel-display.c
+++ b/drivers/gpu/drm/imx/parallel-display.c
@@ -24,6 +24,7 @@
 struct imx_parallel_display {
 	struct drm_connector connector;
 	struct drm_encoder encoder;
+	struct drm_bridge bridge;
 	struct device *dev;
 	void *edid;
 	int edid_len;
@@ -31,7 +32,7 @@ struct imx_parallel_display {
 	u32 bus_flags;
 	struct drm_display_mode mode;
 	struct drm_panel *panel;
-	struct drm_bridge *bridge;
+	struct drm_bridge *next_bridge;
 };
 
 static inline struct imx_parallel_display *con_to_imxpd(struct drm_connector *c)
@@ -44,6 +45,11 @@ static inline struct imx_parallel_display *enc_to_imxpd(struct drm_encoder *e)
 	return container_of(e, struct imx_parallel_display, encoder);
 }
 
+static inline struct imx_parallel_display *bridge_to_imxpd(struct drm_bridge *b)
+{
+	return container_of(b, struct imx_parallel_display, bridge);
+}
+
 static int imx_pd_connector_get_modes(struct drm_connector *connector)
 {
 	struct imx_parallel_display *imxpd = con_to_imxpd(connector);
@@ -89,37 +95,151 @@ static struct drm_encoder *imx_pd_connector_best_encoder(
 	return &imxpd->encoder;
 }
 
-static void imx_pd_encoder_enable(struct drm_encoder *encoder)
+static void imx_pd_bridge_enable(struct drm_bridge *bridge)
 {
-	struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
+	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
 
 	drm_panel_prepare(imxpd->panel);
 	drm_panel_enable(imxpd->panel);
 }
 
-static void imx_pd_encoder_disable(struct drm_encoder *encoder)
+static void imx_pd_bridge_disable(struct drm_bridge *bridge)
 {
-	struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
+	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
 
 	drm_panel_disable(imxpd->panel);
 	drm_panel_unprepare(imxpd->panel);
 }
 
-static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder,
-				       struct drm_crtc_state *crtc_state,
-				       struct drm_connector_state *conn_state)
+static const u32 imx_pd_bus_fmts[] = {
+	MEDIA_BUS_FMT_RGB888_1X24,
+	MEDIA_BUS_FMT_BGR888_1X24,
+	MEDIA_BUS_FMT_GBR888_1X24,
+	MEDIA_BUS_FMT_RGB666_1X18,
+	MEDIA_BUS_FMT_RGB666_1X24_CPADHI,
+	MEDIA_BUS_FMT_RGB565_1X16,
+};
+
+static u32 *
+imx_pd_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+					 struct drm_bridge_state *bridge_state,
+					 struct drm_crtc_state *crtc_state,
+					 struct drm_connector_state *conn_state,
+					 unsigned int *num_output_fmts)
+{
+	struct drm_display_info *di = &conn_state->connector->display_info;
+	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
+	u32 *output_fmts;
+
+	if (!imxpd->bus_format && !di->num_bus_formats)
+		*num_output_fmts = ARRAY_SIZE(imx_pd_bus_fmts);
+	else
+		*num_output_fmts = 1;
+
+	output_fmts = kcalloc(*num_output_fmts, sizeof(*output_fmts),
+			      GFP_KERNEL);
+	if (!output_fmts)
+		return NULL;
+
+	if (!imxpd->bus_format && di->num_bus_formats)
+		output_fmts[0] = di->bus_formats[0];
+	else if (!imxpd->bus_format)
+		memcpy(output_fmts, imx_pd_bus_fmts,
+		       ARRAY_SIZE(imx_pd_bus_fmts));
+	else
+		output_fmts[0] = imxpd->bus_format;
+
+	return output_fmts;
+}
+
+static u32 *
+imx_pd_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+					struct drm_bridge_state *bridge_state,
+					struct drm_crtc_state *crtc_state,
+					struct drm_connector_state *conn_state,
+					u32 output_fmt,
+					unsigned int *num_input_fmts)
+{
+	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
+	u32 *input_fmts;
+
+	*num_input_fmts = 0;
+	if (output_fmt == MEDIA_BUS_FMT_FIXED) {
+		/*
+		 * The next bridge does not support bus format negotiation,
+		 * let's use the default RGB888 value.
+		 */
+		*num_input_fmts = 1;
+		output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+	} else if (!imxpd->bus_format) {
+		unsigned int i;
+
+		for (i = 0; i < ARRAY_SIZE(imx_pd_bus_fmts); i++) {
+			if (imx_pd_bus_fmts[i] == output_fmt) {
+				*num_input_fmts = 1;
+				break;
+			}
+		}
+	} else if (imxpd->bus_format == output_fmt) {
+		*num_input_fmts = 1;
+	}
+
+	if (!*num_input_fmts)
+		return NULL;
+
+	input_fmts = kcalloc(*num_input_fmts, sizeof(*input_fmts),
+			     GFP_KERNEL);
+	if (!input_fmts)
+		return NULL;
+
+	input_fmts[0] = output_fmt;
+	return input_fmts;
+}
+
+static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge,
+				      struct drm_bridge_state *bridge_state,
+				      struct drm_crtc_state *crtc_state,
+				      struct drm_connector_state *conn_state)
 {
 	struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
 	struct drm_display_info *di = &conn_state->connector->display_info;
-	struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
+	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
+	struct drm_bridge_state *next_bridge_state = NULL;
+	struct drm_bridge *next_bridge;
+	u32 bus_flags, bus_fmt;
+	unsigned int i;
 
-	if (!imxpd->bus_format && di->num_bus_formats) {
-		imx_crtc_state->bus_flags = di->bus_flags;
-		imx_crtc_state->bus_format = di->bus_formats[0];
-	} else {
-		imx_crtc_state->bus_flags = imxpd->bus_flags;
-		imx_crtc_state->bus_format = imxpd->bus_format;
+	next_bridge = drm_bridge_chain_get_next_bridge(bridge);
+	if (next_bridge)
+		next_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
+								    next_bridge);
+
+	if (next_bridge_state)
+		bus_flags = next_bridge_state->input_bus_cfg.flags;
+	else if (!imxpd->bus_format && di->num_bus_formats)
+		bus_flags = di->bus_flags;
+	else
+		bus_flags = imxpd->bus_flags;
+
+	bus_fmt = bridge_state->input_bus_cfg.fmt;
+	for (i = 0; i < ARRAY_SIZE(imx_pd_bus_fmts); i++) {
+		if (imx_pd_bus_fmts[i] == bus_fmt)
+			break;
 	}
+
+	if (i == ARRAY_SIZE(imx_pd_bus_fmts))
+		return -EINVAL;
+
+	if (bus_flags &
+	    ~(DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_DE_HIGH |
+	      DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE |
+	      DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE))
+		return -EINVAL;
+
+	bridge_state->output_bus_cfg.flags = bus_flags;
+	bridge_state->input_bus_cfg.flags = bus_flags;
+	imx_crtc_state->bus_flags = bus_flags;
+	imx_crtc_state->bus_format = bridge_state->input_bus_cfg.fmt;
 	imx_crtc_state->di_hsync_pin = 2;
 	imx_crtc_state->di_vsync_pin = 3;
 
@@ -143,10 +263,12 @@ static const struct drm_encoder_funcs imx_pd_encoder_funcs = {
 	.destroy = imx_drm_encoder_destroy,
 };
 
-static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = {
-	.enable = imx_pd_encoder_enable,
-	.disable = imx_pd_encoder_disable,
-	.atomic_check = imx_pd_encoder_atomic_check,
+static const struct drm_bridge_funcs imx_pd_bridge_funcs = {
+	.enable = imx_pd_bridge_enable,
+	.disable = imx_pd_bridge_disable,
+	.atomic_check = imx_pd_bridge_atomic_check,
+	.atomic_get_input_bus_fmts = imx_pd_bridge_atomic_get_input_bus_fmts,
+	.atomic_get_output_bus_fmts = imx_pd_bridge_atomic_get_output_bus_fmts,
 };
 
 static int imx_pd_register(struct drm_device *drm,
@@ -166,11 +288,13 @@ static int imx_pd_register(struct drm_device *drm,
 	 */
 	imxpd->connector.dpms = DRM_MODE_DPMS_OFF;
 
-	drm_encoder_helper_add(encoder, &imx_pd_encoder_helper_funcs);
 	drm_encoder_init(drm, encoder, &imx_pd_encoder_funcs,
 			 DRM_MODE_ENCODER_NONE, NULL);
 
-	if (!imxpd->bridge) {
+	imxpd->bridge.funcs = &imx_pd_bridge_funcs;
+	drm_bridge_attach(encoder, &imxpd->bridge, NULL);
+
+	if (!imxpd->next_bridge) {
 		drm_connector_helper_add(&imxpd->connector,
 				&imx_pd_connector_helper_funcs);
 		drm_connector_init(drm, &imxpd->connector,
@@ -181,8 +305,9 @@ static int imx_pd_register(struct drm_device *drm,
 	if (imxpd->panel)
 		drm_panel_attach(imxpd->panel, &imxpd->connector);
 
-	if (imxpd->bridge) {
-		ret = drm_bridge_attach(encoder, imxpd->bridge, NULL);
+	if (imxpd->next_bridge) {
+		ret = drm_bridge_attach(encoder, imxpd->next_bridge,
+					&imxpd->bridge);
 		if (ret < 0) {
 			dev_err(imxpd->dev, "failed to attach bridge: %d\n",
 				ret);
@@ -227,7 +352,8 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
 	imxpd->bus_format = bus_format;
 
 	/* port@1 is the output port */
-	ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel, &imxpd->bridge);
+	ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel,
+					  &imxpd->next_bridge);
 	if (ret && ret != -ENODEV)
 		return ret;
 
-- 
2.21.0


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

* [PATCH v3 16/21] drm/bridge: lvds-encoder: Implement basic bus format negotiation
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (14 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 15/21] drm/imx: pd: Use bus format/flags provided by the bridge when available Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-12-03 10:14   ` Laurent Pinchart
  2019-10-23 15:45 ` [PATCH v3 17/21] dt-bindings: display: bridge: lvds-transmitter: Add new props Boris Brezillon
                   ` (6 subsequent siblings)
  22 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, Boris Brezillon

Some LVDS encoder might support several input/output bus formats. Add
a way to describe the one used on a specific design by adding optional
'data-mapping' properties to the input/output ports.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
Changes in v3:
* Use bus-width for the rgb24/rgb18 distinction
* Adjust code to match core changes
* Get rid of of_get_data_mapping()
* Don't implement ->atomic_check() (the core now takes care of bus
  flags propagation)

Changes in v2:
* Make the bus-format negotiation logic more generic
---
 drivers/gpu/drm/bridge/lvds-encoder.c | 72 +++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)

diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c
index e2132a8d5106..a2a8f7f4ac97 100644
--- a/drivers/gpu/drm/bridge/lvds-encoder.c
+++ b/drivers/gpu/drm/bridge/lvds-encoder.c
@@ -6,6 +6,7 @@
 #include <linux/gpio/consumer.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/of_graph.h>
 #include <linux/platform_device.h>
 
@@ -16,6 +17,8 @@ struct lvds_encoder {
 	struct drm_bridge bridge;
 	struct drm_bridge *panel_bridge;
 	struct gpio_desc *powerdown_gpio;
+	u32 output_fmt;
+	u32 input_fmt;
 };
 
 static int lvds_encoder_attach(struct drm_bridge *bridge)
@@ -48,10 +51,40 @@ static void lvds_encoder_disable(struct drm_bridge *bridge)
 		gpiod_set_value_cansleep(lvds_encoder->powerdown_gpio, 1);
 }
 
+static u32 *lvds_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)
+{
+	struct lvds_encoder *lvds_encoder = container_of(bridge,
+							 struct lvds_encoder,
+							 bridge);
+	u32 *input_fmts;
+
+	if (output_fmt == MEDIA_BUS_FMT_FIXED ||
+	    output_fmt == lvds_encoder->output_fmt)
+		*num_input_fmts = 1;
+	else
+		*num_input_fmts = 0;
+
+	if (!*num_input_fmts)
+		return NULL;
+
+	input_fmts = kcalloc(*num_input_fmts, sizeof(*input_fmts), GFP_KERNEL);
+	if (!input_fmts)
+		return NULL;
+
+	input_fmts[0] = lvds_encoder->input_fmt;
+	return input_fmts;
+}
+
 static struct drm_bridge_funcs funcs = {
 	.attach = lvds_encoder_attach,
 	.enable = lvds_encoder_enable,
 	.disable = lvds_encoder_disable,
+	.atomic_get_input_bus_fmts = lvds_atomic_get_input_bus_fmts,
 };
 
 static int lvds_encoder_probe(struct platform_device *pdev)
@@ -62,11 +95,16 @@ static int lvds_encoder_probe(struct platform_device *pdev)
 	struct device_node *panel_node;
 	struct drm_panel *panel;
 	struct lvds_encoder *lvds_encoder;
+	const char *output_data_mapping = NULL;
+	u32 input_bus_width = 0;
 
 	lvds_encoder = devm_kzalloc(dev, sizeof(*lvds_encoder), GFP_KERNEL);
 	if (!lvds_encoder)
 		return -ENOMEM;
 
+	lvds_encoder->input_fmt = MEDIA_BUS_FMT_FIXED;
+	lvds_encoder->output_fmt = MEDIA_BUS_FMT_FIXED;
+
 	lvds_encoder->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown",
 							       GPIOD_OUT_HIGH);
 	if (IS_ERR(lvds_encoder->powerdown_gpio)) {
@@ -77,6 +115,25 @@ static int lvds_encoder_probe(struct platform_device *pdev)
 		return err;
 	}
 
+	port = of_graph_get_port_by_id(dev->of_node, 0);
+	if (!port) {
+		dev_dbg(dev, "port 0 not found\n");
+		return -ENXIO;
+	}
+
+	of_node_put(port);
+
+	if (of_property_read_u32(port, "bus-width", &input_bus_width)) {
+		lvds_encoder->input_fmt = MEDIA_BUS_FMT_FIXED;
+	} else if (input_bus_width == 18) {
+		lvds_encoder->input_fmt = MEDIA_BUS_FMT_RGB666_1X18;
+	} else if (input_bus_width == 24) {
+		lvds_encoder->input_fmt = MEDIA_BUS_FMT_RGB888_1X24;
+	} else {
+		dev_dbg(dev, "unsupported bus-width)\n");
+		return -ENOTSUPP;
+	}
+
 	/* Locate the panel DT node. */
 	port = of_graph_get_port_by_id(dev->of_node, 1);
 	if (!port) {
@@ -84,6 +141,21 @@ static int lvds_encoder_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
+	of_property_read_string(port, "data-mapping", &output_data_mapping);
+	if (!output_data_mapping) {
+		lvds_encoder->output_fmt = MEDIA_BUS_FMT_FIXED;
+	} else if (!strcmp(output_data_mapping, "jeida-18")) {
+		lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG;
+	} else if (!strcmp(output_data_mapping, "jeida-24")) {
+		lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
+	} else if (!strcmp(output_data_mapping, "vesa-24")) {
+		lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
+	} else {
+		of_node_put(port);
+		dev_dbg(dev, "unsupported output data-mapping\n");
+		return -ENOTSUPP;
+	}
+
 	endpoint = of_get_child_by_name(port, "endpoint");
 	of_node_put(port);
 	if (!endpoint) {
-- 
2.21.0


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

* [PATCH v3 17/21] dt-bindings: display: bridge: lvds-transmitter: Add new props
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (15 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 16/21] drm/bridge: lvds-encoder: Implement basic bus format negotiation Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-10-25 19:57   ` Rob Herring
  2019-12-02 17:11   ` Laurent Pinchart
  2019-10-23 15:45 ` [PATCH v3 18/21] drm/bridge: panel: Propage bus format/flags Boris Brezillon
                   ` (5 subsequent siblings)
  22 siblings, 2 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, Boris Brezillon

Add the data-mapping property to describe the output bus format and
the bus-width property to describe the input bus format.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
Changes in v3:
* New patch
---
 .../bindings/display/bridge/lvds-transmitter.txt    | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
index 60091db5dfa5..7b43b6f20279 100644
--- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
+++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
@@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
 - Video port 0 for parallel input
 - Video port 1 for LVDS output
 
+Optional port 0 node properties:
+
+- bus-width: number of data lines use to transmit the RGB data.
+	     Can be 18 or 24.
+
+Optional port 1 node properties:
+
+- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml
+		for more details about this LVDS data-mapping property.
+		Supported values:
+		"jeida-18"
+		"jeida-24"
+		"vesa-24"
 
 Example
 -------
-- 
2.21.0


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

* [PATCH v3 18/21] drm/bridge: panel: Propage bus format/flags
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (16 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 17/21] dt-bindings: display: bridge: lvds-transmitter: Add new props Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-12-03 10:17   ` Laurent Pinchart
  2019-10-23 15:45 ` [PATCH v3 19/21] drm/panel: simple: Add support for Toshiba LTA089AC29000 panel Boris Brezillon
                   ` (4 subsequent siblings)
  22 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, Boris Brezillon

So that the previous bridge element in the chain knows which input
format the panel bridge expects.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
Changes in v3:
* Adjust things to match the new bus-format negotiation approach
* Use drm_atomic_helper_bridge_propagate_bus_fmt
* Don't implement ->atomic_check() (the core now takes care of bus
  flags propagation)

Changes in v2:
* Adjust things to match the new bus-format negotiation approach
---
 drivers/gpu/drm/bridge/panel.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index f4e293e7cf64..a70c363a2bd0 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -127,6 +127,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
 	.enable = panel_bridge_enable,
 	.disable = panel_bridge_disable,
 	.post_disable = panel_bridge_post_disable,
+	.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
 };
 
 /**
-- 
2.21.0


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

* [PATCH v3 19/21] drm/panel: simple: Add support for Toshiba LTA089AC29000 panel
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (17 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 18/21] drm/bridge: panel: Propage bus format/flags Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-12-02 17:17   ` Laurent Pinchart
  2019-10-23 15:45 ` [PATCH v3 20/21] dt-bindings: display: panel: Add the LTA089AC29000 variant Boris Brezillon
                   ` (3 subsequent siblings)
  22 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, Boris Brezillon

Add a new entry for the Toshiba LTA089AC29000 panel.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
Changes in v3:
* None
---
 drivers/gpu/drm/panel/panel-simple.c | 36 ++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 5d487686d25c..27c92b44bd95 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -2937,6 +2937,39 @@ static const struct panel_desc toshiba_lt089ac29000 = {
 	.connector_type = DRM_MODE_CONNECTOR_LVDS,
 };
 
+static const struct drm_display_mode toshiba_lta089ac29000_mode = {
+	.clock = 79500,
+	.hdisplay = 1280,
+	.hsync_start = 1280 + 192,
+	.hsync_end = 1280 + 192 + 128,
+	.htotal = 1280 + 192 + 128 + 64,
+	.vdisplay = 768,
+	.vsync_start = 768 + 20,
+	.vsync_end = 768 + 20 + 7,
+	.vtotal = 768 + 20 + 7 + 3,
+	.vrefresh = 60,
+};
+
+static const struct panel_desc toshiba_lta089ac29000 = {
+	.modes = &toshiba_lta089ac29000_mode,
+	.num_modes = 1,
+	.size = {
+		.width = 194,
+		.height = 116,
+	},
+	/*
+	 * FIXME:
+	 * The panel supports 2 bus formats: jeida-24 and jeida-18. The
+	 * mode is selected through the 8b6b_SEL pin. If anyone ever needs
+	 * support for jeida-18 we should probably parse the 'data-mapping'
+	 * property.
+	 * In the unlikely event where 8b6b_SEL is connected to a GPIO, we'd
+	 * need a new infra to allow bus format negotiation at the panel level.
+	 */
+	.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
+};
+
 static const struct drm_display_mode tpk_f07a_0102_mode = {
 	.clock = 33260,
 	.hdisplay = 800,
@@ -3392,6 +3425,9 @@ static const struct of_device_id platform_of_match[] = {
 	}, {
 		.compatible = "toshiba,lt089ac29000",
 		.data = &toshiba_lt089ac29000,
+	}, {
+		.compatible = "toshiba,lta089ac29000",
+		.data = &toshiba_lta089ac29000,
 	}, {
 		.compatible = "tpk,f07a-0102",
 		.data = &tpk_f07a_0102,
-- 
2.21.0


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

* [PATCH v3 20/21] dt-bindings: display: panel: Add the LTA089AC29000 variant
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (18 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 19/21] drm/panel: simple: Add support for Toshiba LTA089AC29000 panel Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-10-25 19:58   ` Rob Herring
  2019-12-02 17:19   ` Laurent Pinchart
  2019-10-23 15:45 ` [PATCH v3 21/21] ARM: dts: imx: imx51-zii-rdu1: Fix the display pipeline definition Boris Brezillon
                   ` (2 subsequent siblings)
  22 siblings, 2 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, Boris Brezillon

The LTA089AC29000 and LT089AC29000 are not exactly the same. Let's add
a new compatible for the LTA variant.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
 .../bindings/display/panel/toshiba,lt089ac29000.txt          | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt b/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt
index 89826116628c..26ebfa098966 100644
--- a/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt
+++ b/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt
@@ -1,7 +1,10 @@
 Toshiba 8.9" WXGA (1280x768) TFT LCD panel
 
 Required properties:
-- compatible: should be "toshiba,lt089ac29000"
+- compatible: should be one of the following
+	      "toshiba,lt089ac29000"
+	      "toshiba,lta089ac29000"
+
 - power-supply: as specified in the base binding
 
 This binding is compatible with the simple-panel binding, which is specified
-- 
2.21.0


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

* [PATCH v3 21/21] ARM: dts: imx: imx51-zii-rdu1: Fix the display pipeline definition
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (19 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 20/21] dt-bindings: display: panel: Add the LTA089AC29000 variant Boris Brezillon
@ 2019-10-23 15:45 ` Boris Brezillon
  2019-10-24 11:27 ` [PATCH v3 00/21] drm: Add support for bus-format negotiation Neil Armstrong
  2019-11-24  0:46 ` Ezequiel Garcia
  22 siblings, 0 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-23 15:45 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, Boris Brezillon

The current definition does not represent the exact display pipeline we
have on the board: the LVDS panel is actually connected through a
parallel -> LVDS bridge. Let's fix that so the driver can select the
proper bus format on the CRTC end.

Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
---
Changes in v3:
* None

Changes in v2:
* None
---
 arch/arm/boot/dts/imx51-zii-rdu1.dts | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/imx51-zii-rdu1.dts b/arch/arm/boot/dts/imx51-zii-rdu1.dts
index 3596060f52e7..3fb84ea7f993 100644
--- a/arch/arm/boot/dts/imx51-zii-rdu1.dts
+++ b/arch/arm/boot/dts/imx51-zii-rdu1.dts
@@ -95,6 +95,28 @@
 			reg = <1>;
 
 			display_out: endpoint {
+				remote-endpoint = <&lvds_encoder_in>;
+			};
+		};
+	};
+
+	lvds-encoder {
+		compatible = "lvds-encoder";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+			bus-width = <24>;
+			lvds_encoder_in: endpoint {
+				remote-endpoint = <&display_out>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+			data-mapping = "jeida-24";
+			lvds_encoder_out: endpoint {
 				remote-endpoint = <&panel_in>;
 			};
 		};
@@ -110,7 +132,7 @@
 
 		port {
 			panel_in: endpoint {
-				remote-endpoint = <&display_out>;
+				remote-endpoint = <&lvds_encoder_out>;
 			};
 		};
 	};
-- 
2.21.0


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

* Re: [PATCH v3 00/21] drm: Add support for bus-format negotiation
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (20 preceding siblings ...)
  2019-10-23 15:45 ` [PATCH v3 21/21] ARM: dts: imx: imx51-zii-rdu1: Fix the display pipeline definition Boris Brezillon
@ 2019-10-24 11:27 ` Neil Armstrong
  2019-10-24 13:22   ` Boris Brezillon
  2019-11-24  0:46 ` Ezequiel Garcia
  22 siblings, 1 reply; 76+ messages in thread
From: Neil Armstrong @ 2019-10-24 11:27 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

hi Boris,

On 23/10/2019 17:44, 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.
> 
> Major changes since v2:
> * Get rid of the dummy bridge embedded in drm_encoder and let encoder
>   drivers provide their own bridge element
> * Clarify APIs and improve the doc
> * Propagate bus flags by default

Seems you forgot my reviewed-bys on patches 5, 8, 11 & 13

> 
> Major changes since the RFC:
> 
> * Add a dummy bridge to the drm_encoder object so that vc4 and exynos
>   DSI drivers can implement the pre_enable/post_disable hooks instead
>   of manually setting encoder->bridge to NULL to control the
>   enable/disable sequence. This change is also a first step towards
>   drm_bridge/drm_encoder unification. New encoder drivers should
>   stop implementing drm_encoder_helper_funcs and switch to
>   drm_bridge_funcs. Existing drivers can be converted progressively
>   (already have a branch where I started converting some of them [1])
> * rework the bus format negotiation to give more control to bridge
>   drivers in the selection process (driver can select at runtime which
>   input bus format they support for a specific output bus format based
>   on any information available in the connector, crtc and bridge state.
> 
> A more detailed changelog is provided in each patch.
> 
> This patch series is also available here [2].

Will test ASAP.

> 
> Thanks,
> 
> Boris
> 
> [1]https://github.com/bbrezillon/linux-0day/commits/drm-encoder-bridge
> [2]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v3
> 
> *** BLURB HERE ***

Blurp

Neil

> 
> Boris Brezillon (21):
>   drm/vc4: Declare the DSI encoder as a bridge element
>   drm/exynos: Don't reset bridge->next
>   drm/exynos: Declare the DSI encoder as a bridge element
>   drm/bridge: Rename bridge helpers targeting a bridge chain
>   drm/bridge: Introduce drm_bridge_chain_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 a drm_bridge_state object
>   drm/bridge: Clarify the atomic enable/disable hooks semantics
>   drm/bridge: Patch atomic hooks to take a drm_bridge_state
>   drm/bridge: Add an ->atomic_check() hook
>   drm/bridge: Add the drm_bridge_chain_get_prev_bridge() helper
>   drm/bridge: Add the necessary bits to support bus format negotiation
>   drm/imx: pd: Use bus format/flags provided by the bridge when
>     available
>   drm/bridge: lvds-encoder: Implement basic bus format negotiation
>   dt-bindings: display: bridge: lvds-transmitter: Add new props
>   drm/bridge: panel: Propage bus format/flags
>   drm/panel: simple: Add support for Toshiba LTA089AC29000 panel
>   dt-bindings: display: panel: Add the LTA089AC29000 variant
>   ARM: dts: imx: imx51-zii-rdu1: Fix the display pipeline definition
> 
>  .../display/bridge/lvds-transmitter.txt       |  13 +
>  .../display/panel/toshiba,lt089ac29000.txt    |   5 +-
>  arch/arm/boot/dts/imx51-zii-rdu1.dts          |  24 +-
>  .../drm/bridge/analogix/analogix_dp_core.c    |  12 +-
>  drivers/gpu/drm/bridge/lvds-encoder.c         |  72 ++
>  drivers/gpu/drm/bridge/panel.c                |   1 +
>  drivers/gpu/drm/drm_atomic.c                  |  39 +
>  drivers/gpu/drm/drm_atomic_helper.c           |  54 +-
>  drivers/gpu/drm/drm_bridge.c                  | 800 +++++++++++++++---
>  drivers/gpu/drm/drm_encoder.c                 |  15 +-
>  drivers/gpu/drm/drm_probe_helper.c            |   4 +-
>  drivers/gpu/drm/exynos/exynos_dp.c            |   1 -
>  drivers/gpu/drm/exynos/exynos_drm_dsi.c       |  90 +-
>  drivers/gpu/drm/imx/parallel-display.c        | 174 +++-
>  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/panel/panel-simple.c          |  36 +
>  drivers/gpu/drm/rcar-du/rcar_du_crtc.c        |  11 +-
>  drivers/gpu/drm/vc4/vc4_dsi.c                 |  90 +-
>  include/drm/drm_atomic.h                      |   3 +
>  include/drm/drm_bridge.h                      | 396 ++++++++-
>  include/drm/drm_encoder.h                     |   9 +-
>  24 files changed, 1588 insertions(+), 286 deletions(-)
> 


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

* Re: [PATCH v3 00/21] drm: Add support for bus-format negotiation
  2019-10-24 11:27 ` [PATCH v3 00/21] drm: Add support for bus-format negotiation Neil Armstrong
@ 2019-10-24 13:22   ` Boris Brezillon
  0 siblings, 0 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-24 13:22 UTC (permalink / raw)
  To: Neil Armstrong
  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,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree

On Thu, 24 Oct 2019 13:27:16 +0200
Neil Armstrong <narmstrong@baylibre.com> wrote:

> hi Boris,
> 
> On 23/10/2019 17:44, 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.
> > 
> > Major changes since v2:
> > * Get rid of the dummy bridge embedded in drm_encoder and let encoder
> >   drivers provide their own bridge element
> > * Clarify APIs and improve the doc
> > * Propagate bus flags by default  
> 
> Seems you forgot my reviewed-bys on patches 5, 8, 11 & 13

Oops, indeed. Can you add them back?

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

* Re: [PATCH v3 04/21] drm/bridge: Rename bridge helpers targeting a bridge chain
  2019-10-23 15:44 ` [PATCH v3 04/21] drm/bridge: Rename bridge helpers targeting a bridge chain Boris Brezillon
@ 2019-10-25 13:26   ` Neil Armstrong
  2019-11-24 10:28   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Neil Armstrong @ 2019-10-25 13:26 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

On 23/10/2019 17:44, Boris Brezillon wrote:
> 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>
> ---
> 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/mediatek/mtk_hdmi.c |   4 +-
>  include/drm/drm_bridge.h            |  64 +++++++-------
>  5 files changed, 112 insertions(+), 102 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/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> index c79b1f855d89..ea68b5adccbe 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/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);
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>

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

* Re: [PATCH v3 05/21] drm/bridge: Introduce drm_bridge_chain_get_next_bridge()
  2019-10-23 15:44 ` [PATCH v3 05/21] drm/bridge: Introduce drm_bridge_chain_get_next_bridge() Boris Brezillon
@ 2019-10-25 13:27   ` Neil Armstrong
  2019-11-24 10:33   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Neil Armstrong @ 2019-10-25 13:27 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

On 23/10/2019 17:44, Boris Brezillon wrote:
> 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>
> ---
> 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/exynos/exynos_drm_dsi.c |  3 ++-
>  drivers/gpu/drm/mediatek/mtk_hdmi.c     |  6 ++++--
>  drivers/gpu/drm/omapdrm/omap_drv.c      |  4 ++--
>  drivers/gpu/drm/omapdrm/omap_encoder.c  |  3 ++-
>  drivers/gpu/drm/vc4/vc4_dsi.c           |  4 +++-
>  include/drm/drm_bridge.h                | 13 +++++++++++++
>  6 files changed, 26 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> index 3915f50b005e..005c67894b78 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> @@ -1593,9 +1593,10 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
>  				  struct mipi_dsi_device *device)
>  {
>  	struct exynos_dsi *dsi = host_to_dsi(host);
> -	struct drm_bridge *out_bridge = dsi->bridge.next;
>  	struct drm_device *drm = dsi->encoder.dev;
> +	struct drm_bridge *out_bridge;
>  
> +	out_bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
>  	if (dsi->panel) {
>  		mutex_lock(&drm->mode_config.mutex);
>  		exynos_dsi_disable(&dsi->bridge);
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> index ea68b5adccbe..cfaa5aab8876 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_chain_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..865164fe28dc 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_chain_get_next_bridge(bridge))
> +			bridge = drm_bridge_chain_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..8ca54081997e 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_chain_get_next_bridge(bridge)) {
>  		if (!bridge->timings)
>  			continue;
>  
> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index 49f8a313e759..49c47185aff0 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> @@ -1644,8 +1644,10 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>  	struct drm_device *drm = dev_get_drvdata(master);
>  	struct vc4_dev *vc4 = to_vc4_dev(drm);
>  	struct vc4_dsi *dsi = dev_get_drvdata(dev);
> +	struct drm_bridge *bridge;
>  
> -	if (dsi->bridge.next)
> +	bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
> +	if (bridge)
>  		pm_runtime_disable(dev);
>  
>  	vc4_dsi_encoder_destroy(dsi->encoder);
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index 726435baf4ad..8aeba83fcf31 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_chain_get_next_bridge() - Get the next bridge in the chain
> + * @bridge: bridge object
> + *
> + * RETURNS:
> + * the next bridge in the chain, or NULL if @bridge is the last.
> + */
> +static inline struct drm_bridge *
> +drm_bridge_chain_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);
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>

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

* Re: [PATCH v3 06/21] drm: Stop accessing encoder->bridge directly
  2019-10-23 15:44 ` [PATCH v3 06/21] drm: Stop accessing encoder->bridge directly Boris Brezillon
@ 2019-10-25 13:28   ` Neil Armstrong
  2019-11-24 10:39   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Neil Armstrong @ 2019-10-25 13:28 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

On 23/10/2019 17:44, Boris Brezillon wrote:
> 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.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c    | 25 +++++++++++++++++--------
>  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 | 11 ++++++++---
>  include/drm/drm_bridge.h               | 15 +++++++++++++++
>  6 files changed, 53 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index cf678be58fa4..f02ddffd4960 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,7 +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,
> +		drm_atomic_bridge_chain_post_disable(bridge,
>  						     old_state);
>  	}
>  
> @@ -1190,6 +1196,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 +1224,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 +1324,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 +1343,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 +1355,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 80d88a55302e..4fe9e723e227 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 = bridge->next;
>  			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..7a1f1e5f0326 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,15 @@ 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 8aeba83fcf31..27eef63ce0ff 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_chain_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);
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>

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

* Re: [PATCH v3 07/21] drm/bridge: Make the bridge chain a double-linked list
  2019-10-23 15:44 ` [PATCH v3 07/21] drm/bridge: Make the bridge chain a double-linked list Boris Brezillon
@ 2019-10-25 13:29   ` Neil Armstrong
  2019-11-05 16:02     ` Neil Armstrong
  2019-11-24 13:57   ` Laurent Pinchart
  1 sibling, 1 reply; 76+ messages in thread
From: Neil Armstrong @ 2019-10-25 13:29 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

On 23/10/2019 17:44, 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>
> ---
> 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 +---
>  include/drm/drm_bridge.h      |  12 ++-
>  include/drm/drm_encoder.h     |   9 +-
>  4 files changed, 135 insertions(+), 73 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index 54c874493c57..c5cf8a9c4237 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.
>   */
> @@ -114,6 +114,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
>  int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  		      struct drm_bridge *previous)
>  {
> +	LIST_HEAD(tmp_list);
>  	int ret;
>  
>  	if (!encoder || !bridge)
> @@ -127,6 +128,7 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  
>  	bridge->dev = encoder->dev;
>  	bridge->encoder = encoder;
> +	list_add_tail(&bridge->chain_node, &tmp_list);
>  
>  	if (bridge->funcs->attach) {
>  		ret = bridge->funcs->attach(bridge);
> @@ -138,9 +140,9 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  	}
>  
>  	if (previous)
> -		previous->next = bridge;
> +		list_splice(&tmp_list, &previous->chain_node);
>  	else
> -		encoder->bridge = bridge;
> +		list_splice(&tmp_list, &encoder->bridge_chain);
>  
>  	return 0;
>  }
> @@ -157,6 +159,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 +193,22 @@ 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 +231,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;
> +
> +	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 (bridge->funcs->mode_valid)
>  		ret = bridge->funcs->mode_valid(bridge, mode);
> +		if (ret != MODE_OK)
> +			return ret;
> +	}
>  
> -	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 +264,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 +294,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 +323,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 +349,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 +375,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 +403,23 @@ 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 +439,19 @@ 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 +471,23 @@ 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, &bridge->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 +506,19 @@ 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, &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 4fe9e723e227..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 = bridge->next;
> -			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/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index 27eef63ce0ff..3ab16c95e59e 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_chain_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_chain_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..9b3dde177c81 100644
> --- a/include/drm/drm_encoder.h
> +++ b/include/drm/drm_encoder.h
> @@ -172,7 +172,14 @@ struct drm_encoder {
>  	 * &drm_connector_state.crtc.
>  	 */
>  	struct drm_crtc *crtc;
> -	struct drm_bridge *bridge;
> +
> +	/**
> +	 * @bridge_chain: Bridges attached to this encoder. The first entry of
> +	 * this list is always &drm_encoder.bridge. It may be followed by other
> +	 * bridge entities.
> +	 */
> +	struct list_head bridge_chain;
> +
>  	const struct drm_encoder_funcs *funcs;
>  	const struct drm_encoder_helper_funcs *helper_private;
>  };
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>

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

* Re: [PATCH v3 08/21] drm/bridge: Add the drm_for_each_bridge_in_chain() helper
  2019-10-23 15:44 ` [PATCH v3 08/21] drm/bridge: Add the drm_for_each_bridge_in_chain() helper Boris Brezillon
@ 2019-10-25 13:30   ` Neil Armstrong
  2019-11-24 14:07   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Neil Armstrong @ 2019-10-25 13:30 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

On 23/10/2019 17:44, Boris Brezillon wrote:
> 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>
> ---
> 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 3ab16c95e59e..238e84ab63a3 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);
>  }
>  
> +/**
> + * 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);
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>

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

* Re: [PATCH v3 10/21] drm/bridge: Clarify the atomic enable/disable hooks semantics
  2019-10-23 15:45 ` [PATCH v3 10/21] drm/bridge: Clarify the atomic enable/disable hooks semantics Boris Brezillon
@ 2019-10-25 14:33   ` Neil Armstrong
  2019-12-02 16:50   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Neil Armstrong @ 2019-10-25 14:33 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

On 23/10/2019 17:45, 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>
> ---
> Changes in v3:
> * New patch
> ---
>  drivers/gpu/drm/drm_bridge.c | 24 ++++++++++++------------
>  include/drm/drm_bridge.h     | 16 ++++++++++++----
>  2 files changed, 24 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index c53966767782..ca74bfe028c9 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -447,7 +447,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,
> @@ -457,7 +457,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;
> @@ -469,7 +469,7 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
>  	list_for_each_entry_reverse(iter, &encoder->bridge_chain,
>  				    chain_node) {
>  		if (iter->funcs->atomic_disable)
> -			iter->funcs->atomic_disable(iter, state);
> +			iter->funcs->atomic_disable(iter, old_state);
>  		else if (iter->funcs->disable)
>  			iter->funcs->disable(iter);
>  
> @@ -483,7 +483,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,
> @@ -493,7 +493,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;
>  
> @@ -504,7 +504,7 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
>  	list_for_each_entry_from(bridge, &encoder->bridge_chain,
>  				 chain_node) {
>  		if (bridge->funcs->atomic_post_disable)
> -			bridge->funcs->atomic_post_disable(bridge, state);
> +			bridge->funcs->atomic_post_disable(bridge, old_state);
>  		else if (bridge->funcs->post_disable)
>  			bridge->funcs->post_disable(bridge);
>  	}
> @@ -515,7 +515,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,
> @@ -525,7 +525,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;
> @@ -537,7 +537,7 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
>  	list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain,
>  				    chain_node) {
>  		if (iter->funcs->atomic_pre_enable)
> -			iter->funcs->atomic_pre_enable(iter, state);
> +			iter->funcs->atomic_pre_enable(iter, old_state);
>  		else if (iter->funcs->pre_enable)
>  			iter->funcs->pre_enable(iter);
>  
> @@ -550,7 +550,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,
> @@ -560,7 +560,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;
>  
> @@ -571,7 +571,7 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
>  	list_for_each_entry_from(bridge, &bridge->encoder->bridge_chain,
>  				 chain_node) {
>  		if (bridge->funcs->atomic_enable)
> -			bridge->funcs->atomic_enable(bridge, state);
> +			bridge->funcs->atomic_enable(bridge, old_state);
>  		else if (bridge->funcs->enable)
>  			bridge->funcs->enable(bridge);
>  	}
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index a608c47d1de5..e814e6d6e7c2 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -266,6 +266,8 @@ struct drm_bridge_funcs {
>  	 * bridge's @atomic_pre_enable or @pre_enable function. If the preceding
>  	 * element is a &drm_encoder it's called right before the encoder's
>  	 * &drm_encoder_helper_funcs.atomic_enable hook.
> +	 * This hook is passed the old atomic state (atomic state after new/old
> +	 * states have been swapped).
>  	 *
>  	 * The display pipe (i.e. clocks and timing signals) feeding this bridge
>  	 * will not yet be running when this callback is called. The bridge must
> @@ -281,7 +283,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:
> @@ -292,6 +294,8 @@ struct drm_bridge_funcs {
>  	 * bridge's @atomic_enable or @enable function. If the preceding element
>  	 * is a &drm_encoder it's called right after the encoder's
>  	 * &drm_encoder_helper_funcs.atomic_enable hook.
> +	 * This hook is passed the old atomic state (atomic state after new/old
> +	 * states have been swapped).
>  	 *
>  	 * The bridge can assume that the display pipe (i.e. clocks and timing
>  	 * signals) feeding it is running when this callback is called. This
> @@ -306,7 +310,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:
>  	 *
> @@ -316,6 +320,8 @@ struct drm_bridge_funcs {
>  	 * bridge's @atomic_disable or @disable vfunc. If the preceding element
>  	 * is a &drm_encoder it's called right before the
>  	 * &drm_encoder_helper_funcs.atomic_disable hook.
> +	 * This hook is passed the old atomic state (atomic state after new/old
> +	 * states have been swapped).
>  	 *
>  	 * The bridge can assume that the display pipe (i.e. clocks and timing
>  	 * signals) feeding it is still running when this callback is called.
> @@ -329,7 +335,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:
> @@ -340,6 +346,8 @@ struct drm_bridge_funcs {
>  	 * @atomic_post_disable or @post_disable function. If the preceding
>  	 * element is a &drm_encoder it's called right after the encoder's
>  	 * &drm_encoder_helper_funcs.atomic_disable hook.
> +	 * This hook is passed the old atomic state (atomic state after new/old
> +	 * states have been swapped).
>  	 *
>  	 * The bridge must assume that the display pipe (i.e. clocks and timing
>  	 * signals) feeding it is no longer running when this callback is
> @@ -355,7 +363,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);
>  
>  	/**
>  	 * @atomic_duplicate_state:
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>

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

* Re: [PATCH v3 13/21] drm/bridge: Add the drm_bridge_chain_get_prev_bridge() helper
  2019-10-23 15:45 ` [PATCH v3 13/21] drm/bridge: Add the drm_bridge_chain_get_prev_bridge() helper Boris Brezillon
@ 2019-10-25 14:34   ` Neil Armstrong
  2019-12-02 17:05   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Neil Armstrong @ 2019-10-25 14:34 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

On 23/10/2019 17:45, Boris Brezillon wrote:
> 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>
> ---
> 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 2beb1ef9a733..3fb518494640 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -526,6 +526,22 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge)
>  	return list_next_entry(bridge, chain_node);
>  }
>  
> +/**
> + * drm_bridge_chain_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_chain_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
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>

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

* Re: [PATCH v3 09/21] drm/bridge: Add a drm_bridge_state object
  2019-10-23 15:45 ` [PATCH v3 09/21] drm/bridge: Add a drm_bridge_state object Boris Brezillon
@ 2019-10-25 14:35   ` Neil Armstrong
  2019-11-05 16:05   ` Neil Armstrong
  2019-12-02 16:42   ` Laurent Pinchart
  2 siblings, 0 replies; 76+ messages in thread
From: Neil Armstrong @ 2019-10-25 14:35 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

On 23/10/2019 17:45, 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>
> ---
> 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        | 168 +++++++++++++++++++++++++++-
>  include/drm/drm_atomic.h            |   3 +
>  include/drm/drm_bridge.h            | 118 +++++++++++++++++++
>  5 files changed, 343 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 419381abbdd1..6c249ce39380 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>
> @@ -1010,6 +1011,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 f02ddffd4960..de985ba7ce2d 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 c5cf8a9c4237..c53966767782 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,38 @@ 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);
> +	else
> +		state = drm_atomic_helper_bridge_duplicate_state(bridge);
> +
> +	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
> +		drm_atomic_helper_bridge_destroy_state(bridge, 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 +147,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;
>  	LIST_HEAD(tmp_list);
>  	int ret;
>  
> @@ -132,19 +166,39 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  
>  	if (bridge->funcs->attach) {
>  		ret = bridge->funcs->attach(bridge);
> -		if (ret < 0) {
> -			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 = drm_atomic_helper_bridge_reset(bridge);
> +
> +	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);
> +
>  	if (previous)
>  		list_splice(&tmp_list, &previous->chain_node);
>  	else
>  		list_splice(&tmp_list, &encoder->bridge_chain);
>  
>  	return 0;
> +
> +err_detach_bridge:
> +	if (bridge->funcs->detach)
> +		bridge->funcs->detach(bridge);
> +
> +err_reset_bridge:
> +	bridge->dev = NULL;
> +	bridge->encoder = NULL;
> +	return ret;
>  }
>  EXPORT_SYMBOL(drm_bridge_attach);
>  
> @@ -156,6 +210,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);
>  
> @@ -522,6 +578,108 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
>  }
>  EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
>  
> +/**
> + * drm_atomic_helper_bridge_destroy_state() - Default destroy state helper
> + * @bridge: the bridge this state refers to
> + * @state: state object to destroy
> + *
> + * Just a simple kfree() for now.
> + */
> +void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge,
> +					    struct drm_bridge_state *state)
> +{
> +	kfree(state);
> +}
> +EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state);
> +
> +/**
> + * __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
> + *
> + * For now it's just a memset(0) plus a state->bridge assignment. Might
> + * be extended in the future.
> + */
> +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_reset() - default &drm_plane_funcs.reset hook for
> + *				      bridges
> + * @bridge: the bridge to reset state on
> + *
> + * Resets the atomic state for @bridge by freeing the state pointer (which
> + * might be NULL, e.g. at driver load time) and allocating a new empty state
> + * object.
> + *
> + * RETURNS:
> + * A valid drm_bridge_state object in case of success, an ERR_PTR()
> + * giving the reaon of the failure otherwise.
> + */
> +struct drm_bridge_state *
> +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge)
> +{
> +	struct drm_bridge_state *bridge_state;
> +
> +	bridge_state = kzalloc(sizeof(*bridge_state), GFP_KERNEL);
> +	if (!bridge_state)
> +		return ERR_PTR(-ENOMEM);
> +
> +	__drm_atomic_helper_bridge_reset(bridge, bridge_state);
> +	return bridge_state;
> +}
> +EXPORT_SYMBOL(drm_atomic_helper_bridge_reset);
> +
> +/**
> + * __drm_atomic_helper_bridge_duplicate_state() - Copy the content of the
> + *						  current bridge state into a
> + *						  new one
> + * @bridge: bridge element the old and new states are referring to
> + * @new: new bridge state to copy to
> + *
> + * Should be used by custom &drm_bridge_funcs.atomic_duplicate() implementation
> + * to copy the previous state into the new object.
> + */
> +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
> +						struct drm_bridge_state *new)
> +{
> +	__drm_atomic_helper_private_obj_duplicate_state(&bridge->base,
> +							&new->base);
> +	new->bridge = bridge;
> +}
> +EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_state);
> +
> +/**
> + * drm_atomic_helper_duplicate_bridge_state() - Default duplicate state helper
> + * @bridge: bridge containing the state to duplicate
> + *
> + * Default implementation of &drm_bridge_funcs.atomic_duplicate().
> + *
> + * RETURNS:
> + * a valid state object or NULL if the allocation fails.
> + */
> +struct drm_bridge_state *
> +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge)
> +{
> +	struct drm_bridge_state *new;
> +
> +	if (WARN_ON(!bridge->base.state))
> +		return NULL;
> +
> +	new = kzalloc(sizeof(*new), GFP_KERNEL);
> +	if (new)
> +		__drm_atomic_helper_bridge_duplicate_state(bridge, new);
> +
> +	return new;
> +}
> +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 927e1205d7aa..1c0a08217712 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 238e84ab63a3..a608c47d1de5 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_atomic.h>
>  #include <drm/drm_encoder.h>
>  #include <drm/drm_mode_object.h>
>  #include <drm/drm_modes.h>
> @@ -33,6 +34,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 +356,48 @@ struct drm_bridge_funcs {
>  	 */
>  	void (*atomic_post_disable)(struct drm_bridge *bridge,
>  				    struct drm_atomic_state *state);
> +
> +	/**
> +	 * @atomic_duplicate_state:
> +	 *
> +	 * Duplicate the current bridge state object (which is guaranteed to be
> +	 * non-NULL).
> +	 *
> +	 * The atomic_duplicate_state() is optional, the core falls back on
> +	 * &drm_atomic_helper_bridge_duplicate_state() when not implemented.
> +	 *
> +	 * 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, the coref falls back on
> +	 * &drm_atomic_helper_bridge_destroy_state() when not implemented.
> +	 */
> +	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, the core falls back on
> +	 * &drm_atomic_helper_bridge_reset() when not implemented.
> +	 *
> +	 * RETURNS:
> +	 * A valid drm_bridge_state object in case of success, an ERR_PTR()
> +	 * giving the reaon of the failure otherwise.
> +	 */
> +	struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
>  };
>  
>  /**
> @@ -380,6 +440,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 +466,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);
> @@ -475,6 +543,56 @@ 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_destroy_state(struct drm_bridge *bridge,
> +					    struct drm_bridge_state *state);
> +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
> +						struct drm_bridge_state *new);
> +struct drm_bridge_state *
> +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge);
> +
> +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 (!obj_state)
> +		return NULL;
> +
> +	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,
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>

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

* Re: [PATCH v3 12/21] drm/bridge: Add an ->atomic_check() hook
  2019-10-23 15:45 ` [PATCH v3 12/21] drm/bridge: Add an ->atomic_check() hook Boris Brezillon
@ 2019-10-25 14:35   ` Neil Armstrong
  2019-12-02 17:03   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Neil Armstrong @ 2019-10-25 14:35 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

On 23/10/2019 17:45, Boris Brezillon wrote:
> 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>
> ---
> 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 de985ba7ce2d..1d0a19511a0d 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 377866e3214f..990e056296bd 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -615,6 +615,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_destroy_state() - Default destroy state helper
>   * @bridge: the bridge this state refers to
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index b1f557d8dba9..2beb1ef9a733 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -127,7 +127,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:
>  	 *
> @@ -391,6 +393,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:
>  	 *
> @@ -542,6 +566,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,
> 

Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>

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

* Re: [PATCH v3 17/21] dt-bindings: display: bridge: lvds-transmitter: Add new props
  2019-10-23 15:45 ` [PATCH v3 17/21] dt-bindings: display: bridge: lvds-transmitter: Add new props Boris Brezillon
@ 2019-10-25 19:57   ` Rob Herring
  2019-10-31 13:04     ` Boris Brezillon
  2019-12-02 17:11   ` Laurent Pinchart
  1 sibling, 1 reply; 76+ messages in thread
From: Rob Herring @ 2019-10-25 19:57 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, Jernej Skrabec, Seung-Woo Kim

On Wed, Oct 23, 2019 at 05:45:08PM +0200, Boris Brezillon wrote:
> Add the data-mapping property to describe the output bus format and
> the bus-width property to describe the input bus format.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> Changes in v3:
> * New patch
> ---
>  .../bindings/display/bridge/lvds-transmitter.txt    | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> index 60091db5dfa5..7b43b6f20279 100644
> --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> @@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
>  - Video port 0 for parallel input
>  - Video port 1 for LVDS output
>  
> +Optional port 0 node properties:
> +
> +- bus-width: number of data lines use to transmit the RGB data.
> +	     Can be 18 or 24.
> +
> +Optional port 1 node properties:
> +
> +- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml
> +		for more details about this LVDS data-mapping property.
> +		Supported values:
> +		"jeida-18"
> +		"jeida-24"
> +		"vesa-24"

This is already defined to be a panel property. Do we need it at both 
ends?

Also, why duplicate all the supported values.

Rob

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

* Re: [PATCH v3 20/21] dt-bindings: display: panel: Add the LTA089AC29000 variant
  2019-10-23 15:45 ` [PATCH v3 20/21] dt-bindings: display: panel: Add the LTA089AC29000 variant Boris Brezillon
@ 2019-10-25 19:58   ` Rob Herring
  2019-12-02 17:19   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Rob Herring @ 2019-10-25 19:58 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, Laurent Pinchart, Jonas Karlman, Jernej Skrabec,
	Mark Rutland, devicetree, Boris Brezillon

On Wed, 23 Oct 2019 17:45:11 +0200, Boris Brezillon wrote:
> The LTA089AC29000 and LT089AC29000 are not exactly the same. Let's add
> a new compatible for the LTA variant.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
>  .../bindings/display/panel/toshiba,lt089ac29000.txt          | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 

Acked-by: Rob Herring <robh@kernel.org>

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

* Re: [PATCH v3 02/21] drm/exynos: Don't reset bridge->next
  2019-10-23 15:44 ` [PATCH v3 02/21] drm/exynos: Don't reset bridge->next Boris Brezillon
@ 2019-10-28 12:19   ` Inki Dae
  0 siblings, 0 replies; 76+ messages in thread
From: Inki Dae @ 2019-10-28 12:19 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: DRI mailing list, 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

2019년 10월 24일 (목) 오전 12:45, Boris Brezillon
<boris.brezillon@collabora.com>님이 작성:
>
> bridge->next is only points to the new bridge if drm_bridge_attach()
> succeeds. No need to reset it manually here.
>
> Note that this change is part of the attempt to make the bridge chain
> a double-linked list. In order to do that we must patch all drivers
> manipulating the bridge->next field.
>
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
> Changes in v2:
> * Add Laurent's R-b (I'd like to have a R-b from the DRM exynos
>   maintainers before applying that one)

Acked-by: Inki Dae <inki.dae@samsung.com>

Thanks,
Inki Dae

> ---
>  drivers/gpu/drm/exynos/exynos_dp.c | 1 -
>  1 file changed, 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c
> index 1e6aa24bf45e..4785885c0f4f 100644
> --- a/drivers/gpu/drm/exynos/exynos_dp.c
> +++ b/drivers/gpu/drm/exynos/exynos_dp.c
> @@ -110,7 +110,6 @@ static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data,
>                 if (ret) {
>                         DRM_DEV_ERROR(dp->dev,
>                                       "Failed to attach bridge to drm\n");
> -                       bridge->next = NULL;
>                         return ret;
>                 }
>         }
> --
> 2.21.0
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v3 17/21] dt-bindings: display: bridge: lvds-transmitter: Add new props
  2019-10-25 19:57   ` Rob Herring
@ 2019-10-31 13:04     ` Boris Brezillon
  0 siblings, 0 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-10-31 13:04 UTC (permalink / raw)
  To: Rob Herring
  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, Jernej Skrabec, Seung-Woo Kim

On Fri, 25 Oct 2019 14:57:11 -0500
Rob Herring <robh@kernel.org> wrote:

> On Wed, Oct 23, 2019 at 05:45:08PM +0200, Boris Brezillon wrote:
> > Add the data-mapping property to describe the output bus format and
> > the bus-width property to describe the input bus format.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > ---
> > Changes in v3:
> > * New patch
> > ---
> >  .../bindings/display/bridge/lvds-transmitter.txt    | 13 +++++++++++++
> >  1 file changed, 13 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> > index 60091db5dfa5..7b43b6f20279 100644
> > --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> > +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> > @@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
> >  - Video port 0 for parallel input
> >  - Video port 1 for LVDS output
> >  
> > +Optional port 0 node properties:
> > +
> > +- bus-width: number of data lines use to transmit the RGB data.
> > +	     Can be 18 or 24.
> > +
> > +Optional port 1 node properties:
> > +
> > +- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml
> > +		for more details about this LVDS data-mapping property.
> > +		Supported values:
> > +		"jeida-18"
> > +		"jeida-24"
> > +		"vesa-24"  
> 
> This is already defined to be a panel property. Do we need it at both 
> ends?

That's a valid point. I'll patch the panel-simple driver to takes this
DT prop into account.

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

* Re: [PATCH v3 07/21] drm/bridge: Make the bridge chain a double-linked list
  2019-10-25 13:29   ` Neil Armstrong
@ 2019-11-05 16:02     ` Neil Armstrong
  2019-11-24  7:48       ` Boris Brezillon
  0 siblings, 1 reply; 76+ messages in thread
From: Neil Armstrong @ 2019-11-05 16:02 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

Hi,


On 25/10/2019 15:29, Neil Armstrong wrote:
> On 23/10/2019 17:44, 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>
>> ---
>> 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 +---
>>  include/drm/drm_bridge.h      |  12 ++-
>>  include/drm/drm_encoder.h     |   9 +-
>>  4 files changed, 135 insertions(+), 73 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
>> index 54c874493c57..c5cf8a9c4237 100644
>> --- a/drivers/gpu/drm/drm_bridge.c
>> +++ b/drivers/gpu/drm/drm_bridge.c

[...]

>>  
>> @@ -426,15 +471,23 @@ 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, &bridge->encoder->bridge_chain,
>> +				    chain_node) {

This should use the encoder local variable in list_for_each_entry_reverse()

>> +		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 +506,19 @@ 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, &bridge->encoder->bridge_chain,
>> +				 chain_node) {

This should use encoder instead of bridge->encoder otherwise bridge will
change and bridge->encoder->bridge_chain won't be valid during the for_each and
cause the following :

[   79.082861] WARNING: CPU: 2 PID: 1999 at drivers/gpu/drm/drm_bridge.c:607 drm_atomic_bridge_chain_enable+0xac/0xc0
...
[   79.210153]  drm_atomic_bridge_chain_enable+0xac/0xc0
[   79.215156]  drm_atomic_helper_commit_modeset_enables+0x138/0x248
[   79.221190]  drm_atomic_helper_commit_tail_rpm+0x2c/0x78
[   79.226452]  commit_tail+0x50/0xc0
[   79.229815]  drm_atomic_helper_commit+0xe8/0x168
[   79.234386]  drm_atomic_commit+0x48/0x58
[   79.238269]  drm_client_modeset_commit_atomic.isra.15+0x184/0x220
[   79.244305]  drm_client_modeset_commit_force+0x64/0x1a0
[   79.249482]  drm_fb_helper_restore_fbdev_mode_unlocked+0x70/0xe8
[   79.255432]  drm_fbdev_client_restore+0x14/0x20
[   79.259920]  drm_client_dev_restore+0x80/0xd8
[   79.264231]  drm_lastclose+0x4c/0x58
[   79.267765]  drm_release+0xa8/0x178

>> +		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 4fe9e723e227..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 = bridge->next;
>> -			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/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
>> index 27eef63ce0ff..3ab16c95e59e 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_chain_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_chain_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..9b3dde177c81 100644
>> --- a/include/drm/drm_encoder.h
>> +++ b/include/drm/drm_encoder.h
>> @@ -172,7 +172,14 @@ struct drm_encoder {
>>  	 * &drm_connector_state.crtc.
>>  	 */
>>  	struct drm_crtc *crtc;
>> -	struct drm_bridge *bridge;
>> +
>> +	/**
>> +	 * @bridge_chain: Bridges attached to this encoder. The first entry of
>> +	 * this list is always &drm_encoder.bridge. It may be followed by other
>> +	 * bridge entities.
>> +	 */
>> +	struct list_head bridge_chain;
>> +
>>  	const struct drm_encoder_funcs *funcs;
>>  	const struct drm_encoder_helper_funcs *helper_private;
>>  };
>>
> 
> Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
> 

With these fixed, you can keep my Reviewed-by

Neil

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

* Re: [PATCH v3 09/21] drm/bridge: Add a drm_bridge_state object
  2019-10-23 15:45 ` [PATCH v3 09/21] drm/bridge: Add a drm_bridge_state object Boris Brezillon
  2019-10-25 14:35   ` Neil Armstrong
@ 2019-11-05 16:05   ` Neil Armstrong
  2019-11-24  7:50     ` Boris Brezillon
  2019-12-02 16:42   ` Laurent Pinchart
  2 siblings, 1 reply; 76+ messages in thread
From: Neil Armstrong @ 2019-11-05 16:05 UTC (permalink / raw)
  To: Boris Brezillon, 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, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

On 23/10/2019 17:45, 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>
> ---
> 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        | 168 +++++++++++++++++++++++++++-
>  include/drm/drm_atomic.h            |   3 +
>  include/drm/drm_bridge.h            | 118 +++++++++++++++++++
>  5 files changed, 343 insertions(+), 5 deletions(-)
> 
[...]

> --- 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 238e84ab63a3..a608c47d1de5 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_atomic.h>
>  #include <drm/drm_encoder.h>
>  #include <drm/drm_mode_object.h>
>  #include <drm/drm_modes.h>
> @@ -33,6 +34,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 +356,48 @@ struct drm_bridge_funcs {
>  	 */
>  	void (*atomic_post_disable)(struct drm_bridge *bridge,
>  				    struct drm_atomic_state *state);
> +
> +	/**
> +	 * @atomic_duplicate_state:
> +	 *
> +	 * Duplicate the current bridge state object (which is guaranteed to be
> +	 * non-NULL).
> +	 *
> +	 * The atomic_duplicate_state() is optional, the core falls back on
> +	 * &drm_atomic_helper_bridge_duplicate_state() when not implemented.
> +	 *
> +	 * 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, the coref falls back on
> +	 * &drm_atomic_helper_bridge_destroy_state() when not implemented.
> +	 */
> +	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, the core falls back on
> +	 * &drm_atomic_helper_bridge_reset() when not implemented.
> +	 *
> +	 * RETURNS:
> +	 * A valid drm_bridge_state object in case of success, an ERR_PTR()
> +	 * giving the reaon of the failure otherwise.
> +	 */
> +	struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
>  };
>  
>  /**
> @@ -380,6 +440,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 +466,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);
> @@ -475,6 +543,56 @@ 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_destroy_state(struct drm_bridge *bridge,
> +					    struct drm_bridge_state *state);
> +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
> +						struct drm_bridge_state *new);
> +struct drm_bridge_state *
> +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge);
> +
> +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 (!obj_state)
> +		return NULL;

drm_atomic_get_private_obj_state will return an error object on error, so should be:
	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,
> 

With this fixed, you can keep my Reviewed-by

Neil

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

* Re: [PATCH v3 00/21] drm: Add support for bus-format negotiation
  2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
                   ` (21 preceding siblings ...)
  2019-10-24 11:27 ` [PATCH v3 00/21] drm: Add support for bus-format negotiation Neil Armstrong
@ 2019-11-24  0:46 ` Ezequiel Garcia
  2019-11-24  7:32   ` Boris Brezillon
  22 siblings, 1 reply; 76+ messages in thread
From: Ezequiel Garcia @ 2019-11-24  0:46 UTC (permalink / raw)
  To: Boris Brezillon, 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

Hi Boris, Neil,

On Wed, 2019-10-23 at 17:44 +0200, 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.
> 
> Major changes since v2:
> * Get rid of the dummy bridge embedded in drm_encoder and let encoder
>   drivers provide their own bridge element
> * Clarify APIs and improve the doc
> * Propagate bus flags by default
> 
> Major changes since the RFC:
> 
> * Add a dummy bridge to the drm_encoder object so that vc4 and exynos
>   DSI drivers can implement the pre_enable/post_disable hooks instead
>   of manually setting encoder->bridge to NULL to control the
>   enable/disable sequence. This change is also a first step towards
>   drm_bridge/drm_encoder unification. New encoder drivers should
>   stop implementing drm_encoder_helper_funcs and switch to
>   drm_bridge_funcs. Existing drivers can be converted progressively
>   (already have a branch where I started converting some of them [1])
> * rework the bus format negotiation to give more control to bridge
>   drivers in the selection process (driver can select at runtime which
>   input bus format they support for a specific output bus format based
>   on any information available in the connector, crtc and bridge state.
> 
> A more detailed changelog is provided in each patch.
> 
> This patch series is also available here [2].
> 
> Thanks,
> 
> Boris
> 
> [1]https://github.com/bbrezillon/linux-0day/commits/drm-encoder-bridge
> [2]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v3
> 
> *** BLURB HERE ***
> 
> Boris Brezillon (21):
>   drm/vc4: Declare the DSI encoder as a bridge element
>   drm/exynos: Don't reset bridge->next
>   drm/exynos: Declare the DSI encoder as a bridge element
>   drm/bridge: Rename bridge helpers targeting a bridge chain
>   drm/bridge: Introduce drm_bridge_chain_get_next_bridge()
>   drm: Stop accessing encoder->bridge directly

Patches 1 to 6 seem to be reviewed, and appear as a good
step forward.

Perhaps we can consider merging these first? That way,
we can reduce the patches needed to rebase and submit
on each iteration.

Regards,
Ezequiel


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

* Re: [PATCH v3 00/21] drm: Add support for bus-format negotiation
  2019-11-24  0:46 ` Ezequiel Garcia
@ 2019-11-24  7:32   ` Boris Brezillon
  2019-11-24  9:34     ` Ezequiel Garcia
  0 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-11-24  7:32 UTC (permalink / raw)
  To: Ezequiel Garcia
  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 Sun, 24 Nov 2019 09:46:41 +0900
Ezequiel Garcia <ezequiel@collabora.com> wrote:

> Hi Boris, Neil,
> 
> On Wed, 2019-10-23 at 17:44 +0200, 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.
> > 
> > Major changes since v2:
> > * Get rid of the dummy bridge embedded in drm_encoder and let encoder
> >   drivers provide their own bridge element
> > * Clarify APIs and improve the doc
> > * Propagate bus flags by default
> > 
> > Major changes since the RFC:
> > 
> > * Add a dummy bridge to the drm_encoder object so that vc4 and exynos
> >   DSI drivers can implement the pre_enable/post_disable hooks instead
> >   of manually setting encoder->bridge to NULL to control the
> >   enable/disable sequence. This change is also a first step towards
> >   drm_bridge/drm_encoder unification. New encoder drivers should
> >   stop implementing drm_encoder_helper_funcs and switch to
> >   drm_bridge_funcs. Existing drivers can be converted progressively
> >   (already have a branch where I started converting some of them [1])
> > * rework the bus format negotiation to give more control to bridge
> >   drivers in the selection process (driver can select at runtime which
> >   input bus format they support for a specific output bus format based
> >   on any information available in the connector, crtc and bridge state.
> > 
> > A more detailed changelog is provided in each patch.
> > 
> > This patch series is also available here [2].
> > 
> > Thanks,
> > 
> > Boris
> > 
> > [1]https://github.com/bbrezillon/linux-0day/commits/drm-encoder-bridge
> > [2]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v3
> > 
> > *** BLURB HERE ***
> > 
> > Boris Brezillon (21):
> >   drm/vc4: Declare the DSI encoder as a bridge element
> >   drm/exynos: Don't reset bridge->next
> >   drm/exynos: Declare the DSI encoder as a bridge element
> >   drm/bridge: Rename bridge helpers targeting a bridge chain
> >   drm/bridge: Introduce drm_bridge_chain_get_next_bridge()
> >   drm: Stop accessing encoder->bridge directly  
> 
> Patches 1 to 6 seem to be reviewed, and appear as a good
> step forward.

AFAICT, patch 1 and 3 are not reviewed, which is kind of blocking me
for patch 4-6. I can (and plan to) apply patch 2.

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

* Re: [PATCH v3 07/21] drm/bridge: Make the bridge chain a double-linked list
  2019-11-05 16:02     ` Neil Armstrong
@ 2019-11-24  7:48       ` Boris Brezillon
  0 siblings, 0 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-11-24  7:48 UTC (permalink / raw)
  To: Neil Armstrong
  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,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree

Hi Neil,

Sorry for the late reply.

On Tue, 5 Nov 2019 17:02:30 +0100
Neil Armstrong <narmstrong@baylibre.com> wrote:

> Hi,
> 
> 
> On 25/10/2019 15:29, Neil Armstrong wrote:
> > On 23/10/2019 17:44, 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>
> >> ---
> >> 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 +---
> >>  include/drm/drm_bridge.h      |  12 ++-
> >>  include/drm/drm_encoder.h     |   9 +-
> >>  4 files changed, 135 insertions(+), 73 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> >> index 54c874493c57..c5cf8a9c4237 100644
> >> --- a/drivers/gpu/drm/drm_bridge.c
> >> +++ b/drivers/gpu/drm/drm_bridge.c  
> 
> [...]
> 
> >>  
> >> @@ -426,15 +471,23 @@ 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, &bridge->encoder->bridge_chain,
> >> +				    chain_node) {  
> 
> This should use the encoder local variable in list_for_each_entry_reverse()
> 
> >> +		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 +506,19 @@ 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, &bridge->encoder->bridge_chain,
> >> +				 chain_node) {  
> 
> This should use encoder instead of bridge->encoder otherwise bridge will
> change and bridge->encoder->bridge_chain won't be valid during the for_each and
> cause the following :

Oops, indeed. I fixed the very same problem in another helper but
somehow missed those 2. Thanks for testing/reporting the bug.

Boris

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

* Re: [PATCH v3 09/21] drm/bridge: Add a drm_bridge_state object
  2019-11-05 16:05   ` Neil Armstrong
@ 2019-11-24  7:50     ` Boris Brezillon
  0 siblings, 0 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-11-24  7:50 UTC (permalink / raw)
  To: Neil Armstrong
  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,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Rob Herring,
	Mark Rutland, devicetree

On Tue, 5 Nov 2019 17:05:16 +0100
Neil Armstrong <narmstrong@baylibre.com> wrote:

> >  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);
> > @@ -475,6 +543,56 @@ 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_destroy_state(struct drm_bridge *bridge,
> > +					    struct drm_bridge_state *state);
> > +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
> > +						struct drm_bridge_state *new);
> > +struct drm_bridge_state *
> > +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge);
> > +
> > +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 (!obj_state)
> > +		return NULL;  
> 
> drm_atomic_get_private_obj_state will return an error object on error, so should be:
> 	if (IS_ERR(obj_state))
>               return ERR_CAST(obj_state);

Right, I'll fix it in v4.

Thanks,

Boris

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

* Re: [PATCH v3 00/21] drm: Add support for bus-format negotiation
  2019-11-24  7:32   ` Boris Brezillon
@ 2019-11-24  9:34     ` Ezequiel Garcia
  0 siblings, 0 replies; 76+ messages in thread
From: Ezequiel Garcia @ 2019-11-24  9:34 UTC (permalink / raw)
  To: Boris Brezillon, Eric Anholt, Inki Dae
  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 Sun, 2019-11-24 at 08:32 +0100, Boris Brezillon wrote:
> On Sun, 24 Nov 2019 09:46:41 +0900
> Ezequiel Garcia <ezequiel@collabora.com> wrote:
> 
> > Hi Boris, Neil,
> > 
> > On Wed, 2019-10-23 at 17:44 +0200, 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.
> > > 
> > > Major changes since v2:
> > > * Get rid of the dummy bridge embedded in drm_encoder and let encoder
> > >   drivers provide their own bridge element
> > > * Clarify APIs and improve the doc
> > > * Propagate bus flags by default
> > > 
> > > Major changes since the RFC:
> > > 
> > > * Add a dummy bridge to the drm_encoder object so that vc4 and exynos
> > >   DSI drivers can implement the pre_enable/post_disable hooks instead
> > >   of manually setting encoder->bridge to NULL to control the
> > >   enable/disable sequence. This change is also a first step towards
> > >   drm_bridge/drm_encoder unification. New encoder drivers should
> > >   stop implementing drm_encoder_helper_funcs and switch to
> > >   drm_bridge_funcs. Existing drivers can be converted progressively
> > >   (already have a branch where I started converting some of them [1])
> > > * rework the bus format negotiation to give more control to bridge
> > >   drivers in the selection process (driver can select at runtime which
> > >   input bus format they support for a specific output bus format based
> > >   on any information available in the connector, crtc and bridge state.
> > > 
> > > A more detailed changelog is provided in each patch.
> > > 
> > > This patch series is also available here [2].
> > > 
> > > Thanks,
> > > 
> > > Boris
> > > 
> > > [1]https://github.com/bbrezillon/linux-0day/commits/drm-encoder-bridge
> > > [2]https://github.com/bbrezillon/linux-0day/commits/drm-bridge-busfmt-v3
> > > 
> > > *** BLURB HERE ***
> > > 
> > > Boris Brezillon (21):
> > >   drm/vc4: Declare the DSI encoder as a bridge element
> > >   drm/exynos: Don't reset bridge->next
> > >   drm/exynos: Declare the DSI encoder as a bridge element
> > >   drm/bridge: Rename bridge helpers targeting a bridge chain
> > >   drm/bridge: Introduce drm_bridge_chain_get_next_bridge()
> > >   drm: Stop accessing encoder->bridge directly  
> > 
> > Patches 1 to 6 seem to be reviewed, and appear as a good
> > step forward.
> 
> AFAICT, patch 1 and 3 are not reviewed, which is kind of blocking me
> for patch 4-6. I can (and plan to) apply patch 2.

Ah, you are right. Let's add Eric for vc4 and Inki for exynos.

For reference, here is the series: https://patchwork.kernel.org/project/dri-devel/list/?series=192359

Thanks,
Ezequiel



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

* Re: [PATCH v3 01/21] drm/vc4: Declare the DSI encoder as a bridge element
  2019-10-23 15:44 ` [PATCH v3 01/21] drm/vc4: Declare the DSI encoder as a bridge element Boris Brezillon
@ 2019-11-24 10:01   ` Laurent Pinchart
  2019-11-24 10:47     ` Boris Brezillon
  0 siblings, 1 reply; 76+ messages in thread
From: Laurent Pinchart @ 2019-11-24 10:01 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:44:52PM +0200, Boris Brezillon wrote:
> Encoder drivers will progressively transition to the drm_bridge
> interface in place of the drm_encoder one.
> 
> Let's start with the VC4 driver, and use the ->pre_{enable,disable}()
> hooks to get rid of the hack resetting encoder->bridge.next (which was
> needed to control the encoder/bridge enable/disable sequence).
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> Changes in v3:
> * Embed a drm_bridge object in vc4_dsi since drm_encoder no longer has
>   a dummy bridge
> 
> Changes in v2:
> * New patch (replaces "drm/vc4: Get rid of the dsi->bridge field")
> ---
>  drivers/gpu/drm/vc4/vc4_dsi.c | 88 +++++++++++++++++++++--------------
>  1 file changed, 52 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index c9ba83ed49b9..49f8a313e759 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> @@ -498,7 +498,11 @@ struct vc4_dsi {
>  
>  	struct mipi_dsi_host dsi_host;
>  	struct drm_encoder *encoder;
> -	struct drm_bridge *bridge;
> +
> +	/* Embed a bridge object so we can implement bridge funcs instead of
> +	 * encoder ones.
> +	 */
> +	struct drm_bridge bridge;
>  
>  	void __iomem *regs;
>  
> @@ -543,6 +547,11 @@ struct vc4_dsi {
>  	struct debugfs_regset32 regset;
>  };
>  
> +static inline struct vc4_dsi *bridge_to_vc4_dsi(struct drm_bridge *bridge)
> +{
> +	return container_of(bridge, struct vc4_dsi, bridge);
> +}
> +
>  #define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host)
>  
>  static inline void
> @@ -747,16 +756,11 @@ dsi_esc_timing(u32 ns)
>  	return DIV_ROUND_UP(ns, ESC_TIME_NS);
>  }
>  
> -static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
> +static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge)
>  {
> -	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
> -	struct vc4_dsi *dsi = vc4_encoder->dsi;
> +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
>  	struct device *dev = &dsi->pdev->dev;
>  
> -	drm_bridge_disable(dsi->bridge);
> -	vc4_dsi_ulps(dsi, true);
> -	drm_bridge_post_disable(dsi->bridge);
> -
>  	clk_disable_unprepare(dsi->pll_phy_clock);
>  	clk_disable_unprepare(dsi->escape_clock);
>  	clk_disable_unprepare(dsi->pixel_clock);
> @@ -764,6 +768,13 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
>  	pm_runtime_put(dev);
>  }
>  
> +static void vc4_dsi_bridge_disable(struct drm_bridge *bridge)
> +{
> +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
> +
> +	vc4_dsi_ulps(dsi, true);
> +}
> +
>  /* Extends the mode's blank intervals to handle BCM2835's integer-only
>   * DSI PLL divider.
>   *
> @@ -777,12 +788,11 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
>   * higher-than-expected clock rate to the panel, but that's what the
>   * firmware does too.
>   */
> -static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
> -				       const struct drm_display_mode *mode,
> -				       struct drm_display_mode *adjusted_mode)
> +static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
> +				      const struct drm_display_mode *mode,
> +				      struct drm_display_mode *adjusted_mode)
>  {
> -	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
> -	struct vc4_dsi *dsi = vc4_encoder->dsi;
> +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
>  	struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock);
>  	unsigned long parent_rate = clk_get_rate(phy_parent);
>  	unsigned long pixel_clock_hz = mode->clock * 1000;
> @@ -816,11 +826,11 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
>  	return true;
>  }
>  
> -static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
> +static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge)
>  {
> +	struct drm_encoder *encoder = bridge->encoder;
>  	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> -	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
> -	struct vc4_dsi *dsi = vc4_encoder->dsi;
> +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
>  	struct device *dev = &dsi->pdev->dev;
>  	bool debug_dump_regs = false;
>  	unsigned long hs_clock;
> @@ -1054,8 +1064,12 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
>  	}
>  
>  	vc4_dsi_ulps(dsi, false);
> +}
>  
> -	drm_bridge_pre_enable(dsi->bridge);

If I'm not mistaken this switches the order of the DSI's encoder
pre-enable and the next bridge's pre-enable. I think it's true for
post-disable too. It may not be a problem, but have this been tested ?

> +static void vc4_dsi_bridge_enable(struct drm_bridge *bridge)
> +{
> +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
> +	bool debug_dump_regs = false;
>  
>  	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
>  		DSI_PORT_WRITE(DISP0_CTRL,
> @@ -1072,8 +1086,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
>  			       DSI_DISP0_ENABLE);
>  	}
>  
> -	drm_bridge_enable(dsi->bridge);
> -
>  	if (debug_dump_regs) {
>  		struct drm_printer p = drm_info_printer(&dsi->pdev->dev);
>  		dev_info(&dsi->pdev->dev, "DSI regs after:\n");
> @@ -1290,10 +1302,12 @@ static const struct mipi_dsi_host_ops vc4_dsi_host_ops = {
>  	.transfer = vc4_dsi_host_transfer,
>  };
>  
> -static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = {
> -	.disable = vc4_dsi_encoder_disable,
> -	.enable = vc4_dsi_encoder_enable,
> -	.mode_fixup = vc4_dsi_encoder_mode_fixup,
> +static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = {
> +	.pre_enable = vc4_dsi_bridge_pre_enable,
> +	.enable = vc4_dsi_bridge_enable,
> +	.disable = vc4_dsi_bridge_disable,
> +	.post_disable = vc4_dsi_bridge_post_disable,
> +	.mode_fixup = vc4_dsi_bridge_mode_fixup,
>  };
>  
>  static const struct of_device_id vc4_dsi_dt_match[] = {
> @@ -1445,6 +1459,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>  	struct vc4_dev *vc4 = to_vc4_dev(drm);
>  	struct vc4_dsi *dsi = dev_get_drvdata(dev);
>  	struct vc4_dsi_encoder *vc4_dsi_encoder;
> +	struct drm_bridge *next_bridge;
>  	struct drm_panel *panel;
>  	const struct of_device_id *match;
>  	dma_cap_mask_t dma_mask;
> @@ -1561,7 +1576,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>  	}
>  
>  	ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
> -					  &panel, &dsi->bridge);
> +					  &panel, &next_bridge);
>  	if (ret) {
>  		/* If the bridge or panel pointed by dev->of_node is not
>  		 * enabled, just return 0 here so that we don't prevent the DRM
> @@ -1576,10 +1591,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>  	}
>  
>  	if (panel) {
> -		dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel,
> +		next_bridge = devm_drm_panel_bridge_add_typed(dev, panel,
>  							      DRM_MODE_CONNECTOR_DSI);
> -		if (IS_ERR(dsi->bridge))
> -			return PTR_ERR(dsi->bridge);
> +		if (IS_ERR(next_bridge))
> +			return PTR_ERR(next_bridge);
>  	}
>  
>  	/* The esc clock rate is supposed to always be 100Mhz. */
> @@ -1598,19 +1613,20 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>  
>  	drm_encoder_init(drm, dsi->encoder, &vc4_dsi_encoder_funcs,
>  			 DRM_MODE_ENCODER_DSI, NULL);
> -	drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs);
>  
> -	ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL);
> +	/* Declare ourself as the first bridge element. */
> +	dsi->bridge.funcs = &vc4_dsi_bridge_funcs;
> +	ret = drm_bridge_attach(dsi->encoder, &dsi->bridge, NULL);
> +	if (ret) {
> +		dev_err(dev, "bridge attach failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = drm_bridge_attach(dsi->encoder, next_bridge, &dsi->bridge);
>  	if (ret) {
>  		dev_err(dev, "bridge attach failed: %d\n", ret);
>  		return ret;
>  	}

This is usually done in the bridge attach operation. As we're in control
we can attach the next bridge here, but I think the driver would look
more standard if you moved the second attach call to this bridge's
attach operation.

With this fixed, and if the driver has been tested and the
enable/disable order change doesn't cause issues,

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

> -	/* Disable the atomic helper calls into the bridge.  We
> -	 * manually call the bridge pre_enable / enable / etc. calls
> -	 * from our driver, since we need to sequence them within the
> -	 * encoder's enable/disable paths.
> -	 */
> -	dsi->encoder->bridge = NULL;
>  
>  	if (dsi->port == 0)
>  		vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
> @@ -1629,7 +1645,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>  	struct vc4_dev *vc4 = to_vc4_dev(drm);
>  	struct vc4_dsi *dsi = dev_get_drvdata(dev);
>  
> -	if (dsi->bridge)
> +	if (dsi->bridge.next)
>  		pm_runtime_disable(dev);
>  
>  	vc4_dsi_encoder_destroy(dsi->encoder);

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 03/21] drm/exynos: Declare the DSI encoder as a bridge element
  2019-10-23 15:44 ` [PATCH v3 03/21] drm/exynos: Declare the DSI encoder as a bridge element Boris Brezillon
@ 2019-11-24 10:24   ` Laurent Pinchart
  2019-11-24 13:17     ` Boris Brezillon
  0 siblings, 1 reply; 76+ messages in thread
From: Laurent Pinchart @ 2019-11-24 10:24 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:44:54PM +0200, Boris Brezillon wrote:
> Encoder drivers will progressively transition to the drm_bridge
> interface in place of the drm_encoder one.
> 
> Converting the Exynos DSI encoder driver to this approach allows us to
> use the ->pre_{enable,disable}()  hooks and get rid of the hack
> resetting encoder->bridge.next (which was needed to control the
> encoder/bridge enable/disable sequence).
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> Changes in v3:
> * Embed a drm_bridge object in exynos_dsi since drm_encoder no longer
>   has a dummy bridge
> 
> Changes in v2:
> * New patch (replacement for "drm/exynos: Get rid of exynos_dsi->out_bridge")
> ---
>  drivers/gpu/drm/exynos/exynos_drm_dsi.c | 89 +++++++++++++++----------
>  1 file changed, 55 insertions(+), 34 deletions(-)
> 
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> index 72726f2c7a9f..3915f50b005e 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> @@ -252,10 +252,10 @@ struct exynos_dsi_driver_data {
>  
>  struct exynos_dsi {
>  	struct drm_encoder encoder;
> +	struct drm_bridge bridge;
>  	struct mipi_dsi_host dsi_host;
>  	struct drm_connector connector;
>  	struct drm_panel *panel;
> -	struct drm_bridge *out_bridge;
>  	struct device *dev;
>  
>  	void __iomem *reg_base;
> @@ -291,6 +291,11 @@ static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e)
>  	return container_of(e, struct exynos_dsi, encoder);
>  }
>  
> +static inline struct exynos_dsi *bridge_to_dsi(struct drm_bridge *b)
> +{
> +	return container_of(b, struct exynos_dsi, bridge);
> +}
> +
>  enum reg_idx {
>  	DSIM_STATUS_REG,	/* Status register */
>  	DSIM_SWRST_REG,		/* Software reset register */
> @@ -1374,25 +1379,38 @@ static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
>  	}
>  }
>  
> -static void exynos_dsi_enable(struct drm_encoder *encoder)
> +static void exynos_dsi_pre_enable(struct drm_bridge *bridge)
>  {
> -	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
> +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
>  	int ret;
>  
>  	if (dsi->state & DSIM_STATE_ENABLED)
>  		return;

This can probably be removed now as the core should ensure that
double-enable or double-disable never occurs, but it can be done in a
separate patch.

>  
>  	pm_runtime_get_sync(dsi->dev);
> -	dsi->state |= DSIM_STATE_ENABLED;
>  
>  	if (dsi->panel) {
>  		ret = drm_panel_prepare(dsi->panel);
>  		if (ret < 0)
>  			goto err_put_sync;
> -	} else {
> -		drm_bridge_pre_enable(dsi->out_bridge);
>  	}

It would be nice to switch to the drm panel bridge, but that can also be
done on top of this series.

>  
> +	dsi->state |= DSIM_STATE_ENABLED;
> +	return;
> +
> +err_put_sync:
> +	pm_runtime_put(dsi->dev);
> +}
> +
> +static void exynos_dsi_enable(struct drm_bridge *bridge)
> +{
> +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
> +	int ret;
> +
> +	if (!(dsi->state & DSIM_STATE_ENABLED) ||
> +	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> +		return;
> +
>  	exynos_dsi_set_display_mode(dsi);
>  	exynos_dsi_set_display_enable(dsi, true);
>  
> @@ -1400,8 +1418,6 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
>  		ret = drm_panel_enable(dsi->panel);
>  		if (ret < 0)
>  			goto err_display_disable;
> -	} else {
> -		drm_bridge_enable(dsi->out_bridge);
>  	}
>  
>  	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
> @@ -1410,28 +1426,30 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
>  err_display_disable:
>  	exynos_dsi_set_display_enable(dsi, false);
>  	drm_panel_unprepare(dsi->panel);

Does this belong here, as drm_panel_prepare() was called in
exynos_dsi_pre_enable() ?

> -
> -err_put_sync:
> -	dsi->state &= ~DSIM_STATE_ENABLED;
> -	pm_runtime_put(dsi->dev);
>  }
>  
> -static void exynos_dsi_disable(struct drm_encoder *encoder)
> +static void exynos_dsi_disable(struct drm_bridge *bridge)
>  {
> -	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
> +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
> +
> +	if (!(dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> +		return;
> +
> +	drm_panel_disable(dsi->panel);
> +	exynos_dsi_set_display_enable(dsi, false);
> +	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> +}
> +
> +static void exynos_dsi_post_disable(struct drm_bridge *bridge)
> +{
> +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
>  
>  	if (!(dsi->state & DSIM_STATE_ENABLED))
>  		return;
>  
> -	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> -
> -	drm_panel_disable(dsi->panel);
> -	drm_bridge_disable(dsi->out_bridge);
> -	exynos_dsi_set_display_enable(dsi, false);
>  	drm_panel_unprepare(dsi->panel);
> -	drm_bridge_post_disable(dsi->out_bridge);
> -	dsi->state &= ~DSIM_STATE_ENABLED;
>  	pm_runtime_put_sync(dsi->dev);
> +	dsi->state &= ~DSIM_STATE_ENABLED;
>  }
>  
>  static enum drm_connector_status
> @@ -1499,9 +1517,11 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder)
>  	return 0;
>  }
>  
> -static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = {
> +static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
> +	.pre_enable = exynos_dsi_pre_enable,
>  	.enable = exynos_dsi_enable,
>  	.disable = exynos_dsi_disable,
> +	.post_disable = exynos_dsi_post_disable,
>  };
>  
>  static const struct drm_encoder_funcs exynos_dsi_encoder_funcs = {
> @@ -1520,9 +1540,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>  
>  	out_bridge  = of_drm_find_bridge(device->dev.of_node);
>  	if (out_bridge) {
> -		drm_bridge_attach(encoder, out_bridge, NULL);
> -		dsi->out_bridge = out_bridge;
> -		encoder->bridge = NULL;
> +		drm_bridge_attach(encoder, out_bridge, &dsi->bridge);
>  	} else {
>  		int ret = exynos_dsi_create_connector(encoder);
>  
> @@ -1575,19 +1593,19 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
>  				  struct mipi_dsi_device *device)
>  {
>  	struct exynos_dsi *dsi = host_to_dsi(host);
> +	struct drm_bridge *out_bridge = dsi->bridge.next;
>  	struct drm_device *drm = dsi->encoder.dev;
>  
>  	if (dsi->panel) {
>  		mutex_lock(&drm->mode_config.mutex);
> -		exynos_dsi_disable(&dsi->encoder);
> +		exynos_dsi_disable(&dsi->bridge);
> +		exynos_dsi_post_disable(&dsi->bridge);
>  		drm_panel_detach(dsi->panel);
>  		dsi->panel = NULL;
>  		dsi->connector.status = connector_status_disconnected;
>  		mutex_unlock(&drm->mode_config.mutex);
> -	} else {
> -		if (dsi->out_bridge->funcs->detach)
> -			dsi->out_bridge->funcs->detach(dsi->out_bridge);
> -		dsi->out_bridge = NULL;
> +	} else if (out_bridge && out_bridge->funcs->detach) {
> +		out_bridge->funcs->detach(out_bridge);

Maybe drm_bridge_detach() ?

>  	}
>  
>  	if (drm->mode_config.poll_enabled)
> @@ -1687,16 +1705,18 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
>  	drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs,
>  			 DRM_MODE_ENCODER_TMDS, NULL);
>  
> -	drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
> -
>  	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
>  	if (ret < 0)
>  		return ret;
>  
> +	/* Declare ourself as the first bridge element. */
> +	dsi->bridge.funcs = &exynos_dsi_bridge_funcs;
> +	drm_bridge_attach(encoder, &dsi->bridge, NULL);
> +
>  	if (dsi->in_bridge_node) {
>  		in_bridge = of_drm_find_bridge(dsi->in_bridge_node);
>  		if (in_bridge)
> -			drm_bridge_attach(encoder, in_bridge, NULL);
> +			drm_bridge_attach(encoder, in_bridge, &dsi->bridge);
>  	}

Same as for patch 01/21, maybe this could be moved to this bridge's
attach operation ? Actually, now that I've read the code, this in_bridge
part looks weird. Why would the DSI encoder have an input bridge that is
has to manage itself ?

I don't feel confident enough to ack this patch. It goes in the right
direction as far as the API evolution is concerned, so if you get an ack
from the Exynos maintainers, I'm happy enough to see it merged.

>  
>  	return mipi_dsi_host_register(&dsi->dsi_host);
> @@ -1708,7 +1728,8 @@ static void exynos_dsi_unbind(struct device *dev, struct device *master,
>  	struct drm_encoder *encoder = dev_get_drvdata(dev);
>  	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
>  
> -	exynos_dsi_disable(encoder);
> +	exynos_dsi_disable(&dsi->bridge);
> +	exynos_dsi_post_disable(&dsi->bridge);
>  
>  	mipi_dsi_host_unregister(&dsi->dsi_host);
>  }

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 04/21] drm/bridge: Rename bridge helpers targeting a bridge chain
  2019-10-23 15:44 ` [PATCH v3 04/21] drm/bridge: Rename bridge helpers targeting a bridge chain Boris Brezillon
  2019-10-25 13:26   ` Neil Armstrong
@ 2019-11-24 10:28   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-11-24 10:28 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:44:55PM +0200, Boris Brezillon wrote:
> 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>

I haven't reviewed this in details but the renaming looks sensible, so

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

> ---
> 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/mediatek/mtk_hdmi.c |   4 +-
>  include/drm/drm_bridge.h            |  64 +++++++-------
>  5 files changed, 112 insertions(+), 102 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/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> index c79b1f855d89..ea68b5adccbe 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/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);

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 05/21] drm/bridge: Introduce drm_bridge_chain_get_next_bridge()
  2019-10-23 15:44 ` [PATCH v3 05/21] drm/bridge: Introduce drm_bridge_chain_get_next_bridge() Boris Brezillon
  2019-10-25 13:27   ` Neil Armstrong
@ 2019-11-24 10:33   ` Laurent Pinchart
  2019-11-24 10:56     ` Boris Brezillon
  1 sibling, 1 reply; 76+ messages in thread
From: Laurent Pinchart @ 2019-11-24 10:33 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:44:56PM +0200, Boris Brezillon wrote:
> 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>
> ---
> 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/exynos/exynos_drm_dsi.c |  3 ++-
>  drivers/gpu/drm/mediatek/mtk_hdmi.c     |  6 ++++--
>  drivers/gpu/drm/omapdrm/omap_drv.c      |  4 ++--
>  drivers/gpu/drm/omapdrm/omap_encoder.c  |  3 ++-
>  drivers/gpu/drm/vc4/vc4_dsi.c           |  4 +++-
>  include/drm/drm_bridge.h                | 13 +++++++++++++
>  6 files changed, 26 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> index 3915f50b005e..005c67894b78 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> @@ -1593,9 +1593,10 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
>  				  struct mipi_dsi_device *device)
>  {
>  	struct exynos_dsi *dsi = host_to_dsi(host);
> -	struct drm_bridge *out_bridge = dsi->bridge.next;
>  	struct drm_device *drm = dsi->encoder.dev;
> +	struct drm_bridge *out_bridge;
>  
> +	out_bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);

You may want to store this in the exynos_dsi structure in the previous
patch where you rework this driver.

>  	if (dsi->panel) {
>  		mutex_lock(&drm->mode_config.mutex);
>  		exynos_dsi_disable(&dsi->bridge);
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> index ea68b5adccbe..cfaa5aab8876 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_chain_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..865164fe28dc 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_chain_get_next_bridge(bridge))
> +			bridge = drm_bridge_chain_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..8ca54081997e 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_chain_get_next_bridge(bridge)) {

A for_each_bridge() macro would be nice (in a separate patch). It could
be used in omap_drv.c above too.

>  		if (!bridge->timings)
>  			continue;
>  
> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index 49f8a313e759..49c47185aff0 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> @@ -1644,8 +1644,10 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>  	struct drm_device *drm = dev_get_drvdata(master);
>  	struct vc4_dev *vc4 = to_vc4_dev(drm);
>  	struct vc4_dsi *dsi = dev_get_drvdata(dev);
> +	struct drm_bridge *bridge;
>  
> -	if (dsi->bridge.next)
> +	bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
> +	if (bridge)
>  		pm_runtime_disable(dev);
>  
>  	vc4_dsi_encoder_destroy(dsi->encoder);
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index 726435baf4ad..8aeba83fcf31 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_chain_get_next_bridge() - Get the next bridge in the chain
> + * @bridge: bridge object
> + *
> + * RETURNS:
> + * the next bridge in the chain, or NULL if @bridge is the last.

Maybe "the next bridge in the chain after @bridge, ..." ?

> + */
> +static inline struct drm_bridge *
> +drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge)

Technically speaking this doesn't operate on a chain but on a bridge, so
I'd name is drm_bridge_get_next_bridge(). I will not insist to the way
of nacking the series for this, so with the rename, but also without,

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

> +{
> +	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);

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 06/21] drm: Stop accessing encoder->bridge directly
  2019-10-23 15:44 ` [PATCH v3 06/21] drm: Stop accessing encoder->bridge directly Boris Brezillon
  2019-10-25 13:28   ` Neil Armstrong
@ 2019-11-24 10:39   ` Laurent Pinchart
  2019-11-24 13:40     ` Boris Brezillon
  1 sibling, 1 reply; 76+ messages in thread
From: Laurent Pinchart @ 2019-11-24 10:39 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:44:57PM +0200, Boris Brezillon wrote:
> 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.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c    | 25 +++++++++++++++++--------
>  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 | 11 ++++++++---
>  include/drm/drm_bridge.h               | 15 +++++++++++++++
>  6 files changed, 53 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index cf678be58fa4..f02ddffd4960 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,7 +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,
> +		drm_atomic_bridge_chain_post_disable(bridge,
>  						     old_state);

This now fits on a single line.

>  	}
>  
> @@ -1190,6 +1196,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 +1224,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 +1324,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 +1343,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 +1355,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 80d88a55302e..4fe9e723e227 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 = bridge->next;

Shouldn't this have been addressed in the previous patch ?

>  			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..7a1f1e5f0326 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,15 @@ 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;
> +

Extra blank line.

With those small issues fixed,

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

>  
>  		/*
>  		 * 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 8aeba83fcf31..27eef63ce0ff 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_chain_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);

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 01/21] drm/vc4: Declare the DSI encoder as a bridge element
  2019-11-24 10:01   ` Laurent Pinchart
@ 2019-11-24 10:47     ` Boris Brezillon
  0 siblings, 0 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-11-24 10:47 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

On Sun, 24 Nov 2019 12:01:30 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> Thank you for the patch.
> 
> On Wed, Oct 23, 2019 at 05:44:52PM +0200, Boris Brezillon wrote:
> > Encoder drivers will progressively transition to the drm_bridge
> > interface in place of the drm_encoder one.
> > 
> > Let's start with the VC4 driver, and use the ->pre_{enable,disable}()
> > hooks to get rid of the hack resetting encoder->bridge.next (which was
> > needed to control the encoder/bridge enable/disable sequence).
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > ---
> > Changes in v3:
> > * Embed a drm_bridge object in vc4_dsi since drm_encoder no longer has
> >   a dummy bridge
> > 
> > Changes in v2:
> > * New patch (replaces "drm/vc4: Get rid of the dsi->bridge field")
> > ---
> >  drivers/gpu/drm/vc4/vc4_dsi.c | 88 +++++++++++++++++++++--------------
> >  1 file changed, 52 insertions(+), 36 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> > index c9ba83ed49b9..49f8a313e759 100644
> > --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> > +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> > @@ -498,7 +498,11 @@ struct vc4_dsi {
> >  
> >  	struct mipi_dsi_host dsi_host;
> >  	struct drm_encoder *encoder;
> > -	struct drm_bridge *bridge;
> > +
> > +	/* Embed a bridge object so we can implement bridge funcs instead of
> > +	 * encoder ones.
> > +	 */
> > +	struct drm_bridge bridge;
> >  
> >  	void __iomem *regs;
> >  
> > @@ -543,6 +547,11 @@ struct vc4_dsi {
> >  	struct debugfs_regset32 regset;
> >  };
> >  
> > +static inline struct vc4_dsi *bridge_to_vc4_dsi(struct drm_bridge *bridge)
> > +{
> > +	return container_of(bridge, struct vc4_dsi, bridge);
> > +}
> > +
> >  #define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host)
> >  
> >  static inline void
> > @@ -747,16 +756,11 @@ dsi_esc_timing(u32 ns)
> >  	return DIV_ROUND_UP(ns, ESC_TIME_NS);
> >  }
> >  
> > -static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
> > +static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge)
> >  {
> > -	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
> > -	struct vc4_dsi *dsi = vc4_encoder->dsi;
> > +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
> >  	struct device *dev = &dsi->pdev->dev;
> >  
> > -	drm_bridge_disable(dsi->bridge);
> > -	vc4_dsi_ulps(dsi, true);
> > -	drm_bridge_post_disable(dsi->bridge);
> > -
> >  	clk_disable_unprepare(dsi->pll_phy_clock);
> >  	clk_disable_unprepare(dsi->escape_clock);
> >  	clk_disable_unprepare(dsi->pixel_clock);
> > @@ -764,6 +768,13 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
> >  	pm_runtime_put(dev);
> >  }
> >  
> > +static void vc4_dsi_bridge_disable(struct drm_bridge *bridge)
> > +{
> > +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
> > +
> > +	vc4_dsi_ulps(dsi, true);
> > +}
> > +
> >  /* Extends the mode's blank intervals to handle BCM2835's integer-only
> >   * DSI PLL divider.
> >   *
> > @@ -777,12 +788,11 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
> >   * higher-than-expected clock rate to the panel, but that's what the
> >   * firmware does too.
> >   */
> > -static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
> > -				       const struct drm_display_mode *mode,
> > -				       struct drm_display_mode *adjusted_mode)
> > +static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
> > +				      const struct drm_display_mode *mode,
> > +				      struct drm_display_mode *adjusted_mode)
> >  {
> > -	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
> > -	struct vc4_dsi *dsi = vc4_encoder->dsi;
> > +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
> >  	struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock);
> >  	unsigned long parent_rate = clk_get_rate(phy_parent);
> >  	unsigned long pixel_clock_hz = mode->clock * 1000;
> > @@ -816,11 +826,11 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
> >  	return true;
> >  }
> >  
> > -static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
> > +static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge)
> >  {
> > +	struct drm_encoder *encoder = bridge->encoder;
> >  	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> > -	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
> > -	struct vc4_dsi *dsi = vc4_encoder->dsi;
> > +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
> >  	struct device *dev = &dsi->pdev->dev;
> >  	bool debug_dump_regs = false;
> >  	unsigned long hs_clock;
> > @@ -1054,8 +1064,12 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
> >  	}
> >  
> >  	vc4_dsi_ulps(dsi, false);
> > +}
> >  
> > -	drm_bridge_pre_enable(dsi->bridge);  
> 
> If I'm not mistaken this switches the order of the DSI's encoder
> pre-enable and the next bridge's pre-enable. I think it's true for
> post-disable too. It may not be a problem, but have this been tested ?


No, it hasn't been tested (I don't have a Rpi with a DSI panel at
hand), and now that you mention it, I don't think it will work (I had
forgotten drm_bridge_pre_enable() iterates the bridge chain in reverse
order).
Well, it will work if the DSI encoder is connected to the RPi DSI panel
since ->prepare() doesn't do anything, but I see that some DSI panel
drivers send DSI commands in their ->prepare() method (BTW, we should
really document the fact that panel drivers can send DSI commands in
the ->prepare() hook).

The only way this can be fixed is by putting the
vc4_dsi_pre_enable/post_disable() code in runtime PM resume/suspend
hooks and let the vc4_dsi_host_transfer() call pm_runtime_get_sync(),
pm_runtime_put() every time a msg is sent.

> 
> > +static void vc4_dsi_bridge_enable(struct drm_bridge *bridge)
> > +{
> > +	struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
> > +	bool debug_dump_regs = false;
> >  
> >  	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> >  		DSI_PORT_WRITE(DISP0_CTRL,
> > @@ -1072,8 +1086,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
> >  			       DSI_DISP0_ENABLE);
> >  	}
> >  
> > -	drm_bridge_enable(dsi->bridge);
> > -
> >  	if (debug_dump_regs) {
> >  		struct drm_printer p = drm_info_printer(&dsi->pdev->dev);
> >  		dev_info(&dsi->pdev->dev, "DSI regs after:\n");
> > @@ -1290,10 +1302,12 @@ static const struct mipi_dsi_host_ops vc4_dsi_host_ops = {
> >  	.transfer = vc4_dsi_host_transfer,
> >  };
> >  
> > -static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = {
> > -	.disable = vc4_dsi_encoder_disable,
> > -	.enable = vc4_dsi_encoder_enable,
> > -	.mode_fixup = vc4_dsi_encoder_mode_fixup,
> > +static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = {
> > +	.pre_enable = vc4_dsi_bridge_pre_enable,
> > +	.enable = vc4_dsi_bridge_enable,
> > +	.disable = vc4_dsi_bridge_disable,
> > +	.post_disable = vc4_dsi_bridge_post_disable,
> > +	.mode_fixup = vc4_dsi_bridge_mode_fixup,
> >  };
> >  
> >  static const struct of_device_id vc4_dsi_dt_match[] = {
> > @@ -1445,6 +1459,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
> >  	struct vc4_dev *vc4 = to_vc4_dev(drm);
> >  	struct vc4_dsi *dsi = dev_get_drvdata(dev);
> >  	struct vc4_dsi_encoder *vc4_dsi_encoder;
> > +	struct drm_bridge *next_bridge;
> >  	struct drm_panel *panel;
> >  	const struct of_device_id *match;
> >  	dma_cap_mask_t dma_mask;
> > @@ -1561,7 +1576,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
> >  	}
> >  
> >  	ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
> > -					  &panel, &dsi->bridge);
> > +					  &panel, &next_bridge);
> >  	if (ret) {
> >  		/* If the bridge or panel pointed by dev->of_node is not
> >  		 * enabled, just return 0 here so that we don't prevent the DRM
> > @@ -1576,10 +1591,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
> >  	}
> >  
> >  	if (panel) {
> > -		dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel,
> > +		next_bridge = devm_drm_panel_bridge_add_typed(dev, panel,
> >  							      DRM_MODE_CONNECTOR_DSI);
> > -		if (IS_ERR(dsi->bridge))
> > -			return PTR_ERR(dsi->bridge);
> > +		if (IS_ERR(next_bridge))
> > +			return PTR_ERR(next_bridge);
> >  	}
> >  
> >  	/* The esc clock rate is supposed to always be 100Mhz. */
> > @@ -1598,19 +1613,20 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
> >  
> >  	drm_encoder_init(drm, dsi->encoder, &vc4_dsi_encoder_funcs,
> >  			 DRM_MODE_ENCODER_DSI, NULL);
> > -	drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs);
> >  
> > -	ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL);
> > +	/* Declare ourself as the first bridge element. */
> > +	dsi->bridge.funcs = &vc4_dsi_bridge_funcs;
> > +	ret = drm_bridge_attach(dsi->encoder, &dsi->bridge, NULL);
> > +	if (ret) {
> > +		dev_err(dev, "bridge attach failed: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = drm_bridge_attach(dsi->encoder, next_bridge, &dsi->bridge);
> >  	if (ret) {
> >  		dev_err(dev, "bridge attach failed: %d\n", ret);
> >  		return ret;
> >  	}  
> 
> This is usually done in the bridge attach operation. As we're in control
> we can attach the next bridge here, but I think the driver would look
> more standard if you moved the second attach call to this bridge's
> attach operation.

I agree.

> 
> With this fixed, and if the driver has been tested and the
> enable/disable order change doesn't cause issues,
> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> > -	/* Disable the atomic helper calls into the bridge.  We
> > -	 * manually call the bridge pre_enable / enable / etc. calls
> > -	 * from our driver, since we need to sequence them within the
> > -	 * encoder's enable/disable paths.
> > -	 */
> > -	dsi->encoder->bridge = NULL;
> >  
> >  	if (dsi->port == 0)
> >  		vc4_debugfs_add_regset32(drm, "dsi0_regs", &dsi->regset);
> > @@ -1629,7 +1645,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
> >  	struct vc4_dev *vc4 = to_vc4_dev(drm);
> >  	struct vc4_dsi *dsi = dev_get_drvdata(dev);
> >  
> > -	if (dsi->bridge)
> > +	if (dsi->bridge.next)
> >  		pm_runtime_disable(dev);
> >  
> >  	vc4_dsi_encoder_destroy(dsi->encoder);  
> 


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

* Re: [PATCH v3 05/21] drm/bridge: Introduce drm_bridge_chain_get_next_bridge()
  2019-11-24 10:33   ` Laurent Pinchart
@ 2019-11-24 10:56     ` Boris Brezillon
  2019-11-24 14:04       ` Laurent Pinchart
  0 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-11-24 10:56 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

On Sun, 24 Nov 2019 12:33:35 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> Thank you for the patch.
> 
> On Wed, Oct 23, 2019 at 05:44:56PM +0200, Boris Brezillon wrote:
> > 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>
> > ---
> > 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/exynos/exynos_drm_dsi.c |  3 ++-
> >  drivers/gpu/drm/mediatek/mtk_hdmi.c     |  6 ++++--
> >  drivers/gpu/drm/omapdrm/omap_drv.c      |  4 ++--
> >  drivers/gpu/drm/omapdrm/omap_encoder.c  |  3 ++-
> >  drivers/gpu/drm/vc4/vc4_dsi.c           |  4 +++-
> >  include/drm/drm_bridge.h                | 13 +++++++++++++
> >  6 files changed, 26 insertions(+), 7 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > index 3915f50b005e..005c67894b78 100644
> > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > @@ -1593,9 +1593,10 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
> >  				  struct mipi_dsi_device *device)
> >  {
> >  	struct exynos_dsi *dsi = host_to_dsi(host);
> > -	struct drm_bridge *out_bridge = dsi->bridge.next;
> >  	struct drm_device *drm = dsi->encoder.dev;
> > +	struct drm_bridge *out_bridge;
> >  
> > +	out_bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);  
> 
> You may want to store this in the exynos_dsi structure in the previous
> patch where you rework this driver.

Do we really need to store it there, since we already have a simple way
to retrieve the next bridge in the chain?

> 
> >  	if (dsi->panel) {
> >  		mutex_lock(&drm->mode_config.mutex);
> >  		exynos_dsi_disable(&dsi->bridge);
> > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> > index ea68b5adccbe..cfaa5aab8876 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_chain_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..865164fe28dc 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_chain_get_next_bridge(bridge))
> > +			bridge = drm_bridge_chain_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..8ca54081997e 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_chain_get_next_bridge(bridge)) {  
> 
> A for_each_bridge() macro would be nice (in a separate patch). It could
> be used in omap_drv.c above too.

It's coming later in the series (patch 8).

> 
> >  		if (!bridge->timings)
> >  			continue;
> >  
> > diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> > index 49f8a313e759..49c47185aff0 100644
> > --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> > +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> > @@ -1644,8 +1644,10 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
> >  	struct drm_device *drm = dev_get_drvdata(master);
> >  	struct vc4_dev *vc4 = to_vc4_dev(drm);
> >  	struct vc4_dsi *dsi = dev_get_drvdata(dev);
> > +	struct drm_bridge *bridge;
> >  
> > -	if (dsi->bridge.next)
> > +	bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
> > +	if (bridge)
> >  		pm_runtime_disable(dev);
> >  
> >  	vc4_dsi_encoder_destroy(dsi->encoder);
> > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > index 726435baf4ad..8aeba83fcf31 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_chain_get_next_bridge() - Get the next bridge in the chain
> > + * @bridge: bridge object
> > + *
> > + * RETURNS:
> > + * the next bridge in the chain, or NULL if @bridge is the last.  
> 
> Maybe "the next bridge in the chain after @bridge, ..." ?

Agreed.

> 
> > + */
> > +static inline struct drm_bridge *
> > +drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge)  
> 
> Technically speaking this doesn't operate on a chain but on a bridge, so
> I'd name is drm_bridge_get_next_bridge(). I will not insist to the way
> of nacking the series for this, so with the rename, but also without,

You're absolutely right, I'll rename the function as suggested.

> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> > +{
> > +	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);  
> 


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

* Re: [PATCH v3 03/21] drm/exynos: Declare the DSI encoder as a bridge element
  2019-11-24 10:24   ` Laurent Pinchart
@ 2019-11-24 13:17     ` Boris Brezillon
  2019-11-24 13:28       ` Boris Brezillon
  2019-11-24 14:02       ` Laurent Pinchart
  0 siblings, 2 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-11-24 13:17 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

Hi Laurent,

On Sun, 24 Nov 2019 12:24:33 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> Thank you for the patch.
> 
> On Wed, Oct 23, 2019 at 05:44:54PM +0200, Boris Brezillon wrote:
> > Encoder drivers will progressively transition to the drm_bridge
> > interface in place of the drm_encoder one.
> > 
> > Converting the Exynos DSI encoder driver to this approach allows us to
> > use the ->pre_{enable,disable}()  hooks and get rid of the hack
> > resetting encoder->bridge.next (which was needed to control the
> > encoder/bridge enable/disable sequence).
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > ---
> > Changes in v3:
> > * Embed a drm_bridge object in exynos_dsi since drm_encoder no longer
> >   has a dummy bridge
> > 
> > Changes in v2:
> > * New patch (replacement for "drm/exynos: Get rid of exynos_dsi->out_bridge")
> > ---
> >  drivers/gpu/drm/exynos/exynos_drm_dsi.c | 89 +++++++++++++++----------
> >  1 file changed, 55 insertions(+), 34 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > index 72726f2c7a9f..3915f50b005e 100644
> > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > @@ -252,10 +252,10 @@ struct exynos_dsi_driver_data {
> >  
> >  struct exynos_dsi {
> >  	struct drm_encoder encoder;
> > +	struct drm_bridge bridge;
> >  	struct mipi_dsi_host dsi_host;
> >  	struct drm_connector connector;
> >  	struct drm_panel *panel;
> > -	struct drm_bridge *out_bridge;
> >  	struct device *dev;
> >  
> >  	void __iomem *reg_base;
> > @@ -291,6 +291,11 @@ static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e)
> >  	return container_of(e, struct exynos_dsi, encoder);
> >  }
> >  
> > +static inline struct exynos_dsi *bridge_to_dsi(struct drm_bridge *b)
> > +{
> > +	return container_of(b, struct exynos_dsi, bridge);
> > +}
> > +
> >  enum reg_idx {
> >  	DSIM_STATUS_REG,	/* Status register */
> >  	DSIM_SWRST_REG,		/* Software reset register */
> > @@ -1374,25 +1379,38 @@ static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
> >  	}
> >  }
> >  
> > -static void exynos_dsi_enable(struct drm_encoder *encoder)
> > +static void exynos_dsi_pre_enable(struct drm_bridge *bridge)
> >  {
> > -	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
> > +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
> >  	int ret;
> >  
> >  	if (dsi->state & DSIM_STATE_ENABLED)
> >  		return;  
> 
> This can probably be removed now as the core should ensure that
> double-enable or double-disable never occurs, but it can be done in a
> separate patch.

Except the enable/disable() implementations handle failures (the
framework does not expect those to fails BTW), and I guess it's
important to know the actual HW state in order to keep runtime PM
get/put calls balanced.

> 
> >  
> >  	pm_runtime_get_sync(dsi->dev);
> > -	dsi->state |= DSIM_STATE_ENABLED;
> >  
> >  	if (dsi->panel) {
> >  		ret = drm_panel_prepare(dsi->panel);
> >  		if (ret < 0)
> >  			goto err_put_sync;
> > -	} else {
> > -		drm_bridge_pre_enable(dsi->out_bridge);
> >  	}  
> 
> It would be nice to switch to the drm panel bridge, but that can also be
> done on top of this series.

I agree, just didn't want to add more stuff to this series.

> 
> >  
> > +	dsi->state |= DSIM_STATE_ENABLED;
> > +	return;
> > +
> > +err_put_sync:
> > +	pm_runtime_put(dsi->dev);
> > +}
> > +
> > +static void exynos_dsi_enable(struct drm_bridge *bridge)
> > +{
> > +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
> > +	int ret;
> > +
> > +	if (!(dsi->state & DSIM_STATE_ENABLED) ||
> > +	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> > +		return;
> > +
> >  	exynos_dsi_set_display_mode(dsi);
> >  	exynos_dsi_set_display_enable(dsi, true);
> >  
> > @@ -1400,8 +1418,6 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
> >  		ret = drm_panel_enable(dsi->panel);
> >  		if (ret < 0)
> >  			goto err_display_disable;
> > -	} else {
> > -		drm_bridge_enable(dsi->out_bridge);
> >  	}
> >  
> >  	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
> > @@ -1410,28 +1426,30 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
> >  err_display_disable:
> >  	exynos_dsi_set_display_enable(dsi, false);
> >  	drm_panel_unprepare(dsi->panel);  
> 
> Does this belong here, as drm_panel_prepare() was called in
> exynos_dsi_pre_enable() ?

Nope, this one should be dropped.

> 
> > -
> > -err_put_sync:
> > -	dsi->state &= ~DSIM_STATE_ENABLED;
> > -	pm_runtime_put(dsi->dev);
> >  }
> >  
> > -static void exynos_dsi_disable(struct drm_encoder *encoder)
> > +static void exynos_dsi_disable(struct drm_bridge *bridge)
> >  {
> > -	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
> > +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
> > +
> > +	if (!(dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> > +		return;
> > +
> > +	drm_panel_disable(dsi->panel);
> > +	exynos_dsi_set_display_enable(dsi, false);
> > +	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> > +}
> > +
> > +static void exynos_dsi_post_disable(struct drm_bridge *bridge)
> > +{
> > +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
> >  
> >  	if (!(dsi->state & DSIM_STATE_ENABLED))
> >  		return;
> >  
> > -	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> > -
> > -	drm_panel_disable(dsi->panel);
> > -	drm_bridge_disable(dsi->out_bridge);
> > -	exynos_dsi_set_display_enable(dsi, false);
> >  	drm_panel_unprepare(dsi->panel);
> > -	drm_bridge_post_disable(dsi->out_bridge);
> > -	dsi->state &= ~DSIM_STATE_ENABLED;
> >  	pm_runtime_put_sync(dsi->dev);
> > +	dsi->state &= ~DSIM_STATE_ENABLED;
> >  }
> >  
> >  static enum drm_connector_status
> > @@ -1499,9 +1517,11 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder)
> >  	return 0;
> >  }
> >  
> > -static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = {
> > +static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
> > +	.pre_enable = exynos_dsi_pre_enable,
> >  	.enable = exynos_dsi_enable,
> >  	.disable = exynos_dsi_disable,
> > +	.post_disable = exynos_dsi_post_disable,
> >  };
> >  
> >  static const struct drm_encoder_funcs exynos_dsi_encoder_funcs = {
> > @@ -1520,9 +1540,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
> >  
> >  	out_bridge  = of_drm_find_bridge(device->dev.of_node);
> >  	if (out_bridge) {
> > -		drm_bridge_attach(encoder, out_bridge, NULL);
> > -		dsi->out_bridge = out_bridge;
> > -		encoder->bridge = NULL;
> > +		drm_bridge_attach(encoder, out_bridge, &dsi->bridge);
> >  	} else {
> >  		int ret = exynos_dsi_create_connector(encoder);
> >  
> > @@ -1575,19 +1593,19 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
> >  				  struct mipi_dsi_device *device)
> >  {
> >  	struct exynos_dsi *dsi = host_to_dsi(host);
> > +	struct drm_bridge *out_bridge = dsi->bridge.next;
> >  	struct drm_device *drm = dsi->encoder.dev;
> >  
> >  	if (dsi->panel) {
> >  		mutex_lock(&drm->mode_config.mutex);
> > -		exynos_dsi_disable(&dsi->encoder);
> > +		exynos_dsi_disable(&dsi->bridge);
> > +		exynos_dsi_post_disable(&dsi->bridge);
> >  		drm_panel_detach(dsi->panel);
> >  		dsi->panel = NULL;
> >  		dsi->connector.status = connector_status_disconnected;
> >  		mutex_unlock(&drm->mode_config.mutex);
> > -	} else {
> > -		if (dsi->out_bridge->funcs->detach)
> > -			dsi->out_bridge->funcs->detach(dsi->out_bridge);
> > -		dsi->out_bridge = NULL;
> > +	} else if (out_bridge && out_bridge->funcs->detach) {
> > +		out_bridge->funcs->detach(out_bridge);  
> 
> Maybe drm_bridge_detach() ?

This function is not exported, and I suppose that's why they used the
function pointer in this driver. I bet there's a good reason for not
exposing this function...

> 
> >  	}
> >  
> >  	if (drm->mode_config.poll_enabled)
> > @@ -1687,16 +1705,18 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
> >  	drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs,
> >  			 DRM_MODE_ENCODER_TMDS, NULL);
> >  
> > -	drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
> > -
> >  	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
> >  	if (ret < 0)
> >  		return ret;
> >  
> > +	/* Declare ourself as the first bridge element. */
> > +	dsi->bridge.funcs = &exynos_dsi_bridge_funcs;
> > +	drm_bridge_attach(encoder, &dsi->bridge, NULL);
> > +
> >  	if (dsi->in_bridge_node) {
> >  		in_bridge = of_drm_find_bridge(dsi->in_bridge_node);
> >  		if (in_bridge)
> > -			drm_bridge_attach(encoder, in_bridge, NULL);
> > +			drm_bridge_attach(encoder, in_bridge, &dsi->bridge);
> >  	}  
> 
> Same as for patch 01/21, maybe this could be moved to this bridge's
> attach operation ? Actually, now that I've read the code, this in_bridge
> part looks weird. Why would the DSI encoder have an input bridge that is
> has to manage itself ?

Yes, I know, it doesn't make any sense. Either we're dealing with a
bridge which can be chained to other bridges (can be placed in the
middle of a chain as well), or we're dealing with an encoder which
precedes any bridges. In the latter case (which is how exynos_dsi is
implemented) in_bridge doesn't have any meaning, and that's even worse
since we're placing the so-called input bridge (AKA previous bridge)
after our encoder (that's what drm_bridge_attach(encoder, in_bridge,
NULL) does).

TBH, I didn't want to go that far and fix existing drivers when I
started this series, so I think I'll rework the patchset to get rid of
the VC4 and exynos patches, even if that means having 2 drivers that
mess up with the encoder->bridge_chain list.

Regards,

Boris

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

* Re: [PATCH v3 03/21] drm/exynos: Declare the DSI encoder as a bridge element
  2019-11-24 13:17     ` Boris Brezillon
@ 2019-11-24 13:28       ` Boris Brezillon
  2019-11-24 14:02       ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-11-24 13:28 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

On Sun, 24 Nov 2019 14:17:27 +0100
Boris Brezillon <boris.brezillon@collabora.com> wrote:

> > > @@ -1687,16 +1705,18 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
> > >  	drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs,
> > >  			 DRM_MODE_ENCODER_TMDS, NULL);
> > >  
> > > -	drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
> > > -
> > >  	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
> > >  	if (ret < 0)
> > >  		return ret;
> > >  
> > > +	/* Declare ourself as the first bridge element. */
> > > +	dsi->bridge.funcs = &exynos_dsi_bridge_funcs;
> > > +	drm_bridge_attach(encoder, &dsi->bridge, NULL);
> > > +
> > >  	if (dsi->in_bridge_node) {
> > >  		in_bridge = of_drm_find_bridge(dsi->in_bridge_node);
> > >  		if (in_bridge)
> > > -			drm_bridge_attach(encoder, in_bridge, NULL);
> > > +			drm_bridge_attach(encoder, in_bridge, &dsi->bridge);
> > >  	}  
> > 
> > Same as for patch 01/21, maybe this could be moved to this bridge's
> > attach operation ? Actually, now that I've read the code, this in_bridge
> > part looks weird. Why would the DSI encoder have an input bridge that is
> > has to manage itself ?
> 
> Yes, I know, it doesn't make any sense. Either we're dealing with a
> bridge which can be chained to other bridges (can be placed in the
> middle of a chain as well), or we're dealing with an encoder which
> precedes any bridges. In the latter case (which is how exynos_dsi is
> implemented) in_bridge doesn't have any meaning, and that's even worse
> since we're placing the so-called input bridge (AKA previous bridge)
> after our encoder (that's what drm_bridge_attach(encoder, in_bridge,
> NULL) does).

More on that topic: I checked the exynos dts we have in mainline and
no one is making use of the ports part of the exynos_dsim bindings, so
maybe we should just deprecate it. The other option would be to patch
the driver to act as a real bridge, but I can't do that without someone
testing my changes and I didn't get much feedback from Exynos
maintainers so far...

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

* Re: [PATCH v3 06/21] drm: Stop accessing encoder->bridge directly
  2019-11-24 10:39   ` Laurent Pinchart
@ 2019-11-24 13:40     ` Boris Brezillon
  0 siblings, 0 replies; 76+ messages in thread
From: Boris Brezillon @ 2019-11-24 13:40 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

On Sun, 24 Nov 2019 12:39:26 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> > @@ -1346,7 +1355,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 80d88a55302e..4fe9e723e227 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 = bridge->next;  
> 
> Shouldn't this have been addressed in the previous patch ?
> 

It should and I will update patch 6 accordingly. This being said, it's
not a big deal since the code is patched to use
list_for_each_entry_safe() in patch 7 ;-).

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

* Re: [PATCH v3 07/21] drm/bridge: Make the bridge chain a double-linked list
  2019-10-23 15:44 ` [PATCH v3 07/21] drm/bridge: Make the bridge chain a double-linked list Boris Brezillon
  2019-10-25 13:29   ` Neil Armstrong
@ 2019-11-24 13:57   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-11-24 13:57 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:44:58PM +0200, 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>
> ---
> 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 +---
>  include/drm/drm_bridge.h      |  12 ++-
>  include/drm/drm_encoder.h     |   9 +-
>  4 files changed, 135 insertions(+), 73 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index 54c874493c57..c5cf8a9c4237 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.
>   */
> @@ -114,6 +114,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
>  int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  		      struct drm_bridge *previous)
>  {
> +	LIST_HEAD(tmp_list);
>  	int ret;
>  
>  	if (!encoder || !bridge)
> @@ -127,6 +128,7 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  
>  	bridge->dev = encoder->dev;
>  	bridge->encoder = encoder;
> +	list_add_tail(&bridge->chain_node, &tmp_list);

Couldn't we simplify this by adding the bridge to the list here ? We
would need to remove it in the error path of the attach operation
though, but wouldn't it make the code easier to read, and more efficient
in the most likely path ?

>  
>  	if (bridge->funcs->attach) {
>  		ret = bridge->funcs->attach(bridge);
> @@ -138,9 +140,9 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  	}
>  
>  	if (previous)
> -		previous->next = bridge;
> +		list_splice(&tmp_list, &previous->chain_node);
>  	else
> -		encoder->bridge = bridge;
> +		list_splice(&tmp_list, &encoder->bridge_chain);
>  
>  	return 0;
>  }
> @@ -157,6 +159,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 +193,22 @@ 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 +231,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;
> +
> +	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 (bridge->funcs->mode_valid)
>  		ret = bridge->funcs->mode_valid(bridge, mode);
> +		if (ret != MODE_OK)
> +			return ret;
> +	}
>  
> -	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 +264,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 +294,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 +323,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 +349,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;

You could reuse the bridge parameter instead of declaring a local
variable, the same way you do in drm_atomic_bridge_chain_enable().

> +
>  	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);

Or are you missing the iter == bridge check ?

> +	}
>  }
>  EXPORT_SYMBOL(drm_bridge_chain_pre_enable);
>  
> @@ -345,13 +375,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 +403,23 @@ 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) {

This can hold on a single line.

> +		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 +439,19 @@ 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) {

This too can hold on a single line.

> +		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 +471,23 @@ 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, &bridge->encoder->bridge_chain,
> +				    chain_node) {

As Neil pointed out, &encoder->bridge_chain.

> +		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 +506,19 @@ 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, &bridge->encoder->bridge_chain,

Here too.

By the way, should these bridge chain functions take an encoder instead
of a bridge (in a separate patch series probably) ?

> +				 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 4fe9e723e227..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 = bridge->next;
> -			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/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index 27eef63ce0ff..3ab16c95e59e 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_chain_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);

A list_next_entry_or_null() would be useful in list.h I think (of course
not as a mandatory part of this series :-)).

>  }
>  
>  /**
> @@ -434,7 +437,8 @@ drm_bridge_chain_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..9b3dde177c81 100644
> --- a/include/drm/drm_encoder.h
> +++ b/include/drm/drm_encoder.h
> @@ -172,7 +172,14 @@ struct drm_encoder {
>  	 * &drm_connector_state.crtc.
>  	 */
>  	struct drm_crtc *crtc;
> -	struct drm_bridge *bridge;
> +
> +	/**
> +	 * @bridge_chain: Bridges attached to this encoder. The first entry of
> +	 * this list is always &drm_encoder.bridge. It may be followed by other

There's no &drm_encoder.bridge anymore :-)

With all those small issues fixed,

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

> +	 * bridge entities.
> +	 */
> +	struct list_head bridge_chain;
> +
>  	const struct drm_encoder_funcs *funcs;
>  	const struct drm_encoder_helper_funcs *helper_private;
>  };

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 03/21] drm/exynos: Declare the DSI encoder as a bridge element
  2019-11-24 13:17     ` Boris Brezillon
  2019-11-24 13:28       ` Boris Brezillon
@ 2019-11-24 14:02       ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-11-24 14: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

Hi Boris,

On Sun, Nov 24, 2019 at 02:17:27PM +0100, Boris Brezillon wrote:
> On Sun, 24 Nov 2019 12:24:33 +0200 Laurent Pinchart wrote:
> > On Wed, Oct 23, 2019 at 05:44:54PM +0200, Boris Brezillon wrote:
> > > Encoder drivers will progressively transition to the drm_bridge
> > > interface in place of the drm_encoder one.
> > > 
> > > Converting the Exynos DSI encoder driver to this approach allows us to
> > > use the ->pre_{enable,disable}()  hooks and get rid of the hack
> > > resetting encoder->bridge.next (which was needed to control the
> > > encoder/bridge enable/disable sequence).
> > > 
> > > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > ---
> > > Changes in v3:
> > > * Embed a drm_bridge object in exynos_dsi since drm_encoder no longer
> > >   has a dummy bridge
> > > 
> > > Changes in v2:
> > > * New patch (replacement for "drm/exynos: Get rid of exynos_dsi->out_bridge")
> > > ---
> > >  drivers/gpu/drm/exynos/exynos_drm_dsi.c | 89 +++++++++++++++----------
> > >  1 file changed, 55 insertions(+), 34 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > > index 72726f2c7a9f..3915f50b005e 100644
> > > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > > +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > > @@ -252,10 +252,10 @@ struct exynos_dsi_driver_data {
> > >  
> > >  struct exynos_dsi {
> > >  	struct drm_encoder encoder;
> > > +	struct drm_bridge bridge;
> > >  	struct mipi_dsi_host dsi_host;
> > >  	struct drm_connector connector;
> > >  	struct drm_panel *panel;
> > > -	struct drm_bridge *out_bridge;
> > >  	struct device *dev;
> > >  
> > >  	void __iomem *reg_base;
> > > @@ -291,6 +291,11 @@ static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e)
> > >  	return container_of(e, struct exynos_dsi, encoder);
> > >  }
> > >  
> > > +static inline struct exynos_dsi *bridge_to_dsi(struct drm_bridge *b)
> > > +{
> > > +	return container_of(b, struct exynos_dsi, bridge);
> > > +}
> > > +
> > >  enum reg_idx {
> > >  	DSIM_STATUS_REG,	/* Status register */
> > >  	DSIM_SWRST_REG,		/* Software reset register */
> > > @@ -1374,25 +1379,38 @@ static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
> > >  	}
> > >  }
> > >  
> > > -static void exynos_dsi_enable(struct drm_encoder *encoder)
> > > +static void exynos_dsi_pre_enable(struct drm_bridge *bridge)
> > >  {
> > > -	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
> > > +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
> > >  	int ret;
> > >  
> > >  	if (dsi->state & DSIM_STATE_ENABLED)
> > >  		return;  
> > 
> > This can probably be removed now as the core should ensure that
> > double-enable or double-disable never occurs, but it can be done in a
> > separate patch.
> 
> Except the enable/disable() implementations handle failures (the
> framework does not expect those to fails BTW), and I guess it's
> important to know the actual HW state in order to keep runtime PM
> get/put calls balanced.
> 
> > >  
> > >  	pm_runtime_get_sync(dsi->dev);
> > > -	dsi->state |= DSIM_STATE_ENABLED;
> > >  
> > >  	if (dsi->panel) {
> > >  		ret = drm_panel_prepare(dsi->panel);
> > >  		if (ret < 0)
> > >  			goto err_put_sync;
> > > -	} else {
> > > -		drm_bridge_pre_enable(dsi->out_bridge);
> > >  	}  
> > 
> > It would be nice to switch to the drm panel bridge, but that can also be
> > done on top of this series.
> 
> I agree, just didn't want to add more stuff to this series.
> 
> > >  
> > > +	dsi->state |= DSIM_STATE_ENABLED;
> > > +	return;
> > > +
> > > +err_put_sync:
> > > +	pm_runtime_put(dsi->dev);
> > > +}
> > > +
> > > +static void exynos_dsi_enable(struct drm_bridge *bridge)
> > > +{
> > > +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
> > > +	int ret;
> > > +
> > > +	if (!(dsi->state & DSIM_STATE_ENABLED) ||
> > > +	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> > > +		return;
> > > +
> > >  	exynos_dsi_set_display_mode(dsi);
> > >  	exynos_dsi_set_display_enable(dsi, true);
> > >  
> > > @@ -1400,8 +1418,6 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
> > >  		ret = drm_panel_enable(dsi->panel);
> > >  		if (ret < 0)
> > >  			goto err_display_disable;
> > > -	} else {
> > > -		drm_bridge_enable(dsi->out_bridge);
> > >  	}
> > >  
> > >  	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
> > > @@ -1410,28 +1426,30 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
> > >  err_display_disable:
> > >  	exynos_dsi_set_display_enable(dsi, false);
> > >  	drm_panel_unprepare(dsi->panel);  
> > 
> > Does this belong here, as drm_panel_prepare() was called in
> > exynos_dsi_pre_enable() ?
> 
> Nope, this one should be dropped.
> 
> > > -
> > > -err_put_sync:
> > > -	dsi->state &= ~DSIM_STATE_ENABLED;
> > > -	pm_runtime_put(dsi->dev);
> > >  }
> > >  
> > > -static void exynos_dsi_disable(struct drm_encoder *encoder)
> > > +static void exynos_dsi_disable(struct drm_bridge *bridge)
> > >  {
> > > -	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
> > > +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
> > > +
> > > +	if (!(dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> > > +		return;
> > > +
> > > +	drm_panel_disable(dsi->panel);
> > > +	exynos_dsi_set_display_enable(dsi, false);
> > > +	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> > > +}
> > > +
> > > +static void exynos_dsi_post_disable(struct drm_bridge *bridge)
> > > +{
> > > +	struct exynos_dsi *dsi = bridge_to_dsi(bridge);
> > >  
> > >  	if (!(dsi->state & DSIM_STATE_ENABLED))
> > >  		return;
> > >  
> > > -	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> > > -
> > > -	drm_panel_disable(dsi->panel);
> > > -	drm_bridge_disable(dsi->out_bridge);
> > > -	exynos_dsi_set_display_enable(dsi, false);
> > >  	drm_panel_unprepare(dsi->panel);
> > > -	drm_bridge_post_disable(dsi->out_bridge);
> > > -	dsi->state &= ~DSIM_STATE_ENABLED;
> > >  	pm_runtime_put_sync(dsi->dev);
> > > +	dsi->state &= ~DSIM_STATE_ENABLED;
> > >  }
> > >  
> > >  static enum drm_connector_status
> > > @@ -1499,9 +1517,11 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder)
> > >  	return 0;
> > >  }
> > >  
> > > -static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = {
> > > +static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
> > > +	.pre_enable = exynos_dsi_pre_enable,
> > >  	.enable = exynos_dsi_enable,
> > >  	.disable = exynos_dsi_disable,
> > > +	.post_disable = exynos_dsi_post_disable,
> > >  };
> > >  
> > >  static const struct drm_encoder_funcs exynos_dsi_encoder_funcs = {
> > > @@ -1520,9 +1540,7 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
> > >  
> > >  	out_bridge  = of_drm_find_bridge(device->dev.of_node);
> > >  	if (out_bridge) {
> > > -		drm_bridge_attach(encoder, out_bridge, NULL);
> > > -		dsi->out_bridge = out_bridge;
> > > -		encoder->bridge = NULL;
> > > +		drm_bridge_attach(encoder, out_bridge, &dsi->bridge);
> > >  	} else {
> > >  		int ret = exynos_dsi_create_connector(encoder);
> > >  
> > > @@ -1575,19 +1593,19 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
> > >  				  struct mipi_dsi_device *device)
> > >  {
> > >  	struct exynos_dsi *dsi = host_to_dsi(host);
> > > +	struct drm_bridge *out_bridge = dsi->bridge.next;
> > >  	struct drm_device *drm = dsi->encoder.dev;
> > >  
> > >  	if (dsi->panel) {
> > >  		mutex_lock(&drm->mode_config.mutex);
> > > -		exynos_dsi_disable(&dsi->encoder);
> > > +		exynos_dsi_disable(&dsi->bridge);
> > > +		exynos_dsi_post_disable(&dsi->bridge);
> > >  		drm_panel_detach(dsi->panel);
> > >  		dsi->panel = NULL;
> > >  		dsi->connector.status = connector_status_disconnected;
> > >  		mutex_unlock(&drm->mode_config.mutex);
> > > -	} else {
> > > -		if (dsi->out_bridge->funcs->detach)
> > > -			dsi->out_bridge->funcs->detach(dsi->out_bridge);
> > > -		dsi->out_bridge = NULL;
> > > +	} else if (out_bridge && out_bridge->funcs->detach) {
> > > +		out_bridge->funcs->detach(out_bridge);  
> > 
> > Maybe drm_bridge_detach() ?
> 
> This function is not exported, and I suppose that's why they used the
> function pointer in this driver. I bet there's a good reason for not
> exposing this function...

Indeed, my bad. It's called by drm_encoder_cleanup(), I assume it's not
enough ? If there's a good use case for exporting it, then I think it
should be exported.

> > >  	}
> > >  
> > >  	if (drm->mode_config.poll_enabled)
> > > @@ -1687,16 +1705,18 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
> > >  	drm_encoder_init(drm_dev, encoder, &exynos_dsi_encoder_funcs,
> > >  			 DRM_MODE_ENCODER_TMDS, NULL);
> > >  
> > > -	drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
> > > -
> > >  	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
> > >  	if (ret < 0)
> > >  		return ret;
> > >  
> > > +	/* Declare ourself as the first bridge element. */
> > > +	dsi->bridge.funcs = &exynos_dsi_bridge_funcs;
> > > +	drm_bridge_attach(encoder, &dsi->bridge, NULL);
> > > +
> > >  	if (dsi->in_bridge_node) {
> > >  		in_bridge = of_drm_find_bridge(dsi->in_bridge_node);
> > >  		if (in_bridge)
> > > -			drm_bridge_attach(encoder, in_bridge, NULL);
> > > +			drm_bridge_attach(encoder, in_bridge, &dsi->bridge);
> > >  	}  
> > 
> > Same as for patch 01/21, maybe this could be moved to this bridge's
> > attach operation ? Actually, now that I've read the code, this in_bridge
> > part looks weird. Why would the DSI encoder have an input bridge that is
> > has to manage itself ?
> 
> Yes, I know, it doesn't make any sense. Either we're dealing with a
> bridge which can be chained to other bridges (can be placed in the
> middle of a chain as well), or we're dealing with an encoder which
> precedes any bridges. In the latter case (which is how exynos_dsi is
> implemented) in_bridge doesn't have any meaning, and that's even worse
> since we're placing the so-called input bridge (AKA previous bridge)
> after our encoder (that's what drm_bridge_attach(encoder, in_bridge,
> NULL) does).

Can we get input from the exynos maintainers ? Or is the driver not
actively maintained anymore ?

> TBH, I didn't want to go that far and fix existing drivers when I
> started this series, so I think I'll rework the patchset to get rid of
> the VC4 and exynos patches, even if that means having 2 drivers that
> mess up with the encoder->bridge_chain list.

I don't mind the above changes really (and the one for VC4 seems pretty
fine so far).

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 05/21] drm/bridge: Introduce drm_bridge_chain_get_next_bridge()
  2019-11-24 10:56     ` Boris Brezillon
@ 2019-11-24 14:04       ` Laurent Pinchart
  0 siblings, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-11-24 14:04 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

Hi Boris,

On Sun, Nov 24, 2019 at 11:56:16AM +0100, Boris Brezillon wrote:
> On Sun, 24 Nov 2019 12:33:35 +0200 Laurent Pinchart wrote:
> > On Wed, Oct 23, 2019 at 05:44:56PM +0200, Boris Brezillon wrote:
> > > 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>
> > > ---
> > > 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/exynos/exynos_drm_dsi.c |  3 ++-
> > >  drivers/gpu/drm/mediatek/mtk_hdmi.c     |  6 ++++--
> > >  drivers/gpu/drm/omapdrm/omap_drv.c      |  4 ++--
> > >  drivers/gpu/drm/omapdrm/omap_encoder.c  |  3 ++-
> > >  drivers/gpu/drm/vc4/vc4_dsi.c           |  4 +++-
> > >  include/drm/drm_bridge.h                | 13 +++++++++++++
> > >  6 files changed, 26 insertions(+), 7 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > > index 3915f50b005e..005c67894b78 100644
> > > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > > +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > > @@ -1593,9 +1593,10 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
> > >  				  struct mipi_dsi_device *device)
> > >  {
> > >  	struct exynos_dsi *dsi = host_to_dsi(host);
> > > -	struct drm_bridge *out_bridge = dsi->bridge.next;
> > >  	struct drm_device *drm = dsi->encoder.dev;
> > > +	struct drm_bridge *out_bridge;
> > >  
> > > +	out_bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);  
> > 
> > You may want to store this in the exynos_dsi structure in the previous
> > patch where you rework this driver.
> 
> Do we really need to store it there, since we already have a simple way
> to retrieve the next bridge in the chain?

We don't have to indeed. I thought it would be simpler, but that's
probably subjective.

> > >  	if (dsi->panel) {
> > >  		mutex_lock(&drm->mode_config.mutex);
> > >  		exynos_dsi_disable(&dsi->bridge);
> > > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> > > index ea68b5adccbe..cfaa5aab8876 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_chain_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..865164fe28dc 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_chain_get_next_bridge(bridge))
> > > +			bridge = drm_bridge_chain_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..8ca54081997e 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_chain_get_next_bridge(bridge)) {  
> > 
> > A for_each_bridge() macro would be nice (in a separate patch). It could
> > be used in omap_drv.c above too.
> 
> It's coming later in the series (patch 8).
> 
> > >  		if (!bridge->timings)
> > >  			continue;
> > >  
> > > diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> > > index 49f8a313e759..49c47185aff0 100644
> > > --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> > > +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> > > @@ -1644,8 +1644,10 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
> > >  	struct drm_device *drm = dev_get_drvdata(master);
> > >  	struct vc4_dev *vc4 = to_vc4_dev(drm);
> > >  	struct vc4_dsi *dsi = dev_get_drvdata(dev);
> > > +	struct drm_bridge *bridge;
> > >  
> > > -	if (dsi->bridge.next)
> > > +	bridge = drm_bridge_chain_get_next_bridge(&dsi->bridge);
> > > +	if (bridge)
> > >  		pm_runtime_disable(dev);
> > >  
> > >  	vc4_dsi_encoder_destroy(dsi->encoder);
> > > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > > index 726435baf4ad..8aeba83fcf31 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_chain_get_next_bridge() - Get the next bridge in the chain
> > > + * @bridge: bridge object
> > > + *
> > > + * RETURNS:
> > > + * the next bridge in the chain, or NULL if @bridge is the last.  
> > 
> > Maybe "the next bridge in the chain after @bridge, ..." ?
> 
> Agreed.
> 
> > > + */
> > > +static inline struct drm_bridge *
> > > +drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge)  
> > 
> > Technically speaking this doesn't operate on a chain but on a bridge, so
> > I'd name is drm_bridge_get_next_bridge(). I will not insist to the way
> > of nacking the series for this, so with the rename, but also without,
> 
> You're absolutely right, I'll rename the function as suggested.
> 
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > 
> > > +{
> > > +	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);  

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 08/21] drm/bridge: Add the drm_for_each_bridge_in_chain() helper
  2019-10-23 15:44 ` [PATCH v3 08/21] drm/bridge: Add the drm_for_each_bridge_in_chain() helper Boris Brezillon
  2019-10-25 13:30   ` Neil Armstrong
@ 2019-11-24 14:07   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-11-24 14:07 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

Hi Boris,

Thank you for the patch.
On Wed, Oct 23, 2019 at 05:44:59PM +0200, Boris Brezillon wrote:
> 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>
> ---
> 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 3ab16c95e59e..238e84ab63a3 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);
>  }
>  
> +/**
> + * for_each_bridge_in_chain() - Iterate over all bridges present in a chain

The name doesn't match the macro below. Apart from that,

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

> + * @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);

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 09/21] drm/bridge: Add a drm_bridge_state object
  2019-10-23 15:45 ` [PATCH v3 09/21] drm/bridge: Add a drm_bridge_state object Boris Brezillon
  2019-10-25 14:35   ` Neil Armstrong
  2019-11-05 16:05   ` Neil Armstrong
@ 2019-12-02 16:42   ` Laurent Pinchart
  2 siblings, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-02 16:42 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:45:00PM +0200, 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>
> ---
> 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        | 168 +++++++++++++++++++++++++++-
>  include/drm/drm_atomic.h            |   3 +
>  include/drm/drm_bridge.h            | 118 +++++++++++++++++++
>  5 files changed, 343 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 419381abbdd1..6c249ce39380 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>
> @@ -1010,6 +1011,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 f02ddffd4960..de985ba7ce2d 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 c5cf8a9c4237..c53966767782 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,38 @@ 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);
> +	else
> +		state = drm_atomic_helper_bridge_duplicate_state(bridge);
> +
> +	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
> +		drm_atomic_helper_bridge_destroy_state(bridge, 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 +147,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;
>  	LIST_HEAD(tmp_list);
>  	int ret;
>  
> @@ -132,19 +166,39 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  
>  	if (bridge->funcs->attach) {
>  		ret = bridge->funcs->attach(bridge);
> -		if (ret < 0) {
> -			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 = drm_atomic_helper_bridge_reset(bridge);
> +
> +	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);
> +
>  	if (previous)
>  		list_splice(&tmp_list, &previous->chain_node);
>  	else
>  		list_splice(&tmp_list, &encoder->bridge_chain);
>  
>  	return 0;
> +
> +err_detach_bridge:
> +	if (bridge->funcs->detach)
> +		bridge->funcs->detach(bridge);
> +
> +err_reset_bridge:
> +	bridge->dev = NULL;
> +	bridge->encoder = NULL;
> +	return ret;
>  }
>  EXPORT_SYMBOL(drm_bridge_attach);
>  
> @@ -156,6 +210,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);
>  
> @@ -522,6 +578,108 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
>  }
>  EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
>  
> +/**
> + * drm_atomic_helper_bridge_destroy_state() - Default destroy state helper
> + * @bridge: the bridge this state refers to
> + * @state: state object to destroy
> + *
> + * Just a simple kfree() for now.
> + */
> +void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge,
> +					    struct drm_bridge_state *state)
> +{
> +	kfree(state);
> +}
> +EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state);

As this function is called as the default in
+drm_bridge_atomic_destroy_priv_state(), does it need to be exported ?

> +
> +/**
> + * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its
> + *					default
> + * @bridge: the bridge this state is refers to

s/is refers/refers/

> + * @state: bridge state to initialize
> + *
> + * For now it's just a memset(0) plus a state->bridge assignment. Might
> + * be extended in the future.

"Might" is an understatement :-) This function will be extended when the
drm_bridge_state structure will gain new fields that need to be
initialized. I would have dropped this paragraph, up to you. You could
maybe replace it with

"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_reset() - default &drm_plane_funcs.reset hook for
> + *				      bridges
> + * @bridge: the bridge to reset state on
> + *
> + * Resets the atomic state for @bridge by freeing the state pointer (which
> + * might be NULL, e.g. at driver load time) and allocating a new empty state
> + * object.

The function doesn't free the state pointer...

> + *
> + * RETURNS:
> + * A valid drm_bridge_state object in case of success, an ERR_PTR()
> + * giving the reaon of the failure otherwise.

s/reaon/reason/

> + */
> +struct drm_bridge_state *
> +drm_atomic_helper_bridge_reset(struct drm_bridge *bridge)
> +{
> +	struct drm_bridge_state *bridge_state;
> +
> +	bridge_state = kzalloc(sizeof(*bridge_state), GFP_KERNEL);
> +	if (!bridge_state)
> +		return ERR_PTR(-ENOMEM);
> +
> +	__drm_atomic_helper_bridge_reset(bridge, bridge_state);
> +	return bridge_state;
> +}
> +EXPORT_SYMBOL(drm_atomic_helper_bridge_reset);
> +
> +/**
> + * __drm_atomic_helper_bridge_duplicate_state() - Copy the content of the
> + *						  current bridge state into a
> + *						  new one

Maybe "Copy atomic bridge state" to match the other helpers ?

> + * @bridge: bridge element the old and new states are referring to
> + * @new: new bridge state to copy to
> + *
> + * Should be used by custom &drm_bridge_funcs.atomic_duplicate() implementation
> + * to copy the previous state into the new object.

The documentation of these helpers seems to differ from the other atomic
state helpers. Shouldn't they be consistent ?

> + */
> +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
> +						struct drm_bridge_state *new)
> +{
> +	__drm_atomic_helper_private_obj_duplicate_state(&bridge->base,
> +							&new->base);
> +	new->bridge = bridge;
> +}
> +EXPORT_SYMBOL(__drm_atomic_helper_bridge_duplicate_state);
> +
> +/**
> + * drm_atomic_helper_duplicate_bridge_state() - Default duplicate state helper
> + * @bridge: bridge containing the state to duplicate
> + *
> + * Default implementation of &drm_bridge_funcs.atomic_duplicate().
> + *
> + * RETURNS:
> + * a valid state object or NULL if the allocation fails.
> + */
> +struct drm_bridge_state *
> +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge)
> +{
> +	struct drm_bridge_state *new;
> +
> +	if (WARN_ON(!bridge->base.state))
> +		return NULL;
> +
> +	new = kzalloc(sizeof(*new), GFP_KERNEL);
> +	if (new)
> +		__drm_atomic_helper_bridge_duplicate_state(bridge, new);
> +
> +	return new;
> +}
> +EXPORT_SYMBOL(drm_atomic_helper_bridge_duplicate_state);

As this function is called as the default in
drm_bridge_atomic_duplicate_priv_state(), does it need to be exported ?

> +
>  #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 927e1205d7aa..1c0a08217712 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 238e84ab63a3..a608c47d1de5 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_atomic.h>

While at it I'd add a blank line above.

>  #include <drm/drm_encoder.h>
>  #include <drm/drm_mode_object.h>
>  #include <drm/drm_modes.h>
> @@ -33,6 +34,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 +356,48 @@ struct drm_bridge_funcs {
>  	 */
>  	void (*atomic_post_disable)(struct drm_bridge *bridge,
>  				    struct drm_atomic_state *state);
> +
> +	/**
> +	 * @atomic_duplicate_state:
> +	 *
> +	 * Duplicate the current bridge state object (which is guaranteed to be
> +	 * non-NULL).
> +	 *
> +	 * The atomic_duplicate_state() is optional, the core falls back on
> +	 * &drm_atomic_helper_bridge_duplicate_state() when not implemented.
> +	 *
> +	 * 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, the coref falls back on

s/coref/core/

> +	 * &drm_atomic_helper_bridge_destroy_state() when not implemented.
> +	 */
> +	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, the core falls back on
> +	 * &drm_atomic_helper_bridge_reset() when not implemented.
> +	 *
> +	 * RETURNS:
> +	 * A valid drm_bridge_state object in case of success, an ERR_PTR()
> +	 * giving the reaon of the failure otherwise.

s/reaon/reason/

> +	 */
> +	struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge);
>  };
>  
>  /**
> @@ -380,6 +440,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 +466,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);
> @@ -475,6 +543,56 @@ 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_destroy_state(struct drm_bridge *bridge,
> +					    struct drm_bridge_state *state);
> +void __drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge,
> +						struct drm_bridge_state *new);
> +struct drm_bridge_state *
> +drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge);
> +
> +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 (!obj_state)
> +		return NULL;
> +
> +	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,

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 10/21] drm/bridge: Clarify the atomic enable/disable hooks semantics
  2019-10-23 15:45 ` [PATCH v3 10/21] drm/bridge: Clarify the atomic enable/disable hooks semantics Boris Brezillon
  2019-10-25 14:33   ` Neil Armstrong
@ 2019-12-02 16:50   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-02 16:50 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:45:01PM +0200, 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>
> ---
> Changes in v3:
> * New patch
> ---
>  drivers/gpu/drm/drm_bridge.c | 24 ++++++++++++------------
>  include/drm/drm_bridge.h     | 16 ++++++++++++----
>  2 files changed, 24 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index c53966767782..ca74bfe028c9 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -447,7 +447,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,
> @@ -457,7 +457,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;
> @@ -469,7 +469,7 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
>  	list_for_each_entry_reverse(iter, &encoder->bridge_chain,
>  				    chain_node) {
>  		if (iter->funcs->atomic_disable)
> -			iter->funcs->atomic_disable(iter, state);
> +			iter->funcs->atomic_disable(iter, old_state);
>  		else if (iter->funcs->disable)
>  			iter->funcs->disable(iter);
>  
> @@ -483,7 +483,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,
> @@ -493,7 +493,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;
>  
> @@ -504,7 +504,7 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
>  	list_for_each_entry_from(bridge, &encoder->bridge_chain,
>  				 chain_node) {
>  		if (bridge->funcs->atomic_post_disable)
> -			bridge->funcs->atomic_post_disable(bridge, state);
> +			bridge->funcs->atomic_post_disable(bridge, old_state);
>  		else if (bridge->funcs->post_disable)
>  			bridge->funcs->post_disable(bridge);
>  	}
> @@ -515,7 +515,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,
> @@ -525,7 +525,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;
> @@ -537,7 +537,7 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
>  	list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain,
>  				    chain_node) {
>  		if (iter->funcs->atomic_pre_enable)
> -			iter->funcs->atomic_pre_enable(iter, state);
> +			iter->funcs->atomic_pre_enable(iter, old_state);
>  		else if (iter->funcs->pre_enable)
>  			iter->funcs->pre_enable(iter);
>  
> @@ -550,7 +550,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,
> @@ -560,7 +560,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;
>  
> @@ -571,7 +571,7 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
>  	list_for_each_entry_from(bridge, &bridge->encoder->bridge_chain,
>  				 chain_node) {
>  		if (bridge->funcs->atomic_enable)
> -			bridge->funcs->atomic_enable(bridge, state);
> +			bridge->funcs->atomic_enable(bridge, old_state);
>  		else if (bridge->funcs->enable)
>  			bridge->funcs->enable(bridge);
>  	}
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index a608c47d1de5..e814e6d6e7c2 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -266,6 +266,8 @@ struct drm_bridge_funcs {
>  	 * bridge's @atomic_pre_enable or @pre_enable function. If the preceding
>  	 * element is a &drm_encoder it's called right before the encoder's
>  	 * &drm_encoder_helper_funcs.atomic_enable hook.
> +	 * This hook is passed the old atomic state (atomic state after new/old
> +	 * states have been swapped).

I think the name change is enough to explain this. If you want to make
it explicit in the documentation, I find the part between parentheses a
bit confusing.

With this comment addressed,

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

>  	 *
>  	 * The display pipe (i.e. clocks and timing signals) feeding this bridge
>  	 * will not yet be running when this callback is called. The bridge must
> @@ -281,7 +283,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:
> @@ -292,6 +294,8 @@ struct drm_bridge_funcs {
>  	 * bridge's @atomic_enable or @enable function. If the preceding element
>  	 * is a &drm_encoder it's called right after the encoder's
>  	 * &drm_encoder_helper_funcs.atomic_enable hook.
> +	 * This hook is passed the old atomic state (atomic state after new/old
> +	 * states have been swapped).
>  	 *
>  	 * The bridge can assume that the display pipe (i.e. clocks and timing
>  	 * signals) feeding it is running when this callback is called. This
> @@ -306,7 +310,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:
>  	 *
> @@ -316,6 +320,8 @@ struct drm_bridge_funcs {
>  	 * bridge's @atomic_disable or @disable vfunc. If the preceding element
>  	 * is a &drm_encoder it's called right before the
>  	 * &drm_encoder_helper_funcs.atomic_disable hook.
> +	 * This hook is passed the old atomic state (atomic state after new/old
> +	 * states have been swapped).
>  	 *
>  	 * The bridge can assume that the display pipe (i.e. clocks and timing
>  	 * signals) feeding it is still running when this callback is called.
> @@ -329,7 +335,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:
> @@ -340,6 +346,8 @@ struct drm_bridge_funcs {
>  	 * @atomic_post_disable or @post_disable function. If the preceding
>  	 * element is a &drm_encoder it's called right after the encoder's
>  	 * &drm_encoder_helper_funcs.atomic_disable hook.
> +	 * This hook is passed the old atomic state (atomic state after new/old
> +	 * states have been swapped).
>  	 *
>  	 * The bridge must assume that the display pipe (i.e. clocks and timing
>  	 * signals) feeding it is no longer running when this callback is
> @@ -355,7 +363,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);
>  
>  	/**
>  	 * @atomic_duplicate_state:

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 11/21] drm/bridge: Patch atomic hooks to take a drm_bridge_state
  2019-10-23 15:45 ` [PATCH v3 11/21] drm/bridge: Patch atomic hooks to take a drm_bridge_state Boris Brezillon
@ 2019-12-02 16:57   ` Laurent Pinchart
  0 siblings, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-02 16:57 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:45:02PM +0200, Boris Brezillon wrote:
> 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>
> ---
> 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    | 12 ++--
>  drivers/gpu/drm/drm_bridge.c                  | 61 +++++++++++++++----
>  include/drm/drm_bridge.h                      | 24 ++++----
>  3 files changed, 69 insertions(+), 28 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..e438e757f2ce 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> @@ -1290,8 +1290,9 @@ struct drm_crtc *analogix_dp_get_new_crtc(struct analogix_dp_device *dp,
>  }
>  
>  static void analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
> -						 struct drm_atomic_state *state)
> +						 struct drm_bridge_state *bstate)

Should we name the parameter old_state ? Same comment below.

With this addressed,

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

>  {
> +	struct drm_atomic_state *state = bstate->base.state;
>  	struct analogix_dp_device *dp = bridge->driver_private;
>  	struct drm_crtc *crtc;
>  	struct drm_crtc_state *old_crtc_state;
> @@ -1367,8 +1368,9 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
>  }
>  
>  static void analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
> -					     struct drm_atomic_state *state)
> +					     struct drm_bridge_state *bstate)
>  {
> +	struct drm_atomic_state *state = bstate->base.state;
>  	struct analogix_dp_device *dp = bridge->driver_private;
>  	struct drm_crtc *crtc;
>  	struct drm_crtc_state *old_crtc_state;
> @@ -1441,8 +1443,9 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
>  }
>  
>  static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
> -					      struct drm_atomic_state *state)
> +					      struct drm_bridge_state *bstate)
>  {
> +	struct drm_atomic_state *state = bstate->base.state;
>  	struct analogix_dp_device *dp = bridge->driver_private;
>  	struct drm_crtc *crtc;
>  	struct drm_crtc_state *new_crtc_state = NULL;
> @@ -1465,8 +1468,9 @@ static void analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
>  
>  static
>  void analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge,
> -					    struct drm_atomic_state *state)
> +					    struct drm_bridge_state *bstate)
>  {
> +	struct drm_atomic_state *state = bstate->base.state;
>  	struct analogix_dp_device *dp = bridge->driver_private;
>  	struct drm_crtc *crtc;
>  	struct drm_crtc_state *new_crtc_state;
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index ca74bfe028c9..377866e3214f 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -468,10 +468,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, old_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;
> @@ -503,10 +512,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, old_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);
> @@ -536,10 +555,19 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
>  	encoder = bridge->encoder;
>  	list_for_each_entry_reverse(iter, &bridge->encoder->bridge_chain,
>  				    chain_node) {
> -		if (iter->funcs->atomic_pre_enable)
> -			iter->funcs->atomic_pre_enable(iter, old_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;
> @@ -570,10 +598,19 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
>  	encoder = bridge->encoder;
>  	list_for_each_entry_from(bridge, &bridge->encoder->bridge_chain,
>  				 chain_node) {
> -		if (bridge->funcs->atomic_enable)
> -			bridge->funcs->atomic_enable(bridge, old_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 e814e6d6e7c2..b1f557d8dba9 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -266,8 +266,8 @@ struct drm_bridge_funcs {
>  	 * bridge's @atomic_pre_enable or @pre_enable function. If the preceding
>  	 * element is a &drm_encoder it's called right before the encoder's
>  	 * &drm_encoder_helper_funcs.atomic_enable hook.
> -	 * This hook is passed the old atomic state (atomic state after new/old
> -	 * states have been swapped).
> +	 * This hook is passed the old bridge state (the new one can be
> +	 * retrieved from bridge->state).
>  	 *
>  	 * The display pipe (i.e. clocks and timing signals) feeding this bridge
>  	 * will not yet be running when this callback is called. The bridge must
> @@ -283,7 +283,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:
> @@ -294,8 +294,8 @@ struct drm_bridge_funcs {
>  	 * bridge's @atomic_enable or @enable function. If the preceding element
>  	 * is a &drm_encoder it's called right after the encoder's
>  	 * &drm_encoder_helper_funcs.atomic_enable hook.
> -	 * This hook is passed the old atomic state (atomic state after new/old
> -	 * states have been swapped).
> +	 * This hook is passed the old bridge state (the new one can be
> +	 * retrieved from bridge->state).
>  	 *
>  	 * The bridge can assume that the display pipe (i.e. clocks and timing
>  	 * signals) feeding it is running when this callback is called. This
> @@ -310,7 +310,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:
>  	 *
> @@ -320,8 +320,8 @@ struct drm_bridge_funcs {
>  	 * bridge's @atomic_disable or @disable vfunc. If the preceding element
>  	 * is a &drm_encoder it's called right before the
>  	 * &drm_encoder_helper_funcs.atomic_disable hook.
> -	 * This hook is passed the old atomic state (atomic state after new/old
> -	 * states have been swapped).
> +	 * This hook is passed the old bridge state (the new one can be
> +	 * retrieved from bridge->state).
>  	 *
>  	 * The bridge can assume that the display pipe (i.e. clocks and timing
>  	 * signals) feeding it is still running when this callback is called.
> @@ -335,7 +335,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:
> @@ -346,8 +346,8 @@ struct drm_bridge_funcs {
>  	 * @atomic_post_disable or @post_disable function. If the preceding
>  	 * element is a &drm_encoder it's called right after the encoder's
>  	 * &drm_encoder_helper_funcs.atomic_disable hook.
> -	 * This hook is passed the old atomic state (atomic state after new/old
> -	 * states have been swapped).
> +	 * This hook is passed the old bridge state (the new one can be
> +	 * retrieved from bridge->state).
>  	 *
>  	 * The bridge must assume that the display pipe (i.e. clocks and timing
>  	 * signals) feeding it is no longer running when this callback is
> @@ -363,7 +363,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:

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 12/21] drm/bridge: Add an ->atomic_check() hook
  2019-10-23 15:45 ` [PATCH v3 12/21] drm/bridge: Add an ->atomic_check() hook Boris Brezillon
  2019-10-25 14:35   ` Neil Armstrong
@ 2019-12-02 17:03   ` Laurent Pinchart
  2019-12-03 10:11     ` Boris Brezillon
  1 sibling, 1 reply; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-02 17:03 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:45:03PM +0200, Boris Brezillon wrote:
> 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>
> ---
> 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 de985ba7ce2d..1d0a19511a0d 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 377866e3214f..990e056296bd 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -615,6 +615,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_destroy_state() - Default destroy state helper
>   * @bridge: the bridge this state refers to
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index b1f557d8dba9..2beb1ef9a733 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -127,7 +127,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:
>  	 *
> @@ -391,6 +393,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.

As explained in the review of the RFC, I think it's a mistake not to
define the semantics of this operation precisely, and in particular not
to define explictly what parameters bridge drivers are allowed to modify
here. I however don't want to make this a prerequisite for your series,
so

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

but I'm sure we'll regret this later when different bridges will have
slightly incompatible implementations.

> +	 *
> +	 * &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:
>  	 *
> @@ -542,6 +566,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,

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 13/21] drm/bridge: Add the drm_bridge_chain_get_prev_bridge() helper
  2019-10-23 15:45 ` [PATCH v3 13/21] drm/bridge: Add the drm_bridge_chain_get_prev_bridge() helper Boris Brezillon
  2019-10-25 14:34   ` Neil Armstrong
@ 2019-12-02 17:05   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-02 17:05 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:45:04PM +0200, Boris Brezillon wrote:
> Will be useful for bridge drivers that want to do bus format
> negotiation with their neighbours.

A general comment, I think the body of the commit message should be
readable on its own, without the subject line. It may be a matter of
personal taste.

> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>

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

> ---
> 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 2beb1ef9a733..3fb518494640 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -526,6 +526,22 @@ drm_bridge_chain_get_next_bridge(struct drm_bridge *bridge)
>  	return list_next_entry(bridge, chain_node);
>  }
>  
> +/**
> + * drm_bridge_chain_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_chain_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

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 17/21] dt-bindings: display: bridge: lvds-transmitter: Add new props
  2019-10-23 15:45 ` [PATCH v3 17/21] dt-bindings: display: bridge: lvds-transmitter: Add new props Boris Brezillon
  2019-10-25 19:57   ` Rob Herring
@ 2019-12-02 17:11   ` Laurent Pinchart
  2019-12-03 12:38     ` Boris Brezillon
  1 sibling, 1 reply; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-02 17:11 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:45:08PM +0200, Boris Brezillon wrote:
> Add the data-mapping property to describe the output bus format and
> the bus-width property to describe the input bus format.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> Changes in v3:
> * New patch
> ---
>  .../bindings/display/bridge/lvds-transmitter.txt    | 13 +++++++++++++

Please note that lvds-transmitter has been renamed to lvds-codec and
converted to YAML as part of a pull request I've recently sent ([1]).
This patch will thus need to be rebased.

[1] https://patchwork.freedesktop.org/patch/342754/

>  1 file changed, 13 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> index 60091db5dfa5..7b43b6f20279 100644
> --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> @@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
>  - Video port 0 for parallel input
>  - Video port 1 for LVDS output
>  
> +Optional port 0 node properties:
> +
> +- bus-width: number of data lines use to transmit the RGB data.
> +	     Can be 18 or 24.
> +
> +Optional port 1 node properties:
> +
> +- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml
> +		for more details about this LVDS data-mapping property.
> +		Supported values:
> +		"jeida-18"
> +		"jeida-24"
> +		"vesa-24"
>  
>  Example
>  -------

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 19/21] drm/panel: simple: Add support for Toshiba LTA089AC29000 panel
  2019-10-23 15:45 ` [PATCH v3 19/21] drm/panel: simple: Add support for Toshiba LTA089AC29000 panel Boris Brezillon
@ 2019-12-02 17:17   ` Laurent Pinchart
  2019-12-03 12:42     ` Boris Brezillon
  0 siblings, 1 reply; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-02 17: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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:45:10PM +0200, Boris Brezillon wrote:
> Add a new entry for the Toshiba LTA089AC29000 panel.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> Changes in v3:
> * None
> ---
>  drivers/gpu/drm/panel/panel-simple.c | 36 ++++++++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
> 
> diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
> index 5d487686d25c..27c92b44bd95 100644
> --- a/drivers/gpu/drm/panel/panel-simple.c
> +++ b/drivers/gpu/drm/panel/panel-simple.c
> @@ -2937,6 +2937,39 @@ static const struct panel_desc toshiba_lt089ac29000 = {
>  	.connector_type = DRM_MODE_CONNECTOR_LVDS,
>  };
>  
> +static const struct drm_display_mode toshiba_lta089ac29000_mode = {
> +	.clock = 79500,
> +	.hdisplay = 1280,
> +	.hsync_start = 1280 + 192,
> +	.hsync_end = 1280 + 192 + 128,
> +	.htotal = 1280 + 192 + 128 + 64,
> +	.vdisplay = 768,
> +	.vsync_start = 768 + 20,
> +	.vsync_end = 768 + 20 + 7,
> +	.vtotal = 768 + 20 + 7 + 3,
> +	.vrefresh = 60,
> +};
> +
> +static const struct panel_desc toshiba_lta089ac29000 = {
> +	.modes = &toshiba_lta089ac29000_mode,
> +	.num_modes = 1,
> +	.size = {
> +		.width = 194,
> +		.height = 116,
> +	},
> +	/*
> +	 * FIXME:
> +	 * The panel supports 2 bus formats: jeida-24 and jeida-18. The
> +	 * mode is selected through the 8b6b_SEL pin. If anyone ever needs
> +	 * support for jeida-18 we should probably parse the 'data-mapping'
> +	 * property.
> +	 * In the unlikely event where 8b6b_SEL is connected to a GPIO, we'd
> +	 * need a new infra to allow bus format negotiation at the panel level.
> +	 */
> +	.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
> +	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
> +};
> +
>  static const struct drm_display_mode tpk_f07a_0102_mode = {
>  	.clock = 33260,
>  	.hdisplay = 800,
> @@ -3392,6 +3425,9 @@ static const struct of_device_id platform_of_match[] = {
>  	}, {
>  		.compatible = "toshiba,lt089ac29000",
>  		.data = &toshiba_lt089ac29000,
> +	}, {
> +		.compatible = "toshiba,lta089ac29000",

Is this really different than "toshiba,lt089ac29000" ? Both have the
exact same timing, the only difference is .bus_format, and according to
https://www.avnet-integrated.eu/fileadmin/user_upload/Files/Displays/Colour_TFT/LT089AC29000.pdf
the "toshiba,lt089ac29000" is an LVDS panel.

> +		.data = &toshiba_lta089ac29000,
>  	}, {
>  		.compatible = "tpk,f07a-0102",
>  		.data = &tpk_f07a_0102,

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 20/21] dt-bindings: display: panel: Add the LTA089AC29000 variant
  2019-10-23 15:45 ` [PATCH v3 20/21] dt-bindings: display: panel: Add the LTA089AC29000 variant Boris Brezillon
  2019-10-25 19:58   ` Rob Herring
@ 2019-12-02 17:19   ` Laurent Pinchart
  1 sibling, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-02 17: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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:45:11PM +0200, Boris Brezillon wrote:
> The LTA089AC29000 and LT089AC29000 are not exactly the same. Let's add
> a new compatible for the LTA variant.

What is the difference ? :-)

> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
>  .../bindings/display/panel/toshiba,lt089ac29000.txt          | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt b/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt
> index 89826116628c..26ebfa098966 100644
> --- a/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt
> +++ b/Documentation/devicetree/bindings/display/panel/toshiba,lt089ac29000.txt
> @@ -1,7 +1,10 @@
>  Toshiba 8.9" WXGA (1280x768) TFT LCD panel
>  
>  Required properties:
> -- compatible: should be "toshiba,lt089ac29000"
> +- compatible: should be one of the following
> +	      "toshiba,lt089ac29000"
> +	      "toshiba,lta089ac29000"
> +
>  - power-supply: as specified in the base binding
>  
>  This binding is compatible with the simple-panel binding, which is specified

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 14/21] drm/bridge: Add the necessary bits to support bus format negotiation
  2019-10-23 15:45 ` [PATCH v3 14/21] drm/bridge: Add the necessary bits to support bus format negotiation Boris Brezillon
@ 2019-12-03 10:03   ` Laurent Pinchart
  0 siblings, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-03 10:03 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:45:05PM +0200, Boris Brezillon wrote:
> drm_bridge_state is extended to describe the input and output bus
> configuration. This bus configuration is exposed through the

s/configuration/configurations/
s/This bus configuration is/These bus configurations are/

> drm_bus_cfg struct which contains 2 properties: the bus format and
> the bus flags.
> 
> 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 reserve

s/reserve/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 let

s/let/lets/

> the previous bridge element decide what to do (most of the time, bridge
> drivers will pick a default bus format of extract this piece of

s/of extract/or extract/

> information from somewhere else, like a FW property).
> 
> Bus flags negotiation is left to drivers which can simply propagate the
> flags from the input of the next bridge element if there's no conversion
> done inside the bridge, or tweak them if the bridge does some kind of
> signal inversion.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> 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 | 257 ++++++++++++++++++++++++++++++++++-
>  include/drm/drm_bridge.h     | 106 +++++++++++++++
>  2 files changed, 362 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index 990e056296bd..6022fb3d406a 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -641,13 +641,251 @@ static int drm_atomic_bridge_check(struct drm_bridge *bridge,
>  	return 0;
>  }
>  
> +static int select_bus_fmt_recursive(struct drm_bridge *first,

s/first/first_bridge/ to make the code more explicit ?

> +				    struct drm_bridge *cur,
> +				    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;
> +	u32 *in_bus_fmts;
> +	int ret;
> +
> +	prev = drm_bridge_chain_get_prev_bridge(cur);
> +	cur_state = drm_atomic_get_new_bridge_state(crtc_state->state, cur);
> +	if (WARN_ON(!cur_state))
> +		return -EINVAL;
> +
> +	/*
> +	 * Bus format negotiation is not supported by this bridge, let's pass

s/Bus format/If bus format/

or move the comment within the if () block.

> +	 * 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->funcs->atomic_get_input_bus_fmts) {
> +		if (cur != first) {
> +			ret = select_bus_fmt_recursive(first, prev, crtc_state,
> +						       conn_state,
> +						       MEDIA_BUS_FMT_FIXED);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		cur_state->input_bus_cfg.fmt = MEDIA_BUS_FMT_FIXED;
> +		cur_state->output_bus_cfg.fmt = out_bus_fmt;
> +		return 0;
> +	}
> +
> +	in_bus_fmts = cur->funcs->atomic_get_input_bus_fmts(cur, 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 == cur) {
> +		cur_state->input_bus_cfg.fmt = in_bus_fmts[0];
> +		cur_state->output_bus_cfg.fmt = out_bus_fmt;
> +		kfree(in_bus_fmts);
> +		return 0;
> +	}
> +
> +	for (i = 0; i < num_in_bus_fmts; i++) {
> +		ret = select_bus_fmt_recursive(first, prev, crtc_state,
> +					       conn_state, in_bus_fmts[i]);
> +		if (ret != -ENOTSUPP)
> +			break;
> +	}
> +
> +	if (!ret) {
> +		cur_state->input_bus_cfg.fmt = in_bus_fmts[i];
> +		cur_state->output_bus_cfg.fmt = 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's providing bus format negotiation between bridge elements. The

s/It's providing/It performs/

> + * 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.

Blank line or reflow.

> + * 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.

Blank line or reflow.

> + * 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 = kzalloc(sizeof(*out_bus_fmts), GFP_KERNEL);

kmalloc would do.

> +		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_chain_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_helper_bridge_propagate_bus_fmt() - Propagate output format to
> + *						  the input end of a bridge

I would move this function above select_bus_fmt_recursive() to avoid
putting it in-between functions that are related to each other.

> + * @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
> + *
> + * Helper that propagate the output format to the input end of bridge.
> + * Particularly useful for dummy bridge elements like the panel_bridge.

I think this needs to be clarified.

 * 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 or if @num_input_fmts is set to 0

s/0/NULL/

and that's actually not true, you just crash if num_input_fmts == NULL
:-) That's fine as num_input_fmts can't be NULL, so just drop the last
part of the sentence.

> + */
> +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;
> +
> +	*num_input_fmts = 1;
> +	input_fmts = kzalloc(sizeof(*input_fmts), GFP_KERNEL);
> +	if (!input_fmts)
> +		return NULL;
> +
> +	input_fmts[0] = output_fmt;
> +	return input_fmts;
> +}
> +EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt);
> +
>  /**
>   * 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()
> @@ -659,12 +897,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 3fb518494640..d37aaf34b882 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -34,15 +34,43 @@ struct drm_bridge;
>  struct drm_bridge_timings;
>  struct drm_panel;
>  
> +/**
> + * struct drm_bus_cfg - bus configuration
> + * @fmt: format used on this bus. This field should not be directly modified
> + *	 by drivers (&drm_atomic_bridge_chain_select_bus_fmts() takes care of
> + *	 the bus format negotiation).

I would mention that the value stored here is one of MEDIA_BUS_FMT_*.

> + * @flags: DRM_BUS_ flags used on this bus. Drivers should set the output bus
> + *	   flags to the input bus flags of the next bridge element (unless they
> + *	   have a good reason not to, like a passive signal inversion between
> + *	   those 2 elements). Input bus flags can be different from the output
> + *	   ones if there's some kind of conversion happening inside the bridge,
> + *	   but most of the times it's safe to just propagate the output bus
> + *	   flags to the input end.
> + *	   This bus flags selection/propagation should be done in the driver
> + *	   &drm_bridge_funcs.atomic_check() hook.
> + *
> + * Encodes the bus format and bus flags used by one end of the bridge or
> + * by the encoder output.

I think this needs to be extended a little bit. I have no trouble
understanding what this structure describes, but it may be less evident
for someone who hasn't followed the development. The commit message
suffers from the same issue, perhaps even more.

How about

 * 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.

With this I would also simplify the description of the flags field as it
would otherwise contain redundant information.

> + */
> +struct drm_bus_cfg {
> +	u32 fmt;

How about spelling this out as format instead of abbreviating it ?

> +	u32 flags;
> +};
> +
>  /**
>   * struct drm_bridge_state - Atomic bridge state object
>   * @base: inherit from &drm_private_state
>   * @bridge: the bridge this state refers to
> + * @input_bus_info: input bus information
> + * @output_bus_info: output bus information

The fields are named s/info/cfg/

>   */
>  struct drm_bridge_state {
>  	struct drm_private_state base;
>  
>  	struct drm_bridge *bridge;
> +
> +	struct drm_bus_cfg input_bus_cfg;
> +	struct drm_bus_cfg output_bus_cfg;
>  };
>  
>  static inline struct drm_bridge_state *
> @@ -393,6 +421,69 @@ 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

s/caller,/caller;/ or s/caller, if/caller if/

> +	 * 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
> +	 * fallback to &drm_connector.display_info.bus_formats[0] if

s/fallback/fall back/

> +	 * &drm_connector.display_info.num_bus_formats > 0,
> +	 * MEDIA_BUS_FMT_FIXED otherwise.

s/MEDIA_BUS_FMT_FIXED/or to MEDIA_BUS_FMT_FIXED/

> +	 */
> +	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.

You need either a blank line, or a reflow of the paragraph.

> +	 * The returned array must be allocated with kmalloc() and will be
> +	 * freed by the caller, if the allocation fails, NULL should be

s/caller,/caller;/ or s/caller, if/caller if/

> +	 * 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).

You need to describe what happens if the output_fmt isn't supported.
Should this return an allocated array of zero size or NULL ?

> +	 *
> +	 * This method is called on all element of the bridge chain as part of

s/element/elements/

> +	 * 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

s/even/Even/

> +	 * 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.

I would add "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:
>  	 *
> @@ -407,6 +498,13 @@ 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.input_bus_cfg.flags it should should happen in

That's twice the same field, did you mean output_bus_cfg.flags for the
second one ?

> +	 * this function. By default those fields are set to the next bridge

s/those fields are set/the &drm_bridge_state.output_bus_cfg.flags field is set/

> +	 * &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.
>  	 */
> @@ -594,6 +692,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 *

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 12/21] drm/bridge: Add an ->atomic_check() hook
  2019-12-02 17:03   ` Laurent Pinchart
@ 2019-12-03 10:11     ` Boris Brezillon
  2019-12-03 10:15       ` Laurent Pinchart
  0 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-12-03 10:11 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

On Mon, 2 Dec 2019 19:03:36 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> > +	/**
> > +	 * @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.  
> 
> As explained in the review of the RFC, I think it's a mistake not to
> define the semantics of this operation precisely, and in particular not
> to define explictly what parameters bridge drivers are allowed to modify
> here. I however don't want to make this a prerequisite for your series,
> so
> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> but I'm sure we'll regret this later when different bridges will have
> slightly incompatible implementations.

Nothing prevents us from working on this clarification after the patch
series has been merged. I just said I couldn't come up with a good
set of rules on my own, as I don't really know what bridge->mode_fixup()
allows us to modify in the first place.

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

* Re: [PATCH v3 16/21] drm/bridge: lvds-encoder: Implement basic bus format negotiation
  2019-10-23 15:45 ` [PATCH v3 16/21] drm/bridge: lvds-encoder: Implement basic bus format negotiation Boris Brezillon
@ 2019-12-03 10:14   ` Laurent Pinchart
  0 siblings, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-03 10:14 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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:45:07PM +0200, Boris Brezillon wrote:
> Some LVDS encoder might support several input/output bus formats. Add

s/encoder/encoders/

> a way to describe the one used on a specific design by adding optional

s/the one/the format/

> 'data-mapping' properties to the input/output ports.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> Changes in v3:
> * Use bus-width for the rgb24/rgb18 distinction
> * Adjust code to match core changes
> * Get rid of of_get_data_mapping()
> * Don't implement ->atomic_check() (the core now takes care of bus
>   flags propagation)
> 
> Changes in v2:
> * Make the bus-format negotiation logic more generic
> ---
>  drivers/gpu/drm/bridge/lvds-encoder.c | 72 +++++++++++++++++++++++++++
>  1 file changed, 72 insertions(+)
> 
> diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c
> index e2132a8d5106..a2a8f7f4ac97 100644
> --- a/drivers/gpu/drm/bridge/lvds-encoder.c
> +++ b/drivers/gpu/drm/bridge/lvds-encoder.c
> @@ -6,6 +6,7 @@
>  #include <linux/gpio/consumer.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
> +#include <linux/of_device.h>
>  #include <linux/of_graph.h>
>  #include <linux/platform_device.h>
>  
> @@ -16,6 +17,8 @@ struct lvds_encoder {
>  	struct drm_bridge bridge;
>  	struct drm_bridge *panel_bridge;
>  	struct gpio_desc *powerdown_gpio;
> +	u32 output_fmt;
> +	u32 input_fmt;
>  };
>  
>  static int lvds_encoder_attach(struct drm_bridge *bridge)
> @@ -48,10 +51,40 @@ static void lvds_encoder_disable(struct drm_bridge *bridge)
>  		gpiod_set_value_cansleep(lvds_encoder->powerdown_gpio, 1);
>  }
>  
> +static u32 *lvds_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)
> +{
> +	struct lvds_encoder *lvds_encoder = container_of(bridge,
> +							 struct lvds_encoder,
> +							 bridge);
> +	u32 *input_fmts;
> +
> +	if (output_fmt == MEDIA_BUS_FMT_FIXED ||
> +	    output_fmt == lvds_encoder->output_fmt)
> +		*num_input_fmts = 1;
> +	else
> +		*num_input_fmts = 0;
> +
> +	if (!*num_input_fmts)
> +		return NULL;
> +
> +	input_fmts = kcalloc(*num_input_fmts, sizeof(*input_fmts), GFP_KERNEL);

As *num_input_fmts == 1, you can

	input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);

> +	if (!input_fmts)
> +		return NULL;
> +
> +	input_fmts[0] = lvds_encoder->input_fmt;
> +	return input_fmts;
> +}
> +
>  static struct drm_bridge_funcs funcs = {
>  	.attach = lvds_encoder_attach,
>  	.enable = lvds_encoder_enable,
>  	.disable = lvds_encoder_disable,
> +	.atomic_get_input_bus_fmts = lvds_atomic_get_input_bus_fmts,
>  };
>  
>  static int lvds_encoder_probe(struct platform_device *pdev)
> @@ -62,11 +95,16 @@ static int lvds_encoder_probe(struct platform_device *pdev)
>  	struct device_node *panel_node;
>  	struct drm_panel *panel;
>  	struct lvds_encoder *lvds_encoder;
> +	const char *output_data_mapping = NULL;
> +	u32 input_bus_width = 0;
>  
>  	lvds_encoder = devm_kzalloc(dev, sizeof(*lvds_encoder), GFP_KERNEL);
>  	if (!lvds_encoder)
>  		return -ENOMEM;
>  
> +	lvds_encoder->input_fmt = MEDIA_BUS_FMT_FIXED;
> +	lvds_encoder->output_fmt = MEDIA_BUS_FMT_FIXED;
> +
>  	lvds_encoder->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown",
>  							       GPIOD_OUT_HIGH);
>  	if (IS_ERR(lvds_encoder->powerdown_gpio)) {
> @@ -77,6 +115,25 @@ static int lvds_encoder_probe(struct platform_device *pdev)
>  		return err;
>  	}
>  
> +	port = of_graph_get_port_by_id(dev->of_node, 0);
> +	if (!port) {
> +		dev_dbg(dev, "port 0 not found\n");
> +		return -ENXIO;
> +	}
> +
> +	of_node_put(port);
> +
> +	if (of_property_read_u32(port, "bus-width", &input_bus_width)) {

You're using port after calling of_node_put().

> +		lvds_encoder->input_fmt = MEDIA_BUS_FMT_FIXED;
> +	} else if (input_bus_width == 18) {
> +		lvds_encoder->input_fmt = MEDIA_BUS_FMT_RGB666_1X18;
> +	} else if (input_bus_width == 24) {
> +		lvds_encoder->input_fmt = MEDIA_BUS_FMT_RGB888_1X24;
> +	} else {
> +		dev_dbg(dev, "unsupported bus-width)\n");

s/bus-width)/bus-width/

but I think you should make the error message a bit clearer

		dev_dbg(dev, "unsupported bus-width value %u on port 0\n",
			input_bus_width);

> +		return -ENOTSUPP;
> +	}
> +
>  	/* Locate the panel DT node. */
>  	port = of_graph_get_port_by_id(dev->of_node, 1);
>  	if (!port) {
> @@ -84,6 +141,21 @@ static int lvds_encoder_probe(struct platform_device *pdev)
>  		return -ENXIO;
>  	}
>  
> +	of_property_read_string(port, "data-mapping", &output_data_mapping);
> +	if (!output_data_mapping) {
> +		lvds_encoder->output_fmt = MEDIA_BUS_FMT_FIXED;
> +	} else if (!strcmp(output_data_mapping, "jeida-18")) {
> +		lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG;
> +	} else if (!strcmp(output_data_mapping, "jeida-24")) {
> +		lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
> +	} else if (!strcmp(output_data_mapping, "vesa-24")) {
> +		lvds_encoder->output_fmt = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
> +	} else {
> +		of_node_put(port);
> +		dev_dbg(dev, "unsupported output data-mapping\n");
> +		return -ENOTSUPP;
> +	}
> +
>  	endpoint = of_get_child_by_name(port, "endpoint");
>  	of_node_put(port);
>  	if (!endpoint) {

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 12/21] drm/bridge: Add an ->atomic_check() hook
  2019-12-03 10:11     ` Boris Brezillon
@ 2019-12-03 10:15       ` Laurent Pinchart
  0 siblings, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-03 10: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

Hi Boris,

On Tue, Dec 03, 2019 at 11:11:51AM +0100, Boris Brezillon wrote:
> On Mon, 2 Dec 2019 19:03:36 +0200 Laurent Pinchart wrote:
> 
> > > +	/**
> > > +	 * @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.  
> > 
> > As explained in the review of the RFC, I think it's a mistake not to
> > define the semantics of this operation precisely, and in particular not
> > to define explictly what parameters bridge drivers are allowed to modify
> > here. I however don't want to make this a prerequisite for your series,
> > so
> > 
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > 
> > but I'm sure we'll regret this later when different bridges will have
> > slightly incompatible implementations.
> 
> Nothing prevents us from working on this clarification after the patch
> series has been merged. I just said I couldn't come up with a good
> set of rules on my own, as I don't really know what bridge->mode_fixup()
> allows us to modify in the first place.

We can of course work on it later, but we both know it won't happen for
some time, until different bridges will behave differently and we'll
have to sort a really big mess :-)

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 18/21] drm/bridge: panel: Propage bus format/flags
  2019-10-23 15:45 ` [PATCH v3 18/21] drm/bridge: panel: Propage bus format/flags Boris Brezillon
@ 2019-12-03 10:17   ` Laurent Pinchart
  0 siblings, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-03 10: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

Hi Boris,

Thank you for the patch.

On Wed, Oct 23, 2019 at 05:45:09PM +0200, Boris Brezillon wrote:
> So that the previous bridge element in the chain knows which input
> format the panel bridge expects.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> Changes in v3:
> * Adjust things to match the new bus-format negotiation approach
> * Use drm_atomic_helper_bridge_propagate_bus_fmt
> * Don't implement ->atomic_check() (the core now takes care of bus
>   flags propagation)
> 
> Changes in v2:
> * Adjust things to match the new bus-format negotiation approach
> ---
>  drivers/gpu/drm/bridge/panel.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> index f4e293e7cf64..a70c363a2bd0 100644
> --- a/drivers/gpu/drm/bridge/panel.c
> +++ b/drivers/gpu/drm/bridge/panel.c
> @@ -127,6 +127,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
>  	.enable = panel_bridge_enable,
>  	.disable = panel_bridge_disable,
>  	.post_disable = panel_bridge_post_disable,
> +	.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,

Shouldn't the format be retrieved from the panel instead of from the
connector ? We're moving towards removing connector creation from
bridges, so I think it would be more future-proof.

>  };
>  
>  /**

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 17/21] dt-bindings: display: bridge: lvds-transmitter: Add new props
  2019-12-02 17:11   ` Laurent Pinchart
@ 2019-12-03 12:38     ` Boris Brezillon
  2019-12-03 13:22       ` Laurent Pinchart
  0 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-12-03 12:38 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

On Mon, 2 Dec 2019 19:11:40 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> Thank you for the patch.
> 
> On Wed, Oct 23, 2019 at 05:45:08PM +0200, Boris Brezillon wrote:
> > Add the data-mapping property to describe the output bus format and
> > the bus-width property to describe the input bus format.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > ---
> > Changes in v3:
> > * New patch
> > ---
> >  .../bindings/display/bridge/lvds-transmitter.txt    | 13 +++++++++++++  
> 
> Please note that lvds-transmitter has been renamed to lvds-codec and
> converted to YAML as part of a pull request I've recently sent ([1]).
> This patch will thus need to be rebased.

Let me know when it's been merged, I'll ask for a drm-next ->
drm-misc-next backmerge.

> 
> [1] https://patchwork.freedesktop.org/patch/342754/
> 
> >  1 file changed, 13 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> > index 60091db5dfa5..7b43b6f20279 100644
> > --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> > +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> > @@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
> >  - Video port 0 for parallel input
> >  - Video port 1 for LVDS output
> >  
> > +Optional port 0 node properties:
> > +
> > +- bus-width: number of data lines use to transmit the RGB data.
> > +	     Can be 18 or 24.
> > +
> > +Optional port 1 node properties:
> > +
> > +- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml
> > +		for more details about this LVDS data-mapping property.
> > +		Supported values:
> > +		"jeida-18"
> > +		"jeida-24"
> > +		"vesa-24"
> >  
> >  Example
> >  -------  
> 


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

* Re: [PATCH v3 19/21] drm/panel: simple: Add support for Toshiba LTA089AC29000 panel
  2019-12-02 17:17   ` Laurent Pinchart
@ 2019-12-03 12:42     ` Boris Brezillon
  2019-12-03 13:28       ` Laurent Pinchart
  0 siblings, 1 reply; 76+ messages in thread
From: Boris Brezillon @ 2019-12-03 12: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

On Mon, 2 Dec 2019 19:17:24 +0200
Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:

> Hi Boris,
> 
> Thank you for the patch.
> 
> On Wed, Oct 23, 2019 at 05:45:10PM +0200, Boris Brezillon wrote:
> > Add a new entry for the Toshiba LTA089AC29000 panel.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > ---
> > Changes in v3:
> > * None
> > ---
> >  drivers/gpu/drm/panel/panel-simple.c | 36 ++++++++++++++++++++++++++++
> >  1 file changed, 36 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
> > index 5d487686d25c..27c92b44bd95 100644
> > --- a/drivers/gpu/drm/panel/panel-simple.c
> > +++ b/drivers/gpu/drm/panel/panel-simple.c
> > @@ -2937,6 +2937,39 @@ static const struct panel_desc toshiba_lt089ac29000 = {
> >  	.connector_type = DRM_MODE_CONNECTOR_LVDS,
> >  };
> >  
> > +static const struct drm_display_mode toshiba_lta089ac29000_mode = {
> > +	.clock = 79500,
> > +	.hdisplay = 1280,
> > +	.hsync_start = 1280 + 192,
> > +	.hsync_end = 1280 + 192 + 128,
> > +	.htotal = 1280 + 192 + 128 + 64,
> > +	.vdisplay = 768,
> > +	.vsync_start = 768 + 20,
> > +	.vsync_end = 768 + 20 + 7,
> > +	.vtotal = 768 + 20 + 7 + 3,
> > +	.vrefresh = 60,
> > +};
> > +
> > +static const struct panel_desc toshiba_lta089ac29000 = {
> > +	.modes = &toshiba_lta089ac29000_mode,
> > +	.num_modes = 1,
> > +	.size = {
> > +		.width = 194,
> > +		.height = 116,
> > +	},
> > +	/*
> > +	 * FIXME:
> > +	 * The panel supports 2 bus formats: jeida-24 and jeida-18. The
> > +	 * mode is selected through the 8b6b_SEL pin. If anyone ever needs
> > +	 * support for jeida-18 we should probably parse the 'data-mapping'
> > +	 * property.
> > +	 * In the unlikely event where 8b6b_SEL is connected to a GPIO, we'd
> > +	 * need a new infra to allow bus format negotiation at the panel level.
> > +	 */
> > +	.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
> > +	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
> > +};
> > +
> >  static const struct drm_display_mode tpk_f07a_0102_mode = {
> >  	.clock = 33260,
> >  	.hdisplay = 800,
> > @@ -3392,6 +3425,9 @@ static const struct of_device_id platform_of_match[] = {
> >  	}, {
> >  		.compatible = "toshiba,lt089ac29000",
> >  		.data = &toshiba_lt089ac29000,
> > +	}, {
> > +		.compatible = "toshiba,lta089ac29000",  
> 
> Is this really different than "toshiba,lt089ac29000" ? Both have the
> exact same timing, the only difference is .bus_format, and according to
> https://www.avnet-integrated.eu/fileadmin/user_upload/Files/Displays/Colour_TFT/LT089AC29000.pdf
> the "toshiba,lt089ac29000" is an LVDS panel.

I think you can select the LVDS format on the LTA variant which you
can't do on the LT089AC29000. But I agree, LT089AC29000 is definitely
not an DPI/RGB panel. At the same time, changing the definition is
likely to break existing users :-(.

> 
> > +		.data = &toshiba_lta089ac29000,
> >  	}, {
> >  		.compatible = "tpk,f07a-0102",
> >  		.data = &tpk_f07a_0102,  
> 


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

* Re: [PATCH v3 17/21] dt-bindings: display: bridge: lvds-transmitter: Add new props
  2019-12-03 12:38     ` Boris Brezillon
@ 2019-12-03 13:22       ` Laurent Pinchart
  0 siblings, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-03 13:22 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

Hi Boris,

On Tue, Dec 03, 2019 at 01:38:43PM +0100, Boris Brezillon wrote:
> On Mon, 2 Dec 2019 19:11:40 +0200 Laurent Pinchart wrote:
> > On Wed, Oct 23, 2019 at 05:45:08PM +0200, Boris Brezillon wrote:
> > > Add the data-mapping property to describe the output bus format and
> > > the bus-width property to describe the input bus format.
> > > 
> > > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > ---
> > > Changes in v3:
> > > * New patch
> > > ---
> > >  .../bindings/display/bridge/lvds-transmitter.txt    | 13 +++++++++++++  
> > 
> > Please note that lvds-transmitter has been renamed to lvds-codec and
> > converted to YAML as part of a pull request I've recently sent ([1]).
> > This patch will thus need to be rebased.
> 
> Let me know when it's been merged, I'll ask for a drm-next ->
> drm-misc-next backmerge.

I expect Dave to merge it when the merge window will close.

> > [1] https://patchwork.freedesktop.org/patch/342754/
> > 
> > >  1 file changed, 13 insertions(+)
> > > 
> > > diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> > > index 60091db5dfa5..7b43b6f20279 100644
> > > --- a/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> > > +++ b/Documentation/devicetree/bindings/display/bridge/lvds-transmitter.txt
> > > @@ -36,6 +36,19 @@ graph bindings specified in Documentation/devicetree/bindings/graph.txt.
> > >  - Video port 0 for parallel input
> > >  - Video port 1 for LVDS output
> > >  
> > > +Optional port 0 node properties:
> > > +
> > > +- bus-width: number of data lines use to transmit the RGB data.
> > > +	     Can be 18 or 24.
> > > +
> > > +Optional port 1 node properties:
> > > +
> > > +- data-mapping: see Documentation/devicetree/bindings/display/panel/lvds.yaml
> > > +		for more details about this LVDS data-mapping property.
> > > +		Supported values:
> > > +		"jeida-18"
> > > +		"jeida-24"
> > > +		"vesa-24"
> > >  
> > >  Example
> > >  -------  

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 19/21] drm/panel: simple: Add support for Toshiba LTA089AC29000 panel
  2019-12-03 12:42     ` Boris Brezillon
@ 2019-12-03 13:28       ` Laurent Pinchart
  0 siblings, 0 replies; 76+ messages in thread
From: Laurent Pinchart @ 2019-12-03 13:28 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

Hi Boris,

On Tue, Dec 03, 2019 at 01:42:22PM +0100, Boris Brezillon wrote:
> On Mon, 2 Dec 2019 19:17:24 +0200 Laurent Pinchart wrote:
> > On Wed, Oct 23, 2019 at 05:45:10PM +0200, Boris Brezillon wrote:
> > > Add a new entry for the Toshiba LTA089AC29000 panel.
> > > 
> > > Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> > > ---
> > > Changes in v3:
> > > * None
> > > ---
> > >  drivers/gpu/drm/panel/panel-simple.c | 36 ++++++++++++++++++++++++++++
> > >  1 file changed, 36 insertions(+)
> > > 
> > > diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
> > > index 5d487686d25c..27c92b44bd95 100644
> > > --- a/drivers/gpu/drm/panel/panel-simple.c
> > > +++ b/drivers/gpu/drm/panel/panel-simple.c
> > > @@ -2937,6 +2937,39 @@ static const struct panel_desc toshiba_lt089ac29000 = {
> > >  	.connector_type = DRM_MODE_CONNECTOR_LVDS,
> > >  };
> > >  
> > > +static const struct drm_display_mode toshiba_lta089ac29000_mode = {
> > > +	.clock = 79500,
> > > +	.hdisplay = 1280,
> > > +	.hsync_start = 1280 + 192,
> > > +	.hsync_end = 1280 + 192 + 128,
> > > +	.htotal = 1280 + 192 + 128 + 64,
> > > +	.vdisplay = 768,
> > > +	.vsync_start = 768 + 20,
> > > +	.vsync_end = 768 + 20 + 7,
> > > +	.vtotal = 768 + 20 + 7 + 3,
> > > +	.vrefresh = 60,
> > > +};
> > > +
> > > +static const struct panel_desc toshiba_lta089ac29000 = {
> > > +	.modes = &toshiba_lta089ac29000_mode,
> > > +	.num_modes = 1,
> > > +	.size = {
> > > +		.width = 194,
> > > +		.height = 116,
> > > +	},
> > > +	/*
> > > +	 * FIXME:
> > > +	 * The panel supports 2 bus formats: jeida-24 and jeida-18. The
> > > +	 * mode is selected through the 8b6b_SEL pin. If anyone ever needs
> > > +	 * support for jeida-18 we should probably parse the 'data-mapping'
> > > +	 * property.
> > > +	 * In the unlikely event where 8b6b_SEL is connected to a GPIO, we'd
> > > +	 * need a new infra to allow bus format negotiation at the panel level.
> > > +	 */
> > > +	.bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
> > > +	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
> > > +};
> > > +
> > >  static const struct drm_display_mode tpk_f07a_0102_mode = {
> > >  	.clock = 33260,
> > >  	.hdisplay = 800,
> > > @@ -3392,6 +3425,9 @@ static const struct of_device_id platform_of_match[] = {
> > >  	}, {
> > >  		.compatible = "toshiba,lt089ac29000",
> > >  		.data = &toshiba_lt089ac29000,
> > > +	}, {
> > > +		.compatible = "toshiba,lta089ac29000",  
> > 
> > Is this really different than "toshiba,lt089ac29000" ? Both have the
> > exact same timing, the only difference is .bus_format, and according to
> > https://www.avnet-integrated.eu/fileadmin/user_upload/Files/Displays/Colour_TFT/LT089AC29000.pdf
> > the "toshiba,lt089ac29000" is an LVDS panel.
> 
> I think you can select the LVDS format on the LTA variant which you
> can't do on the LT089AC29000. But I agree, LT089AC29000 is definitely
> not an DPI/RGB panel. At the same time, changing the definition is
> likely to break existing users :-(.

Or the information could just be unused :-)

> > > +		.data = &toshiba_lta089ac29000,
> > >  	}, {
> > >  		.compatible = "tpk,f07a-0102",
> > >  		.data = &tpk_f07a_0102,  

-- 
Regards,

Laurent Pinchart

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

* Re: [PATCH v3 15/21] drm/imx: pd: Use bus format/flags provided by the bridge when available
  2019-10-23 15:45 ` [PATCH v3 15/21] drm/imx: pd: Use bus format/flags provided by the bridge when available Boris Brezillon
@ 2019-12-03 13:50   ` Philipp Zabel
  0 siblings, 0 replies; 76+ messages in thread
From: Philipp Zabel @ 2019-12-03 13:50 UTC (permalink / raw)
  To: Boris Brezillon, 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, Rob Clark,
	Andrzej Hajda, Neil Armstrong, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Rob Herring, Mark Rutland, devicetree

Hi Boris,

On Wed, 2019-10-23 at 17:45 +0200, Boris Brezillon wrote:
> Now that bridges can expose the bus format/flags they expect, we can
> use those instead of the relying on the display_info provided by the
> connector (which is only valid if the encoder is directly connected
> to bridge element driving the panel/display).
> 
> We also explicitly expose the bus formats supported by our encoder by
> filling encoder->output_bus_caps with proper info.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
> ---
> Hi Philipp,
> 
> I think I addressed all your comments except the addition of
> SYNC_DRIVE_POSEDGE/NEGEDGE flags, which would require changing
> ipu_crtc_mode_set_nofb() to take those flags into account or
> turning those flags into their PIXDATA counterpart. If you don't mind,
> I'd like to leave that for later.

I'm fine with this. I think it was phrased as a suggestion, too.

[...]
> -static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder,
> -				       struct drm_crtc_state *crtc_state,
> -				       struct drm_connector_state *conn_state)
> +static const u32 imx_pd_bus_fmts[] = {
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	MEDIA_BUS_FMT_BGR888_1X24,
> +	MEDIA_BUS_FMT_GBR888_1X24,

GBR888 likely isn't useful (it's only used for the internal TV Encoder
on i.MX5 so far), but it doesn't really hurt to include it either.

> +	MEDIA_BUS_FMT_RGB666_1X18,
> +	MEDIA_BUS_FMT_RGB666_1X24_CPADHI,
> +	MEDIA_BUS_FMT_RGB565_1X16,
> +};
> +
> +static u32 *
> +imx_pd_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
> +					 struct drm_bridge_state *bridge_state,
> +					 struct drm_crtc_state *crtc_state,
> +					 struct drm_connector_state *conn_state,
> +					 unsigned int *num_output_fmts)
> +{
> +	struct drm_display_info *di = &conn_state->connector->display_info;
> +	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
> +	u32 *output_fmts;
> +
> +	if (!imxpd->bus_format && !di->num_bus_formats)
> +		*num_output_fmts = ARRAY_SIZE(imx_pd_bus_fmts);

In this case we could just:

		return kmemdup(imx_pd_bus_fmts, sizeof(imx_pd_bus_fmts));

and simplify the remaining function a tiny bit.

> +	else
> +		*num_output_fmts = 1;
> +
> +	output_fmts = kcalloc(*num_output_fmts, sizeof(*output_fmts),
> +			      GFP_KERNEL);
> +	if (!output_fmts)
> +		return NULL;
> +
> +	if (!imxpd->bus_format && di->num_bus_formats)
> +		output_fmts[0] = di->bus_formats[0];
> +	else if (!imxpd->bus_format)
> +		memcpy(output_fmts, imx_pd_bus_fmts,
> +		       ARRAY_SIZE(imx_pd_bus_fmts));
> +	else
> +		output_fmts[0] = imxpd->bus_format;
> +
> +	return output_fmts;
> +}
> +
> +static u32 *
> +imx_pd_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> +					struct drm_bridge_state *bridge_state,
> +					struct drm_crtc_state *crtc_state,
> +					struct drm_connector_state *conn_state,
> +					u32 output_fmt,
> +					unsigned int *num_input_fmts)
> +{
> +	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
> +	u32 *input_fmts;
> +
> +	*num_input_fmts = 0;
> +	if (output_fmt == MEDIA_BUS_FMT_FIXED) {
> +		/*
> +		 * The next bridge does not support bus format negotiation,
> +		 * let's use the default RGB888 value.
> +		 */
> +		*num_input_fmts = 1;
> +		output_fmt = MEDIA_BUS_FMT_RGB888_1X24;
> +	} else if (!imxpd->bus_format) {
> +		unsigned int i;
> +
> +		for (i = 0; i < ARRAY_SIZE(imx_pd_bus_fmts); i++) {
> +			if (imx_pd_bus_fmts[i] == output_fmt) {
> +				*num_input_fmts = 1;
> +				break;
> +			}
> +		}

If this loop was split out into a helper function, it could be reused in
.atomic_check below, for example:

		if (imx_pd_format_supported(output_fmt))
			*num_input_fmts = 1;

> +	} else if (imxpd->bus_format == output_fmt) {
> +		*num_input_fmts = 1;
> +	}
> +
> +	if (!*num_input_fmts)
> +		return NULL;
> +
> +	input_fmts = kcalloc(*num_input_fmts, sizeof(*input_fmts),
> +			     GFP_KERNEL);
> +	if (!input_fmts)
> +		return NULL;
> +
> +	input_fmts[0] = output_fmt;
> +	return input_fmts;
> +}
> +
> +static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge,
> +				      struct drm_bridge_state *bridge_state,
> +				      struct drm_crtc_state *crtc_state,
> +				      struct drm_connector_state *conn_state)
>  {
>  	struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
>  	struct drm_display_info *di = &conn_state->connector->display_info;
> -	struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
> +	struct imx_parallel_display *imxpd = bridge_to_imxpd(bridge);
> +	struct drm_bridge_state *next_bridge_state = NULL;
> +	struct drm_bridge *next_bridge;
> +	u32 bus_flags, bus_fmt;
> +	unsigned int i;
>  
> -	if (!imxpd->bus_format && di->num_bus_formats) {
> -		imx_crtc_state->bus_flags = di->bus_flags;
> -		imx_crtc_state->bus_format = di->bus_formats[0];
> -	} else {
> -		imx_crtc_state->bus_flags = imxpd->bus_flags;
> -		imx_crtc_state->bus_format = imxpd->bus_format;
> +	next_bridge = drm_bridge_chain_get_next_bridge(bridge);
> +	if (next_bridge)
> +		next_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
> +								    next_bridge);
> +
> +	if (next_bridge_state)
> +		bus_flags = next_bridge_state->input_bus_cfg.flags;
> +	else if (!imxpd->bus_format && di->num_bus_formats)
> +		bus_flags = di->bus_flags;
> +	else
> +		bus_flags = imxpd->bus_flags;
> +
> +	bus_fmt = bridge_state->input_bus_cfg.fmt;
> +	for (i = 0; i < ARRAY_SIZE(imx_pd_bus_fmts); i++) {
> +		if (imx_pd_bus_fmts[i] == bus_fmt)
> +			break;
>  	}
> +
> +	if (i == ARRAY_SIZE(imx_pd_bus_fmts))
> +		return -EINVAL;

See above, this could just reuse:

	if (!imx_pd_format_supported(bus_fmt))
		return -EINVAL;

> +
> +	if (bus_flags &
> +	    ~(DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_DE_HIGH |
> +	      DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE |
> +	      DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE))
> +		return -EINVAL;
> +
> +	bridge_state->output_bus_cfg.flags = bus_flags;
> +	bridge_state->input_bus_cfg.flags = bus_flags;
> +	imx_crtc_state->bus_flags = bus_flags;
> +	imx_crtc_state->bus_format = bridge_state->input_bus_cfg.fmt;
>  	imx_crtc_state->di_hsync_pin = 2;
>  	imx_crtc_state->di_vsync_pin = 3;
[...] 

Otherwise this looks good to me.

regards
Philipp


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

end of thread, back to index

Thread overview: 76+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-23 15:44 [PATCH v3 00/21] drm: Add support for bus-format negotiation Boris Brezillon
2019-10-23 15:44 ` [PATCH v3 01/21] drm/vc4: Declare the DSI encoder as a bridge element Boris Brezillon
2019-11-24 10:01   ` Laurent Pinchart
2019-11-24 10:47     ` Boris Brezillon
2019-10-23 15:44 ` [PATCH v3 02/21] drm/exynos: Don't reset bridge->next Boris Brezillon
2019-10-28 12:19   ` Inki Dae
2019-10-23 15:44 ` [PATCH v3 03/21] drm/exynos: Declare the DSI encoder as a bridge element Boris Brezillon
2019-11-24 10:24   ` Laurent Pinchart
2019-11-24 13:17     ` Boris Brezillon
2019-11-24 13:28       ` Boris Brezillon
2019-11-24 14:02       ` Laurent Pinchart
2019-10-23 15:44 ` [PATCH v3 04/21] drm/bridge: Rename bridge helpers targeting a bridge chain Boris Brezillon
2019-10-25 13:26   ` Neil Armstrong
2019-11-24 10:28   ` Laurent Pinchart
2019-10-23 15:44 ` [PATCH v3 05/21] drm/bridge: Introduce drm_bridge_chain_get_next_bridge() Boris Brezillon
2019-10-25 13:27   ` Neil Armstrong
2019-11-24 10:33   ` Laurent Pinchart
2019-11-24 10:56     ` Boris Brezillon
2019-11-24 14:04       ` Laurent Pinchart
2019-10-23 15:44 ` [PATCH v3 06/21] drm: Stop accessing encoder->bridge directly Boris Brezillon
2019-10-25 13:28   ` Neil Armstrong
2019-11-24 10:39   ` Laurent Pinchart
2019-11-24 13:40     ` Boris Brezillon
2019-10-23 15:44 ` [PATCH v3 07/21] drm/bridge: Make the bridge chain a double-linked list Boris Brezillon
2019-10-25 13:29   ` Neil Armstrong
2019-11-05 16:02     ` Neil Armstrong
2019-11-24  7:48       ` Boris Brezillon
2019-11-24 13:57   ` Laurent Pinchart
2019-10-23 15:44 ` [PATCH v3 08/21] drm/bridge: Add the drm_for_each_bridge_in_chain() helper Boris Brezillon
2019-10-25 13:30   ` Neil Armstrong
2019-11-24 14:07   ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 09/21] drm/bridge: Add a drm_bridge_state object Boris Brezillon
2019-10-25 14:35   ` Neil Armstrong
2019-11-05 16:05   ` Neil Armstrong
2019-11-24  7:50     ` Boris Brezillon
2019-12-02 16:42   ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 10/21] drm/bridge: Clarify the atomic enable/disable hooks semantics Boris Brezillon
2019-10-25 14:33   ` Neil Armstrong
2019-12-02 16:50   ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 11/21] drm/bridge: Patch atomic hooks to take a drm_bridge_state Boris Brezillon
2019-12-02 16:57   ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 12/21] drm/bridge: Add an ->atomic_check() hook Boris Brezillon
2019-10-25 14:35   ` Neil Armstrong
2019-12-02 17:03   ` Laurent Pinchart
2019-12-03 10:11     ` Boris Brezillon
2019-12-03 10:15       ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 13/21] drm/bridge: Add the drm_bridge_chain_get_prev_bridge() helper Boris Brezillon
2019-10-25 14:34   ` Neil Armstrong
2019-12-02 17:05   ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 14/21] drm/bridge: Add the necessary bits to support bus format negotiation Boris Brezillon
2019-12-03 10:03   ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 15/21] drm/imx: pd: Use bus format/flags provided by the bridge when available Boris Brezillon
2019-12-03 13:50   ` Philipp Zabel
2019-10-23 15:45 ` [PATCH v3 16/21] drm/bridge: lvds-encoder: Implement basic bus format negotiation Boris Brezillon
2019-12-03 10:14   ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 17/21] dt-bindings: display: bridge: lvds-transmitter: Add new props Boris Brezillon
2019-10-25 19:57   ` Rob Herring
2019-10-31 13:04     ` Boris Brezillon
2019-12-02 17:11   ` Laurent Pinchart
2019-12-03 12:38     ` Boris Brezillon
2019-12-03 13:22       ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 18/21] drm/bridge: panel: Propage bus format/flags Boris Brezillon
2019-12-03 10:17   ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 19/21] drm/panel: simple: Add support for Toshiba LTA089AC29000 panel Boris Brezillon
2019-12-02 17:17   ` Laurent Pinchart
2019-12-03 12:42     ` Boris Brezillon
2019-12-03 13:28       ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 20/21] dt-bindings: display: panel: Add the LTA089AC29000 variant Boris Brezillon
2019-10-25 19:58   ` Rob Herring
2019-12-02 17:19   ` Laurent Pinchart
2019-10-23 15:45 ` [PATCH v3 21/21] ARM: dts: imx: imx51-zii-rdu1: Fix the display pipeline definition Boris Brezillon
2019-10-24 11:27 ` [PATCH v3 00/21] drm: Add support for bus-format negotiation Neil Armstrong
2019-10-24 13:22   ` Boris Brezillon
2019-11-24  0:46 ` Ezequiel Garcia
2019-11-24  7:32   ` Boris Brezillon
2019-11-24  9:34     ` Ezequiel Garcia

Linux-Devicetree Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-devicetree/0 linux-devicetree/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-devicetree linux-devicetree/ https://lore.kernel.org/linux-devicetree \
		devicetree@vger.kernel.org
	public-inbox-index linux-devicetree

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-devicetree


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git