diff mbox series

[3/6] drm/sun4i: dsi: Add bridge support

Message ID 20190315130825.9005-4-jagan@amarulasolutions.com (mailing list archive)
State New, archived
Headers show
Series drm/bridge: Add ICN6211 MIPI-DSI/RGB bridge | expand

Commit Message

Jagan Teki March 15, 2019, 1:08 p.m. UTC
Some display panels would come up with a non-DSI output which
can have an option to connect DSI interface by means of bridge
convertor.

This DSI to non-DSI bridge convertor would require a bridge
driver that would communicate the DSI controller for bridge
functionalities.

So, add support for bridge functionalities in Allwinner DSI
controller.

Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 65 +++++++++++++++++++-------
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h |  1 +
 2 files changed, 49 insertions(+), 17 deletions(-)

Comments

Paul Kocialkowski March 15, 2019, 1:32 p.m. UTC | #1
Hi,

On Fri, 2019-03-15 at 18:38 +0530, Jagan Teki wrote:
> Some display panels would come up with a non-DSI output which
> can have an option to connect DSI interface by means of bridge
> convertor.
> 
> This DSI to non-DSI bridge convertor would require a bridge
> driver that would communicate the DSI controller for bridge
> functionalities.
> 
> So, add support for bridge functionalities in Allwinner DSI
> controller.

See a few comments below.

> Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
> ---
>  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 65 +++++++++++++++++++-------
>  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h |  1 +
>  2 files changed, 49 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> index 0960b96b62cc..64d74313b842 100644
> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> @@ -781,6 +781,9 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
>  	if (!IS_ERR(dsi->panel))
>  		drm_panel_prepare(dsi->panel);
>  
> +	if (!IS_ERR(dsi->bridge))
> +		drm_bridge_pre_enable(dsi->bridge);
> +
>  	/*
>  	 * FIXME: This should be moved after the switch to HS mode.
>  	 *
> @@ -796,6 +799,9 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
>  	if (!IS_ERR(dsi->panel))
>  		drm_panel_enable(dsi->panel);
>  
> +	if (!IS_ERR(dsi->bridge))
> +		drm_bridge_enable(dsi->bridge);
> +
>  	sun6i_dsi_start(dsi, DSI_START_HSC);
>  
>  	udelay(1000);
> @@ -812,6 +818,9 @@ static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder)
>  	if (!IS_ERR(dsi->panel)) {
>  		drm_panel_disable(dsi->panel);
>  		drm_panel_unprepare(dsi->panel);
> +	} else if (!IS_ERR(dsi->bridge)) {
> +		drm_bridge_disable(dsi->bridge);
> +		drm_bridge_post_disable(dsi->bridge);
>  	}
>  
>  	phy_power_off(dsi->dphy);
> @@ -973,11 +982,16 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host,
>  	struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);
>  
>  	dsi->device = device;
> -	dsi->panel = of_drm_find_panel(device->dev.of_node);
> -	if (IS_ERR(dsi->panel))
> -		return PTR_ERR(dsi->panel);
>  
> -	dev_info(host->dev, "Attached device %s\n", device->name);
> +	dsi->bridge = of_drm_find_bridge(device->dev.of_node);
> +	if (!dsi->bridge) {

You are using IS_ERR to check that the bridge is alive in the changes
above, but switch to checking that it's non-NULL at this point.

Are both guaranteed to be interchangeable?

> +		dsi->panel = of_drm_find_panel(device->dev.of_node);
> +		if (IS_ERR(dsi->panel))
> +			return PTR_ERR(dsi->panel);
> +	}

You should probably use drm_of_find_panel_or_bridge instead of
duplicating the logic here.

> +	dev_info(host->dev, "Attached %s %s\n",
> +		 dsi->bridge ? "bridge" : "panel", device->name);
>  
>  	return 0;
>  }
> @@ -1055,8 +1069,10 @@ static int sun6i_dsi_bind(struct device *dev, struct device *master,
>  	struct sun4i_tcon *tcon0 = sun4i_get_tcon0(drm);
>  	int ret;
>  
> -	if (!dsi->panel)
> +	if (!(dsi->panel || dsi->bridge)) {
> +		dev_info(drm->dev, "No panel or bridge found... DSI output disabled\n");
>  		return -EPROBE_DEFER;
> +	}
>  
>  	dsi->drv = drv;
>  
> @@ -1078,19 +1094,29 @@ static int sun6i_dsi_bind(struct device *dev, struct device *master,
>  	}
>  	dsi->encoder.possible_crtcs = BIT(0);
>  
> -	drm_connector_helper_add(&dsi->connector,
> -				 &sun6i_dsi_connector_helper_funcs);
> -	ret = drm_connector_init(drm, &dsi->connector,
> -				 &sun6i_dsi_connector_funcs,
> -				 DRM_MODE_CONNECTOR_DSI);
> -	if (ret) {
> -		dev_err(dsi->dev,
> -			"Couldn't initialise the DSI connector\n");
> -		goto err_cleanup_connector;
> +	if (dsi->panel) {
> +		drm_connector_helper_add(&dsi->connector,
> +					 &sun6i_dsi_connector_helper_funcs);
> +		ret = drm_connector_init(drm, &dsi->connector,
> +					 &sun6i_dsi_connector_funcs,
> +					 DRM_MODE_CONNECTOR_DSI);
> +		if (ret) {
> +			dev_err(dsi->dev,
> +				"Couldn't initialise the DSI connector\n");
> +			goto err_cleanup_connector;
> +		}
> +
> +		drm_connector_attach_encoder(&dsi->connector, &dsi->encoder);
> +		drm_panel_attach(dsi->panel, &dsi->connector);
>  	}
>  
> -	drm_connector_attach_encoder(&dsi->connector, &dsi->encoder);
> -	drm_panel_attach(dsi->panel, &dsi->connector);
> +	if (dsi->bridge) {
> +		ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL);
> +		if (ret) {
> +			dev_err(dsi->dev, "Couldn't attach the DSI bridge\n");
> +			goto err_cleanup_connector;
> +		}
> +	}
>  
>  	return 0;
>  
> @@ -1104,7 +1130,12 @@ static void sun6i_dsi_unbind(struct device *dev, struct device *master,
>  {
>  	struct sun6i_dsi *dsi = dev_get_drvdata(dev);
>  
> -	drm_panel_detach(dsi->panel);
> +	if (dsi->panel)
> +		drm_panel_detach(dsi->panel);
> +
> +	if (dsi->bridge)
> +		drm_bridge_detach(dsi->bridge);

As I mentionned in the first patch, this is quite suspicious since the
DRM core should be in charge of detaching the bridge, not the driver.

Cheers,

Paul

> +
>  }
>  
>  static const struct component_ops sun6i_dsi_ops = {
> diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
> index 5c4983212f89..76874ff8e3ef 100644
> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
> +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
> @@ -36,6 +36,7 @@ struct sun6i_dsi {
>  	struct sun4i_tcon	*tcon;
>  	struct mipi_dsi_device	*device;
>  	struct drm_panel	*panel;
> +	struct drm_bridge	*bridge;
>  	const struct sun6i_dsi_variant	*variant;
>  };
>  
> -- 
> 2.18.0.321.gffc6fa0e3
>
Maxime Ripard March 15, 2019, 1:45 p.m. UTC | #2
On Fri, Mar 15, 2019 at 02:32:55PM +0100, Paul Kocialkowski wrote:
> Hi,
> 
> On Fri, 2019-03-15 at 18:38 +0530, Jagan Teki wrote:
> > Some display panels would come up with a non-DSI output which
> > can have an option to connect DSI interface by means of bridge
> > convertor.
> > 
> > This DSI to non-DSI bridge convertor would require a bridge
> > driver that would communicate the DSI controller for bridge
> > functionalities.
> > 
> > So, add support for bridge functionalities in Allwinner DSI
> > controller.
> 
> See a few comments below.
> 
> > Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
> > ---
> >  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 65 +++++++++++++++++++-------
> >  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h |  1 +
> >  2 files changed, 49 insertions(+), 17 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> > index 0960b96b62cc..64d74313b842 100644
> > --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> > +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> > @@ -781,6 +781,9 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
> >  	if (!IS_ERR(dsi->panel))
> >  		drm_panel_prepare(dsi->panel);
> >  
> > +	if (!IS_ERR(dsi->bridge))
> > +		drm_bridge_pre_enable(dsi->bridge);
> > +
> >  	/*
> >  	 * FIXME: This should be moved after the switch to HS mode.
> >  	 *
> > @@ -796,6 +799,9 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
> >  	if (!IS_ERR(dsi->panel))
> >  		drm_panel_enable(dsi->panel);
> >  
> > +	if (!IS_ERR(dsi->bridge))
> > +		drm_bridge_enable(dsi->bridge);
> > +
> >  	sun6i_dsi_start(dsi, DSI_START_HSC);
> >  
> >  	udelay(1000);
> > @@ -812,6 +818,9 @@ static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder)
> >  	if (!IS_ERR(dsi->panel)) {
> >  		drm_panel_disable(dsi->panel);
> >  		drm_panel_unprepare(dsi->panel);
> > +	} else if (!IS_ERR(dsi->bridge)) {
> > +		drm_bridge_disable(dsi->bridge);
> > +		drm_bridge_post_disable(dsi->bridge);
> >  	}
> >  
> >  	phy_power_off(dsi->dphy);
> > @@ -973,11 +982,16 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host,
> >  	struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);
> >  
> >  	dsi->device = device;
> > -	dsi->panel = of_drm_find_panel(device->dev.of_node);
> > -	if (IS_ERR(dsi->panel))
> > -		return PTR_ERR(dsi->panel);
> >  
> > -	dev_info(host->dev, "Attached device %s\n", device->name);
> > +	dsi->bridge = of_drm_find_bridge(device->dev.of_node);
> > +	if (!dsi->bridge) {
> 
> You are using IS_ERR to check that the bridge is alive in the changes
> above, but switch to checking that it's non-NULL at this point.
> 
> Are both guaranteed to be interchangeable?

They aren't. Any ERR_PTR will be !NULL

> > +		dsi->panel = of_drm_find_panel(device->dev.of_node);
> > +		if (IS_ERR(dsi->panel))
> > +			return PTR_ERR(dsi->panel);
> > +	}
> 
> You should probably use drm_of_find_panel_or_bridge instead of
> duplicating the logic here.

Or we can even use the drm_panel_bridge_add to simplify things.

Maxime
Paul Kocialkowski March 15, 2019, 1:48 p.m. UTC | #3
Hi,

On Fri, 2019-03-15 at 14:45 +0100, Maxime Ripard wrote:
> On Fri, Mar 15, 2019 at 02:32:55PM +0100, Paul Kocialkowski wrote:
> > Hi,
> > 
> > On Fri, 2019-03-15 at 18:38 +0530, Jagan Teki wrote:
> > > Some display panels would come up with a non-DSI output which
> > > can have an option to connect DSI interface by means of bridge
> > > convertor.
> > > 
> > > This DSI to non-DSI bridge convertor would require a bridge
> > > driver that would communicate the DSI controller for bridge
> > > functionalities.
> > > 
> > > So, add support for bridge functionalities in Allwinner DSI
> > > controller.
> > 
> > See a few comments below.
> > 
> > > Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
> > > ---
> > >  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 65 +++++++++++++++++++-------
> > >  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h |  1 +
> > >  2 files changed, 49 insertions(+), 17 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> > > index 0960b96b62cc..64d74313b842 100644
> > > --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> > > +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> > > @@ -781,6 +781,9 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
> > >  	if (!IS_ERR(dsi->panel))
> > >  		drm_panel_prepare(dsi->panel);
> > >  
> > > +	if (!IS_ERR(dsi->bridge))
> > > +		drm_bridge_pre_enable(dsi->bridge);
> > > +
> > >  	/*
> > >  	 * FIXME: This should be moved after the switch to HS mode.
> > >  	 *
> > > @@ -796,6 +799,9 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
> > >  	if (!IS_ERR(dsi->panel))
> > >  		drm_panel_enable(dsi->panel);
> > >  
> > > +	if (!IS_ERR(dsi->bridge))
> > > +		drm_bridge_enable(dsi->bridge);
> > > +
> > >  	sun6i_dsi_start(dsi, DSI_START_HSC);
> > >  
> > >  	udelay(1000);
> > > @@ -812,6 +818,9 @@ static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder)
> > >  	if (!IS_ERR(dsi->panel)) {
> > >  		drm_panel_disable(dsi->panel);
> > >  		drm_panel_unprepare(dsi->panel);
> > > +	} else if (!IS_ERR(dsi->bridge)) {
> > > +		drm_bridge_disable(dsi->bridge);
> > > +		drm_bridge_post_disable(dsi->bridge);
> > >  	}
> > >  
> > >  	phy_power_off(dsi->dphy);
> > > @@ -973,11 +982,16 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host,
> > >  	struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);
> > >  
> > >  	dsi->device = device;
> > > -	dsi->panel = of_drm_find_panel(device->dev.of_node);
> > > -	if (IS_ERR(dsi->panel))
> > > -		return PTR_ERR(dsi->panel);
> > >  
> > > -	dev_info(host->dev, "Attached device %s\n", device->name);
> > > +	dsi->bridge = of_drm_find_bridge(device->dev.of_node);
> > > +	if (!dsi->bridge) {
> > 
> > You are using IS_ERR to check that the bridge is alive in the changes
> > above, but switch to checking that it's non-NULL at this point.
> > 
> > Are both guaranteed to be interchangeable?
> 
> They aren't. Any ERR_PTR will be !NULL
> 
> > > +		dsi->panel = of_drm_find_panel(device->dev.of_node);
> > > +		if (IS_ERR(dsi->panel))
> > > +			return PTR_ERR(dsi->panel);
> > > +	}
> > 
> > You should probably use drm_of_find_panel_or_bridge instead of
> > duplicating the logic here.
> 
> Or we can even use the drm_panel_bridge_add to simplify things.

Indeed, I was just looking at that and it seems to be specifically
tailored for this use case.

Cheers,

Paul
Jagan Teki May 22, 2019, 12:01 p.m. UTC | #4
Hi Paul and Maxime,

On Fri, Mar 15, 2019 at 7:03 PM Paul Kocialkowski
<paul.kocialkowski@bootlin.com> wrote:
>
> Hi,
>
> On Fri, 2019-03-15 at 18:38 +0530, Jagan Teki wrote:
> > Some display panels would come up with a non-DSI output which
> > can have an option to connect DSI interface by means of bridge
> > convertor.
> >
> > This DSI to non-DSI bridge convertor would require a bridge
> > driver that would communicate the DSI controller for bridge
> > functionalities.
> >
> > So, add support for bridge functionalities in Allwinner DSI
> > controller.
>
> See a few comments below.
>
> > Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
> > ---
> >  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 65 +++++++++++++++++++-------
> >  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h |  1 +
> >  2 files changed, 49 insertions(+), 17 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> > index 0960b96b62cc..64d74313b842 100644
> > --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> > +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> > @@ -781,6 +781,9 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
> >       if (!IS_ERR(dsi->panel))
> >               drm_panel_prepare(dsi->panel);
> >
> > +     if (!IS_ERR(dsi->bridge))
> > +             drm_bridge_pre_enable(dsi->bridge);
> > +
> >       /*
> >        * FIXME: This should be moved after the switch to HS mode.
> >        *
> > @@ -796,6 +799,9 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
> >       if (!IS_ERR(dsi->panel))
> >               drm_panel_enable(dsi->panel);
> >
> > +     if (!IS_ERR(dsi->bridge))
> > +             drm_bridge_enable(dsi->bridge);
> > +
> >       sun6i_dsi_start(dsi, DSI_START_HSC);
> >
> >       udelay(1000);
> > @@ -812,6 +818,9 @@ static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder)
> >       if (!IS_ERR(dsi->panel)) {
> >               drm_panel_disable(dsi->panel);
> >               drm_panel_unprepare(dsi->panel);
> > +     } else if (!IS_ERR(dsi->bridge)) {
> > +             drm_bridge_disable(dsi->bridge);
> > +             drm_bridge_post_disable(dsi->bridge);
> >       }
> >
> >       phy_power_off(dsi->dphy);
> > @@ -973,11 +982,16 @@ static int sun6i_dsi_attach(struct mipi_dsi_host *host,
> >       struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);
> >
> >       dsi->device = device;
> > -     dsi->panel = of_drm_find_panel(device->dev.of_node);
> > -     if (IS_ERR(dsi->panel))
> > -             return PTR_ERR(dsi->panel);
> >
> > -     dev_info(host->dev, "Attached device %s\n", device->name);
> > +     dsi->bridge = of_drm_find_bridge(device->dev.of_node);
> > +     if (!dsi->bridge) {
>
> You are using IS_ERR to check that the bridge is alive in the changes
> above, but switch to checking that it's non-NULL at this point.
>
> Are both guaranteed to be interchangeable?
>
> > +             dsi->panel = of_drm_find_panel(device->dev.of_node);
> > +             if (IS_ERR(dsi->panel))
> > +                     return PTR_ERR(dsi->panel);
> > +     }
>
> You should probably use drm_of_find_panel_or_bridge instead of
> duplicating the logic here.

True, In-fact I did try this API. but pipeline were unable to bound.
Usually the panel and bridge were attached first and then the pipeline
bound would start from front-end (in A33) But in my below cases I have
seen only panel or bridge attached but no pipeline bound at all.

And I'm using drm_of_find_panel_or_bridge(host->dev->of_node, 1, 0,
&dsi->panel, &dsi->bridge); in dsi attach API.

Case-1, panel:

&dsi {
    vcc-dsi-supply = <&reg_dcdc1>;        /* VCC3V3-DSI */
    status = "okay";

    ports {
        dsi_out: port@1 {
            reg = <1>;

            dsi_out_panel: endpoint {
                remote-endpoint = <&panel_out_dsi>;
            };
        };
    };

    panel@0 {
        compatible = "bananapi,s070wv20-ct16-icn6211";
        reg = <0>;
        enable-gpios = <&pio 1 7 GPIO_ACTIVE_HIGH>; /* LCD-PWR-EN: PB7 */
        reset-gpios = <&r_pio 0 5 GPIO_ACTIVE_HIGH>; /* LCD-RST: PL5 */
        backlight = <&backlight>;

        port {
            panel_out_dsi: endpoint {
                remote-endpoint = <&dsi_out_panel>;
            };
        };
    };
};

