diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_cdclk.c')
-rw-r--r-- | drivers/gpu/drm/i915/display/intel_cdclk.c | 207 |
1 files changed, 185 insertions, 22 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 2aaaba090cc0..2fb030b1ff1d 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -37,6 +37,7 @@ #include "intel_pci_config.h" #include "intel_pcode.h" #include "intel_psr.h" +#include "intel_vdsc.h" #include "vlv_sideband.h" /** @@ -469,7 +470,7 @@ static void hsw_get_cdclk(struct drm_i915_private *dev_priv, cdclk_config->cdclk = 450000; else if (freq == LCPLL_CLK_FREQ_450) cdclk_config->cdclk = 450000; - else if (IS_HSW_ULT(dev_priv)) + else if (IS_HASWELL_ULT(dev_priv)) cdclk_config->cdclk = 337500; else cdclk_config->cdclk = 540000; @@ -1908,7 +1909,7 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv, * mailbox communication, skip * this step. */ - if (DISPLAY_VER(dev_priv) >= 14) + if (DISPLAY_VER(dev_priv) >= 14 || IS_DG2(dev_priv)) /* NOOP */; else if (DISPLAY_VER(dev_priv) >= 11) ret = skl_pcode_request(&dev_priv->uncore, SKL_PCODE_CDCLK_CONTROL, @@ -1944,10 +1945,10 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv, * NOOP - No Pcode communication needed for * Display versions 14 and beyond */; - else if (DISPLAY_VER(dev_priv) >= 11) + else if (DISPLAY_VER(dev_priv) >= 11 && !IS_DG2(dev_priv)) ret = snb_pcode_write(&dev_priv->uncore, SKL_PCODE_CDCLK_CONTROL, cdclk_config->voltage_level); - else + if (DISPLAY_VER(dev_priv) < 11) { /* * The timeout isn't specified, the 2ms used here is based on * experiment. @@ -1958,7 +1959,7 @@ static void bxt_set_cdclk(struct drm_i915_private *dev_priv, HSW_PCODE_DE_WRITE_FREQ_REQ, cdclk_config->voltage_level, 150, 2); - + } if (ret) { drm_err(&dev_priv->drm, "PCode CDCLK freq set failed, (err %d, freq %d)\n", @@ -2254,6 +2255,38 @@ void intel_cdclk_dump_config(struct drm_i915_private *i915, cdclk_config->voltage_level); } +static void intel_pcode_notify(struct drm_i915_private *i915, + u8 voltage_level, + u8 active_pipe_count, + u16 cdclk, + bool cdclk_update_valid, + bool pipe_count_update_valid) +{ + int ret; + u32 update_mask = 0; + + if (!IS_DG2(i915)) + return; + + update_mask = DISPLAY_TO_PCODE_UPDATE_MASK(cdclk, active_pipe_count, voltage_level); + + if (cdclk_update_valid) + update_mask |= DISPLAY_TO_PCODE_CDCLK_VALID; + + if (pipe_count_update_valid) + update_mask |= DISPLAY_TO_PCODE_PIPE_COUNT_VALID; + + ret = skl_pcode_request(&i915->uncore, SKL_PCODE_CDCLK_CONTROL, + SKL_CDCLK_PREPARE_FOR_CHANGE | + update_mask, + SKL_CDCLK_READY_FOR_CHANGE, + SKL_CDCLK_READY_FOR_CHANGE, 3); + if (ret) + drm_err(&i915->drm, + "Failed to inform PCU about display config (err %d)\n", + ret); +} + /** * intel_set_cdclk - Push the CDCLK configuration to the hardware * @dev_priv: i915 device @@ -2323,6 +2356,88 @@ static void intel_set_cdclk(struct drm_i915_private *dev_priv, } } +static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + const struct intel_cdclk_state *old_cdclk_state = + intel_atomic_get_old_cdclk_state(state); + const struct intel_cdclk_state *new_cdclk_state = + intel_atomic_get_new_cdclk_state(state); + unsigned int cdclk = 0; u8 voltage_level, num_active_pipes = 0; + bool change_cdclk, update_pipe_count; + + if (!intel_cdclk_changed(&old_cdclk_state->actual, + &new_cdclk_state->actual) && + new_cdclk_state->active_pipes == + old_cdclk_state->active_pipes) + return; + + /* According to "Sequence Before Frequency Change", voltage level set to 0x3 */ + voltage_level = DISPLAY_TO_PCODE_VOLTAGE_MAX; + + change_cdclk = new_cdclk_state->actual.cdclk != old_cdclk_state->actual.cdclk; + update_pipe_count = hweight8(new_cdclk_state->active_pipes) > + hweight8(old_cdclk_state->active_pipes); + + /* + * According to "Sequence Before Frequency Change", + * if CDCLK is increasing, set bits 25:16 to upcoming CDCLK, + * if CDCLK is decreasing or not changing, set bits 25:16 to current CDCLK, + * which basically means we choose the maximum of old and new CDCLK, if we know both + */ + if (change_cdclk) + cdclk = max(new_cdclk_state->actual.cdclk, old_cdclk_state->actual.cdclk); + + /* + * According to "Sequence For Pipe Count Change", + * if pipe count is increasing, set bits 25:16 to upcoming pipe count + * (power well is enabled) + * no action if it is decreasing, before the change + */ + if (update_pipe_count) + num_active_pipes = hweight8(new_cdclk_state->active_pipes); + + intel_pcode_notify(i915, voltage_level, num_active_pipes, cdclk, + change_cdclk, update_pipe_count); +} + +static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + const struct intel_cdclk_state *new_cdclk_state = + intel_atomic_get_new_cdclk_state(state); + const struct intel_cdclk_state *old_cdclk_state = + intel_atomic_get_old_cdclk_state(state); + unsigned int cdclk = 0; u8 voltage_level, num_active_pipes = 0; + bool update_cdclk, update_pipe_count; + + /* According to "Sequence After Frequency Change", set voltage to used level */ + voltage_level = new_cdclk_state->actual.voltage_level; + + update_cdclk = new_cdclk_state->actual.cdclk != old_cdclk_state->actual.cdclk; + update_pipe_count = hweight8(new_cdclk_state->active_pipes) < + hweight8(old_cdclk_state->active_pipes); + + /* + * According to "Sequence After Frequency Change", + * set bits 25:16 to current CDCLK + */ + if (update_cdclk) + cdclk = new_cdclk_state->actual.cdclk; + + /* + * According to "Sequence For Pipe Count Change", + * if pipe count is decreasing, set bits 25:16 to current pipe count, + * after the change(power well is disabled) + * no action if it is increasing, after the change + */ + if (update_pipe_count) + num_active_pipes = hweight8(new_cdclk_state->active_pipes); + + intel_pcode_notify(i915, voltage_level, num_active_pipes, cdclk, + update_cdclk, update_pipe_count); +} + /** * intel_set_cdclk_pre_plane_update - Push the CDCLK state to the hardware * @state: intel atomic state @@ -2333,7 +2448,7 @@ static void intel_set_cdclk(struct drm_i915_private *dev_priv, void intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) { - struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct drm_i915_private *i915 = to_i915(state->base.dev); const struct intel_cdclk_state *old_cdclk_state = intel_atomic_get_old_cdclk_state(state); const struct intel_cdclk_state *new_cdclk_state = @@ -2344,11 +2459,14 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) &new_cdclk_state->actual)) return; + if (IS_DG2(i915)) + intel_cdclk_pcode_pre_notify(state); + if (pipe == INVALID_PIPE || old_cdclk_state->actual.cdclk <= new_cdclk_state->actual.cdclk) { - drm_WARN_ON(&dev_priv->drm, !new_cdclk_state->base.changed); + drm_WARN_ON(&i915->drm, !new_cdclk_state->base.changed); - intel_set_cdclk(dev_priv, &new_cdclk_state->actual, pipe); + intel_set_cdclk(i915, &new_cdclk_state->actual, pipe); } } @@ -2362,7 +2480,7 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) void intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) { - struct drm_i915_private *dev_priv = to_i915(state->base.dev); + struct drm_i915_private *i915 = to_i915(state->base.dev); const struct intel_cdclk_state *old_cdclk_state = intel_atomic_get_old_cdclk_state(state); const struct intel_cdclk_state *new_cdclk_state = @@ -2373,11 +2491,14 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) &new_cdclk_state->actual)) return; + if (IS_DG2(i915)) + intel_cdclk_pcode_post_notify(state); + if (pipe != INVALID_PIPE && old_cdclk_state->actual.cdclk > new_cdclk_state->actual.cdclk) { - drm_WARN_ON(&dev_priv->drm, !new_cdclk_state->base.changed); + drm_WARN_ON(&i915->drm, !new_cdclk_state->base.changed); - intel_set_cdclk(dev_priv, &new_cdclk_state->actual, pipe); + intel_set_cdclk(i915, &new_cdclk_state->actual, pipe); } } @@ -2487,9 +2608,16 @@ int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state) * When we decide to use only one VDSC engine, since * each VDSC operates with 1 ppc throughput, pixel clock * cannot be higher than the VDSC clock (cdclk) + * If there 2 VDSC engines, then pixel clock can't be higher than + * VDSC clock(cdclk) * 2 and so on. */ - if (crtc_state->dsc.compression_enable && !crtc_state->dsc.dsc_split) - min_cdclk = max(min_cdclk, (int)crtc_state->pixel_rate); + if (crtc_state->dsc.compression_enable) { + int num_vdsc_instances = intel_dsc_get_num_vdsc_instances(crtc_state); + + min_cdclk = max_t(int, min_cdclk, + DIV_ROUND_UP(crtc_state->pixel_rate, + num_vdsc_instances)); + } /* * HACK. Currently for TGL/DG2 platforms we calculate @@ -2839,7 +2967,7 @@ int intel_cdclk_atomic_check(struct intel_atomic_state *state, { const struct intel_cdclk_state *old_cdclk_state; const struct intel_cdclk_state *new_cdclk_state; - struct intel_plane_state *plane_state; + struct intel_plane_state __maybe_unused *plane_state; struct intel_plane *plane; int ret; int i; @@ -2883,6 +3011,21 @@ int intel_cdclk_init(struct drm_i915_private *dev_priv) return 0; } +static bool intel_cdclk_need_serialize(struct drm_i915_private *i915, + const struct intel_cdclk_state *old_cdclk_state, + const struct intel_cdclk_state *new_cdclk_state) +{ + bool power_well_cnt_changed = hweight8(old_cdclk_state->active_pipes) != + hweight8(new_cdclk_state->active_pipes); + bool cdclk_changed = intel_cdclk_changed(&old_cdclk_state->actual, + &new_cdclk_state->actual); + /* + * We need to poke hw for gen >= 12, because we notify PCode if + * pipe power well count changes. + */ + return cdclk_changed || (IS_DG2(i915) && power_well_cnt_changed); +} + int intel_modeset_calc_cdclk(struct intel_atomic_state *state) { struct drm_i915_private *dev_priv = to_i915(state->base.dev); @@ -2904,8 +3047,7 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state) if (ret) return ret; - if (intel_cdclk_changed(&old_cdclk_state->actual, - &new_cdclk_state->actual)) { + if (intel_cdclk_need_serialize(dev_priv, old_cdclk_state, new_cdclk_state)) { /* * Also serialize commits across all crtcs * if the actual hw needs to be poked. @@ -3013,7 +3155,7 @@ static int intel_compute_max_dotclk(struct drm_i915_private *dev_priv) */ void intel_update_max_cdclk(struct drm_i915_private *dev_priv) { - if (IS_JSL_EHL(dev_priv)) { + if (IS_JASPERLAKE(dev_priv) || IS_ELKHARTLAKE(dev_priv)) { if (dev_priv->display.cdclk.hw.ref == 24000) dev_priv->display.cdclk.max_cdclk_freq = 552000; else @@ -3058,9 +3200,9 @@ void intel_update_max_cdclk(struct drm_i915_private *dev_priv) */ if (intel_de_read(dev_priv, FUSE_STRAP) & HSW_CDCLK_LIMIT) dev_priv->display.cdclk.max_cdclk_freq = 450000; - else if (IS_BDW_ULX(dev_priv)) + else if (IS_BROADWELL_ULX(dev_priv)) dev_priv->display.cdclk.max_cdclk_freq = 450000; - else if (IS_BDW_ULT(dev_priv)) + else if (IS_BROADWELL_ULT(dev_priv)) dev_priv->display.cdclk.max_cdclk_freq = 540000; else dev_priv->display.cdclk.max_cdclk_freq = 675000; @@ -3247,6 +3389,27 @@ u32 intel_read_rawclk(struct drm_i915_private *dev_priv) return freq; } +static int i915_cdclk_info_show(struct seq_file *m, void *unused) +{ + struct drm_i915_private *i915 = m->private; + + seq_printf(m, "Current CD clock frequency: %d kHz\n", i915->display.cdclk.hw.cdclk); + seq_printf(m, "Max CD clock frequency: %d kHz\n", i915->display.cdclk.max_cdclk_freq); + seq_printf(m, "Max pixel clock frequency: %d kHz\n", i915->max_dotclk_freq); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(i915_cdclk_info); + +void intel_cdclk_debugfs_register(struct drm_i915_private *i915) +{ + struct drm_minor *minor = i915->drm.primary; + + debugfs_create_file("i915_cdclk_info", 0444, minor->debugfs_root, + i915, &i915_cdclk_info_fops); +} + static const struct intel_cdclk_funcs mtl_cdclk_funcs = { .get_cdclk = bxt_get_cdclk, .set_cdclk = bxt_set_cdclk, @@ -3404,10 +3567,10 @@ void intel_init_cdclk_hooks(struct drm_i915_private *dev_priv) dev_priv->display.cdclk.table = dg2_cdclk_table; } else if (IS_ALDERLAKE_P(dev_priv)) { /* Wa_22011320316:adl-p[a0] */ - if (IS_ADLP_DISPLAY_STEP(dev_priv, STEP_A0, STEP_B0)) { + if (IS_ALDERLAKE_P(dev_priv) && IS_DISPLAY_STEP(dev_priv, STEP_A0, STEP_B0)) { dev_priv->display.cdclk.table = adlp_a_step_cdclk_table; dev_priv->display.funcs.cdclk = &tgl_cdclk_funcs; - } else if (IS_ADLP_RPLU(dev_priv)) { + } else if (IS_RAPTORLAKE_U(dev_priv)) { dev_priv->display.cdclk.table = rplu_cdclk_table; dev_priv->display.funcs.cdclk = &rplu_cdclk_funcs; } else { @@ -3420,7 +3583,7 @@ void intel_init_cdclk_hooks(struct drm_i915_private *dev_priv) } else if (DISPLAY_VER(dev_priv) >= 12) { dev_priv->display.funcs.cdclk = &tgl_cdclk_funcs; dev_priv->display.cdclk.table = icl_cdclk_table; - } else if (IS_JSL_EHL(dev_priv)) { + } else if (IS_JASPERLAKE(dev_priv) || IS_ELKHARTLAKE(dev_priv)) { dev_priv->display.funcs.cdclk = &ehl_cdclk_funcs; dev_priv->display.cdclk.table = icl_cdclk_table; } else if (DISPLAY_VER(dev_priv) >= 11) { |