[Intel-gfx] [PATCH 1/9] drm/i915: Move PCH refclok stuff into its own file
David Airlie
airlied at redhat.com
Sun Oct 17 23:56:33 UTC 2021
On Fri, Oct 15, 2021 at 5:16 PM Ville Syrjala
<ville.syrjala at linux.intel.com> wrote:
>
> From: Ville Syrjälä <ville.syrjala at linux.intel.com>
>
> Move the PCH refclk stuff (including all the LPT/WPT
> iCLKIP/CLKOUT_DP things) to its own file.
>
> We also suck in the mPHY programming from intel_fdi.c
> since we're the only caller.
The title of the patch has a typo refclok->reclock.
Other than that this looks fine,
Reviewed-by: Dave Airlie <airlied at redhat.com>
>
> Cc: Dave Airlie <airlied at redhat.com>
> Cc: Jani Nikula <jani.nikula at intel.com>
> Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
> ---
> drivers/gpu/drm/i915/Makefile | 1 +
> drivers/gpu/drm/i915/display/intel_crt.c | 1 +
> drivers/gpu/drm/i915/display/intel_display.c | 537 +--------------
> drivers/gpu/drm/i915/display/intel_display.h | 4 -
> .../drm/i915/display/intel_display_power.c | 1 +
> drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 1 +
> drivers/gpu/drm/i915/display/intel_fdi.c | 99 ---
> drivers/gpu/drm/i915/display/intel_fdi.h | 1 -
> .../gpu/drm/i915/display/intel_pch_refclk.c | 648 ++++++++++++++++++
> .../gpu/drm/i915/display/intel_pch_refclk.h | 21 +
> drivers/gpu/drm/i915/i915_drv.c | 1 +
> 11 files changed, 675 insertions(+), 640 deletions(-)
> create mode 100644 drivers/gpu/drm/i915/display/intel_pch_refclk.c
> create mode 100644 drivers/gpu/drm/i915/display/intel_pch_refclk.h
>
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index 660bb03de6fc..96f3b8f6c50d 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -226,6 +226,7 @@ i915-y += \
> display/intel_hotplug.o \
> display/intel_lpe_audio.o \
> display/intel_overlay.o \
> + display/intel_pch_refclk.o \
> display/intel_plane_initial.o \
> display/intel_psr.o \
> display/intel_quirks.o \
> diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c
> index 1c161eeed82f..bf03bd0ecd43 100644
> --- a/drivers/gpu/drm/i915/display/intel_crt.c
> +++ b/drivers/gpu/drm/i915/display/intel_crt.c
> @@ -45,6 +45,7 @@
> #include "intel_fifo_underrun.h"
> #include "intel_gmbus.h"
> #include "intel_hotplug.h"
> +#include "intel_pch_refclk.h"
>
> /* Here's the desired hotplug mode */
> #define ADPA_HOTPLUG_BITS (ADPA_CRT_HOTPLUG_PERIOD_128 | \
> diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
> index ff598b6cd953..995050443065 100644
> --- a/drivers/gpu/drm/i915/display/intel_display.c
> +++ b/drivers/gpu/drm/i915/display/intel_display.c
> @@ -96,6 +96,7 @@
> #include "intel_hotplug.h"
> #include "intel_overlay.h"
> #include "intel_panel.h"
> +#include "intel_pch_refclk.h"
> #include "intel_pcode.h"
> #include "intel_pipe_crc.h"
> #include "intel_plane_initial.h"
> @@ -103,7 +104,6 @@
> #include "intel_pps.h"
> #include "intel_psr.h"
> #include "intel_quirks.h"
> -#include "intel_sbi.h"
> #include "intel_sprite.h"
> #include "intel_tc.h"
> #include "intel_vga.h"
> @@ -1388,133 +1388,6 @@ bool intel_has_pending_fb_unpin(struct drm_i915_private *dev_priv)
> return false;
> }
>
> -void lpt_disable_iclkip(struct drm_i915_private *dev_priv)
> -{
> - u32 temp;
> -
> - intel_de_write(dev_priv, PIXCLK_GATE, PIXCLK_GATE_GATE);
> -
> - mutex_lock(&dev_priv->sb_lock);
> -
> - temp = intel_sbi_read(dev_priv, SBI_SSCCTL6, SBI_ICLK);
> - temp |= SBI_SSCCTL_DISABLE;
> - intel_sbi_write(dev_priv, SBI_SSCCTL6, temp, SBI_ICLK);
> -
> - mutex_unlock(&dev_priv->sb_lock);
> -}
> -
> -/* Program iCLKIP clock to the desired frequency */
> -static void lpt_program_iclkip(const struct intel_crtc_state *crtc_state)
> -{
> - struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
> - struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
> - int clock = crtc_state->hw.adjusted_mode.crtc_clock;
> - u32 divsel, phaseinc, auxdiv, phasedir = 0;
> - u32 temp;
> -
> - lpt_disable_iclkip(dev_priv);
> -
> - /* The iCLK virtual clock root frequency is in MHz,
> - * but the adjusted_mode->crtc_clock in in KHz. To get the
> - * divisors, it is necessary to divide one by another, so we
> - * convert the virtual clock precision to KHz here for higher
> - * precision.
> - */
> - for (auxdiv = 0; auxdiv < 2; auxdiv++) {
> - u32 iclk_virtual_root_freq = 172800 * 1000;
> - u32 iclk_pi_range = 64;
> - u32 desired_divisor;
> -
> - desired_divisor = DIV_ROUND_CLOSEST(iclk_virtual_root_freq,
> - clock << auxdiv);
> - divsel = (desired_divisor / iclk_pi_range) - 2;
> - phaseinc = desired_divisor % iclk_pi_range;
> -
> - /*
> - * Near 20MHz is a corner case which is
> - * out of range for the 7-bit divisor
> - */
> - if (divsel <= 0x7f)
> - break;
> - }
> -
> - /* This should not happen with any sane values */
> - drm_WARN_ON(&dev_priv->drm, SBI_SSCDIVINTPHASE_DIVSEL(divsel) &
> - ~SBI_SSCDIVINTPHASE_DIVSEL_MASK);
> - drm_WARN_ON(&dev_priv->drm, SBI_SSCDIVINTPHASE_DIR(phasedir) &
> - ~SBI_SSCDIVINTPHASE_INCVAL_MASK);
> -
> - drm_dbg_kms(&dev_priv->drm,
> - "iCLKIP clock: found settings for %dKHz refresh rate: auxdiv=%x, divsel=%x, phasedir=%x, phaseinc=%x\n",
> - clock, auxdiv, divsel, phasedir, phaseinc);
> -
> - mutex_lock(&dev_priv->sb_lock);
> -
> - /* Program SSCDIVINTPHASE6 */
> - temp = intel_sbi_read(dev_priv, SBI_SSCDIVINTPHASE6, SBI_ICLK);
> - temp &= ~SBI_SSCDIVINTPHASE_DIVSEL_MASK;
> - temp |= SBI_SSCDIVINTPHASE_DIVSEL(divsel);
> - temp &= ~SBI_SSCDIVINTPHASE_INCVAL_MASK;
> - temp |= SBI_SSCDIVINTPHASE_INCVAL(phaseinc);
> - temp |= SBI_SSCDIVINTPHASE_DIR(phasedir);
> - temp |= SBI_SSCDIVINTPHASE_PROPAGATE;
> - intel_sbi_write(dev_priv, SBI_SSCDIVINTPHASE6, temp, SBI_ICLK);
> -
> - /* Program SSCAUXDIV */
> - temp = intel_sbi_read(dev_priv, SBI_SSCAUXDIV6, SBI_ICLK);
> - temp &= ~SBI_SSCAUXDIV_FINALDIV2SEL(1);
> - temp |= SBI_SSCAUXDIV_FINALDIV2SEL(auxdiv);
> - intel_sbi_write(dev_priv, SBI_SSCAUXDIV6, temp, SBI_ICLK);
> -
> - /* Enable modulator and associated divider */
> - temp = intel_sbi_read(dev_priv, SBI_SSCCTL6, SBI_ICLK);
> - temp &= ~SBI_SSCCTL_DISABLE;
> - intel_sbi_write(dev_priv, SBI_SSCCTL6, temp, SBI_ICLK);
> -
> - mutex_unlock(&dev_priv->sb_lock);
> -
> - /* Wait for initialization time */
> - udelay(24);
> -
> - intel_de_write(dev_priv, PIXCLK_GATE, PIXCLK_GATE_UNGATE);
> -}
> -
> -int lpt_get_iclkip(struct drm_i915_private *dev_priv)
> -{
> - u32 divsel, phaseinc, auxdiv;
> - u32 iclk_virtual_root_freq = 172800 * 1000;
> - u32 iclk_pi_range = 64;
> - u32 desired_divisor;
> - u32 temp;
> -
> - if ((intel_de_read(dev_priv, PIXCLK_GATE) & PIXCLK_GATE_UNGATE) == 0)
> - return 0;
> -
> - mutex_lock(&dev_priv->sb_lock);
> -
> - temp = intel_sbi_read(dev_priv, SBI_SSCCTL6, SBI_ICLK);
> - if (temp & SBI_SSCCTL_DISABLE) {
> - mutex_unlock(&dev_priv->sb_lock);
> - return 0;
> - }
> -
> - temp = intel_sbi_read(dev_priv, SBI_SSCDIVINTPHASE6, SBI_ICLK);
> - divsel = (temp & SBI_SSCDIVINTPHASE_DIVSEL_MASK) >>
> - SBI_SSCDIVINTPHASE_DIVSEL_SHIFT;
> - phaseinc = (temp & SBI_SSCDIVINTPHASE_INCVAL_MASK) >>
> - SBI_SSCDIVINTPHASE_INCVAL_SHIFT;
> -
> - temp = intel_sbi_read(dev_priv, SBI_SSCAUXDIV6, SBI_ICLK);
> - auxdiv = (temp & SBI_SSCAUXDIV_FINALDIV2SEL_MASK) >>
> - SBI_SSCAUXDIV_FINALDIV2SEL_SHIFT;
> -
> - mutex_unlock(&dev_priv->sb_lock);
> -
> - desired_divisor = (divsel + 2) * iclk_pi_range + phaseinc;
> -
> - return DIV_ROUND_CLOSEST(iclk_virtual_root_freq,
> - desired_divisor << auxdiv);
> -}
>
> static void ilk_pch_transcoder_set_timings(const struct intel_crtc_state *crtc_state,
> enum pipe pch_transcoder)
> @@ -4299,414 +4172,6 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
> return ret;
> }
>
> -static void ilk_init_pch_refclk(struct drm_i915_private *dev_priv)
> -{
> - struct intel_encoder *encoder;
> - int i;
> - u32 val, final;
> - bool has_lvds = false;
> - bool has_cpu_edp = false;
> - bool has_panel = false;
> - bool has_ck505 = false;
> - bool can_ssc = false;
> - bool using_ssc_source = false;
> -
> - /* We need to take the global config into account */
> - for_each_intel_encoder(&dev_priv->drm, encoder) {
> - switch (encoder->type) {
> - case INTEL_OUTPUT_LVDS:
> - has_panel = true;
> - has_lvds = true;
> - break;
> - case INTEL_OUTPUT_EDP:
> - has_panel = true;
> - if (encoder->port == PORT_A)
> - has_cpu_edp = true;
> - break;
> - default:
> - break;
> - }
> - }
> -
> - if (HAS_PCH_IBX(dev_priv)) {
> - has_ck505 = dev_priv->vbt.display_clock_mode;
> - can_ssc = has_ck505;
> - } else {
> - has_ck505 = false;
> - can_ssc = true;
> - }
> -
> - /* Check if any DPLLs are using the SSC source */
> - for (i = 0; i < dev_priv->dpll.num_shared_dpll; i++) {
> - u32 temp = intel_de_read(dev_priv, PCH_DPLL(i));
> -
> - if (!(temp & DPLL_VCO_ENABLE))
> - continue;
> -
> - if ((temp & PLL_REF_INPUT_MASK) ==
> - PLLB_REF_INPUT_SPREADSPECTRUMIN) {
> - using_ssc_source = true;
> - break;
> - }
> - }
> -
> - drm_dbg_kms(&dev_priv->drm,
> - "has_panel %d has_lvds %d has_ck505 %d using_ssc_source %d\n",
> - has_panel, has_lvds, has_ck505, using_ssc_source);
> -
> - /* Ironlake: try to setup display ref clock before DPLL
> - * enabling. This is only under driver's control after
> - * PCH B stepping, previous chipset stepping should be
> - * ignoring this setting.
> - */
> - val = intel_de_read(dev_priv, PCH_DREF_CONTROL);
> -
> - /* As we must carefully and slowly disable/enable each source in turn,
> - * compute the final state we want first and check if we need to
> - * make any changes at all.
> - */
> - final = val;
> - final &= ~DREF_NONSPREAD_SOURCE_MASK;
> - if (has_ck505)
> - final |= DREF_NONSPREAD_CK505_ENABLE;
> - else
> - final |= DREF_NONSPREAD_SOURCE_ENABLE;
> -
> - final &= ~DREF_SSC_SOURCE_MASK;
> - final &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
> - final &= ~DREF_SSC1_ENABLE;
> -
> - if (has_panel) {
> - final |= DREF_SSC_SOURCE_ENABLE;
> -
> - if (intel_panel_use_ssc(dev_priv) && can_ssc)
> - final |= DREF_SSC1_ENABLE;
> -
> - if (has_cpu_edp) {
> - if (intel_panel_use_ssc(dev_priv) && can_ssc)
> - final |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
> - else
> - final |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
> - } else
> - final |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
> - } else if (using_ssc_source) {
> - final |= DREF_SSC_SOURCE_ENABLE;
> - final |= DREF_SSC1_ENABLE;
> - }
> -
> - if (final == val)
> - return;
> -
> - /* Always enable nonspread source */
> - val &= ~DREF_NONSPREAD_SOURCE_MASK;
> -
> - if (has_ck505)
> - val |= DREF_NONSPREAD_CK505_ENABLE;
> - else
> - val |= DREF_NONSPREAD_SOURCE_ENABLE;
> -
> - if (has_panel) {
> - val &= ~DREF_SSC_SOURCE_MASK;
> - val |= DREF_SSC_SOURCE_ENABLE;
> -
> - /* SSC must be turned on before enabling the CPU output */
> - if (intel_panel_use_ssc(dev_priv) && can_ssc) {
> - drm_dbg_kms(&dev_priv->drm, "Using SSC on panel\n");
> - val |= DREF_SSC1_ENABLE;
> - } else
> - val &= ~DREF_SSC1_ENABLE;
> -
> - /* Get SSC going before enabling the outputs */
> - intel_de_write(dev_priv, PCH_DREF_CONTROL, val);
> - intel_de_posting_read(dev_priv, PCH_DREF_CONTROL);
> - udelay(200);
> -
> - val &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
> -
> - /* Enable CPU source on CPU attached eDP */
> - if (has_cpu_edp) {
> - if (intel_panel_use_ssc(dev_priv) && can_ssc) {
> - drm_dbg_kms(&dev_priv->drm,
> - "Using SSC on eDP\n");
> - val |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
> - } else
> - val |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
> - } else
> - val |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
> -
> - intel_de_write(dev_priv, PCH_DREF_CONTROL, val);
> - intel_de_posting_read(dev_priv, PCH_DREF_CONTROL);
> - udelay(200);
> - } else {
> - drm_dbg_kms(&dev_priv->drm, "Disabling CPU source output\n");
> -
> - val &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
> -
> - /* Turn off CPU output */
> - val |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
> -
> - intel_de_write(dev_priv, PCH_DREF_CONTROL, val);
> - intel_de_posting_read(dev_priv, PCH_DREF_CONTROL);
> - udelay(200);
> -
> - if (!using_ssc_source) {
> - drm_dbg_kms(&dev_priv->drm, "Disabling SSC source\n");
> -
> - /* Turn off the SSC source */
> - val &= ~DREF_SSC_SOURCE_MASK;
> - val |= DREF_SSC_SOURCE_DISABLE;
> -
> - /* Turn off SSC1 */
> - val &= ~DREF_SSC1_ENABLE;
> -
> - intel_de_write(dev_priv, PCH_DREF_CONTROL, val);
> - intel_de_posting_read(dev_priv, PCH_DREF_CONTROL);
> - udelay(200);
> - }
> - }
> -
> - BUG_ON(val != final);
> -}
> -
> -/* Implements 3 different sequences from BSpec chapter "Display iCLK
> - * Programming" based on the parameters passed:
> - * - Sequence to enable CLKOUT_DP
> - * - Sequence to enable CLKOUT_DP without spread
> - * - Sequence to enable CLKOUT_DP for FDI usage and configure PCH FDI I/O
> - */
> -static void lpt_enable_clkout_dp(struct drm_i915_private *dev_priv,
> - bool with_spread, bool with_fdi)
> -{
> - u32 reg, tmp;
> -
> - if (drm_WARN(&dev_priv->drm, with_fdi && !with_spread,
> - "FDI requires downspread\n"))
> - with_spread = true;
> - if (drm_WARN(&dev_priv->drm, HAS_PCH_LPT_LP(dev_priv) &&
> - with_fdi, "LP PCH doesn't have FDI\n"))
> - with_fdi = false;
> -
> - mutex_lock(&dev_priv->sb_lock);
> -
> - tmp = intel_sbi_read(dev_priv, SBI_SSCCTL, SBI_ICLK);
> - tmp &= ~SBI_SSCCTL_DISABLE;
> - tmp |= SBI_SSCCTL_PATHALT;
> - intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
> -
> - udelay(24);
> -
> - if (with_spread) {
> - tmp = intel_sbi_read(dev_priv, SBI_SSCCTL, SBI_ICLK);
> - tmp &= ~SBI_SSCCTL_PATHALT;
> - intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
> -
> - if (with_fdi)
> - lpt_fdi_program_mphy(dev_priv);
> - }
> -
> - reg = HAS_PCH_LPT_LP(dev_priv) ? SBI_GEN0 : SBI_DBUFF0;
> - tmp = intel_sbi_read(dev_priv, reg, SBI_ICLK);
> - tmp |= SBI_GEN0_CFG_BUFFENABLE_DISABLE;
> - intel_sbi_write(dev_priv, reg, tmp, SBI_ICLK);
> -
> - mutex_unlock(&dev_priv->sb_lock);
> -}
> -
> -/* Sequence to disable CLKOUT_DP */
> -void lpt_disable_clkout_dp(struct drm_i915_private *dev_priv)
> -{
> - u32 reg, tmp;
> -
> - mutex_lock(&dev_priv->sb_lock);
> -
> - reg = HAS_PCH_LPT_LP(dev_priv) ? SBI_GEN0 : SBI_DBUFF0;
> - tmp = intel_sbi_read(dev_priv, reg, SBI_ICLK);
> - tmp &= ~SBI_GEN0_CFG_BUFFENABLE_DISABLE;
> - intel_sbi_write(dev_priv, reg, tmp, SBI_ICLK);
> -
> - tmp = intel_sbi_read(dev_priv, SBI_SSCCTL, SBI_ICLK);
> - if (!(tmp & SBI_SSCCTL_DISABLE)) {
> - if (!(tmp & SBI_SSCCTL_PATHALT)) {
> - tmp |= SBI_SSCCTL_PATHALT;
> - intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
> - udelay(32);
> - }
> - tmp |= SBI_SSCCTL_DISABLE;
> - intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
> - }
> -
> - mutex_unlock(&dev_priv->sb_lock);
> -}
> -
> -#define BEND_IDX(steps) ((50 + (steps)) / 5)
> -
> -static const u16 sscdivintphase[] = {
> - [BEND_IDX( 50)] = 0x3B23,
> - [BEND_IDX( 45)] = 0x3B23,
> - [BEND_IDX( 40)] = 0x3C23,
> - [BEND_IDX( 35)] = 0x3C23,
> - [BEND_IDX( 30)] = 0x3D23,
> - [BEND_IDX( 25)] = 0x3D23,
> - [BEND_IDX( 20)] = 0x3E23,
> - [BEND_IDX( 15)] = 0x3E23,
> - [BEND_IDX( 10)] = 0x3F23,
> - [BEND_IDX( 5)] = 0x3F23,
> - [BEND_IDX( 0)] = 0x0025,
> - [BEND_IDX( -5)] = 0x0025,
> - [BEND_IDX(-10)] = 0x0125,
> - [BEND_IDX(-15)] = 0x0125,
> - [BEND_IDX(-20)] = 0x0225,
> - [BEND_IDX(-25)] = 0x0225,
> - [BEND_IDX(-30)] = 0x0325,
> - [BEND_IDX(-35)] = 0x0325,
> - [BEND_IDX(-40)] = 0x0425,
> - [BEND_IDX(-45)] = 0x0425,
> - [BEND_IDX(-50)] = 0x0525,
> -};
> -
> -/*
> - * Bend CLKOUT_DP
> - * steps -50 to 50 inclusive, in steps of 5
> - * < 0 slow down the clock, > 0 speed up the clock, 0 == no bend (135MHz)
> - * change in clock period = -(steps / 10) * 5.787 ps
> - */
> -static void lpt_bend_clkout_dp(struct drm_i915_private *dev_priv, int steps)
> -{
> - u32 tmp;
> - int idx = BEND_IDX(steps);
> -
> - if (drm_WARN_ON(&dev_priv->drm, steps % 5 != 0))
> - return;
> -
> - if (drm_WARN_ON(&dev_priv->drm, idx >= ARRAY_SIZE(sscdivintphase)))
> - return;
> -
> - mutex_lock(&dev_priv->sb_lock);
> -
> - if (steps % 10 != 0)
> - tmp = 0xAAAAAAAB;
> - else
> - tmp = 0x00000000;
> - intel_sbi_write(dev_priv, SBI_SSCDITHPHASE, tmp, SBI_ICLK);
> -
> - tmp = intel_sbi_read(dev_priv, SBI_SSCDIVINTPHASE, SBI_ICLK);
> - tmp &= 0xffff0000;
> - tmp |= sscdivintphase[idx];
> - intel_sbi_write(dev_priv, SBI_SSCDIVINTPHASE, tmp, SBI_ICLK);
> -
> - mutex_unlock(&dev_priv->sb_lock);
> -}
> -
> -#undef BEND_IDX
> -
> -static bool spll_uses_pch_ssc(struct drm_i915_private *dev_priv)
> -{
> - u32 fuse_strap = intel_de_read(dev_priv, FUSE_STRAP);
> - u32 ctl = intel_de_read(dev_priv, SPLL_CTL);
> -
> - if ((ctl & SPLL_PLL_ENABLE) == 0)
> - return false;
> -
> - if ((ctl & SPLL_REF_MASK) == SPLL_REF_MUXED_SSC &&
> - (fuse_strap & HSW_CPU_SSC_ENABLE) == 0)
> - return true;
> -
> - if (IS_BROADWELL(dev_priv) &&
> - (ctl & SPLL_REF_MASK) == SPLL_REF_PCH_SSC_BDW)
> - return true;
> -
> - return false;
> -}
> -
> -static bool wrpll_uses_pch_ssc(struct drm_i915_private *dev_priv,
> - enum intel_dpll_id id)
> -{
> - u32 fuse_strap = intel_de_read(dev_priv, FUSE_STRAP);
> - u32 ctl = intel_de_read(dev_priv, WRPLL_CTL(id));
> -
> - if ((ctl & WRPLL_PLL_ENABLE) == 0)
> - return false;
> -
> - if ((ctl & WRPLL_REF_MASK) == WRPLL_REF_PCH_SSC)
> - return true;
> -
> - if ((IS_BROADWELL(dev_priv) || IS_HSW_ULT(dev_priv)) &&
> - (ctl & WRPLL_REF_MASK) == WRPLL_REF_MUXED_SSC_BDW &&
> - (fuse_strap & HSW_CPU_SSC_ENABLE) == 0)
> - return true;
> -
> - return false;
> -}
> -
> -static void lpt_init_pch_refclk(struct drm_i915_private *dev_priv)
> -{
> - struct intel_encoder *encoder;
> - bool has_fdi = false;
> -
> - for_each_intel_encoder(&dev_priv->drm, encoder) {
> - switch (encoder->type) {
> - case INTEL_OUTPUT_ANALOG:
> - has_fdi = true;
> - break;
> - default:
> - break;
> - }
> - }
> -
> - /*
> - * The BIOS may have decided to use the PCH SSC
> - * reference so we must not disable it until the
> - * relevant PLLs have stopped relying on it. We'll
> - * just leave the PCH SSC reference enabled in case
> - * any active PLL is using it. It will get disabled
> - * after runtime suspend if we don't have FDI.
> - *
> - * TODO: Move the whole reference clock handling
> - * to the modeset sequence proper so that we can
> - * actually enable/disable/reconfigure these things
> - * safely. To do that we need to introduce a real
> - * clock hierarchy. That would also allow us to do
> - * clock bending finally.
> - */
> - dev_priv->pch_ssc_use = 0;
> -
> - if (spll_uses_pch_ssc(dev_priv)) {
> - drm_dbg_kms(&dev_priv->drm, "SPLL using PCH SSC\n");
> - dev_priv->pch_ssc_use |= BIT(DPLL_ID_SPLL);
> - }
> -
> - if (wrpll_uses_pch_ssc(dev_priv, DPLL_ID_WRPLL1)) {
> - drm_dbg_kms(&dev_priv->drm, "WRPLL1 using PCH SSC\n");
> - dev_priv->pch_ssc_use |= BIT(DPLL_ID_WRPLL1);
> - }
> -
> - if (wrpll_uses_pch_ssc(dev_priv, DPLL_ID_WRPLL2)) {
> - drm_dbg_kms(&dev_priv->drm, "WRPLL2 using PCH SSC\n");
> - dev_priv->pch_ssc_use |= BIT(DPLL_ID_WRPLL2);
> - }
> -
> - if (dev_priv->pch_ssc_use)
> - return;
> -
> - if (has_fdi) {
> - lpt_bend_clkout_dp(dev_priv, 0);
> - lpt_enable_clkout_dp(dev_priv, true, true);
> - } else {
> - lpt_disable_clkout_dp(dev_priv);
> - }
> -}
> -
> -/*
> - * Initialize reference clocks when the driver loads
> - */
> -void intel_init_pch_refclk(struct drm_i915_private *dev_priv)
> -{
> - if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv))
> - ilk_init_pch_refclk(dev_priv);
> - else if (HAS_PCH_LPT(dev_priv))
> - lpt_init_pch_refclk(dev_priv);
> -}
> -
> static void ilk_set_pipeconf(const struct intel_crtc_state *crtc_state)
> {
> struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
> diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
> index 0c76bf57f86b..39c18b8807f9 100644
> --- a/drivers/gpu/drm/i915/display/intel_display.h
> +++ b/drivers/gpu/drm/i915/display/intel_display.h
> @@ -521,7 +521,6 @@ void intel_link_compute_m_n(u16 bpp, int nlanes,
> int pixel_clock, int link_clock,
> struct intel_link_m_n *m_n,
> bool constant_n, bool fec_enable);
> -void lpt_disable_clkout_dp(struct drm_i915_private *dev_priv);
> u32 intel_plane_fb_max_stride(struct drm_i915_private *dev_priv,
> u32 pixel_format, u64 modifier);
> enum drm_mode_status
> @@ -544,7 +543,6 @@ int vlv_get_cck_clock_hpll(struct drm_i915_private *dev_priv,
> const char *name, u32 reg);
> void lpt_pch_enable(const struct intel_crtc_state *crtc_state);
> void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv);
> -void lpt_disable_iclkip(struct drm_i915_private *dev_priv);
> void intel_init_display_hooks(struct drm_i915_private *dev_priv);
> unsigned int intel_fb_xy_to_linear(int x, int y,
> const struct intel_plane_state *state,
> @@ -583,7 +581,6 @@ intel_framebuffer_create(struct drm_i915_gem_object *obj,
> void assert_pch_transcoder_disabled(struct drm_i915_private *dev_priv,
> enum pipe pipe);
>
> -int lpt_get_iclkip(struct drm_i915_private *dev_priv);
> bool intel_fuzzy_clock_check(int clock1, int clock2);
>
> void intel_display_prepare_reset(struct drm_i915_private *dev_priv);
> @@ -632,7 +629,6 @@ void intel_modeset_driver_remove(struct drm_i915_private *i915);
> void intel_modeset_driver_remove_noirq(struct drm_i915_private *i915);
> void intel_modeset_driver_remove_nogem(struct drm_i915_private *i915);
> void intel_display_resume(struct drm_device *dev);
> -void intel_init_pch_refclk(struct drm_i915_private *dev_priv);
> int intel_modeset_all_pipes(struct intel_atomic_state *state);
>
> /* modesetting asserts */
> diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c
> index 1672604f9ef7..d88da0d0f05a 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_power.c
> +++ b/drivers/gpu/drm/i915/display/intel_display_power.c
> @@ -15,6 +15,7 @@
> #include "intel_dpio_phy.h"
> #include "intel_dpll.h"
> #include "intel_hotplug.h"
> +#include "intel_pch_refclk.h"
> #include "intel_pcode.h"
> #include "intel_pm.h"
> #include "intel_pps.h"
> diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
> index 0a7e04db04be..ca69b67bbc23 100644
> --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
> +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
> @@ -26,6 +26,7 @@
> #include "intel_dpio_phy.h"
> #include "intel_dpll.h"
> #include "intel_dpll_mgr.h"
> +#include "intel_pch_refclk.h"
> #include "intel_tc.h"
>
> /**
> diff --git a/drivers/gpu/drm/i915/display/intel_fdi.c b/drivers/gpu/drm/i915/display/intel_fdi.c
> index dd2cf0c59921..d1c1600c66cb 100644
> --- a/drivers/gpu/drm/i915/display/intel_fdi.c
> +++ b/drivers/gpu/drm/i915/display/intel_fdi.c
> @@ -8,7 +8,6 @@
> #include "intel_de.h"
> #include "intel_display_types.h"
> #include "intel_fdi.h"
> -#include "intel_sbi.h"
>
> static void assert_fdi_tx(struct drm_i915_private *dev_priv,
> enum pipe pipe, bool state)
> @@ -1006,104 +1005,6 @@ void ilk_fdi_disable(struct intel_crtc *crtc)
> udelay(100);
> }
>
> -static void lpt_fdi_reset_mphy(struct drm_i915_private *dev_priv)
> -{
> - u32 tmp;
> -
> - tmp = intel_de_read(dev_priv, SOUTH_CHICKEN2);
> - tmp |= FDI_MPHY_IOSFSB_RESET_CTL;
> - intel_de_write(dev_priv, SOUTH_CHICKEN2, tmp);
> -
> - if (wait_for_us(intel_de_read(dev_priv, SOUTH_CHICKEN2) &
> - FDI_MPHY_IOSFSB_RESET_STATUS, 100))
> - drm_err(&dev_priv->drm, "FDI mPHY reset assert timeout\n");
> -
> - tmp = intel_de_read(dev_priv, SOUTH_CHICKEN2);
> - tmp &= ~FDI_MPHY_IOSFSB_RESET_CTL;
> - intel_de_write(dev_priv, SOUTH_CHICKEN2, tmp);
> -
> - if (wait_for_us((intel_de_read(dev_priv, SOUTH_CHICKEN2) &
> - FDI_MPHY_IOSFSB_RESET_STATUS) == 0, 100))
> - drm_err(&dev_priv->drm, "FDI mPHY reset de-assert timeout\n");
> -}
> -
> -/* WaMPhyProgramming:hsw */
> -void lpt_fdi_program_mphy(struct drm_i915_private *dev_priv)
> -{
> - u32 tmp;
> -
> - lpt_fdi_reset_mphy(dev_priv);
> -
> - tmp = intel_sbi_read(dev_priv, 0x8008, SBI_MPHY);
> - tmp &= ~(0xFF << 24);
> - tmp |= (0x12 << 24);
> - intel_sbi_write(dev_priv, 0x8008, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x2008, SBI_MPHY);
> - tmp |= (1 << 11);
> - intel_sbi_write(dev_priv, 0x2008, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x2108, SBI_MPHY);
> - tmp |= (1 << 11);
> - intel_sbi_write(dev_priv, 0x2108, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x206C, SBI_MPHY);
> - tmp |= (1 << 24) | (1 << 21) | (1 << 18);
> - intel_sbi_write(dev_priv, 0x206C, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x216C, SBI_MPHY);
> - tmp |= (1 << 24) | (1 << 21) | (1 << 18);
> - intel_sbi_write(dev_priv, 0x216C, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x2080, SBI_MPHY);
> - tmp &= ~(7 << 13);
> - tmp |= (5 << 13);
> - intel_sbi_write(dev_priv, 0x2080, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x2180, SBI_MPHY);
> - tmp &= ~(7 << 13);
> - tmp |= (5 << 13);
> - intel_sbi_write(dev_priv, 0x2180, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x208C, SBI_MPHY);
> - tmp &= ~0xFF;
> - tmp |= 0x1C;
> - intel_sbi_write(dev_priv, 0x208C, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x218C, SBI_MPHY);
> - tmp &= ~0xFF;
> - tmp |= 0x1C;
> - intel_sbi_write(dev_priv, 0x218C, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x2098, SBI_MPHY);
> - tmp &= ~(0xFF << 16);
> - tmp |= (0x1C << 16);
> - intel_sbi_write(dev_priv, 0x2098, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x2198, SBI_MPHY);
> - tmp &= ~(0xFF << 16);
> - tmp |= (0x1C << 16);
> - intel_sbi_write(dev_priv, 0x2198, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x20C4, SBI_MPHY);
> - tmp |= (1 << 27);
> - intel_sbi_write(dev_priv, 0x20C4, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x21C4, SBI_MPHY);
> - tmp |= (1 << 27);
> - intel_sbi_write(dev_priv, 0x21C4, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x20EC, SBI_MPHY);
> - tmp &= ~(0xF << 28);
> - tmp |= (4 << 28);
> - intel_sbi_write(dev_priv, 0x20EC, tmp, SBI_MPHY);
> -
> - tmp = intel_sbi_read(dev_priv, 0x21EC, SBI_MPHY);
> - tmp &= ~(0xF << 28);
> - tmp |= (4 << 28);
> - intel_sbi_write(dev_priv, 0x21EC, tmp, SBI_MPHY);
> -}
> -
> static const struct intel_fdi_funcs ilk_funcs = {
> .fdi_link_train = ilk_fdi_link_train,
> };
> diff --git a/drivers/gpu/drm/i915/display/intel_fdi.h b/drivers/gpu/drm/i915/display/intel_fdi.h
> index 640d6585c137..5a361730f80a 100644
> --- a/drivers/gpu/drm/i915/display/intel_fdi.h
> +++ b/drivers/gpu/drm/i915/display/intel_fdi.h
> @@ -24,7 +24,6 @@ void intel_fdi_init_hook(struct drm_i915_private *dev_priv);
> void hsw_fdi_link_train(struct intel_encoder *encoder,
> const struct intel_crtc_state *crtc_state);
> void intel_fdi_pll_freq_update(struct drm_i915_private *i915);
> -void lpt_fdi_program_mphy(struct drm_i915_private *i915);
>
> void intel_fdi_link_train(struct intel_crtc *crtc,
> const struct intel_crtc_state *crtc_state);
> diff --git a/drivers/gpu/drm/i915/display/intel_pch_refclk.c b/drivers/gpu/drm/i915/display/intel_pch_refclk.c
> new file mode 100644
> index 000000000000..b688fd87e3da
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/display/intel_pch_refclk.c
> @@ -0,0 +1,648 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2021 Intel Corporation
> + */
> +
> +#include "intel_de.h"
> +#include "intel_display_types.h"
> +#include "intel_panel.h"
> +#include "intel_pch_refclk.h"
> +#include "intel_sbi.h"
> +
> +static void lpt_fdi_reset_mphy(struct drm_i915_private *dev_priv)
> +{
> + u32 tmp;
> +
> + tmp = intel_de_read(dev_priv, SOUTH_CHICKEN2);
> + tmp |= FDI_MPHY_IOSFSB_RESET_CTL;
> + intel_de_write(dev_priv, SOUTH_CHICKEN2, tmp);
> +
> + if (wait_for_us(intel_de_read(dev_priv, SOUTH_CHICKEN2) &
> + FDI_MPHY_IOSFSB_RESET_STATUS, 100))
> + drm_err(&dev_priv->drm, "FDI mPHY reset assert timeout\n");
> +
> + tmp = intel_de_read(dev_priv, SOUTH_CHICKEN2);
> + tmp &= ~FDI_MPHY_IOSFSB_RESET_CTL;
> + intel_de_write(dev_priv, SOUTH_CHICKEN2, tmp);
> +
> + if (wait_for_us((intel_de_read(dev_priv, SOUTH_CHICKEN2) &
> + FDI_MPHY_IOSFSB_RESET_STATUS) == 0, 100))
> + drm_err(&dev_priv->drm, "FDI mPHY reset de-assert timeout\n");
> +}
> +
> +/* WaMPhyProgramming:hsw */
> +static void lpt_fdi_program_mphy(struct drm_i915_private *dev_priv)
> +{
> + u32 tmp;
> +
> + lpt_fdi_reset_mphy(dev_priv);
> +
> + tmp = intel_sbi_read(dev_priv, 0x8008, SBI_MPHY);
> + tmp &= ~(0xFF << 24);
> + tmp |= (0x12 << 24);
> + intel_sbi_write(dev_priv, 0x8008, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x2008, SBI_MPHY);
> + tmp |= (1 << 11);
> + intel_sbi_write(dev_priv, 0x2008, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x2108, SBI_MPHY);
> + tmp |= (1 << 11);
> + intel_sbi_write(dev_priv, 0x2108, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x206C, SBI_MPHY);
> + tmp |= (1 << 24) | (1 << 21) | (1 << 18);
> + intel_sbi_write(dev_priv, 0x206C, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x216C, SBI_MPHY);
> + tmp |= (1 << 24) | (1 << 21) | (1 << 18);
> + intel_sbi_write(dev_priv, 0x216C, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x2080, SBI_MPHY);
> + tmp &= ~(7 << 13);
> + tmp |= (5 << 13);
> + intel_sbi_write(dev_priv, 0x2080, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x2180, SBI_MPHY);
> + tmp &= ~(7 << 13);
> + tmp |= (5 << 13);
> + intel_sbi_write(dev_priv, 0x2180, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x208C, SBI_MPHY);
> + tmp &= ~0xFF;
> + tmp |= 0x1C;
> + intel_sbi_write(dev_priv, 0x208C, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x218C, SBI_MPHY);
> + tmp &= ~0xFF;
> + tmp |= 0x1C;
> + intel_sbi_write(dev_priv, 0x218C, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x2098, SBI_MPHY);
> + tmp &= ~(0xFF << 16);
> + tmp |= (0x1C << 16);
> + intel_sbi_write(dev_priv, 0x2098, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x2198, SBI_MPHY);
> + tmp &= ~(0xFF << 16);
> + tmp |= (0x1C << 16);
> + intel_sbi_write(dev_priv, 0x2198, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x20C4, SBI_MPHY);
> + tmp |= (1 << 27);
> + intel_sbi_write(dev_priv, 0x20C4, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x21C4, SBI_MPHY);
> + tmp |= (1 << 27);
> + intel_sbi_write(dev_priv, 0x21C4, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x20EC, SBI_MPHY);
> + tmp &= ~(0xF << 28);
> + tmp |= (4 << 28);
> + intel_sbi_write(dev_priv, 0x20EC, tmp, SBI_MPHY);
> +
> + tmp = intel_sbi_read(dev_priv, 0x21EC, SBI_MPHY);
> + tmp &= ~(0xF << 28);
> + tmp |= (4 << 28);
> + intel_sbi_write(dev_priv, 0x21EC, tmp, SBI_MPHY);
> +}
> +
> +void lpt_disable_iclkip(struct drm_i915_private *dev_priv)
> +{
> + u32 temp;
> +
> + intel_de_write(dev_priv, PIXCLK_GATE, PIXCLK_GATE_GATE);
> +
> + mutex_lock(&dev_priv->sb_lock);
> +
> + temp = intel_sbi_read(dev_priv, SBI_SSCCTL6, SBI_ICLK);
> + temp |= SBI_SSCCTL_DISABLE;
> + intel_sbi_write(dev_priv, SBI_SSCCTL6, temp, SBI_ICLK);
> +
> + mutex_unlock(&dev_priv->sb_lock);
> +}
> +
> +/* Program iCLKIP clock to the desired frequency */
> +void lpt_program_iclkip(const struct intel_crtc_state *crtc_state)
> +{
> + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
> + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
> + int clock = crtc_state->hw.adjusted_mode.crtc_clock;
> + u32 divsel, phaseinc, auxdiv, phasedir = 0;
> + u32 temp;
> +
> + lpt_disable_iclkip(dev_priv);
> +
> + /* The iCLK virtual clock root frequency is in MHz,
> + * but the adjusted_mode->crtc_clock in KHz. To get the
> + * divisors, it is necessary to divide one by another, so we
> + * convert the virtual clock precision to KHz here for higher
> + * precision.
> + */
> + for (auxdiv = 0; auxdiv < 2; auxdiv++) {
> + u32 iclk_virtual_root_freq = 172800 * 1000;
> + u32 iclk_pi_range = 64;
> + u32 desired_divisor;
> +
> + desired_divisor = DIV_ROUND_CLOSEST(iclk_virtual_root_freq,
> + clock << auxdiv);
> + divsel = (desired_divisor / iclk_pi_range) - 2;
> + phaseinc = desired_divisor % iclk_pi_range;
> +
> + /*
> + * Near 20MHz is a corner case which is
> + * out of range for the 7-bit divisor
> + */
> + if (divsel <= 0x7f)
> + break;
> + }
> +
> + /* This should not happen with any sane values */
> + drm_WARN_ON(&dev_priv->drm, SBI_SSCDIVINTPHASE_DIVSEL(divsel) &
> + ~SBI_SSCDIVINTPHASE_DIVSEL_MASK);
> + drm_WARN_ON(&dev_priv->drm, SBI_SSCDIVINTPHASE_DIR(phasedir) &
> + ~SBI_SSCDIVINTPHASE_INCVAL_MASK);
> +
> + drm_dbg_kms(&dev_priv->drm,
> + "iCLKIP clock: found settings for %dKHz refresh rate: auxdiv=%x, divsel=%x, phasedir=%x, phaseinc=%x\n",
> + clock, auxdiv, divsel, phasedir, phaseinc);
> +
> + mutex_lock(&dev_priv->sb_lock);
> +
> + /* Program SSCDIVINTPHASE6 */
> + temp = intel_sbi_read(dev_priv, SBI_SSCDIVINTPHASE6, SBI_ICLK);
> + temp &= ~SBI_SSCDIVINTPHASE_DIVSEL_MASK;
> + temp |= SBI_SSCDIVINTPHASE_DIVSEL(divsel);
> + temp &= ~SBI_SSCDIVINTPHASE_INCVAL_MASK;
> + temp |= SBI_SSCDIVINTPHASE_INCVAL(phaseinc);
> + temp |= SBI_SSCDIVINTPHASE_DIR(phasedir);
> + temp |= SBI_SSCDIVINTPHASE_PROPAGATE;
> + intel_sbi_write(dev_priv, SBI_SSCDIVINTPHASE6, temp, SBI_ICLK);
> +
> + /* Program SSCAUXDIV */
> + temp = intel_sbi_read(dev_priv, SBI_SSCAUXDIV6, SBI_ICLK);
> + temp &= ~SBI_SSCAUXDIV_FINALDIV2SEL(1);
> + temp |= SBI_SSCAUXDIV_FINALDIV2SEL(auxdiv);
> + intel_sbi_write(dev_priv, SBI_SSCAUXDIV6, temp, SBI_ICLK);
> +
> + /* Enable modulator and associated divider */
> + temp = intel_sbi_read(dev_priv, SBI_SSCCTL6, SBI_ICLK);
> + temp &= ~SBI_SSCCTL_DISABLE;
> + intel_sbi_write(dev_priv, SBI_SSCCTL6, temp, SBI_ICLK);
> +
> + mutex_unlock(&dev_priv->sb_lock);
> +
> + /* Wait for initialization time */
> + udelay(24);
> +
> + intel_de_write(dev_priv, PIXCLK_GATE, PIXCLK_GATE_UNGATE);
> +}
> +
> +int lpt_get_iclkip(struct drm_i915_private *dev_priv)
> +{
> + u32 divsel, phaseinc, auxdiv;
> + u32 iclk_virtual_root_freq = 172800 * 1000;
> + u32 iclk_pi_range = 64;
> + u32 desired_divisor;
> + u32 temp;
> +
> + if ((intel_de_read(dev_priv, PIXCLK_GATE) & PIXCLK_GATE_UNGATE) == 0)
> + return 0;
> +
> + mutex_lock(&dev_priv->sb_lock);
> +
> + temp = intel_sbi_read(dev_priv, SBI_SSCCTL6, SBI_ICLK);
> + if (temp & SBI_SSCCTL_DISABLE) {
> + mutex_unlock(&dev_priv->sb_lock);
> + return 0;
> + }
> +
> + temp = intel_sbi_read(dev_priv, SBI_SSCDIVINTPHASE6, SBI_ICLK);
> + divsel = (temp & SBI_SSCDIVINTPHASE_DIVSEL_MASK) >>
> + SBI_SSCDIVINTPHASE_DIVSEL_SHIFT;
> + phaseinc = (temp & SBI_SSCDIVINTPHASE_INCVAL_MASK) >>
> + SBI_SSCDIVINTPHASE_INCVAL_SHIFT;
> +
> + temp = intel_sbi_read(dev_priv, SBI_SSCAUXDIV6, SBI_ICLK);
> + auxdiv = (temp & SBI_SSCAUXDIV_FINALDIV2SEL_MASK) >>
> + SBI_SSCAUXDIV_FINALDIV2SEL_SHIFT;
> +
> + mutex_unlock(&dev_priv->sb_lock);
> +
> + desired_divisor = (divsel + 2) * iclk_pi_range + phaseinc;
> +
> + return DIV_ROUND_CLOSEST(iclk_virtual_root_freq,
> + desired_divisor << auxdiv);
> +}
> +
> +/* Implements 3 different sequences from BSpec chapter "Display iCLK
> + * Programming" based on the parameters passed:
> + * - Sequence to enable CLKOUT_DP
> + * - Sequence to enable CLKOUT_DP without spread
> + * - Sequence to enable CLKOUT_DP for FDI usage and configure PCH FDI I/O
> + */
> +static void lpt_enable_clkout_dp(struct drm_i915_private *dev_priv,
> + bool with_spread, bool with_fdi)
> +{
> + u32 reg, tmp;
> +
> + if (drm_WARN(&dev_priv->drm, with_fdi && !with_spread,
> + "FDI requires downspread\n"))
> + with_spread = true;
> + if (drm_WARN(&dev_priv->drm, HAS_PCH_LPT_LP(dev_priv) &&
> + with_fdi, "LP PCH doesn't have FDI\n"))
> + with_fdi = false;
> +
> + mutex_lock(&dev_priv->sb_lock);
> +
> + tmp = intel_sbi_read(dev_priv, SBI_SSCCTL, SBI_ICLK);
> + tmp &= ~SBI_SSCCTL_DISABLE;
> + tmp |= SBI_SSCCTL_PATHALT;
> + intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
> +
> + udelay(24);
> +
> + if (with_spread) {
> + tmp = intel_sbi_read(dev_priv, SBI_SSCCTL, SBI_ICLK);
> + tmp &= ~SBI_SSCCTL_PATHALT;
> + intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
> +
> + if (with_fdi)
> + lpt_fdi_program_mphy(dev_priv);
> + }
> +
> + reg = HAS_PCH_LPT_LP(dev_priv) ? SBI_GEN0 : SBI_DBUFF0;
> + tmp = intel_sbi_read(dev_priv, reg, SBI_ICLK);
> + tmp |= SBI_GEN0_CFG_BUFFENABLE_DISABLE;
> + intel_sbi_write(dev_priv, reg, tmp, SBI_ICLK);
> +
> + mutex_unlock(&dev_priv->sb_lock);
> +}
> +
> +/* Sequence to disable CLKOUT_DP */
> +void lpt_disable_clkout_dp(struct drm_i915_private *dev_priv)
> +{
> + u32 reg, tmp;
> +
> + mutex_lock(&dev_priv->sb_lock);
> +
> + reg = HAS_PCH_LPT_LP(dev_priv) ? SBI_GEN0 : SBI_DBUFF0;
> + tmp = intel_sbi_read(dev_priv, reg, SBI_ICLK);
> + tmp &= ~SBI_GEN0_CFG_BUFFENABLE_DISABLE;
> + intel_sbi_write(dev_priv, reg, tmp, SBI_ICLK);
> +
> + tmp = intel_sbi_read(dev_priv, SBI_SSCCTL, SBI_ICLK);
> + if (!(tmp & SBI_SSCCTL_DISABLE)) {
> + if (!(tmp & SBI_SSCCTL_PATHALT)) {
> + tmp |= SBI_SSCCTL_PATHALT;
> + intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
> + udelay(32);
> + }
> + tmp |= SBI_SSCCTL_DISABLE;
> + intel_sbi_write(dev_priv, SBI_SSCCTL, tmp, SBI_ICLK);
> + }
> +
> + mutex_unlock(&dev_priv->sb_lock);
> +}
> +
> +#define BEND_IDX(steps) ((50 + (steps)) / 5)
> +
> +static const u16 sscdivintphase[] = {
> + [BEND_IDX( 50)] = 0x3B23,
> + [BEND_IDX( 45)] = 0x3B23,
> + [BEND_IDX( 40)] = 0x3C23,
> + [BEND_IDX( 35)] = 0x3C23,
> + [BEND_IDX( 30)] = 0x3D23,
> + [BEND_IDX( 25)] = 0x3D23,
> + [BEND_IDX( 20)] = 0x3E23,
> + [BEND_IDX( 15)] = 0x3E23,
> + [BEND_IDX( 10)] = 0x3F23,
> + [BEND_IDX( 5)] = 0x3F23,
> + [BEND_IDX( 0)] = 0x0025,
> + [BEND_IDX( -5)] = 0x0025,
> + [BEND_IDX(-10)] = 0x0125,
> + [BEND_IDX(-15)] = 0x0125,
> + [BEND_IDX(-20)] = 0x0225,
> + [BEND_IDX(-25)] = 0x0225,
> + [BEND_IDX(-30)] = 0x0325,
> + [BEND_IDX(-35)] = 0x0325,
> + [BEND_IDX(-40)] = 0x0425,
> + [BEND_IDX(-45)] = 0x0425,
> + [BEND_IDX(-50)] = 0x0525,
> +};
> +
> +/*
> + * Bend CLKOUT_DP
> + * steps -50 to 50 inclusive, in steps of 5
> + * < 0 slow down the clock, > 0 speed up the clock, 0 == no bend (135MHz)
> + * change in clock period = -(steps / 10) * 5.787 ps
> + */
> +static void lpt_bend_clkout_dp(struct drm_i915_private *dev_priv, int steps)
> +{
> + u32 tmp;
> + int idx = BEND_IDX(steps);
> +
> + if (drm_WARN_ON(&dev_priv->drm, steps % 5 != 0))
> + return;
> +
> + if (drm_WARN_ON(&dev_priv->drm, idx >= ARRAY_SIZE(sscdivintphase)))
> + return;
> +
> + mutex_lock(&dev_priv->sb_lock);
> +
> + if (steps % 10 != 0)
> + tmp = 0xAAAAAAAB;
> + else
> + tmp = 0x00000000;
> + intel_sbi_write(dev_priv, SBI_SSCDITHPHASE, tmp, SBI_ICLK);
> +
> + tmp = intel_sbi_read(dev_priv, SBI_SSCDIVINTPHASE, SBI_ICLK);
> + tmp &= 0xffff0000;
> + tmp |= sscdivintphase[idx];
> + intel_sbi_write(dev_priv, SBI_SSCDIVINTPHASE, tmp, SBI_ICLK);
> +
> + mutex_unlock(&dev_priv->sb_lock);
> +}
> +
> +#undef BEND_IDX
> +
> +static bool spll_uses_pch_ssc(struct drm_i915_private *dev_priv)
> +{
> + u32 fuse_strap = intel_de_read(dev_priv, FUSE_STRAP);
> + u32 ctl = intel_de_read(dev_priv, SPLL_CTL);
> +
> + if ((ctl & SPLL_PLL_ENABLE) == 0)
> + return false;
> +
> + if ((ctl & SPLL_REF_MASK) == SPLL_REF_MUXED_SSC &&
> + (fuse_strap & HSW_CPU_SSC_ENABLE) == 0)
> + return true;
> +
> + if (IS_BROADWELL(dev_priv) &&
> + (ctl & SPLL_REF_MASK) == SPLL_REF_PCH_SSC_BDW)
> + return true;
> +
> + return false;
> +}
> +
> +static bool wrpll_uses_pch_ssc(struct drm_i915_private *dev_priv,
> + enum intel_dpll_id id)
> +{
> + u32 fuse_strap = intel_de_read(dev_priv, FUSE_STRAP);
> + u32 ctl = intel_de_read(dev_priv, WRPLL_CTL(id));
> +
> + if ((ctl & WRPLL_PLL_ENABLE) == 0)
> + return false;
> +
> + if ((ctl & WRPLL_REF_MASK) == WRPLL_REF_PCH_SSC)
> + return true;
> +
> + if ((IS_BROADWELL(dev_priv) || IS_HSW_ULT(dev_priv)) &&
> + (ctl & WRPLL_REF_MASK) == WRPLL_REF_MUXED_SSC_BDW &&
> + (fuse_strap & HSW_CPU_SSC_ENABLE) == 0)
> + return true;
> +
> + return false;
> +}
> +
> +static void lpt_init_pch_refclk(struct drm_i915_private *dev_priv)
> +{
> + struct intel_encoder *encoder;
> + bool has_fdi = false;
> +
> + for_each_intel_encoder(&dev_priv->drm, encoder) {
> + switch (encoder->type) {
> + case INTEL_OUTPUT_ANALOG:
> + has_fdi = true;
> + break;
> + default:
> + break;
> + }
> + }
> +
> + /*
> + * The BIOS may have decided to use the PCH SSC
> + * reference so we must not disable it until the
> + * relevant PLLs have stopped relying on it. We'll
> + * just leave the PCH SSC reference enabled in case
> + * any active PLL is using it. It will get disabled
> + * after runtime suspend if we don't have FDI.
> + *
> + * TODO: Move the whole reference clock handling
> + * to the modeset sequence proper so that we can
> + * actually enable/disable/reconfigure these things
> + * safely. To do that we need to introduce a real
> + * clock hierarchy. That would also allow us to do
> + * clock bending finally.
> + */
> + dev_priv->pch_ssc_use = 0;
> +
> + if (spll_uses_pch_ssc(dev_priv)) {
> + drm_dbg_kms(&dev_priv->drm, "SPLL using PCH SSC\n");
> + dev_priv->pch_ssc_use |= BIT(DPLL_ID_SPLL);
> + }
> +
> + if (wrpll_uses_pch_ssc(dev_priv, DPLL_ID_WRPLL1)) {
> + drm_dbg_kms(&dev_priv->drm, "WRPLL1 using PCH SSC\n");
> + dev_priv->pch_ssc_use |= BIT(DPLL_ID_WRPLL1);
> + }
> +
> + if (wrpll_uses_pch_ssc(dev_priv, DPLL_ID_WRPLL2)) {
> + drm_dbg_kms(&dev_priv->drm, "WRPLL2 using PCH SSC\n");
> + dev_priv->pch_ssc_use |= BIT(DPLL_ID_WRPLL2);
> + }
> +
> + if (dev_priv->pch_ssc_use)
> + return;
> +
> + if (has_fdi) {
> + lpt_bend_clkout_dp(dev_priv, 0);
> + lpt_enable_clkout_dp(dev_priv, true, true);
> + } else {
> + lpt_disable_clkout_dp(dev_priv);
> + }
> +}
> +
> +static void ilk_init_pch_refclk(struct drm_i915_private *dev_priv)
> +{
> + struct intel_encoder *encoder;
> + int i;
> + u32 val, final;
> + bool has_lvds = false;
> + bool has_cpu_edp = false;
> + bool has_panel = false;
> + bool has_ck505 = false;
> + bool can_ssc = false;
> + bool using_ssc_source = false;
> +
> + /* We need to take the global config into account */
> + for_each_intel_encoder(&dev_priv->drm, encoder) {
> + switch (encoder->type) {
> + case INTEL_OUTPUT_LVDS:
> + has_panel = true;
> + has_lvds = true;
> + break;
> + case INTEL_OUTPUT_EDP:
> + has_panel = true;
> + if (encoder->port == PORT_A)
> + has_cpu_edp = true;
> + break;
> + default:
> + break;
> + }
> + }
> +
> + if (HAS_PCH_IBX(dev_priv)) {
> + has_ck505 = dev_priv->vbt.display_clock_mode;
> + can_ssc = has_ck505;
> + } else {
> + has_ck505 = false;
> + can_ssc = true;
> + }
> +
> + /* Check if any DPLLs are using the SSC source */
> + for (i = 0; i < dev_priv->dpll.num_shared_dpll; i++) {
> + u32 temp = intel_de_read(dev_priv, PCH_DPLL(i));
> +
> + if (!(temp & DPLL_VCO_ENABLE))
> + continue;
> +
> + if ((temp & PLL_REF_INPUT_MASK) ==
> + PLLB_REF_INPUT_SPREADSPECTRUMIN) {
> + using_ssc_source = true;
> + break;
> + }
> + }
> +
> + drm_dbg_kms(&dev_priv->drm,
> + "has_panel %d has_lvds %d has_ck505 %d using_ssc_source %d\n",
> + has_panel, has_lvds, has_ck505, using_ssc_source);
> +
> + /* Ironlake: try to setup display ref clock before DPLL
> + * enabling. This is only under driver's control after
> + * PCH B stepping, previous chipset stepping should be
> + * ignoring this setting.
> + */
> + val = intel_de_read(dev_priv, PCH_DREF_CONTROL);
> +
> + /* As we must carefully and slowly disable/enable each source in turn,
> + * compute the final state we want first and check if we need to
> + * make any changes at all.
> + */
> + final = val;
> + final &= ~DREF_NONSPREAD_SOURCE_MASK;
> + if (has_ck505)
> + final |= DREF_NONSPREAD_CK505_ENABLE;
> + else
> + final |= DREF_NONSPREAD_SOURCE_ENABLE;
> +
> + final &= ~DREF_SSC_SOURCE_MASK;
> + final &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
> + final &= ~DREF_SSC1_ENABLE;
> +
> + if (has_panel) {
> + final |= DREF_SSC_SOURCE_ENABLE;
> +
> + if (intel_panel_use_ssc(dev_priv) && can_ssc)
> + final |= DREF_SSC1_ENABLE;
> +
> + if (has_cpu_edp) {
> + if (intel_panel_use_ssc(dev_priv) && can_ssc)
> + final |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
> + else
> + final |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
> + } else {
> + final |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
> + }
> + } else if (using_ssc_source) {
> + final |= DREF_SSC_SOURCE_ENABLE;
> + final |= DREF_SSC1_ENABLE;
> + }
> +
> + if (final == val)
> + return;
> +
> + /* Always enable nonspread source */
> + val &= ~DREF_NONSPREAD_SOURCE_MASK;
> +
> + if (has_ck505)
> + val |= DREF_NONSPREAD_CK505_ENABLE;
> + else
> + val |= DREF_NONSPREAD_SOURCE_ENABLE;
> +
> + if (has_panel) {
> + val &= ~DREF_SSC_SOURCE_MASK;
> + val |= DREF_SSC_SOURCE_ENABLE;
> +
> + /* SSC must be turned on before enabling the CPU output */
> + if (intel_panel_use_ssc(dev_priv) && can_ssc) {
> + drm_dbg_kms(&dev_priv->drm, "Using SSC on panel\n");
> + val |= DREF_SSC1_ENABLE;
> + } else {
> + val &= ~DREF_SSC1_ENABLE;
> + }
> +
> + /* Get SSC going before enabling the outputs */
> + intel_de_write(dev_priv, PCH_DREF_CONTROL, val);
> + intel_de_posting_read(dev_priv, PCH_DREF_CONTROL);
> + udelay(200);
> +
> + val &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
> +
> + /* Enable CPU source on CPU attached eDP */
> + if (has_cpu_edp) {
> + if (intel_panel_use_ssc(dev_priv) && can_ssc) {
> + drm_dbg_kms(&dev_priv->drm,
> + "Using SSC on eDP\n");
> + val |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
> + } else {
> + val |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
> + }
> + } else {
> + val |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
> + }
> +
> + intel_de_write(dev_priv, PCH_DREF_CONTROL, val);
> + intel_de_posting_read(dev_priv, PCH_DREF_CONTROL);
> + udelay(200);
> + } else {
> + drm_dbg_kms(&dev_priv->drm, "Disabling CPU source output\n");
> +
> + val &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
> +
> + /* Turn off CPU output */
> + val |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
> +
> + intel_de_write(dev_priv, PCH_DREF_CONTROL, val);
> + intel_de_posting_read(dev_priv, PCH_DREF_CONTROL);
> + udelay(200);
> +
> + if (!using_ssc_source) {
> + drm_dbg_kms(&dev_priv->drm, "Disabling SSC source\n");
> +
> + /* Turn off the SSC source */
> + val &= ~DREF_SSC_SOURCE_MASK;
> + val |= DREF_SSC_SOURCE_DISABLE;
> +
> + /* Turn off SSC1 */
> + val &= ~DREF_SSC1_ENABLE;
> +
> + intel_de_write(dev_priv, PCH_DREF_CONTROL, val);
> + intel_de_posting_read(dev_priv, PCH_DREF_CONTROL);
> + udelay(200);
> + }
> + }
> +
> + BUG_ON(val != final);
> +}
> +
> +/*
> + * Initialize reference clocks when the driver loads
> + */
> +void intel_init_pch_refclk(struct drm_i915_private *dev_priv)
> +{
> + if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv))
> + ilk_init_pch_refclk(dev_priv);
> + else if (HAS_PCH_LPT(dev_priv))
> + lpt_init_pch_refclk(dev_priv);
> +}
> diff --git a/drivers/gpu/drm/i915/display/intel_pch_refclk.h b/drivers/gpu/drm/i915/display/intel_pch_refclk.h
> new file mode 100644
> index 000000000000..12ab2c75a800
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/display/intel_pch_refclk.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2021 Intel Corporation
> + */
> +
> +#ifndef _INTEL_PCH_REFCLK_H_
> +#define _INTEL_PCH_REFCLK_H_
> +
> +#include <linux/types.h>
> +
> +struct drm_i915_private;
> +struct intel_crtc_state;
> +
> +void lpt_program_iclkip(const struct intel_crtc_state *crtc_state);
> +void lpt_disable_iclkip(struct drm_i915_private *dev_priv);
> +int lpt_get_iclkip(struct drm_i915_private *dev_priv);
> +
> +void intel_init_pch_refclk(struct drm_i915_private *dev_priv);
> +void lpt_disable_clkout_dp(struct drm_i915_private *dev_priv);
> +
> +#endif
> diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
> index b18a250e5d2e..1e5b75ae9932 100644
> --- a/drivers/gpu/drm/i915/i915_drv.c
> +++ b/drivers/gpu/drm/i915/i915_drv.c
> @@ -54,6 +54,7 @@
> #include "display/intel_fbdev.h"
> #include "display/intel_hotplug.h"
> #include "display/intel_overlay.h"
> +#include "display/intel_pch_refclk.h"
> #include "display/intel_pipe_crc.h"
> #include "display/intel_pps.h"
> #include "display/intel_sprite.h"
> --
> 2.32.0
>
More information about the Intel-gfx
mailing list