Message ID | 20180427231436.9353-1-paulo.r.zanoni@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Fri, Apr 27, 2018 at 04:14:36PM -0700, Paulo Zanoni wrote: > This commit introduces the definitions for the ICL clocks and adds the > basic functions to the shared DPLL framework. It adds code for the > Enable and Disable sequences for some PLLs, but it does not have the > code to compute the actual PLL values, which are marked as TODO > comments and should be introduced as separate commits. > > Special thanks to James Ausmus for investigating and fixing a bug with > the placement of icl_unmap_plls_to_ports() function. > > v2: > - Rebase around dpll_lock changes. > v3: > - The spec now says what the timeouts should be. > - Touch DPCLKA_CFGCR0_ICL at the appropriate time so we don't freeze > the machine. > - Checkpatch found a white space problem. > - Small adjustments before upstreaming. > v4: > - Move the ICL checks out of the *map_plls_to_ports() functions > (James) > - Add extra encoder check (James) > - Call icl_unmap_plls_to_ports() later (James) > v5: > - Rebase after the pll struct changes. > v6: > - Properly make the unmap function based on encoders_post_disable() > with regarding to checks and iterators. > - Address checkpatch comment on "min = max = x()". > > Cc: James Ausmus <james.ausmus@intel.com> > Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> Reviewed-by: James Ausmus <james.ausmus@intel.com> > --- > drivers/gpu/drm/i915/i915_debugfs.c | 22 +++ > drivers/gpu/drm/i915/intel_ddi.c | 98 ++++++++++- > drivers/gpu/drm/i915/intel_display.c | 16 ++ > drivers/gpu/drm/i915/intel_dpll_mgr.c | 313 +++++++++++++++++++++++++++++++++- > drivers/gpu/drm/i915/intel_dpll_mgr.h | 41 +++++ > drivers/gpu/drm/i915/intel_drv.h | 6 + > 6 files changed, 491 insertions(+), 5 deletions(-) > > diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c > index cb1a804bf72e..ba8927cb1dcc 100644 > --- a/drivers/gpu/drm/i915/i915_debugfs.c > +++ b/drivers/gpu/drm/i915/i915_debugfs.c > @@ -3365,6 +3365,28 @@ static int i915_shared_dplls_info(struct seq_file *m, void *unused) > seq_printf(m, " fp0: 0x%08x\n", pll->state.hw_state.fp0); > seq_printf(m, " fp1: 0x%08x\n", pll->state.hw_state.fp1); > seq_printf(m, " wrpll: 0x%08x\n", pll->state.hw_state.wrpll); > + seq_printf(m, " cfgcr0: 0x%08x\n", pll->state.hw_state.cfgcr0); > + seq_printf(m, " cfgcr1: 0x%08x\n", pll->state.hw_state.cfgcr1); > + seq_printf(m, " mg_refclkin_ctl: 0x%08x\n", > + pll->state.hw_state.mg_refclkin_ctl); > + seq_printf(m, " mg_clktop2_coreclkctl1: 0x%08x\n", > + pll->state.hw_state.mg_clktop2_coreclkctl1); > + seq_printf(m, " mg_clktop2_hsclkctl: 0x%08x\n", > + pll->state.hw_state.mg_clktop2_hsclkctl); > + seq_printf(m, " mg_pll_div0: 0x%08x\n", > + pll->state.hw_state.mg_pll_div0); > + seq_printf(m, " mg_pll_div1: 0x%08x\n", > + pll->state.hw_state.mg_pll_div1); > + seq_printf(m, " mg_pll_lf: 0x%08x\n", > + pll->state.hw_state.mg_pll_lf); > + seq_printf(m, " mg_pll_frac_lock: 0x%08x\n", > + pll->state.hw_state.mg_pll_frac_lock); > + seq_printf(m, " mg_pll_ssc: 0x%08x\n", > + pll->state.hw_state.mg_pll_ssc); > + seq_printf(m, " mg_pll_bias: 0x%08x\n", > + pll->state.hw_state.mg_pll_bias); > + seq_printf(m, " mg_pll_tdc_coldst_bias: 0x%08x\n", > + pll->state.hw_state.mg_pll_tdc_coldst_bias); > } > drm_modeset_unlock_all(dev); > > diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c > index 92cb26b18a9b..178a7b0d0149 100644 > --- a/drivers/gpu/drm/i915/intel_ddi.c > +++ b/drivers/gpu/drm/i915/intel_ddi.c > @@ -1013,6 +1013,25 @@ static uint32_t hsw_pll_to_ddi_pll_sel(const struct intel_shared_dpll *pll) > } > } > > +static uint32_t icl_pll_to_ddi_pll_sel(struct intel_encoder *encoder, > + const struct intel_shared_dpll *pll) > +{ > + const enum intel_dpll_id id = pll->info->id; > + > + switch (id) { > + default: > + MISSING_CASE(id); > + case DPLL_ID_ICL_DPLL0: > + case DPLL_ID_ICL_DPLL1: > + return DDI_CLK_SEL_NONE; > + case DPLL_ID_ICL_MGPLL1: > + case DPLL_ID_ICL_MGPLL2: > + case DPLL_ID_ICL_MGPLL3: > + case DPLL_ID_ICL_MGPLL4: > + return DDI_CLK_SEL_MG; > + } > +} > + > /* Starting with Haswell, different DDI ports can work in FDI mode for > * connection to the PCH-located connectors. For this, it is necessary to train > * both the DDI port and PCH receiver for the desired DDI buffer settings. > @@ -2234,6 +2253,69 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp) > return DDI_BUF_TRANS_SELECT(level); > } > > +void icl_map_plls_to_ports(struct drm_crtc *crtc, > + struct intel_crtc_state *crtc_state, > + struct drm_atomic_state *old_state) > +{ > + struct intel_shared_dpll *pll = crtc_state->shared_dpll; > + struct drm_i915_private *dev_priv = to_i915(crtc->dev); > + struct drm_connector_state *conn_state; > + struct drm_connector *conn; > + int i; > + > + for_each_new_connector_in_state(old_state, conn, conn_state, i) { > + struct intel_encoder *encoder = > + to_intel_encoder(conn_state->best_encoder); > + enum port port = encoder->port; > + uint32_t val; > + > + if (conn_state->crtc != crtc) > + continue; > + > + mutex_lock(&dev_priv->dpll_lock); > + > + val = I915_READ(DPCLKA_CFGCR0_ICL); > + WARN_ON((val & DPCLKA_CFGCR0_DDI_CLK_OFF(port)) == 0); > + > + if (port == PORT_A || port == PORT_B) { > + val &= ~DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(port); > + val |= DPCLKA_CFGCR0_DDI_CLK_SEL(pll->info->id, port); > + I915_WRITE(DPCLKA_CFGCR0_ICL, val); > + POSTING_READ(DPCLKA_CFGCR0_ICL); > + } > + > + val &= ~DPCLKA_CFGCR0_DDI_CLK_OFF(port); > + I915_WRITE(DPCLKA_CFGCR0_ICL, val); > + > + mutex_unlock(&dev_priv->dpll_lock); > + } > +} > + > +void icl_unmap_plls_to_ports(struct drm_crtc *crtc, > + struct intel_crtc_state *crtc_state, > + struct drm_atomic_state *old_state) > +{ > + struct drm_i915_private *dev_priv = to_i915(crtc->dev); > + struct drm_connector_state *old_conn_state; > + struct drm_connector *conn; > + int i; > + > + for_each_old_connector_in_state(old_state, conn, old_conn_state, i) { > + struct intel_encoder *encoder = > + to_intel_encoder(old_conn_state->best_encoder); > + enum port port = encoder->port; > + > + if (old_conn_state->crtc != crtc) > + continue; > + > + mutex_lock(&dev_priv->dpll_lock); > + I915_WRITE(DPCLKA_CFGCR0_ICL, > + I915_READ(DPCLKA_CFGCR0_ICL) | > + DPCLKA_CFGCR0_DDI_CLK_OFF(port)); > + mutex_unlock(&dev_priv->dpll_lock); > + } > +} > + > static void intel_ddi_clk_select(struct intel_encoder *encoder, > const struct intel_shared_dpll *pll) > { > @@ -2246,7 +2328,11 @@ static void intel_ddi_clk_select(struct intel_encoder *encoder, > > mutex_lock(&dev_priv->dpll_lock); > > - if (IS_CANNONLAKE(dev_priv)) { > + if (IS_ICELAKE(dev_priv)) { > + if (port >= PORT_C) > + I915_WRITE(DDI_CLK_SEL(port), > + icl_pll_to_ddi_pll_sel(encoder, pll)); > + } else if (IS_CANNONLAKE(dev_priv)) { > /* Configure DPCLKA_CFGCR0 to map the DPLL to the DDI. */ > val = I915_READ(DPCLKA_CFGCR0); > val &= ~DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(port); > @@ -2284,14 +2370,18 @@ static void intel_ddi_clk_disable(struct intel_encoder *encoder) > struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); > enum port port = encoder->port; > > - if (IS_CANNONLAKE(dev_priv)) > + if (IS_ICELAKE(dev_priv)) { > + if (port >= PORT_C) > + I915_WRITE(DDI_CLK_SEL(port), DDI_CLK_SEL_NONE); > + } else if (IS_CANNONLAKE(dev_priv)) { > I915_WRITE(DPCLKA_CFGCR0, I915_READ(DPCLKA_CFGCR0) | > DPCLKA_CFGCR0_DDI_CLK_OFF(port)); > - else if (IS_GEN9_BC(dev_priv)) > + } else if (IS_GEN9_BC(dev_priv)) { > I915_WRITE(DPLL_CTRL2, I915_READ(DPLL_CTRL2) | > DPLL_CTRL2_DDI_CLK_OFF(port)); > - else if (INTEL_GEN(dev_priv) < 9) > + } else if (INTEL_GEN(dev_priv) < 9) { > I915_WRITE(PORT_CLK_SEL(port), PORT_CLK_SEL_NONE); > + } > } > > static void intel_ddi_pre_enable_dp(struct intel_encoder *encoder, > diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c > index 48576ea2d36c..1d3da5b7d33c 100644 > --- a/drivers/gpu/drm/i915/intel_display.c > +++ b/drivers/gpu/drm/i915/intel_display.c > @@ -5559,6 +5559,9 @@ static void haswell_crtc_enable(struct intel_crtc_state *pipe_config, > if (intel_crtc->config->shared_dpll) > intel_enable_shared_dpll(intel_crtc); > > + if (INTEL_GEN(dev_priv) >= 11) > + icl_map_plls_to_ports(crtc, pipe_config, old_state); > + > if (intel_crtc_has_dp_encoder(intel_crtc->config)) > intel_dp_set_m_n(intel_crtc, M1_N1); > > @@ -5756,6 +5759,9 @@ static void haswell_crtc_disable(struct intel_crtc_state *old_crtc_state, > intel_ddi_disable_pipe_clock(intel_crtc->config); > > intel_encoders_post_disable(crtc, old_crtc_state, old_state); > + > + if (INTEL_GEN(dev_priv) >= 11) > + icl_unmap_plls_to_ports(crtc, old_crtc_state, old_state); > } > > static void i9xx_pfit_enable(struct intel_crtc *crtc) > @@ -11386,6 +11392,16 @@ intel_pipe_config_compare(struct drm_i915_private *dev_priv, > PIPE_CONF_CHECK_X(dpll_hw_state.pll9); > PIPE_CONF_CHECK_X(dpll_hw_state.pll10); > PIPE_CONF_CHECK_X(dpll_hw_state.pcsdw12); > + PIPE_CONF_CHECK_X(dpll_hw_state.mg_refclkin_ctl); > + PIPE_CONF_CHECK_X(dpll_hw_state.mg_clktop2_coreclkctl1); > + PIPE_CONF_CHECK_X(dpll_hw_state.mg_clktop2_hsclkctl); > + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_div0); > + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_div1); > + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_lf); > + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_frac_lock); > + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_ssc); > + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_bias); > + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_tdc_coldst_bias); > > PIPE_CONF_CHECK_X(dsi_pll.ctrl); > PIPE_CONF_CHECK_X(dsi_pll.div); > diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.c b/drivers/gpu/drm/i915/intel_dpll_mgr.c > index d5e114e9660b..14f5414ceab2 100644 > --- a/drivers/gpu/drm/i915/intel_dpll_mgr.c > +++ b/drivers/gpu/drm/i915/intel_dpll_mgr.c > @@ -2399,6 +2399,315 @@ static const struct intel_dpll_mgr cnl_pll_mgr = { > .dump_hw_state = cnl_dump_hw_state, > }; > > +static bool icl_calc_dpll_state(struct intel_crtc_state *crtc_state, > + struct intel_encoder *encoder, int clock, > + struct intel_dpll_hw_state *pll_state) > +{ > + /* TODO */ > + return true; > +} > + > +static enum port icl_mg_pll_id_to_port(enum intel_dpll_id id) > +{ > + return id - DPLL_ID_ICL_MGPLL1 + PORT_C; > +} > + > +static enum intel_dpll_id icl_port_to_mg_pll_id(enum port port) > +{ > + return port - PORT_C + DPLL_ID_ICL_MGPLL1; > +} > + > +static bool icl_calc_mg_pll_state(struct intel_crtc_state *crtc_state, > + struct intel_encoder *encoder, int clock, > + struct intel_dpll_hw_state *pll_state) > +{ > + /* TODO */ > + return true; > +} > + > +static struct intel_shared_dpll * > +icl_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, > + struct intel_encoder *encoder) > +{ > + struct intel_shared_dpll *pll; > + struct intel_dpll_hw_state pll_state = {}; > + enum port port = encoder->port; > + enum intel_dpll_id min, max; > + int clock = crtc_state->port_clock; > + bool ret; > + > + switch (port) { > + case PORT_A: > + case PORT_B: > + min = DPLL_ID_ICL_DPLL0; > + max = DPLL_ID_ICL_DPLL1; > + ret = icl_calc_dpll_state(crtc_state, encoder, clock, > + &pll_state); > + break; > + case PORT_C: > + case PORT_D: > + case PORT_E: > + case PORT_F: > + min = icl_port_to_mg_pll_id(port); > + max = min; > + ret = icl_calc_mg_pll_state(crtc_state, encoder, clock, > + &pll_state); > + break; > + default: > + MISSING_CASE(port); > + return NULL; > + } > + > + if (!ret) { > + DRM_DEBUG_KMS("Could not calculate PLL state.\n"); > + return NULL; > + } > + > + crtc_state->dpll_hw_state = pll_state; > + > + pll = intel_find_shared_dpll(crtc, crtc_state, min, max); > + if (!pll) { > + DRM_DEBUG_KMS("No PLL selected\n"); > + return NULL; > + } > + > + intel_reference_shared_dpll(pll, crtc_state); > + > + return pll; > +} > + > +static i915_reg_t icl_pll_id_to_enable_reg(enum intel_dpll_id id) > +{ > + switch (id) { > + default: > + MISSING_CASE(id); > + case DPLL_ID_ICL_DPLL0: > + case DPLL_ID_ICL_DPLL1: > + return CNL_DPLL_ENABLE(id); > + case DPLL_ID_ICL_MGPLL1: > + case DPLL_ID_ICL_MGPLL2: > + case DPLL_ID_ICL_MGPLL3: > + case DPLL_ID_ICL_MGPLL4: > + return MG_PLL_ENABLE(icl_mg_pll_id_to_port(id)); > + } > +} > + > +static bool icl_pll_get_hw_state(struct drm_i915_private *dev_priv, > + struct intel_shared_dpll *pll, > + struct intel_dpll_hw_state *hw_state) > +{ > + const enum intel_dpll_id id = pll->info->id; > + uint32_t val; > + enum port port; > + bool ret = false; > + > + if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS)) > + return false; > + > + val = I915_READ(icl_pll_id_to_enable_reg(id)); > + if (!(val & PLL_ENABLE)) > + goto out; > + > + switch (id) { > + case DPLL_ID_ICL_DPLL0: > + case DPLL_ID_ICL_DPLL1: > + hw_state->cfgcr0 = I915_READ(ICL_DPLL_CFGCR0(id)); > + hw_state->cfgcr1 = I915_READ(ICL_DPLL_CFGCR1(id)); > + break; > + case DPLL_ID_ICL_MGPLL1: > + case DPLL_ID_ICL_MGPLL2: > + case DPLL_ID_ICL_MGPLL3: > + case DPLL_ID_ICL_MGPLL4: > + port = icl_mg_pll_id_to_port(id); > + hw_state->mg_refclkin_ctl = I915_READ(MG_REFCLKIN_CTL(port)); > + hw_state->mg_clktop2_coreclkctl1 = > + I915_READ(MG_CLKTOP2_CORECLKCTL1(port)); > + hw_state->mg_clktop2_hsclkctl = > + I915_READ(MG_CLKTOP2_HSCLKCTL(port)); > + hw_state->mg_pll_div0 = I915_READ(MG_PLL_DIV0(port)); > + hw_state->mg_pll_div1 = I915_READ(MG_PLL_DIV1(port)); > + hw_state->mg_pll_lf = I915_READ(MG_PLL_LF(port)); > + hw_state->mg_pll_frac_lock = I915_READ(MG_PLL_FRAC_LOCK(port)); > + hw_state->mg_pll_ssc = I915_READ(MG_PLL_SSC(port)); > + hw_state->mg_pll_bias = I915_READ(MG_PLL_BIAS(port)); > + hw_state->mg_pll_tdc_coldst_bias = > + I915_READ(MG_PLL_TDC_COLDST_BIAS(port)); > + break; > + default: > + MISSING_CASE(id); > + } > + > + ret = true; > +out: > + intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS); > + return ret; > +} > + > +static void icl_dpll_write(struct drm_i915_private *dev_priv, > + struct intel_shared_dpll *pll) > +{ > + struct intel_dpll_hw_state *hw_state = &pll->state.hw_state; > + const enum intel_dpll_id id = pll->info->id; > + > + I915_WRITE(ICL_DPLL_CFGCR0(id), hw_state->cfgcr0); > + I915_WRITE(ICL_DPLL_CFGCR1(id), hw_state->cfgcr1); > + POSTING_READ(ICL_DPLL_CFGCR1(id)); > +} > + > +static void icl_mg_pll_write(struct drm_i915_private *dev_priv, > + struct intel_shared_dpll *pll) > +{ > + struct intel_dpll_hw_state *hw_state = &pll->state.hw_state; > + enum port port = icl_mg_pll_id_to_port(pll->info->id); > + > + I915_WRITE(MG_REFCLKIN_CTL(port), hw_state->mg_refclkin_ctl); > + I915_WRITE(MG_CLKTOP2_CORECLKCTL1(port), > + hw_state->mg_clktop2_coreclkctl1); > + I915_WRITE(MG_CLKTOP2_HSCLKCTL(port), hw_state->mg_clktop2_hsclkctl); > + I915_WRITE(MG_PLL_DIV0(port), hw_state->mg_pll_div0); > + I915_WRITE(MG_PLL_DIV1(port), hw_state->mg_pll_div1); > + I915_WRITE(MG_PLL_LF(port), hw_state->mg_pll_lf); > + I915_WRITE(MG_PLL_FRAC_LOCK(port), hw_state->mg_pll_frac_lock); > + I915_WRITE(MG_PLL_SSC(port), hw_state->mg_pll_ssc); > + I915_WRITE(MG_PLL_BIAS(port), hw_state->mg_pll_bias); > + I915_WRITE(MG_PLL_TDC_COLDST_BIAS(port), > + hw_state->mg_pll_tdc_coldst_bias); > + POSTING_READ(MG_PLL_TDC_COLDST_BIAS(port)); > +} > + > +static void icl_pll_enable(struct drm_i915_private *dev_priv, > + struct intel_shared_dpll *pll) > +{ > + const enum intel_dpll_id id = pll->info->id; > + i915_reg_t enable_reg = icl_pll_id_to_enable_reg(id); > + uint32_t val; > + > + val = I915_READ(enable_reg); > + val |= PLL_POWER_ENABLE; > + I915_WRITE(enable_reg, val); > + > + /* > + * The spec says we need to "wait" but it also says it should be > + * immediate. > + */ > + if (intel_wait_for_register(dev_priv, enable_reg, PLL_POWER_STATE, > + PLL_POWER_STATE, 1)) > + DRM_ERROR("PLL %d Power not enabled\n", id); > + > + switch (id) { > + case DPLL_ID_ICL_DPLL0: > + case DPLL_ID_ICL_DPLL1: > + icl_dpll_write(dev_priv, pll); > + break; > + case DPLL_ID_ICL_MGPLL1: > + case DPLL_ID_ICL_MGPLL2: > + case DPLL_ID_ICL_MGPLL3: > + case DPLL_ID_ICL_MGPLL4: > + icl_mg_pll_write(dev_priv, pll); > + break; > + default: > + MISSING_CASE(id); > + } > + > + /* > + * DVFS pre sequence would be here, but in our driver the cdclk code > + * paths should already be setting the appropriate voltage, hence we do > + * nothign here. > + */ > + > + val = I915_READ(enable_reg); > + val |= PLL_ENABLE; > + I915_WRITE(enable_reg, val); > + > + if (intel_wait_for_register(dev_priv, enable_reg, PLL_LOCK, PLL_LOCK, > + 1)) /* 600us actually. */ > + DRM_ERROR("PLL %d not locked\n", id); > + > + /* DVFS post sequence would be here. See the comment above. */ > +} > + > +static void icl_pll_disable(struct drm_i915_private *dev_priv, > + struct intel_shared_dpll *pll) > +{ > + const enum intel_dpll_id id = pll->info->id; > + i915_reg_t enable_reg = icl_pll_id_to_enable_reg(id); > + uint32_t val; > + > + /* The first steps are done by intel_ddi_post_disable(). */ > + > + /* > + * DVFS pre sequence would be here, but in our driver the cdclk code > + * paths should already be setting the appropriate voltage, hence we do > + * nothign here. > + */ > + > + val = I915_READ(enable_reg); > + val &= ~PLL_ENABLE; > + I915_WRITE(enable_reg, val); > + > + /* Timeout is actually 1us. */ > + if (intel_wait_for_register(dev_priv, enable_reg, PLL_LOCK, 0, 1)) > + DRM_ERROR("PLL %d locked\n", id); > + > + /* DVFS post sequence would be here. See the comment above. */ > + > + val = I915_READ(enable_reg); > + val &= ~PLL_POWER_ENABLE; > + I915_WRITE(enable_reg, val); > + > + /* > + * The spec says we need to "wait" but it also says it should be > + * immediate. > + */ > + if (intel_wait_for_register(dev_priv, enable_reg, PLL_POWER_STATE, 0, > + 1)) > + DRM_ERROR("PLL %d Power not disabled\n", id); > +} > + > +static void icl_dump_hw_state(struct drm_i915_private *dev_priv, > + struct intel_dpll_hw_state *hw_state) > +{ > + DRM_DEBUG_KMS("dpll_hw_state: cfgcr0: 0x%x, cfgcr1: 0x%x, " > + "mg_refclkin_ctl: 0x%x, hg_clktop2_coreclkctl1: 0x%x, " > + "mg_clktop2_hsclkctl: 0x%x, mg_pll_div0: 0x%x, " > + "mg_pll_div2: 0x%x, mg_pll_lf: 0x%x, " > + "mg_pll_frac_lock: 0x%x, mg_pll_ssc: 0x%x, " > + "mg_pll_bias: 0x%x, mg_pll_tdc_coldst_bias: 0x%x\n", > + hw_state->cfgcr0, hw_state->cfgcr1, > + hw_state->mg_refclkin_ctl, > + hw_state->mg_clktop2_coreclkctl1, > + hw_state->mg_clktop2_hsclkctl, > + hw_state->mg_pll_div0, > + hw_state->mg_pll_div1, > + hw_state->mg_pll_lf, > + hw_state->mg_pll_frac_lock, > + hw_state->mg_pll_ssc, > + hw_state->mg_pll_bias, > + hw_state->mg_pll_tdc_coldst_bias); > +} > + > +static const struct intel_shared_dpll_funcs icl_pll_funcs = { > + .enable = icl_pll_enable, > + .disable = icl_pll_disable, > + .get_hw_state = icl_pll_get_hw_state, > +}; > + > +static const struct dpll_info icl_plls[] = { > + { "DPLL 0", &icl_pll_funcs, DPLL_ID_ICL_DPLL0, 0 }, > + { "DPLL 1", &icl_pll_funcs, DPLL_ID_ICL_DPLL1, 0 }, > + { "MG PLL 1", &icl_pll_funcs, DPLL_ID_ICL_MGPLL1, 0 }, > + { "MG PLL 2", &icl_pll_funcs, DPLL_ID_ICL_MGPLL2, 0 }, > + { "MG PLL 3", &icl_pll_funcs, DPLL_ID_ICL_MGPLL3, 0 }, > + { "MG PLL 4", &icl_pll_funcs, DPLL_ID_ICL_MGPLL4, 0 }, > + { }, > +}; > + > +static const struct intel_dpll_mgr icl_pll_mgr = { > + .dpll_info = icl_plls, > + .get_dpll = icl_get_dpll, > + .dump_hw_state = icl_dump_hw_state, > +}; > + > /** > * intel_shared_dpll_init - Initialize shared DPLLs > * @dev: drm device > @@ -2412,7 +2721,9 @@ void intel_shared_dpll_init(struct drm_device *dev) > const struct dpll_info *dpll_info; > int i; > > - if (IS_CANNONLAKE(dev_priv)) > + if (IS_ICELAKE(dev_priv)) > + dpll_mgr = &icl_pll_mgr; > + else if (IS_CANNONLAKE(dev_priv)) > dpll_mgr = &cnl_pll_mgr; > else if (IS_GEN9_BC(dev_priv)) > dpll_mgr = &skl_pll_mgr; > diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.h b/drivers/gpu/drm/i915/intel_dpll_mgr.h > index 4febfaa90bde..7a0cd564a9ee 100644 > --- a/drivers/gpu/drm/i915/intel_dpll_mgr.h > +++ b/drivers/gpu/drm/i915/intel_dpll_mgr.h > @@ -103,6 +103,32 @@ enum intel_dpll_id { > * @DPLL_ID_SKL_DPLL3: SKL and later DPLL3 > */ > DPLL_ID_SKL_DPLL3 = 3, > + > + > + /** > + * @DPLL_ID_ICL_DPLL0: ICL combo PHY DPLL0 > + */ > + DPLL_ID_ICL_DPLL0 = 0, > + /** > + * @DPLL_ID_ICL_DPLL1: ICL combo PHY DPLL1 > + */ > + DPLL_ID_ICL_DPLL1 = 1, > + /** > + * @DPLL_ID_ICL_MGPLL1: ICL MG PLL 1 port 1 (C) > + */ > + DPLL_ID_ICL_MGPLL1 = 2, > + /** > + * @DPLL_ID_ICL_MGPLL2: ICL MG PLL 1 port 2 (D) > + */ > + DPLL_ID_ICL_MGPLL2 = 3, > + /** > + * @DPLL_ID_ICL_MGPLL3: ICL MG PLL 1 port 3 (E) > + */ > + DPLL_ID_ICL_MGPLL3 = 4, > + /** > + * @DPLL_ID_ICL_MGPLL4: ICL MG PLL 1 port 4 (F) > + */ > + DPLL_ID_ICL_MGPLL4 = 5, > }; > #define I915_NUM_PLLS 6 > > @@ -135,6 +161,21 @@ struct intel_dpll_hw_state { > /* bxt */ > uint32_t ebb0, ebb4, pll0, pll1, pll2, pll3, pll6, pll8, pll9, pll10, > pcsdw12; > + > + /* > + * ICL uses the following, already defined: > + * uint32_t cfgcr0, cfgcr1; > + */ > + uint32_t mg_refclkin_ctl; > + uint32_t mg_clktop2_coreclkctl1; > + uint32_t mg_clktop2_hsclkctl; > + uint32_t mg_pll_div0; > + uint32_t mg_pll_div1; > + uint32_t mg_pll_lf; > + uint32_t mg_pll_frac_lock; > + uint32_t mg_pll_ssc; > + uint32_t mg_pll_bias; > + uint32_t mg_pll_tdc_coldst_bias; > }; > > /** > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h > index 9bba0354ccd3..49053150e192 100644 > --- a/drivers/gpu/drm/i915/intel_drv.h > +++ b/drivers/gpu/drm/i915/intel_drv.h > @@ -1405,6 +1405,12 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp); > u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder); > int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder, > bool enable); > +void icl_map_plls_to_ports(struct drm_crtc *crtc, > + struct intel_crtc_state *crtc_state, > + struct drm_atomic_state *old_state); > +void icl_unmap_plls_to_ports(struct drm_crtc *crtc, > + struct intel_crtc_state *crtc_state, > + struct drm_atomic_state *old_state); > > unsigned int intel_fb_align_height(const struct drm_framebuffer *fb, > int plane, unsigned int height); > -- > 2.14.3 >
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index cb1a804bf72e..ba8927cb1dcc 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -3365,6 +3365,28 @@ static int i915_shared_dplls_info(struct seq_file *m, void *unused) seq_printf(m, " fp0: 0x%08x\n", pll->state.hw_state.fp0); seq_printf(m, " fp1: 0x%08x\n", pll->state.hw_state.fp1); seq_printf(m, " wrpll: 0x%08x\n", pll->state.hw_state.wrpll); + seq_printf(m, " cfgcr0: 0x%08x\n", pll->state.hw_state.cfgcr0); + seq_printf(m, " cfgcr1: 0x%08x\n", pll->state.hw_state.cfgcr1); + seq_printf(m, " mg_refclkin_ctl: 0x%08x\n", + pll->state.hw_state.mg_refclkin_ctl); + seq_printf(m, " mg_clktop2_coreclkctl1: 0x%08x\n", + pll->state.hw_state.mg_clktop2_coreclkctl1); + seq_printf(m, " mg_clktop2_hsclkctl: 0x%08x\n", + pll->state.hw_state.mg_clktop2_hsclkctl); + seq_printf(m, " mg_pll_div0: 0x%08x\n", + pll->state.hw_state.mg_pll_div0); + seq_printf(m, " mg_pll_div1: 0x%08x\n", + pll->state.hw_state.mg_pll_div1); + seq_printf(m, " mg_pll_lf: 0x%08x\n", + pll->state.hw_state.mg_pll_lf); + seq_printf(m, " mg_pll_frac_lock: 0x%08x\n", + pll->state.hw_state.mg_pll_frac_lock); + seq_printf(m, " mg_pll_ssc: 0x%08x\n", + pll->state.hw_state.mg_pll_ssc); + seq_printf(m, " mg_pll_bias: 0x%08x\n", + pll->state.hw_state.mg_pll_bias); + seq_printf(m, " mg_pll_tdc_coldst_bias: 0x%08x\n", + pll->state.hw_state.mg_pll_tdc_coldst_bias); } drm_modeset_unlock_all(dev); diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 92cb26b18a9b..178a7b0d0149 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1013,6 +1013,25 @@ static uint32_t hsw_pll_to_ddi_pll_sel(const struct intel_shared_dpll *pll) } } +static uint32_t icl_pll_to_ddi_pll_sel(struct intel_encoder *encoder, + const struct intel_shared_dpll *pll) +{ + const enum intel_dpll_id id = pll->info->id; + + switch (id) { + default: + MISSING_CASE(id); + case DPLL_ID_ICL_DPLL0: + case DPLL_ID_ICL_DPLL1: + return DDI_CLK_SEL_NONE; + case DPLL_ID_ICL_MGPLL1: + case DPLL_ID_ICL_MGPLL2: + case DPLL_ID_ICL_MGPLL3: + case DPLL_ID_ICL_MGPLL4: + return DDI_CLK_SEL_MG; + } +} + /* Starting with Haswell, different DDI ports can work in FDI mode for * connection to the PCH-located connectors. For this, it is necessary to train * both the DDI port and PCH receiver for the desired DDI buffer settings. @@ -2234,6 +2253,69 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp) return DDI_BUF_TRANS_SELECT(level); } +void icl_map_plls_to_ports(struct drm_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct drm_atomic_state *old_state) +{ + struct intel_shared_dpll *pll = crtc_state->shared_dpll; + struct drm_i915_private *dev_priv = to_i915(crtc->dev); + struct drm_connector_state *conn_state; + struct drm_connector *conn; + int i; + + for_each_new_connector_in_state(old_state, conn, conn_state, i) { + struct intel_encoder *encoder = + to_intel_encoder(conn_state->best_encoder); + enum port port = encoder->port; + uint32_t val; + + if (conn_state->crtc != crtc) + continue; + + mutex_lock(&dev_priv->dpll_lock); + + val = I915_READ(DPCLKA_CFGCR0_ICL); + WARN_ON((val & DPCLKA_CFGCR0_DDI_CLK_OFF(port)) == 0); + + if (port == PORT_A || port == PORT_B) { + val &= ~DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(port); + val |= DPCLKA_CFGCR0_DDI_CLK_SEL(pll->info->id, port); + I915_WRITE(DPCLKA_CFGCR0_ICL, val); + POSTING_READ(DPCLKA_CFGCR0_ICL); + } + + val &= ~DPCLKA_CFGCR0_DDI_CLK_OFF(port); + I915_WRITE(DPCLKA_CFGCR0_ICL, val); + + mutex_unlock(&dev_priv->dpll_lock); + } +} + +void icl_unmap_plls_to_ports(struct drm_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct drm_atomic_state *old_state) +{ + struct drm_i915_private *dev_priv = to_i915(crtc->dev); + struct drm_connector_state *old_conn_state; + struct drm_connector *conn; + int i; + + for_each_old_connector_in_state(old_state, conn, old_conn_state, i) { + struct intel_encoder *encoder = + to_intel_encoder(old_conn_state->best_encoder); + enum port port = encoder->port; + + if (old_conn_state->crtc != crtc) + continue; + + mutex_lock(&dev_priv->dpll_lock); + I915_WRITE(DPCLKA_CFGCR0_ICL, + I915_READ(DPCLKA_CFGCR0_ICL) | + DPCLKA_CFGCR0_DDI_CLK_OFF(port)); + mutex_unlock(&dev_priv->dpll_lock); + } +} + static void intel_ddi_clk_select(struct intel_encoder *encoder, const struct intel_shared_dpll *pll) { @@ -2246,7 +2328,11 @@ static void intel_ddi_clk_select(struct intel_encoder *encoder, mutex_lock(&dev_priv->dpll_lock); - if (IS_CANNONLAKE(dev_priv)) { + if (IS_ICELAKE(dev_priv)) { + if (port >= PORT_C) + I915_WRITE(DDI_CLK_SEL(port), + icl_pll_to_ddi_pll_sel(encoder, pll)); + } else if (IS_CANNONLAKE(dev_priv)) { /* Configure DPCLKA_CFGCR0 to map the DPLL to the DDI. */ val = I915_READ(DPCLKA_CFGCR0); val &= ~DPCLKA_CFGCR0_DDI_CLK_SEL_MASK(port); @@ -2284,14 +2370,18 @@ static void intel_ddi_clk_disable(struct intel_encoder *encoder) struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); enum port port = encoder->port; - if (IS_CANNONLAKE(dev_priv)) + if (IS_ICELAKE(dev_priv)) { + if (port >= PORT_C) + I915_WRITE(DDI_CLK_SEL(port), DDI_CLK_SEL_NONE); + } else if (IS_CANNONLAKE(dev_priv)) { I915_WRITE(DPCLKA_CFGCR0, I915_READ(DPCLKA_CFGCR0) | DPCLKA_CFGCR0_DDI_CLK_OFF(port)); - else if (IS_GEN9_BC(dev_priv)) + } else if (IS_GEN9_BC(dev_priv)) { I915_WRITE(DPLL_CTRL2, I915_READ(DPLL_CTRL2) | DPLL_CTRL2_DDI_CLK_OFF(port)); - else if (INTEL_GEN(dev_priv) < 9) + } else if (INTEL_GEN(dev_priv) < 9) { I915_WRITE(PORT_CLK_SEL(port), PORT_CLK_SEL_NONE); + } } static void intel_ddi_pre_enable_dp(struct intel_encoder *encoder, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 48576ea2d36c..1d3da5b7d33c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5559,6 +5559,9 @@ static void haswell_crtc_enable(struct intel_crtc_state *pipe_config, if (intel_crtc->config->shared_dpll) intel_enable_shared_dpll(intel_crtc); + if (INTEL_GEN(dev_priv) >= 11) + icl_map_plls_to_ports(crtc, pipe_config, old_state); + if (intel_crtc_has_dp_encoder(intel_crtc->config)) intel_dp_set_m_n(intel_crtc, M1_N1); @@ -5756,6 +5759,9 @@ static void haswell_crtc_disable(struct intel_crtc_state *old_crtc_state, intel_ddi_disable_pipe_clock(intel_crtc->config); intel_encoders_post_disable(crtc, old_crtc_state, old_state); + + if (INTEL_GEN(dev_priv) >= 11) + icl_unmap_plls_to_ports(crtc, old_crtc_state, old_state); } static void i9xx_pfit_enable(struct intel_crtc *crtc) @@ -11386,6 +11392,16 @@ intel_pipe_config_compare(struct drm_i915_private *dev_priv, PIPE_CONF_CHECK_X(dpll_hw_state.pll9); PIPE_CONF_CHECK_X(dpll_hw_state.pll10); PIPE_CONF_CHECK_X(dpll_hw_state.pcsdw12); + PIPE_CONF_CHECK_X(dpll_hw_state.mg_refclkin_ctl); + PIPE_CONF_CHECK_X(dpll_hw_state.mg_clktop2_coreclkctl1); + PIPE_CONF_CHECK_X(dpll_hw_state.mg_clktop2_hsclkctl); + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_div0); + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_div1); + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_lf); + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_frac_lock); + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_ssc); + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_bias); + PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_tdc_coldst_bias); PIPE_CONF_CHECK_X(dsi_pll.ctrl); PIPE_CONF_CHECK_X(dsi_pll.div); diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.c b/drivers/gpu/drm/i915/intel_dpll_mgr.c index d5e114e9660b..14f5414ceab2 100644 --- a/drivers/gpu/drm/i915/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/intel_dpll_mgr.c @@ -2399,6 +2399,315 @@ static const struct intel_dpll_mgr cnl_pll_mgr = { .dump_hw_state = cnl_dump_hw_state, }; +static bool icl_calc_dpll_state(struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder, int clock, + struct intel_dpll_hw_state *pll_state) +{ + /* TODO */ + return true; +} + +static enum port icl_mg_pll_id_to_port(enum intel_dpll_id id) +{ + return id - DPLL_ID_ICL_MGPLL1 + PORT_C; +} + +static enum intel_dpll_id icl_port_to_mg_pll_id(enum port port) +{ + return port - PORT_C + DPLL_ID_ICL_MGPLL1; +} + +static bool icl_calc_mg_pll_state(struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder, int clock, + struct intel_dpll_hw_state *pll_state) +{ + /* TODO */ + return true; +} + +static struct intel_shared_dpll * +icl_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + struct intel_shared_dpll *pll; + struct intel_dpll_hw_state pll_state = {}; + enum port port = encoder->port; + enum intel_dpll_id min, max; + int clock = crtc_state->port_clock; + bool ret; + + switch (port) { + case PORT_A: + case PORT_B: + min = DPLL_ID_ICL_DPLL0; + max = DPLL_ID_ICL_DPLL1; + ret = icl_calc_dpll_state(crtc_state, encoder, clock, + &pll_state); + break; + case PORT_C: + case PORT_D: + case PORT_E: + case PORT_F: + min = icl_port_to_mg_pll_id(port); + max = min; + ret = icl_calc_mg_pll_state(crtc_state, encoder, clock, + &pll_state); + break; + default: + MISSING_CASE(port); + return NULL; + } + + if (!ret) { + DRM_DEBUG_KMS("Could not calculate PLL state.\n"); + return NULL; + } + + crtc_state->dpll_hw_state = pll_state; + + pll = intel_find_shared_dpll(crtc, crtc_state, min, max); + if (!pll) { + DRM_DEBUG_KMS("No PLL selected\n"); + return NULL; + } + + intel_reference_shared_dpll(pll, crtc_state); + + return pll; +} + +static i915_reg_t icl_pll_id_to_enable_reg(enum intel_dpll_id id) +{ + switch (id) { + default: + MISSING_CASE(id); + case DPLL_ID_ICL_DPLL0: + case DPLL_ID_ICL_DPLL1: + return CNL_DPLL_ENABLE(id); + case DPLL_ID_ICL_MGPLL1: + case DPLL_ID_ICL_MGPLL2: + case DPLL_ID_ICL_MGPLL3: + case DPLL_ID_ICL_MGPLL4: + return MG_PLL_ENABLE(icl_mg_pll_id_to_port(id)); + } +} + +static bool icl_pll_get_hw_state(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll, + struct intel_dpll_hw_state *hw_state) +{ + const enum intel_dpll_id id = pll->info->id; + uint32_t val; + enum port port; + bool ret = false; + + if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS)) + return false; + + val = I915_READ(icl_pll_id_to_enable_reg(id)); + if (!(val & PLL_ENABLE)) + goto out; + + switch (id) { + case DPLL_ID_ICL_DPLL0: + case DPLL_ID_ICL_DPLL1: + hw_state->cfgcr0 = I915_READ(ICL_DPLL_CFGCR0(id)); + hw_state->cfgcr1 = I915_READ(ICL_DPLL_CFGCR1(id)); + break; + case DPLL_ID_ICL_MGPLL1: + case DPLL_ID_ICL_MGPLL2: + case DPLL_ID_ICL_MGPLL3: + case DPLL_ID_ICL_MGPLL4: + port = icl_mg_pll_id_to_port(id); + hw_state->mg_refclkin_ctl = I915_READ(MG_REFCLKIN_CTL(port)); + hw_state->mg_clktop2_coreclkctl1 = + I915_READ(MG_CLKTOP2_CORECLKCTL1(port)); + hw_state->mg_clktop2_hsclkctl = + I915_READ(MG_CLKTOP2_HSCLKCTL(port)); + hw_state->mg_pll_div0 = I915_READ(MG_PLL_DIV0(port)); + hw_state->mg_pll_div1 = I915_READ(MG_PLL_DIV1(port)); + hw_state->mg_pll_lf = I915_READ(MG_PLL_LF(port)); + hw_state->mg_pll_frac_lock = I915_READ(MG_PLL_FRAC_LOCK(port)); + hw_state->mg_pll_ssc = I915_READ(MG_PLL_SSC(port)); + hw_state->mg_pll_bias = I915_READ(MG_PLL_BIAS(port)); + hw_state->mg_pll_tdc_coldst_bias = + I915_READ(MG_PLL_TDC_COLDST_BIAS(port)); + break; + default: + MISSING_CASE(id); + } + + ret = true; +out: + intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS); + return ret; +} + +static void icl_dpll_write(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) +{ + struct intel_dpll_hw_state *hw_state = &pll->state.hw_state; + const enum intel_dpll_id id = pll->info->id; + + I915_WRITE(ICL_DPLL_CFGCR0(id), hw_state->cfgcr0); + I915_WRITE(ICL_DPLL_CFGCR1(id), hw_state->cfgcr1); + POSTING_READ(ICL_DPLL_CFGCR1(id)); +} + +static void icl_mg_pll_write(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) +{ + struct intel_dpll_hw_state *hw_state = &pll->state.hw_state; + enum port port = icl_mg_pll_id_to_port(pll->info->id); + + I915_WRITE(MG_REFCLKIN_CTL(port), hw_state->mg_refclkin_ctl); + I915_WRITE(MG_CLKTOP2_CORECLKCTL1(port), + hw_state->mg_clktop2_coreclkctl1); + I915_WRITE(MG_CLKTOP2_HSCLKCTL(port), hw_state->mg_clktop2_hsclkctl); + I915_WRITE(MG_PLL_DIV0(port), hw_state->mg_pll_div0); + I915_WRITE(MG_PLL_DIV1(port), hw_state->mg_pll_div1); + I915_WRITE(MG_PLL_LF(port), hw_state->mg_pll_lf); + I915_WRITE(MG_PLL_FRAC_LOCK(port), hw_state->mg_pll_frac_lock); + I915_WRITE(MG_PLL_SSC(port), hw_state->mg_pll_ssc); + I915_WRITE(MG_PLL_BIAS(port), hw_state->mg_pll_bias); + I915_WRITE(MG_PLL_TDC_COLDST_BIAS(port), + hw_state->mg_pll_tdc_coldst_bias); + POSTING_READ(MG_PLL_TDC_COLDST_BIAS(port)); +} + +static void icl_pll_enable(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) +{ + const enum intel_dpll_id id = pll->info->id; + i915_reg_t enable_reg = icl_pll_id_to_enable_reg(id); + uint32_t val; + + val = I915_READ(enable_reg); + val |= PLL_POWER_ENABLE; + I915_WRITE(enable_reg, val); + + /* + * The spec says we need to "wait" but it also says it should be + * immediate. + */ + if (intel_wait_for_register(dev_priv, enable_reg, PLL_POWER_STATE, + PLL_POWER_STATE, 1)) + DRM_ERROR("PLL %d Power not enabled\n", id); + + switch (id) { + case DPLL_ID_ICL_DPLL0: + case DPLL_ID_ICL_DPLL1: + icl_dpll_write(dev_priv, pll); + break; + case DPLL_ID_ICL_MGPLL1: + case DPLL_ID_ICL_MGPLL2: + case DPLL_ID_ICL_MGPLL3: + case DPLL_ID_ICL_MGPLL4: + icl_mg_pll_write(dev_priv, pll); + break; + default: + MISSING_CASE(id); + } + + /* + * DVFS pre sequence would be here, but in our driver the cdclk code + * paths should already be setting the appropriate voltage, hence we do + * nothign here. + */ + + val = I915_READ(enable_reg); + val |= PLL_ENABLE; + I915_WRITE(enable_reg, val); + + if (intel_wait_for_register(dev_priv, enable_reg, PLL_LOCK, PLL_LOCK, + 1)) /* 600us actually. */ + DRM_ERROR("PLL %d not locked\n", id); + + /* DVFS post sequence would be here. See the comment above. */ +} + +static void icl_pll_disable(struct drm_i915_private *dev_priv, + struct intel_shared_dpll *pll) +{ + const enum intel_dpll_id id = pll->info->id; + i915_reg_t enable_reg = icl_pll_id_to_enable_reg(id); + uint32_t val; + + /* The first steps are done by intel_ddi_post_disable(). */ + + /* + * DVFS pre sequence would be here, but in our driver the cdclk code + * paths should already be setting the appropriate voltage, hence we do + * nothign here. + */ + + val = I915_READ(enable_reg); + val &= ~PLL_ENABLE; + I915_WRITE(enable_reg, val); + + /* Timeout is actually 1us. */ + if (intel_wait_for_register(dev_priv, enable_reg, PLL_LOCK, 0, 1)) + DRM_ERROR("PLL %d locked\n", id); + + /* DVFS post sequence would be here. See the comment above. */ + + val = I915_READ(enable_reg); + val &= ~PLL_POWER_ENABLE; + I915_WRITE(enable_reg, val); + + /* + * The spec says we need to "wait" but it also says it should be + * immediate. + */ + if (intel_wait_for_register(dev_priv, enable_reg, PLL_POWER_STATE, 0, + 1)) + DRM_ERROR("PLL %d Power not disabled\n", id); +} + +static void icl_dump_hw_state(struct drm_i915_private *dev_priv, + struct intel_dpll_hw_state *hw_state) +{ + DRM_DEBUG_KMS("dpll_hw_state: cfgcr0: 0x%x, cfgcr1: 0x%x, " + "mg_refclkin_ctl: 0x%x, hg_clktop2_coreclkctl1: 0x%x, " + "mg_clktop2_hsclkctl: 0x%x, mg_pll_div0: 0x%x, " + "mg_pll_div2: 0x%x, mg_pll_lf: 0x%x, " + "mg_pll_frac_lock: 0x%x, mg_pll_ssc: 0x%x, " + "mg_pll_bias: 0x%x, mg_pll_tdc_coldst_bias: 0x%x\n", + hw_state->cfgcr0, hw_state->cfgcr1, + hw_state->mg_refclkin_ctl, + hw_state->mg_clktop2_coreclkctl1, + hw_state->mg_clktop2_hsclkctl, + hw_state->mg_pll_div0, + hw_state->mg_pll_div1, + hw_state->mg_pll_lf, + hw_state->mg_pll_frac_lock, + hw_state->mg_pll_ssc, + hw_state->mg_pll_bias, + hw_state->mg_pll_tdc_coldst_bias); +} + +static const struct intel_shared_dpll_funcs icl_pll_funcs = { + .enable = icl_pll_enable, + .disable = icl_pll_disable, + .get_hw_state = icl_pll_get_hw_state, +}; + +static const struct dpll_info icl_plls[] = { + { "DPLL 0", &icl_pll_funcs, DPLL_ID_ICL_DPLL0, 0 }, + { "DPLL 1", &icl_pll_funcs, DPLL_ID_ICL_DPLL1, 0 }, + { "MG PLL 1", &icl_pll_funcs, DPLL_ID_ICL_MGPLL1, 0 }, + { "MG PLL 2", &icl_pll_funcs, DPLL_ID_ICL_MGPLL2, 0 }, + { "MG PLL 3", &icl_pll_funcs, DPLL_ID_ICL_MGPLL3, 0 }, + { "MG PLL 4", &icl_pll_funcs, DPLL_ID_ICL_MGPLL4, 0 }, + { }, +}; + +static const struct intel_dpll_mgr icl_pll_mgr = { + .dpll_info = icl_plls, + .get_dpll = icl_get_dpll, + .dump_hw_state = icl_dump_hw_state, +}; + /** * intel_shared_dpll_init - Initialize shared DPLLs * @dev: drm device @@ -2412,7 +2721,9 @@ void intel_shared_dpll_init(struct drm_device *dev) const struct dpll_info *dpll_info; int i; - if (IS_CANNONLAKE(dev_priv)) + if (IS_ICELAKE(dev_priv)) + dpll_mgr = &icl_pll_mgr; + else if (IS_CANNONLAKE(dev_priv)) dpll_mgr = &cnl_pll_mgr; else if (IS_GEN9_BC(dev_priv)) dpll_mgr = &skl_pll_mgr; diff --git a/drivers/gpu/drm/i915/intel_dpll_mgr.h b/drivers/gpu/drm/i915/intel_dpll_mgr.h index 4febfaa90bde..7a0cd564a9ee 100644 --- a/drivers/gpu/drm/i915/intel_dpll_mgr.h +++ b/drivers/gpu/drm/i915/intel_dpll_mgr.h @@ -103,6 +103,32 @@ enum intel_dpll_id { * @DPLL_ID_SKL_DPLL3: SKL and later DPLL3 */ DPLL_ID_SKL_DPLL3 = 3, + + + /** + * @DPLL_ID_ICL_DPLL0: ICL combo PHY DPLL0 + */ + DPLL_ID_ICL_DPLL0 = 0, + /** + * @DPLL_ID_ICL_DPLL1: ICL combo PHY DPLL1 + */ + DPLL_ID_ICL_DPLL1 = 1, + /** + * @DPLL_ID_ICL_MGPLL1: ICL MG PLL 1 port 1 (C) + */ + DPLL_ID_ICL_MGPLL1 = 2, + /** + * @DPLL_ID_ICL_MGPLL2: ICL MG PLL 1 port 2 (D) + */ + DPLL_ID_ICL_MGPLL2 = 3, + /** + * @DPLL_ID_ICL_MGPLL3: ICL MG PLL 1 port 3 (E) + */ + DPLL_ID_ICL_MGPLL3 = 4, + /** + * @DPLL_ID_ICL_MGPLL4: ICL MG PLL 1 port 4 (F) + */ + DPLL_ID_ICL_MGPLL4 = 5, }; #define I915_NUM_PLLS 6 @@ -135,6 +161,21 @@ struct intel_dpll_hw_state { /* bxt */ uint32_t ebb0, ebb4, pll0, pll1, pll2, pll3, pll6, pll8, pll9, pll10, pcsdw12; + + /* + * ICL uses the following, already defined: + * uint32_t cfgcr0, cfgcr1; + */ + uint32_t mg_refclkin_ctl; + uint32_t mg_clktop2_coreclkctl1; + uint32_t mg_clktop2_hsclkctl; + uint32_t mg_pll_div0; + uint32_t mg_pll_div1; + uint32_t mg_pll_lf; + uint32_t mg_pll_frac_lock; + uint32_t mg_pll_ssc; + uint32_t mg_pll_bias; + uint32_t mg_pll_tdc_coldst_bias; }; /** diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9bba0354ccd3..49053150e192 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1405,6 +1405,12 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp); u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder); int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder, bool enable); +void icl_map_plls_to_ports(struct drm_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct drm_atomic_state *old_state); +void icl_unmap_plls_to_ports(struct drm_crtc *crtc, + struct intel_crtc_state *crtc_state, + struct drm_atomic_state *old_state); unsigned int intel_fb_align_height(const struct drm_framebuffer *fb, int plane, unsigned int height);
This commit introduces the definitions for the ICL clocks and adds the basic functions to the shared DPLL framework. It adds code for the Enable and Disable sequences for some PLLs, but it does not have the code to compute the actual PLL values, which are marked as TODO comments and should be introduced as separate commits. Special thanks to James Ausmus for investigating and fixing a bug with the placement of icl_unmap_plls_to_ports() function. v2: - Rebase around dpll_lock changes. v3: - The spec now says what the timeouts should be. - Touch DPCLKA_CFGCR0_ICL at the appropriate time so we don't freeze the machine. - Checkpatch found a white space problem. - Small adjustments before upstreaming. v4: - Move the ICL checks out of the *map_plls_to_ports() functions (James) - Add extra encoder check (James) - Call icl_unmap_plls_to_ports() later (James) v5: - Rebase after the pll struct changes. v6: - Properly make the unmap function based on encoders_post_disable() with regarding to checks and iterators. - Address checkpatch comment on "min = max = x()". Cc: James Ausmus <james.ausmus@intel.com> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com> --- drivers/gpu/drm/i915/i915_debugfs.c | 22 +++ drivers/gpu/drm/i915/intel_ddi.c | 98 ++++++++++- drivers/gpu/drm/i915/intel_display.c | 16 ++ drivers/gpu/drm/i915/intel_dpll_mgr.c | 313 +++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_dpll_mgr.h | 41 +++++ drivers/gpu/drm/i915/intel_drv.h | 6 + 6 files changed, 491 insertions(+), 5 deletions(-)