[PATCH 4/5] drm/msm/dsi: Allow dsi to connect to an external bridge

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



There are platforms where the DSI output can be connected to another
encoder bridge chip (DSI to HDMI, DSI to LVDS etc).

Add support for external bridge support to the dsi driver. We assume that
the external bridge chip would be of the type drm_bridge. The dsi driver's
internal drm_bridge (msm_dsi->bridge) is linked to the external bridge's
drm_bridge struct.

In the case we're connected to an external bridge, we don't need to create
and manage a connector within our driver, it's the bridge driver's
responsibility to create one.

Signed-off-by: Archit Taneja <architt@xxxxxxxxxxxxxx>
---
 drivers/gpu/drm/msm/dsi/dsi.c         | 62 +++++++++++++++++++++++++++++++----
 drivers/gpu/drm/msm/dsi/dsi.h         | 12 ++++++-
 drivers/gpu/drm/msm/dsi/dsi_host.c    | 15 ++++++++-
 drivers/gpu/drm/msm/dsi/dsi_manager.c |  2 +-
 4 files changed, 81 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c
index e991270..d91185d 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.c
+++ b/drivers/gpu/drm/msm/dsi/dsi.c
@@ -223,12 +223,59 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev,
 		msm_dsi->encoders[i] = encoders[i];
 	}
 
-	msm_dsi->connector = msm_dsi_manager_connector_init(msm_dsi->id);
-	if (IS_ERR(msm_dsi->connector)) {
-		ret = PTR_ERR(msm_dsi->connector);
-		dev_err(dev->dev, "failed to create dsi connector: %d\n", ret);
-		msm_dsi->connector = NULL;
-		goto fail;
+	/*
+	 * check if the dsi encoder output is connected to a panel or an
+	 * external bridge. We create a connector only if we're connected to a
+	 * drm_panel device. When we're connected to an external bridge, we
+	 * assume that the drm_bridge driver will create the connector itself.
+	 */
+	msm_dsi->external_bridge = msm_dsi_host_get_bridge(msm_dsi->host,
+					&msm_dsi->device_flags);
+
+	if (msm_dsi->external_bridge) {
+		struct drm_encoder *encoder = msm_dsi_get_encoder(msm_dsi);
+		struct drm_bridge *int_bridge, *ext_bridge;
+		struct drm_connector *connector;
+		struct list_head *connector_list;
+
+		int_bridge = msm_dsi->bridge;
+		ext_bridge = msm_dsi->external_bridge;
+
+		/* link the internal dsi bridge to the external bridge */
+		int_bridge->next = ext_bridge;
+		/* set the external bridge's encoder as dsi's encoder */
+		ext_bridge->encoder = encoder;
+
+		drm_bridge_attach(msm_dsi->dev, ext_bridge);
+
+		/*
+		 * we need the drm_connector created by the external bridge
+		 * driver (or someone else) to feed it to our driver's
+		 * priv->connector[] list
+		 */
+		connector_list = &dev->mode_config.connector_list;
+
+		list_for_each_entry(connector, connector_list, head) {
+			int i;
+
+			for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+				if (connector->encoder_ids[i] ==
+						encoder->base.id) {
+					msm_dsi->connector = connector;
+					break;
+				}
+			}
+		}
+	} else {
+		msm_dsi->connector =
+			msm_dsi_manager_connector_init(msm_dsi->id);
+		if (IS_ERR(msm_dsi->connector)) {
+			ret = PTR_ERR(msm_dsi->connector);
+			dev_err(dev->dev,
+				"failed to create dsi connector: %d\n", ret);
+			msm_dsi->connector = NULL;
+			goto fail;
+		}
 	}
 
 	priv->bridges[priv->num_bridges++]       = msm_dsi->bridge;
@@ -242,7 +289,8 @@ fail:
 			msm_dsi_manager_bridge_destroy(msm_dsi->bridge);
 			msm_dsi->bridge = NULL;
 		}
-		if (msm_dsi->connector) {
+		/* don't destroy connector if we didn't make it */
+		if (msm_dsi->connector && !msm_dsi->external_bridge) {
 			msm_dsi->connector->funcs->destroy(msm_dsi->connector);
 			msm_dsi->connector = NULL;
 		}
diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h
index d36a449..d179ed9 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.h
+++ b/drivers/gpu/drm/msm/dsi/dsi.h
@@ -53,12 +53,20 @@ struct msm_dsi {
 	struct drm_device *dev;
 	struct platform_device *pdev;
 
+	/* connector managed by us when we're connected to a drm_panel */
 	struct drm_connector *connector;
+	/* internal dsi bridge attached to MDP interface */
 	struct drm_bridge *bridge;
 
 	struct mipi_dsi_host *host;
 	struct msm_dsi_phy *phy;
+
+	/*
+	 * panel/external_bridge connected to dsi bridge output, only one of the
+	 * two can be valid at a time
+	 */
 	struct drm_panel *panel;
+	struct drm_bridge *external_bridge;
 	unsigned long device_flags;
 
 	struct device *phy_dev;
@@ -86,7 +94,7 @@ void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi);
 /* msm dsi */
 static inline bool msm_dsi_device_connected(struct msm_dsi *msm_dsi)
 {
-	return msm_dsi->panel;
+	return msm_dsi->panel || msm_dsi->external_bridge;
 }
 
 struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi);
@@ -133,6 +141,8 @@ int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host,
 					struct drm_display_mode *mode);
 struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host,
 					unsigned long *panel_flags);
+struct drm_bridge *msm_dsi_host_get_bridge(struct mipi_dsi_host *host,
+					unsigned long *bridge_flags);
 int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer);
 void msm_dsi_host_unregister(struct mipi_dsi_host *host);
 int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host,
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c
index e5a44e7..2031a31 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -1626,7 +1626,8 @@ int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer)
 		 */
 		if (check_defer) {
 			if (!of_drm_find_panel(msm_host->device_node))
-				return -EPROBE_DEFER;
+				if (!of_drm_find_bridge(msm_host->device_node))
+					return -EPROBE_DEFER;
 		}
 	}
 
@@ -2042,3 +2043,15 @@ struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host,
 	return panel;
 }
 
+struct drm_bridge *msm_dsi_host_get_bridge(struct mipi_dsi_host *host,
+				unsigned long *bridge_flags)
+{
+	struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
+	struct drm_bridge *bridge;
+
+	bridge = of_drm_find_bridge(msm_host->device_node);
+	if (bridge_flags)
+		*bridge_flags = msm_host->mode_flags;
+
+	return bridge;
+}
diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c
index c4c95c7..026e223 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_manager.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c
@@ -517,7 +517,7 @@ static const struct drm_bridge_funcs dsi_mgr_bridge_funcs = {
 	.mode_set = dsi_mgr_bridge_mode_set,
 };
 
-/* initialize connector */
+/* initialize connector when we're connected to a drm_panel */
 struct drm_connector *msm_dsi_manager_connector_init(u8 id)
 {
 	struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
hosted by The Linux Foundation

--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux