summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/msm/dsi/dsi_manager.c
diff options
context:
space:
mode:
authorHai Li <hali@codeaurora.org>2017-01-07 14:24:38 +0530
committerRob Clark <robdclark@gmail.com>2017-02-06 11:28:45 -0500
commitb62aa70a98c5401ada63657eee7c88da70bdbb27 (patch)
treef4e2363af3d9bcfc03743939ae0fd8f981192a26 /drivers/gpu/drm/msm/dsi/dsi_manager.c
parent34d9545b9f769c6553e31a6820c9cb51f5e93099 (diff)
drm/msm/dsi: Move PHY operations out of host
Since DSI PHY has been a separate platform device, it should not depend on the resources in host to be functional. This change is to trigger PHY operations in manager, instead of host, so that host and PHY can be completely separated. Signed-off-by: Hai Li <hali@codeaurora.org> Signed-off-by: Archit Taneja <architt@codeaurora.org> Signed-off-by: Rob Clark <robdclark@gmail.com>
Diffstat (limited to 'drivers/gpu/drm/msm/dsi/dsi_manager.c')
-rw-r--r--drivers/gpu/drm/msm/dsi/dsi_manager.c178
1 files changed, 105 insertions, 73 deletions
diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c
index cb67e78d775b..921270ea6059 100644
--- a/drivers/gpu/drm/msm/dsi/dsi_manager.c
+++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c
@@ -125,6 +125,84 @@ static int dsi_mgr_setup_components(int id)
return ret;
}
+static int enable_phy(struct msm_dsi *msm_dsi, int src_pll_id,
+ struct msm_dsi_phy_shared_timings *shared_timings)
+{
+ struct msm_dsi_phy_clk_request clk_req;
+ int ret;
+
+ msm_dsi_host_get_phy_clk_req(msm_dsi->host, &clk_req);
+
+ ret = msm_dsi_phy_enable(msm_dsi->phy, src_pll_id, &clk_req);
+ msm_dsi_phy_get_shared_timings(msm_dsi->phy, shared_timings);
+
+ return ret;
+}
+
+static int
+dsi_mgr_phy_enable(int id,
+ struct msm_dsi_phy_shared_timings shared_timings[DSI_MAX])
+{
+ struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
+ struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER);
+ struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE);
+ int src_pll_id = IS_DUAL_DSI() ? DSI_CLOCK_MASTER : id;
+ int ret;
+
+ /* In case of dual DSI, some registers in PHY1 have been programmed
+ * during PLL0 clock's set_rate. The PHY1 reset called by host1 here
+ * will silently reset those PHY1 registers. Therefore we need to reset
+ * and enable both PHYs before any PLL clock operation.
+ */
+ if (IS_DUAL_DSI() && mdsi && sdsi) {
+ if (!mdsi->phy_enabled && !sdsi->phy_enabled) {
+ msm_dsi_host_reset_phy(mdsi->host);
+ msm_dsi_host_reset_phy(sdsi->host);
+
+ ret = enable_phy(mdsi, src_pll_id,
+ &shared_timings[DSI_CLOCK_MASTER]);
+ if (ret)
+ return ret;
+ ret = enable_phy(sdsi, src_pll_id,
+ &shared_timings[DSI_CLOCK_SLAVE]);
+ if (ret) {
+ msm_dsi_phy_disable(mdsi->phy);
+ return ret;
+ }
+ }
+ } else {
+ msm_dsi_host_reset_phy(mdsi->host);
+ ret = enable_phy(msm_dsi, src_pll_id, &shared_timings[id]);
+ if (ret)
+ return ret;
+ }
+
+ msm_dsi->phy_enabled = true;
+
+ return 0;
+}
+
+static void dsi_mgr_phy_disable(int id)
+{
+ struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
+ struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER);
+ struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE);
+
+ /* disable DSI phy
+ * In dual-dsi configuration, the phy should be disabled for the
+ * first controller only when the second controller is disabled.
+ */
+ msm_dsi->phy_enabled = false;
+ if (IS_DUAL_DSI() && mdsi && sdsi) {
+ if (!mdsi->phy_enabled && !sdsi->phy_enabled) {
+ msm_dsi_phy_disable(sdsi->phy);
+ msm_dsi_phy_disable(mdsi->phy);
+ }
+ } else {
+ msm_dsi_phy_disable(msm_dsi->phy);
+ }
+}
+
struct dsi_connector {
struct drm_connector base;
int id;
@@ -360,22 +438,31 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge)
struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1);
struct mipi_dsi_host *host = msm_dsi->host;
struct drm_panel *panel = msm_dsi->panel;
+ struct msm_dsi_phy_shared_timings phy_shared_timings[DSI_MAX];
bool is_dual_dsi = IS_DUAL_DSI();
int ret;
DBG("id=%d", id);
- if (!msm_dsi_device_connected(msm_dsi) ||
- (is_dual_dsi && (DSI_1 == id)))
+ if (!msm_dsi_device_connected(msm_dsi))
return;
- ret = msm_dsi_host_power_on(host);
+ ret = dsi_mgr_phy_enable(id, phy_shared_timings);
+ if (ret)
+ goto phy_en_fail;
+
+ /* Do nothing with the host if it is DSI 1 in case of dual DSI */
+ if (is_dual_dsi && (DSI_1 == id))
+ return;
+
+ ret = msm_dsi_host_power_on(host, &phy_shared_timings[id]);
if (ret) {
pr_err("%s: power on host %d failed, %d\n", __func__, id, ret);
goto host_on_fail;
}
if (is_dual_dsi && msm_dsi1) {
- ret = msm_dsi_host_power_on(msm_dsi1->host);
+ ret = msm_dsi_host_power_on(msm_dsi1->host,
+ &phy_shared_timings[DSI_1]);
if (ret) {
pr_err("%s: power on host1 failed, %d\n",
__func__, ret);
@@ -434,6 +521,8 @@ panel_prep_fail:
host1_on_fail:
msm_dsi_host_power_off(host);
host_on_fail:
+ dsi_mgr_phy_disable(id);
+phy_en_fail:
return;
}
@@ -459,10 +548,17 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge)
DBG("id=%d", id);
- if (!msm_dsi_device_connected(msm_dsi) ||
- (is_dual_dsi && (DSI_1 == id)))
+ if (!msm_dsi_device_connected(msm_dsi))
return;
+ /*
+ * Do nothing with the host if it is DSI 1 in case of dual DSI.
+ * It is safe to call dsi_mgr_phy_disable() here because a single PHY
+ * won't be diabled until both PHYs request disable.
+ */
+ if (is_dual_dsi && (DSI_1 == id))
+ goto disable_phy;
+
if (panel) {
ret = drm_panel_disable(panel);
if (ret)
@@ -497,6 +593,9 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge)
pr_err("%s: host1 power off failed, %d\n",
__func__, ret);
}
+
+disable_phy:
+ dsi_mgr_phy_disable(id);
}
static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge,
@@ -664,73 +763,6 @@ void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge)
{
}
-int msm_dsi_manager_phy_enable(int id,
- const unsigned long bit_rate, const unsigned long esc_rate,
- struct msm_dsi_phy_shared_timings *shared_timings)
-{
- struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
- struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER);
- struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE);
- struct msm_dsi_phy *phy = msm_dsi->phy;
- int src_pll_id = IS_DUAL_DSI() ? DSI_CLOCK_MASTER : id;
- int ret;
-
- /* In case of dual DSI, some registers in PHY1 have been programmed
- * during PLL0 clock's set_rate. The PHY1 reset called by host1 here
- * will silently reset those PHY1 registers. Therefore we need to reset
- * and enable both PHYs before any PLL clock operation.
- */
- if (IS_DUAL_DSI() && mdsi && sdsi) {
- if (!mdsi->phy_enabled && !sdsi->phy_enabled) {
- msm_dsi_host_reset_phy(mdsi->host);
- msm_dsi_host_reset_phy(sdsi->host);
- ret = msm_dsi_phy_enable(mdsi->phy, src_pll_id,
- bit_rate, esc_rate);
- if (ret)
- return ret;
- ret = msm_dsi_phy_enable(sdsi->phy, src_pll_id,
- bit_rate, esc_rate);
- if (ret) {
- msm_dsi_phy_disable(mdsi->phy);
- return ret;
- }
- }
- } else {
- msm_dsi_host_reset_phy(msm_dsi->host);
- ret = msm_dsi_phy_enable(msm_dsi->phy, src_pll_id, bit_rate,
- esc_rate);
- if (ret)
- return ret;
- }
-
- msm_dsi->phy_enabled = true;
- msm_dsi_phy_get_shared_timings(phy, shared_timings);
-
- return 0;
-}
-
-void msm_dsi_manager_phy_disable(int id)
-{
- struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
- struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER);
- struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE);
- struct msm_dsi_phy *phy = msm_dsi->phy;
-
- /* disable DSI phy
- * In dual-dsi configuration, the phy should be disabled for the
- * first controller only when the second controller is disabled.
- */
- msm_dsi->phy_enabled = false;
- if (IS_DUAL_DSI() && mdsi && sdsi) {
- if (!mdsi->phy_enabled && !sdsi->phy_enabled) {
- msm_dsi_phy_disable(sdsi->phy);
- msm_dsi_phy_disable(mdsi->phy);
- }
- } else {
- msm_dsi_phy_disable(phy);
- }
-}
-
int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg)
{
struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);