diff mbox

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

Message ID 1409830075-11139-64-git-send-email-damien.lespiau@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Lespiau, Damien Sept. 4, 2014, 11:27 a.m. UTC
From: Satheeshakrishna M <satheeshakrishna.m@intel.com>

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)

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

Comments

Paulo Zanoni Sept. 23, 2014, 2:28 p.m. UTC | #1
2014-09-04 8:27 GMT-03:00 Damien Lespiau <damien.lespiau@intel.com>:
> From: Satheeshakrishna M <satheeshakrishna.m@intel.com>
>
> 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 :)

Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>

>
> Signed-off-by: Satheeshakrishna M <satheeshakrishna.m@intel.com> (v1)
> Signed-off-by: Damien Lespiau <damien.lespiau@intel.com>
> ---
>  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@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
Satheeshakrishna M Oct. 1, 2014, 10:52 a.m. UTC | #2
On 9/23/2014 7:58 PM, Paulo Zanoni wrote:
> 2014-09-04 8:27 GMT-03:00 Damien Lespiau<damien.lespiau@intel.com>:
>> From: Satheeshakrishna M<satheeshakrishna.m@intel.com>
>>
>> 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@intel.com>
>
>> Signed-off-by: Satheeshakrishna M<satheeshakrishna.m@intel.com>  (v1)
>> Signed-off-by: Damien Lespiau<damien.lespiau@intel.com>
>> ---
>>   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@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
>
diff mbox

Patch

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));