Re: [PATCH 63/89] drm/i915/skl: Define shared DPLLs for Skylake

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 9/23/2014 7:58 PM, Paulo Zanoni wrote:
2014-09-04 8:27 GMT-03:00 Damien Lespiau<damien.lespiau@xxxxxxxxx>:
From: Satheeshakrishna M<satheeshakrishna.m@xxxxxxxxx>

On skylake, DPLL 1, 2 and 3 can be used for DP and HDMI. The shared dpll
framework allows us to share those DPLLs among DDIs when possible.

The most tricky part is to provide a DPLL state that can be easily
compared. DPLL_CRTL1 is shared by all the DPLLs, 6 bits each. The
per-dpll crtl1 field of the hw state is then normalized to be the same
value if 2 DPLLs do indeed have identical values for those 6 bits.

v2: Port the code to the shared DPLL infrastructure (Damien)

XXX: This patch would benefit from a bunchf of macros for handling ctrl1
to encapsulate the masking and shifting (Daniel)
Are you planning to do the XXX above before merging?

The only suspect thing is that the spec doesn't mention
if/when/how-much we need to sleep after enabling the clocks... I hope
this won't be a problem later :)
We added a very conservative number, so shouldn't be a problem.
Reviewed-by: Paulo Zanoni<paulo.r.zanoni@xxxxxxxxx>

Signed-off-by: Satheeshakrishna M<satheeshakrishna.m@xxxxxxxxx>  (v1)
Signed-off-by: Damien Lespiau<damien.lespiau@xxxxxxxxx>
---
  drivers/gpu/drm/i915/i915_drv.h  |  11 ++++
  drivers/gpu/drm/i915/intel_ddi.c | 126 ++++++++++++++++++++++++++++++++++++++-
  2 files changed, 136 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 65e5ffb..a6e14db 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -225,6 +225,17 @@ struct intel_dpll_hw_state {

         /* hsw, bdw */
         uint32_t wrpll;
+
+       /* skl */
+       /*
+        * DPLL_CTRL1 has 6 bits for each each this DPLL. We store those in
+        * lower part of crtl1 and they get shifted into position when writing
+        * the register.  This allows us to easily compare the state to share
+        * the DPLL.
+        */
+       uint32_t ctrl1;
+       /* HDMI only, 0 when used for DP */
+       uint32_t cfgcr1, cfgcr2;
  };

  struct intel_shared_dpll {
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index b5cfb07..53ac23d 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -1522,12 +1522,136 @@ static void hsw_shared_dplls_init(struct drm_i915_private *dev_priv)
         }
  }

