drm/msm/dsi: Save/Restore PLL status across PHY reset
authorHai Li <hali@codeaurora.org>
Fri, 3 Jul 2015 14:09:46 +0000 (10:09 -0400)
committerRob Clark <robdclark@gmail.com>
Sat, 15 Aug 2015 22:27:18 +0000 (18:27 -0400)
Reset DSI PHY silently changes its PLL registers to reset status,
which will make cached status in clock driver invalid and result
in wrong output rate of link clocks. The current restore mechanism
in DSI PLL does not cover all the cases. This change is to recover
PLL status after PHY reset to match HW status with cached status
in clock driver.

Signed-off-by: Hai Li <hali@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
drivers/gpu/drm/msm/dsi/dsi.h
drivers/gpu/drm/msm/dsi/dsi_manager.c
drivers/gpu/drm/msm/dsi/pll/dsi_pll.c
drivers/gpu/drm/msm/dsi/pll/dsi_pll.h
drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c

index e18872a62806686c78537306a5f4dd75b2d3f2e7..912057df5c020417479bb7cc3a278871fa44fcf0 100644 (file)
@@ -95,6 +95,8 @@ struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev,
 void msm_dsi_pll_destroy(struct msm_dsi_pll *pll);
 int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll,
        struct clk **byte_clk_provider, struct clk **pixel_clk_provider);
+void msm_dsi_pll_save_state(struct msm_dsi_pll *pll);
+int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll);
 #else
 static inline struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev,
                         enum msm_dsi_phy_type type, int id) {
@@ -108,6 +110,13 @@ static inline int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll,
 {
        return -ENODEV;
 }
+static inline void msm_dsi_pll_save_state(struct msm_dsi_pll *pll)
+{
+}
+static inline int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll)
+{
+       return 0;
+}
 #endif
 
 /* dsi host */
index bd247b7dbc44c12b16610fa568c38e66d1d168f9..ca4ff4ab663e8f86ca49e69450ef0688655348ce 100644 (file)
@@ -611,12 +611,28 @@ int msm_dsi_manager_phy_enable(int id,
        struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id);
        struct msm_dsi_phy *phy = msm_dsi->phy;
        int src_pll_id = IS_DUAL_DSI() ? DSI_CLOCK_MASTER : id;
+       struct msm_dsi_pll *pll = msm_dsi_phy_get_pll(msm_dsi->phy);
        int ret;
 
        ret = msm_dsi_phy_enable(phy, src_pll_id, bit_rate, esc_rate);
        if (ret)
                return ret;
 
+       /*
+        * Reset DSI PHY silently changes its PLL registers to reset status,
+        * which will confuse clock driver and result in wrong output rate of
+        * link clocks. Restore PLL status if its PLL is being used as clock
+        * source.
+        */
+       if (!IS_DUAL_DSI() || (id == DSI_CLOCK_MASTER)) {
+               ret = msm_dsi_pll_restore_state(pll);
+               if (ret) {
+                       pr_err("%s: failed to restore pll state\n", __func__);
+                       msm_dsi_phy_disable(phy);
+                       return ret;
+               }
+       }
+
        msm_dsi->phy_enabled = true;
        msm_dsi_phy_get_clk_pre_post(phy, clk_pre, clk_post);
 
@@ -629,6 +645,11 @@ void msm_dsi_manager_phy_disable(int 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;
+       struct msm_dsi_pll *pll = msm_dsi_phy_get_pll(msm_dsi->phy);
+
+       /* Save PLL status if it is a clock source */
+       if (!IS_DUAL_DSI() || (id == DSI_CLOCK_MASTER))
+               msm_dsi_pll_save_state(pll);
 
        /* disable DSI phy
         * In dual-dsi configuration, the phy should be disabled for the
index 509376fdd1120e883519178048c9831aca63130d..5104fc9f9a5398e71d4869264f49f3a74c801b84 100644 (file)
@@ -72,31 +72,14 @@ long msm_dsi_pll_helper_clk_round_rate(struct clk_hw *hw,
 int msm_dsi_pll_helper_clk_prepare(struct clk_hw *hw)
 {
        struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
-       int ret;
-
-       /*
-        * Certain PLLs need to update the same VCO rate and registers
-        * after resume in suspend/resume scenario.
-        */
-       if (pll->restore_state) {
-               ret = pll->restore_state(pll);
-               if (ret)
-                       goto error;
-       }
 
-       ret = dsi_pll_enable(pll);
-
-error:
-       return ret;
+       return dsi_pll_enable(pll);
 }
 
 void msm_dsi_pll_helper_clk_unprepare(struct clk_hw *hw)
 {
        struct msm_dsi_pll *pll = hw_clk_to_pll(hw);
 
-       if (pll->save_state)
-               pll->save_state(pll);
-
        dsi_pll_disable(pll);
 }
 
@@ -134,6 +117,29 @@ void msm_dsi_pll_destroy(struct msm_dsi_pll *pll)
                pll->destroy(pll);
 }
 
+void msm_dsi_pll_save_state(struct msm_dsi_pll *pll)
+{
+       if (pll->save_state) {
+               pll->save_state(pll);
+               pll->state_saved = true;
+       }
+}
+
+int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll)
+{
+       int ret;
+
+       if (pll->restore_state && pll->state_saved) {
+               ret = pll->restore_state(pll);
+               if (ret)
+                       return ret;
+
+               pll->state_saved = false;
+       }
+
+       return 0;
+}
+
 struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev,
                        enum msm_dsi_phy_type type, int id)
 {
index 5a3bb241c039b7403ad1e9683281a71d4d4e21ba..b69df19f1fd486dc870c18e975275404cf5a5f54 100644 (file)
@@ -27,6 +27,7 @@ struct msm_dsi_pll {
 
        struct clk_hw   clk_hw;
        bool            pll_on;
+       bool            state_saved;
 
        unsigned long   min_rate;
        unsigned long   max_rate;
index eb8ac3097ff501b19bea92c69ed73bf1caa988de..1912cfcca48c85eb4c788d0dda0366ae06371ba3 100644 (file)
@@ -465,26 +465,21 @@ static int dsi_pll_28nm_restore_state(struct msm_dsi_pll *pll)
        void __iomem *base = pll_28nm->mmio;
        int ret;
 
-       if ((cached_state->vco_rate != 0) &&
-               (cached_state->vco_rate == __clk_get_rate(pll->clk_hw.clk))) {
-               ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw,
-                                               cached_state->vco_rate, 0);
-               if (ret) {
-                       dev_err(&pll_28nm->pdev->dev,
-                               "restore vco rate failed. ret=%d\n", ret);
-                       return ret;
-               }
-
-               pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG,
-                               cached_state->postdiv3);
-               pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG,
-                               cached_state->postdiv1);
-               pll_write(base + REG_DSI_28nm_PHY_PLL_VREG_CFG,
-                               cached_state->byte_mux);
-
-               cached_state->vco_rate = 0;
+       ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw,
+                                       cached_state->vco_rate, 0);
+       if (ret) {
+               dev_err(&pll_28nm->pdev->dev,
+                       "restore vco rate failed. ret=%d\n", ret);
+               return ret;
        }
 
+       pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG,
+                       cached_state->postdiv3);
+       pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG,
+                       cached_state->postdiv1);
+       pll_write(base + REG_DSI_28nm_PHY_PLL_VREG_CFG,
+                       cached_state->byte_mux);
+
        return 0;
 }