MLK-19961 drm/imx/dcss: Fix 27MHz pixel clock platform freeze
authorLaurentiu Palcu <laurentiu.palcu@nxp.com>
Wed, 17 Oct 2018 08:20:34 +0000 (11:20 +0300)
committerLeonard Crestez <leonard.crestez@nxp.com>
Wed, 17 Apr 2019 23:51:34 +0000 (02:51 +0300)
When the VIDEO_PLL2 clock code was moved to the DCSS driver, a
regression was introduced and any mode requiring a 27MHz pixel clock
would instantly freeze the platform.

It turns out, after setting the clocks in bypass mode, PLL_CLKE was
never set. Hence, DCSS was not getting any clock. Without a valid clock,
any attempt to access DTG registers will freeze the system.

This patch:
 * sets PLL_CLKE when bypass is used;
 * simplifies the pll code a little;
 * increases the atomic CRTC enable timeout to 500ms to accommodate the
   delay after which the clock is available when bypass is used;

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@nxp.com>
CC: Oliver Brown <oliver.brown@nxp.com>
drivers/gpu/drm/imx/dcss/dcss-crtc.c
drivers/gpu/imx/dcss/dcss-pll.c

index f8be36c..d73be20 100644 (file)
@@ -264,7 +264,7 @@ static void dcss_crtc_atomic_enable(struct drm_crtc *crtc,
 
        reinit_completion(&dcss_crtc->en_dis_completion);
        wait_for_completion_timeout(&dcss_crtc->en_dis_completion,
-                                   msecs_to_jiffies(100));
+                                   msecs_to_jiffies(500));
 
        crtc->enabled = true;
 }
index 2f2ff31..3a188e8 100644 (file)
@@ -283,11 +283,6 @@ int dcss_pll_set_rate(struct dcss_soc *dcss, u32 freq, u32 ref_clk,
                /* use hdmi 27 mhz */
                pll_control_reg = readl(reg + VIDEO_PLL2_CFG0_OFFSET);
                pll_control_reg &= ~SSCG_PLL_REFCLK_SEL_MASK;
-               writel(pll_control_reg, reg + VIDEO_PLL2_CFG0_OFFSET);
-               dev_dbg(pll->dcss->dev, "pll reg offset %x data %x\n",
-                        VIDEO_PLL2_CFG0_OFFSET, pll_control_reg);
-
-               pll_control_reg = readl(reg + VIDEO_PLL2_CFG0_OFFSET);
                pll_control_reg |= ref_clk & SSCG_PLL_REFCLK_SEL_MASK;
                writel(pll_control_reg, reg + VIDEO_PLL2_CFG0_OFFSET);
                dev_dbg(pll->dcss->dev, "pll reg offset %x data %x\n",
@@ -318,11 +313,6 @@ int dcss_pll_set_rate(struct dcss_soc *dcss, u32 freq, u32 ref_clk,
                /* reference 2 requires HDMI to be initialized */
                pll_control_reg = readl(reg + VIDEO_PLL2_CFG0_OFFSET);
                pll_control_reg &= ~SSCG_PLL_REFCLK_SEL_MASK;
-               writel(pll_control_reg, reg + VIDEO_PLL2_CFG0_OFFSET);
-               dev_dbg(pll->dcss->dev, "pll reg offset %x data %x\n",
-                        VIDEO_PLL2_CFG0_OFFSET, pll_control_reg);
-
-               pll_control_reg = readl(reg + VIDEO_PLL2_CFG0_OFFSET);
                pll_control_reg |= ref_clk & SSCG_PLL_REFCLK_SEL_MASK;
                writel(pll_control_reg, reg + VIDEO_PLL2_CFG0_OFFSET);
                dev_dbg(pll->dcss->dev, "pll reg offset %x data %x\n",
@@ -353,62 +343,43 @@ int dcss_pll_enable(struct dcss_soc *dcss)
        struct dcss_pll_priv *pll = dcss->pll_priv;
        void __iomem *base_reg = pll->base_reg;
        u32 reg;
+       int lock_count = 0;
 
-       if (pll->data.fout != 27000000) {
-               int lock_count = 0;
-
-               /* Clear power down bit */
-               reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
-               reg &= ~SSCG_PLL_PD_MASK;
-               writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
-               dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
-                        VIDEO_PLL2_CFG0_OFFSET, reg);
-
-               /* Enable clk output  */
-               reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
-               reg |= SSCG_PLL_VIDEO_PLL2_CLKE_MASK;
-               writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
-               dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
-                        VIDEO_PLL2_CFG0_OFFSET, reg);
+       reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
 
+       if (pll->data.fout != 27000000) {
                /* Clear bypass */
-               reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
                reg &= ~SSCG_PLL_BYPASS1_MASK;
-               writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
-               dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
-                        VIDEO_PLL2_CFG0_OFFSET, reg);
-
-               udelay(100);
-
-               reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
                reg &= ~SSCG_PLL_BYPASS2_MASK;
-               writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
-               dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
-                        VIDEO_PLL2_CFG0_OFFSET, reg);
-               /* Wait until lock */
-
-               while (!(readl(base_reg + VIDEO_PLL2_CFG0_OFFSET) &
-                        SSCG_PLL_LOCK_MASK)) {
-                       udelay(100);
-                       if (lock_count++ > (10 * 1000)) {
-                               dev_err(dcss->dev,
-                                       "failed to lock video pll 2!\n");
-                               return 1;
-                       }
-               }
        } else {
                /* use bypass mode for 27000000 */
-               reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
                reg |= SSCG_PLL_BYPASS1_MASK;
-               writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
-               dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
-                        VIDEO_PLL2_CFG0_OFFSET, reg);
-
-               reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
                reg |= SSCG_PLL_BYPASS2_MASK;
-               writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
-               dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
-                        VIDEO_PLL2_CFG0_OFFSET, reg);
+       }
+
+       writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
+       dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
+               VIDEO_PLL2_CFG0_OFFSET, reg);
+
+       /* Clear power down bit */
+       reg = readl(base_reg + VIDEO_PLL2_CFG0_OFFSET);
+       reg &= ~SSCG_PLL_PD_MASK;
+
+       /* Enable clk output  */
+       reg |= SSCG_PLL_VIDEO_PLL2_CLKE_MASK;
+       writel(reg, base_reg + VIDEO_PLL2_CFG0_OFFSET);
+       dev_dbg(dcss->dev, "pll reg offset %x data %x\n",
+               VIDEO_PLL2_CFG0_OFFSET, reg);
+
+       /* Wait until lock */
+       while (!(readl(base_reg + VIDEO_PLL2_CFG0_OFFSET) &
+                SSCG_PLL_LOCK_MASK) && pll->data.fout != 27000000) {
+               udelay(100);
+               if (lock_count++ > (10 * 1000)) {
+                       dev_err(dcss->dev,
+                               "failed to lock video pll 2!\n");
+                       return 1;
+               }
        }
 
        return 0;