Case-2, bridge:

    panel {
        compatible = "bananapi,s070wv20-ct16", "simple-panel";
        enable-gpios = <&pio 1 7 GPIO_ACTIVE_HIGH>; /* LCD-PWR-EN: PB7 */
        backlight = <&backlight>;

        port {

            panel_out_bridge: endpoint {
                remote-endpoint = <&bridge_out_panel>;
            };
        };
    };

&dsi {
    vcc-dsi-supply = <&reg_dcdc1>;        /* VCC-DSI */
    status = "okay";

    ports {
        dsi_out: port@1 {
            reg = <1>;

            dsi_out_bridge: endpoint {
                remote-endpoint = <&bridge_out_dsi>;
            };
        };
    };

    bridge@0 {
        reg = <0>;
        compatible = "bananapi,icn6211", "chipone,icn6211";
        reset-gpios = <&r_pio 0 5 GPIO_ACTIVE_HIGH>; /* LCD-RST: PL5 */
        #address-cells = <1>;
        #size-cells = <0>;

        ports {
            #address-cells = <1>;
            #size-cells = <0>;

            bridge_in: port@0 {
                reg = <0>;

                bridge_out_dsi: endpoint {
                    remote-endpoint = <&dsi_out_bridge>;
                };
            };

            bridge_out: port@1 {
                reg = <1>;

                bridge_out_panel: endpoint {
                    remote-endpoint = <&panel_out_bridge>;
                };
            };
        };
    };
};

