aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArchit Taneja <architt@codeaurora.org>2015-05-04 12:09:19 +0530
committerSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2015-07-06 13:25:03 +0100
commit03440fb701922dc7c0b31f6754691debab098f9d (patch)
treea4a5b9607aa0fa751d5b870c77ba94856f47f74e
parent4019bf26ea3b3be3de7100d6942b8cabc464e2c1 (diff)
drm/msm/dsi: external bridge support
In order for dsi to support external bridge chips, the dsi driver needs to check whether the dsi host's child specified in DT is a bridge or a panel. We first look for a panel node, if we don't have one, we look for a bridge. If we connect to an external bridge, we don't need to worry about creating a connector, the bridge driver is responsible for that. Signed-off-by: Archit Taneja <architt@codeaurora.org> Conflicts: drivers/gpu/drm/msm/dsi/dsi.c drivers/gpu/drm/msm/dsi/dsi_manager.c
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi.c38
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi.h6
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_host.c45
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_manager.c127
4 files changed, 194 insertions, 22 deletions
diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c
index 1f2561e2ff718..8de8d5722762e 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.c
+++ b/drivers/gpu/drm/msm/dsi/dsi.c
@@ -223,12 +223,38 @@ 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;
+ msm_dsi->ext_bridge = msm_dsi_host_get_ext_bridge(msm_dsi->host);
+
+ /*
+ * skip making connector if we have a bridge. the bridge driver will take
+ * care of creating a connector
+ */
+ if (msm_dsi->ext_bridge) {
+ struct drm_encoder *encoder = encoders[MSM_DSI_VIDEO_ENCODER_ID];
+ struct drm_connector *connector;
+ struct drm_bridge *int_bridge = msm_dsi->bridge;
+ struct drm_bridge *ext_bridge = msm_dsi->ext_bridge;
+
+ int_bridge->next = ext_bridge;
+ ext_bridge->encoder = encoder;
+
+ drm_bridge_attach(msm_dsi->dev, ext_bridge);
+
+ /* TODO: don't just pick up the first connector, pick the one the bridge
+ * driver just created
+ */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector)
+ msm_dsi->connector = connector;
+ }
+ } 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;
diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h
index 5e29aadc0beea..fbfbc31a2d7ad 100644
--- a/drivers/gpu/drm/msm/dsi/dsi.h
+++ b/drivers/gpu/drm/msm/dsi/dsi.h
@@ -54,6 +54,7 @@ struct msm_dsi {
struct platform_device *pdev;
struct drm_connector *connector;
+ /* internal bridge to link to mdp5 encoder */
struct drm_bridge *bridge;
struct mipi_dsi_host *host;
@@ -61,7 +62,11 @@ struct msm_dsi {
struct drm_panel *panel;
unsigned long panel_flags;
+ /* dsi host is connected to a bridge/encoder chip */
+ struct drm_bridge *ext_bridge;
+
struct device *phy_dev;
+
bool phy_enabled;
/* the encoders we are hooked to (outside of dsi block) */
@@ -128,6 +133,7 @@ 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_ext_bridge(struct mipi_dsi_host *host);
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 de04009233035..2627570724a4b 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_host.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_host.c
@@ -219,6 +219,9 @@ struct msm_dsi_host {
enum mipi_dsi_pixel_format format;
unsigned long mode_flags;
+ /* external bridge info */
+ struct device_node *ext_bridge_node;
+
u32 dma_cmd_ctrl_restore;
bool registered;
@@ -1372,6 +1375,7 @@ static int dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *dsi)
{
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
+ struct device_node *bridge, *np = msm_host->pdev->dev.of_node;
int ret;
msm_host->channel = dsi->channel;
@@ -1379,6 +1383,13 @@ static int dsi_host_attach(struct mipi_dsi_host *host,
msm_host->format = dsi->format;
msm_host->mode_flags = dsi->mode_flags;
+ /* if we have an external bridge, don't populate panel data */
+ bridge = of_get_child_by_name(np, "bridge");
+ if (bridge) {
+ of_node_put(bridge);
+ return 0;
+ }
+
msm_host->panel_node = dsi->dev.of_node;
/* Some gpios defined in panel DT need to be controlled by host */
@@ -1579,11 +1590,26 @@ int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer)
* create framebuffer.
*/
if (check_defer) {
+ /*
+ * first look for a child panel node, if a panel node
+ * exists but the corresponding panel device isn't
+ * probed, defer msm dsi's probe
+ *
+ * if a child panel isn't specified at all, try to look
+ * for a bridge phandle, defer probe if it there is a
+ * bridge phandle but the bridge device isn't added yet
+ */
node = of_get_child_by_name(msm_host->pdev->dev.of_node,
"panel");
if (node) {
if (!of_drm_find_panel(node))
return -EPROBE_DEFER;
+ } else {
+ struct drm_bridge *bridge;
+
+ bridge = msm_dsi_host_get_ext_bridge(host);
+ if (!bridge)
+ return -EPROBE_DEFER;
}
}
}
@@ -1882,7 +1908,6 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host)
DBG("dsi host already on");
goto unlock_ret;
}
-
ret = dsi_calc_clk_rate(msm_host);
if (ret) {
pr_err("%s: unable to calc clk rate, %d\n", __func__, ret);
@@ -2000,3 +2025,21 @@ struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host,
return panel;
}
+struct drm_bridge *msm_dsi_host_get_ext_bridge(struct mipi_dsi_host *host)
+{
+ struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
+ struct drm_bridge *bridge;
+ struct device_node *np = msm_host->pdev->dev.of_node;
+
+ msm_host->ext_bridge_node = of_get_child_by_name(np, "bridge");
+ if (!msm_host->ext_bridge_node) {
+ dev_err(&msm_host->pdev->dev, "bridge not found\n");
+ return NULL;
+ }
+
+ bridge = of_drm_find_bridge(msm_host->ext_bridge_node);
+
+ of_node_put(msm_host->ext_bridge_node);
+
+ return bridge;
+}
diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c
index bd247b7dbc44c..829ed76ae3b71 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_manager.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c
@@ -333,7 +333,31 @@ dsi_mgr_connector_best_encoder(struct drm_connector *connector)
return msm_dsi_get_encoder(msm_dsi);
}
-static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge)
+static void pre_enable_bridge(struct drm_bridge *bridge)
+{
+ int id = dsi_mgr_bridge_get_id(bridge);
+ struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
+ struct mipi_dsi_host *host = msm_dsi->host;
+ struct drm_bridge *ext_bridge = msm_dsi->ext_bridge;
+ int ret;
+
+ if (!ext_bridge)
+ return;
+
+ ret = msm_dsi_host_power_on(host);
+ if (ret) {
+ pr_err("%s: power on host %d failed, %d\n", __func__, id, ret);
+ return;
+ }
+
+ ret = msm_dsi_host_enable(host);
+ if (ret) {
+ pr_err("%s: enable host %d failed, %d\n", __func__, id, ret);
+ msm_dsi_host_power_off(host);
+ }
+}
+
+static void pre_enable_panel(struct drm_bridge *bridge)
{
int id = dsi_mgr_bridge_get_id(bridge);
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
@@ -344,6 +368,7 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge)
int ret;
DBG("id=%d", id);
+
if (!panel || (is_dual_dsi && (DSI_1 == id)))
return;
@@ -409,17 +434,27 @@ host_on_fail:
return;
}
-static void dsi_mgr_bridge_enable(struct drm_bridge *bridge)
+static void post_disable_bridge(struct drm_bridge *bridge)
{
- DBG("");
-}
+ int id = dsi_mgr_bridge_get_id(bridge);
+ struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
+ struct mipi_dsi_host *host = msm_dsi->host;
+ struct drm_bridge *ext_bridge = msm_dsi->ext_bridge;
+ int ret;
-static void dsi_mgr_bridge_disable(struct drm_bridge *bridge)
-{
- DBG("");
+ if (!ext_bridge)
+ return;
+
+ ret = msm_dsi_host_disable(host);
+ if (ret)
+ pr_err("%s: host %d disable failed, %d\n", __func__, id, ret);
+
+ ret = msm_dsi_host_power_off(host);
+ if (ret)
+ pr_err("%s: host %d power off failed,%d\n", __func__, id, ret);
}
-static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge)
+static void post_disable_panel(struct drm_bridge *bridge)
{
int id = dsi_mgr_bridge_get_id(bridge);
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
@@ -464,7 +499,21 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge)
}
}
-static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge,
+static void mode_set_bridge(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ int id = dsi_mgr_bridge_get_id(bridge);
+ struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
+ struct mipi_dsi_host *host = msm_dsi->host;
+
+ if (!msm_dsi->ext_bridge)
+ return;
+
+ msm_dsi_host_set_display_mode(host, adjusted_mode);
+}
+
+static void mode_set_panel(struct drm_bridge *bridge,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
@@ -474,6 +523,56 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge,
struct mipi_dsi_host *host = msm_dsi->host;
bool is_dual_dsi = IS_DUAL_DSI();
+ if (is_dual_dsi && (DSI_1 == id))
+ return;
+
+ msm_dsi_host_set_display_mode(host, adjusted_mode);
+ if (is_dual_dsi && other_dsi)
+ msm_dsi_host_set_display_mode(other_dsi->host, adjusted_mode);
+}
+/*
+ * for now, the dsi bridge functions ignore dual dsi mode if an external
+ * bridge IC is connected to it
+ */
+static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge)
+{
+ int id = dsi_mgr_bridge_get_id(bridge);
+ struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
+
+ if (msm_dsi->panel)
+ pre_enable_panel(bridge);
+ else
+ pre_enable_bridge(bridge);
+}
+
+static void dsi_mgr_bridge_enable(struct drm_bridge *bridge)
+{
+ DBG("");
+}
+
+static void dsi_mgr_bridge_disable(struct drm_bridge *bridge)
+{
+ DBG("");
+}
+
+static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge)
+{
+ int id = dsi_mgr_bridge_get_id(bridge);
+ struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
+
+ if (msm_dsi->panel)
+ post_disable_panel(bridge);
+ else
+ post_disable_bridge(bridge);
+}
+
+static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ int id = dsi_mgr_bridge_get_id(bridge);
+ struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
+
DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
mode->base.id, mode->name,
mode->vrefresh, mode->clock,
@@ -483,12 +582,10 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge,
mode->vsync_end, mode->vtotal,
mode->type, mode->flags);
- if (is_dual_dsi && (DSI_1 == id))
- return;
-
- msm_dsi_host_set_display_mode(host, adjusted_mode);
- if (is_dual_dsi && other_dsi)
- msm_dsi_host_set_display_mode(other_dsi->host, adjusted_mode);
+ if (msm_dsi->panel)
+ mode_set_panel(bridge, mode, adjusted_mode);
+ else
+ mode_set_bridge(bridge, mode, adjusted_mode);
}
static const struct drm_connector_funcs dsi_mgr_connector_funcs = {