+static const char * const skl_ddi_pll_names[] = {
+       "DPLL 1",
+       "DPLL 2",
+       "DPLL 3",
+};
+
+struct skl_dpll_regs {
+       u32 ctl, cfgcr1, cfgcr2;
+};
+
+/* this array is indexed by the *shared* pll id */
+static const struct skl_dpll_regs skl_dpll_regs[3] = {
+       {
+               /* DPLL 1 */
+               .ctl = LCPLL2_CTL,
+               .cfgcr1 = DPLL1_CFGCR1,
+               .cfgcr2 = DPLL1_CFGCR2,
+       },
+       {
+               /* DPLL 2 */
+               .ctl = WRPLL_CTL1,
+               .cfgcr1 = DPLL2_CFGCR1,
+               .cfgcr2 = DPLL2_CFGCR2,
+       },
+       {
+               /* DPLL 3 */
+               .ctl = WRPLL_CTL2,
+               .cfgcr1 = DPLL3_CFGCR1,
+               .cfgcr2 = DPLL3_CFGCR2,
+       },
+};
+
+static void skl_ddi_pll_enable(struct drm_i915_private *dev_priv,
+                              struct intel_shared_dpll *pll)
+{
+       uint32_t val;
+       unsigned int dpll;
+       const struct skl_dpll_regs *regs = skl_dpll_regs;
+
+       /* DPLL0 is not part of the shared DPLLs, so pll->id is 0 for DPLL1 */
+       dpll = pll->id + 1;
+
+       val = I915_READ(DPLL_CTRL1);
+
+       val &= ~(DPLL_CTRL1_HDMI_MODE(dpll) | DPLL_CTRL1_SSC(dpll) |
+                DPLL_CRTL1_LINK_RATE_MASK(dpll));
+       val |= pll->hw_state.ctrl1 << (dpll * 6);
+
+       I915_WRITE(DPLL_CTRL1, val);
+       POSTING_READ(DPLL_CTRL1);
+
+       I915_WRITE(regs[pll->id].cfgcr1, pll->hw_state.cfgcr1);
+       I915_WRITE(regs[pll->id].cfgcr2, pll->hw_state.cfgcr2);
+       POSTING_READ(regs[pll->id].cfgcr1);
+       POSTING_READ(regs[pll->id].cfgcr2);
+
+       /* the enable bit is always bit 31 */
+       I915_WRITE(regs[pll->id].ctl,
+                  I915_READ(regs[pll->id].ctl) | LCPLL_PLL_ENABLE);
+
+       if (wait_for(I915_READ(DPLL_STATUS) & DPLL_LOCK(dpll), 5))
+               DRM_ERROR("DPLL %d not locked\n", dpll);
+}
+
+static void skl_ddi_pll_disable(struct drm_i915_private *dev_priv,
+                               struct intel_shared_dpll *pll)
+{
+       const struct skl_dpll_regs *regs = skl_dpll_regs;
+
+       /* the enable bit is always bit 31 */
+       I915_WRITE(regs[pll->id].ctl,
+                  I915_READ(regs[pll->id].ctl) & ~LCPLL_PLL_ENABLE);
+       POSTING_READ(regs[pll->id].ctl);
+}
+
+static bool skl_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv,
+                                    struct intel_shared_dpll *pll,
+                                    struct intel_dpll_hw_state *hw_state)
+{
+       uint32_t val;
+       unsigned int dpll;
+       const struct skl_dpll_regs *regs = skl_dpll_regs;
+
+       if (!intel_display_power_enabled(dev_priv, POWER_DOMAIN_PLLS))
+               return false;
+
+       /* DPLL0 is not part of the shared DPLLs, so pll->id is 0 for DPLL1 */
+       dpll = pll->id + 1;
+
+       val = I915_READ(regs[pll->id].ctl);
+       if (!(val & LCPLL_PLL_ENABLE))
+               return false;
+
+       val = I915_READ(DPLL_CTRL1);
+       hw_state->ctrl1 = (val >> (dpll * 6)) & 0x3f;
+
+       /* avoid reading back stale values if HDMI mode is not enabled */
+       if (val & DPLL_CTRL1_HDMI_MODE(dpll)) {
+               hw_state->cfgcr1 = I915_READ(regs[pll->id].cfgcr1);
+               hw_state->cfgcr2 = I915_READ(regs[pll->id].cfgcr2);
+       }
+
+       return true;
+}
+
+static void skl_shared_dplls_init(struct drm_i915_private *dev_priv)
+{
+       int i;
+
+       dev_priv->num_shared_dpll = 3;
+
+       for (i = 0; i < dev_priv->num_shared_dpll; i++) {
+               dev_priv->shared_dplls[i].id = i;
+               dev_priv->shared_dplls[i].name = skl_ddi_pll_names[i];
+               dev_priv->shared_dplls[i].disable = skl_ddi_pll_disable;
+               dev_priv->shared_dplls[i].enable = skl_ddi_pll_enable;
+               dev_priv->shared_dplls[i].get_hw_state =
+                       skl_ddi_pll_get_hw_state;
+       }
+}
+
  void intel_ddi_pll_init(struct drm_device *dev)
  {
         struct drm_i915_private *dev_priv = dev->dev_private;
         uint32_t val = I915_READ(LCPLL_CTL);

-       hsw_shared_dplls_init(dev_priv);
+       if (IS_SKYLAKE(dev))
+               skl_shared_dplls_init(dev_priv);
+       else
+               hsw_shared_dplls_init(dev_priv);

         DRM_DEBUG_KMS("CDCLK running at %dKHz\n",
                       intel_ddi_get_cdclk_freq(dev_priv));
--
1.8.3.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/intel-gfx


_______________________________________________
Intel-gfx mailing list
Intel-gfx@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/intel-gfx




[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]
  Powered by Linux