On Mon, 27 Feb 2023, Stanislav Lisovskiy <stanislav.lisovskiy@xxxxxxxxx> wrote: > Display to communicate display pipe count/CDCLK/voltage configuration > to Pcode for more accurate power accounting for gen >= 12. > Existing sequence is only sending the voltage value to the Pcode. > Adding new sequence with current cdclk associate with voltage value masking. > Adding pcode request when any pipe power well will disable or enable. > > v2: - Make intel_cdclk_need_serialize static to make CI compiler happy. > > Signed-off-by: Stanislav Lisovskiy <stanislav.lisovskiy@xxxxxxxxx> > --- > drivers/gpu/drm/i915/display/intel_cdclk.c | 174 +++++++++++++++++++-- > drivers/gpu/drm/i915/display/intel_cdclk.h | 2 + > drivers/gpu/drm/i915/i915_reg.h | 14 ++ > 3 files changed, 176 insertions(+), 14 deletions(-) > > diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c > index 084a483f9776..df5a21f6a393 100644 > --- a/drivers/gpu/drm/i915/display/intel_cdclk.c > +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c > @@ -1932,10 +1932,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. > @@ -1946,7 +1946,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", > @@ -2242,6 +2242,40 @@ 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, > + u8 cdclk, > + bool cdclk_update_valid, > + bool pipe_count_update_valid) > +{ > + int ret; > + u32 update_mask = 0; > + > + if (DISPLAY_VER(i915) < 12) > + 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); > + return; Superfluous return. > + } > +} > + > /** > * intel_set_cdclk - Push the CDCLK configuration to the hardware > * @dev_priv: i915 device > @@ -2311,6 +2345,98 @@ static void intel_set_cdclk(struct drm_i915_private *dev_priv, > } > } > > +/** > + * intel_cdclk_power_usage_to_pcode_pre_notification: display to pcode notification > + * before the enabling power wells. > + * send notification with cdclk, number of pipes, voltage_level. > + * @state: intel atomic state > + */ > +void intel_cdclk_power_usage_to_pcode_pre_notification(struct intel_atomic_state *state) This could be static? Not happy with the name, really, but at least it could use a verb. Notify instead of notification. > +{ > + struct drm_i915_private *dev_priv = to_i915(state->base.dev); No new dev_priv please. > + 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(dev_priv, voltage_level, num_active_pipes, cdclk, > + change_cdclk, update_pipe_count); > +} > + > +/* intel_cdclk_power_usage_to_pcode_post_notification: after frequency change sending > + * voltage_level, active pipes, current CDCLK frequency. > + * @state: intel atomic state > + */ > +void intel_cdclk_power_usage_to_pcode_post_notification(struct intel_atomic_state *state) Ditto about static & naming. > +{ > + struct drm_i915_private *dev_priv = to_i915(state->base.dev); No new dev_priv please. > + 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(dev_priv, 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 > @@ -2321,7 +2447,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 = > @@ -2332,11 +2458,14 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state) > &new_cdclk_state->actual)) > return; > > + if (DISPLAY_VER(i915) >= 12) > + intel_cdclk_power_usage_to_pcode_pre_notification(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); > } > } > > @@ -2350,7 +2479,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 = > @@ -2361,11 +2490,14 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state) > &new_cdclk_state->actual)) > return; > > + if (DISPLAY_VER(i915) >= 12) > + intel_cdclk_power_usage_to_pcode_post_notification(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); > } > } > > @@ -2871,6 +3003,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) > +{ > + /* > + * We need to poke hw for gen >= 12, because we notify PCode if > + * pipe power well count changes. > + */ > + return intel_cdclk_changed(&old_cdclk_state->actual, > + &new_cdclk_state->actual) || > + ((DISPLAY_VER(i915) >= 12 && > + hweight8(old_cdclk_state->active_pipes) != > + hweight8(new_cdclk_state->active_pipes))); That's getting a bit unweildy for one expression. > +} > + > int intel_modeset_calc_cdclk(struct intel_atomic_state *state) > { > struct drm_i915_private *dev_priv = to_i915(state->base.dev); > @@ -2892,8 +3039,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. > @@ -2905,9 +3051,9 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state) > old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk || > intel_cdclk_changed(&old_cdclk_state->logical, > &new_cdclk_state->logical)) { > - ret = intel_atomic_lock_global_state(&new_cdclk_state->base); > - if (ret) > - return ret; > + ret = intel_atomic_lock_global_state(&new_cdclk_state->base); > + if (ret) > + return ret; > } else { > return 0; > } > diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.h b/drivers/gpu/drm/i915/display/intel_cdclk.h > index 51e2f6a11ce4..fa356adc61d9 100644 > --- a/drivers/gpu/drm/i915/display/intel_cdclk.h > +++ b/drivers/gpu/drm/i915/display/intel_cdclk.h > @@ -64,6 +64,8 @@ bool intel_cdclk_needs_modeset(const struct intel_cdclk_config *a, > const struct intel_cdclk_config *b); > void intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state); > void intel_set_cdclk_post_plane_update(struct intel_atomic_state *state); > +void intel_cdclk_power_usage_to_pcode_pre_notification(struct intel_atomic_state *state); > +void intel_cdclk_power_usage_to_pcode_post_notification(struct intel_atomic_state *state); > void intel_cdclk_dump_config(struct drm_i915_private *i915, > const struct intel_cdclk_config *cdclk_config, > const char *context); > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h > index c1efa655fb68..9f786952585b 100644 > --- a/drivers/gpu/drm/i915/i915_reg.h > +++ b/drivers/gpu/drm/i915/i915_reg.h > @@ -6526,6 +6526,20 @@ > #define ICL_PCODE_MEM_SS_READ_GLOBAL_INFO (0x0 << 8) > #define ICL_PCODE_MEM_SS_READ_QGV_POINT_INFO(point) (((point) << 16) | (0x1 << 8)) > #define ADL_PCODE_MEM_SS_READ_PSF_GV_INFO ((0) | (0x2 << 8)) > +#define DISPLAY_TO_PCODE_CDCLK_MAX 0x28D > +#define DISPLAY_TO_PCODE_VOLTAGE_MASK REG_GENMASK(1, 0) > +#define DISPLAY_TO_PCODE_VOLTAGE_MAX DISPLAY_TO_PCODE_VOLTAGE_MASK > +#define DISPLAY_TO_PCODE_CDCLK_VALID REG_BIT(27) > +#define DISPLAY_TO_PCODE_PIPE_COUNT_VALID REG_BIT(31) > +#define DISPLAY_TO_PCODE_CDCLK_MASK REG_GENMASK(25, 16) > +#define DISPLAY_TO_PCODE_PIPE_COUNT_MASK REG_GENMASK(30, 28) > +#define DISPLAY_TO_PCODE_CDCLK(x) REG_FIELD_PREP(DISPLAY_TO_PCODE_CDCLK_MASK, (x)) > +#define DISPLAY_TO_PCODE_PIPE_COUNT(x) REG_FIELD_PREP(DISPLAY_TO_PCODE_PIPE_COUNT_MASK, (x)) > +#define DISPLAY_TO_PCODE_VOLTAGE(x) REG_FIELD_PREP(DISPLAY_TO_PCODE_VOLTAGE_MASK, (x)) > +#define DISPLAY_TO_PCODE_UPDATE_MASK(cdclk, num_pipes, voltage_level) \ > + (DISPLAY_TO_PCODE_CDCLK(cdclk)) | \ > + (DISPLAY_TO_PCODE_PIPE_COUNT(num_pipes)) | \ > + (DISPLAY_TO_PCODE_VOLTAGE(voltage_level)) > #define ICL_PCODE_SAGV_DE_MEM_SS_CONFIG 0xe > #define ICL_PCODE_REP_QGV_MASK REG_GENMASK(1, 0) > #define ICL_PCODE_REP_QGV_SAFE REG_FIELD_PREP(ICL_PCODE_REP_QGV_MASK, 0) -- Jani Nikula, Intel Open Source Graphics Center