From: "Ville Syrjälä" <ville.syrjala@linux.intel.com>
To: Damien Lespiau <damien.lespiau@intel.com>
Cc: intel-gfx@lists.freedesktop.org
Subject: Re: [PATCH 6/8] drm/i915/skl: Deinit/init the display at suspend/resume
Date: Tue, 5 May 2015 21:56:02 +0300 [thread overview]
Message-ID: <20150505185602.GN18908@intel.com> (raw)
In-Reply-To: <1430408363-20905-7-git-send-email-damien.lespiau@intel.com>
On Thu, Apr 30, 2015 at 04:39:21PM +0100, Damien Lespiau wrote:
> We need to re-init the display hardware when going out of suspend. This
> includes:
>
> - Hooking the PCH to the reset logic
> - Restoring CDCDLK
> - Enabling the DDB power
>
> Among those, only the CDCDLK one is a bit tricky. There's some
> complexity in that:
>
> - DPLL0 (which is the source for CDCLK) has two VCOs, each with a set
> of supported frequencies. As eDP also uses DPLL0 for its link rate,
> once DPLL0 is on, we restrict the possible eDP link rates the chosen
> VCO.
> - CDCLK also limits the bandwidth available to push pixels.
> - My current PCU firmware never ack the frequency change request
>
> So, as a first step, this commit restore what the BIOS set, until I can
> do more testing.
>
> ----
> In case that's of interest for the reviewer, I've unit tested the
> function that derives the decimal frequency field:
>
> #include <stdio.h>
> #include <stdint.h>
> #include <assert.h>
>
> #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
>
> static const struct dpll_freq {
> unsigned int freq;
> unsigned int decimal;
> } freqs[] = {
> { .freq = 308570, .decimal = 0b01001100111},
> { .freq = 337500, .decimal = 0b01010100001},
> { .freq = 432000, .decimal = 0b01101011110},
> { .freq = 450000, .decimal = 0b01110000010},
> { .freq = 540000, .decimal = 0b10000110110},
> { .freq = 617140, .decimal = 0b10011010000},
> { .freq = 675000, .decimal = 0b10101000100},
> };
>
> static void intbits(unsigned int v)
> {
> int i;
>
> for(i = 10; i >= 0; i--)
> putchar('0' + ((v >> i) & 1));
> }
>
> static unsigned int freq_decimal(unsigned int freq /* in kHz */)
> {
> unsigned int mhz, dot5;
>
> mhz = freq / 1000;
> dot5 = (freq - mhz * 1000) >= 500;
>
> return (mhz - 1) << 1 | dot5;
> }
>
> static void test_freq(const struct dpll_freq *entry)
> {
> unsigned int decimal = freq_decimal(entry->freq);
>
> printf("freq: %d, expected: ", entry->freq);
> intbits(entry->decimal);
> printf(", got: ");
> intbits(decimal);
> putchar('\n');
>
> assert(decimal == entry->decimal);
> }
>
> int main(int argc, char **argv)
> {
> int i;
>
> for (i = 0; i < ARRAY_SIZE(freqs); i++)
> test_freq(&freqs[i]);
>
> return 0;
> }
>
> Signed-off-by: Damien Lespiau <damien.lespiau@intel.com>
> ---
> drivers/gpu/drm/i915/i915_drv.c | 26 ++++-
> drivers/gpu/drm/i915/i915_drv.h | 1 +
> drivers/gpu/drm/i915/i915_reg.h | 3 +
> drivers/gpu/drm/i915/intel_ddi.c | 2 +
> drivers/gpu/drm/i915/intel_display.c | 208 +++++++++++++++++++++++++++++++++++
> drivers/gpu/drm/i915/intel_drv.h | 2 +
> 6 files changed, 240 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
> index e70adfd..58db0c6 100644
> --- a/drivers/gpu/drm/i915/i915_drv.c
> +++ b/drivers/gpu/drm/i915/i915_drv.c
> @@ -574,6 +574,7 @@ static void intel_suspend_encoders(struct drm_i915_private *dev_priv)
> static int intel_suspend_complete(struct drm_i915_private *dev_priv);
> static int vlv_resume_prepare(struct drm_i915_private *dev_priv,
> bool rpm_resume);
> +static int skl_resume_prepare(struct drm_i915_private *dev_priv);
>
> static int i915_drm_suspend(struct drm_device *dev)
> {
> @@ -781,6 +782,9 @@ static int i915_drm_resume_early(struct drm_device *dev)
>
> if (IS_VALLEYVIEW(dev_priv))
> ret = vlv_resume_prepare(dev_priv, false);
> + else if (IS_SKYLAKE(dev_priv))
> + ret = skl_resume_prepare(dev_priv);
> +
> if (ret)
> DRM_ERROR("Resume prepare failed: %d,Continuing resume\n", ret);
>
> @@ -1009,6 +1013,20 @@ static int hsw_suspend_complete(struct drm_i915_private *dev_priv)
> return 0;
> }
>
> +static int skl_suspend_complete(struct drm_i915_private *dev_priv)
> +{
> + skl_display_suspend(dev_priv);
> +
> + return 0;
> +}
> +
> +static int skl_resume_prepare(struct drm_i915_private *dev_priv)
> +{
> + skl_display_resume(dev_priv);
> +
> + return 0;
> +}
> +
> static int bxt_suspend_complete(struct drm_i915_private *dev_priv)
> {
> struct drm_device *dev = dev_priv->dev;
> @@ -1500,7 +1518,9 @@ static int intel_runtime_resume(struct device *device)
> if (IS_GEN6(dev_priv))
> intel_init_pch_refclk(dev);
>
> - if (IS_BROXTON(dev))
> + if (IS_SKYLAKE(dev))
> + ret = skl_resume_prepare(dev_priv);
> + else if (IS_BROXTON(dev))
> ret = bxt_resume_prepare(dev_priv);
> else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
> hsw_disable_pc8(dev_priv);
> @@ -1534,7 +1554,9 @@ static int intel_suspend_complete(struct drm_i915_private *dev_priv)
> struct drm_device *dev = dev_priv->dev;
> int ret;
>
> - if (IS_BROXTON(dev))
> + if (IS_SKYLAKE(dev))
> + ret = skl_suspend_complete(dev_priv);
> + else if (IS_BROXTON(dev))
> ret = bxt_suspend_complete(dev_priv);
> else if (IS_HASWELL(dev) || IS_BROADWELL(dev))
> ret = hsw_suspend_complete(dev_priv);
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 908c124..f637667 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -1659,6 +1659,7 @@ struct drm_i915_private {
> int num_fence_regs; /* 8 on pre-965, 16 otherwise */
>
> unsigned int fsb_freq, mem_freq, is_ddr3;
> + unsigned int skl_boot_cdclk;
> unsigned int cdclk_freq;
> unsigned int hpll_freq;
>
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 6d428a5..4b063a8 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -6647,6 +6647,9 @@ enum skl_disp_power_wells {
> #define GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT 8
> #define GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT 16
> #define GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT 24
> +#define SKL_PCODE_CDCLK_CONTROL 0x7
> +#define SKL_CDCLK_PREPARE_FOR_CHANGE 0x3
> +#define SKL_CDCLK_READY_FOR_CHANGE 0x1
> #define GEN6_PCODE_WRITE_MIN_FREQ_TABLE 0x8
> #define GEN6_PCODE_READ_MIN_FREQ_TABLE 0x9
> #define GEN6_READ_OC_PARAMS 0xc
> diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
> index d5bee8b..f76bcd3 100644
> --- a/drivers/gpu/drm/i915/intel_ddi.c
> +++ b/drivers/gpu/drm/i915/intel_ddi.c
> @@ -2481,6 +2481,8 @@ void intel_ddi_pll_init(struct drm_device *dev)
> if (IS_SKYLAKE(dev)) {
> if (!(I915_READ(LCPLL1_CTL) & LCPLL_PLL_ENABLE))
> DRM_ERROR("LCPLL1 is disabled\n");
> + else
> + intel_display_power_get(dev_priv, POWER_DOMAIN_PLLS);
> } else if (IS_BROXTON(dev)) {
> broxton_init_cdclk(dev);
> broxton_ddi_phy_init(dev);
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index f2f4ad5..9c8338f 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -5420,6 +5420,213 @@ void broxton_uninit_cdclk(struct drm_device *dev)
> intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
> }
>
> +/*
> + * For now, we store the CDCLK frequency set by the BIOS and restore it at
> + * resume.
> + */
> +static void skl_init_cdclk(struct drm_i915_private *dev_priv)
> +{
> + if (!IS_SKYLAKE(dev_priv))
> + return;
> +
> + dev_priv->skl_boot_cdclk =
> + dev_priv->display.get_display_clock_speed(dev_priv->dev);
> +
> + DRM_DEBUG_DRIVER("CDCLK: %d kHz\n", dev_priv->skl_boot_cdclk);
> +}
The naming is a bit confusing wrt. bxt now.
So it looks like broxton_init_cdclk() == skl_display_resume(),
and skl_init_cdclk() just reads out the current setting and stashes it
somewhere, and there's no counterpart for that on bxt. Would be nice to
unify a bit to avoid loads of confusion.
> +
> +static const struct skl_cdclk_entry {
> + unsigned int freq;
> + unsigned int vco;
> +} skl_cdclk_frequencies[] = {
> + { .freq = 308570, .vco = 8640 },
> + { .freq = 337500, .vco = 8100 },
> + { .freq = 432000, .vco = 8640 },
> + { .freq = 450000, .vco = 8100 },
> + { .freq = 540000, .vco = 8100 },
> + { .freq = 617140, .vco = 8640 },
> + { .freq = 675000, .vco = 8100 },
> +};
> +
> +static unsigned int skl_cdclk_get_vco(unsigned int freq)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(skl_cdclk_frequencies); i++) {
> + const struct skl_cdclk_entry *e = &skl_cdclk_frequencies[i];
> +
> + if (e->freq == freq)
> + return e->vco;
> + }
> +
> + return 8100;
> +}
> +
> +static unsigned int skl_cdlck_decimal(unsigned int freq)
> +{
> + unsigned int mhz, dot5;
> +
> + mhz = freq / 1000;
> + dot5 = (freq - mhz * 1000) >= 500;
> +
> + return (mhz - 1) << 1 | dot5;
> +}
Just '(freq - 1000) / 500' like we do for bxt?
> +
> +static void
> +skl_dpll0_enable(struct drm_i915_private *dev_priv, unsigned int required_vco)
> +{
> + unsigned int min_freq;
> + u32 val;
> +
> + /* select the minimum CDCLK before enabling DPLL 0 */
> + val = I915_READ(CDCLK_CTL);
> + val &= ~CDCLK_FREQ_SEL_MASK | ~CDCLK_FREQ_DECIMAL_MASK;
> + val |= CDCLK_FREQ_337_308;
> +
> + if (required_vco == 8640)
> + min_freq = 308570;
> + else
> + min_freq = 337500;
> +
> + val = CDCLK_FREQ_337_308 | skl_cdlck_decimal(min_freq);
> +
> + I915_WRITE(CDCLK_CTL, val);
> + POSTING_READ(CDCLK_CTL);
> +
> + /*
> + * We always enable DPLL0 with the lowest link rate possible, but still
> + * taking into account the VCO required to operate the eDP panel at the
> + * desired frequency. The usual DP link rates operate with a VCO of
> + * 8100 while the eDP 1.4 alternate link rates need a VCO of 8640.
> + * The modeset code is responsible for the selection of the exact link
> + * rate later on, with the constraint of choosing a frequency that
> + * works with required_vco.
> + */
> + val = I915_READ(DPLL_CTRL1);
> +
> + val &= ~(DPLL_CTRL1_HDMI_MODE(0) | DPLL_CTRL1_SSC(0) |
> + DPLL_CTRL1_LINK_RATE_MASK(0));
> + val |= DPLL_CTRL1_OVERRIDE(0);
> + if (required_vco == 8640)
> + val |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1080, 0);
> + else
> + val |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, 0);
Hmm. These new pll registers are very confusing. But looks correct
based on my understanding.
> +
> + I915_WRITE(DPLL_CTRL1, val);
> + POSTING_READ(DPLL_CTRL1);
> +
> + I915_WRITE(LCPLL1_CTL, I915_READ(LCPLL1_CTL) | LCPLL_PLL_ENABLE);
> +
> + if (wait_for(I915_READ(LCPLL1_CTL) & (1 << 30), 5))
LCPLL_PLL_LOCK
> + DRM_ERROR("DPLL0 not locked\n");
> +}
> +
> +static void skl_set_cdclk(struct drm_i915_private *dev_priv, unsigned int freq)
> +{
> + int ret;
> + u32 val, freq_select, freq_decimal, pcu_ack;
> +
> + DRM_DEBUG_DRIVER("Changing CDCLK to %dKHz\n", freq);
> +
> + /* inform PCU we want to change CDCLK */
> + val = SKL_CDCLK_PREPARE_FOR_CHANGE;
> + mutex_lock(&dev_priv->rps.hw_lock);
> + ret = sandybridge_pcode_read(dev_priv, SKL_PCODE_CDCLK_CONTROL, &val);
> + mutex_unlock(&dev_priv->rps.hw_lock);
> + if (ret || !(val & SKL_CDCLK_READY_FOR_CHANGE)) {
> + DRM_ERROR("failed to inform PCU about cdclk change\n");
> + return;
> + }
Spec says we should keep repeating this until we get the
SKL_CDCLK_READY_FOR_CHANGE bit or 150us timeout has passed. The code
however will only do it once.
> +
> + /* set CDCLK_CTL */
> + switch(freq) {
> + case 450000:
> + case 432000:
> + freq_select = CDCLK_FREQ_450_432;
> + pcu_ack = 1;
> + break;
> + case 540000:
> + freq_select = CDCLK_FREQ_540;
> + pcu_ack = 2;
> + break;
> + case 308570:
> + case 337500:
> + default:
> + freq_select = CDCLK_FREQ_337_308;
> + pcu_ack = 0;
> + break;
> + case 617140:
> + case 675000:
> + freq_select = CDCLK_FREQ_675_617;
> + pcu_ack = 3;
> + break;
> + }
> +
> + freq_decimal = skl_cdlck_decimal(freq);
> +
> + I915_WRITE(CDCLK_CTL, freq_select | freq_decimal);
> + POSTING_READ(CDCLK_CTL);
> +
> + /* inform PCU of the change */
> + mutex_lock(&dev_priv->rps.hw_lock);
> + sandybridge_pcode_write(dev_priv, SKL_PCODE_CDCLK_CONTROL, pcu_ack);
> + mutex_unlock(&dev_priv->rps.hw_lock);
> +}
> +
> +void skl_display_suspend(struct drm_i915_private *dev_priv)
> +{
> + /* disable DBUF power */
> + I915_WRITE(DBUF_CTL, I915_READ(DBUF_CTL) & ~DBUF_POWER_REQUEST);
> + POSTING_READ(DBUF_CTL);
> +
> + udelay(10);
> +
> + if (I915_READ(DBUF_CTL) & DBUF_POWER_STATE)
> + DRM_ERROR("DBuf power disable timeout\n");
> +
> + /* disable DPLL0 */
> + I915_WRITE(LCPLL1_CTL, I915_READ(LCPLL1_CTL) & ~LCPLL_PLL_ENABLE);
> + if (wait_for(!(I915_READ(LCPLL1_CTL) & (1 << 30)), 1))
LCPLL_PLL_LOCK
> + DRM_ERROR("Couldn't disable DPLL0\n");
> +
> + intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS);
> +}
> +
> +void skl_display_resume(struct drm_i915_private *dev_priv)
> +{
> + u32 val;
> + unsigned int required_vco;
> +
> + /* enable PCH reset handshake */
> + val = I915_READ(HSW_NDE_RSTWRN_OPT);
> + I915_WRITE(HSW_NDE_RSTWRN_OPT, val & RESET_PCH_HANDSHAKE_ENABLE);
s/&/|/
> +
> + /* enable PG1 and Misc I/O */
> + intel_display_power_get(dev_priv, POWER_DOMAIN_PLLS);
> +
> + /* DPLL0 already enabed !? */
> + if (I915_READ(LCPLL1_CTL) & LCPLL_PLL_ENABLE) {
> + DRM_DEBUG_DRIVER("DPLL0 already running\n");
> + return;
> + }
> +
> + /* enable DPLL0 */
> + required_vco = skl_cdclk_get_vco(dev_priv->skl_boot_cdclk);
> + skl_dpll0_enable(dev_priv, required_vco);
> +
> + /* set CDCLK to the frequency the BIOS chose */
> + skl_set_cdclk(dev_priv, dev_priv->skl_boot_cdclk);
> +
> + /* enable DBUF power */
> + I915_WRITE(DBUF_CTL, I915_READ(DBUF_CTL) | DBUF_POWER_REQUEST);
> + POSTING_READ(DBUF_CTL);
> +
> + udelay(10);
> +
> + if (!(I915_READ(DBUF_CTL) & DBUF_POWER_STATE))
> + DRM_ERROR("DBuf power enable timeout\n");
> +}
> +
> /* returns HPLL frequency in kHz */
> static int valleyview_get_vco(struct drm_i915_private *dev_priv)
> {
> @@ -14573,6 +14780,7 @@ void intel_modeset_init(struct drm_device *dev)
>
> intel_init_dpio(dev);
>
> + skl_init_cdclk(dev_priv);
Maybe just stick that into the ddi pll init code?
> intel_shared_dpll_init(dev);
>
> /* Just disable it once at startup */
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 43fe003..67275a1 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -1123,6 +1123,8 @@ void broxton_ddi_phy_init(struct drm_device *dev);
> void broxton_ddi_phy_uninit(struct drm_device *dev);
> void bxt_enable_dc9(struct drm_i915_private *dev_priv);
> void bxt_disable_dc9(struct drm_i915_private *dev_priv);
> +void skl_display_suspend(struct drm_i915_private *dev_priv);
> +void skl_display_resume(struct drm_i915_private *dev_priv);
> void intel_dp_get_m_n(struct intel_crtc *crtc,
> struct intel_crtc_state *pipe_config);
> void intel_dp_set_m_n(struct intel_crtc *crtc, enum link_m_n_set m_n);
> --
> 2.1.0
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
--
Ville Syrjälä
Intel OTC
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx
next prev parent reply other threads:[~2015-05-05 18:56 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-04-30 15:39 [PATCH 0/8] SKL suspend/resume Damien Lespiau
2015-04-30 15:39 ` [PATCH 1/8] drm/i915/skl: Add the INIT power domain to the MISC I/O power well Damien Lespiau
2015-05-05 18:54 ` Ville Syrjälä
2015-04-30 15:39 ` [PATCH 2/8] drm/i915/skl: Fix the CTRL typo in the DPLL_CRTL1 defines Damien Lespiau
2015-04-30 15:39 ` [PATCH 3/8] drm/i915: Re-order the PCU opcodes Damien Lespiau
2015-04-30 15:39 ` [PATCH 4/8] drm/i915: Merge the GEN9 memory latency PCU opcode with its friends Damien Lespiau
2015-05-05 18:54 ` Ville Syrjälä
2015-04-30 15:39 ` [PATCH 5/8] drm/i915/skl: Make the Misc I/O power well part of the PLLS domain Damien Lespiau
2015-05-05 18:55 ` Ville Syrjälä
2015-04-30 15:39 ` [PATCH 6/8] drm/i915/skl: Deinit/init the display at suspend/resume Damien Lespiau
2015-05-05 18:56 ` Ville Syrjälä [this message]
2015-05-06 11:10 ` Ville Syrjälä
2015-05-06 10:52 ` Daniel Vetter
2015-04-30 15:39 ` [PATCH 7/8] drm/i915/skl: Change CDCLK behind PCU's back Damien Lespiau
2015-05-06 10:53 ` Daniel Vetter
2015-05-06 10:58 ` Damien Lespiau
2015-04-30 15:39 ` [PATCH 8/8] drm/i915/skl: gen6+ platforms support runtime PM Damien Lespiau
2015-05-02 8:20 ` shuang.he
2015-05-05 18:56 ` Ville Syrjälä
2015-05-06 10:54 ` Daniel Vetter
2015-05-06 10:55 ` Damien Lespiau
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20150505185602.GN18908@intel.com \
--to=ville.syrjala@linux.intel.com \
--cc=damien.lespiau@intel.com \
--cc=intel-gfx@lists.freedesktop.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.