I think, I'm sure about the pipeline connections as per my
understanding. but something loosely missed here or in the code.
Please do let me know for any suggestions.

Jagan.
diff mbox series

Patch

diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index 0960b96b62cc..64d74313b842 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -781,6 +781,9 @@  static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
 	if (!IS_ERR(dsi->panel))
 		drm_panel_prepare(dsi->panel);
 
+	if (!IS_ERR(dsi->bridge))
+		drm_bridge_pre_enable(dsi->bridge);
+
 	/*
 	 * FIXME: This should be moved after the switch to HS mode.
 	 *
@@ -796,6 +799,9 @@  static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
 	if (!IS_ERR(dsi->panel))
 		drm_panel_enable(dsi->panel);
 
+	if (!IS_ERR(dsi->bridge))
+		drm_bridge_enable(dsi->bridge);
+
 	sun6i_dsi_start(dsi, DSI_START_HSC);
 
 	udelay(1000);
@@ -812,6 +818,9 @@  static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder)
 	if (!IS_ERR(dsi->panel)) {
 		drm_panel_disable(dsi->panel);
 		drm_panel_unprepare(dsi->panel);
+	} else if (!IS_ERR(dsi->bridge)) {
+		drm_bridge_disable(dsi->bridge);
+		drm_bridge_post_disable(dsi->bridge);
 	}
 
 	phy_power_off(dsi->dphy);
@@ -973,11 +982,16 @@  static int sun6i_dsi_attach(struct mipi_dsi_host *host,
 	struct sun6i_dsi *dsi = host_to_sun6i_dsi(host);
 
 	dsi->device = device;
-	dsi->panel = of_drm_find_panel(device->dev.of_node);
-	if (IS_ERR(dsi->panel))
-		return PTR_ERR(dsi->panel);
 
-	dev_info(host->dev, "Attached device %s\n", device->name);
+	dsi->bridge = of_drm_find_bridge(device->dev.of_node);
+	if (!dsi->bridge) {
+		dsi->panel = of_drm_find_panel(device->dev.of_node);
+		if (IS_ERR(dsi->panel))
+			return PTR_ERR(dsi->panel);
+	}
+
+	dev_info(host->dev, "Attached %s %s\n",
+		 dsi->bridge ? "bridge" : "panel", device->name);
 
 	return 0;
 }
@@ -1055,8 +1069,10 @@  static int sun6i_dsi_bind(struct device *dev, struct device *master,
 	struct sun4i_tcon *tcon0 = sun4i_get_tcon0(drm);
 	int ret;
 
-	if (!dsi->panel)
+	if (!(dsi->panel || dsi->bridge)) {
+		dev_info(drm->dev, "No panel or bridge found... DSI output disabled\n");
 		return -EPROBE_DEFER;
+	}
 
 	dsi->drv = drv;
 
@@ -1078,19 +1094,29 @@  static int sun6i_dsi_bind(struct device *dev, struct device *master,
 	}
 	dsi->encoder.possible_crtcs = BIT(0);
 
-	drm_connector_helper_add(&dsi->connector,
-				 &sun6i_dsi_connector_helper_funcs);
-	ret = drm_connector_init(drm, &dsi->connector,
-				 &sun6i_dsi_connector_funcs,
-				 DRM_MODE_CONNECTOR_DSI);
-	if (ret) {
-		dev_err(dsi->dev,
-			"Couldn't initialise the DSI connector\n");
-		goto err_cleanup_connector;
+	if (dsi->panel) {
+		drm_connector_helper_add(&dsi->connector,
+					 &sun6i_dsi_connector_helper_funcs);
+		ret = drm_connector_init(drm, &dsi->connector,
+					 &sun6i_dsi_connector_funcs,
+					 DRM_MODE_CONNECTOR_DSI);
+		if (ret) {
+			dev_err(dsi->dev,
+				"Couldn't initialise the DSI connector\n");
+			goto err_cleanup_connector;
+		}
+
+		drm_connector_attach_encoder(&dsi->connector, &dsi->encoder);
+		drm_panel_attach(dsi->panel, &dsi->connector);
 	}
 
-	drm_connector_attach_encoder(&dsi->connector, &dsi->encoder);
-	drm_panel_attach(dsi->panel, &dsi->connector);
+	if (dsi->bridge) {
+		ret = drm_bridge_attach(&dsi->encoder, dsi->bridge, NULL);
+		if (ret) {
+			dev_err(dsi->dev, "Couldn't attach the DSI bridge\n");
+			goto err_cleanup_connector;
+		}
+	}
 
 	return 0;
 
@@ -1104,7 +1130,12 @@  static void sun6i_dsi_unbind(struct device *dev, struct device *master,
 {
 	struct sun6i_dsi *dsi = dev_get_drvdata(dev);
 
-	drm_panel_detach(dsi->panel);
+	if (dsi->panel)
+		drm_panel_detach(dsi->panel);
+
+	if (dsi->bridge)
+		drm_bridge_detach(dsi->bridge);
+
 }
 
 static const struct component_ops sun6i_dsi_ops = {
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
index 5c4983212f89..76874ff8e3ef 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
@@ -36,6 +36,7 @@  struct sun6i_dsi {
 	struct sun4i_tcon	*tcon;
 	struct mipi_dsi_device	*device;
 	struct drm_panel	*panel;
+	struct drm_bridge	*bridge;
 	const struct sun6i_dsi_variant	*variant;
 };