diff mbox series

[08/60] drm/bridge: Extend bridge API to disable connector creation

Message ID 20190707181937.6250-5-laurent.pinchart@ideasonboard.com (mailing list archive)
State New, archived
Headers show
Series drm/omap: Replace custom display drivers with drm_bridge and drm_panel | expand

Commit Message

Laurent Pinchart July 7, 2019, 6:18 p.m. UTC
Most bridge drivers create a DRM connector to model the connector at the
output of the bridge. This model is historical and has worked pretty
well so far, but causes several issues:

- It prevents supporting more complex display pipelines where DRM
connector operations are split over multiple components. For instance a
pipeline with a bridge connected to the DDC signals to read EDID data,
and another one connected to the HPD signal to detect connection and
disconnection, will not be possible to support through this model.

- It requires every bridge driver to implement similar connector
handling code, resulting in code duplication.

- It assumes that a bridge will either be wired to a connector or to
another bridge, but doesn't support bridges that can be used in both
positions very well (although there is some ad-hoc support for this in
the analogix_dp bridge driver).

In order to solve these issues, ownership of the connector should be
moved to the display controller driver (where it can be implemented
using helpers provided by the core).

Extend the bridge API to allow disabling connector creation in bridge
drivers as a first step towards the new model. The new create_connector
argument to the bridge .attach() operation tells the bridge driver
whether to create a connector. Set the argument to true unconditionally,
and modify all existing bridge drivers to return an error when connector
creation is not requested as they don't support this feature yet.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/arc/arcpgu_hdmi.c                        | 2 +-
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c         | 2 +-
 drivers/gpu/drm/bridge/adv7511/adv7511_drv.c             | 6 +++++-
 drivers/gpu/drm/bridge/analogix-anx78xx.c                | 6 +++++-
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c       | 8 ++++++--
 drivers/gpu/drm/bridge/cdns-dsi.c                        | 6 ++++--
 drivers/gpu/drm/bridge/lvds-encoder.c                    | 4 ++--
 drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 6 +++++-
 drivers/gpu/drm/bridge/nxp-ptn3460.c                     | 6 +++++-
 drivers/gpu/drm/bridge/panel.c                           | 5 ++++-
 drivers/gpu/drm/bridge/parade-ps8622.c                   | 5 ++++-
 drivers/gpu/drm/bridge/sii902x.c                         | 6 +++++-
 drivers/gpu/drm/bridge/sil-sii8620.c                     | 2 +-
 drivers/gpu/drm/bridge/simple-bridge.c                   | 6 +++++-
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c                | 8 ++++++--
 drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c            | 8 +++++---
 drivers/gpu/drm/bridge/tc358764.c                        | 5 ++++-
 drivers/gpu/drm/bridge/tc358767.c                        | 5 ++++-
 drivers/gpu/drm/bridge/thc63lvd1024.c                    | 5 +++--
 drivers/gpu/drm/bridge/ti-sn65dsi86.c                    | 5 ++++-
 drivers/gpu/drm/bridge/ti-tfp410.c                       | 5 ++++-
 drivers/gpu/drm/drm_bridge.c                             | 5 +++--
 drivers/gpu/drm/drm_simple_kms_helper.c                  | 2 +-
 drivers/gpu/drm/exynos/exynos_dp.c                       | 3 ++-
 drivers/gpu/drm/exynos/exynos_drm_dsi.c                  | 4 ++--
 drivers/gpu/drm/exynos/exynos_hdmi.c                     | 2 +-
 drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c                | 2 +-
 drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c             | 2 +-
 drivers/gpu/drm/i2c/tda998x_drv.c                        | 8 ++++++--
 drivers/gpu/drm/imx/imx-ldb.c                            | 2 +-
 drivers/gpu/drm/imx/parallel-display.c                   | 2 +-
 drivers/gpu/drm/mcde/mcde_dsi.c                          | 6 +++++-
 drivers/gpu/drm/mediatek/mtk_dpi.c                       | 2 +-
 drivers/gpu/drm/mediatek/mtk_dsi.c                       | 2 +-
 drivers/gpu/drm/mediatek/mtk_hdmi.c                      | 8 ++++++--
 drivers/gpu/drm/msm/dsi/dsi_manager.c                    | 4 ++--
 drivers/gpu/drm/msm/edp/edp_bridge.c                     | 2 +-
 drivers/gpu/drm/msm/hdmi/hdmi_bridge.c                   | 2 +-
 drivers/gpu/drm/omapdrm/omap_drv.c                       | 3 ++-
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c                | 2 +-
 drivers/gpu/drm/rcar-du/rcar_lvds.c                      | 7 +++++--
 drivers/gpu/drm/rockchip/rockchip_lvds.c                 | 2 +-
 drivers/gpu/drm/rockchip/rockchip_rgb.c                  | 2 +-
 drivers/gpu/drm/sti/sti_dvo.c                            | 2 +-
 drivers/gpu/drm/sti/sti_hda.c                            | 2 +-
 drivers/gpu/drm/sti/sti_hdmi.c                           | 2 +-
 drivers/gpu/drm/stm/ltdc.c                               | 2 +-
 drivers/gpu/drm/sun4i/sun4i_lvds.c                       | 2 +-
 drivers/gpu/drm/sun4i/sun4i_rgb.c                        | 2 +-
 drivers/gpu/drm/tilcdc/tilcdc_external.c                 | 2 +-
 drivers/gpu/drm/vc4/vc4_dpi.c                            | 2 +-
 drivers/gpu/drm/vc4/vc4_dsi.c                            | 2 +-
 include/drm/drm_bridge.h                                 | 4 ++--
 53 files changed, 140 insertions(+), 67 deletions(-)

Comments

Andrzej Hajda July 17, 2019, 6:39 a.m. UTC | #1
On 07.07.2019 20:18, Laurent Pinchart wrote:
> Most bridge drivers create a DRM connector to model the connector at the
> output of the bridge. This model is historical and has worked pretty
> well so far, but causes several issues:
>
> - It prevents supporting more complex display pipelines where DRM
> connector operations are split over multiple components. For instance a
> pipeline with a bridge connected to the DDC signals to read EDID data,
> and another one connected to the HPD signal to detect connection and
> disconnection, will not be possible to support through this model.
>
> - It requires every bridge driver to implement similar connector
> handling code, resulting in code duplication.
>
> - It assumes that a bridge will either be wired to a connector or to
> another bridge, but doesn't support bridges that can be used in both
> positions very well (although there is some ad-hoc support for this in
> the analogix_dp bridge driver).
>
> In order to solve these issues, ownership of the connector should be
> moved to the display controller driver (where it can be implemented
> using helpers provided by the core).
>
> Extend the bridge API to allow disabling connector creation in bridge
> drivers as a first step towards the new model. The new create_connector
> argument to the bridge .attach() operation tells the bridge driver
> whether to create a connector. Set the argument to true unconditionally,
> and modify all existing bridge drivers to return an error when connector
> creation is not requested as they don't support this feature yet.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/gpu/drm/arc/arcpgu_hdmi.c                        | 2 +-
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c         | 2 +-
>  drivers/gpu/drm/bridge/adv7511/adv7511_drv.c             | 6 +++++-
>  drivers/gpu/drm/bridge/analogix-anx78xx.c                | 6 +++++-
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c       | 8 ++++++--
>  drivers/gpu/drm/bridge/cdns-dsi.c                        | 6 ++++--
>  drivers/gpu/drm/bridge/lvds-encoder.c                    | 4 ++--
>  drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 6 +++++-
>  drivers/gpu/drm/bridge/nxp-ptn3460.c                     | 6 +++++-
>  drivers/gpu/drm/bridge/panel.c                           | 5 ++++-
>  drivers/gpu/drm/bridge/parade-ps8622.c                   | 5 ++++-
>  drivers/gpu/drm/bridge/sii902x.c                         | 6 +++++-
>  drivers/gpu/drm/bridge/sil-sii8620.c                     | 2 +-
>  drivers/gpu/drm/bridge/simple-bridge.c                   | 6 +++++-
>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.c                | 8 ++++++--
>  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c            | 8 +++++---
>  drivers/gpu/drm/bridge/tc358764.c                        | 5 ++++-
>  drivers/gpu/drm/bridge/tc358767.c                        | 5 ++++-
>  drivers/gpu/drm/bridge/thc63lvd1024.c                    | 5 +++--
>  drivers/gpu/drm/bridge/ti-sn65dsi86.c                    | 5 ++++-
>  drivers/gpu/drm/bridge/ti-tfp410.c                       | 5 ++++-
>  drivers/gpu/drm/drm_bridge.c                             | 5 +++--
>  drivers/gpu/drm/drm_simple_kms_helper.c                  | 2 +-
>  drivers/gpu/drm/exynos/exynos_dp.c                       | 3 ++-
>  drivers/gpu/drm/exynos/exynos_drm_dsi.c                  | 4 ++--
>  drivers/gpu/drm/exynos/exynos_hdmi.c                     | 2 +-
>  drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c                | 2 +-
>  drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c             | 2 +-
>  drivers/gpu/drm/i2c/tda998x_drv.c                        | 8 ++++++--
>  drivers/gpu/drm/imx/imx-ldb.c                            | 2 +-
>  drivers/gpu/drm/imx/parallel-display.c                   | 2 +-
>  drivers/gpu/drm/mcde/mcde_dsi.c                          | 6 +++++-
>  drivers/gpu/drm/mediatek/mtk_dpi.c                       | 2 +-
>  drivers/gpu/drm/mediatek/mtk_dsi.c                       | 2 +-
>  drivers/gpu/drm/mediatek/mtk_hdmi.c                      | 8 ++++++--
>  drivers/gpu/drm/msm/dsi/dsi_manager.c                    | 4 ++--
>  drivers/gpu/drm/msm/edp/edp_bridge.c                     | 2 +-
>  drivers/gpu/drm/msm/hdmi/hdmi_bridge.c                   | 2 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c                       | 3 ++-
>  drivers/gpu/drm/rcar-du/rcar_du_encoder.c                | 2 +-
>  drivers/gpu/drm/rcar-du/rcar_lvds.c                      | 7 +++++--
>  drivers/gpu/drm/rockchip/rockchip_lvds.c                 | 2 +-
>  drivers/gpu/drm/rockchip/rockchip_rgb.c                  | 2 +-
>  drivers/gpu/drm/sti/sti_dvo.c                            | 2 +-
>  drivers/gpu/drm/sti/sti_hda.c                            | 2 +-
>  drivers/gpu/drm/sti/sti_hdmi.c                           | 2 +-
>  drivers/gpu/drm/stm/ltdc.c                               | 2 +-
>  drivers/gpu/drm/sun4i/sun4i_lvds.c                       | 2 +-
>  drivers/gpu/drm/sun4i/sun4i_rgb.c                        | 2 +-
>  drivers/gpu/drm/tilcdc/tilcdc_external.c                 | 2 +-
>  drivers/gpu/drm/vc4/vc4_dpi.c                            | 2 +-
>  drivers/gpu/drm/vc4/vc4_dsi.c                            | 2 +-
>  include/drm/drm_bridge.h                                 | 4 ++--
>  53 files changed, 140 insertions(+), 67 deletions(-)
>
> diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> index 98aac743cc26..739f2358f1d5 100644
> --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c
> +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> @@ -39,7 +39,7 @@ int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np)
>  		return ret;
>  
>  	/* Link drm_bridge to encoder */
> -	ret = drm_bridge_attach(encoder, bridge, NULL);
> +	ret = drm_bridge_attach(encoder, bridge, NULL, true);


Few suggestions:

1. Maybe it would be more convenient to add flags argument instead of bool:

- code should be more readable: ret = drm_bridge_attach(encoder, bridge,
NULL, DRM_BRIDGE_FLAG_NO_CONNECTOR)

- it can be easily expanded later with other flags, there at least two
drivers which would benefit from DRM_BRIDGE_FLAG_NO_CHAINING flag.

2. If the patch can be applied atomically it is OK as is, if not you can
use preprocessor vararg magic to support new and old syntax, sth like:

#define _drm_bridge_attach(encoder, bridge, prev, flags, optarg...)
__drm_bridge_attach(encoder, bridge, prev, flags)

#define drm_bridge_attach(encoder, bridge, prev, optarg...)
_drm_bridge_attach(encoder, bridge, prev, ##optarg, 0)

3. Maybe more convenient would be to just set the flags directly before
attachment:

    bridge->dont_create_connector = true;

    ret = drm_bridge_attach(encoder, bridge, NULL);

    This way it will be still expandable, and less changes.


Regards

Andrzej


>  	if (ret)
>  		drm_encoder_cleanup(encoder);
>  
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
> index f73d8a92274e..606841d2c0b0 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
> @@ -123,7 +123,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
>  	}
>  
>  	if (bridge) {
> -		ret = drm_bridge_attach(&output->encoder, bridge, NULL);
> +		ret = drm_bridge_attach(&output->encoder, bridge, NULL, true);
>  		if (!ret)
>  			return 0;
>  
> diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
> index f6d2681f6927..c67ba30edec4 100644
> --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
> +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
> @@ -847,11 +847,15 @@ static void adv7511_bridge_mode_set(struct drm_bridge *bridge,
>  	adv7511_mode_set(adv, mode, adj_mode);
>  }
>  
> -static int adv7511_bridge_attach(struct drm_bridge *bridge)
> +static int adv7511_bridge_attach(struct drm_bridge *bridge,
> +				 bool create_connector)
>  {
>  	struct adv7511 *adv = bridge_to_adv7511(bridge);
>  	int ret;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	if (!bridge->encoder) {
>  		DRM_ERROR("Parent encoder object not found");
>  		return -ENODEV;
> diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c
> index 3c7cc5af735c..f72755e59e12 100644
> --- a/drivers/gpu/drm/bridge/analogix-anx78xx.c
> +++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c
> @@ -998,11 +998,15 @@ static const struct drm_connector_funcs anx78xx_connector_funcs = {
>  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>  };
>  
> -static int anx78xx_bridge_attach(struct drm_bridge *bridge)
> +static int anx78xx_bridge_attach(struct drm_bridge *bridge,
> +				 bool create_connector)
>  {
>  	struct anx78xx *anx78xx = bridge_to_anx78xx(bridge);
>  	int err;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	if (!bridge->encoder) {
>  		DRM_ERROR("Parent encoder object not found");
>  		return -ENODEV;
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> index 3f7f4880be09..f6a1bdcc09d6 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> @@ -1179,13 +1179,17 @@ static const struct drm_connector_funcs analogix_dp_connector_funcs = {
>  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>  };
>  
> -static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
> +static int analogix_dp_bridge_attach(struct drm_bridge *bridge,
> +				     bool create_connector)
>  {
>  	struct analogix_dp_device *dp = bridge->driver_private;
>  	struct drm_encoder *encoder = dp->encoder;
>  	struct drm_connector *connector = NULL;
>  	int ret = 0;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	if (!bridge->encoder) {
>  		DRM_ERROR("Parent encoder object not found");
>  		return -ENODEV;
> @@ -1463,7 +1467,7 @@ static int analogix_dp_create_bridge(struct drm_device *drm_dev,
>  	bridge->driver_private = dp;
>  	bridge->funcs = &analogix_dp_bridge_funcs;
>  
> -	ret = drm_bridge_attach(dp->encoder, bridge, NULL);
> +	ret = drm_bridge_attach(dp->encoder, bridge, NULL, true);
>  	if (ret) {
>  		DRM_ERROR("failed to attach drm bridge\n");
>  		return -EINVAL;
> diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
> index 6166dca6be81..45f50852cfbb 100644
> --- a/drivers/gpu/drm/bridge/cdns-dsi.c
> +++ b/drivers/gpu/drm/bridge/cdns-dsi.c
> @@ -645,7 +645,8 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
>  	return 0;
>  }
>  
> -static int cdns_dsi_bridge_attach(struct drm_bridge *bridge)
> +static int cdns_dsi_bridge_attach(struct drm_bridge *bridge,
> +				  bool create_connector)
>  {
>  	struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
>  	struct cdns_dsi *dsi = input_to_dsi(input);
> @@ -657,7 +658,8 @@ static int cdns_dsi_bridge_attach(struct drm_bridge *bridge)
>  		return -ENOTSUPP;
>  	}
>  
> -	return drm_bridge_attach(bridge->encoder, output->bridge, bridge);
> +	return drm_bridge_attach(bridge->encoder, output->bridge, bridge,
> +				 create_connector);
>  }
>  
>  static enum drm_mode_status
> diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c
> index 2ab2c234f26c..bafab97521af 100644
> --- a/drivers/gpu/drm/bridge/lvds-encoder.c
> +++ b/drivers/gpu/drm/bridge/lvds-encoder.c
> @@ -18,14 +18,14 @@ struct lvds_encoder {
>  	struct gpio_desc *powerdown_gpio;
>  };
>  
> -static int lvds_encoder_attach(struct drm_bridge *bridge)
> +static int lvds_encoder_attach(struct drm_bridge *bridge, bool create_connector)
>  {
>  	struct lvds_encoder *lvds_encoder = container_of(bridge,
>  							 struct lvds_encoder,
>  							 bridge);
>  
>  	return drm_bridge_attach(bridge->encoder, lvds_encoder->panel_bridge,
> -				 bridge);
> +				 bridge, create_connector);
>  }
>  
>  static void lvds_encoder_enable(struct drm_bridge *bridge)
> diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
> index 79311f8354bd..4250e2235f50 100644
> --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
> +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
> @@ -206,13 +206,17 @@ static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id)
>  	return IRQ_HANDLED;
>  }
>  
> -static int ge_b850v3_lvds_attach(struct drm_bridge *bridge)
> +static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
> +				 bool create_connector)
>  {
>  	struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector;
>  	struct i2c_client *stdp4028_i2c
>  			= ge_b850v3_lvds_ptr->stdp4028_i2c;
>  	int ret;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	if (!bridge->encoder) {
>  		DRM_ERROR("Parent encoder object not found");
>  		return -ENODEV;
> diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c
> index 98bc650b8c95..6bef439261da 100644
> --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c
> +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c
> @@ -238,11 +238,15 @@ static const struct drm_connector_funcs ptn3460_connector_funcs = {
>  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>  };
>  
> -static int ptn3460_bridge_attach(struct drm_bridge *bridge)
> +static int ptn3460_bridge_attach(struct drm_bridge *bridge,
> +				 bool create_connector)
>  {
>  	struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
>  	int ret;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	if (!bridge->encoder) {
>  		DRM_ERROR("Parent encoder object not found");
>  		return -ENODEV;
> diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> index b12ae3a4c5f1..98ad4abf2409 100644
> --- a/drivers/gpu/drm/bridge/panel.c
> +++ b/drivers/gpu/drm/bridge/panel.c
> @@ -52,12 +52,15 @@ static const struct drm_connector_funcs panel_bridge_connector_funcs = {
>  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>  };
>  
> -static int panel_bridge_attach(struct drm_bridge *bridge)
> +static int panel_bridge_attach(struct drm_bridge *bridge, bool create_connector)
>  {
>  	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
>  	struct drm_connector *connector = &panel_bridge->connector;
>  	int ret;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	if (!bridge->encoder) {
>  		DRM_ERROR("Missing encoder\n");
>  		return -ENODEV;
> diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c
> index 2d88146e4836..b9243d51489b 100644
> --- a/drivers/gpu/drm/bridge/parade-ps8622.c
> +++ b/drivers/gpu/drm/bridge/parade-ps8622.c
> @@ -476,11 +476,14 @@ static const struct drm_connector_funcs ps8622_connector_funcs = {
>  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>  };
>  
> -static int ps8622_attach(struct drm_bridge *bridge)
> +static int ps8622_attach(struct drm_bridge *bridge, bool create_connector)
>  {
>  	struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
>  	int ret;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	if (!bridge->encoder) {
>  		DRM_ERROR("Parent encoder object not found");
>  		return -ENODEV;
> diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
> index dd7aa466b280..18904b1082b6 100644
> --- a/drivers/gpu/drm/bridge/sii902x.c
> +++ b/drivers/gpu/drm/bridge/sii902x.c
> @@ -396,12 +396,16 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
>  	mutex_unlock(&sii902x->mutex);
>  }
>  
> -static int sii902x_bridge_attach(struct drm_bridge *bridge)
> +static int sii902x_bridge_attach(struct drm_bridge *bridge,
> +				 bool create_connector)
>  {
>  	struct sii902x *sii902x = bridge_to_sii902x(bridge);
>  	struct drm_device *drm = bridge->dev;
>  	int ret;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	drm_connector_helper_add(&sii902x->connector,
>  				 &sii902x_connector_helper_funcs);
>  
> diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
> index 0cc293a6ac24..ea6529df7d9c 100644
> --- a/drivers/gpu/drm/bridge/sil-sii8620.c
> +++ b/drivers/gpu/drm/bridge/sil-sii8620.c
> @@ -2203,7 +2203,7 @@ static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
>  	return container_of(bridge, struct sii8620, bridge);
>  }
>  
> -static int sii8620_attach(struct drm_bridge *bridge)
> +static int sii8620_attach(struct drm_bridge *bridge, bool create_connector)
>  {
>  	struct sii8620 *ctx = bridge_to_sii8620(bridge);
>  
> diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c
> index 7495b9bef865..86885eb6e28d 100644
> --- a/drivers/gpu/drm/bridge/simple-bridge.c
> +++ b/drivers/gpu/drm/bridge/simple-bridge.c
> @@ -108,11 +108,15 @@ static const struct drm_connector_funcs simple_bridge_con_funcs = {
>  	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
>  };
>  
> -static int simple_bridge_attach(struct drm_bridge *bridge)
> +static int simple_bridge_attach(struct drm_bridge *bridge,
> +				bool create_connector)
>  {
>  	struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
>  	int ret;
>  
> +	if (!create_connector)
> +		return 0;
> +
>  	if (!bridge->encoder) {
>  		DRM_ERROR("Missing encoder\n");
>  		return -ENODEV;
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> index c6490949d9db..930d67c618dd 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -2174,12 +2174,16 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
>  	.get_modes = dw_hdmi_connector_get_modes,
>  };
>  
> -static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
> +static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
> +				 bool create_connector)
>  {
>  	struct dw_hdmi *hdmi = bridge->driver_private;
>  	struct drm_encoder *encoder = bridge->encoder;
>  	struct drm_connector *connector = &hdmi->connector;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	connector->interlace_allowed = 1;
>  	connector->polled = DRM_CONNECTOR_POLL_HPD;
>  
> @@ -2857,7 +2861,7 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
>  	if (IS_ERR(hdmi))
>  		return hdmi;
>  
> -	ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL);
> +	ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, true);
>  	if (ret) {
>  		dw_hdmi_remove(hdmi);
>  		DRM_ERROR("Failed to initialize bridge with drm\n");
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> index 281c58bab1a1..05cf97ad524f 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> @@ -906,7 +906,8 @@ dw_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge,
>  	return mode_status;
>  }
>  
> -static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
> +static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge,
> +				     bool create_connector)
>  {
>  	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
>  
> @@ -919,7 +920,8 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
>  	bridge->encoder->encoder_type = DRM_MODE_ENCODER_DSI;
>  
>  	/* Attach the panel-bridge to the dsi bridge */
> -	return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge);
> +	return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge,
> +				 create_connector);
>  }
>  
>  static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
> @@ -1064,7 +1066,7 @@ int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder)
>  {
>  	int ret;
>  
> -	ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
> +	ret = drm_bridge_attach(encoder, &dsi->bridge, NULL, true);
>  	if (ret) {
>  		DRM_ERROR("Failed to initialize bridge with drm\n");
>  		return ret;
> diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c
> index 170f162ffa55..6016f3aae42f 100644
> --- a/drivers/gpu/drm/bridge/tc358764.c
> +++ b/drivers/gpu/drm/bridge/tc358764.c
> @@ -348,12 +348,15 @@ static void tc358764_enable(struct drm_bridge *bridge)
>  		dev_err(ctx->dev, "error enabling panel (%d)\n", ret);
>  }
>  
> -static int tc358764_attach(struct drm_bridge *bridge)
> +static int tc358764_attach(struct drm_bridge *bridge, bool create_connector)
>  {
>  	struct tc358764 *ctx = bridge_to_tc358764(bridge);
>  	struct drm_device *drm = bridge->dev;
>  	int ret;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	ctx->connector.polled = DRM_CONNECTOR_POLL_HPD;
>  	ret = drm_connector_init(drm, &ctx->connector,
>  				 &tc358764_connector_funcs,
> diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
> index 13ade28a36a8..e2b2d2660adc 100644
> --- a/drivers/gpu/drm/bridge/tc358767.c
> +++ b/drivers/gpu/drm/bridge/tc358767.c
> @@ -1273,13 +1273,16 @@ static const struct drm_connector_funcs tc_connector_funcs = {
>  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>  };
>  
> -static int tc_bridge_attach(struct drm_bridge *bridge)
> +static int tc_bridge_attach(struct drm_bridge *bridge, bool create_connector)
>  {
>  	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
>  	struct tc_data *tc = bridge_to_tc(bridge);
>  	struct drm_device *drm = bridge->dev;
>  	int ret;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	/* Create DP/eDP connector */
>  	drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs);
>  	ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs,
> diff --git a/drivers/gpu/drm/bridge/thc63lvd1024.c b/drivers/gpu/drm/bridge/thc63lvd1024.c
> index 3d74129b2995..86f3b96f95db 100644
> --- a/drivers/gpu/drm/bridge/thc63lvd1024.c
> +++ b/drivers/gpu/drm/bridge/thc63lvd1024.c
> @@ -42,11 +42,12 @@ static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge)
>  	return container_of(bridge, struct thc63_dev, bridge);
>  }
>  
> -static int thc63_attach(struct drm_bridge *bridge)
> +static int thc63_attach(struct drm_bridge *bridge, bool create_connector)
>  {
>  	struct thc63_dev *thc63 = to_thc63(bridge);
>  
> -	return drm_bridge_attach(bridge->encoder, thc63->next, bridge);
> +	return drm_bridge_attach(bridge->encoder, thc63->next, bridge,
> +				 create_connector);
>  }
>  
>  static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge,
> diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> index b77a52d05061..dbe265f7112d 100644
> --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> @@ -224,7 +224,7 @@ static int ti_sn_bridge_parse_regulators(struct ti_sn_bridge *pdata)
>  				       pdata->supplies);
>  }
>  
> -static int ti_sn_bridge_attach(struct drm_bridge *bridge)
> +static int ti_sn_bridge_attach(struct drm_bridge *bridge, bool create_connector)
>  {
>  	int ret, val;
>  	struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge);
> @@ -235,6 +235,9 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge)
>  						   .node = NULL,
>  						 };
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	ret = drm_connector_init(bridge->dev, &pdata->connector,
>  				 &ti_sn_bridge_connector_funcs,
>  				 DRM_MODE_CONNECTOR_eDP);
> diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c
> index 4e76b2b27374..8d4690e436c3 100644
> --- a/drivers/gpu/drm/bridge/ti-tfp410.c
> +++ b/drivers/gpu/drm/bridge/ti-tfp410.c
> @@ -121,11 +121,14 @@ static const struct drm_connector_funcs tfp410_con_funcs = {
>  	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
>  };
>  
> -static int tfp410_attach(struct drm_bridge *bridge)
> +static int tfp410_attach(struct drm_bridge *bridge, bool create_connector)
>  {
>  	struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
>  	int ret;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	if (!bridge->encoder) {
>  		dev_err(dvi->dev, "Missing encoder\n");
>  		return -ENODEV;
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index cba537c99e43..519577f363e3 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -95,6 +95,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
>   * @encoder: DRM encoder
>   * @bridge: bridge to attach
>   * @previous: previous bridge in the chain (optional)
> + * @create_connector: true if the bridge should create a drm_connector
>   *
>   * Called by a kms driver to link the bridge to an encoder's chain. The previous
>   * argument specifies the previous bridge in the chain. If NULL, the bridge is
> @@ -112,7 +113,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
>   * Zero on success, error code on failure
>   */
>  int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
> -		      struct drm_bridge *previous)
> +		      struct drm_bridge *previous, bool create_connector)
>  {
>  	int ret;
>  
> @@ -129,7 +130,7 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
>  	bridge->encoder = encoder;
>  
>  	if (bridge->funcs->attach) {
> -		ret = bridge->funcs->attach(bridge);
> +		ret = bridge->funcs->attach(bridge, create_connector);
>  		if (ret < 0) {
>  			bridge->dev = NULL;
>  			bridge->encoder = NULL;
> diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
> index b11910f14c46..a367ef1e5081 100644
> --- a/drivers/gpu/drm/drm_simple_kms_helper.c
> +++ b/drivers/gpu/drm/drm_simple_kms_helper.c
> @@ -228,7 +228,7 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
>  int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
>  					  struct drm_bridge *bridge)
>  {
> -	return drm_bridge_attach(&pipe->encoder, bridge, NULL);
> +	return drm_bridge_attach(&pipe->encoder, bridge, NULL, true);
>  }
>  EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
>  
> diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c
> index 3a0f0ba8c63a..02b98e6ca52d 100644
> --- a/drivers/gpu/drm/exynos/exynos_dp.c
> +++ b/drivers/gpu/drm/exynos/exynos_dp.c
> @@ -105,7 +105,8 @@ static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data,
>  
>  	/* Pre-empt DP connector creation if there's a bridge */
>  	if (dp->ptn_bridge) {
> -		ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge);
> +		ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge,
> +					true);
>  		if (ret) {
>  			DRM_DEV_ERROR(dp->dev,
>  				      "Failed to attach bridge to drm\n");
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> index 5f6f523821a2..768acc79d10c 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> @@ -1522,7 +1522,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);
> +		drm_bridge_attach(encoder, out_bridge, NULL, true);
>  		dsi->out_bridge = out_bridge;
>  		encoder->bridge = NULL;
>  	} else {
> @@ -1698,7 +1698,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
>  	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, NULL, true);
>  	}
>  
>  	return mipi_dsi_host_register(&dsi->dsi_host);
> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
> index bc1565f1822a..808c98101d56 100644
> --- a/drivers/gpu/drm/exynos/exynos_hdmi.c
> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
> @@ -952,7 +952,7 @@ static int hdmi_create_connector(struct drm_encoder *encoder)
>  	drm_connector_attach_encoder(connector, encoder);
>  
>  	if (hdata->bridge) {
> -		ret = drm_bridge_attach(encoder, hdata->bridge, NULL);
> +		ret = drm_bridge_attach(encoder, hdata->bridge, NULL, true);
>  		if (ret)
>  			DRM_DEV_ERROR(hdata->dev, "Failed to attach bridge\n");
>  	}
> diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
> index c49e9e3740f8..8d22618ccf2e 100644
> --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
> +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
> @@ -159,5 +159,5 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
>  		return fsl_dcu_attach_panel(fsl_dev, panel);
>  	}
>  
> -	return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL);
> +	return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL, true);
>  }
> diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
> index 3d6c45097f51..eac8ec1512ab 100644
> --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
> +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
> @@ -780,7 +780,7 @@ static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi)
>  	int ret;
>  
>  	/* associate the bridge to dsi encoder */
> -	ret = drm_bridge_attach(encoder, bridge, NULL);
> +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
>  	if (ret) {
>  		DRM_ERROR("failed to attach external bridge\n");
>  		return ret;
> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
> index 3d368c43185f..6b2e648b6c4d 100644
> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> @@ -1366,10 +1366,14 @@ static int tda998x_connector_init(struct tda998x_priv *priv,
>  
>  /* DRM bridge functions */
>  
> -static int tda998x_bridge_attach(struct drm_bridge *bridge)
> +static int tda998x_bridge_attach(struct drm_bridge *bridge,
> +				 bool create_connector)
>  {
>  	struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	return tda998x_connector_init(priv, bridge->dev);
>  }
>  
> @@ -2033,7 +2037,7 @@ static int tda998x_encoder_init(struct device *dev, struct drm_device *drm)
>  	if (ret)
>  		goto err_encoder;
>  
> -	ret = drm_bridge_attach(&priv->encoder, &priv->bridge, NULL);
> +	ret = drm_bridge_attach(&priv->encoder, &priv->bridge, NULL, true);
>  	if (ret)
>  		goto err_bridge;
>  
> diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c
> index 383733302280..845ead25ade7 100644
> --- a/drivers/gpu/drm/imx/imx-ldb.c
> +++ b/drivers/gpu/drm/imx/imx-ldb.c
> @@ -446,7 +446,7 @@ static int imx_ldb_register(struct drm_device *drm,
>  
>  	if (imx_ldb_ch->bridge) {
>  		ret = drm_bridge_attach(&imx_ldb_ch->encoder,
> -					imx_ldb_ch->bridge, NULL);
> +					imx_ldb_ch->bridge, NULL, true);
>  		if (ret) {
>  			DRM_ERROR("Failed to initialize bridge with drm\n");
>  			return ret;
> diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c
> index 1a76de1e8e7b..cd746592d2a7 100644
> --- a/drivers/gpu/drm/imx/parallel-display.c
> +++ b/drivers/gpu/drm/imx/parallel-display.c
> @@ -182,7 +182,7 @@ static int imx_pd_register(struct drm_device *drm,
>  		drm_panel_attach(imxpd->panel, &imxpd->connector);
>  
>  	if (imxpd->bridge) {
> -		ret = drm_bridge_attach(encoder, imxpd->bridge, NULL);
> +		ret = drm_bridge_attach(encoder, imxpd->bridge, NULL, true);
>  		if (ret < 0) {
>  			dev_err(imxpd->dev, "failed to attach bridge: %d\n",
>  				ret);
> diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
> index 07f7090d08b3..f2deadc980f8 100644
> --- a/drivers/gpu/drm/mcde/mcde_dsi.c
> +++ b/drivers/gpu/drm/mcde/mcde_dsi.c
> @@ -817,12 +817,16 @@ mcde_dsi_connector_helper_funcs = {
>  	.get_modes = mcde_dsi_get_modes,
>  };
>  
> -static int mcde_dsi_bridge_attach(struct drm_bridge *bridge)
> +static int mcde_dsi_bridge_attach(struct drm_bridge *bridge,
> +				  bool create_connector)
>  {
>  	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
>  	struct drm_device *drm = bridge->dev;
>  	int ret;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	drm_connector_helper_add(&d->connector,
>  				 &mcde_dsi_connector_helper_funcs);
>  
> diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> index bacd989cc9aa..1ff27bb17016 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> @@ -604,7 +604,7 @@ static int mtk_dpi_bind(struct device *dev, struct device *master, void *data)
>  	/* Currently DPI0 is fixed to be driven by OVL1 */
>  	dpi->encoder.possible_crtcs = BIT(1);
>  
> -	ret = drm_bridge_attach(&dpi->encoder, dpi->bridge, NULL);
> +	ret = drm_bridge_attach(&dpi->encoder, dpi->bridge, NULL, true);
>  	if (ret) {
>  		dev_err(dev, "Failed to attach bridge: %d\n", ret);
>  		goto err_cleanup;
> diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
> index b91c4616644a..9c5bac48b44f 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dsi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
> @@ -819,7 +819,7 @@ static int mtk_dsi_create_conn_enc(struct drm_device *drm, struct mtk_dsi *dsi)
>  
>  	/* If there's a bridge, attach to it and let it create the connector */
>  	if (dsi->bridge) {
> -		ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL);
> +		ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL, true);
>  		if (ret) {
>  			DRM_ERROR("Failed to attach bridge to drm\n");
>  			goto err_encoder_cleanup;
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> index 5d6a9f094df5..d3248a881cf0 100644
> --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> @@ -1290,11 +1290,15 @@ static void mtk_hdmi_hpd_event(bool hpd, struct device *dev)
>   * Bridge callbacks
>   */
>  
> -static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge)
> +static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge,
> +				  bool create_connector)
>  {
>  	struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
>  	int ret;
>  
> +	if (!create_connector)
> +		return -EINVAL;
> +
>  	ret = drm_connector_init(bridge->encoder->dev, &hdmi->conn,
>  				 &mtk_hdmi_connector_funcs,
>  				 DRM_MODE_CONNECTOR_HDMIA);
> @@ -1318,7 +1322,7 @@ static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge)
>  
>  	if (hdmi->next_bridge) {
>  		ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge,
> -					bridge);
> +					bridge, create_connector);
>  		if (ret) {
>  			dev_err(hdmi->dev,
>  				"Failed to attach external bridge: %d\n", ret);
> diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c
> index 271aa7bbca92..ca733086041a 100644
> --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c
> +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c
> @@ -664,7 +664,7 @@ struct drm_bridge *msm_dsi_manager_bridge_init(u8 id)
>  	bridge = &dsi_bridge->base;
>  	bridge->funcs = &dsi_mgr_bridge_funcs;
>  
> -	ret = drm_bridge_attach(encoder, bridge, NULL);
> +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
>  	if (ret)
>  		goto fail;
>  
> @@ -693,7 +693,7 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id)
>  	encoder = msm_dsi->encoder;
>  
>  	/* link the internal dsi bridge to the external bridge */
> -	drm_bridge_attach(encoder, ext_bridge, int_bridge);
> +	drm_bridge_attach(encoder, ext_bridge, int_bridge, true);
>  
>  	/*
>  	 * we need the drm_connector created by the external bridge
> diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c
> index 2950bba4aca9..32a463c84cc1 100644
> --- a/drivers/gpu/drm/msm/edp/edp_bridge.c
> +++ b/drivers/gpu/drm/msm/edp/edp_bridge.c
> @@ -91,7 +91,7 @@ struct drm_bridge *msm_edp_bridge_init(struct msm_edp *edp)
>  	bridge = &edp_bridge->base;
>  	bridge->funcs = &edp_bridge_funcs;
>  
> -	ret = drm_bridge_attach(edp->encoder, bridge, NULL);
> +	ret = drm_bridge_attach(edp->encoder, bridge, NULL, true);
>  	if (ret)
>  		goto fail;
>  
> diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
> index 03197b8959ba..d7738aafcff8 100644
> --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
> +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
> @@ -296,7 +296,7 @@ struct drm_bridge *msm_hdmi_bridge_init(struct hdmi *hdmi)
>  	bridge = &hdmi_bridge->base;
>  	bridge->funcs = &msm_hdmi_bridge_funcs;
>  
> -	ret = drm_bridge_attach(hdmi->encoder, bridge, NULL);
> +	ret = drm_bridge_attach(hdmi->encoder, bridge, NULL, true);
>  	if (ret)
>  		goto fail;
>  
> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
> index 672e0f8ad11c..837d0cd20dd1 100644
> --- a/drivers/gpu/drm/omapdrm/omap_drv.c
> +++ b/drivers/gpu/drm/omapdrm/omap_drv.c
> @@ -301,7 +301,8 @@ static int omap_modeset_init(struct drm_device *dev)
>  
>  		if (pipe->output->bridge) {
>  			ret = drm_bridge_attach(pipe->encoder,
> -						pipe->output->bridge, NULL);
> +						pipe->output->bridge, NULL,
> +						true);
>  			if (ret < 0)
>  				return ret;
>  		}
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> index 0f00bdfe2366..74c2ae5ce687 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> @@ -120,7 +120,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
>  	 * Attach the bridge to the encoder. The bridge will create the
>  	 * connector.
>  	 */
> -	ret = drm_bridge_attach(encoder, bridge, NULL);
> +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
>  	if (ret) {
>  		drm_encoder_cleanup(encoder);
>  		return ret;
> diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c
> index 1c62578590f4..a8d8b05c4731 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c
> @@ -605,7 +605,7 @@ static void rcar_lvds_mode_set(struct drm_bridge *bridge,
>  	rcar_lvds_get_lvds_mode(lvds);
>  }
>  
> -static int rcar_lvds_attach(struct drm_bridge *bridge)
> +static int rcar_lvds_attach(struct drm_bridge *bridge, bool create_connector)
>  {
>  	struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
>  	struct drm_connector *connector = &lvds->connector;
> @@ -615,7 +615,10 @@ static int rcar_lvds_attach(struct drm_bridge *bridge)
>  	/* If we have a next bridge just attach it. */
>  	if (lvds->next_bridge)
>  		return drm_bridge_attach(bridge->encoder, lvds->next_bridge,
> -					 bridge);
> +					 bridge, create_connector);
> +
> +	if (!create_connector)
> +		return -EINVAL;
>  
>  	/* Otherwise if we have a panel, create a connector. */
>  	if (!lvds->panel)
> diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
> index 830858a809e5..7ca412d294b2 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
> @@ -440,7 +440,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
>  			goto err_free_connector;
>  		}
>  	} else {
> -		ret = drm_bridge_attach(encoder, lvds->bridge, NULL);
> +		ret = drm_bridge_attach(encoder, lvds->bridge, NULL, true);
>  		if (ret) {
>  			DRM_DEV_ERROR(drm_dev->dev,
>  				      "failed to attach bridge: %d\n", ret);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c
> index ce4d82d293e4..8218bbd09a72 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_rgb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c
> @@ -143,7 +143,7 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
>  
>  	rgb->bridge = bridge;
>  
> -	ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
> +	ret = drm_bridge_attach(encoder, rgb->bridge, NULL, true);
>  	if (ret) {
>  		DRM_DEV_ERROR(drm_dev->dev,
>  			      "failed to attach bridge: %d\n", ret);
> diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c
> index 9e6d5d8b7030..f09209621568 100644
> --- a/drivers/gpu/drm/sti/sti_dvo.c
> +++ b/drivers/gpu/drm/sti/sti_dvo.c
> @@ -468,7 +468,7 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data)
>  	bridge->of_node = dvo->dev.of_node;
>  	drm_bridge_add(bridge);
>  
> -	err = drm_bridge_attach(encoder, bridge, NULL);
> +	err = drm_bridge_attach(encoder, bridge, NULL, true);
>  	if (err) {
>  		DRM_ERROR("Failed to attach bridge\n");
>  		return err;
> diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
> index 94e404f13234..87e0fb742dc8 100644
> --- a/drivers/gpu/drm/sti/sti_hda.c
> +++ b/drivers/gpu/drm/sti/sti_hda.c
> @@ -700,7 +700,7 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data)
>  
>  	bridge->driver_private = hda;
>  	bridge->funcs = &sti_hda_bridge_funcs;
> -	drm_bridge_attach(encoder, bridge, NULL);
> +	drm_bridge_attach(encoder, bridge, NULL, true);
>  
>  	connector->encoder = encoder;
>  
> diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
> index f03d617edc4c..8c0ffe6833f9 100644
> --- a/drivers/gpu/drm/sti/sti_hdmi.c
> +++ b/drivers/gpu/drm/sti/sti_hdmi.c
> @@ -1276,7 +1276,7 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
>  
>  	bridge->driver_private = hdmi;
>  	bridge->funcs = &sti_hdmi_bridge_funcs;
> -	drm_bridge_attach(encoder, bridge, NULL);
> +	drm_bridge_attach(encoder, bridge, NULL, true);
>  
>  	connector->encoder = encoder;
>  
> diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
> index 2fe6c4a8d915..10a9f848c5f6 100644
> --- a/drivers/gpu/drm/stm/ltdc.c
> +++ b/drivers/gpu/drm/stm/ltdc.c
> @@ -1053,7 +1053,7 @@ static int ltdc_encoder_init(struct drm_device *ddev, struct drm_bridge *bridge)
>  	drm_encoder_init(ddev, encoder, &ltdc_encoder_funcs,
>  			 DRM_MODE_ENCODER_DPI, NULL);
>  
> -	ret = drm_bridge_attach(encoder, bridge, NULL);
> +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
>  	if (ret) {
>  		drm_encoder_cleanup(encoder);
>  		return -EINVAL;
> diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> index 3a3ba99fed22..3e5170fa1e67 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_lvds.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> @@ -155,7 +155,7 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
>  	}
>  
>  	if (bridge) {
> -		ret = drm_bridge_attach(encoder, bridge, NULL);
> +		ret = drm_bridge_attach(encoder, bridge, NULL, true);
>  		if (ret) {
>  			dev_err(drm->dev, "Couldn't attach our bridge\n");
>  			goto err_cleanup_connector;
> diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
> index a901ec689b62..3f8629445fbb 100644
> --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c
> +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
> @@ -252,7 +252,7 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
>  	}
>  
>  	if (rgb->bridge) {
> -		ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
> +		ret = drm_bridge_attach(encoder, rgb->bridge, NULL, true);
>  		if (ret) {
>  			dev_err(drm->dev, "Couldn't attach our bridge\n");
>  			goto err_cleanup_connector;
> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c
> index e9969cd36610..ec693c11e455 100644
> --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c
> +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c
> @@ -168,7 +168,7 @@ int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge)
>  
>  	priv->external_encoder->possible_crtcs = BIT(0);
>  
> -	ret = drm_bridge_attach(priv->external_encoder, bridge, NULL);
> +	ret = drm_bridge_attach(priv->external_encoder, bridge, NULL, true);
>  	if (ret) {
>  		dev_err(ddev->dev, "drm_bridge_attach() failed %d\n", ret);
>  		return ret;
> diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
> index 34f90ca8f479..2d7c5cf0d468 100644
> --- a/drivers/gpu/drm/vc4/vc4_dpi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
> @@ -262,7 +262,7 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
>  	if (panel)
>  		bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DPI);
>  
> -	return drm_bridge_attach(dpi->encoder, bridge, NULL);
> +	return drm_bridge_attach(dpi->encoder, bridge, NULL, true);
>  }
>  
>  static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index 2ea4e20b7b8a..3edd7ffc7383 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> @@ -1607,7 +1607,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>  			 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);
> +	ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL, true);
>  	if (ret) {
>  		dev_err(dev, "bridge attach failed: %d\n", ret);
>  		return ret;
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index 7616f6562fe4..08dc15f93ded 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -48,7 +48,7 @@ struct drm_bridge_funcs {
>  	 *
>  	 * Zero on success, error code on failure.
>  	 */
> -	int (*attach)(struct drm_bridge *bridge);
> +	int (*attach)(struct drm_bridge *bridge, bool create_connector);
>  
>  	/**
>  	 * @detach:
> @@ -404,7 +404,7 @@ 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);
>  int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
> -		      struct drm_bridge *previous);
> +		      struct drm_bridge *previous, bool create_connector);
>  
>  bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
>  			   const struct drm_display_mode *mode,
Laurent Pinchart Aug. 8, 2019, 2:25 p.m. UTC | #2
Hi Andrzej,

On Wed, Jul 17, 2019 at 08:39:47AM +0200, Andrzej Hajda wrote:
> On 07.07.2019 20:18, Laurent Pinchart wrote:
> > Most bridge drivers create a DRM connector to model the connector at the
> > output of the bridge. This model is historical and has worked pretty
> > well so far, but causes several issues:
> >
> > - It prevents supporting more complex display pipelines where DRM
> > connector operations are split over multiple components. For instance a
> > pipeline with a bridge connected to the DDC signals to read EDID data,
> > and another one connected to the HPD signal to detect connection and
> > disconnection, will not be possible to support through this model.
> >
> > - It requires every bridge driver to implement similar connector
> > handling code, resulting in code duplication.
> >
> > - It assumes that a bridge will either be wired to a connector or to
> > another bridge, but doesn't support bridges that can be used in both
> > positions very well (although there is some ad-hoc support for this in
> > the analogix_dp bridge driver).
> >
> > In order to solve these issues, ownership of the connector should be
> > moved to the display controller driver (where it can be implemented
> > using helpers provided by the core).
> >
> > Extend the bridge API to allow disabling connector creation in bridge
> > drivers as a first step towards the new model. The new create_connector
> > argument to the bridge .attach() operation tells the bridge driver
> > whether to create a connector. Set the argument to true unconditionally,
> > and modify all existing bridge drivers to return an error when connector
> > creation is not requested as they don't support this feature yet.
> >
> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  drivers/gpu/drm/arc/arcpgu_hdmi.c                        | 2 +-
> >  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c         | 2 +-
> >  drivers/gpu/drm/bridge/adv7511/adv7511_drv.c             | 6 +++++-
> >  drivers/gpu/drm/bridge/analogix-anx78xx.c                | 6 +++++-
> >  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c       | 8 ++++++--
> >  drivers/gpu/drm/bridge/cdns-dsi.c                        | 6 ++++--
> >  drivers/gpu/drm/bridge/lvds-encoder.c                    | 4 ++--
> >  drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 6 +++++-
> >  drivers/gpu/drm/bridge/nxp-ptn3460.c                     | 6 +++++-
> >  drivers/gpu/drm/bridge/panel.c                           | 5 ++++-
> >  drivers/gpu/drm/bridge/parade-ps8622.c                   | 5 ++++-
> >  drivers/gpu/drm/bridge/sii902x.c                         | 6 +++++-
> >  drivers/gpu/drm/bridge/sil-sii8620.c                     | 2 +-
> >  drivers/gpu/drm/bridge/simple-bridge.c                   | 6 +++++-
> >  drivers/gpu/drm/bridge/synopsys/dw-hdmi.c                | 8 ++++++--
> >  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c            | 8 +++++---
> >  drivers/gpu/drm/bridge/tc358764.c                        | 5 ++++-
> >  drivers/gpu/drm/bridge/tc358767.c                        | 5 ++++-
> >  drivers/gpu/drm/bridge/thc63lvd1024.c                    | 5 +++--
> >  drivers/gpu/drm/bridge/ti-sn65dsi86.c                    | 5 ++++-
> >  drivers/gpu/drm/bridge/ti-tfp410.c                       | 5 ++++-
> >  drivers/gpu/drm/drm_bridge.c                             | 5 +++--
> >  drivers/gpu/drm/drm_simple_kms_helper.c                  | 2 +-
> >  drivers/gpu/drm/exynos/exynos_dp.c                       | 3 ++-
> >  drivers/gpu/drm/exynos/exynos_drm_dsi.c                  | 4 ++--
> >  drivers/gpu/drm/exynos/exynos_hdmi.c                     | 2 +-
> >  drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c                | 2 +-
> >  drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c             | 2 +-
> >  drivers/gpu/drm/i2c/tda998x_drv.c                        | 8 ++++++--
> >  drivers/gpu/drm/imx/imx-ldb.c                            | 2 +-
> >  drivers/gpu/drm/imx/parallel-display.c                   | 2 +-
> >  drivers/gpu/drm/mcde/mcde_dsi.c                          | 6 +++++-
> >  drivers/gpu/drm/mediatek/mtk_dpi.c                       | 2 +-
> >  drivers/gpu/drm/mediatek/mtk_dsi.c                       | 2 +-
> >  drivers/gpu/drm/mediatek/mtk_hdmi.c                      | 8 ++++++--
> >  drivers/gpu/drm/msm/dsi/dsi_manager.c                    | 4 ++--
> >  drivers/gpu/drm/msm/edp/edp_bridge.c                     | 2 +-
> >  drivers/gpu/drm/msm/hdmi/hdmi_bridge.c                   | 2 +-
> >  drivers/gpu/drm/omapdrm/omap_drv.c                       | 3 ++-
> >  drivers/gpu/drm/rcar-du/rcar_du_encoder.c                | 2 +-
> >  drivers/gpu/drm/rcar-du/rcar_lvds.c                      | 7 +++++--
> >  drivers/gpu/drm/rockchip/rockchip_lvds.c                 | 2 +-
> >  drivers/gpu/drm/rockchip/rockchip_rgb.c                  | 2 +-
> >  drivers/gpu/drm/sti/sti_dvo.c                            | 2 +-
> >  drivers/gpu/drm/sti/sti_hda.c                            | 2 +-
> >  drivers/gpu/drm/sti/sti_hdmi.c                           | 2 +-
> >  drivers/gpu/drm/stm/ltdc.c                               | 2 +-
> >  drivers/gpu/drm/sun4i/sun4i_lvds.c                       | 2 +-
> >  drivers/gpu/drm/sun4i/sun4i_rgb.c                        | 2 +-
> >  drivers/gpu/drm/tilcdc/tilcdc_external.c                 | 2 +-
> >  drivers/gpu/drm/vc4/vc4_dpi.c                            | 2 +-
> >  drivers/gpu/drm/vc4/vc4_dsi.c                            | 2 +-
> >  include/drm/drm_bridge.h                                 | 4 ++--
> >  53 files changed, 140 insertions(+), 67 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> > index 98aac743cc26..739f2358f1d5 100644
> > --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c
> > +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> > @@ -39,7 +39,7 @@ int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np)
> >  		return ret;
> >  
> >  	/* Link drm_bridge to encoder */
> > -	ret = drm_bridge_attach(encoder, bridge, NULL);
> > +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
> 
> Few suggestions:
> 
> 1. Maybe it would be more convenient to add flags argument instead of bool:
> 
> - code should be more readable: ret = drm_bridge_attach(encoder, bridge,
> NULL, DRM_BRIDGE_FLAG_NO_CONNECTOR)
> 
> - it can be easily expanded later with other flags, there at least two
> drivers which would benefit from DRM_BRIDGE_FLAG_NO_CHAINING flag.

Please note that I think this flag should disappear once drivers get
converted to the new model. This will however take some time. I'm not
opposed to turning the book into a flag though. I was hoping to receive
more review comments on this particular patch, but as that's not the
case, I can already proceed with the change.

What would the DRM_BRIDGE_FLAG_NO_CHAINING flag be used for ?

> 2. If the patch can be applied atomically it is OK as is, if not you can
> use preprocessor vararg magic to support new and old syntax, sth like:
> 
> #define _drm_bridge_attach(encoder, bridge, prev, flags, optarg...)
> __drm_bridge_attach(encoder, bridge, prev, flags)
> 
> #define drm_bridge_attach(encoder, bridge, prev, optarg...)
> _drm_bridge_attach(encoder, bridge, prev, ##optarg, 0)

Good point. I'll try to do this atomically, but if it fails I'll follow
your suggestion.

> 3. Maybe more convenient would be to just set the flags directly before
> attachment:
> 
>     bridge->dont_create_connector = true;
> 
>     ret = drm_bridge_attach(encoder, bridge, NULL);
> 
>     This way it will be still expandable, and less changes.

Bridges that are chained would need to set the dont_create_connector
flag of the next bridge. It would be a bit ugly, but would make this
patch smaller. On the other hand we would need to keep the if
(!create_connector) check in the .attach() handlers, and it would be
easier to miss it in bridge drivers (current or new) than with an
explicit argument to the .attach() operation. I would thus have a
preference for the new argument to .attach(). Especially if it can help
you with DRM_BRIDGE_FLAG_NO_CHAINING :-)

> >  	if (ret)
> >  		drm_encoder_cleanup(encoder);
> >  
> > diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
> > index f73d8a92274e..606841d2c0b0 100644
> > --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
> > +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
> > @@ -123,7 +123,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
> >  	}
> >  
> >  	if (bridge) {
> > -		ret = drm_bridge_attach(&output->encoder, bridge, NULL);
> > +		ret = drm_bridge_attach(&output->encoder, bridge, NULL, true);
> >  		if (!ret)
> >  			return 0;
> >  
> > diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
> > index f6d2681f6927..c67ba30edec4 100644
> > --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
> > +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
> > @@ -847,11 +847,15 @@ static void adv7511_bridge_mode_set(struct drm_bridge *bridge,
> >  	adv7511_mode_set(adv, mode, adj_mode);
> >  }
> >  
> > -static int adv7511_bridge_attach(struct drm_bridge *bridge)
> > +static int adv7511_bridge_attach(struct drm_bridge *bridge,
> > +				 bool create_connector)
> >  {
> >  	struct adv7511 *adv = bridge_to_adv7511(bridge);
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	if (!bridge->encoder) {
> >  		DRM_ERROR("Parent encoder object not found");
> >  		return -ENODEV;
> > diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c
> > index 3c7cc5af735c..f72755e59e12 100644
> > --- a/drivers/gpu/drm/bridge/analogix-anx78xx.c
> > +++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c
> > @@ -998,11 +998,15 @@ static const struct drm_connector_funcs anx78xx_connector_funcs = {
> >  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> >  };
> >  
> > -static int anx78xx_bridge_attach(struct drm_bridge *bridge)
> > +static int anx78xx_bridge_attach(struct drm_bridge *bridge,
> > +				 bool create_connector)
> >  {
> >  	struct anx78xx *anx78xx = bridge_to_anx78xx(bridge);
> >  	int err;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	if (!bridge->encoder) {
> >  		DRM_ERROR("Parent encoder object not found");
> >  		return -ENODEV;
> > diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> > index 3f7f4880be09..f6a1bdcc09d6 100644
> > --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> > +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> > @@ -1179,13 +1179,17 @@ static const struct drm_connector_funcs analogix_dp_connector_funcs = {
> >  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> >  };
> >  
> > -static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
> > +static int analogix_dp_bridge_attach(struct drm_bridge *bridge,
> > +				     bool create_connector)
> >  {
> >  	struct analogix_dp_device *dp = bridge->driver_private;
> >  	struct drm_encoder *encoder = dp->encoder;
> >  	struct drm_connector *connector = NULL;
> >  	int ret = 0;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	if (!bridge->encoder) {
> >  		DRM_ERROR("Parent encoder object not found");
> >  		return -ENODEV;
> > @@ -1463,7 +1467,7 @@ static int analogix_dp_create_bridge(struct drm_device *drm_dev,
> >  	bridge->driver_private = dp;
> >  	bridge->funcs = &analogix_dp_bridge_funcs;
> >  
> > -	ret = drm_bridge_attach(dp->encoder, bridge, NULL);
> > +	ret = drm_bridge_attach(dp->encoder, bridge, NULL, true);
> >  	if (ret) {
> >  		DRM_ERROR("failed to attach drm bridge\n");
> >  		return -EINVAL;
> > diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
> > index 6166dca6be81..45f50852cfbb 100644
> > --- a/drivers/gpu/drm/bridge/cdns-dsi.c
> > +++ b/drivers/gpu/drm/bridge/cdns-dsi.c
> > @@ -645,7 +645,8 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
> >  	return 0;
> >  }
> >  
> > -static int cdns_dsi_bridge_attach(struct drm_bridge *bridge)
> > +static int cdns_dsi_bridge_attach(struct drm_bridge *bridge,
> > +				  bool create_connector)
> >  {
> >  	struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
> >  	struct cdns_dsi *dsi = input_to_dsi(input);
> > @@ -657,7 +658,8 @@ static int cdns_dsi_bridge_attach(struct drm_bridge *bridge)
> >  		return -ENOTSUPP;
> >  	}
> >  
> > -	return drm_bridge_attach(bridge->encoder, output->bridge, bridge);
> > +	return drm_bridge_attach(bridge->encoder, output->bridge, bridge,
> > +				 create_connector);
> >  }
> >  
> >  static enum drm_mode_status
> > diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c
> > index 2ab2c234f26c..bafab97521af 100644
> > --- a/drivers/gpu/drm/bridge/lvds-encoder.c
> > +++ b/drivers/gpu/drm/bridge/lvds-encoder.c
> > @@ -18,14 +18,14 @@ struct lvds_encoder {
> >  	struct gpio_desc *powerdown_gpio;
> >  };
> >  
> > -static int lvds_encoder_attach(struct drm_bridge *bridge)
> > +static int lvds_encoder_attach(struct drm_bridge *bridge, bool create_connector)
> >  {
> >  	struct lvds_encoder *lvds_encoder = container_of(bridge,
> >  							 struct lvds_encoder,
> >  							 bridge);
> >  
> >  	return drm_bridge_attach(bridge->encoder, lvds_encoder->panel_bridge,
> > -				 bridge);
> > +				 bridge, create_connector);
> >  }
> >  
> >  static void lvds_encoder_enable(struct drm_bridge *bridge)
> > diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
> > index 79311f8354bd..4250e2235f50 100644
> > --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
> > +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
> > @@ -206,13 +206,17 @@ static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id)
> >  	return IRQ_HANDLED;
> >  }
> >  
> > -static int ge_b850v3_lvds_attach(struct drm_bridge *bridge)
> > +static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
> > +				 bool create_connector)
> >  {
> >  	struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector;
> >  	struct i2c_client *stdp4028_i2c
> >  			= ge_b850v3_lvds_ptr->stdp4028_i2c;
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	if (!bridge->encoder) {
> >  		DRM_ERROR("Parent encoder object not found");
> >  		return -ENODEV;
> > diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c
> > index 98bc650b8c95..6bef439261da 100644
> > --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c
> > +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c
> > @@ -238,11 +238,15 @@ static const struct drm_connector_funcs ptn3460_connector_funcs = {
> >  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> >  };
> >  
> > -static int ptn3460_bridge_attach(struct drm_bridge *bridge)
> > +static int ptn3460_bridge_attach(struct drm_bridge *bridge,
> > +				 bool create_connector)
> >  {
> >  	struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	if (!bridge->encoder) {
> >  		DRM_ERROR("Parent encoder object not found");
> >  		return -ENODEV;
> > diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
> > index b12ae3a4c5f1..98ad4abf2409 100644
> > --- a/drivers/gpu/drm/bridge/panel.c
> > +++ b/drivers/gpu/drm/bridge/panel.c
> > @@ -52,12 +52,15 @@ static const struct drm_connector_funcs panel_bridge_connector_funcs = {
> >  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> >  };
> >  
> > -static int panel_bridge_attach(struct drm_bridge *bridge)
> > +static int panel_bridge_attach(struct drm_bridge *bridge, bool create_connector)
> >  {
> >  	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
> >  	struct drm_connector *connector = &panel_bridge->connector;
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	if (!bridge->encoder) {
> >  		DRM_ERROR("Missing encoder\n");
> >  		return -ENODEV;
> > diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c
> > index 2d88146e4836..b9243d51489b 100644
> > --- a/drivers/gpu/drm/bridge/parade-ps8622.c
> > +++ b/drivers/gpu/drm/bridge/parade-ps8622.c
> > @@ -476,11 +476,14 @@ static const struct drm_connector_funcs ps8622_connector_funcs = {
> >  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> >  };
> >  
> > -static int ps8622_attach(struct drm_bridge *bridge)
> > +static int ps8622_attach(struct drm_bridge *bridge, bool create_connector)
> >  {
> >  	struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	if (!bridge->encoder) {
> >  		DRM_ERROR("Parent encoder object not found");
> >  		return -ENODEV;
> > diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
> > index dd7aa466b280..18904b1082b6 100644
> > --- a/drivers/gpu/drm/bridge/sii902x.c
> > +++ b/drivers/gpu/drm/bridge/sii902x.c
> > @@ -396,12 +396,16 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
> >  	mutex_unlock(&sii902x->mutex);
> >  }
> >  
> > -static int sii902x_bridge_attach(struct drm_bridge *bridge)
> > +static int sii902x_bridge_attach(struct drm_bridge *bridge,
> > +				 bool create_connector)
> >  {
> >  	struct sii902x *sii902x = bridge_to_sii902x(bridge);
> >  	struct drm_device *drm = bridge->dev;
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	drm_connector_helper_add(&sii902x->connector,
> >  				 &sii902x_connector_helper_funcs);
> >  
> > diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
> > index 0cc293a6ac24..ea6529df7d9c 100644
> > --- a/drivers/gpu/drm/bridge/sil-sii8620.c
> > +++ b/drivers/gpu/drm/bridge/sil-sii8620.c
> > @@ -2203,7 +2203,7 @@ static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
> >  	return container_of(bridge, struct sii8620, bridge);
> >  }
> >  
> > -static int sii8620_attach(struct drm_bridge *bridge)
> > +static int sii8620_attach(struct drm_bridge *bridge, bool create_connector)
> >  {
> >  	struct sii8620 *ctx = bridge_to_sii8620(bridge);
> >  
> > diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c
> > index 7495b9bef865..86885eb6e28d 100644
> > --- a/drivers/gpu/drm/bridge/simple-bridge.c
> > +++ b/drivers/gpu/drm/bridge/simple-bridge.c
> > @@ -108,11 +108,15 @@ static const struct drm_connector_funcs simple_bridge_con_funcs = {
> >  	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
> >  };
> >  
> > -static int simple_bridge_attach(struct drm_bridge *bridge)
> > +static int simple_bridge_attach(struct drm_bridge *bridge,
> > +				bool create_connector)
> >  {
> >  	struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return 0;
> > +
> >  	if (!bridge->encoder) {
> >  		DRM_ERROR("Missing encoder\n");
> >  		return -ENODEV;
> > diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> > index c6490949d9db..930d67c618dd 100644
> > --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> > +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> > @@ -2174,12 +2174,16 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
> >  	.get_modes = dw_hdmi_connector_get_modes,
> >  };
> >  
> > -static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
> > +static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
> > +				 bool create_connector)
> >  {
> >  	struct dw_hdmi *hdmi = bridge->driver_private;
> >  	struct drm_encoder *encoder = bridge->encoder;
> >  	struct drm_connector *connector = &hdmi->connector;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	connector->interlace_allowed = 1;
> >  	connector->polled = DRM_CONNECTOR_POLL_HPD;
> >  
> > @@ -2857,7 +2861,7 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
> >  	if (IS_ERR(hdmi))
> >  		return hdmi;
> >  
> > -	ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL);
> > +	ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, true);
> >  	if (ret) {
> >  		dw_hdmi_remove(hdmi);
> >  		DRM_ERROR("Failed to initialize bridge with drm\n");
> > diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> > index 281c58bab1a1..05cf97ad524f 100644
> > --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> > +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> > @@ -906,7 +906,8 @@ dw_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge,
> >  	return mode_status;
> >  }
> >  
> > -static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
> > +static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge,
> > +				     bool create_connector)
> >  {
> >  	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
> >  
> > @@ -919,7 +920,8 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
> >  	bridge->encoder->encoder_type = DRM_MODE_ENCODER_DSI;
> >  
> >  	/* Attach the panel-bridge to the dsi bridge */
> > -	return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge);
> > +	return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge,
> > +				 create_connector);
> >  }
> >  
> >  static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
> > @@ -1064,7 +1066,7 @@ int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder)
> >  {
> >  	int ret;
> >  
> > -	ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
> > +	ret = drm_bridge_attach(encoder, &dsi->bridge, NULL, true);
> >  	if (ret) {
> >  		DRM_ERROR("Failed to initialize bridge with drm\n");
> >  		return ret;
> > diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c
> > index 170f162ffa55..6016f3aae42f 100644
> > --- a/drivers/gpu/drm/bridge/tc358764.c
> > +++ b/drivers/gpu/drm/bridge/tc358764.c
> > @@ -348,12 +348,15 @@ static void tc358764_enable(struct drm_bridge *bridge)
> >  		dev_err(ctx->dev, "error enabling panel (%d)\n", ret);
> >  }
> >  
> > -static int tc358764_attach(struct drm_bridge *bridge)
> > +static int tc358764_attach(struct drm_bridge *bridge, bool create_connector)
> >  {
> >  	struct tc358764 *ctx = bridge_to_tc358764(bridge);
> >  	struct drm_device *drm = bridge->dev;
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	ctx->connector.polled = DRM_CONNECTOR_POLL_HPD;
> >  	ret = drm_connector_init(drm, &ctx->connector,
> >  				 &tc358764_connector_funcs,
> > diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
> > index 13ade28a36a8..e2b2d2660adc 100644
> > --- a/drivers/gpu/drm/bridge/tc358767.c
> > +++ b/drivers/gpu/drm/bridge/tc358767.c
> > @@ -1273,13 +1273,16 @@ static const struct drm_connector_funcs tc_connector_funcs = {
> >  	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> >  };
> >  
> > -static int tc_bridge_attach(struct drm_bridge *bridge)
> > +static int tc_bridge_attach(struct drm_bridge *bridge, bool create_connector)
> >  {
> >  	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
> >  	struct tc_data *tc = bridge_to_tc(bridge);
> >  	struct drm_device *drm = bridge->dev;
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	/* Create DP/eDP connector */
> >  	drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs);
> >  	ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs,
> > diff --git a/drivers/gpu/drm/bridge/thc63lvd1024.c b/drivers/gpu/drm/bridge/thc63lvd1024.c
> > index 3d74129b2995..86f3b96f95db 100644
> > --- a/drivers/gpu/drm/bridge/thc63lvd1024.c
> > +++ b/drivers/gpu/drm/bridge/thc63lvd1024.c
> > @@ -42,11 +42,12 @@ static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge)
> >  	return container_of(bridge, struct thc63_dev, bridge);
> >  }
> >  
> > -static int thc63_attach(struct drm_bridge *bridge)
> > +static int thc63_attach(struct drm_bridge *bridge, bool create_connector)
> >  {
> >  	struct thc63_dev *thc63 = to_thc63(bridge);
> >  
> > -	return drm_bridge_attach(bridge->encoder, thc63->next, bridge);
> > +	return drm_bridge_attach(bridge->encoder, thc63->next, bridge,
> > +				 create_connector);
> >  }
> >  
> >  static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge,
> > diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> > index b77a52d05061..dbe265f7112d 100644
> > --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> > +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
> > @@ -224,7 +224,7 @@ static int ti_sn_bridge_parse_regulators(struct ti_sn_bridge *pdata)
> >  				       pdata->supplies);
> >  }
> >  
> > -static int ti_sn_bridge_attach(struct drm_bridge *bridge)
> > +static int ti_sn_bridge_attach(struct drm_bridge *bridge, bool create_connector)
> >  {
> >  	int ret, val;
> >  	struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge);
> > @@ -235,6 +235,9 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge)
> >  						   .node = NULL,
> >  						 };
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	ret = drm_connector_init(bridge->dev, &pdata->connector,
> >  				 &ti_sn_bridge_connector_funcs,
> >  				 DRM_MODE_CONNECTOR_eDP);
> > diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c
> > index 4e76b2b27374..8d4690e436c3 100644
> > --- a/drivers/gpu/drm/bridge/ti-tfp410.c
> > +++ b/drivers/gpu/drm/bridge/ti-tfp410.c
> > @@ -121,11 +121,14 @@ static const struct drm_connector_funcs tfp410_con_funcs = {
> >  	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
> >  };
> >  
> > -static int tfp410_attach(struct drm_bridge *bridge)
> > +static int tfp410_attach(struct drm_bridge *bridge, bool create_connector)
> >  {
> >  	struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	if (!bridge->encoder) {
> >  		dev_err(dvi->dev, "Missing encoder\n");
> >  		return -ENODEV;
> > diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> > index cba537c99e43..519577f363e3 100644
> > --- a/drivers/gpu/drm/drm_bridge.c
> > +++ b/drivers/gpu/drm/drm_bridge.c
> > @@ -95,6 +95,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
> >   * @encoder: DRM encoder
> >   * @bridge: bridge to attach
> >   * @previous: previous bridge in the chain (optional)
> > + * @create_connector: true if the bridge should create a drm_connector
> >   *
> >   * Called by a kms driver to link the bridge to an encoder's chain. The previous
> >   * argument specifies the previous bridge in the chain. If NULL, the bridge is
> > @@ -112,7 +113,7 @@ EXPORT_SYMBOL(drm_bridge_remove);
> >   * Zero on success, error code on failure
> >   */
> >  int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
> > -		      struct drm_bridge *previous)
> > +		      struct drm_bridge *previous, bool create_connector)
> >  {
> >  	int ret;
> >  
> > @@ -129,7 +130,7 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
> >  	bridge->encoder = encoder;
> >  
> >  	if (bridge->funcs->attach) {
> > -		ret = bridge->funcs->attach(bridge);
> > +		ret = bridge->funcs->attach(bridge, create_connector);
> >  		if (ret < 0) {
> >  			bridge->dev = NULL;
> >  			bridge->encoder = NULL;
> > diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
> > index b11910f14c46..a367ef1e5081 100644
> > --- a/drivers/gpu/drm/drm_simple_kms_helper.c
> > +++ b/drivers/gpu/drm/drm_simple_kms_helper.c
> > @@ -228,7 +228,7 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
> >  int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
> >  					  struct drm_bridge *bridge)
> >  {
> > -	return drm_bridge_attach(&pipe->encoder, bridge, NULL);
> > +	return drm_bridge_attach(&pipe->encoder, bridge, NULL, true);
> >  }
> >  EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
> >  
> > diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c
> > index 3a0f0ba8c63a..02b98e6ca52d 100644
> > --- a/drivers/gpu/drm/exynos/exynos_dp.c
> > +++ b/drivers/gpu/drm/exynos/exynos_dp.c
> > @@ -105,7 +105,8 @@ static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data,
> >  
> >  	/* Pre-empt DP connector creation if there's a bridge */
> >  	if (dp->ptn_bridge) {
> > -		ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge);
> > +		ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge,
> > +					true);
> >  		if (ret) {
> >  			DRM_DEV_ERROR(dp->dev,
> >  				      "Failed to attach bridge to drm\n");
> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > index 5f6f523821a2..768acc79d10c 100644
> > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > @@ -1522,7 +1522,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);
> > +		drm_bridge_attach(encoder, out_bridge, NULL, true);
> >  		dsi->out_bridge = out_bridge;
> >  		encoder->bridge = NULL;
> >  	} else {
> > @@ -1698,7 +1698,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
> >  	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, NULL, true);
> >  	}
> >  
> >  	return mipi_dsi_host_register(&dsi->dsi_host);
> > diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
> > index bc1565f1822a..808c98101d56 100644
> > --- a/drivers/gpu/drm/exynos/exynos_hdmi.c
> > +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
> > @@ -952,7 +952,7 @@ static int hdmi_create_connector(struct drm_encoder *encoder)
> >  	drm_connector_attach_encoder(connector, encoder);
> >  
> >  	if (hdata->bridge) {
> > -		ret = drm_bridge_attach(encoder, hdata->bridge, NULL);
> > +		ret = drm_bridge_attach(encoder, hdata->bridge, NULL, true);
> >  		if (ret)
> >  			DRM_DEV_ERROR(hdata->dev, "Failed to attach bridge\n");
> >  	}
> > diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
> > index c49e9e3740f8..8d22618ccf2e 100644
> > --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
> > +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
> > @@ -159,5 +159,5 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
> >  		return fsl_dcu_attach_panel(fsl_dev, panel);
> >  	}
> >  
> > -	return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL);
> > +	return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL, true);
> >  }
> > diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
> > index 3d6c45097f51..eac8ec1512ab 100644
> > --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
> > +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
> > @@ -780,7 +780,7 @@ static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi)
> >  	int ret;
> >  
> >  	/* associate the bridge to dsi encoder */
> > -	ret = drm_bridge_attach(encoder, bridge, NULL);
> > +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
> >  	if (ret) {
> >  		DRM_ERROR("failed to attach external bridge\n");
> >  		return ret;
> > diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
> > index 3d368c43185f..6b2e648b6c4d 100644
> > --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> > +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> > @@ -1366,10 +1366,14 @@ static int tda998x_connector_init(struct tda998x_priv *priv,
> >  
> >  /* DRM bridge functions */
> >  
> > -static int tda998x_bridge_attach(struct drm_bridge *bridge)
> > +static int tda998x_bridge_attach(struct drm_bridge *bridge,
> > +				 bool create_connector)
> >  {
> >  	struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	return tda998x_connector_init(priv, bridge->dev);
> >  }
> >  
> > @@ -2033,7 +2037,7 @@ static int tda998x_encoder_init(struct device *dev, struct drm_device *drm)
> >  	if (ret)
> >  		goto err_encoder;
> >  
> > -	ret = drm_bridge_attach(&priv->encoder, &priv->bridge, NULL);
> > +	ret = drm_bridge_attach(&priv->encoder, &priv->bridge, NULL, true);
> >  	if (ret)
> >  		goto err_bridge;
> >  
> > diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c
> > index 383733302280..845ead25ade7 100644
> > --- a/drivers/gpu/drm/imx/imx-ldb.c
> > +++ b/drivers/gpu/drm/imx/imx-ldb.c
> > @@ -446,7 +446,7 @@ static int imx_ldb_register(struct drm_device *drm,
> >  
> >  	if (imx_ldb_ch->bridge) {
> >  		ret = drm_bridge_attach(&imx_ldb_ch->encoder,
> > -					imx_ldb_ch->bridge, NULL);
> > +					imx_ldb_ch->bridge, NULL, true);
> >  		if (ret) {
> >  			DRM_ERROR("Failed to initialize bridge with drm\n");
> >  			return ret;
> > diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c
> > index 1a76de1e8e7b..cd746592d2a7 100644
> > --- a/drivers/gpu/drm/imx/parallel-display.c
> > +++ b/drivers/gpu/drm/imx/parallel-display.c
> > @@ -182,7 +182,7 @@ static int imx_pd_register(struct drm_device *drm,
> >  		drm_panel_attach(imxpd->panel, &imxpd->connector);
> >  
> >  	if (imxpd->bridge) {
> > -		ret = drm_bridge_attach(encoder, imxpd->bridge, NULL);
> > +		ret = drm_bridge_attach(encoder, imxpd->bridge, NULL, true);
> >  		if (ret < 0) {
> >  			dev_err(imxpd->dev, "failed to attach bridge: %d\n",
> >  				ret);
> > diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
> > index 07f7090d08b3..f2deadc980f8 100644
> > --- a/drivers/gpu/drm/mcde/mcde_dsi.c
> > +++ b/drivers/gpu/drm/mcde/mcde_dsi.c
> > @@ -817,12 +817,16 @@ mcde_dsi_connector_helper_funcs = {
> >  	.get_modes = mcde_dsi_get_modes,
> >  };
> >  
> > -static int mcde_dsi_bridge_attach(struct drm_bridge *bridge)
> > +static int mcde_dsi_bridge_attach(struct drm_bridge *bridge,
> > +				  bool create_connector)
> >  {
> >  	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
> >  	struct drm_device *drm = bridge->dev;
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	drm_connector_helper_add(&d->connector,
> >  				 &mcde_dsi_connector_helper_funcs);
> >  
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > index bacd989cc9aa..1ff27bb17016 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dpi.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
> > @@ -604,7 +604,7 @@ static int mtk_dpi_bind(struct device *dev, struct device *master, void *data)
> >  	/* Currently DPI0 is fixed to be driven by OVL1 */
> >  	dpi->encoder.possible_crtcs = BIT(1);
> >  
> > -	ret = drm_bridge_attach(&dpi->encoder, dpi->bridge, NULL);
> > +	ret = drm_bridge_attach(&dpi->encoder, dpi->bridge, NULL, true);
> >  	if (ret) {
> >  		dev_err(dev, "Failed to attach bridge: %d\n", ret);
> >  		goto err_cleanup;
> > diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
> > index b91c4616644a..9c5bac48b44f 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_dsi.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
> > @@ -819,7 +819,7 @@ static int mtk_dsi_create_conn_enc(struct drm_device *drm, struct mtk_dsi *dsi)
> >  
> >  	/* If there's a bridge, attach to it and let it create the connector */
> >  	if (dsi->bridge) {
> > -		ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL);
> > +		ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL, true);
> >  		if (ret) {
> >  			DRM_ERROR("Failed to attach bridge to drm\n");
> >  			goto err_encoder_cleanup;
> > diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> > index 5d6a9f094df5..d3248a881cf0 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> > @@ -1290,11 +1290,15 @@ static void mtk_hdmi_hpd_event(bool hpd, struct device *dev)
> >   * Bridge callbacks
> >   */
> >  
> > -static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge)
> > +static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge,
> > +				  bool create_connector)
> >  {
> >  	struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
> >  	int ret;
> >  
> > +	if (!create_connector)
> > +		return -EINVAL;
> > +
> >  	ret = drm_connector_init(bridge->encoder->dev, &hdmi->conn,
> >  				 &mtk_hdmi_connector_funcs,
> >  				 DRM_MODE_CONNECTOR_HDMIA);
> > @@ -1318,7 +1322,7 @@ static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge)
> >  
> >  	if (hdmi->next_bridge) {
> >  		ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge,
> > -					bridge);
> > +					bridge, create_connector);
> >  		if (ret) {
> >  			dev_err(hdmi->dev,
> >  				"Failed to attach external bridge: %d\n", ret);
> > diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c
> > index 271aa7bbca92..ca733086041a 100644
> > --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c
> > +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c
> > @@ -664,7 +664,7 @@ struct drm_bridge *msm_dsi_manager_bridge_init(u8 id)
> >  	bridge = &dsi_bridge->base;
> >  	bridge->funcs = &dsi_mgr_bridge_funcs;
> >  
> > -	ret = drm_bridge_attach(encoder, bridge, NULL);
> > +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
> >  	if (ret)
> >  		goto fail;
> >  
> > @@ -693,7 +693,7 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id)
> >  	encoder = msm_dsi->encoder;
> >  
> >  	/* link the internal dsi bridge to the external bridge */
> > -	drm_bridge_attach(encoder, ext_bridge, int_bridge);
> > +	drm_bridge_attach(encoder, ext_bridge, int_bridge, true);
> >  
> >  	/*
> >  	 * we need the drm_connector created by the external bridge
> > diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c
> > index 2950bba4aca9..32a463c84cc1 100644
> > --- a/drivers/gpu/drm/msm/edp/edp_bridge.c
> > +++ b/drivers/gpu/drm/msm/edp/edp_bridge.c
> > @@ -91,7 +91,7 @@ struct drm_bridge *msm_edp_bridge_init(struct msm_edp *edp)
> >  	bridge = &edp_bridge->base;
> >  	bridge->funcs = &edp_bridge_funcs;
> >  
> > -	ret = drm_bridge_attach(edp->encoder, bridge, NULL);
> > +	ret = drm_bridge_attach(edp->encoder, bridge, NULL, true);
> >  	if (ret)
> >  		goto fail;
> >  
> > diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
> > index 03197b8959ba..d7738aafcff8 100644
> > --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
> > +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
> > @@ -296,7 +296,7 @@ struct drm_bridge *msm_hdmi_bridge_init(struct hdmi *hdmi)
> >  	bridge = &hdmi_bridge->base;
> >  	bridge->funcs = &msm_hdmi_bridge_funcs;
> >  
> > -	ret = drm_bridge_attach(hdmi->encoder, bridge, NULL);
> > +	ret = drm_bridge_attach(hdmi->encoder, bridge, NULL, true);
> >  	if (ret)
> >  		goto fail;
> >  
> > diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
> > index 672e0f8ad11c..837d0cd20dd1 100644
> > --- a/drivers/gpu/drm/omapdrm/omap_drv.c
> > +++ b/drivers/gpu/drm/omapdrm/omap_drv.c
> > @@ -301,7 +301,8 @@ static int omap_modeset_init(struct drm_device *dev)
> >  
> >  		if (pipe->output->bridge) {
> >  			ret = drm_bridge_attach(pipe->encoder,
> > -						pipe->output->bridge, NULL);
> > +						pipe->output->bridge, NULL,
> > +						true);
> >  			if (ret < 0)
> >  				return ret;
> >  		}
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> > index 0f00bdfe2366..74c2ae5ce687 100644
> > --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> > +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> > @@ -120,7 +120,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
> >  	 * Attach the bridge to the encoder. The bridge will create the
> >  	 * connector.
> >  	 */
> > -	ret = drm_bridge_attach(encoder, bridge, NULL);
> > +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
> >  	if (ret) {
> >  		drm_encoder_cleanup(encoder);
> >  		return ret;
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c
> > index 1c62578590f4..a8d8b05c4731 100644
> > --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c
> > +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c
> > @@ -605,7 +605,7 @@ static void rcar_lvds_mode_set(struct drm_bridge *bridge,
> >  	rcar_lvds_get_lvds_mode(lvds);
> >  }
> >  
> > -static int rcar_lvds_attach(struct drm_bridge *bridge)
> > +static int rcar_lvds_attach(struct drm_bridge *bridge, bool create_connector)
> >  {
> >  	struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
> >  	struct drm_connector *connector = &lvds->connector;
> > @@ -615,7 +615,10 @@ static int rcar_lvds_attach(struct drm_bridge *bridge)
> >  	/* If we have a next bridge just attach it. */
> >  	if (lvds->next_bridge)
> >  		return drm_bridge_attach(bridge->encoder, lvds->next_bridge,
> > -					 bridge);
> > +					 bridge, create_connector);
> > +
> > +	if (!create_connector)
> > +		return -EINVAL;
> >  
> >  	/* Otherwise if we have a panel, create a connector. */
> >  	if (!lvds->panel)
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
> > index 830858a809e5..7ca412d294b2 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
> > @@ -440,7 +440,7 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
> >  			goto err_free_connector;
> >  		}
> >  	} else {
> > -		ret = drm_bridge_attach(encoder, lvds->bridge, NULL);
> > +		ret = drm_bridge_attach(encoder, lvds->bridge, NULL, true);
> >  		if (ret) {
> >  			DRM_DEV_ERROR(drm_dev->dev,
> >  				      "failed to attach bridge: %d\n", ret);
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c
> > index ce4d82d293e4..8218bbd09a72 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_rgb.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c
> > @@ -143,7 +143,7 @@ struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
> >  
> >  	rgb->bridge = bridge;
> >  
> > -	ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
> > +	ret = drm_bridge_attach(encoder, rgb->bridge, NULL, true);
> >  	if (ret) {
> >  		DRM_DEV_ERROR(drm_dev->dev,
> >  			      "failed to attach bridge: %d\n", ret);
> > diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c
> > index 9e6d5d8b7030..f09209621568 100644
> > --- a/drivers/gpu/drm/sti/sti_dvo.c
> > +++ b/drivers/gpu/drm/sti/sti_dvo.c
> > @@ -468,7 +468,7 @@ static int sti_dvo_bind(struct device *dev, struct device *master, void *data)
> >  	bridge->of_node = dvo->dev.of_node;
> >  	drm_bridge_add(bridge);
> >  
> > -	err = drm_bridge_attach(encoder, bridge, NULL);
> > +	err = drm_bridge_attach(encoder, bridge, NULL, true);
> >  	if (err) {
> >  		DRM_ERROR("Failed to attach bridge\n");
> >  		return err;
> > diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
> > index 94e404f13234..87e0fb742dc8 100644
> > --- a/drivers/gpu/drm/sti/sti_hda.c
> > +++ b/drivers/gpu/drm/sti/sti_hda.c
> > @@ -700,7 +700,7 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data)
> >  
> >  	bridge->driver_private = hda;
> >  	bridge->funcs = &sti_hda_bridge_funcs;
> > -	drm_bridge_attach(encoder, bridge, NULL);
> > +	drm_bridge_attach(encoder, bridge, NULL, true);
> >  
> >  	connector->encoder = encoder;
> >  
> > diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
> > index f03d617edc4c..8c0ffe6833f9 100644
> > --- a/drivers/gpu/drm/sti/sti_hdmi.c
> > +++ b/drivers/gpu/drm/sti/sti_hdmi.c
> > @@ -1276,7 +1276,7 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
> >  
> >  	bridge->driver_private = hdmi;
> >  	bridge->funcs = &sti_hdmi_bridge_funcs;
> > -	drm_bridge_attach(encoder, bridge, NULL);
> > +	drm_bridge_attach(encoder, bridge, NULL, true);
> >  
> >  	connector->encoder = encoder;
> >  
> > diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
> > index 2fe6c4a8d915..10a9f848c5f6 100644
> > --- a/drivers/gpu/drm/stm/ltdc.c
> > +++ b/drivers/gpu/drm/stm/ltdc.c
> > @@ -1053,7 +1053,7 @@ static int ltdc_encoder_init(struct drm_device *ddev, struct drm_bridge *bridge)
> >  	drm_encoder_init(ddev, encoder, &ltdc_encoder_funcs,
> >  			 DRM_MODE_ENCODER_DPI, NULL);
> >  
> > -	ret = drm_bridge_attach(encoder, bridge, NULL);
> > +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
> >  	if (ret) {
> >  		drm_encoder_cleanup(encoder);
> >  		return -EINVAL;
> > diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> > index 3a3ba99fed22..3e5170fa1e67 100644
> > --- a/drivers/gpu/drm/sun4i/sun4i_lvds.c
> > +++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
> > @@ -155,7 +155,7 @@ int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
> >  	}
> >  
> >  	if (bridge) {
> > -		ret = drm_bridge_attach(encoder, bridge, NULL);
> > +		ret = drm_bridge_attach(encoder, bridge, NULL, true);
> >  		if (ret) {
> >  			dev_err(drm->dev, "Couldn't attach our bridge\n");
> >  			goto err_cleanup_connector;
> > diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
> > index a901ec689b62..3f8629445fbb 100644
> > --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c
> > +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
> > @@ -252,7 +252,7 @@ int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
> >  	}
> >  
> >  	if (rgb->bridge) {
> > -		ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
> > +		ret = drm_bridge_attach(encoder, rgb->bridge, NULL, true);
> >  		if (ret) {
> >  			dev_err(drm->dev, "Couldn't attach our bridge\n");
> >  			goto err_cleanup_connector;
> > diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c
> > index e9969cd36610..ec693c11e455 100644
> > --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c
> > +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c
> > @@ -168,7 +168,7 @@ int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge)
> >  
> >  	priv->external_encoder->possible_crtcs = BIT(0);
> >  
> > -	ret = drm_bridge_attach(priv->external_encoder, bridge, NULL);
> > +	ret = drm_bridge_attach(priv->external_encoder, bridge, NULL, true);
> >  	if (ret) {
> >  		dev_err(ddev->dev, "drm_bridge_attach() failed %d\n", ret);
> >  		return ret;
> > diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
> > index 34f90ca8f479..2d7c5cf0d468 100644
> > --- a/drivers/gpu/drm/vc4/vc4_dpi.c
> > +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
> > @@ -262,7 +262,7 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
> >  	if (panel)
> >  		bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DPI);
> >  
> > -	return drm_bridge_attach(dpi->encoder, bridge, NULL);
> > +	return drm_bridge_attach(dpi->encoder, bridge, NULL, true);
> >  }
> >  
> >  static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
> > diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> > index 2ea4e20b7b8a..3edd7ffc7383 100644
> > --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> > +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> > @@ -1607,7 +1607,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
> >  			 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);
> > +	ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL, true);
> >  	if (ret) {
> >  		dev_err(dev, "bridge attach failed: %d\n", ret);
> >  		return ret;
> > diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> > index 7616f6562fe4..08dc15f93ded 100644
> > --- a/include/drm/drm_bridge.h
> > +++ b/include/drm/drm_bridge.h
> > @@ -48,7 +48,7 @@ struct drm_bridge_funcs {
> >  	 *
> >  	 * Zero on success, error code on failure.
> >  	 */
> > -	int (*attach)(struct drm_bridge *bridge);
> > +	int (*attach)(struct drm_bridge *bridge, bool create_connector);
> >  
> >  	/**
> >  	 * @detach:
> > @@ -404,7 +404,7 @@ 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);
> >  int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
> > -		      struct drm_bridge *previous);
> > +		      struct drm_bridge *previous, bool create_connector);
> >  
> >  bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
> >  			   const struct drm_display_mode *mode,
Andrzej Hajda Aug. 8, 2019, 5:36 p.m. UTC | #3
On 08.08.2019 16:25, Laurent Pinchart wrote:
> Hi Andrzej,
>
> On Wed, Jul 17, 2019 at 08:39:47AM +0200, Andrzej Hajda wrote:
>> On 07.07.2019 20:18, Laurent Pinchart wrote:
>>> Most bridge drivers create a DRM connector to model the connector at the
>>> output of the bridge. This model is historical and has worked pretty
>>> well so far, but causes several issues:
>>>
>>> - It prevents supporting more complex display pipelines where DRM
>>> connector operations are split over multiple components. For instance a
>>> pipeline with a bridge connected to the DDC signals to read EDID data,
>>> and another one connected to the HPD signal to detect connection and
>>> disconnection, will not be possible to support through this model.
>>>
>>> - It requires every bridge driver to implement similar connector
>>> handling code, resulting in code duplication.
>>>
>>> - It assumes that a bridge will either be wired to a connector or to
>>> another bridge, but doesn't support bridges that can be used in both
>>> positions very well (although there is some ad-hoc support for this in
>>> the analogix_dp bridge driver).
>>>
>>> In order to solve these issues, ownership of the connector should be
>>> moved to the display controller driver (where it can be implemented
>>> using helpers provided by the core).
>>>
>>> Extend the bridge API to allow disabling connector creation in bridge
>>> drivers as a first step towards the new model. The new create_connector
>>> argument to the bridge .attach() operation tells the bridge driver
>>> whether to create a connector. Set the argument to true unconditionally,
>>> and modify all existing bridge drivers to return an error when connector
>>> creation is not requested as they don't support this feature yet.
>>>
>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>> ---
>>>  drivers/gpu/drm/arc/arcpgu_hdmi.c                        | 2 +-
>>>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c         | 2 +-
>>>  drivers/gpu/drm/bridge/adv7511/adv7511_drv.c             | 6 +++++-
>>>  drivers/gpu/drm/bridge/analogix-anx78xx.c                | 6 +++++-
>>>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c       | 8 ++++++--
>>>  drivers/gpu/drm/bridge/cdns-dsi.c                        | 6 ++++--
>>>  drivers/gpu/drm/bridge/lvds-encoder.c                    | 4 ++--
>>>  drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 6 +++++-
>>>  drivers/gpu/drm/bridge/nxp-ptn3460.c                     | 6 +++++-
>>>  drivers/gpu/drm/bridge/panel.c                           | 5 ++++-
>>>  drivers/gpu/drm/bridge/parade-ps8622.c                   | 5 ++++-
>>>  drivers/gpu/drm/bridge/sii902x.c                         | 6 +++++-
>>>  drivers/gpu/drm/bridge/sil-sii8620.c                     | 2 +-
>>>  drivers/gpu/drm/bridge/simple-bridge.c                   | 6 +++++-
>>>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.c                | 8 ++++++--
>>>  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c            | 8 +++++---
>>>  drivers/gpu/drm/bridge/tc358764.c                        | 5 ++++-
>>>  drivers/gpu/drm/bridge/tc358767.c                        | 5 ++++-
>>>  drivers/gpu/drm/bridge/thc63lvd1024.c                    | 5 +++--
>>>  drivers/gpu/drm/bridge/ti-sn65dsi86.c                    | 5 ++++-
>>>  drivers/gpu/drm/bridge/ti-tfp410.c                       | 5 ++++-
>>>  drivers/gpu/drm/drm_bridge.c                             | 5 +++--
>>>  drivers/gpu/drm/drm_simple_kms_helper.c                  | 2 +-
>>>  drivers/gpu/drm/exynos/exynos_dp.c                       | 3 ++-
>>>  drivers/gpu/drm/exynos/exynos_drm_dsi.c                  | 4 ++--
>>>  drivers/gpu/drm/exynos/exynos_hdmi.c                     | 2 +-
>>>  drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c                | 2 +-
>>>  drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c             | 2 +-
>>>  drivers/gpu/drm/i2c/tda998x_drv.c                        | 8 ++++++--
>>>  drivers/gpu/drm/imx/imx-ldb.c                            | 2 +-
>>>  drivers/gpu/drm/imx/parallel-display.c                   | 2 +-
>>>  drivers/gpu/drm/mcde/mcde_dsi.c                          | 6 +++++-
>>>  drivers/gpu/drm/mediatek/mtk_dpi.c                       | 2 +-
>>>  drivers/gpu/drm/mediatek/mtk_dsi.c                       | 2 +-
>>>  drivers/gpu/drm/mediatek/mtk_hdmi.c                      | 8 ++++++--
>>>  drivers/gpu/drm/msm/dsi/dsi_manager.c                    | 4 ++--
>>>  drivers/gpu/drm/msm/edp/edp_bridge.c                     | 2 +-
>>>  drivers/gpu/drm/msm/hdmi/hdmi_bridge.c                   | 2 +-
>>>  drivers/gpu/drm/omapdrm/omap_drv.c                       | 3 ++-
>>>  drivers/gpu/drm/rcar-du/rcar_du_encoder.c                | 2 +-
>>>  drivers/gpu/drm/rcar-du/rcar_lvds.c                      | 7 +++++--
>>>  drivers/gpu/drm/rockchip/rockchip_lvds.c                 | 2 +-
>>>  drivers/gpu/drm/rockchip/rockchip_rgb.c                  | 2 +-
>>>  drivers/gpu/drm/sti/sti_dvo.c                            | 2 +-
>>>  drivers/gpu/drm/sti/sti_hda.c                            | 2 +-
>>>  drivers/gpu/drm/sti/sti_hdmi.c                           | 2 +-
>>>  drivers/gpu/drm/stm/ltdc.c                               | 2 +-
>>>  drivers/gpu/drm/sun4i/sun4i_lvds.c                       | 2 +-
>>>  drivers/gpu/drm/sun4i/sun4i_rgb.c                        | 2 +-
>>>  drivers/gpu/drm/tilcdc/tilcdc_external.c                 | 2 +-
>>>  drivers/gpu/drm/vc4/vc4_dpi.c                            | 2 +-
>>>  drivers/gpu/drm/vc4/vc4_dsi.c                            | 2 +-
>>>  include/drm/drm_bridge.h                                 | 4 ++--
>>>  53 files changed, 140 insertions(+), 67 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c
>>> index 98aac743cc26..739f2358f1d5 100644
>>> --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c
>>> +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c
>>> @@ -39,7 +39,7 @@ int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np)
>>>  		return ret;
>>>  
>>>  	/* Link drm_bridge to encoder */
>>> -	ret = drm_bridge_attach(encoder, bridge, NULL);
>>> +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
>> Few suggestions:
>>
>> 1. Maybe it would be more convenient to add flags argument instead of bool:
>>
>> - code should be more readable: ret = drm_bridge_attach(encoder, bridge,
>> NULL, DRM_BRIDGE_FLAG_NO_CONNECTOR)
>>
>> - it can be easily expanded later with other flags, there at least two
>> drivers which would benefit from DRM_BRIDGE_FLAG_NO_CHAINING flag.
> Please note that I think this flag should disappear once drivers get
> converted to the new model. This will however take some time. I'm not
> opposed to turning the book into a flag though. I was hoping to receive
> more review comments on this particular patch, but as that's not the
> case, I can already proceed with the change.
>
> What would the DRM_BRIDGE_FLAG_NO_CHAINING flag be used for ?


To avoid setting encoder->bridge or previous_bridge->next field to
attached bridge (last lines of drm_bridge_attach code).

This is for sure the case of exynos_dsi and vc4_dsi, but I guess it can
affect other drivers as well, probably they just use other workarounds
or have more flexible hardware.

Generally idea that order of calling
pre_enable/enable/disable/post_disable callbacks in chained
encoder/bridges is fixed is wrong IMHO, only video source component
knows in which moment it should enable its sink and if it should do
something after. And it does not work at all with sources/sinks having
more than one output/input.


>
>> 2. If the patch can be applied atomically it is OK as is, if not you can
>> use preprocessor vararg magic to support new and old syntax, sth like:
>>
>> #define _drm_bridge_attach(encoder, bridge, prev, flags, optarg...)
>> __drm_bridge_attach(encoder, bridge, prev, flags)
>>
>> #define drm_bridge_attach(encoder, bridge, prev, optarg...)
>> _drm_bridge_attach(encoder, bridge, prev, ##optarg, 0)
> Good point. I'll try to do this atomically, but if it fails I'll follow
> your suggestion.
>
>> 3. Maybe more convenient would be to just set the flags directly before
>> attachment:
>>
>>     bridge->dont_create_connector = true;
>>
>>     ret = drm_bridge_attach(encoder, bridge, NULL);
>>
>>     This way it will be still expandable, and less changes.
> Bridges that are chained would need to set the dont_create_connector
> flag of the next bridge. It would be a bit ugly, but would make this
> patch smaller. On the other hand we would need to keep the if
> (!create_connector) check in the .attach() handlers, and it would be
> easier to miss it in bridge drivers (current or new) than with an
> explicit argument to the .attach() operation. I would thus have a
> preference for the new argument to .attach(). Especially if it can help
> you with DRM_BRIDGE_FLAG_NO_CHAINING :-)

bridge->dont_chain would work as well :)

Btw I wonder if it could be possible to disallow creating connectors at all by new bridges - it would speed-up transition.


Another long term idea. Since bridges can be attached to:
- encoder,
- another bridge,
- crtc (I have one example, but I guess there could be more),
- even before crtc (image postprocessing)
And since bridge output goes to:
- another bridge,
- panel.

Wouldn't be better to create drm_source and drm_sink (do not respond with xkcd picture :) ):
- drm_source will be embedded in source device context,
- drm_sink will be embedded in sink device context.
We could make then transitions of bridges to drm_sink with drm_source embeded in its context, and panels to drm_sink.
This way we could drop these crazy constructs:
- if sink is panel then do sth, elsif is bridge then do sth_else,
- if src is bridge then do sth, elsif is encoder ... elsif ....
- helpers of_find_panel_or_bridge,
- drm_panel_bridge,
Also we could implement easily multi input/output bridges/panels/crtcs whatever.
And hpd callbacks you have proposed in another patch would fit better to drm_source.ops.
...


Regards
Andrzej
Laurent Pinchart Aug. 8, 2019, 6:50 p.m. UTC | #4
Hi Andrzej,

On Thu, Aug 08, 2019 at 07:36:49PM +0200, Andrzej Hajda wrote:
> On 08.08.2019 16:25, Laurent Pinchart wrote:
> > On Wed, Jul 17, 2019 at 08:39:47AM +0200, Andrzej Hajda wrote:
> >> On 07.07.2019 20:18, Laurent Pinchart wrote:
> >>> Most bridge drivers create a DRM connector to model the connector at the
> >>> output of the bridge. This model is historical and has worked pretty
> >>> well so far, but causes several issues:
> >>>
> >>> - It prevents supporting more complex display pipelines where DRM
> >>> connector operations are split over multiple components. For instance a
> >>> pipeline with a bridge connected to the DDC signals to read EDID data,
> >>> and another one connected to the HPD signal to detect connection and
> >>> disconnection, will not be possible to support through this model.
> >>>
> >>> - It requires every bridge driver to implement similar connector
> >>> handling code, resulting in code duplication.
> >>>
> >>> - It assumes that a bridge will either be wired to a connector or to
> >>> another bridge, but doesn't support bridges that can be used in both
> >>> positions very well (although there is some ad-hoc support for this in
> >>> the analogix_dp bridge driver).
> >>>
> >>> In order to solve these issues, ownership of the connector should be
> >>> moved to the display controller driver (where it can be implemented
> >>> using helpers provided by the core).
> >>>
> >>> Extend the bridge API to allow disabling connector creation in bridge
> >>> drivers as a first step towards the new model. The new create_connector
> >>> argument to the bridge .attach() operation tells the bridge driver
> >>> whether to create a connector. Set the argument to true unconditionally,
> >>> and modify all existing bridge drivers to return an error when connector
> >>> creation is not requested as they don't support this feature yet.
> >>>
> >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >>> ---
> >>>  drivers/gpu/drm/arc/arcpgu_hdmi.c                        | 2 +-
> >>>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c         | 2 +-
> >>>  drivers/gpu/drm/bridge/adv7511/adv7511_drv.c             | 6 +++++-
> >>>  drivers/gpu/drm/bridge/analogix-anx78xx.c                | 6 +++++-
> >>>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c       | 8 ++++++--
> >>>  drivers/gpu/drm/bridge/cdns-dsi.c                        | 6 ++++--
> >>>  drivers/gpu/drm/bridge/lvds-encoder.c                    | 4 ++--
> >>>  drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 6 +++++-
> >>>  drivers/gpu/drm/bridge/nxp-ptn3460.c                     | 6 +++++-
> >>>  drivers/gpu/drm/bridge/panel.c                           | 5 ++++-
> >>>  drivers/gpu/drm/bridge/parade-ps8622.c                   | 5 ++++-
> >>>  drivers/gpu/drm/bridge/sii902x.c                         | 6 +++++-
> >>>  drivers/gpu/drm/bridge/sil-sii8620.c                     | 2 +-
> >>>  drivers/gpu/drm/bridge/simple-bridge.c                   | 6 +++++-
> >>>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.c                | 8 ++++++--
> >>>  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c            | 8 +++++---
> >>>  drivers/gpu/drm/bridge/tc358764.c                        | 5 ++++-
> >>>  drivers/gpu/drm/bridge/tc358767.c                        | 5 ++++-
> >>>  drivers/gpu/drm/bridge/thc63lvd1024.c                    | 5 +++--
> >>>  drivers/gpu/drm/bridge/ti-sn65dsi86.c                    | 5 ++++-
> >>>  drivers/gpu/drm/bridge/ti-tfp410.c                       | 5 ++++-
> >>>  drivers/gpu/drm/drm_bridge.c                             | 5 +++--
> >>>  drivers/gpu/drm/drm_simple_kms_helper.c                  | 2 +-
> >>>  drivers/gpu/drm/exynos/exynos_dp.c                       | 3 ++-
> >>>  drivers/gpu/drm/exynos/exynos_drm_dsi.c                  | 4 ++--
> >>>  drivers/gpu/drm/exynos/exynos_hdmi.c                     | 2 +-
> >>>  drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c                | 2 +-
> >>>  drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c             | 2 +-
> >>>  drivers/gpu/drm/i2c/tda998x_drv.c                        | 8 ++++++--
> >>>  drivers/gpu/drm/imx/imx-ldb.c                            | 2 +-
> >>>  drivers/gpu/drm/imx/parallel-display.c                   | 2 +-
> >>>  drivers/gpu/drm/mcde/mcde_dsi.c                          | 6 +++++-
> >>>  drivers/gpu/drm/mediatek/mtk_dpi.c                       | 2 +-
> >>>  drivers/gpu/drm/mediatek/mtk_dsi.c                       | 2 +-
> >>>  drivers/gpu/drm/mediatek/mtk_hdmi.c                      | 8 ++++++--
> >>>  drivers/gpu/drm/msm/dsi/dsi_manager.c                    | 4 ++--
> >>>  drivers/gpu/drm/msm/edp/edp_bridge.c                     | 2 +-
> >>>  drivers/gpu/drm/msm/hdmi/hdmi_bridge.c                   | 2 +-
> >>>  drivers/gpu/drm/omapdrm/omap_drv.c                       | 3 ++-
> >>>  drivers/gpu/drm/rcar-du/rcar_du_encoder.c                | 2 +-
> >>>  drivers/gpu/drm/rcar-du/rcar_lvds.c                      | 7 +++++--
> >>>  drivers/gpu/drm/rockchip/rockchip_lvds.c                 | 2 +-
> >>>  drivers/gpu/drm/rockchip/rockchip_rgb.c                  | 2 +-
> >>>  drivers/gpu/drm/sti/sti_dvo.c                            | 2 +-
> >>>  drivers/gpu/drm/sti/sti_hda.c                            | 2 +-
> >>>  drivers/gpu/drm/sti/sti_hdmi.c                           | 2 +-
> >>>  drivers/gpu/drm/stm/ltdc.c                               | 2 +-
> >>>  drivers/gpu/drm/sun4i/sun4i_lvds.c                       | 2 +-
> >>>  drivers/gpu/drm/sun4i/sun4i_rgb.c                        | 2 +-
> >>>  drivers/gpu/drm/tilcdc/tilcdc_external.c                 | 2 +-
> >>>  drivers/gpu/drm/vc4/vc4_dpi.c                            | 2 +-
> >>>  drivers/gpu/drm/vc4/vc4_dsi.c                            | 2 +-
> >>>  include/drm/drm_bridge.h                                 | 4 ++--
> >>>  53 files changed, 140 insertions(+), 67 deletions(-)
> >>>
> >>> diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> >>> index 98aac743cc26..739f2358f1d5 100644
> >>> --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c
> >>> +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> >>> @@ -39,7 +39,7 @@ int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np)
> >>>  		return ret;
> >>>  
> >>>  	/* Link drm_bridge to encoder */
> >>> -	ret = drm_bridge_attach(encoder, bridge, NULL);
> >>> +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
> >> Few suggestions:
> >>
> >> 1. Maybe it would be more convenient to add flags argument instead of bool:
> >>
> >> - code should be more readable: ret = drm_bridge_attach(encoder, bridge,
> >> NULL, DRM_BRIDGE_FLAG_NO_CONNECTOR)
> >>
> >> - it can be easily expanded later with other flags, there at least two
> >> drivers which would benefit from DRM_BRIDGE_FLAG_NO_CHAINING flag.
> > Please note that I think this flag should disappear once drivers get
> > converted to the new model. This will however take some time. I'm not
> > opposed to turning the book into a flag though. I was hoping to receive
> > more review comments on this particular patch, but as that's not the
> > case, I can already proceed with the change.
> >
> > What would the DRM_BRIDGE_FLAG_NO_CHAINING flag be used for ?
> 
> To avoid setting encoder->bridge or previous_bridge->next field to
> attached bridge (last lines of drm_bridge_attach code).
> 
> This is for sure the case of exynos_dsi and vc4_dsi, but I guess it can
> affect other drivers as well, probably they just use other workarounds
> or have more flexible hardware.
> 
> Generally idea that order of calling
> pre_enable/enable/disable/post_disable callbacks in chained
> encoder/bridges is fixed is wrong IMHO, only video source component
> knows in which moment it should enable its sink and if it should do
> something after. And it does not work at all with sources/sinks having
> more than one output/input.

Makes sense. I think I would however still set the ->next field, in
order to track bridges, and only skip chaining of the enable/disable
operations.

> >> 2. If the patch can be applied atomically it is OK as is, if not you can
> >> use preprocessor vararg magic to support new and old syntax, sth like:
> >>
> >> #define _drm_bridge_attach(encoder, bridge, prev, flags, optarg...)
> >> __drm_bridge_attach(encoder, bridge, prev, flags)
> >>
> >> #define drm_bridge_attach(encoder, bridge, prev, optarg...)
> >> _drm_bridge_attach(encoder, bridge, prev, ##optarg, 0)
> >
> > Good point. I'll try to do this atomically, but if it fails I'll follow
> > your suggestion.
> >
> >> 3. Maybe more convenient would be to just set the flags directly before
> >> attachment:
> >>
> >>     bridge->dont_create_connector = true;
> >>
> >>     ret = drm_bridge_attach(encoder, bridge, NULL);
> >>
> >>     This way it will be still expandable, and less changes.
> >
> > Bridges that are chained would need to set the dont_create_connector
> > flag of the next bridge. It would be a bit ugly, but would make this
> > patch smaller. On the other hand we would need to keep the if
> > (!create_connector) check in the .attach() handlers, and it would be
> > easier to miss it in bridge drivers (current or new) than with an
> > explicit argument to the .attach() operation. I would thus have a
> > preference for the new argument to .attach(). Especially if it can help
> > you with DRM_BRIDGE_FLAG_NO_CHAINING :-)
> 
> bridge->dont_chain would work as well :)
> 
> Btw I wonder if it could be possible to disallow creating connectors
> at all by new bridges - it would speed-up transition.

If we can agree on that, that would be wonderful !

> Another long term idea. Since bridges can be attached to:
> - encoder,
> - another bridge,
> - crtc (I have one example, but I guess there could be more),
> - even before crtc (image postprocessing)
> And since bridge output goes to:
> - another bridge,
> - panel.
> 
> Wouldn't be better to create drm_source and drm_sink (do not respond
> with xkcd picture :) ):
>
> - drm_source will be embedded in source device context,
> - drm_sink will be embedded in sink device context.
>
> We could make then transitions of bridges to drm_sink with drm_source
> embeded in its context, and panels to drm_sink.
> This way we could drop these crazy constructs:
>
> - if sink is panel then do sth, elsif is bridge then do sth_else,
> - if src is bridge then do sth, elsif is encoder ... elsif ....
> - helpers of_find_panel_or_bridge,
> - drm_panel_bridge,
>
> Also we could implement easily multi input/output bridges/panels/crtcs whatever.
> And hpd callbacks you have proposed in another patch would fit better
> to drm_source.ops.
> ...

My best answer to this is: one step at a time :-) Yes, that's the
direction I want to take (there will of course be lots of bikeshedding
on the details), and this series is one step in that direction. What I'm
really trying to do here is simplify bridge implementations by creating
a clear API that shrinks as much as possible the duties of bridge
drivers. This moves existing complexity outside of bridges, and we of
course don't want to duplicate it in all display controller drivers, so
I'm also creating helpers that centralise the complexity. Their use is
not mandatory (Daniel has hammered the "no midlayer" message enough),
but for the vast majority of display controllers, it should greatly
simplify the code.

My next planned steps are:

- Convert more bridge drivers to support DRM_BRIDGE_FLAG_NO_CONNECTOR
- Convert more display controller drivers to use DRM_BRIDGE_FLAG_NO_CONNECTOR
- Drop support for !DRM_BRIDGE_FLAG_NO_CONNECTOR in bridge drivers once
  they are only used by display controllers that set
  DRM_BRIDGE_FLAG_NO_CONNECTOR

In parallel, I want to address drm_panel ad drm_panel_bridge. With the
extension of the bridge API to expose connector-related operations done
in this series, the distinction between panel and bridges becomes much
thinner, opening the door to lots of simplifications.
Daniel Vetter Aug. 14, 2019, 8:18 a.m. UTC | #5
On Thu, Aug 08, 2019 at 07:36:49PM +0200, Andrzej Hajda wrote:
> On 08.08.2019 16:25, Laurent Pinchart wrote:
> > Hi Andrzej,
> >
> > On Wed, Jul 17, 2019 at 08:39:47AM +0200, Andrzej Hajda wrote:
> >> On 07.07.2019 20:18, Laurent Pinchart wrote:
> >>> Most bridge drivers create a DRM connector to model the connector at the
> >>> output of the bridge. This model is historical and has worked pretty
> >>> well so far, but causes several issues:
> >>>
> >>> - It prevents supporting more complex display pipelines where DRM
> >>> connector operations are split over multiple components. For instance a
> >>> pipeline with a bridge connected to the DDC signals to read EDID data,
> >>> and another one connected to the HPD signal to detect connection and
> >>> disconnection, will not be possible to support through this model.
> >>>
> >>> - It requires every bridge driver to implement similar connector
> >>> handling code, resulting in code duplication.
> >>>
> >>> - It assumes that a bridge will either be wired to a connector or to
> >>> another bridge, but doesn't support bridges that can be used in both
> >>> positions very well (although there is some ad-hoc support for this in
> >>> the analogix_dp bridge driver).
> >>>
> >>> In order to solve these issues, ownership of the connector should be
> >>> moved to the display controller driver (where it can be implemented
> >>> using helpers provided by the core).
> >>>
> >>> Extend the bridge API to allow disabling connector creation in bridge
> >>> drivers as a first step towards the new model. The new create_connector
> >>> argument to the bridge .attach() operation tells the bridge driver
> >>> whether to create a connector. Set the argument to true unconditionally,
> >>> and modify all existing bridge drivers to return an error when connector
> >>> creation is not requested as they don't support this feature yet.
> >>>
> >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >>> ---
> >>>  drivers/gpu/drm/arc/arcpgu_hdmi.c                        | 2 +-
> >>>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c         | 2 +-
> >>>  drivers/gpu/drm/bridge/adv7511/adv7511_drv.c             | 6 +++++-
> >>>  drivers/gpu/drm/bridge/analogix-anx78xx.c                | 6 +++++-
> >>>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c       | 8 ++++++--
> >>>  drivers/gpu/drm/bridge/cdns-dsi.c                        | 6 ++++--
> >>>  drivers/gpu/drm/bridge/lvds-encoder.c                    | 4 ++--
> >>>  drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 6 +++++-
> >>>  drivers/gpu/drm/bridge/nxp-ptn3460.c                     | 6 +++++-
> >>>  drivers/gpu/drm/bridge/panel.c                           | 5 ++++-
> >>>  drivers/gpu/drm/bridge/parade-ps8622.c                   | 5 ++++-
> >>>  drivers/gpu/drm/bridge/sii902x.c                         | 6 +++++-
> >>>  drivers/gpu/drm/bridge/sil-sii8620.c                     | 2 +-
> >>>  drivers/gpu/drm/bridge/simple-bridge.c                   | 6 +++++-
> >>>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.c                | 8 ++++++--
> >>>  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c            | 8 +++++---
> >>>  drivers/gpu/drm/bridge/tc358764.c                        | 5 ++++-
> >>>  drivers/gpu/drm/bridge/tc358767.c                        | 5 ++++-
> >>>  drivers/gpu/drm/bridge/thc63lvd1024.c                    | 5 +++--
> >>>  drivers/gpu/drm/bridge/ti-sn65dsi86.c                    | 5 ++++-
> >>>  drivers/gpu/drm/bridge/ti-tfp410.c                       | 5 ++++-
> >>>  drivers/gpu/drm/drm_bridge.c                             | 5 +++--
> >>>  drivers/gpu/drm/drm_simple_kms_helper.c                  | 2 +-
> >>>  drivers/gpu/drm/exynos/exynos_dp.c                       | 3 ++-
> >>>  drivers/gpu/drm/exynos/exynos_drm_dsi.c                  | 4 ++--
> >>>  drivers/gpu/drm/exynos/exynos_hdmi.c                     | 2 +-
> >>>  drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c                | 2 +-
> >>>  drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c             | 2 +-
> >>>  drivers/gpu/drm/i2c/tda998x_drv.c                        | 8 ++++++--
> >>>  drivers/gpu/drm/imx/imx-ldb.c                            | 2 +-
> >>>  drivers/gpu/drm/imx/parallel-display.c                   | 2 +-
> >>>  drivers/gpu/drm/mcde/mcde_dsi.c                          | 6 +++++-
> >>>  drivers/gpu/drm/mediatek/mtk_dpi.c                       | 2 +-
> >>>  drivers/gpu/drm/mediatek/mtk_dsi.c                       | 2 +-
> >>>  drivers/gpu/drm/mediatek/mtk_hdmi.c                      | 8 ++++++--
> >>>  drivers/gpu/drm/msm/dsi/dsi_manager.c                    | 4 ++--
> >>>  drivers/gpu/drm/msm/edp/edp_bridge.c                     | 2 +-
> >>>  drivers/gpu/drm/msm/hdmi/hdmi_bridge.c                   | 2 +-
> >>>  drivers/gpu/drm/omapdrm/omap_drv.c                       | 3 ++-
> >>>  drivers/gpu/drm/rcar-du/rcar_du_encoder.c                | 2 +-
> >>>  drivers/gpu/drm/rcar-du/rcar_lvds.c                      | 7 +++++--
> >>>  drivers/gpu/drm/rockchip/rockchip_lvds.c                 | 2 +-
> >>>  drivers/gpu/drm/rockchip/rockchip_rgb.c                  | 2 +-
> >>>  drivers/gpu/drm/sti/sti_dvo.c                            | 2 +-
> >>>  drivers/gpu/drm/sti/sti_hda.c                            | 2 +-
> >>>  drivers/gpu/drm/sti/sti_hdmi.c                           | 2 +-
> >>>  drivers/gpu/drm/stm/ltdc.c                               | 2 +-
> >>>  drivers/gpu/drm/sun4i/sun4i_lvds.c                       | 2 +-
> >>>  drivers/gpu/drm/sun4i/sun4i_rgb.c                        | 2 +-
> >>>  drivers/gpu/drm/tilcdc/tilcdc_external.c                 | 2 +-
> >>>  drivers/gpu/drm/vc4/vc4_dpi.c                            | 2 +-
> >>>  drivers/gpu/drm/vc4/vc4_dsi.c                            | 2 +-
> >>>  include/drm/drm_bridge.h                                 | 4 ++--
> >>>  53 files changed, 140 insertions(+), 67 deletions(-)
> >>>
> >>> diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> >>> index 98aac743cc26..739f2358f1d5 100644
> >>> --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c
> >>> +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> >>> @@ -39,7 +39,7 @@ int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np)
> >>>  		return ret;
> >>>  
> >>>  	/* Link drm_bridge to encoder */
> >>> -	ret = drm_bridge_attach(encoder, bridge, NULL);
> >>> +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
> >> Few suggestions:
> >>
> >> 1. Maybe it would be more convenient to add flags argument instead of bool:
> >>
> >> - code should be more readable: ret = drm_bridge_attach(encoder, bridge,
> >> NULL, DRM_BRIDGE_FLAG_NO_CONNECTOR)
> >>
> >> - it can be easily expanded later with other flags, there at least two
> >> drivers which would benefit from DRM_BRIDGE_FLAG_NO_CHAINING flag.
> > Please note that I think this flag should disappear once drivers get
> > converted to the new model. This will however take some time. I'm not
> > opposed to turning the book into a flag though. I was hoping to receive
> > more review comments on this particular patch, but as that's not the
> > case, I can already proceed with the change.
> >
> > What would the DRM_BRIDGE_FLAG_NO_CHAINING flag be used for ?
> 
> 
> To avoid setting encoder->bridge or previous_bridge->next field to
> attached bridge (last lines of drm_bridge_attach code).
> 
> This is for sure the case of exynos_dsi and vc4_dsi, but I guess it can
> affect other drivers as well, probably they just use other workarounds
> or have more flexible hardware.
> 
> Generally idea that order of calling
> pre_enable/enable/disable/post_disable callbacks in chained
> encoder/bridges is fixed is wrong IMHO, only video source component
> knows in which moment it should enable its sink and if it should do
> something after. And it does not work at all with sources/sinks having
> more than one output/input.

This doesn't make sense to me ... if you don't want to have a chained
bridge, don't attach it? You can just open-code the attach and call the
bridge functions directly when appropriate. I think that's the better
long-term plan than trying to have flags for every possible topology
existing out there ...
-Daniel

> 
> 
> >
> >> 2. If the patch can be applied atomically it is OK as is, if not you can
> >> use preprocessor vararg magic to support new and old syntax, sth like:
> >>
> >> #define _drm_bridge_attach(encoder, bridge, prev, flags, optarg...)
> >> __drm_bridge_attach(encoder, bridge, prev, flags)
> >>
> >> #define drm_bridge_attach(encoder, bridge, prev, optarg...)
> >> _drm_bridge_attach(encoder, bridge, prev, ##optarg, 0)
> > Good point. I'll try to do this atomically, but if it fails I'll follow
> > your suggestion.
> >
> >> 3. Maybe more convenient would be to just set the flags directly before
> >> attachment:
> >>
> >>     bridge->dont_create_connector = true;
> >>
> >>     ret = drm_bridge_attach(encoder, bridge, NULL);
> >>
> >>     This way it will be still expandable, and less changes.
> > Bridges that are chained would need to set the dont_create_connector
> > flag of the next bridge. It would be a bit ugly, but would make this
> > patch smaller. On the other hand we would need to keep the if
> > (!create_connector) check in the .attach() handlers, and it would be
> > easier to miss it in bridge drivers (current or new) than with an
> > explicit argument to the .attach() operation. I would thus have a
> > preference for the new argument to .attach(). Especially if it can help
> > you with DRM_BRIDGE_FLAG_NO_CHAINING :-)
> 
> bridge->dont_chain would work as well :)
> 
> Btw I wonder if it could be possible to disallow creating connectors at all by new bridges - it would speed-up transition.
> 
> 
> Another long term idea. Since bridges can be attached to:
> - encoder,
> - another bridge,
> - crtc (I have one example, but I guess there could be more),
> - even before crtc (image postprocessing)
> And since bridge output goes to:
> - another bridge,
> - panel.
> 
> Wouldn't be better to create drm_source and drm_sink (do not respond with xkcd picture :) ):
> - drm_source will be embedded in source device context,
> - drm_sink will be embedded in sink device context.
> We could make then transitions of bridges to drm_sink with drm_source embeded in its context, and panels to drm_sink.
> This way we could drop these crazy constructs:
> - if sink is panel then do sth, elsif is bridge then do sth_else,
> - if src is bridge then do sth, elsif is encoder ... elsif ....
> - helpers of_find_panel_or_bridge,
> - drm_panel_bridge,
> Also we could implement easily multi input/output bridges/panels/crtcs whatever.
> And hpd callbacks you have proposed in another patch would fit better to drm_source.ops.
> ...
> 
> 
> Regards
> Andrzej
> 
>
Laurent Pinchart Aug. 14, 2019, 9:55 a.m. UTC | #6
On Wed, Aug 14, 2019 at 10:18:40AM +0200, Daniel Vetter wrote:
> On Thu, Aug 08, 2019 at 07:36:49PM +0200, Andrzej Hajda wrote:
> > On 08.08.2019 16:25, Laurent Pinchart wrote:
> > > On Wed, Jul 17, 2019 at 08:39:47AM +0200, Andrzej Hajda wrote:
> > >> On 07.07.2019 20:18, Laurent Pinchart wrote:
> > >>> Most bridge drivers create a DRM connector to model the connector at the
> > >>> output of the bridge. This model is historical and has worked pretty
> > >>> well so far, but causes several issues:
> > >>>
> > >>> - It prevents supporting more complex display pipelines where DRM
> > >>> connector operations are split over multiple components. For instance a
> > >>> pipeline with a bridge connected to the DDC signals to read EDID data,
> > >>> and another one connected to the HPD signal to detect connection and
> > >>> disconnection, will not be possible to support through this model.
> > >>>
> > >>> - It requires every bridge driver to implement similar connector
> > >>> handling code, resulting in code duplication.
> > >>>
> > >>> - It assumes that a bridge will either be wired to a connector or to
> > >>> another bridge, but doesn't support bridges that can be used in both
> > >>> positions very well (although there is some ad-hoc support for this in
> > >>> the analogix_dp bridge driver).
> > >>>
> > >>> In order to solve these issues, ownership of the connector should be
> > >>> moved to the display controller driver (where it can be implemented
> > >>> using helpers provided by the core).
> > >>>
> > >>> Extend the bridge API to allow disabling connector creation in bridge
> > >>> drivers as a first step towards the new model. The new create_connector
> > >>> argument to the bridge .attach() operation tells the bridge driver
> > >>> whether to create a connector. Set the argument to true unconditionally,
> > >>> and modify all existing bridge drivers to return an error when connector
> > >>> creation is not requested as they don't support this feature yet.
> > >>>
> > >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > >>> ---
> > >>>  drivers/gpu/drm/arc/arcpgu_hdmi.c                        | 2 +-
> > >>>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c         | 2 +-
> > >>>  drivers/gpu/drm/bridge/adv7511/adv7511_drv.c             | 6 +++++-
> > >>>  drivers/gpu/drm/bridge/analogix-anx78xx.c                | 6 +++++-
> > >>>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c       | 8 ++++++--
> > >>>  drivers/gpu/drm/bridge/cdns-dsi.c                        | 6 ++++--
> > >>>  drivers/gpu/drm/bridge/lvds-encoder.c                    | 4 ++--
> > >>>  drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 6 +++++-
> > >>>  drivers/gpu/drm/bridge/nxp-ptn3460.c                     | 6 +++++-
> > >>>  drivers/gpu/drm/bridge/panel.c                           | 5 ++++-
> > >>>  drivers/gpu/drm/bridge/parade-ps8622.c                   | 5 ++++-
> > >>>  drivers/gpu/drm/bridge/sii902x.c                         | 6 +++++-
> > >>>  drivers/gpu/drm/bridge/sil-sii8620.c                     | 2 +-
> > >>>  drivers/gpu/drm/bridge/simple-bridge.c                   | 6 +++++-
> > >>>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.c                | 8 ++++++--
> > >>>  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c            | 8 +++++---
> > >>>  drivers/gpu/drm/bridge/tc358764.c                        | 5 ++++-
> > >>>  drivers/gpu/drm/bridge/tc358767.c                        | 5 ++++-
> > >>>  drivers/gpu/drm/bridge/thc63lvd1024.c                    | 5 +++--
> > >>>  drivers/gpu/drm/bridge/ti-sn65dsi86.c                    | 5 ++++-
> > >>>  drivers/gpu/drm/bridge/ti-tfp410.c                       | 5 ++++-
> > >>>  drivers/gpu/drm/drm_bridge.c                             | 5 +++--
> > >>>  drivers/gpu/drm/drm_simple_kms_helper.c                  | 2 +-
> > >>>  drivers/gpu/drm/exynos/exynos_dp.c                       | 3 ++-
> > >>>  drivers/gpu/drm/exynos/exynos_drm_dsi.c                  | 4 ++--
> > >>>  drivers/gpu/drm/exynos/exynos_hdmi.c                     | 2 +-
> > >>>  drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c                | 2 +-
> > >>>  drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c             | 2 +-
> > >>>  drivers/gpu/drm/i2c/tda998x_drv.c                        | 8 ++++++--
> > >>>  drivers/gpu/drm/imx/imx-ldb.c                            | 2 +-
> > >>>  drivers/gpu/drm/imx/parallel-display.c                   | 2 +-
> > >>>  drivers/gpu/drm/mcde/mcde_dsi.c                          | 6 +++++-
> > >>>  drivers/gpu/drm/mediatek/mtk_dpi.c                       | 2 +-
> > >>>  drivers/gpu/drm/mediatek/mtk_dsi.c                       | 2 +-
> > >>>  drivers/gpu/drm/mediatek/mtk_hdmi.c                      | 8 ++++++--
> > >>>  drivers/gpu/drm/msm/dsi/dsi_manager.c                    | 4 ++--
> > >>>  drivers/gpu/drm/msm/edp/edp_bridge.c                     | 2 +-
> > >>>  drivers/gpu/drm/msm/hdmi/hdmi_bridge.c                   | 2 +-
> > >>>  drivers/gpu/drm/omapdrm/omap_drv.c                       | 3 ++-
> > >>>  drivers/gpu/drm/rcar-du/rcar_du_encoder.c                | 2 +-
> > >>>  drivers/gpu/drm/rcar-du/rcar_lvds.c                      | 7 +++++--
> > >>>  drivers/gpu/drm/rockchip/rockchip_lvds.c                 | 2 +-
> > >>>  drivers/gpu/drm/rockchip/rockchip_rgb.c                  | 2 +-
> > >>>  drivers/gpu/drm/sti/sti_dvo.c                            | 2 +-
> > >>>  drivers/gpu/drm/sti/sti_hda.c                            | 2 +-
> > >>>  drivers/gpu/drm/sti/sti_hdmi.c                           | 2 +-
> > >>>  drivers/gpu/drm/stm/ltdc.c                               | 2 +-
> > >>>  drivers/gpu/drm/sun4i/sun4i_lvds.c                       | 2 +-
> > >>>  drivers/gpu/drm/sun4i/sun4i_rgb.c                        | 2 +-
> > >>>  drivers/gpu/drm/tilcdc/tilcdc_external.c                 | 2 +-
> > >>>  drivers/gpu/drm/vc4/vc4_dpi.c                            | 2 +-
> > >>>  drivers/gpu/drm/vc4/vc4_dsi.c                            | 2 +-
> > >>>  include/drm/drm_bridge.h                                 | 4 ++--
> > >>>  53 files changed, 140 insertions(+), 67 deletions(-)
> > >>>
> > >>> diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> > >>> index 98aac743cc26..739f2358f1d5 100644
> > >>> --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c
> > >>> +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> > >>> @@ -39,7 +39,7 @@ int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np)
> > >>>  		return ret;
> > >>>  
> > >>>  	/* Link drm_bridge to encoder */
> > >>> -	ret = drm_bridge_attach(encoder, bridge, NULL);
> > >>> +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
> > >> Few suggestions:
> > >>
> > >> 1. Maybe it would be more convenient to add flags argument instead of bool:
> > >>
> > >> - code should be more readable: ret = drm_bridge_attach(encoder, bridge,
> > >> NULL, DRM_BRIDGE_FLAG_NO_CONNECTOR)
> > >>
> > >> - it can be easily expanded later with other flags, there at least two
> > >> drivers which would benefit from DRM_BRIDGE_FLAG_NO_CHAINING flag.
> > >
> > > Please note that I think this flag should disappear once drivers get
> > > converted to the new model. This will however take some time. I'm not
> > > opposed to turning the book into a flag though. I was hoping to receive
> > > more review comments on this particular patch, but as that's not the
> > > case, I can already proceed with the change.
> > >
> > > What would the DRM_BRIDGE_FLAG_NO_CHAINING flag be used for ?
> > 
> > To avoid setting encoder->bridge or previous_bridge->next field to
> > attached bridge (last lines of drm_bridge_attach code).
> > 
> > This is for sure the case of exynos_dsi and vc4_dsi, but I guess it can
> > affect other drivers as well, probably they just use other workarounds
> > or have more flexible hardware.
> > 
> > Generally idea that order of calling
> > pre_enable/enable/disable/post_disable callbacks in chained
> > encoder/bridges is fixed is wrong IMHO, only video source component
> > knows in which moment it should enable its sink and if it should do
> > something after. And it does not work at all with sources/sinks having
> > more than one output/input.
> 
> This doesn't make sense to me ... if you don't want to have a chained
> bridge, don't attach it? You can just open-code the attach and call the
> bridge functions directly when appropriate. I think that's the better
> long-term plan than trying to have flags for every possible topology
> existing out there ...

I think the whole topology should be known to the display driver, so the
next (and possibly previous if we later need it) pointers should be set
in all cases. Otherwise we can't, for instance, notify all bridges of
HPD events. Sure, a bridge could decide not to attach at all, but then
it would have to reimplement lots of logic to forward everything
manually, when the need for manual forwarding is usually limited to a
certain category of operations.

> > >> 2. If the patch can be applied atomically it is OK as is, if not you can
> > >> use preprocessor vararg magic to support new and old syntax, sth like:
> > >>
> > >> #define _drm_bridge_attach(encoder, bridge, prev, flags, optarg...)
> > >> __drm_bridge_attach(encoder, bridge, prev, flags)
> > >>
> > >> #define drm_bridge_attach(encoder, bridge, prev, optarg...)
> > >> _drm_bridge_attach(encoder, bridge, prev, ##optarg, 0)
> > > Good point. I'll try to do this atomically, but if it fails I'll follow
> > > your suggestion.
> > >
> > >> 3. Maybe more convenient would be to just set the flags directly before
> > >> attachment:
> > >>
> > >>     bridge->dont_create_connector = true;
> > >>
> > >>     ret = drm_bridge_attach(encoder, bridge, NULL);
> > >>
> > >>     This way it will be still expandable, and less changes.
> > > Bridges that are chained would need to set the dont_create_connector
> > > flag of the next bridge. It would be a bit ugly, but would make this
> > > patch smaller. On the other hand we would need to keep the if
> > > (!create_connector) check in the .attach() handlers, and it would be
> > > easier to miss it in bridge drivers (current or new) than with an
> > > explicit argument to the .attach() operation. I would thus have a
> > > preference for the new argument to .attach(). Especially if it can help
> > > you with DRM_BRIDGE_FLAG_NO_CHAINING :-)
> > 
> > bridge->dont_chain would work as well :)
> > 
> > Btw I wonder if it could be possible to disallow creating connectors at all by new bridges - it would speed-up transition.
> > 
> > 
> > Another long term idea. Since bridges can be attached to:
> > - encoder,
> > - another bridge,
> > - crtc (I have one example, but I guess there could be more),
> > - even before crtc (image postprocessing)
> > And since bridge output goes to:
> > - another bridge,
> > - panel.
> > 
> > Wouldn't be better to create drm_source and drm_sink (do not respond with xkcd picture :) ):
> > - drm_source will be embedded in source device context,
> > - drm_sink will be embedded in sink device context.
> > We could make then transitions of bridges to drm_sink with drm_source embeded in its context, and panels to drm_sink.
> > This way we could drop these crazy constructs:
> > - if sink is panel then do sth, elsif is bridge then do sth_else,
> > - if src is bridge then do sth, elsif is encoder ... elsif ....
> > - helpers of_find_panel_or_bridge,
> > - drm_panel_bridge,
> > Also we could implement easily multi input/output bridges/panels/crtcs whatever.
> > And hpd callbacks you have proposed in another patch would fit better to drm_source.ops.
> > ...
> > 
> > 
> > Regards
> > Andrzej
> > 
> > 
> 
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
Daniel Vetter Aug. 14, 2019, 12:24 p.m. UTC | #7
On Wed, Aug 14, 2019 at 12:55:29PM +0300, Laurent Pinchart wrote:
> On Wed, Aug 14, 2019 at 10:18:40AM +0200, Daniel Vetter wrote:
> > On Thu, Aug 08, 2019 at 07:36:49PM +0200, Andrzej Hajda wrote:
> > > On 08.08.2019 16:25, Laurent Pinchart wrote:
> > > > On Wed, Jul 17, 2019 at 08:39:47AM +0200, Andrzej Hajda wrote:
> > > >> On 07.07.2019 20:18, Laurent Pinchart wrote:
> > > >>> Most bridge drivers create a DRM connector to model the connector at the
> > > >>> output of the bridge. This model is historical and has worked pretty
> > > >>> well so far, but causes several issues:
> > > >>>
> > > >>> - It prevents supporting more complex display pipelines where DRM
> > > >>> connector operations are split over multiple components. For instance a
> > > >>> pipeline with a bridge connected to the DDC signals to read EDID data,
> > > >>> and another one connected to the HPD signal to detect connection and
> > > >>> disconnection, will not be possible to support through this model.
> > > >>>
> > > >>> - It requires every bridge driver to implement similar connector
> > > >>> handling code, resulting in code duplication.
> > > >>>
> > > >>> - It assumes that a bridge will either be wired to a connector or to
> > > >>> another bridge, but doesn't support bridges that can be used in both
> > > >>> positions very well (although there is some ad-hoc support for this in
> > > >>> the analogix_dp bridge driver).
> > > >>>
> > > >>> In order to solve these issues, ownership of the connector should be
> > > >>> moved to the display controller driver (where it can be implemented
> > > >>> using helpers provided by the core).
> > > >>>
> > > >>> Extend the bridge API to allow disabling connector creation in bridge
> > > >>> drivers as a first step towards the new model. The new create_connector
> > > >>> argument to the bridge .attach() operation tells the bridge driver
> > > >>> whether to create a connector. Set the argument to true unconditionally,
> > > >>> and modify all existing bridge drivers to return an error when connector
> > > >>> creation is not requested as they don't support this feature yet.
> > > >>>
> > > >>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > >>> ---
> > > >>>  drivers/gpu/drm/arc/arcpgu_hdmi.c                        | 2 +-
> > > >>>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c         | 2 +-
> > > >>>  drivers/gpu/drm/bridge/adv7511/adv7511_drv.c             | 6 +++++-
> > > >>>  drivers/gpu/drm/bridge/analogix-anx78xx.c                | 6 +++++-
> > > >>>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c       | 8 ++++++--
> > > >>>  drivers/gpu/drm/bridge/cdns-dsi.c                        | 6 ++++--
> > > >>>  drivers/gpu/drm/bridge/lvds-encoder.c                    | 4 ++--
> > > >>>  drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 6 +++++-
> > > >>>  drivers/gpu/drm/bridge/nxp-ptn3460.c                     | 6 +++++-
> > > >>>  drivers/gpu/drm/bridge/panel.c                           | 5 ++++-
> > > >>>  drivers/gpu/drm/bridge/parade-ps8622.c                   | 5 ++++-
> > > >>>  drivers/gpu/drm/bridge/sii902x.c                         | 6 +++++-
> > > >>>  drivers/gpu/drm/bridge/sil-sii8620.c                     | 2 +-
> > > >>>  drivers/gpu/drm/bridge/simple-bridge.c                   | 6 +++++-
> > > >>>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.c                | 8 ++++++--
> > > >>>  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c            | 8 +++++---
> > > >>>  drivers/gpu/drm/bridge/tc358764.c                        | 5 ++++-
> > > >>>  drivers/gpu/drm/bridge/tc358767.c                        | 5 ++++-
> > > >>>  drivers/gpu/drm/bridge/thc63lvd1024.c                    | 5 +++--
> > > >>>  drivers/gpu/drm/bridge/ti-sn65dsi86.c                    | 5 ++++-
> > > >>>  drivers/gpu/drm/bridge/ti-tfp410.c                       | 5 ++++-
> > > >>>  drivers/gpu/drm/drm_bridge.c                             | 5 +++--
> > > >>>  drivers/gpu/drm/drm_simple_kms_helper.c                  | 2 +-
> > > >>>  drivers/gpu/drm/exynos/exynos_dp.c                       | 3 ++-
> > > >>>  drivers/gpu/drm/exynos/exynos_drm_dsi.c                  | 4 ++--
> > > >>>  drivers/gpu/drm/exynos/exynos_hdmi.c                     | 2 +-
> > > >>>  drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c                | 2 +-
> > > >>>  drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c             | 2 +-
> > > >>>  drivers/gpu/drm/i2c/tda998x_drv.c                        | 8 ++++++--
> > > >>>  drivers/gpu/drm/imx/imx-ldb.c                            | 2 +-
> > > >>>  drivers/gpu/drm/imx/parallel-display.c                   | 2 +-
> > > >>>  drivers/gpu/drm/mcde/mcde_dsi.c                          | 6 +++++-
> > > >>>  drivers/gpu/drm/mediatek/mtk_dpi.c                       | 2 +-
> > > >>>  drivers/gpu/drm/mediatek/mtk_dsi.c                       | 2 +-
> > > >>>  drivers/gpu/drm/mediatek/mtk_hdmi.c                      | 8 ++++++--
> > > >>>  drivers/gpu/drm/msm/dsi/dsi_manager.c                    | 4 ++--
> > > >>>  drivers/gpu/drm/msm/edp/edp_bridge.c                     | 2 +-
> > > >>>  drivers/gpu/drm/msm/hdmi/hdmi_bridge.c                   | 2 +-
> > > >>>  drivers/gpu/drm/omapdrm/omap_drv.c                       | 3 ++-
> > > >>>  drivers/gpu/drm/rcar-du/rcar_du_encoder.c                | 2 +-
> > > >>>  drivers/gpu/drm/rcar-du/rcar_lvds.c                      | 7 +++++--
> > > >>>  drivers/gpu/drm/rockchip/rockchip_lvds.c                 | 2 +-
> > > >>>  drivers/gpu/drm/rockchip/rockchip_rgb.c                  | 2 +-
> > > >>>  drivers/gpu/drm/sti/sti_dvo.c                            | 2 +-
> > > >>>  drivers/gpu/drm/sti/sti_hda.c                            | 2 +-
> > > >>>  drivers/gpu/drm/sti/sti_hdmi.c                           | 2 +-
> > > >>>  drivers/gpu/drm/stm/ltdc.c                               | 2 +-
> > > >>>  drivers/gpu/drm/sun4i/sun4i_lvds.c                       | 2 +-
> > > >>>  drivers/gpu/drm/sun4i/sun4i_rgb.c                        | 2 +-
> > > >>>  drivers/gpu/drm/tilcdc/tilcdc_external.c                 | 2 +-
> > > >>>  drivers/gpu/drm/vc4/vc4_dpi.c                            | 2 +-
> > > >>>  drivers/gpu/drm/vc4/vc4_dsi.c                            | 2 +-
> > > >>>  include/drm/drm_bridge.h                                 | 4 ++--
> > > >>>  53 files changed, 140 insertions(+), 67 deletions(-)
> > > >>>
> > > >>> diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> > > >>> index 98aac743cc26..739f2358f1d5 100644
> > > >>> --- a/drivers/gpu/drm/arc/arcpgu_hdmi.c
> > > >>> +++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c
> > > >>> @@ -39,7 +39,7 @@ int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np)
> > > >>>  		return ret;
> > > >>>  
> > > >>>  	/* Link drm_bridge to encoder */
> > > >>> -	ret = drm_bridge_attach(encoder, bridge, NULL);
> > > >>> +	ret = drm_bridge_attach(encoder, bridge, NULL, true);
> > > >> Few suggestions:
> > > >>
> > > >> 1. Maybe it would be more convenient to add flags argument instead of bool:
> > > >>
> > > >> - code should be more readable: ret = drm_bridge_attach(encoder, bridge,
> > > >> NULL, DRM_BRIDGE_FLAG_NO_CONNECTOR)
> > > >>
> > > >> - it can be easily expanded later with other flags, there at least two
> > > >> drivers which would benefit from DRM_BRIDGE_FLAG_NO_CHAINING flag.
> > > >
> > > > Please note that I think this flag should disappear once drivers get
> > > > converted to the new model. This will however take some time. I'm not
> > > > opposed to turning the book into a flag though. I was hoping to receive
> > > > more review comments on this particular patch, but as that's not the
> > > > case, I can already proceed with the change.
> > > >
> > > > What would the DRM_BRIDGE_FLAG_NO_CHAINING flag be used for ?
> > > 
> > > To avoid setting encoder->bridge or previous_bridge->next field to
> > > attached bridge (last lines of drm_bridge_attach code).
> > > 
> > > This is for sure the case of exynos_dsi and vc4_dsi, but I guess it can
> > > affect other drivers as well, probably they just use other workarounds
> > > or have more flexible hardware.
> > > 
> > > Generally idea that order of calling
> > > pre_enable/enable/disable/post_disable callbacks in chained
> > > encoder/bridges is fixed is wrong IMHO, only video source component
> > > knows in which moment it should enable its sink and if it should do
> > > something after. And it does not work at all with sources/sinks having
> > > more than one output/input.
> > 
> > This doesn't make sense to me ... if you don't want to have a chained
> > bridge, don't attach it? You can just open-code the attach and call the
> > bridge functions directly when appropriate. I think that's the better
> > long-term plan than trying to have flags for every possible topology
> > existing out there ...
> 
> I think the whole topology should be known to the display driver, so the
> next (and possibly previous if we later need it) pointers should be set
> in all cases. Otherwise we can't, for instance, notify all bridges of
> HPD events. Sure, a bridge could decide not to attach at all, but then
> it would have to reimplement lots of logic to forward everything
> manually, when the need for manual forwarding is usually limited to a
> certain category of operations.

That's kinda what I meant, if you have special topology it's going to be
more typing. Once we have more examples then we can lift that to helpers
and make it generic. But setting some kind of topology that isn't correct
feels a bit fishy ...

Another option is that we have an overall function for enable/disable,
with he current helpers as default implementations. But at that point you
might as well type your own I think.
-Daniel

> 
> > > >> 2. If the patch can be applied atomically it is OK as is, if not you can
> > > >> use preprocessor vararg magic to support new and old syntax, sth like:
> > > >>
> > > >> #define _drm_bridge_attach(encoder, bridge, prev, flags, optarg...)
> > > >> __drm_bridge_attach(encoder, bridge, prev, flags)
> > > >>
> > > >> #define drm_bridge_attach(encoder, bridge, prev, optarg...)
> > > >> _drm_bridge_attach(encoder, bridge, prev, ##optarg, 0)
> > > > Good point. I'll try to do this atomically, but if it fails I'll follow
> > > > your suggestion.
> > > >
> > > >> 3. Maybe more convenient would be to just set the flags directly before
> > > >> attachment:
> > > >>
> > > >>     bridge->dont_create_connector = true;
> > > >>
> > > >>     ret = drm_bridge_attach(encoder, bridge, NULL);
> > > >>
> > > >>     This way it will be still expandable, and less changes.
> > > > Bridges that are chained would need to set the dont_create_connector
> > > > flag of the next bridge. It would be a bit ugly, but would make this
> > > > patch smaller. On the other hand we would need to keep the if
> > > > (!create_connector) check in the .attach() handlers, and it would be
> > > > easier to miss it in bridge drivers (current or new) than with an
> > > > explicit argument to the .attach() operation. I would thus have a
> > > > preference for the new argument to .attach(). Especially if it can help
> > > > you with DRM_BRIDGE_FLAG_NO_CHAINING :-)
> > > 
> > > bridge->dont_chain would work as well :)
> > > 
> > > Btw I wonder if it could be possible to disallow creating connectors at all by new bridges - it would speed-up transition.
> > > 
> > > 
> > > Another long term idea. Since bridges can be attached to:
> > > - encoder,
> > > - another bridge,
> > > - crtc (I have one example, but I guess there could be more),
> > > - even before crtc (image postprocessing)
> > > And since bridge output goes to:
> > > - another bridge,
> > > - panel.
> > > 
> > > Wouldn't be better to create drm_source and drm_sink (do not respond with xkcd picture :) ):
> > > - drm_source will be embedded in source device context,
> > > - drm_sink will be embedded in sink device context.
> > > We could make then transitions of bridges to drm_sink with drm_source embeded in its context, and panels to drm_sink.
> > > This way we could drop these crazy constructs:
> > > - if sink is panel then do sth, elsif is bridge then do sth_else,
> > > - if src is bridge then do sth, elsif is encoder ... elsif ....
> > > - helpers of_find_panel_or_bridge,
> > > - drm_panel_bridge,
> > > Also we could implement easily multi input/output bridges/panels/crtcs whatever.
> > > And hpd callbacks you have proposed in another patch would fit better to drm_source.ops.
> > > ...
> > > 
> > > 
> > > Regards
> > > Andrzej
> > > 
> > > 
> > 
> > -- 
> > Daniel Vetter
> > Software Engineer, Intel Corporation
> > http://blog.ffwll.ch
> 
> -- 
> Regards,
> 
> Laurent Pinchart
diff mbox series

Patch

diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c
index 98aac743cc26..739f2358f1d5 100644
--- a/drivers/gpu/drm/arc/arcpgu_hdmi.c
+++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c
@@ -39,7 +39,7 @@  int arcpgu_drm_hdmi_init(struct drm_device *drm, struct device_node *np)
 		return ret;
 
 	/* Link drm_bridge to encoder */
-	ret = drm_bridge_attach(encoder, bridge, NULL);
+	ret = drm_bridge_attach(encoder, bridge, NULL, true);
 	if (ret)
 		drm_encoder_cleanup(encoder);
 
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
index f73d8a92274e..606841d2c0b0 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
@@ -123,7 +123,7 @@  static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
 	}
 
 	if (bridge) {
-		ret = drm_bridge_attach(&output->encoder, bridge, NULL);
+		ret = drm_bridge_attach(&output->encoder, bridge, NULL, true);
 		if (!ret)
 			return 0;
 
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index f6d2681f6927..c67ba30edec4 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -847,11 +847,15 @@  static void adv7511_bridge_mode_set(struct drm_bridge *bridge,
 	adv7511_mode_set(adv, mode, adj_mode);
 }
 
-static int adv7511_bridge_attach(struct drm_bridge *bridge)
+static int adv7511_bridge_attach(struct drm_bridge *bridge,
+				 bool create_connector)
 {
 	struct adv7511 *adv = bridge_to_adv7511(bridge);
 	int ret;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	if (!bridge->encoder) {
 		DRM_ERROR("Parent encoder object not found");
 		return -ENODEV;
diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c
index 3c7cc5af735c..f72755e59e12 100644
--- a/drivers/gpu/drm/bridge/analogix-anx78xx.c
+++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c
@@ -998,11 +998,15 @@  static const struct drm_connector_funcs anx78xx_connector_funcs = {
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
-static int anx78xx_bridge_attach(struct drm_bridge *bridge)
+static int anx78xx_bridge_attach(struct drm_bridge *bridge,
+				 bool create_connector)
 {
 	struct anx78xx *anx78xx = bridge_to_anx78xx(bridge);
 	int err;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	if (!bridge->encoder) {
 		DRM_ERROR("Parent encoder object not found");
 		return -ENODEV;
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 3f7f4880be09..f6a1bdcc09d6 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -1179,13 +1179,17 @@  static const struct drm_connector_funcs analogix_dp_connector_funcs = {
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
-static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
+static int analogix_dp_bridge_attach(struct drm_bridge *bridge,
+				     bool create_connector)
 {
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_encoder *encoder = dp->encoder;
 	struct drm_connector *connector = NULL;
 	int ret = 0;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	if (!bridge->encoder) {
 		DRM_ERROR("Parent encoder object not found");
 		return -ENODEV;
@@ -1463,7 +1467,7 @@  static int analogix_dp_create_bridge(struct drm_device *drm_dev,
 	bridge->driver_private = dp;
 	bridge->funcs = &analogix_dp_bridge_funcs;
 
-	ret = drm_bridge_attach(dp->encoder, bridge, NULL);
+	ret = drm_bridge_attach(dp->encoder, bridge, NULL, true);
 	if (ret) {
 		DRM_ERROR("failed to attach drm bridge\n");
 		return -EINVAL;
diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
index 6166dca6be81..45f50852cfbb 100644
--- a/drivers/gpu/drm/bridge/cdns-dsi.c
+++ b/drivers/gpu/drm/bridge/cdns-dsi.c
@@ -645,7 +645,8 @@  static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
 	return 0;
 }
 
-static int cdns_dsi_bridge_attach(struct drm_bridge *bridge)
+static int cdns_dsi_bridge_attach(struct drm_bridge *bridge,
+				  bool create_connector)
 {
 	struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
 	struct cdns_dsi *dsi = input_to_dsi(input);
@@ -657,7 +658,8 @@  static int cdns_dsi_bridge_attach(struct drm_bridge *bridge)
 		return -ENOTSUPP;
 	}
 
-	return drm_bridge_attach(bridge->encoder, output->bridge, bridge);
+	return drm_bridge_attach(bridge->encoder, output->bridge, bridge,
+				 create_connector);
 }
 
 static enum drm_mode_status
diff --git a/drivers/gpu/drm/bridge/lvds-encoder.c b/drivers/gpu/drm/bridge/lvds-encoder.c
index 2ab2c234f26c..bafab97521af 100644
--- a/drivers/gpu/drm/bridge/lvds-encoder.c
+++ b/drivers/gpu/drm/bridge/lvds-encoder.c
@@ -18,14 +18,14 @@  struct lvds_encoder {
 	struct gpio_desc *powerdown_gpio;
 };
 
-static int lvds_encoder_attach(struct drm_bridge *bridge)
+static int lvds_encoder_attach(struct drm_bridge *bridge, bool create_connector)
 {
 	struct lvds_encoder *lvds_encoder = container_of(bridge,
 							 struct lvds_encoder,
 							 bridge);
 
 	return drm_bridge_attach(bridge->encoder, lvds_encoder->panel_bridge,
-				 bridge);
+				 bridge, create_connector);
 }
 
 static void lvds_encoder_enable(struct drm_bridge *bridge)
diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
index 79311f8354bd..4250e2235f50 100644
--- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
+++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
@@ -206,13 +206,17 @@  static irqreturn_t ge_b850v3_lvds_irq_handler(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static int ge_b850v3_lvds_attach(struct drm_bridge *bridge)
+static int ge_b850v3_lvds_attach(struct drm_bridge *bridge,
+				 bool create_connector)
 {
 	struct drm_connector *connector = &ge_b850v3_lvds_ptr->connector;
 	struct i2c_client *stdp4028_i2c
 			= ge_b850v3_lvds_ptr->stdp4028_i2c;
 	int ret;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	if (!bridge->encoder) {
 		DRM_ERROR("Parent encoder object not found");
 		return -ENODEV;
diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c
index 98bc650b8c95..6bef439261da 100644
--- a/drivers/gpu/drm/bridge/nxp-ptn3460.c
+++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c
@@ -238,11 +238,15 @@  static const struct drm_connector_funcs ptn3460_connector_funcs = {
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
-static int ptn3460_bridge_attach(struct drm_bridge *bridge)
+static int ptn3460_bridge_attach(struct drm_bridge *bridge,
+				 bool create_connector)
 {
 	struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
 	int ret;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	if (!bridge->encoder) {
 		DRM_ERROR("Parent encoder object not found");
 		return -ENODEV;
diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index b12ae3a4c5f1..98ad4abf2409 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -52,12 +52,15 @@  static const struct drm_connector_funcs panel_bridge_connector_funcs = {
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
-static int panel_bridge_attach(struct drm_bridge *bridge)
+static int panel_bridge_attach(struct drm_bridge *bridge, bool create_connector)
 {
 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
 	struct drm_connector *connector = &panel_bridge->connector;
 	int ret;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	if (!bridge->encoder) {
 		DRM_ERROR("Missing encoder\n");
 		return -ENODEV;
diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c
index 2d88146e4836..b9243d51489b 100644
--- a/drivers/gpu/drm/bridge/parade-ps8622.c
+++ b/drivers/gpu/drm/bridge/parade-ps8622.c
@@ -476,11 +476,14 @@  static const struct drm_connector_funcs ps8622_connector_funcs = {
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
-static int ps8622_attach(struct drm_bridge *bridge)
+static int ps8622_attach(struct drm_bridge *bridge, bool create_connector)
 {
 	struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge);
 	int ret;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	if (!bridge->encoder) {
 		DRM_ERROR("Parent encoder object not found");
 		return -ENODEV;
diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
index dd7aa466b280..18904b1082b6 100644
--- a/drivers/gpu/drm/bridge/sii902x.c
+++ b/drivers/gpu/drm/bridge/sii902x.c
@@ -396,12 +396,16 @@  static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
 	mutex_unlock(&sii902x->mutex);
 }
 
-static int sii902x_bridge_attach(struct drm_bridge *bridge)
+static int sii902x_bridge_attach(struct drm_bridge *bridge,
+				 bool create_connector)
 {
 	struct sii902x *sii902x = bridge_to_sii902x(bridge);
 	struct drm_device *drm = bridge->dev;
 	int ret;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	drm_connector_helper_add(&sii902x->connector,
 				 &sii902x_connector_helper_funcs);
 
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
index 0cc293a6ac24..ea6529df7d9c 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.c
+++ b/drivers/gpu/drm/bridge/sil-sii8620.c
@@ -2203,7 +2203,7 @@  static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
 	return container_of(bridge, struct sii8620, bridge);
 }
 
-static int sii8620_attach(struct drm_bridge *bridge)
+static int sii8620_attach(struct drm_bridge *bridge, bool create_connector)
 {
 	struct sii8620 *ctx = bridge_to_sii8620(bridge);
 
diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c
index 7495b9bef865..86885eb6e28d 100644
--- a/drivers/gpu/drm/bridge/simple-bridge.c
+++ b/drivers/gpu/drm/bridge/simple-bridge.c
@@ -108,11 +108,15 @@  static const struct drm_connector_funcs simple_bridge_con_funcs = {
 	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
 };
 
-static int simple_bridge_attach(struct drm_bridge *bridge)
+static int simple_bridge_attach(struct drm_bridge *bridge,
+				bool create_connector)
 {
 	struct simple_bridge *sbridge = drm_bridge_to_simple_bridge(bridge);
 	int ret;
 
+	if (!create_connector)
+		return 0;
+
 	if (!bridge->encoder) {
 		DRM_ERROR("Missing encoder\n");
 		return -ENODEV;
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index c6490949d9db..930d67c618dd 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -2174,12 +2174,16 @@  static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
 	.get_modes = dw_hdmi_connector_get_modes,
 };
 
-static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
+static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
+				 bool create_connector)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
 	struct drm_encoder *encoder = bridge->encoder;
 	struct drm_connector *connector = &hdmi->connector;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	connector->interlace_allowed = 1;
 	connector->polled = DRM_CONNECTOR_POLL_HPD;
 
@@ -2857,7 +2861,7 @@  struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
 	if (IS_ERR(hdmi))
 		return hdmi;
 
-	ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL);
+	ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL, true);
 	if (ret) {
 		dw_hdmi_remove(hdmi);
 		DRM_ERROR("Failed to initialize bridge with drm\n");
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index 281c58bab1a1..05cf97ad524f 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -906,7 +906,8 @@  dw_mipi_dsi_bridge_mode_valid(struct drm_bridge *bridge,
 	return mode_status;
 }
 
-static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
+static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge,
+				     bool create_connector)
 {
 	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
 
@@ -919,7 +920,8 @@  static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
 	bridge->encoder->encoder_type = DRM_MODE_ENCODER_DSI;
 
 	/* Attach the panel-bridge to the dsi bridge */
-	return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge);
+	return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge,
+				 create_connector);
 }
 
 static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = {
@@ -1064,7 +1066,7 @@  int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder)
 {
 	int ret;
 
-	ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
+	ret = drm_bridge_attach(encoder, &dsi->bridge, NULL, true);
 	if (ret) {
 		DRM_ERROR("Failed to initialize bridge with drm\n");
 		return ret;
diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c
index 170f162ffa55..6016f3aae42f 100644
--- a/drivers/gpu/drm/bridge/tc358764.c
+++ b/drivers/gpu/drm/bridge/tc358764.c
@@ -348,12 +348,15 @@  static void tc358764_enable(struct drm_bridge *bridge)
 		dev_err(ctx->dev, "error enabling panel (%d)\n", ret);
 }
 
-static int tc358764_attach(struct drm_bridge *bridge)
+static int tc358764_attach(struct drm_bridge *bridge, bool create_connector)
 {
 	struct tc358764 *ctx = bridge_to_tc358764(bridge);
 	struct drm_device *drm = bridge->dev;
 	int ret;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	ctx->connector.polled = DRM_CONNECTOR_POLL_HPD;
 	ret = drm_connector_init(drm, &ctx->connector,
 				 &tc358764_connector_funcs,
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index 13ade28a36a8..e2b2d2660adc 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -1273,13 +1273,16 @@  static const struct drm_connector_funcs tc_connector_funcs = {
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
-static int tc_bridge_attach(struct drm_bridge *bridge)
+static int tc_bridge_attach(struct drm_bridge *bridge, bool create_connector)
 {
 	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
 	struct tc_data *tc = bridge_to_tc(bridge);
 	struct drm_device *drm = bridge->dev;
 	int ret;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	/* Create DP/eDP connector */
 	drm_connector_helper_add(&tc->connector, &tc_connector_helper_funcs);
 	ret = drm_connector_init(drm, &tc->connector, &tc_connector_funcs,
diff --git a/drivers/gpu/drm/bridge/thc63lvd1024.c b/drivers/gpu/drm/bridge/thc63lvd1024.c
index 3d74129b2995..86f3b96f95db 100644
--- a/drivers/gpu/drm/bridge/thc63lvd1024.c
+++ b/drivers/gpu/drm/bridge/thc63lvd1024.c
@@ -42,11 +42,12 @@  static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge)
 	return container_of(bridge, struct thc63_dev, bridge);
 }
 
-static int thc63_attach(struct drm_bridge *bridge)
+static int thc63_attach(struct drm_bridge *bridge, bool create_connector)
 {
 	struct thc63_dev *thc63 = to_thc63(bridge);
 
-	return drm_bridge_attach(bridge->encoder, thc63->next, bridge);
+	return drm_bridge_attach(bridge->encoder, thc63->next, bridge,
+				 create_connector);
 }
 
 static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge,
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
index b77a52d05061..dbe265f7112d 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
@@ -224,7 +224,7 @@  static int ti_sn_bridge_parse_regulators(struct ti_sn_bridge *pdata)
 				       pdata->supplies);
 }
 
-static int ti_sn_bridge_attach(struct drm_bridge *bridge)
+static int ti_sn_bridge_attach(struct drm_bridge *bridge, bool create_connector)
 {
 	int ret, val;
 	struct ti_sn_bridge *pdata = bridge_to_ti_sn_bridge(bridge);
@@ -235,6 +235,9 @@  static int ti_sn_bridge_attach(struct drm_bridge *bridge)
 						   .node = NULL,
 						 };
 
+	if (!create_connector)
+		return -EINVAL;
+
 	ret = drm_connector_init(bridge->dev, &pdata->connector,
 				 &ti_sn_bridge_connector_funcs,
 				 DRM_MODE_CONNECTOR_eDP);
diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c
index 4e76b2b27374..8d4690e436c3 100644
--- a/drivers/gpu/drm/bridge/ti-tfp410.c
+++ b/drivers/gpu/drm/bridge/ti-tfp410.c
@@ -121,11 +121,14 @@  static const struct drm_connector_funcs tfp410_con_funcs = {
 	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
 };
 
-static int tfp410_attach(struct drm_bridge *bridge)
+static int tfp410_attach(struct drm_bridge *bridge, bool create_connector)
 {
 	struct tfp410 *dvi = drm_bridge_to_tfp410(bridge);
 	int ret;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	if (!bridge->encoder) {
 		dev_err(dvi->dev, "Missing encoder\n");
 		return -ENODEV;
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index cba537c99e43..519577f363e3 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -95,6 +95,7 @@  EXPORT_SYMBOL(drm_bridge_remove);
  * @encoder: DRM encoder
  * @bridge: bridge to attach
  * @previous: previous bridge in the chain (optional)
+ * @create_connector: true if the bridge should create a drm_connector
  *
  * Called by a kms driver to link the bridge to an encoder's chain. The previous
  * argument specifies the previous bridge in the chain. If NULL, the bridge is
@@ -112,7 +113,7 @@  EXPORT_SYMBOL(drm_bridge_remove);
  * Zero on success, error code on failure
  */
 int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
-		      struct drm_bridge *previous)
+		      struct drm_bridge *previous, bool create_connector)
 {
 	int ret;
 
@@ -129,7 +130,7 @@  int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
 	bridge->encoder = encoder;
 
 	if (bridge->funcs->attach) {
-		ret = bridge->funcs->attach(bridge);
+		ret = bridge->funcs->attach(bridge, create_connector);
 		if (ret < 0) {
 			bridge->dev = NULL;
 			bridge->encoder = NULL;
diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
index b11910f14c46..a367ef1e5081 100644
--- a/drivers/gpu/drm/drm_simple_kms_helper.c
+++ b/drivers/gpu/drm/drm_simple_kms_helper.c
@@ -228,7 +228,7 @@  static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
 int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
 					  struct drm_bridge *bridge)
 {
-	return drm_bridge_attach(&pipe->encoder, bridge, NULL);
+	return drm_bridge_attach(&pipe->encoder, bridge, NULL, true);
 }
 EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
 
diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c
index 3a0f0ba8c63a..02b98e6ca52d 100644
--- a/drivers/gpu/drm/exynos/exynos_dp.c
+++ b/drivers/gpu/drm/exynos/exynos_dp.c
@@ -105,7 +105,8 @@  static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data,
 
 	/* Pre-empt DP connector creation if there's a bridge */
 	if (dp->ptn_bridge) {
-		ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge);
+		ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge,
+					true);
 		if (ret) {
 			DRM_DEV_ERROR(dp->dev,
 				      "Failed to attach bridge to drm\n");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 5f6f523821a2..768acc79d10c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1522,7 +1522,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);
+		drm_bridge_attach(encoder, out_bridge, NULL, true);
 		dsi->out_bridge = out_bridge;
 		encoder->bridge = NULL;
 	} else {
@@ -1698,7 +1698,7 @@  static int exynos_dsi_bind(struct device *dev, struct device *master,
 	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, NULL, true);
 	}
 
 	return mipi_dsi_host_register(&dsi->dsi_host);
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index bc1565f1822a..808c98101d56 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -952,7 +952,7 @@  static int hdmi_create_connector(struct drm_encoder *encoder)
 	drm_connector_attach_encoder(connector, encoder);
 
 	if (hdata->bridge) {
-		ret = drm_bridge_attach(encoder, hdata->bridge, NULL);
+		ret = drm_bridge_attach(encoder, hdata->bridge, NULL, true);
 		if (ret)
 			DRM_DEV_ERROR(hdata->dev, "Failed to attach bridge\n");
 	}
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
index c49e9e3740f8..8d22618ccf2e 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
@@ -159,5 +159,5 @@  int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev)
 		return fsl_dcu_attach_panel(fsl_dev, panel);
 	}
 
-	return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL);
+	return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL, true);
 }
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
index 3d6c45097f51..eac8ec1512ab 100644
--- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
+++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
@@ -780,7 +780,7 @@  static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi)
 	int ret;
 
 	/* associate the bridge to dsi encoder */
-	ret = drm_bridge_attach(encoder, bridge, NULL);
+	ret = drm_bridge_attach(encoder, bridge, NULL, true);
 	if (ret) {
 		DRM_ERROR("failed to attach external bridge\n");
 		return ret;
diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c
index 3d368c43185f..6b2e648b6c4d 100644
--- a/drivers/gpu/drm/i2c/tda998x_drv.c
+++ b/drivers/gpu/drm/i2c/tda998x_drv.c
@@ -1366,10 +1366,14 @@  static int tda998x_connector_init(struct tda998x_priv *priv,
 
 /* DRM bridge functions */
 
-static int tda998x_bridge_attach(struct drm_bridge *bridge)
+static int tda998x_bridge_attach(struct drm_bridge *bridge,
+				 bool create_connector)
 {
 	struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge);
 
+	if (!create_connector)
+		return -EINVAL;
+
 	return tda998x_connector_init(priv, bridge->dev);
 }
 
@@ -2033,7 +2037,7 @@  static int tda998x_encoder_init(struct device *dev, struct drm_device *drm)
 	if (ret)
 		goto err_encoder;
 
-	ret = drm_bridge_attach(&priv->encoder, &priv->bridge, NULL);
+	ret = drm_bridge_attach(&priv->encoder, &priv->bridge, NULL, true);
 	if (ret)
 		goto err_bridge;
 
diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c
index 383733302280..845ead25ade7 100644
--- a/drivers/gpu/drm/imx/imx-ldb.c
+++ b/drivers/gpu/drm/imx/imx-ldb.c
@@ -446,7 +446,7 @@  static int imx_ldb_register(struct drm_device *drm,
 
 	if (imx_ldb_ch->bridge) {
 		ret = drm_bridge_attach(&imx_ldb_ch->encoder,
-					imx_ldb_ch->bridge, NULL);
+					imx_ldb_ch->bridge, NULL, true);
 		if (ret) {
 			DRM_ERROR("Failed to initialize bridge with drm\n");
 			return ret;
diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c
index 1a76de1e8e7b..cd746592d2a7 100644
--- a/drivers/gpu/drm/imx/parallel-display.c
+++ b/drivers/gpu/drm/imx/parallel-display.c
@@ -182,7 +182,7 @@  static int imx_pd_register(struct drm_device *drm,
 		drm_panel_attach(imxpd->panel, &imxpd->connector);
 
 	if (imxpd->bridge) {
-		ret = drm_bridge_attach(encoder, imxpd->bridge, NULL);
+		ret = drm_bridge_attach(encoder, imxpd->bridge, NULL, true);
 		if (ret < 0) {
 			dev_err(imxpd->dev, "failed to attach bridge: %d\n",
 				ret);
diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c
index 07f7090d08b3..f2deadc980f8 100644
--- a/drivers/gpu/drm/mcde/mcde_dsi.c
+++ b/drivers/gpu/drm/mcde/mcde_dsi.c
@@ -817,12 +817,16 @@  mcde_dsi_connector_helper_funcs = {
 	.get_modes = mcde_dsi_get_modes,
 };
 
-static int mcde_dsi_bridge_attach(struct drm_bridge *bridge)
+static int mcde_dsi_bridge_attach(struct drm_bridge *bridge,
+				  bool create_connector)
 {
 	struct mcde_dsi *d = bridge_to_mcde_dsi(bridge);
 	struct drm_device *drm = bridge->dev;
 	int ret;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	drm_connector_helper_add(&d->connector,
 				 &mcde_dsi_connector_helper_funcs);
 
diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c
index bacd989cc9aa..1ff27bb17016 100644
--- a/drivers/gpu/drm/mediatek/mtk_dpi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dpi.c
@@ -604,7 +604,7 @@  static int mtk_dpi_bind(struct device *dev, struct device *master, void *data)
 	/* Currently DPI0 is fixed to be driven by OVL1 */
 	dpi->encoder.possible_crtcs = BIT(1);
 
-	ret = drm_bridge_attach(&dpi->encoder, dpi->bridge, NULL);
+	ret = drm_bridge_attach(&dpi->encoder, dpi->bridge, NULL, true);
 	if (ret) {
 		dev_err(dev, "Failed to attach bridge: %d\n", ret);
 		goto err_cleanup;
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index b91c4616644a..9c5bac48b44f 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -819,7 +819,7 @@  static int mtk_dsi_create_conn_enc(struct drm_device *drm, struct mtk_dsi *dsi)
 
 	/* If there's a bridge, attach to it and let it create the connector */
 	if (dsi->bridge) {
-		ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL);
+		ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL, true);
 		if (ret) {
 			DRM_ERROR("Failed to attach bridge to drm\n");
 			goto err_encoder_cleanup;
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index 5d6a9f094df5..d3248a881cf0 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -1290,11 +1290,15 @@  static void mtk_hdmi_hpd_event(bool hpd, struct device *dev)
  * Bridge callbacks
  */
 
-static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge)
+static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge,
+				  bool create_connector)
 {
 	struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
 	int ret;
 
+	if (!create_connector)
+		return -EINVAL;
+
 	ret = drm_connector_init(bridge->encoder->dev, &hdmi->conn,
 				 &mtk_hdmi_connector_funcs,
 				 DRM_MODE_CONNECTOR_HDMIA);
@@ -1318,7 +1322,7 @@  static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge)
 
 	if (hdmi->next_bridge) {
 		ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge,
-					bridge);
+					bridge, create_connector);
 		if (ret) {
 			dev_err(hdmi->dev,
 				"Failed to attach external bridge: %d\n", ret);
diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c
index 271aa7bbca92..ca733086041a 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_manager.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c
@@ -664,7 +664,7 @@  struct drm_bridge *msm_dsi_manager_bridge_init(u8 id)
 	bridge = &dsi_bridge->base;
 	bridge->funcs = &dsi_mgr_bridge_funcs;
 
-	ret = drm_bridge_attach(encoder, bridge, NULL);
+	ret = drm_bridge_attach(encoder, bridge, NULL, true);
 	if (ret)
 		goto fail;
 
@@ -693,7 +693,7 @@  struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id)
 	encoder = msm_dsi->encoder;
 
 	/* link the internal dsi bridge to the external bridge */
-	drm_bridge_attach(encoder, ext_bridge, int_bridge);
+	drm_bridge_attach(encoder, ext_bridge, int_bridge, true);
 
 	/*
 	 * we need the drm_connector created by the external bridge
diff --git a/drivers/gpu/drm/msm/edp/edp_bridge.c b/drivers/gpu/drm/msm/edp/edp_bridge.c
index 2950bba4aca9..32a463c84cc1 100644
--- a/drivers/gpu/drm/msm/edp/edp_bridge.c
+++ b/drivers/gpu/drm/msm/edp/edp_bridge.c
@@ -91,7 +91,7 @@  struct drm_bridge *msm_edp_bridge_init(struct msm_edp *edp)
 	bridge = &edp_bridge->base;
 	bridge->funcs = &edp_bridge_funcs;
 
-	ret = drm_bridge_attach(edp->encoder, bridge, NULL);
+	ret = drm_bridge_attach(edp->encoder, bridge, NULL, true);
 	if (ret)
 		goto fail;
 
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
index 03197b8959ba..d7738aafcff8 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
@@ -296,7 +296,7 @@  struct drm_bridge *msm_hdmi_bridge_init(struct hdmi *hdmi)
 	bridge = &hdmi_bridge->base;
 	bridge->funcs = &msm_hdmi_bridge_funcs;
 
-	ret = drm_bridge_attach(hdmi->encoder, bridge, NULL);
+	ret = drm_bridge_attach(hdmi->encoder, bridge, NULL, true);
 	if (ret)
 		goto fail;
 
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 672e0f8ad11c..837d0cd20dd1 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -301,7 +301,8 @@  static int omap_modeset_init(struct drm_device *dev)
 
 		if (pipe->output->bridge) {
 			ret = drm_bridge_attach(pipe->encoder,
-						pipe->output->bridge, NULL);
+						pipe->output->bridge, NULL,
+						true);
 			if (ret < 0)
 				return ret;
 		}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index 0f00bdfe2366..74c2ae5ce687 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -120,7 +120,7 @@  int rcar_du_encoder_init(struct rcar_du_device *rcdu,
 	 * Attach the bridge to the encoder. The bridge will create the
 	 * connector.
 	 */
-	ret = drm_bridge_attach(encoder, bridge, NULL);
+	ret = drm_bridge_attach(encoder, bridge, NULL, true);
 	if (ret) {
 		drm_encoder_cleanup(encoder);
 		return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c
index 1c62578590f4..a8d8b05c4731 100644
--- a/drivers/gpu/drm/rcar-du/rcar_lvds.c
+++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c
@@ -605,7 +605,7 @@  static void rcar_lvds_mode_set(struct drm_bridge *bridge,
 	rcar_lvds_get_lvds_mode(lvds);
 }
 
-static int rcar_lvds_attach(struct drm_bridge *bridge)
+static int rcar_lvds_attach(struct drm_bridge *bridge, bool create_connector)
 {
 	struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
 	struct drm_connector *connector = &lvds->connector;
@@ -615,7 +615,10 @@  static int rcar_lvds_attach(struct drm_bridge *bridge)
 	/* If we have a next bridge just attach it. */
 	if (lvds->next_bridge)
 		return drm_bridge_attach(bridge->encoder, lvds->next_bridge,
-					 bridge);
+					 bridge, create_connector);
+
+	if (!create_connector)
+		return -EINVAL;
 
 	/* Otherwise if we have a panel, create a connector. */
 	if (!lvds->panel)
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index 830858a809e5..7ca412d294b2 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -440,7 +440,7 @@  static int rockchip_lvds_bind(struct device *dev, struct device *master,
 			goto err_free_connector;
 		}
 	} else {
-		ret = drm_bridge_attach(encoder, lvds->bridge, NULL);
+		ret = drm_bridge_attach(encoder, lvds->bridge, NULL, true);
 		if (ret) {
 			DRM_DEV_ERROR(drm_dev->dev,
 				      "failed to attach bridge: %d\n", ret);
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c
index ce4d82d293e4..8218bbd09a72 100644
--- a/drivers/gpu/drm/rockchip/rockchip_rgb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c
@@ -143,7 +143,7 @@  struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
 
 	rgb->bridge = bridge;
 
-	ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
+	ret = drm_bridge_attach(encoder, rgb->bridge, NULL, true);
 	if (ret) {
 		DRM_DEV_ERROR(drm_dev->dev,
 			      "failed to attach bridge: %d\n", ret);
diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c
index 9e6d5d8b7030..f09209621568 100644
--- a/drivers/gpu/drm/sti/sti_dvo.c
+++ b/drivers/gpu/drm/sti/sti_dvo.c
@@ -468,7 +468,7 @@  static int sti_dvo_bind(struct device *dev, struct device *master, void *data)
 	bridge->of_node = dvo->dev.of_node;
 	drm_bridge_add(bridge);
 
-	err = drm_bridge_attach(encoder, bridge, NULL);
+	err = drm_bridge_attach(encoder, bridge, NULL, true);
 	if (err) {
 		DRM_ERROR("Failed to attach bridge\n");
 		return err;
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
index 94e404f13234..87e0fb742dc8 100644
--- a/drivers/gpu/drm/sti/sti_hda.c
+++ b/drivers/gpu/drm/sti/sti_hda.c
@@ -700,7 +700,7 @@  static int sti_hda_bind(struct device *dev, struct device *master, void *data)
 
 	bridge->driver_private = hda;
 	bridge->funcs = &sti_hda_bridge_funcs;
-	drm_bridge_attach(encoder, bridge, NULL);
+	drm_bridge_attach(encoder, bridge, NULL, true);
 
 	connector->encoder = encoder;
 
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
index f03d617edc4c..8c0ffe6833f9 100644
--- a/drivers/gpu/drm/sti/sti_hdmi.c
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -1276,7 +1276,7 @@  static int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 	bridge->driver_private = hdmi;
 	bridge->funcs = &sti_hdmi_bridge_funcs;
-	drm_bridge_attach(encoder, bridge, NULL);
+	drm_bridge_attach(encoder, bridge, NULL, true);
 
 	connector->encoder = encoder;
 
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 2fe6c4a8d915..10a9f848c5f6 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -1053,7 +1053,7 @@  static int ltdc_encoder_init(struct drm_device *ddev, struct drm_bridge *bridge)
 	drm_encoder_init(ddev, encoder, &ltdc_encoder_funcs,
 			 DRM_MODE_ENCODER_DPI, NULL);
 
-	ret = drm_bridge_attach(encoder, bridge, NULL);
+	ret = drm_bridge_attach(encoder, bridge, NULL, true);
 	if (ret) {
 		drm_encoder_cleanup(encoder);
 		return -EINVAL;
diff --git a/drivers/gpu/drm/sun4i/sun4i_lvds.c b/drivers/gpu/drm/sun4i/sun4i_lvds.c
index 3a3ba99fed22..3e5170fa1e67 100644
--- a/drivers/gpu/drm/sun4i/sun4i_lvds.c
+++ b/drivers/gpu/drm/sun4i/sun4i_lvds.c
@@ -155,7 +155,7 @@  int sun4i_lvds_init(struct drm_device *drm, struct sun4i_tcon *tcon)
 	}
 
 	if (bridge) {
-		ret = drm_bridge_attach(encoder, bridge, NULL);
+		ret = drm_bridge_attach(encoder, bridge, NULL, true);
 		if (ret) {
 			dev_err(drm->dev, "Couldn't attach our bridge\n");
 			goto err_cleanup_connector;
diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
index a901ec689b62..3f8629445fbb 100644
--- a/drivers/gpu/drm/sun4i/sun4i_rgb.c
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
@@ -252,7 +252,7 @@  int sun4i_rgb_init(struct drm_device *drm, struct sun4i_tcon *tcon)
 	}
 
 	if (rgb->bridge) {
-		ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
+		ret = drm_bridge_attach(encoder, rgb->bridge, NULL, true);
 		if (ret) {
 			dev_err(drm->dev, "Couldn't attach our bridge\n");
 			goto err_cleanup_connector;
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c
index e9969cd36610..ec693c11e455 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_external.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c
@@ -168,7 +168,7 @@  int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge)
 
 	priv->external_encoder->possible_crtcs = BIT(0);
 
-	ret = drm_bridge_attach(priv->external_encoder, bridge, NULL);
+	ret = drm_bridge_attach(priv->external_encoder, bridge, NULL, true);
 	if (ret) {
 		dev_err(ddev->dev, "drm_bridge_attach() failed %d\n", ret);
 		return ret;
diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 34f90ca8f479..2d7c5cf0d468 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -262,7 +262,7 @@  static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
 	if (panel)
 		bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DPI);
 
-	return drm_bridge_attach(dpi->encoder, bridge, NULL);
+	return drm_bridge_attach(dpi->encoder, bridge, NULL, true);
 }
 
 static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 2ea4e20b7b8a..3edd7ffc7383 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1607,7 +1607,7 @@  static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 			 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);
+	ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL, true);
 	if (ret) {
 		dev_err(dev, "bridge attach failed: %d\n", ret);
 		return ret;
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 7616f6562fe4..08dc15f93ded 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -48,7 +48,7 @@  struct drm_bridge_funcs {
 	 *
 	 * Zero on success, error code on failure.
 	 */
-	int (*attach)(struct drm_bridge *bridge);
+	int (*attach)(struct drm_bridge *bridge, bool create_connector);
 
 	/**
 	 * @detach:
@@ -404,7 +404,7 @@  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);
 int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
-		      struct drm_bridge *previous);
+		      struct drm_bridge *previous, bool create_connector);
 
 bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
 			   const struct drm_display_mode *mode,