diff options
author | Archit Taneja <architt@codeaurora.org> | 2015-05-04 12:09:19 +0530 |
---|---|---|
committer | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | 2015-07-06 13:25:03 +0100 |
commit | 03440fb701922dc7c0b31f6754691debab098f9d (patch) | |
tree | a4a5b9607aa0fa751d5b870c77ba94856f47f74e | |
parent | 4019bf26ea3b3be3de7100d6942b8cabc464e2c1 (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.c | 38 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/dsi/dsi.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/dsi/dsi_host.c | 45 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/dsi/dsi_manager.c | 127 |
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 = { |