diff mbox series

[v5,5/9] drm/msm/dp: Add eDP support via aux_bus

Message ID 1647452154-16361-6-git-send-email-quic_sbillaka@quicinc.com
State Superseded
Headers show
Series [v5,1/9] arm64: dts: qcom: sc7280: rename edp_out label to mdss_edp_out | expand

Commit Message

Sankeerth Billakanti (QUIC) March 16, 2022, 5:35 p.m. UTC
This patch adds support for generic eDP sink through aux_bus.
The eDP/DP controller driver should support aux transactions originating
from the panel-edp driver and hence should be initialized and ready.

	The panel bridge supporting the panel should be ready before
the bridge connector is initialized. The generic panel probe needs the
controller resources to be enabled to support aux tractions originating
from it. So, the host_init and phy_init are moved to execute before the
panel probe.

	The host_init has to return early if the core is already
initialized so that the regulator and clock votes for the controller
resources are balanced.

	EV_HPD_INIT_SETUP needs to execute immediately to enable the
interrupts for the aux transactions from panel-edp to get the modes
supported.

Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
---
 drivers/gpu/drm/msm/dp/dp_display.c | 65 +++++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/msm/dp/dp_drm.c     | 10 +++---
 drivers/gpu/drm/msm/dp/dp_parser.c  | 21 +-----------
 drivers/gpu/drm/msm/dp/dp_parser.h  |  1 +
 4 files changed, 70 insertions(+), 27 deletions(-)

Comments

Stephen Boyd March 17, 2022, 9:37 p.m. UTC | #1
Quoting Sankeerth Billakanti (2022-03-16 10:35:50)
>         This patch adds support for generic eDP sink through aux_bus.

Please unindent commit text paragraphs. This isn't a book.

> The eDP/DP controller driver should support aux transactions originating
> from the panel-edp driver and hence should be initialized and ready.
>
>         The panel bridge supporting the panel should be ready before
> the bridge connector is initialized. The generic panel probe needs the
> controller resources to be enabled to support aux tractions originating

s/tractions/transactions/

> from it. So, the host_init and phy_init are moved to execute before the
> panel probe.
>
>         The host_init has to return early if the core is already
> initialized so that the regulator and clock votes for the controller
> resources are balanced.
>
>         EV_HPD_INIT_SETUP needs to execute immediately to enable the
> interrupts for the aux transactions from panel-edp to get the modes
> supported.

There are a lot of things going on in this patch. Can it be split up?

>
> Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
> ---
>  drivers/gpu/drm/msm/dp/dp_display.c | 65 +++++++++++++++++++++++++++++++++++--
>  drivers/gpu/drm/msm/dp/dp_drm.c     | 10 +++---
>  drivers/gpu/drm/msm/dp/dp_parser.c  | 21 +-----------
>  drivers/gpu/drm/msm/dp/dp_parser.h  |  1 +
>  4 files changed, 70 insertions(+), 27 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 382b3aa..688bbed 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -10,6 +10,7 @@
>  #include <linux/component.h>
>  #include <linux/of_irq.h>
>  #include <linux/delay.h>
> +#include <drm/drm_dp_aux_bus.h>
>
>  #include "msm_drv.h"
>  #include "msm_kms.h"
> @@ -265,8 +266,6 @@ static int dp_display_bind(struct device *dev, struct device *master,
>                 goto end;
>         }
>
> -       dp->dp_display.next_bridge = dp->parser->next_bridge;
> -
>         dp->aux->drm_dev = drm;
>         rc = dp_aux_register(dp->aux);
>         if (rc) {
> @@ -421,6 +420,11 @@ static void dp_display_host_init(struct dp_display_private *dp)
>                 dp->dp_display.connector_type, dp->core_initialized,
>                 dp->phy_initialized);
>
> +       if (dp->core_initialized) {
> +               DRM_DEBUG_DP("DP core already initialized\n");
> +               return;
> +       }
> +
>         dp_power_init(dp->power, false);
>         dp_ctrl_reset_irq_ctrl(dp->ctrl, true);
>         dp_aux_init(dp->aux);
> @@ -433,6 +437,11 @@ static void dp_display_host_deinit(struct dp_display_private *dp)
>                 dp->dp_display.connector_type, dp->core_initialized,
>                 dp->phy_initialized);
>
> +       if (!dp->core_initialized) {
> +               DRM_DEBUG_DP("DP core not initialized\n");
> +               return;
> +       }
> +
>         dp_ctrl_reset_irq_ctrl(dp->ctrl, false);
>         dp_aux_deinit(dp->aux);
>         dp_power_deinit(dp->power);
> @@ -1502,7 +1511,7 @@ void msm_dp_irq_postinstall(struct msm_dp *dp_display)
>
>         dp_hpd_event_setup(dp);
>
> -       dp_add_event(dp, EV_HPD_INIT_SETUP, 0, 100);
> +       dp_add_event(dp, EV_HPD_INIT_SETUP, 0, 0);
>  }
>
>  void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor)
> @@ -1524,6 +1533,52 @@ void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor)
>         }
>  }
>
> +static int dp_display_get_next_bridge(struct msm_dp *dp)
> +{
> +       int rc = 0;

Drop initialization.

> +       struct dp_display_private *dp_priv;
> +       struct device_node *aux_bus;
> +       struct device *dev;
> +
> +       dp_priv = container_of(dp, struct dp_display_private, dp_display);
> +       dev = &dp_priv->pdev->dev;
> +       aux_bus = of_get_child_by_name(dev->of_node, "aux-bus");
> +
> +       if (aux_bus) {
> +               dp_display_host_init(dp_priv);
> +               dp_catalog_ctrl_hpd_config(dp_priv->catalog);
> +               enable_irq(dp_priv->irq);
> +               dp_display_host_phy_init(dp_priv);
> +
> +               devm_of_dp_aux_populate_ep_devices(dp_priv->aux);
> +
> +               disable_irq(dp_priv->irq);

Why do we disable irq?

> +       }

The aux_bus node leaked.

> +
> +       /*
> +        * External bridges are mandatory for eDP interfaces: one has to
> +        * provide at least an eDP panel (which gets wrapped into panel-bridge).
> +        *
> +        * For DisplayPort interfaces external bridges are optional, so
> +        * silently ignore an error if one is not present (-ENODEV).
> +        */
> +       rc = dp_parser_find_next_bridge(dp_priv->parser);
> +       if (rc == -ENODEV) {
> +               if (dp->connector_type == DRM_MODE_CONNECTOR_eDP) {
> +                       DRM_ERROR("eDP: next bridge is not present\n");
> +                       return rc;
> +               }
> +       } else if (rc) {
> +               if (rc != -EPROBE_DEFER)
> +                       DRM_ERROR("DP: error parsing next bridge: %d\n", rc);
> +               return rc;
> +       }
> +
> +       dp->next_bridge = dp_priv->parser->next_bridge;
> +
> +       return 0;
> +}
> +
>  int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
>                         struct drm_encoder *encoder)
>  {
> @@ -1547,6 +1602,10 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
>
>         dp_display->encoder = encoder;
>
> +       ret = dp_display_get_next_bridge(dp_display);

Didn't we just move bridge attachment out of modeset? Why is it being
done here?

> +       if (ret)
> +               return ret;
> +
>         dp_display->bridge = dp_bridge_init(dp_display, dev, encoder);
>         if (IS_ERR(dp_display->bridge)) {
>                 ret = PTR_ERR(dp_display->bridge);
> diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
> index 7ce1aca..5254bd6 100644
> --- a/drivers/gpu/drm/msm/dp/dp_drm.c
> +++ b/drivers/gpu/drm/msm/dp/dp_drm.c
> @@ -114,10 +114,12 @@ struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *
>         bridge->funcs = &dp_bridge_ops;
>         bridge->type = dp_display->connector_type;
>
> -       bridge->ops =
> -               DRM_BRIDGE_OP_DETECT |
> -               DRM_BRIDGE_OP_HPD |
> -               DRM_BRIDGE_OP_MODES;
> +       if (bridge->type == DRM_MODE_CONNECTOR_DisplayPort) {

Why can't eDP have bridge ops that are the same?

> +               bridge->ops =
> +                       DRM_BRIDGE_OP_DETECT |
> +                       DRM_BRIDGE_OP_HPD |
> +                       DRM_BRIDGE_OP_MODES;
> +       }
>
>         rc = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
>         if (rc) {
Sankeerth Billakanti (QUIC) March 25, 2022, 2:11 p.m. UTC | #2
> -----Original Message-----
> From: Stephen Boyd <swboyd@chromium.org>
> Sent: Friday, March 18, 2022 3:08 AM
> To: Sankeerth Billakanti (QUIC) <quic_sbillaka@quicinc.com>;
> devicetree@vger.kernel.org; dri-devel@lists.freedesktop.org;
> freedreno@lists.freedesktop.org; linux-arm-msm@vger.kernel.org; linux-
> kernel@vger.kernel.org
> Cc: robdclark@gmail.com; seanpaul@chromium.org; quic_kalyant
> <quic_kalyant@quicinc.com>; Abhinav Kumar (QUIC)
> <quic_abhinavk@quicinc.com>; dianders@chromium.org; Kuogee Hsieh
> (QUIC) <quic_khsieh@quicinc.com>; agross@kernel.org;
> bjorn.andersson@linaro.org; robh+dt@kernel.org; krzk+dt@kernel.org;
> sean@poorly.run; airlied@linux.ie; daniel@ffwll.ch;
> thierry.reding@gmail.com; sam@ravnborg.org;
> dmitry.baryshkov@linaro.org; quic_vproddut <quic_vproddut@quicinc.com>
> Subject: Re: [PATCH v5 5/9] drm/msm/dp: Add eDP support via aux_bus
> 
> Quoting Sankeerth Billakanti (2022-03-16 10:35:50)
> >         This patch adds support for generic eDP sink through aux_bus.
> 
> Please unindent commit text paragraphs. This isn't a book.
> 

Okay. Will change it.

> > The eDP/DP controller driver should support aux transactions
> > originating from the panel-edp driver and hence should be initialized and
> ready.
> >
> >         The panel bridge supporting the panel should be ready before
> > the bridge connector is initialized. The generic panel probe needs the
> > controller resources to be enabled to support aux tractions
> > originating
> 
> s/tractions/transactions/
>

Will correct it
 
> > from it. So, the host_init and phy_init are moved to execute before
> > the panel probe.
> >
> >         The host_init has to return early if the core is already
> > initialized so that the regulator and clock votes for the controller
> > resources are balanced.
> >
> >         EV_HPD_INIT_SETUP needs to execute immediately to enable the
> > interrupts for the aux transactions from panel-edp to get the modes
> > supported.
> 
> There are a lot of things going on in this patch. Can it be split up?
>

I can split them up.

> >
> > Signed-off-by: Sankeerth Billakanti <quic_sbillaka@quicinc.com>
> > ---
> >  drivers/gpu/drm/msm/dp/dp_display.c | 65
> +++++++++++++++++++++++++++++++++++--
> >  drivers/gpu/drm/msm/dp/dp_drm.c     | 10 +++---
> >  drivers/gpu/drm/msm/dp/dp_parser.c  | 21 +-----------
> > drivers/gpu/drm/msm/dp/dp_parser.h  |  1 +
> >  4 files changed, 70 insertions(+), 27 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/msm/dp/dp_display.c
> > b/drivers/gpu/drm/msm/dp/dp_display.c
> > index 382b3aa..688bbed 100644
> > --- a/drivers/gpu/drm/msm/dp/dp_display.c
> > +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> > @@ -10,6 +10,7 @@
> >  #include <linux/component.h>
> >  #include <linux/of_irq.h>
> >  #include <linux/delay.h>
> > +#include <drm/drm_dp_aux_bus.h>
> >
> >  #include "msm_drv.h"
> >  #include "msm_kms.h"
> > @@ -265,8 +266,6 @@ static int dp_display_bind(struct device *dev, struct
> device *master,
> >                 goto end;
> >         }
> >
> > -       dp->dp_display.next_bridge = dp->parser->next_bridge;
> > -
> >         dp->aux->drm_dev = drm;
> >         rc = dp_aux_register(dp->aux);
> >         if (rc) {
> > @@ -421,6 +420,11 @@ static void dp_display_host_init(struct
> dp_display_private *dp)
> >                 dp->dp_display.connector_type, dp->core_initialized,
> >                 dp->phy_initialized);
> >
> > +       if (dp->core_initialized) {
> > +               DRM_DEBUG_DP("DP core already initialized\n");
> > +               return;
> > +       }
> > +
> >         dp_power_init(dp->power, false);
> >         dp_ctrl_reset_irq_ctrl(dp->ctrl, true);
> >         dp_aux_init(dp->aux);
> > @@ -433,6 +437,11 @@ static void dp_display_host_deinit(struct
> dp_display_private *dp)
> >                 dp->dp_display.connector_type, dp->core_initialized,
> >                 dp->phy_initialized);
> >
> > +       if (!dp->core_initialized) {
> > +               DRM_DEBUG_DP("DP core not initialized\n");
> > +               return;
> > +       }
> > +
> >         dp_ctrl_reset_irq_ctrl(dp->ctrl, false);
> >         dp_aux_deinit(dp->aux);
> >         dp_power_deinit(dp->power);
> > @@ -1502,7 +1511,7 @@ void msm_dp_irq_postinstall(struct msm_dp
> > *dp_display)
> >
> >         dp_hpd_event_setup(dp);
> >
> > -       dp_add_event(dp, EV_HPD_INIT_SETUP, 0, 100);
> > +       dp_add_event(dp, EV_HPD_INIT_SETUP, 0, 0);
> >  }
> >
> >  void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor
> > *minor) @@ -1524,6 +1533,52 @@ void msm_dp_debugfs_init(struct
> msm_dp *dp_display, struct drm_minor *minor)
> >         }
> >  }
> >
> > +static int dp_display_get_next_bridge(struct msm_dp *dp) {
> > +       int rc = 0;
> 
> Drop initialization.
> 

Okay.

> > +       struct dp_display_private *dp_priv;
> > +       struct device_node *aux_bus;
> > +       struct device *dev;
> > +
> > +       dp_priv = container_of(dp, struct dp_display_private, dp_display);
> > +       dev = &dp_priv->pdev->dev;
> > +       aux_bus = of_get_child_by_name(dev->of_node, "aux-bus");
> > +
> > +       if (aux_bus) {
> > +               dp_display_host_init(dp_priv);
> > +               dp_catalog_ctrl_hpd_config(dp_priv->catalog);
> > +               enable_irq(dp_priv->irq);
> > +               dp_display_host_phy_init(dp_priv);
> > +
> > +               devm_of_dp_aux_populate_ep_devices(dp_priv->aux);
> > +
> > +               disable_irq(dp_priv->irq);
> 
> Why do we disable irq?
> 

To support panel without aux_bus.

If aux_bus is not present and eDP panel is enumerated as a single mode simple sharp panel (panel-edp.c),
the clocks and aux resources required for the panel will be enabled in dp_display_config_hpd and irq will also
be enabled from there like external DP display. So, the dp_display_config_hpd is to be executed for both eDP and DP.

We disabled it here to balance it with the enable_irq in dp_display_config_hpd, which executes for both edp and dp.
 
> > +       }
> 
> The aux_bus node leaked.
>

Will add a of_node_put.
 
> > +
> > +       /*
> > +        * External bridges are mandatory for eDP interfaces: one has to
> > +        * provide at least an eDP panel (which gets wrapped into panel-
> bridge).
> > +        *
> > +        * For DisplayPort interfaces external bridges are optional, so
> > +        * silently ignore an error if one is not present (-ENODEV).
> > +        */
> > +       rc = dp_parser_find_next_bridge(dp_priv->parser);
> > +       if (rc == -ENODEV) {
> > +               if (dp->connector_type == DRM_MODE_CONNECTOR_eDP) {
> > +                       DRM_ERROR("eDP: next bridge is not present\n");
> > +                       return rc;
> > +               }
> > +       } else if (rc) {
> > +               if (rc != -EPROBE_DEFER)
> > +                       DRM_ERROR("DP: error parsing next bridge: %d\n", rc);
> > +               return rc;
> > +       }
> > +
> > +       dp->next_bridge = dp_priv->parser->next_bridge;
> > +
> > +       return 0;
> > +}
> > +
> >  int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device
> *dev,
> >                         struct drm_encoder *encoder)  { @@ -1547,6
> > +1602,10 @@ int msm_dp_modeset_init(struct msm_dp *dp_display,
> struct
> > drm_device *dev,
> >
> >         dp_display->encoder = encoder;
> >
> > +       ret = dp_display_get_next_bridge(dp_display);
> 
> Didn't we just move bridge attachment out of modeset? Why is it being done
> here?
> 

After Dmitry's patches, there is a need to get all the required bridges before the bridge_connector_init.
The bridge_connector_init will instantiate the ops for the farthest bridge. If we do not get the next_bridge here,
then the get_modes for eDP will be using the dp_bridge function instead of the panel bridge function.

> > +       if (ret)
> > +               return ret;
> > +
> >         dp_display->bridge = dp_bridge_init(dp_display, dev, encoder);
> >         if (IS_ERR(dp_display->bridge)) {
> >                 ret = PTR_ERR(dp_display->bridge); diff --git
> > a/drivers/gpu/drm/msm/dp/dp_drm.c
> b/drivers/gpu/drm/msm/dp/dp_drm.c
> > index 7ce1aca..5254bd6 100644
> > --- a/drivers/gpu/drm/msm/dp/dp_drm.c
> > +++ b/drivers/gpu/drm/msm/dp/dp_drm.c
> > @@ -114,10 +114,12 @@ struct drm_bridge *dp_bridge_init(struct
> msm_dp *dp_display, struct drm_device *
> >         bridge->funcs = &dp_bridge_ops;
> >         bridge->type = dp_display->connector_type;
> >
> > -       bridge->ops =
> > -               DRM_BRIDGE_OP_DETECT |
> > -               DRM_BRIDGE_OP_HPD |
> > -               DRM_BRIDGE_OP_MODES;
> > +       if (bridge->type == DRM_MODE_CONNECTOR_DisplayPort) {
> 
> Why can't eDP have bridge ops that are the same?
> 

eDP needs to be reported as always connected. Whichever bridge is setting these ops flags should provide the ops.
The farthest bridge from the encoder with the ops flag set should implement the ops.
drm_bridge_connector_detect  reports always connected for eDP. So, we don't need DRM_BRIDGE_OP_DETECT 
eDP panel bridge will add DRM_BRIDGE_OP_MODES in drm_panel_bridge_add_typed and will call panel_edp_get_modes.
As we are not supporting HPD for EDP, we are not setting the HPD ops flag.

> > +               bridge->ops =
> > +                       DRM_BRIDGE_OP_DETECT |
> > +                       DRM_BRIDGE_OP_HPD |
> > +                       DRM_BRIDGE_OP_MODES;
> > +       }
> >
> >         rc = drm_bridge_attach(encoder, bridge, NULL,
> DRM_BRIDGE_ATTACH_NO_CONNECTOR);
> >         if (rc) {
Doug Anderson March 25, 2022, 4:45 p.m. UTC | #3
Hi,

On Fri, Mar 25, 2022 at 7:11 AM Sankeerth Billakanti (QUIC)
<quic_sbillaka@quicinc.com> wrote:
>
> > > @@ -114,10 +114,12 @@ struct drm_bridge *dp_bridge_init(struct
> > msm_dp *dp_display, struct drm_device *
> > >         bridge->funcs = &dp_bridge_ops;
> > >         bridge->type = dp_display->connector_type;
> > >
> > > -       bridge->ops =
> > > -               DRM_BRIDGE_OP_DETECT |
> > > -               DRM_BRIDGE_OP_HPD |
> > > -               DRM_BRIDGE_OP_MODES;
> > > +       if (bridge->type == DRM_MODE_CONNECTOR_DisplayPort) {
> >
> > Why can't eDP have bridge ops that are the same?
> >
>
> eDP needs to be reported as always connected. Whichever bridge is setting these ops flags should provide the ops.
> The farthest bridge from the encoder with the ops flag set should implement the ops.
> drm_bridge_connector_detect  reports always connected for eDP. So, we don't need DRM_BRIDGE_OP_DETECT
> eDP panel bridge will add DRM_BRIDGE_OP_MODES in drm_panel_bridge_add_typed and will call panel_edp_get_modes.
> As we are not supporting HPD for EDP, we are not setting the HPD ops flag.

Right. It's Expected that eDP and DP would have different ops. If we
define "detect" and "HPD" as whether the display is _physically_
connected, not the status of the poorly-named eDP "HPD" pin, then eDP
is _supposed_ to be considered always connected and thus would never
support DETECT / HPD.

...and right that the panel is expected to handle the modes.

This matches how things have been progressing in Laurent's patches
(taken over by Kieran) to add full DP support to sn65dsi86. For
instance:

https://lore.kernel.org/r/20220317131250.1481275-3-kieran.bingham+renesas@ideasonboard.com/
https://lore.kernel.org/r/20220317131250.1481275-4-kieran.bingham+renesas@ideasonboard.com/

-Doug
diff mbox series

Patch

diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 382b3aa..688bbed 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -10,6 +10,7 @@ 
 #include <linux/component.h>
 #include <linux/of_irq.h>
 #include <linux/delay.h>
+#include <drm/drm_dp_aux_bus.h>
 
 #include "msm_drv.h"
 #include "msm_kms.h"
@@ -265,8 +266,6 @@  static int dp_display_bind(struct device *dev, struct device *master,
 		goto end;
 	}
 
-	dp->dp_display.next_bridge = dp->parser->next_bridge;
-
 	dp->aux->drm_dev = drm;
 	rc = dp_aux_register(dp->aux);
 	if (rc) {
@@ -421,6 +420,11 @@  static void dp_display_host_init(struct dp_display_private *dp)
 		dp->dp_display.connector_type, dp->core_initialized,
 		dp->phy_initialized);
 
+	if (dp->core_initialized) {
+		DRM_DEBUG_DP("DP core already initialized\n");
+		return;
+	}
+
 	dp_power_init(dp->power, false);
 	dp_ctrl_reset_irq_ctrl(dp->ctrl, true);
 	dp_aux_init(dp->aux);
@@ -433,6 +437,11 @@  static void dp_display_host_deinit(struct dp_display_private *dp)
 		dp->dp_display.connector_type, dp->core_initialized,
 		dp->phy_initialized);
 
+	if (!dp->core_initialized) {
+		DRM_DEBUG_DP("DP core not initialized\n");
+		return;
+	}
+
 	dp_ctrl_reset_irq_ctrl(dp->ctrl, false);
 	dp_aux_deinit(dp->aux);
 	dp_power_deinit(dp->power);
@@ -1502,7 +1511,7 @@  void msm_dp_irq_postinstall(struct msm_dp *dp_display)
 
 	dp_hpd_event_setup(dp);
 
-	dp_add_event(dp, EV_HPD_INIT_SETUP, 0, 100);
+	dp_add_event(dp, EV_HPD_INIT_SETUP, 0, 0);
 }
 
 void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor)
@@ -1524,6 +1533,52 @@  void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor)
 	}
 }
 
+static int dp_display_get_next_bridge(struct msm_dp *dp)
+{
+	int rc = 0;
+	struct dp_display_private *dp_priv;
+	struct device_node *aux_bus;
+	struct device *dev;
+
+	dp_priv = container_of(dp, struct dp_display_private, dp_display);
+	dev = &dp_priv->pdev->dev;
+	aux_bus = of_get_child_by_name(dev->of_node, "aux-bus");
+
+	if (aux_bus) {
+		dp_display_host_init(dp_priv);
+		dp_catalog_ctrl_hpd_config(dp_priv->catalog);
+		enable_irq(dp_priv->irq);
+		dp_display_host_phy_init(dp_priv);
+
+		devm_of_dp_aux_populate_ep_devices(dp_priv->aux);
+
+		disable_irq(dp_priv->irq);
+	}
+
+	/*
+	 * External bridges are mandatory for eDP interfaces: one has to
+	 * provide at least an eDP panel (which gets wrapped into panel-bridge).
+	 *
+	 * For DisplayPort interfaces external bridges are optional, so
+	 * silently ignore an error if one is not present (-ENODEV).
+	 */
+	rc = dp_parser_find_next_bridge(dp_priv->parser);
+	if (rc == -ENODEV) {
+		if (dp->connector_type == DRM_MODE_CONNECTOR_eDP) {
+			DRM_ERROR("eDP: next bridge is not present\n");
+			return rc;
+		}
+	} else if (rc) {
+		if (rc != -EPROBE_DEFER)
+			DRM_ERROR("DP: error parsing next bridge: %d\n", rc);
+		return rc;
+	}
+
+	dp->next_bridge = dp_priv->parser->next_bridge;
+
+	return 0;
+}
+
 int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
 			struct drm_encoder *encoder)
 {
@@ -1547,6 +1602,10 @@  int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
 
 	dp_display->encoder = encoder;
 
+	ret = dp_display_get_next_bridge(dp_display);
+	if (ret)
+		return ret;
+
 	dp_display->bridge = dp_bridge_init(dp_display, dev, encoder);
 	if (IS_ERR(dp_display->bridge)) {
 		ret = PTR_ERR(dp_display->bridge);
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 7ce1aca..5254bd6 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -114,10 +114,12 @@  struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *
 	bridge->funcs = &dp_bridge_ops;
 	bridge->type = dp_display->connector_type;
 
-	bridge->ops =
-		DRM_BRIDGE_OP_DETECT |
-		DRM_BRIDGE_OP_HPD |
-		DRM_BRIDGE_OP_MODES;
+	if (bridge->type == DRM_MODE_CONNECTOR_DisplayPort) {
+		bridge->ops =
+			DRM_BRIDGE_OP_DETECT |
+			DRM_BRIDGE_OP_HPD |
+			DRM_BRIDGE_OP_MODES;
+	}
 
 	rc = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
 	if (rc) {
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c
index 1056b8d..6317dce 100644
--- a/drivers/gpu/drm/msm/dp/dp_parser.c
+++ b/drivers/gpu/drm/msm/dp/dp_parser.c
@@ -265,7 +265,7 @@  static int dp_parser_clock(struct dp_parser *parser)
 	return 0;
 }
 
-static int dp_parser_find_next_bridge(struct dp_parser *parser)
+int dp_parser_find_next_bridge(struct dp_parser *parser)
 {
 	struct device *dev = &parser->pdev->dev;
 	struct drm_bridge *bridge;
@@ -300,25 +300,6 @@  static int dp_parser_parse(struct dp_parser *parser, int connector_type)
 	if (rc)
 		return rc;
 
-	/*
-	 * External bridges are mandatory for eDP interfaces: one has to
-	 * provide at least an eDP panel (which gets wrapped into panel-bridge).
-	 *
-	 * For DisplayPort interfaces external bridges are optional, so
-	 * silently ignore an error if one is not present (-ENODEV).
-	 */
-	rc = dp_parser_find_next_bridge(parser);
-	if (rc == -ENODEV) {
-		if (connector_type == DRM_MODE_CONNECTOR_eDP) {
-			DRM_ERROR("eDP: next bridge is not present\n");
-			return rc;
-		}
-	} else if (rc) {
-		if (rc != -EPROBE_DEFER)
-			DRM_ERROR("DP: error parsing next bridge: %d\n", rc);
-		return rc;
-	}
-
 	/* Map the corresponding regulator information according to
 	 * version. Currently, since we only have one supported platform,
 	 * mapping the regulator directly.
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h
index d371bae..091ff41 100644
--- a/drivers/gpu/drm/msm/dp/dp_parser.h
+++ b/drivers/gpu/drm/msm/dp/dp_parser.h
@@ -140,5 +140,6 @@  struct dp_parser {
  * can be parsed using this module.
  */
 struct dp_parser *dp_parser_get(struct platform_device *pdev);
+int dp_parser_find_next_bridge(struct dp_parser *parser);
 
 #endif