Re: [PATCH v15 7/7] drm/i915/mtl: Add support for PM DEMAND

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Thank you for the series and the review. Pushed with a minor spelling fix in the comment s/qclck_gc/qclk_gv/

--Radhakrishna(RK) Sripada

> -----Original Message-----
> From: Intel-gfx <intel-gfx-bounces@xxxxxxxxxxxxxxxxxxxxx> On Behalf Of Vinod
> Govindapillai
> Sent: Tuesday, June 6, 2023 1:11 PM
> To: intel-gfx@xxxxxxxxxxxxxxxxxxxxx
> Cc: jani.saarinene@xxxxxxxxx
> Subject:  [PATCH v15 7/7] drm/i915/mtl: Add support for PM DEMAND
> 
> From: Mika Kahola <mika.kahola@xxxxxxxxx>
> 
> MTL introduces a new way to instruct the PUnit with
> power and bandwidth requirements of DE. Add the functionality
> to program the registers and handle waits using interrupts.
> The current wait time for timeouts is programmed for 10 msecs to
> factor in the worst case scenarios. Changes made to use REG_BIT
> for a register that we touched(GEN8_DE_MISC_IER _MMIO).
> 
> Wa_14016740474 is added which applies to Xe_LPD+ display
> 
> v2: checkpatch warning fixes, simplify program pmdemand part
> 
> v3: update to dbufs and pipes values to pmdemand register(stan)
>     Removed the macro usage in update_pmdemand_values()
> 
> v4: move the pmdemand_pre_plane_update before cdclk update
>     pmdemand_needs_update included cdclk params comparisons
>     pmdemand_state NULL check (Gustavo)
>     pmdemand.o in sorted order in the makefile (Jani)
>     update pmdemand misc irq handler loop (Gustavo)
>     active phys bitmask and programming correction (Gustavo)
> 
> v5: simplify pmdemand_state structure
>     simplify methods to find active phys and max port clock
>     Timeout in case of previou pmdemand task pending (Gustavo)
> 
> v6: rebasing
>     updates to max_ddiclk calculations (Gustavo)
>     updates to active_phys count method (Gustavo)
> 
> v7: use two separate loop to iterate throug old and new
>     crtc states to calculate the active phys (Gustavo)
> 
> v8: use uniform function names (Gustavo)
> 
> v9: For phys change iterate through connectors (Imre)
>     Look for change in phys for pmdemand update (Gustavo, Imre)
>     Some more stlying changes (Imre)
>     Update pmdemand state during HW readout/sanitize (Imre)
> 
> v10: Fix CI checkpatch warnings
> 
> v11: use correct pmdemand object pointer during hw readout,
>      simplify the check for phys need update (Gustavo)
> 
> v12: Handle possible non serialize cases (Imre)
>      Initialise also pmdemand params HW readout (Imre)
>      Update active phys mask during sanitize calls (Imre)
>      Check TC/encoder changes to limit connector update (Imre)
> 
> v13: Check display version before accessing pmdemand functions
> 
> v14: Move is_serialized to intel_global_state.c
>      simplify update params and other stlying issues (Imre)
> 
> Bspec: 66451, 64636, 64602, 64603
> Cc: Matt Atwood <matthew.s.atwood@xxxxxxxxx>
> Cc: Matt Roper <matthew.d.roper@xxxxxxxxx>
> Cc: Lucas De Marchi <lucas.demarchi@xxxxxxxxx>
> Cc: Gustavo Sousa <gustavo.sousa@xxxxxxxxx>
> Signed-off-by: José Roberto de Souza <jose.souza@xxxxxxxxx>
> Signed-off-by: Radhakrishna Sripada <radhakrishna.sripada@xxxxxxxxx>
> Signed-off-by: Gustavo Sousa <gustavo.sousa@xxxxxxxxx>
> Signed-off-by: Mika Kahola <mika.kahola@xxxxxxxxx>
> Signed-off-by: Vinod Govindapillai <vinod.govindapillai@xxxxxxxxx>
> Reviewed-by: Stanislav Lisovskiy <stanislav.lisovskiy@xxxxxxxxx> #v4
> Acked-by: Gustavo Sousa <gustavo.sousa@xxxxxxxxx> #v11
> Reviewed-by: Imre Deak <imre.deak@xxxxxxxxx>
> ---
>  drivers/gpu/drm/i915/Makefile                 |   1 +
>  drivers/gpu/drm/i915/display/intel_display.c  |  14 +
>  .../gpu/drm/i915/display/intel_display_core.h |   9 +
>  .../drm/i915/display/intel_display_driver.c   |   7 +
>  .../gpu/drm/i915/display/intel_display_irq.c  |  23 +-
>  .../drm/i915/display/intel_display_power.c    |  14 +-
>  .../gpu/drm/i915/display/intel_global_state.c |  12 +
>  .../gpu/drm/i915/display/intel_global_state.h |   2 +
>  .../drm/i915/display/intel_modeset_setup.c    |  32 +
>  drivers/gpu/drm/i915/display/intel_pmdemand.c | 620 ++++++++++++++++++
>  drivers/gpu/drm/i915/display/intel_pmdemand.h |  67 ++
>  drivers/gpu/drm/i915/i915_reg.h               |  26 +-
>  12 files changed, 821 insertions(+), 6 deletions(-)
>  create mode 100644 drivers/gpu/drm/i915/display/intel_pmdemand.c
>  create mode 100644 drivers/gpu/drm/i915/display/intel_pmdemand.h
> 
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index 1c9ed4c52760..2cd8de174bf6 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -269,6 +269,7 @@ i915-y += \
>  	display/intel_pch_display.o \
>  	display/intel_pch_refclk.o \
>  	display/intel_plane_initial.o \
> +	display/intel_pmdemand.o \
>  	display/intel_psr.o \
>  	display/intel_quirks.o \
>  	display/intel_sprite.o \
> diff --git a/drivers/gpu/drm/i915/display/intel_display.c
> b/drivers/gpu/drm/i915/display/intel_display.c
> index f51a55f4e9d0..5cbf5eae2414 100644
> --- a/drivers/gpu/drm/i915/display/intel_display.c
> +++ b/drivers/gpu/drm/i915/display/intel_display.c
> @@ -99,6 +99,7 @@
>  #include "intel_pcode.h"
>  #include "intel_pipe_crc.h"
>  #include "intel_plane_initial.h"
> +#include "intel_pmdemand.h"
>  #include "intel_pps.h"
>  #include "intel_psr.h"
>  #include "intel_sdvo.h"
> @@ -6352,6 +6353,10 @@ int intel_atomic_check(struct drm_device *dev,
>  			return ret;
>  	}
> 
> +	ret = intel_pmdemand_atomic_check(state);
> +	if (ret)
> +		goto fail;
> +
>  	ret = intel_atomic_check_crtcs(state);
>  	if (ret)
>  		goto fail;
> @@ -6997,6 +7002,14 @@ static void intel_atomic_commit_tail(struct
> intel_atomic_state *state)
>  	for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i)
>  		crtc->config = new_crtc_state;
> 
> +	/*
> +	 * In XE_LPD+ Pmdemand combines many parameters such as voltage
> index,
> +	 * plls, cdclk frequency, QGV point selection parameter etc. Voltage
> +	 * index, cdclk/ddiclk frequencies are supposed to be configured before
> +	 * the cdclk config is set.
> +	 */
> +	intel_pmdemand_pre_plane_update(state);
> +
>  	if (state->modeset) {
>  		drm_atomic_helper_update_legacy_modeset_state(dev,
> &state->base);
> 
> @@ -7116,6 +7129,7 @@ static void intel_atomic_commit_tail(struct
> intel_atomic_state *state)
>  		intel_verify_planes(state);
> 
>  	intel_sagv_post_plane_update(state);
> +	intel_pmdemand_post_plane_update(state);
> 
>  	drm_atomic_helper_commit_hw_done(&state->base);
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h
> b/drivers/gpu/drm/i915/display/intel_display_core.h
> index dd8e08c8598f..8d2243c71dd8 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_core.h
> +++ b/drivers/gpu/drm/i915/display/intel_display_core.h
> @@ -345,6 +345,15 @@ struct intel_display {
>  		struct intel_global_obj obj;
>  	} dbuf;
> 
> +	struct {
> +		wait_queue_head_t waitqueue;
> +
> +		/* mutex to protect pmdemand programming sequence */
> +		struct mutex lock;
> +
> +		struct intel_global_obj obj;
> +	} pmdemand;
> +
>  	struct {
>  		/*
>  		 * dkl.phy_lock protects against concurrent access of the
> diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c
> b/drivers/gpu/drm/i915/display/intel_display_driver.c
> index 60ce10fc7205..dc8de861339d 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_driver.c
> +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
> @@ -47,6 +47,7 @@
>  #include "intel_opregion.h"
>  #include "intel_overlay.h"
>  #include "intel_plane_initial.h"
> +#include "intel_pmdemand.h"
>  #include "intel_pps.h"
>  #include "intel_quirks.h"
>  #include "intel_vga.h"
> @@ -211,6 +212,8 @@ int intel_display_driver_probe_noirq(struct
> drm_i915_private *i915)
>  	if (ret < 0)
>  		goto cleanup_vga;
> 
> +	intel_pmdemand_init_early(i915);
> +
>  	intel_power_domains_init_hw(i915, false);
> 
>  	if (!HAS_DISPLAY(i915))
> @@ -240,6 +243,10 @@ int intel_display_driver_probe_noirq(struct
> drm_i915_private *i915)
>  	if (ret)
>  		goto cleanup_vga_client_pw_domain_dmc;
> 
> +	ret = intel_pmdemand_init(i915);
> +	if (ret)
> +		goto cleanup_vga_client_pw_domain_dmc;
> +
>  	init_llist_head(&i915->display.atomic_helper.free_list);
>  	INIT_WORK(&i915->display.atomic_helper.free_work,
>  		  intel_atomic_helper_free_state_worker);
> diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c
> b/drivers/gpu/drm/i915/display/intel_display_irq.c
> index 3b2a287d2041..0b3739310f81 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_irq.c
> +++ b/drivers/gpu/drm/i915/display/intel_display_irq.c
> @@ -18,6 +18,7 @@
>  #include "intel_fifo_underrun.h"
>  #include "intel_gmbus.h"
>  #include "intel_hotplug_irq.h"
> +#include "intel_pmdemand.h"
>  #include "intel_psr.h"
>  #include "intel_psr_regs.h"
> 
> @@ -827,12 +828,27 @@ static u32 gen8_de_pipe_fault_mask(struct
> drm_i915_private *dev_priv)
>  		return GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
>  }
> 
> +static void intel_pmdemand_irq_handler(struct drm_i915_private *dev_priv)
> +{
> +	wake_up_all(&dev_priv->display.pmdemand.waitqueue);
> +}
> +
>  static void
>  gen8_de_misc_irq_handler(struct drm_i915_private *dev_priv, u32 iir)
>  {
>  	bool found = false;
> 
> -	if (iir & GEN8_DE_MISC_GSE) {
> +	if (DISPLAY_VER(dev_priv) >= 14) {
> +		if (iir & (XELPDP_PMDEMAND_RSP |
> +			   XELPDP_PMDEMAND_RSPTOUT_ERR)) {
> +			if (iir & XELPDP_PMDEMAND_RSPTOUT_ERR)
> +				drm_dbg(&dev_priv->drm,
> +					"Error waiting for Punit PM Demand
> Response\n");
> +
> +			intel_pmdemand_irq_handler(dev_priv);
> +			found = true;
> +		}
> +	} else if (iir & GEN8_DE_MISC_GSE) {
>  		intel_opregion_asle_intr(dev_priv);
>  		found = true;
>  	}
> @@ -1576,7 +1592,10 @@ void gen8_de_irq_postinstall(struct
> drm_i915_private *dev_priv)
>  	if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv))
>  		de_port_masked |= BXT_DE_PORT_GMBUS;
> 
> -	if (DISPLAY_VER(dev_priv) >= 11) {
> +	if (DISPLAY_VER(dev_priv) >= 14) {
> +		de_misc_masked |= XELPDP_PMDEMAND_RSPTOUT_ERR |
> +				  XELPDP_PMDEMAND_RSP;
> +	} else if (DISPLAY_VER(dev_priv) >= 11) {
>  		enum port port;
> 
>  		if (intel_bios_is_dsi_present(dev_priv, &port))
> diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c
> b/drivers/gpu/drm/i915/display/intel_display_power.c
> index 2f4f00ae2f57..db5437043904 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_power.c
> +++ b/drivers/gpu/drm/i915/display/intel_display_power.c
> @@ -20,6 +20,7 @@
>  #include "intel_mchbar_regs.h"
>  #include "intel_pch_refclk.h"
>  #include "intel_pcode.h"
> +#include "intel_pmdemand.h"
>  #include "intel_pps_regs.h"
>  #include "intel_snps_phy.h"
>  #include "skl_watermark.h"
> @@ -1082,20 +1083,29 @@ void gen9_dbuf_slices_update(struct
> drm_i915_private *dev_priv,
> 
>  static void gen9_dbuf_enable(struct drm_i915_private *dev_priv)
>  {
> +	u8 slices_mask;
> +
>  	dev_priv->display.dbuf.enabled_slices =
>  		intel_enabled_dbuf_slices_mask(dev_priv);
> 
> +	slices_mask = BIT(DBUF_S1) | dev_priv->display.dbuf.enabled_slices;
> +
> +	if (DISPLAY_VER(dev_priv) >= 14)
> +		intel_pmdemand_program_dbuf(dev_priv, slices_mask);
> +
>  	/*
>  	 * Just power up at least 1 slice, we will
>  	 * figure out later which slices we have and what we need.
>  	 */
> -	gen9_dbuf_slices_update(dev_priv, BIT(DBUF_S1) |
> -				dev_priv->display.dbuf.enabled_slices);
> +	gen9_dbuf_slices_update(dev_priv, slices_mask);
>  }
> 
>  static void gen9_dbuf_disable(struct drm_i915_private *dev_priv)
>  {
>  	gen9_dbuf_slices_update(dev_priv, 0);
> +
> +	if (DISPLAY_VER(dev_priv) >= 14)
> +		intel_pmdemand_program_dbuf(dev_priv, 0);
>  }
> 
>  static void gen12_dbuf_slices_config(struct drm_i915_private *dev_priv)
> diff --git a/drivers/gpu/drm/i915/display/intel_global_state.c
> b/drivers/gpu/drm/i915/display/intel_global_state.c
> index 02b593b1e2ea..e8e8be54143b 100644
> --- a/drivers/gpu/drm/i915/display/intel_global_state.c
> +++ b/drivers/gpu/drm/i915/display/intel_global_state.c
> @@ -255,3 +255,15 @@ int intel_atomic_serialize_global_state(struct
> intel_global_state *obj_state)
> 
>  	return 0;
>  }
> +
> +bool
> +intel_atomic_global_state_is_serialized(struct intel_atomic_state *state)
> +{
> +	struct drm_i915_private *i915 = to_i915(state->base.dev);
> +	struct intel_crtc *crtc;
> +
> +	for_each_intel_crtc(&i915->drm, crtc)
> +		if (!intel_atomic_get_new_crtc_state(state, crtc))
> +			return false;
> +	return true;
> +}
> diff --git a/drivers/gpu/drm/i915/display/intel_global_state.h
> b/drivers/gpu/drm/i915/display/intel_global_state.h
> index f01ee0bb3e5a..5477de8f0b30 100644
> --- a/drivers/gpu/drm/i915/display/intel_global_state.h
> +++ b/drivers/gpu/drm/i915/display/intel_global_state.h
> @@ -87,4 +87,6 @@ void intel_atomic_clear_global_state(struct
> intel_atomic_state *state);
>  int intel_atomic_lock_global_state(struct intel_global_state *obj_state);
>  int intel_atomic_serialize_global_state(struct intel_global_state *obj_state);
> 
> +bool intel_atomic_global_state_is_serialized(struct intel_atomic_state *state);
> +
>  #endif
> diff --git a/drivers/gpu/drm/i915/display/intel_modeset_setup.c
> b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
> index 5ff99ca7f1de..b8f43efb0ab5 100644
> --- a/drivers/gpu/drm/i915/display/intel_modeset_setup.c
> +++ b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
> @@ -26,6 +26,7 @@
>  #include "intel_fifo_underrun.h"
>  #include "intel_modeset_setup.h"
>  #include "intel_pch_display.h"
> +#include "intel_pmdemand.h"
>  #include "intel_tc.h"
>  #include "intel_vblank.h"
>  #include "intel_wm.h"
> @@ -115,6 +116,8 @@ static void set_encoder_for_connector(struct
> intel_connector *connector,
>  static void reset_encoder_connector_state(struct intel_encoder *encoder)
>  {
>  	struct drm_i915_private *i915 = to_i915(encoder->base.dev);
> +	struct intel_pmdemand_state *pmdemand_state =
> +		to_intel_pmdemand_state(i915->display.pmdemand.obj.state);
>  	struct intel_connector *connector;
>  	struct drm_connector_list_iter conn_iter;
> 
> @@ -123,6 +126,10 @@ static void reset_encoder_connector_state(struct
> intel_encoder *encoder)
>  		if (connector->base.encoder != &encoder->base)
>  			continue;
> 
> +		/* Clear the corresponding bit in pmdemand active phys mask */
> +		intel_pmdemand_update_phys_mask(i915, encoder,
> +						pmdemand_state, false);
> +
>  		set_encoder_for_connector(connector, NULL);
> 
>  		connector->base.dpms = DRM_MODE_DPMS_OFF;
> @@ -151,6 +158,8 @@ static void
> intel_crtc_disable_noatomic_complete(struct intel_crtc *crtc)
>  		to_intel_cdclk_state(i915->display.cdclk.obj.state);
>  	struct intel_dbuf_state *dbuf_state =
>  		to_intel_dbuf_state(i915->display.dbuf.obj.state);
> +	struct intel_pmdemand_state *pmdemand_state =
> +		to_intel_pmdemand_state(i915->display.pmdemand.obj.state);
>  	struct intel_crtc_state *crtc_state =
>  		to_intel_crtc_state(crtc->base.state);
>  	enum pipe pipe = crtc->pipe;
> @@ -174,6 +183,8 @@ static void
> intel_crtc_disable_noatomic_complete(struct intel_crtc *crtc)
> 
>  	bw_state->data_rate[pipe] = 0;
>  	bw_state->num_active_planes[pipe] = 0;
> +
> +	intel_pmdemand_update_port_clock(i915, pmdemand_state, pipe, 0);
>  }
> 
>  /*
> @@ -552,6 +563,8 @@ static void intel_sanitize_encoder(struct intel_encoder
> *encoder)
>  	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
>  	struct intel_crtc_state *crtc_state = crtc ?
>  		to_intel_crtc_state(crtc->base.state) : NULL;
> +	struct intel_pmdemand_state *pmdemand_state =
> +		to_intel_pmdemand_state(i915->display.pmdemand.obj.state);
> 
>  	/*
>  	 * We need to check both for a crtc link (meaning that the encoder is
> @@ -575,6 +588,10 @@ static void intel_sanitize_encoder(struct intel_encoder
> *encoder)
>  			    encoder->base.base.id,
>  			    encoder->base.name);
> 
> +		/* Clear the corresponding bit in pmdemand active phys mask */
> +		intel_pmdemand_update_phys_mask(i915, encoder,
> +						pmdemand_state, false);
> +
>  		/*
>  		 * Connector is active, but has no active pipe. This is fallout
>  		 * from our resume register restoring. Disable the encoder
> @@ -661,6 +678,8 @@ static void intel_modeset_readout_hw_state(struct
> drm_i915_private *i915)
>  		to_intel_cdclk_state(i915->display.cdclk.obj.state);
>  	struct intel_dbuf_state *dbuf_state =
>  		to_intel_dbuf_state(i915->display.dbuf.obj.state);
> +	struct intel_pmdemand_state *pmdemand_state =
> +		to_intel_pmdemand_state(i915->display.pmdemand.obj.state);
>  	enum pipe pipe;
>  	struct intel_crtc *crtc;
>  	struct intel_encoder *encoder;
> @@ -724,7 +743,15 @@ static void intel_modeset_readout_hw_state(struct
> drm_i915_private *i915)
>  					intel_encoder_get_config(encoder,
> slave_crtc_state);
>  				}
>  			}
> +
> +			intel_pmdemand_update_phys_mask(i915, encoder,
> +							pmdemand_state,
> +							true);
>  		} else {
> +			intel_pmdemand_update_phys_mask(i915, encoder,
> +							pmdemand_state,
> +							false);
> +
>  			encoder->base.crtc = NULL;
>  		}
> 
> @@ -841,8 +868,13 @@ static void intel_modeset_readout_hw_state(struct
> drm_i915_private *i915)
>  		cdclk_state->min_voltage_level[crtc->pipe] =
>  			crtc_state->min_voltage_level;
> 
> +		intel_pmdemand_update_port_clock(i915, pmdemand_state,
> pipe,
> +						 crtc_state->port_clock);
> +
>  		intel_bw_crtc_update(bw_state, crtc_state);
>  	}
> +
> +	intel_pmdemand_init_pmdemand_params(i915, pmdemand_state);
>  }
> 
>  static void
> diff --git a/drivers/gpu/drm/i915/display/intel_pmdemand.c
> b/drivers/gpu/drm/i915/display/intel_pmdemand.c
> new file mode 100644
> index 000000000000..2f8cf29a861b
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/display/intel_pmdemand.c
> @@ -0,0 +1,620 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2023 Intel Corporation
> + */
> +
> +#include <linux/bitops.h>
> +
> +#include "i915_drv.h"
> +#include "i915_reg.h"
> +#include "intel_atomic.h"
> +#include "intel_bw.h"
> +#include "intel_cdclk.h"
> +#include "intel_de.h"
> +#include "intel_display_trace.h"
> +#include "intel_pmdemand.h"
> +#include "skl_watermark.h"
> +
> +static struct intel_global_state *
> +intel_pmdemand_duplicate_state(struct intel_global_obj *obj)
> +{
> +	struct intel_pmdemand_state *pmdemand_state;
> +
> +	pmdemand_state = kmemdup(obj->state, sizeof(*pmdemand_state),
> GFP_KERNEL);
> +	if (!pmdemand_state)
> +		return NULL;
> +
> +	return &pmdemand_state->base;
> +}
> +
> +static void intel_pmdemand_destroy_state(struct intel_global_obj *obj,
> +					 struct intel_global_state *state)
> +{
> +	kfree(state);
> +}
> +
> +static const struct intel_global_state_funcs intel_pmdemand_funcs = {
> +	.atomic_duplicate_state = intel_pmdemand_duplicate_state,
> +	.atomic_destroy_state = intel_pmdemand_destroy_state,
> +};
> +
> +static struct intel_pmdemand_state *
> +intel_atomic_get_pmdemand_state(struct intel_atomic_state *state)
> +{
> +	struct drm_i915_private *i915 = to_i915(state->base.dev);
> +	struct intel_global_state *pmdemand_state =
> +		intel_atomic_get_global_obj_state(state,
> +						  &i915-
> >display.pmdemand.obj);
> +
> +	if (IS_ERR(pmdemand_state))
> +		return ERR_CAST(pmdemand_state);
> +
> +	return to_intel_pmdemand_state(pmdemand_state);
> +}
> +
> +static struct intel_pmdemand_state *
> +intel_atomic_get_old_pmdemand_state(struct intel_atomic_state *state)
> +{
> +	struct drm_i915_private *i915 = to_i915(state->base.dev);
> +	struct intel_global_state *pmdemand_state =
> +		intel_atomic_get_old_global_obj_state(state,
> +						      &i915-
> >display.pmdemand.obj);
> +
> +	if (!pmdemand_state)
> +		return NULL;
> +
> +	return to_intel_pmdemand_state(pmdemand_state);
> +}
> +
> +static struct intel_pmdemand_state *
> +intel_atomic_get_new_pmdemand_state(struct intel_atomic_state *state)
> +{
> +	struct drm_i915_private *i915 = to_i915(state->base.dev);
> +	struct intel_global_state *pmdemand_state =
> +		intel_atomic_get_new_global_obj_state(state,
> +						      &i915-
> >display.pmdemand.obj);
> +
> +	if (!pmdemand_state)
> +		return NULL;
> +
> +	return to_intel_pmdemand_state(pmdemand_state);
> +}
> +
> +int intel_pmdemand_init(struct drm_i915_private *i915)
> +{
> +	struct intel_pmdemand_state *pmdemand_state;
> +
> +	pmdemand_state = kzalloc(sizeof(*pmdemand_state), GFP_KERNEL);
> +	if (!pmdemand_state)
> +		return -ENOMEM;
> +
> +	intel_atomic_global_obj_init(i915, &i915->display.pmdemand.obj,
> +				     &pmdemand_state->base,
> +				     &intel_pmdemand_funcs);
> +
> +	if (IS_MTL_DISPLAY_STEP(i915, STEP_A0, STEP_C0))
> +		/* Wa_14016740474 */
> +		intel_de_rmw(i915, XELPD_CHICKEN_DCPR_3, 0,
> DMD_RSP_TIMEOUT_DISABLE);
> +
> +	return 0;
> +}
> +
> +void intel_pmdemand_init_early(struct drm_i915_private *i915)
> +{
> +	mutex_init(&i915->display.pmdemand.lock);
> +	init_waitqueue_head(&i915->display.pmdemand.waitqueue);
> +}
> +
> +void
> +intel_pmdemand_update_phys_mask(struct drm_i915_private *i915,
> +				struct intel_encoder *encoder,
> +				struct intel_pmdemand_state
> *pmdemand_state,
> +				bool set_bit)
> +{
> +	enum phy phy;
> +
> +	if (DISPLAY_VER(i915) < 14)
> +		return;
> +
> +	if (!encoder)
> +		return;
> +
> +	phy = intel_port_to_phy(i915, encoder->port);
> +	if (intel_phy_is_tc(i915, phy))
> +		return;
> +
> +	if (set_bit)
> +		pmdemand_state->active_combo_phys_mask |= BIT(phy);
> +	else
> +		pmdemand_state->active_combo_phys_mask &= ~BIT(phy);
> +}
> +
> +void
> +intel_pmdemand_update_port_clock(struct drm_i915_private *i915,
> +				 struct intel_pmdemand_state
> *pmdemand_state,
> +				 enum pipe pipe, int port_clock)
> +{
> +	if (DISPLAY_VER(i915) < 14)
> +		return;
> +
> +	pmdemand_state->ddi_clocks[pipe] = port_clock;
> +}
> +
> +static void
> +intel_pmdemand_update_max_ddiclk(struct drm_i915_private *i915,
> +				 struct intel_atomic_state *state,
> +				 struct intel_pmdemand_state
> *pmdemand_state)
> +{
> +	int max_ddiclk = 0;
> +	const struct intel_crtc_state *new_crtc_state;
> +	struct intel_crtc *crtc;
> +	int i;
> +
> +	for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i)
> +		intel_pmdemand_update_port_clock(i915, pmdemand_state,
> +						 crtc->pipe,
> +						 new_crtc_state->port_clock);
> +
> +	for (i = 0; i < ARRAY_SIZE(pmdemand_state->ddi_clocks); i++)
> +		max_ddiclk = max(pmdemand_state->ddi_clocks[i],
> max_ddiclk);
> +
> +	pmdemand_state->params.ddiclk_max = DIV_ROUND_UP(max_ddiclk,
> 1000);
> +}
> +
> +static void
> +intel_pmdemand_update_connector_phys(struct drm_i915_private *i915,
> +				     struct intel_atomic_state *state,
> +				     struct drm_connector_state *conn_state,
> +				     bool set_bit,
> +				     struct intel_pmdemand_state
> *pmdemand_state)
> +{
> +	struct intel_encoder *encoder = to_intel_encoder(conn_state-
> >best_encoder);
> +	struct intel_crtc *crtc = to_intel_crtc(conn_state->crtc);
> +	struct intel_crtc_state *crtc_state;
> +
> +	if (!crtc)
> +		return;
> +
> +	if (set_bit)
> +		crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
> +	else
> +		crtc_state = intel_atomic_get_old_crtc_state(state, crtc);
> +
> +	if (!crtc_state->hw.active)
> +		return;
> +
> +	intel_pmdemand_update_phys_mask(i915, encoder, pmdemand_state,
> +					set_bit);
> +}
> +
> +static void
> +intel_pmdemand_update_active_non_tc_phys(struct drm_i915_private *i915,
> +					 struct intel_atomic_state *state,
> +					 struct intel_pmdemand_state
> *pmdemand_state)
> +{
> +	struct drm_connector_state *old_conn_state;
> +	struct drm_connector_state *new_conn_state;
> +	struct drm_connector *connector;
> +	int i;
> +
> +	for_each_oldnew_connector_in_state(&state->base, connector,
> +					   old_conn_state, new_conn_state, i) {
> +		if (!intel_connector_needs_modeset(state, connector))
> +			continue;
> +
> +		/* First clear the active phys in the old connector state */
> +		intel_pmdemand_update_connector_phys(i915, state,
> +						     old_conn_state, false,
> +						     pmdemand_state);
> +
> +		/* Then set the active phys in new connector state */
> +		intel_pmdemand_update_connector_phys(i915, state,
> +						     new_conn_state, true,
> +						     pmdemand_state);
> +	}
> +
> +	pmdemand_state->params.active_phys =
> +		min_t(u16, hweight16(pmdemand_state-
> >active_combo_phys_mask),
> +		      7);
> +}
> +
> +static bool
> +intel_pmdemand_encoder_has_tc_phy(struct drm_i915_private *i915,
> +				  struct intel_encoder *encoder)
> +{
> +	enum phy phy;
> +
> +	if (!encoder)
> +		return false;
> +
> +	phy = intel_port_to_phy(i915, encoder->port);
> +
> +	return intel_phy_is_tc(i915, phy);
> +}
> +
> +static bool
> +intel_pmdemand_connector_needs_update(struct intel_atomic_state *state)
> +{
> +	struct drm_i915_private *i915 = to_i915(state->base.dev);
> +	struct drm_connector_state *old_conn_state;
> +	struct drm_connector_state *new_conn_state;
> +	struct drm_connector *connector;
> +	int i;
> +
> +	for_each_oldnew_connector_in_state(&state->base, connector,
> +					   old_conn_state, new_conn_state, i) {
> +		struct intel_encoder *old_encoder =
> +			to_intel_encoder(old_conn_state->best_encoder);
> +		struct intel_encoder *new_encoder =
> +			to_intel_encoder(new_conn_state->best_encoder);
> +
> +		if (!intel_connector_needs_modeset(state, connector))
> +			continue;
> +
> +		if (old_encoder == new_encoder ||
> +		    (intel_pmdemand_encoder_has_tc_phy(i915, old_encoder)
> &&
> +		     intel_pmdemand_encoder_has_tc_phy(i915, new_encoder)))
> +			continue;
> +
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +static bool intel_pmdemand_needs_update(struct intel_atomic_state *state)
> +{
> +	const struct intel_bw_state *new_bw_state, *old_bw_state;
> +	const struct intel_cdclk_state *new_cdclk_state, *old_cdclk_state;
> +	const struct intel_crtc_state *new_crtc_state, *old_crtc_state;
> +	const struct intel_dbuf_state *new_dbuf_state, *old_dbuf_state;
> +	struct intel_crtc *crtc;
> +	int i;
> +
> +	new_bw_state = intel_atomic_get_new_bw_state(state);
> +	old_bw_state = intel_atomic_get_old_bw_state(state);
> +	if (new_bw_state && new_bw_state->qgv_point_peakbw !=
> +	    old_bw_state->qgv_point_peakbw)
> +		return true;
> +
> +	new_dbuf_state = intel_atomic_get_new_dbuf_state(state);
> +	old_dbuf_state = intel_atomic_get_old_dbuf_state(state);
> +	if (new_dbuf_state &&
> +	    (new_dbuf_state->active_pipes !=
> +	     old_dbuf_state->active_pipes ||
> +	     new_dbuf_state->enabled_slices !=
> +	     old_dbuf_state->enabled_slices))
> +		return true;
> +
> +	new_cdclk_state = intel_atomic_get_new_cdclk_state(state);
> +	old_cdclk_state = intel_atomic_get_old_cdclk_state(state);
> +	if (new_cdclk_state &&
> +	    (new_cdclk_state->actual.cdclk !=
> +	     old_cdclk_state->actual.cdclk ||
> +	     new_cdclk_state->actual.voltage_level !=
> +	     old_cdclk_state->actual.voltage_level))
> +		return true;
> +
> +	for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
> +					    new_crtc_state, i)
> +		if (new_crtc_state->port_clock != old_crtc_state->port_clock)
> +			return true;
> +
> +	return intel_pmdemand_connector_needs_update(state);
> +}
> +
> +int intel_pmdemand_atomic_check(struct intel_atomic_state *state)
> +{
> +	struct drm_i915_private *i915 = to_i915(state->base.dev);
> +	const struct intel_bw_state *new_bw_state;
> +	const struct intel_cdclk_state *new_cdclk_state;
> +	const struct intel_dbuf_state *new_dbuf_state;
> +	struct intel_pmdemand_state *new_pmdemand_state;
> +
> +	if (DISPLAY_VER(i915) < 14)
> +		return 0;
> +
> +	if (!intel_pmdemand_needs_update(state))
> +		return 0;
> +
> +	new_pmdemand_state = intel_atomic_get_pmdemand_state(state);
> +	if (IS_ERR(new_pmdemand_state))
> +		return PTR_ERR(new_pmdemand_state);
> +
> +	new_bw_state = intel_atomic_get_bw_state(state);
> +	if (IS_ERR(new_bw_state))
> +		return PTR_ERR(new_bw_state);
> +
> +	/* firmware will calculate the qclck_gc_index, requirement is set to 0 */
> +	new_pmdemand_state->params.qclk_gv_index = 0;
> +	new_pmdemand_state->params.qclk_gv_bw = new_bw_state-
> >qgv_point_peakbw;
> +
> +	new_dbuf_state = intel_atomic_get_dbuf_state(state);
> +	if (IS_ERR(new_dbuf_state))
> +		return PTR_ERR(new_dbuf_state);
> +
> +	new_pmdemand_state->params.active_pipes =
> +		min_t(u8, hweight8(new_dbuf_state->active_pipes), 3);
> +	new_pmdemand_state->params.active_dbufs =
> +		min_t(u8, hweight8(new_dbuf_state->enabled_slices), 3);
> +
> +	new_cdclk_state = intel_atomic_get_cdclk_state(state);
> +	if (IS_ERR(new_cdclk_state))
> +		return PTR_ERR(new_cdclk_state);
> +
> +	new_pmdemand_state->params.voltage_index =
> +		new_cdclk_state->actual.voltage_level;
> +	new_pmdemand_state->params.cdclk_freq_mhz =
> +		DIV_ROUND_UP(new_cdclk_state->actual.cdclk, 1000);
> +
> +	intel_pmdemand_update_max_ddiclk(i915, state,
> new_pmdemand_state);
> +
> +	intel_pmdemand_update_active_non_tc_phys(i915, state,
> new_pmdemand_state);
> +
> +	/*
> +	 * Active_PLLs starts with 1 because of CDCLK PLL.
> +	 * TODO: Missing to account genlock filter when it gets used.
> +	 */
> +	new_pmdemand_state->params.plls =
> +		min_t(u16, new_pmdemand_state->params.active_phys + 1, 7);
> +
> +	/*
> +	 * Setting scalers to max as it can not be calculated during flips and
> +	 * fastsets without taking global states locks.
> +	 */
> +	new_pmdemand_state->params.scalers = 7;
> +
> +	if (state->base.allow_modeset)
> +		return
> intel_atomic_serialize_global_state(&new_pmdemand_state->base);
> +	else
> +		return
> intel_atomic_lock_global_state(&new_pmdemand_state->base);
> +}
> +
> +static bool intel_pmdemand_check_prev_transaction(struct drm_i915_private
> *i915)
> +{
> +	return !(intel_de_wait_for_clear(i915,
> +
> XELPDP_INITIATE_PMDEMAND_REQUEST(1),
> +					 XELPDP_PMDEMAND_REQ_ENABLE,
> 10) ||
> +		 intel_de_wait_for_clear(i915,
> +					 GEN12_DCPR_STATUS_1,
> +
> XELPDP_PMDEMAND_INFLIGHT_STATUS, 10));
> +}
> +
> +void
> +intel_pmdemand_init_pmdemand_params(struct drm_i915_private *i915,
> +				    struct intel_pmdemand_state
> *pmdemand_state)
> +{
> +	u32 reg1, reg2;
> +
> +	if (DISPLAY_VER(i915) < 14)
> +		return;
> +
> +	mutex_lock(&i915->display.pmdemand.lock);
> +	if (drm_WARN_ON(&i915->drm,
> +			!intel_pmdemand_check_prev_transaction(i915))) {
> +		memset(&pmdemand_state->params, 0,
> +		       sizeof(pmdemand_state->params));
> +		goto unlock;
> +	}
> +
> +	reg1 = intel_de_read(i915,
> XELPDP_INITIATE_PMDEMAND_REQUEST(0));
> +
> +	reg2 = intel_de_read(i915,
> XELPDP_INITIATE_PMDEMAND_REQUEST(1));
> +
> +	/* Set 1*/
> +	pmdemand_state->params.qclk_gv_bw =
> +		REG_FIELD_GET(XELPDP_PMDEMAND_QCLK_GV_BW_MASK,
> reg1);
> +	pmdemand_state->params.voltage_index =
> +		REG_FIELD_GET(XELPDP_PMDEMAND_VOLTAGE_INDEX_MASK,
> reg1);
> +	pmdemand_state->params.qclk_gv_index =
> +		REG_FIELD_GET(XELPDP_PMDEMAND_QCLK_GV_INDEX_MASK,
> reg1);
> +	pmdemand_state->params.active_pipes =
> +		REG_FIELD_GET(XELPDP_PMDEMAND_PIPES_MASK, reg1);
> +	pmdemand_state->params.active_dbufs =
> +		REG_FIELD_GET(XELPDP_PMDEMAND_DBUFS_MASK, reg1);
> +	pmdemand_state->params.active_phys =
> +		REG_FIELD_GET(XELPDP_PMDEMAND_PHYS_MASK, reg1);
> +
> +	/* Set 2*/
> +	pmdemand_state->params.cdclk_freq_mhz =
> +		REG_FIELD_GET(XELPDP_PMDEMAND_CDCLK_FREQ_MASK,
> reg2);
> +	pmdemand_state->params.ddiclk_max =
> +		REG_FIELD_GET(XELPDP_PMDEMAND_DDICLK_FREQ_MASK,
> reg2);
> +	pmdemand_state->params.scalers =
> +		REG_FIELD_GET(XELPDP_PMDEMAND_SCALERS_MASK, reg2);
> +
> +unlock:
> +	mutex_unlock(&i915->display.pmdemand.lock);
> +}
> +
> +static bool intel_pmdemand_req_complete(struct drm_i915_private *i915)
> +{
> +	return !(intel_de_read(i915,
> XELPDP_INITIATE_PMDEMAND_REQUEST(1)) &
> +		 XELPDP_PMDEMAND_REQ_ENABLE);
> +}
> +
> +static void intel_pmdemand_wait(struct drm_i915_private *i915)
> +{
> +	if (!wait_event_timeout(i915->display.pmdemand.waitqueue,
> +				intel_pmdemand_req_complete(i915),
> +				msecs_to_jiffies_timeout(10)))
> +		drm_err(&i915->drm,
> +			"timed out waiting for Punit PM Demand Response\n");
> +}
> +
> +/* Required to be programmed during Display Init Sequences. */
> +void intel_pmdemand_program_dbuf(struct drm_i915_private *i915,
> +				 u8 dbuf_slices)
> +{
> +	u32 dbufs = min_t(u32, hweight8(dbuf_slices), 3);
> +
> +	mutex_lock(&i915->display.pmdemand.lock);
> +	if (drm_WARN_ON(&i915->drm,
> +			!intel_pmdemand_check_prev_transaction(i915)))
> +		goto unlock;
> +
> +	intel_de_rmw(i915, XELPDP_INITIATE_PMDEMAND_REQUEST(0),
> +		     XELPDP_PMDEMAND_DBUFS_MASK,
> +		     REG_FIELD_PREP(XELPDP_PMDEMAND_DBUFS_MASK,
> dbufs));
> +	intel_de_rmw(i915, XELPDP_INITIATE_PMDEMAND_REQUEST(1), 0,
> +		     XELPDP_PMDEMAND_REQ_ENABLE);
> +
> +	intel_pmdemand_wait(i915);
> +
> +unlock:
> +	mutex_unlock(&i915->display.pmdemand.lock);
> +}
> +
> +static void
> +intel_pmdemand_update_params(const struct intel_pmdemand_state *new,
> +			     const struct intel_pmdemand_state *old,
> +			     u32 *reg1, u32 *reg2, bool serialized)
> +{
> +	/*
> +	 * The pmdemand parameter updates happens in two steps. Pre plane
> and
> +	 * post plane updates. During the pre plane, as DE might still be
> +	 * handling with some old operations, to avoid unexpected performance
> +	 * issues, program the pmdemand parameters with higher of old and
> new
> +	 * values. And then after once settled, use the new parameter values
> +	 * as part of the post plane update.
> +	 *
> +	 * If the pmdemand params update happens without modeset allowed,
> this
> +	 * means we can't serialize the updates. So that implies possibility of
> +	 * some parallel atomic commits affecting the pmdemand parameters.
> In
> +	 * that case, we need to consider the current values from the register
> +	 * as well. So in pre-plane case, we need to check the max of old, new
> +	 * and current register value if not serialized. In post plane update
> +	 * we need to consider max of new and current register value if not
> +	 * serialized
> +	 */
> +
> +#define update_reg(reg, field, mask) do { \
> +	u32 current_val = serialized ? 0 : REG_FIELD_GET((mask), *(reg)); \
> +	u32 old_val = old ? old->params.field : 0; \
> +	u32 new_val = new->params.field; \
> +\
> +	*(reg) &= ~(mask); \
> +	*(reg) |= REG_FIELD_PREP((mask), max3(old_val, new_val,
> current_val)); \
> +} while (0)
> +
> +	/* Set 1*/
> +	update_reg(reg1, qclk_gv_bw,
> XELPDP_PMDEMAND_QCLK_GV_BW_MASK);
> +	update_reg(reg1, voltage_index,
> XELPDP_PMDEMAND_VOLTAGE_INDEX_MASK);
> +	update_reg(reg1, qclk_gv_index,
> XELPDP_PMDEMAND_QCLK_GV_INDEX_MASK);
> +	update_reg(reg1, active_pipes, XELPDP_PMDEMAND_PIPES_MASK);
> +	update_reg(reg1, active_dbufs, XELPDP_PMDEMAND_DBUFS_MASK);
> +	update_reg(reg1, active_phys, XELPDP_PMDEMAND_PHYS_MASK);
> +
> +	/* Set 2*/
> +	update_reg(reg2, cdclk_freq_mhz,
> XELPDP_PMDEMAND_CDCLK_FREQ_MASK);
> +	update_reg(reg2, ddiclk_max,
> XELPDP_PMDEMAND_DDICLK_FREQ_MASK);
> +	update_reg(reg2, scalers, XELPDP_PMDEMAND_SCALERS_MASK);
> +	update_reg(reg2, plls, XELPDP_PMDEMAND_PLLS_MASK);
> +
> +#undef update_reg
> +}
> +
> +static void
> +intel_pmdemand_program_params(struct drm_i915_private *i915,
> +			      const struct intel_pmdemand_state *new,
> +			      const struct intel_pmdemand_state *old,
> +			      bool serialized)
> +{
> +	bool changed = false;
> +	u32 reg1, mod_reg1;
> +	u32 reg2, mod_reg2;
> +
> +	mutex_lock(&i915->display.pmdemand.lock);
> +	if (drm_WARN_ON(&i915->drm,
> +			!intel_pmdemand_check_prev_transaction(i915)))
> +		goto unlock;
> +
> +	reg1 = intel_de_read(i915,
> XELPDP_INITIATE_PMDEMAND_REQUEST(0));
> +	mod_reg1 = reg1;
> +
> +	reg2 = intel_de_read(i915,
> XELPDP_INITIATE_PMDEMAND_REQUEST(1));
> +	mod_reg2 = reg2;
> +
> +	intel_pmdemand_update_params(new, old, &mod_reg1, &mod_reg2,
> +				     serialized);
> +
> +	if (reg1 != mod_reg1) {
> +		intel_de_write(i915,
> XELPDP_INITIATE_PMDEMAND_REQUEST(0),
> +			       mod_reg1);
> +		changed = true;
> +	}
> +
> +	if (reg2 != mod_reg2) {
> +		intel_de_write(i915,
> XELPDP_INITIATE_PMDEMAND_REQUEST(1),
> +			       mod_reg2);
> +		changed = true;
> +	}
> +
> +	/* Initiate pm demand request only if register values are changed */
> +	if (!changed)
> +		goto unlock;
> +
> +	drm_dbg_kms(&i915->drm,
> +		    "initate pmdemand request values: (0x%x 0x%x)\n",
> +		    mod_reg1, mod_reg2);
> +
> +	intel_de_rmw(i915, XELPDP_INITIATE_PMDEMAND_REQUEST(1), 0,
> +		     XELPDP_PMDEMAND_REQ_ENABLE);
> +
> +	intel_pmdemand_wait(i915);
> +
> +unlock:
> +	mutex_unlock(&i915->display.pmdemand.lock);
> +}
> +
> +static bool
> +intel_pmdemand_state_changed(const struct intel_pmdemand_state *new,
> +			     const struct intel_pmdemand_state *old)
> +{
> +	return memcmp(&new->params, &old->params, sizeof(new->params))
> != 0;
> +}
> +
> +void intel_pmdemand_pre_plane_update(struct intel_atomic_state *state)
> +{
> +	struct drm_i915_private *i915 = to_i915(state->base.dev);
> +	const struct intel_pmdemand_state *new_pmdemand_state =
> +		intel_atomic_get_new_pmdemand_state(state);
> +	const struct intel_pmdemand_state *old_pmdemand_state =
> +		intel_atomic_get_old_pmdemand_state(state);
> +
> +	if (DISPLAY_VER(i915) < 14)
> +		return;
> +
> +	if (!new_pmdemand_state ||
> +	    !intel_pmdemand_state_changed(new_pmdemand_state,
> +					  old_pmdemand_state))
> +		return;
> +
> +	WARN_ON(!new_pmdemand_state->base.changed);
> +
> +	intel_pmdemand_program_params(i915, new_pmdemand_state,
> +				      old_pmdemand_state,
> +
> intel_atomic_global_state_is_serialized(state));
> +}
> +
> +void intel_pmdemand_post_plane_update(struct intel_atomic_state *state)
> +{
> +	struct drm_i915_private *i915 = to_i915(state->base.dev);
> +	const struct intel_pmdemand_state *new_pmdemand_state =
> +		intel_atomic_get_new_pmdemand_state(state);
> +	const struct intel_pmdemand_state *old_pmdemand_state =
> +		intel_atomic_get_old_pmdemand_state(state);
> +
> +	if (DISPLAY_VER(i915) < 14)
> +		return;
> +
> +	if (!new_pmdemand_state ||
> +	    !intel_pmdemand_state_changed(new_pmdemand_state,
> +					  old_pmdemand_state))
> +		return;
> +
> +	WARN_ON(!new_pmdemand_state->base.changed);
> +
> +	intel_pmdemand_program_params(i915, new_pmdemand_state, NULL,
> +
> intel_atomic_global_state_is_serialized(state));
> +}
> diff --git a/drivers/gpu/drm/i915/display/intel_pmdemand.h
> b/drivers/gpu/drm/i915/display/intel_pmdemand.h
> new file mode 100644
> index 000000000000..2941a1a18b72
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/display/intel_pmdemand.h
> @@ -0,0 +1,67 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2023 Intel Corporation
> + */
> +
> +#ifndef __INTEL_PMDEMAND_H__
> +#define __INTEL_PMDEMAND_H__
> +
> +#include "intel_display_limits.h"
> +#include "intel_global_state.h"
> +
> +struct drm_i915_private;
> +struct intel_atomic_state;
> +struct intel_crtc_state;
> +struct intel_encoder;
> +struct intel_plane_state;
> +
> +struct pmdemand_params {
> +	u16 qclk_gv_bw;
> +	u8 voltage_index;
> +	u8 qclk_gv_index;
> +	u8 active_pipes;
> +	u8 active_dbufs;
> +	/* Total number of non type C active phys from active_phys_mask */
> +	u8 active_phys;
> +	u8 plls;
> +	u16 cdclk_freq_mhz;
> +	/* max from ddi_clocks[] */
> +	u16 ddiclk_max;
> +	u8 scalers;
> +};
> +
> +struct intel_pmdemand_state {
> +	struct intel_global_state base;
> +
> +	/* Maintain a persistent list of port clocks across all crtcs */
> +	int ddi_clocks[I915_MAX_PIPES];
> +
> +	/* Maintain a persistent list of non type C phys mask */
> +	u16 active_combo_phys_mask;
> +
> +	/* Parameters to be configured in the pmdemand registers */
> +	struct pmdemand_params params;
> +};
> +
> +#define to_intel_pmdemand_state(x) container_of((x), \
> +						struct intel_pmdemand_state,
> \
> +						base)
> +
> +void intel_pmdemand_init_early(struct drm_i915_private *i915);
> +int intel_pmdemand_init(struct drm_i915_private *i915);
> +void intel_pmdemand_init_pmdemand_params(struct drm_i915_private *i915,
> +					 struct intel_pmdemand_state
> *pmdemand_state);
> +void intel_pmdemand_update_port_clock(struct drm_i915_private *i915,
> +				      struct intel_pmdemand_state
> *pmdemand_state,
> +				      enum pipe pipe, int port_clock);
> +void intel_pmdemand_update_phys_mask(struct drm_i915_private *i915,
> +				     struct intel_encoder *encoder,
> +				     struct intel_pmdemand_state
> *pmdemand_state,
> +				     bool clear_bit);
> +void intel_pmdemand_program_dbuf(struct drm_i915_private *i915,
> +				 u8 dbuf_slices);
> +void intel_pmdemand_pre_plane_update(struct intel_atomic_state *state);
> +void intel_pmdemand_post_plane_update(struct intel_atomic_state *state);
> +int intel_pmdemand_atomic_check(struct intel_atomic_state *state);
> +
> +#endif /* __INTEL_PMDEMAND_H__ */
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 0523418129c5..27d75215d3eb 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -4418,8 +4418,10 @@
>  #define GEN8_DE_MISC_IMR _MMIO(0x44464)
>  #define GEN8_DE_MISC_IIR _MMIO(0x44468)
>  #define GEN8_DE_MISC_IER _MMIO(0x4446c)
> -#define  GEN8_DE_MISC_GSE		(1 << 27)
> -#define  GEN8_DE_EDP_PSR		(1 << 19)
> +#define  XELPDP_PMDEMAND_RSPTOUT_ERR	REG_BIT(27)
> +#define  GEN8_DE_MISC_GSE		REG_BIT(27)
> +#define  GEN8_DE_EDP_PSR		REG_BIT(19)
> +#define  XELPDP_PMDEMAND_RSP		REG_BIT(3)
> 
>  #define GEN8_PCU_ISR _MMIO(0x444e0)
>  #define GEN8_PCU_IMR _MMIO(0x444e4)
> @@ -4504,6 +4506,23 @@
>  #define  XELPDP_DP_ALT_HPD_LONG_DETECT		REG_BIT(1)
>  #define  XELPDP_DP_ALT_HPD_SHORT_DETECT		REG_BIT(0)
> 
> +#define XELPDP_INITIATE_PMDEMAND_REQUEST(dword)
> 	_MMIO(0x45230 + 4 * (dword))
> +#define  XELPDP_PMDEMAND_QCLK_GV_BW_MASK
> 	REG_GENMASK(31, 16)
> +#define  XELPDP_PMDEMAND_VOLTAGE_INDEX_MASK
> 	REG_GENMASK(14, 12)
> +#define  XELPDP_PMDEMAND_QCLK_GV_INDEX_MASK
> 	REG_GENMASK(11, 8)
> +#define  XELPDP_PMDEMAND_PIPES_MASK
> 	REG_GENMASK(7, 6)
> +#define  XELPDP_PMDEMAND_DBUFS_MASK
> 	REG_GENMASK(5, 4)
> +#define  XELPDP_PMDEMAND_PHYS_MASK
> 	REG_GENMASK(2, 0)
> +
> +#define  XELPDP_PMDEMAND_REQ_ENABLE			REG_BIT(31)
> +#define  XELPDP_PMDEMAND_CDCLK_FREQ_MASK
> 	REG_GENMASK(30, 20)
> +#define  XELPDP_PMDEMAND_DDICLK_FREQ_MASK
> 	REG_GENMASK(18, 8)
> +#define  XELPDP_PMDEMAND_SCALERS_MASK
> 	REG_GENMASK(6, 4)
> +#define  XELPDP_PMDEMAND_PLLS_MASK
> 	REG_GENMASK(2, 0)
> +
> +#define GEN12_DCPR_STATUS_1
> 	_MMIO(0x46440)
> +#define  XELPDP_PMDEMAND_INFLIGHT_STATUS		REG_BIT(26)
> +
>  #define ILK_DISPLAY_CHICKEN2	_MMIO(0x42004)
>  /* Required on all Ironlake and Sandybridge according to the B-Spec. */
>  #define   ILK_ELPIN_409_SELECT	REG_BIT(25)
> @@ -4663,6 +4682,9 @@
>  #define   DCPR_SEND_RESP_IMM			REG_BIT(25)
>  #define   DCPR_CLEAR_MEMSTAT_DIS		REG_BIT(24)
> 
> +#define XELPD_CHICKEN_DCPR_3			_MMIO(0x46438)
> +#define   DMD_RSP_TIMEOUT_DISABLE		REG_BIT(19)
> +
>  #define SKL_DFSM			_MMIO(0x51000)
>  #define   SKL_DFSM_DISPLAY_PM_DISABLE	(1 << 27)
>  #define   SKL_DFSM_DISPLAY_HDCP_DISABLE	(1 << 25)
> --
> 2.34.1





[Index of Archives]     [AMD Graphics]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux