diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_runtime_pm.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_runtime_pm.c | 1248 | 
1 files changed, 934 insertions, 314 deletions
diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index 7401cf90b0db..4f43d9b32e66 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -49,25 +49,88 @@   * present for a given platform.   */ -#define GEN9_ENABLE_DC5(dev) 0 -#define SKL_ENABLE_DC6(dev) IS_SKYLAKE(dev) -  #define for_each_power_well(i, power_well, domain_mask, power_domains)	\  	for (i = 0;							\  	     i < (power_domains)->power_well_count &&			\  		 ((power_well) = &(power_domains)->power_wells[i]);	\  	     i++)							\ -		if ((power_well)->domains & (domain_mask)) +		for_each_if ((power_well)->domains & (domain_mask))  #define for_each_power_well_rev(i, power_well, domain_mask, power_domains) \  	for (i = (power_domains)->power_well_count - 1;			 \  	     i >= 0 && ((power_well) = &(power_domains)->power_wells[i]);\  	     i--)							 \ -		if ((power_well)->domains & (domain_mask)) +		for_each_if ((power_well)->domains & (domain_mask))  bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv,  				    int power_well_id); +const char * +intel_display_power_domain_str(enum intel_display_power_domain domain) +{ +	switch (domain) { +	case POWER_DOMAIN_PIPE_A: +		return "PIPE_A"; +	case POWER_DOMAIN_PIPE_B: +		return "PIPE_B"; +	case POWER_DOMAIN_PIPE_C: +		return "PIPE_C"; +	case POWER_DOMAIN_PIPE_A_PANEL_FITTER: +		return "PIPE_A_PANEL_FITTER"; +	case POWER_DOMAIN_PIPE_B_PANEL_FITTER: +		return "PIPE_B_PANEL_FITTER"; +	case POWER_DOMAIN_PIPE_C_PANEL_FITTER: +		return "PIPE_C_PANEL_FITTER"; +	case POWER_DOMAIN_TRANSCODER_A: +		return "TRANSCODER_A"; +	case POWER_DOMAIN_TRANSCODER_B: +		return "TRANSCODER_B"; +	case POWER_DOMAIN_TRANSCODER_C: +		return "TRANSCODER_C"; +	case POWER_DOMAIN_TRANSCODER_EDP: +		return "TRANSCODER_EDP"; +	case POWER_DOMAIN_PORT_DDI_A_LANES: +		return "PORT_DDI_A_LANES"; +	case POWER_DOMAIN_PORT_DDI_B_LANES: +		return "PORT_DDI_B_LANES"; +	case POWER_DOMAIN_PORT_DDI_C_LANES: +		return "PORT_DDI_C_LANES"; +	case POWER_DOMAIN_PORT_DDI_D_LANES: +		return "PORT_DDI_D_LANES"; +	case POWER_DOMAIN_PORT_DDI_E_LANES: +		return "PORT_DDI_E_LANES"; +	case POWER_DOMAIN_PORT_DSI: +		return "PORT_DSI"; +	case POWER_DOMAIN_PORT_CRT: +		return "PORT_CRT"; +	case POWER_DOMAIN_PORT_OTHER: +		return "PORT_OTHER"; +	case POWER_DOMAIN_VGA: +		return "VGA"; +	case POWER_DOMAIN_AUDIO: +		return "AUDIO"; +	case POWER_DOMAIN_PLLS: +		return "PLLS"; +	case POWER_DOMAIN_AUX_A: +		return "AUX_A"; +	case POWER_DOMAIN_AUX_B: +		return "AUX_B"; +	case POWER_DOMAIN_AUX_C: +		return "AUX_C"; +	case POWER_DOMAIN_AUX_D: +		return "AUX_D"; +	case POWER_DOMAIN_GMBUS: +		return "GMBUS"; +	case POWER_DOMAIN_INIT: +		return "INIT"; +	case POWER_DOMAIN_MODESET: +		return "MODESET"; +	default: +		MISSING_CASE(domain); +		return "?"; +	} +} +  static void intel_power_well_enable(struct drm_i915_private *dev_priv,  				    struct i915_power_well *power_well)  { @@ -244,12 +307,6 @@ static void skl_power_well_post_enable(struct drm_i915_private *dev_priv,  		gen8_irq_power_well_post_enable(dev_priv,  						1 << PIPE_C | 1 << PIPE_B);  	} - -	if (power_well->data == SKL_DISP_PW_1) { -		if (!dev_priv->power_domains.initializing) -			intel_prepare_ddi(dev); -		gen8_irq_power_well_post_enable(dev_priv, 1 << PIPE_A); -	}  }  static void hsw_set_power_well(struct drm_i915_private *dev_priv, @@ -292,58 +349,38 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv,  	BIT(POWER_DOMAIN_TRANSCODER_C) |		\  	BIT(POWER_DOMAIN_PIPE_B_PANEL_FITTER) |		\  	BIT(POWER_DOMAIN_PIPE_C_PANEL_FITTER) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_D_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_E_2_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_B_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_D_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_E_LANES) |		\  	BIT(POWER_DOMAIN_AUX_B) |                       \  	BIT(POWER_DOMAIN_AUX_C) |			\  	BIT(POWER_DOMAIN_AUX_D) |			\  	BIT(POWER_DOMAIN_AUDIO) |			\  	BIT(POWER_DOMAIN_VGA) |				\  	BIT(POWER_DOMAIN_INIT)) -#define SKL_DISPLAY_POWERWELL_1_POWER_DOMAINS (		\ -	SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS |		\ -	BIT(POWER_DOMAIN_PLLS) |			\ -	BIT(POWER_DOMAIN_PIPE_A) |			\ -	BIT(POWER_DOMAIN_TRANSCODER_EDP) |		\ -	BIT(POWER_DOMAIN_PIPE_A_PANEL_FITTER) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_A_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_A_4_LANES) |		\ -	BIT(POWER_DOMAIN_AUX_A) |			\ -	BIT(POWER_DOMAIN_INIT))  #define SKL_DISPLAY_DDI_A_E_POWER_DOMAINS (		\ -	BIT(POWER_DOMAIN_PORT_DDI_A_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_A_4_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_E_2_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_A_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_E_LANES) |		\  	BIT(POWER_DOMAIN_INIT))  #define SKL_DISPLAY_DDI_B_POWER_DOMAINS (		\ -	BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_B_LANES) |		\  	BIT(POWER_DOMAIN_INIT))  #define SKL_DISPLAY_DDI_C_POWER_DOMAINS (		\ -	BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |		\  	BIT(POWER_DOMAIN_INIT))  #define SKL_DISPLAY_DDI_D_POWER_DOMAINS (		\ -	BIT(POWER_DOMAIN_PORT_DDI_D_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_D_LANES) |		\  	BIT(POWER_DOMAIN_INIT)) -#define SKL_DISPLAY_MISC_IO_POWER_DOMAINS (		\ -	SKL_DISPLAY_POWERWELL_1_POWER_DOMAINS |		\ -	BIT(POWER_DOMAIN_PLLS) |			\ +#define SKL_DISPLAY_DC_OFF_POWER_DOMAINS (		\ +	SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS |		\ +	BIT(POWER_DOMAIN_MODESET) |			\ +	BIT(POWER_DOMAIN_AUX_A) |			\  	BIT(POWER_DOMAIN_INIT))  #define SKL_DISPLAY_ALWAYS_ON_POWER_DOMAINS (		\ -	(POWER_DOMAIN_MASK & ~(SKL_DISPLAY_POWERWELL_1_POWER_DOMAINS |	\ +	(POWER_DOMAIN_MASK & ~(				\  	SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS |		\ -	SKL_DISPLAY_DDI_A_E_POWER_DOMAINS |		\ -	SKL_DISPLAY_DDI_B_POWER_DOMAINS |		\ -	SKL_DISPLAY_DDI_C_POWER_DOMAINS |		\ -	SKL_DISPLAY_DDI_D_POWER_DOMAINS |		\ -	SKL_DISPLAY_MISC_IO_POWER_DOMAINS)) |		\ +	SKL_DISPLAY_DC_OFF_POWER_DOMAINS)) |		\  	BIT(POWER_DOMAIN_INIT))  #define BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS (		\ @@ -354,25 +391,28 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv,  	BIT(POWER_DOMAIN_TRANSCODER_C) |		\  	BIT(POWER_DOMAIN_PIPE_B_PANEL_FITTER) |		\  	BIT(POWER_DOMAIN_PIPE_C_PANEL_FITTER) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_B_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |		\  	BIT(POWER_DOMAIN_AUX_B) |			\  	BIT(POWER_DOMAIN_AUX_C) |			\  	BIT(POWER_DOMAIN_AUDIO) |			\  	BIT(POWER_DOMAIN_VGA) |				\ +	BIT(POWER_DOMAIN_GMBUS) |			\  	BIT(POWER_DOMAIN_INIT))  #define BXT_DISPLAY_POWERWELL_1_POWER_DOMAINS (		\  	BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS |		\  	BIT(POWER_DOMAIN_PIPE_A) |			\  	BIT(POWER_DOMAIN_TRANSCODER_EDP) |		\  	BIT(POWER_DOMAIN_PIPE_A_PANEL_FITTER) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_A_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_A_4_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_A_LANES) |		\  	BIT(POWER_DOMAIN_AUX_A) |			\  	BIT(POWER_DOMAIN_PLLS) |			\  	BIT(POWER_DOMAIN_INIT)) +#define BXT_DISPLAY_DC_OFF_POWER_DOMAINS (		\ +	BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS |		\ +	BIT(POWER_DOMAIN_MODESET) |			\ +	BIT(POWER_DOMAIN_AUX_A) |			\ +	BIT(POWER_DOMAIN_INIT))  #define BXT_DISPLAY_ALWAYS_ON_POWER_DOMAINS (		\  	(POWER_DOMAIN_MASK & ~(BXT_DISPLAY_POWERWELL_1_POWER_DOMAINS |	\  	BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS)) |	\ @@ -416,46 +456,119 @@ static void assert_can_disable_dc9(struct drm_i915_private *dev_priv)  	  */  } -void bxt_enable_dc9(struct drm_i915_private *dev_priv) +static void gen9_set_dc_state_debugmask_memory_up( +			struct drm_i915_private *dev_priv) +{ +	uint32_t val; + +	/* The below bit doesn't need to be cleared ever afterwards */ +	val = I915_READ(DC_STATE_DEBUG); +	if (!(val & DC_STATE_DEBUG_MASK_MEMORY_UP)) { +		val |= DC_STATE_DEBUG_MASK_MEMORY_UP; +		I915_WRITE(DC_STATE_DEBUG, val); +		POSTING_READ(DC_STATE_DEBUG); +	} +} + +static void gen9_write_dc_state(struct drm_i915_private *dev_priv, +				u32 state) +{ +	int rewrites = 0; +	int rereads = 0; +	u32 v; + +	I915_WRITE(DC_STATE_EN, state); + +	/* It has been observed that disabling the dc6 state sometimes +	 * doesn't stick and dmc keeps returning old value. Make sure +	 * the write really sticks enough times and also force rewrite until +	 * we are confident that state is exactly what we want. +	 */ +	do  { +		v = I915_READ(DC_STATE_EN); + +		if (v != state) { +			I915_WRITE(DC_STATE_EN, state); +			rewrites++; +			rereads = 0; +		} else if (rereads++ > 5) { +			break; +		} + +	} while (rewrites < 100); + +	if (v != state) +		DRM_ERROR("Writing dc state to 0x%x failed, now 0x%x\n", +			  state, v); + +	/* Most of the times we need one retry, avoid spam */ +	if (rewrites > 1) +		DRM_DEBUG_KMS("Rewrote dc state to 0x%x %d times\n", +			      state, rewrites); +} + +static void gen9_set_dc_state(struct drm_i915_private *dev_priv, uint32_t state)  {  	uint32_t val; +	uint32_t mask; + +	mask = DC_STATE_EN_UPTO_DC5; +	if (IS_BROXTON(dev_priv)) +		mask |= DC_STATE_EN_DC9; +	else +		mask |= DC_STATE_EN_UPTO_DC6; + +	WARN_ON_ONCE(state & ~mask); + +	if (i915.enable_dc == 0) +		state = DC_STATE_DISABLE; +	else if (i915.enable_dc == 1 && state > DC_STATE_EN_UPTO_DC5) +		state = DC_STATE_EN_UPTO_DC5; + +	if (state & DC_STATE_EN_UPTO_DC5_DC6_MASK) +		gen9_set_dc_state_debugmask_memory_up(dev_priv); + +	val = I915_READ(DC_STATE_EN); +	DRM_DEBUG_KMS("Setting DC state from %02x to %02x\n", +		      val & mask, state); + +	/* Check if DMC is ignoring our DC state requests */ +	if ((val & mask) != dev_priv->csr.dc_state) +		DRM_ERROR("DC state mismatch (0x%x -> 0x%x)\n", +			  dev_priv->csr.dc_state, val & mask); + +	val &= ~mask; +	val |= state; + +	gen9_write_dc_state(dev_priv, val); + +	dev_priv->csr.dc_state = val & mask; +} +void bxt_enable_dc9(struct drm_i915_private *dev_priv) +{  	assert_can_enable_dc9(dev_priv);  	DRM_DEBUG_KMS("Enabling DC9\n"); -	val = I915_READ(DC_STATE_EN); -	val |= DC_STATE_EN_DC9; -	I915_WRITE(DC_STATE_EN, val); -	POSTING_READ(DC_STATE_EN); +	gen9_set_dc_state(dev_priv, DC_STATE_EN_DC9);  }  void bxt_disable_dc9(struct drm_i915_private *dev_priv)  { -	uint32_t val; -  	assert_can_disable_dc9(dev_priv);  	DRM_DEBUG_KMS("Disabling DC9\n"); -	val = I915_READ(DC_STATE_EN); -	val &= ~DC_STATE_EN_DC9; -	I915_WRITE(DC_STATE_EN, val); -	POSTING_READ(DC_STATE_EN); +	gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);  } -static void gen9_set_dc_state_debugmask_memory_up( -			struct drm_i915_private *dev_priv) +static void assert_csr_loaded(struct drm_i915_private *dev_priv)  { -	uint32_t val; - -	/* The below bit doesn't need to be cleared ever afterwards */ -	val = I915_READ(DC_STATE_DEBUG); -	if (!(val & DC_STATE_DEBUG_MASK_MEMORY_UP)) { -		val |= DC_STATE_DEBUG_MASK_MEMORY_UP; -		I915_WRITE(DC_STATE_DEBUG, val); -		POSTING_READ(DC_STATE_DEBUG); -	} +	WARN_ONCE(!I915_READ(CSR_PROGRAM(0)), +		  "CSR program storage start is NULL\n"); +	WARN_ONCE(!I915_READ(CSR_SSP_BASE), "CSR SSP Base Not fine\n"); +	WARN_ONCE(!I915_READ(CSR_HTP_SKL), "CSR HTP Not fine\n");  }  static void assert_can_enable_dc5(struct drm_i915_private *dev_priv) @@ -464,22 +577,19 @@ static void assert_can_enable_dc5(struct drm_i915_private *dev_priv)  	bool pg2_enabled = intel_display_power_well_is_enabled(dev_priv,  					SKL_DISP_PW_2); -	WARN(!IS_SKYLAKE(dev), "Platform doesn't support DC5.\n"); -	WARN(!HAS_RUNTIME_PM(dev), "Runtime PM not enabled.\n"); -	WARN(pg2_enabled, "PG2 not disabled to enable DC5.\n"); +	WARN_ONCE(!IS_SKYLAKE(dev), "Platform doesn't support DC5.\n"); +	WARN_ONCE(!HAS_RUNTIME_PM(dev), "Runtime PM not enabled.\n"); +	WARN_ONCE(pg2_enabled, "PG2 not disabled to enable DC5.\n"); -	WARN((I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC5), -				"DC5 already programmed to be enabled.\n"); -	WARN(dev_priv->pm.suspended, -		"DC5 cannot be enabled, if platform is runtime-suspended.\n"); +	WARN_ONCE((I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC5), +		  "DC5 already programmed to be enabled.\n"); +	assert_rpm_wakelock_held(dev_priv);  	assert_csr_loaded(dev_priv);  }  static void assert_can_disable_dc5(struct drm_i915_private *dev_priv)  { -	bool pg2_enabled = intel_display_power_well_is_enabled(dev_priv, -					SKL_DISP_PW_2);  	/*  	 * During initialization, the firmware may not be loaded yet.  	 * We still want to make sure that the DC enabling flag is cleared. @@ -487,52 +597,28 @@ static void assert_can_disable_dc5(struct drm_i915_private *dev_priv)  	if (dev_priv->power_domains.initializing)  		return; -	WARN(!pg2_enabled, "PG2 not enabled to disable DC5.\n"); -	WARN(dev_priv->pm.suspended, -		"Disabling of DC5 while platform is runtime-suspended should never happen.\n"); +	assert_rpm_wakelock_held(dev_priv);  }  static void gen9_enable_dc5(struct drm_i915_private *dev_priv)  { -	uint32_t val; -  	assert_can_enable_dc5(dev_priv);  	DRM_DEBUG_KMS("Enabling DC5\n"); -	gen9_set_dc_state_debugmask_memory_up(dev_priv); - -	val = I915_READ(DC_STATE_EN); -	val &= ~DC_STATE_EN_UPTO_DC5_DC6_MASK; -	val |= DC_STATE_EN_UPTO_DC5; -	I915_WRITE(DC_STATE_EN, val); -	POSTING_READ(DC_STATE_EN); -} - -static void gen9_disable_dc5(struct drm_i915_private *dev_priv) -{ -	uint32_t val; - -	assert_can_disable_dc5(dev_priv); - -	DRM_DEBUG_KMS("Disabling DC5\n"); - -	val = I915_READ(DC_STATE_EN); -	val &= ~DC_STATE_EN_UPTO_DC5; -	I915_WRITE(DC_STATE_EN, val); -	POSTING_READ(DC_STATE_EN); +	gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC5);  }  static void assert_can_enable_dc6(struct drm_i915_private *dev_priv)  {  	struct drm_device *dev = dev_priv->dev; -	WARN(!IS_SKYLAKE(dev), "Platform doesn't support DC6.\n"); -	WARN(!HAS_RUNTIME_PM(dev), "Runtime PM not enabled.\n"); -	WARN(I915_READ(UTIL_PIN_CTL) & UTIL_PIN_ENABLE, -		"Backlight is not disabled.\n"); -	WARN((I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC6), -		"DC6 already programmed to be enabled.\n"); +	WARN_ONCE(!IS_SKYLAKE(dev), "Platform doesn't support DC6.\n"); +	WARN_ONCE(!HAS_RUNTIME_PM(dev), "Runtime PM not enabled.\n"); +	WARN_ONCE(I915_READ(UTIL_PIN_CTL) & UTIL_PIN_ENABLE, +		  "Backlight is not disabled.\n"); +	WARN_ONCE((I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC6), +		  "DC6 already programmed to be enabled.\n");  	assert_csr_loaded(dev_priv);  } @@ -546,40 +632,37 @@ static void assert_can_disable_dc6(struct drm_i915_private *dev_priv)  	if (dev_priv->power_domains.initializing)  		return; -	assert_csr_loaded(dev_priv); -	WARN(!(I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC6), -		"DC6 already programmed to be disabled.\n"); +	WARN_ONCE(!(I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC6), +		  "DC6 already programmed to be disabled.\n");  } -static void skl_enable_dc6(struct drm_i915_private *dev_priv) +static void gen9_disable_dc5_dc6(struct drm_i915_private *dev_priv)  { -	uint32_t val; +	assert_can_disable_dc5(dev_priv); +	if (IS_SKYLAKE(dev_priv) && i915.enable_dc != 0 && i915.enable_dc != 1) +		assert_can_disable_dc6(dev_priv); + +	gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); +} + +void skl_enable_dc6(struct drm_i915_private *dev_priv) +{  	assert_can_enable_dc6(dev_priv);  	DRM_DEBUG_KMS("Enabling DC6\n"); -	gen9_set_dc_state_debugmask_memory_up(dev_priv); +	gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6); -	val = I915_READ(DC_STATE_EN); -	val &= ~DC_STATE_EN_UPTO_DC5_DC6_MASK; -	val |= DC_STATE_EN_UPTO_DC6; -	I915_WRITE(DC_STATE_EN, val); -	POSTING_READ(DC_STATE_EN);  } -static void skl_disable_dc6(struct drm_i915_private *dev_priv) +void skl_disable_dc6(struct drm_i915_private *dev_priv)  { -	uint32_t val; -  	assert_can_disable_dc6(dev_priv);  	DRM_DEBUG_KMS("Disabling DC6\n"); -	val = I915_READ(DC_STATE_EN); -	val &= ~DC_STATE_EN_UPTO_DC6; -	I915_WRITE(DC_STATE_EN, val); -	POSTING_READ(DC_STATE_EN); +	gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);  }  static void skl_set_power_well(struct drm_i915_private *dev_priv, @@ -629,20 +712,16 @@ static void skl_set_power_well(struct drm_i915_private *dev_priv,  				!I915_READ(HSW_PWR_WELL_BIOS),  				"Invalid for power well status to be enabled, unless done by the BIOS, \  				when request is to disable!\n"); -			if ((GEN9_ENABLE_DC5(dev) || SKL_ENABLE_DC6(dev)) && -				power_well->data == SKL_DISP_PW_2) { -				if (SKL_ENABLE_DC6(dev)) { -					skl_disable_dc6(dev_priv); -					/* -					 * DDI buffer programming unnecessary during driver-load/resume -					 * as it's already done during modeset initialization then. -					 * It's also invalid here as encoder list is still uninitialized. -					 */ -					if (!dev_priv->power_domains.initializing) -						intel_prepare_ddi(dev); -				} else { -					gen9_disable_dc5(dev_priv); -				} +			if (power_well->data == SKL_DISP_PW_2) { +				/* +				 * DDI buffer programming unnecessary during +				 * driver-load/resume as it's already done +				 * during modeset initialization then. It's +				 * also invalid here as encoder list is still +				 * uninitialized. +				 */ +				if (!dev_priv->power_domains.initializing) +					intel_prepare_ddi(dev);  			}  			I915_WRITE(HSW_PWR_WELL_DRIVER, tmp | req_mask);  		} @@ -660,25 +739,6 @@ static void skl_set_power_well(struct drm_i915_private *dev_priv,  			I915_WRITE(HSW_PWR_WELL_DRIVER,	tmp & ~req_mask);  			POSTING_READ(HSW_PWR_WELL_DRIVER);  			DRM_DEBUG_KMS("Disabling %s\n", power_well->name); - -			if ((GEN9_ENABLE_DC5(dev) || SKL_ENABLE_DC6(dev)) && -				power_well->data == SKL_DISP_PW_2) { -				enum csr_state state; -				/* TODO: wait for a completion event or -				 * similar here instead of busy -				 * waiting using wait_for function. -				 */ -				wait_for((state = intel_csr_load_status_get(dev_priv)) != -						FW_UNINITIALIZED, 1000); -				if (state != FW_LOADED) -					DRM_ERROR("CSR firmware not ready (%d)\n", -							state); -				else -					if (SKL_ENABLE_DC6(dev)) -						skl_enable_dc6(dev_priv); -					else -						gen9_enable_dc5(dev_priv); -			}  		}  	} @@ -753,6 +813,41 @@ static void skl_power_well_disable(struct drm_i915_private *dev_priv,  	skl_set_power_well(dev_priv, power_well, false);  } +static bool gen9_dc_off_power_well_enabled(struct drm_i915_private *dev_priv, +					   struct i915_power_well *power_well) +{ +	return (I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC5_DC6_MASK) == 0; +} + +static void gen9_dc_off_power_well_enable(struct drm_i915_private *dev_priv, +					  struct i915_power_well *power_well) +{ +	gen9_disable_dc5_dc6(dev_priv); +} + +static void gen9_dc_off_power_well_disable(struct drm_i915_private *dev_priv, +					   struct i915_power_well *power_well) +{ +	if (IS_SKYLAKE(dev_priv) && i915.enable_dc != 0 && i915.enable_dc != 1) +		skl_enable_dc6(dev_priv); +	else +		gen9_enable_dc5(dev_priv); +} + +static void gen9_dc_off_power_well_sync_hw(struct drm_i915_private *dev_priv, +					   struct i915_power_well *power_well) +{ +	if (power_well->count > 0) { +		gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); +	} else { +		if (IS_SKYLAKE(dev_priv) && i915.enable_dc != 0 && +		    i915.enable_dc != 1) +			gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6); +		else +			gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC5); +	} +} +  static void i9xx_always_on_power_well_noop(struct drm_i915_private *dev_priv,  					   struct i915_power_well *power_well)  { @@ -856,6 +951,25 @@ static bool vlv_power_well_enabled(struct drm_i915_private *dev_priv,  static void vlv_display_power_well_init(struct drm_i915_private *dev_priv)  { +	enum pipe pipe; + +	/* +	 * Enable the CRI clock source so we can get at the +	 * display and the reference clock for VGA +	 * hotplug / manual detection. Supposedly DSI also +	 * needs the ref clock up and running. +	 * +	 * CHV DPLL B/C have some issues if VGA mode is enabled. +	 */ +	for_each_pipe(dev_priv->dev, pipe) { +		u32 val = I915_READ(DPLL(pipe)); + +		val |= DPLL_REF_CLK_ENABLE_VLV | DPLL_VGA_MODE_DIS; +		if (pipe != PIPE_A) +			val |= DPLL_INTEGRATED_CRI_CLK_VLV; + +		I915_WRITE(DPLL(pipe), val); +	}  	spin_lock_irq(&dev_priv->irq_lock);  	valleyview_enable_display_irqs(dev_priv); @@ -907,13 +1021,7 @@ static void vlv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv,  {  	WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC); -	/* -	 * Enable the CRI clock source so we can get at the -	 * display and the reference clock for VGA -	 * hotplug / manual detection. -	 */ -	I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | DPLL_VGA_MODE_DIS | -		   DPLL_REF_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV); +	/* since ref/cri clock was enabled */  	udelay(1); /* >10ns for cmnreset, >0ns for sidereset */  	vlv_set_power_well(dev_priv, power_well, true); @@ -948,30 +1056,151 @@ static void vlv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv,  	vlv_set_power_well(dev_priv, power_well, false);  } +#define POWER_DOMAIN_MASK (BIT(POWER_DOMAIN_NUM) - 1) + +static struct i915_power_well *lookup_power_well(struct drm_i915_private *dev_priv, +						 int power_well_id) +{ +	struct i915_power_domains *power_domains = &dev_priv->power_domains; +	int i; + +	for (i = 0; i < power_domains->power_well_count; i++) { +		struct i915_power_well *power_well; + +		power_well = &power_domains->power_wells[i]; +		if (power_well->data == power_well_id) +			return power_well; +	} + +	return NULL; +} + +#define BITS_SET(val, bits) (((val) & (bits)) == (bits)) + +static void assert_chv_phy_status(struct drm_i915_private *dev_priv) +{ +	struct i915_power_well *cmn_bc = +		lookup_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_BC); +	struct i915_power_well *cmn_d = +		lookup_power_well(dev_priv, PUNIT_POWER_WELL_DPIO_CMN_D); +	u32 phy_control = dev_priv->chv_phy_control; +	u32 phy_status = 0; +	u32 phy_status_mask = 0xffffffff; +	u32 tmp; + +	/* +	 * The BIOS can leave the PHY is some weird state +	 * where it doesn't fully power down some parts. +	 * Disable the asserts until the PHY has been fully +	 * reset (ie. the power well has been disabled at +	 * least once). +	 */ +	if (!dev_priv->chv_phy_assert[DPIO_PHY0]) +		phy_status_mask &= ~(PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH0) | +				     PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 0) | +				     PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 1) | +				     PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH1) | +				     PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 0) | +				     PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 1)); + +	if (!dev_priv->chv_phy_assert[DPIO_PHY1]) +		phy_status_mask &= ~(PHY_STATUS_CMN_LDO(DPIO_PHY1, DPIO_CH0) | +				     PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 0) | +				     PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 1)); + +	if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) { +		phy_status |= PHY_POWERGOOD(DPIO_PHY0); + +		/* this assumes override is only used to enable lanes */ +		if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH0)) == 0) +			phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH0); + +		if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH1)) == 0) +			phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1); + +		/* CL1 is on whenever anything is on in either channel */ +		if (BITS_SET(phy_control, +			     PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH0) | +			     PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1))) +			phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH0); + +		/* +		 * The DPLLB check accounts for the pipe B + port A usage +		 * with CL2 powered up but all the lanes in the second channel +		 * powered down. +		 */ +		if (BITS_SET(phy_control, +			     PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY0, DPIO_CH1)) && +		    (I915_READ(DPLL(PIPE_B)) & DPLL_VCO_ENABLE) == 0) +			phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY0, DPIO_CH1); + +		if (BITS_SET(phy_control, +			     PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY0, DPIO_CH0))) +			phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 0); +		if (BITS_SET(phy_control, +			     PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY0, DPIO_CH0))) +			phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH0, 1); + +		if (BITS_SET(phy_control, +			     PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY0, DPIO_CH1))) +			phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 0); +		if (BITS_SET(phy_control, +			     PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY0, DPIO_CH1))) +			phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY0, DPIO_CH1, 1); +	} + +	if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) { +		phy_status |= PHY_POWERGOOD(DPIO_PHY1); + +		/* this assumes override is only used to enable lanes */ +		if ((phy_control & PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY1, DPIO_CH0)) == 0) +			phy_control |= PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY1, DPIO_CH0); + +		if (BITS_SET(phy_control, +			     PHY_CH_POWER_DOWN_OVRD(0xf, DPIO_PHY1, DPIO_CH0))) +			phy_status |= PHY_STATUS_CMN_LDO(DPIO_PHY1, DPIO_CH0); + +		if (BITS_SET(phy_control, +			     PHY_CH_POWER_DOWN_OVRD(0x3, DPIO_PHY1, DPIO_CH0))) +			phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 0); +		if (BITS_SET(phy_control, +			     PHY_CH_POWER_DOWN_OVRD(0xc, DPIO_PHY1, DPIO_CH0))) +			phy_status |= PHY_STATUS_SPLINE_LDO(DPIO_PHY1, DPIO_CH0, 1); +	} + +	phy_status &= phy_status_mask; + +	/* +	 * The PHY may be busy with some initial calibration and whatnot, +	 * so the power state can take a while to actually change. +	 */ +	if (wait_for((tmp = I915_READ(DISPLAY_PHY_STATUS) & phy_status_mask) == phy_status, 10)) +		WARN(phy_status != tmp, +		     "Unexpected PHY_STATUS 0x%08x, expected 0x%08x (PHY_CONTROL=0x%08x)\n", +		     tmp, phy_status, dev_priv->chv_phy_control); +} + +#undef BITS_SET +  static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv,  					   struct i915_power_well *power_well)  {  	enum dpio_phy phy; +	enum pipe pipe; +	uint32_t tmp;  	WARN_ON_ONCE(power_well->data != PUNIT_POWER_WELL_DPIO_CMN_BC &&  		     power_well->data != PUNIT_POWER_WELL_DPIO_CMN_D); -	/* -	 * Enable the CRI clock source so we can get at the -	 * display and the reference clock for VGA -	 * hotplug / manual detection. -	 */  	if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { +		pipe = PIPE_A;  		phy = DPIO_PHY0; -		I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | DPLL_VGA_MODE_DIS | -			   DPLL_REF_CLK_ENABLE_VLV); -		I915_WRITE(DPLL(PIPE_B), I915_READ(DPLL(PIPE_B)) | DPLL_VGA_MODE_DIS | -			   DPLL_REF_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV);  	} else { +		pipe = PIPE_C;  		phy = DPIO_PHY1; -		I915_WRITE(DPLL(PIPE_C), I915_READ(DPLL(PIPE_C)) | DPLL_VGA_MODE_DIS | -			   DPLL_REF_CLK_ENABLE_VLV | DPLL_INTEGRATED_CRI_CLK_VLV);  	} + +	/* since ref/cri clock was enabled */  	udelay(1); /* >10ns for cmnreset, >0ns for sidereset */  	vlv_set_power_well(dev_priv, power_well, true); @@ -979,8 +1208,38 @@ static void chv_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv,  	if (wait_for(I915_READ(DISPLAY_PHY_STATUS) & PHY_POWERGOOD(phy), 1))  		DRM_ERROR("Display PHY %d is not power up\n", phy); +	mutex_lock(&dev_priv->sb_lock); + +	/* Enable dynamic power down */ +	tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW28); +	tmp |= DPIO_DYNPWRDOWNEN_CH0 | DPIO_CL1POWERDOWNEN | +		DPIO_SUS_CLK_CONFIG_GATE_CLKREQ; +	vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW28, tmp); + +	if (power_well->data == PUNIT_POWER_WELL_DPIO_CMN_BC) { +		tmp = vlv_dpio_read(dev_priv, pipe, _CHV_CMN_DW6_CH1); +		tmp |= DPIO_DYNPWRDOWNEN_CH1; +		vlv_dpio_write(dev_priv, pipe, _CHV_CMN_DW6_CH1, tmp); +	} else { +		/* +		 * Force the non-existing CL2 off. BXT does this +		 * too, so maybe it saves some power even though +		 * CL2 doesn't exist? +		 */ +		tmp = vlv_dpio_read(dev_priv, pipe, CHV_CMN_DW30); +		tmp |= DPIO_CL2_LDOFUSE_PWRENB; +		vlv_dpio_write(dev_priv, pipe, CHV_CMN_DW30, tmp); +	} + +	mutex_unlock(&dev_priv->sb_lock); +  	dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(phy);  	I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); + +	DRM_DEBUG_KMS("Enabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", +		      phy, dev_priv->chv_phy_control); + +	assert_chv_phy_status(dev_priv);  }  static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv, @@ -1004,6 +1263,137 @@ static void chv_dpio_cmn_power_well_disable(struct drm_i915_private *dev_priv,  	I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control);  	vlv_set_power_well(dev_priv, power_well, false); + +	DRM_DEBUG_KMS("Disabled DPIO PHY%d (PHY_CONTROL=0x%08x)\n", +		      phy, dev_priv->chv_phy_control); + +	/* PHY is fully reset now, so we can enable the PHY state asserts */ +	dev_priv->chv_phy_assert[phy] = true; + +	assert_chv_phy_status(dev_priv); +} + +static void assert_chv_phy_powergate(struct drm_i915_private *dev_priv, enum dpio_phy phy, +				     enum dpio_channel ch, bool override, unsigned int mask) +{ +	enum pipe pipe = phy == DPIO_PHY0 ? PIPE_A : PIPE_C; +	u32 reg, val, expected, actual; + +	/* +	 * The BIOS can leave the PHY is some weird state +	 * where it doesn't fully power down some parts. +	 * Disable the asserts until the PHY has been fully +	 * reset (ie. the power well has been disabled at +	 * least once). +	 */ +	if (!dev_priv->chv_phy_assert[phy]) +		return; + +	if (ch == DPIO_CH0) +		reg = _CHV_CMN_DW0_CH0; +	else +		reg = _CHV_CMN_DW6_CH1; + +	mutex_lock(&dev_priv->sb_lock); +	val = vlv_dpio_read(dev_priv, pipe, reg); +	mutex_unlock(&dev_priv->sb_lock); + +	/* +	 * This assumes !override is only used when the port is disabled. +	 * All lanes should power down even without the override when +	 * the port is disabled. +	 */ +	if (!override || mask == 0xf) { +		expected = DPIO_ALLDL_POWERDOWN | DPIO_ANYDL_POWERDOWN; +		/* +		 * If CH1 common lane is not active anymore +		 * (eg. for pipe B DPLL) the entire channel will +		 * shut down, which causes the common lane registers +		 * to read as 0. That means we can't actually check +		 * the lane power down status bits, but as the entire +		 * register reads as 0 it's a good indication that the +		 * channel is indeed entirely powered down. +		 */ +		if (ch == DPIO_CH1 && val == 0) +			expected = 0; +	} else if (mask != 0x0) { +		expected = DPIO_ANYDL_POWERDOWN; +	} else { +		expected = 0; +	} + +	if (ch == DPIO_CH0) +		actual = val >> DPIO_ANYDL_POWERDOWN_SHIFT_CH0; +	else +		actual = val >> DPIO_ANYDL_POWERDOWN_SHIFT_CH1; +	actual &= DPIO_ALLDL_POWERDOWN | DPIO_ANYDL_POWERDOWN; + +	WARN(actual != expected, +	     "Unexpected DPIO lane power down: all %d, any %d. Expected: all %d, any %d. (0x%x = 0x%08x)\n", +	     !!(actual & DPIO_ALLDL_POWERDOWN), !!(actual & DPIO_ANYDL_POWERDOWN), +	     !!(expected & DPIO_ALLDL_POWERDOWN), !!(expected & DPIO_ANYDL_POWERDOWN), +	     reg, val); +} + +bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy, +			  enum dpio_channel ch, bool override) +{ +	struct i915_power_domains *power_domains = &dev_priv->power_domains; +	bool was_override; + +	mutex_lock(&power_domains->lock); + +	was_override = dev_priv->chv_phy_control & PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + +	if (override == was_override) +		goto out; + +	if (override) +		dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); +	else +		dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + +	I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); + +	DRM_DEBUG_KMS("Power gating DPIO PHY%d CH%d (DPIO_PHY_CONTROL=0x%08x)\n", +		      phy, ch, dev_priv->chv_phy_control); + +	assert_chv_phy_status(dev_priv); + +out: +	mutex_unlock(&power_domains->lock); + +	return was_override; +} + +void chv_phy_powergate_lanes(struct intel_encoder *encoder, +			     bool override, unsigned int mask) +{ +	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); +	struct i915_power_domains *power_domains = &dev_priv->power_domains; +	enum dpio_phy phy = vlv_dport_to_phy(enc_to_dig_port(&encoder->base)); +	enum dpio_channel ch = vlv_dport_to_channel(enc_to_dig_port(&encoder->base)); + +	mutex_lock(&power_domains->lock); + +	dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD(0xf, phy, ch); +	dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD(mask, phy, ch); + +	if (override) +		dev_priv->chv_phy_control |= PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); +	else +		dev_priv->chv_phy_control &= ~PHY_CH_POWER_DOWN_OVRD_EN(phy, ch); + +	I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); + +	DRM_DEBUG_KMS("Power gating DPIO PHY%d CH%d lanes 0x%x (PHY_CONTROL=0x%08x)\n", +		      phy, ch, mask, dev_priv->chv_phy_control); + +	assert_chv_phy_status(dev_priv); + +	assert_chv_phy_powergate(dev_priv, phy, ch, override, mask); + +	mutex_unlock(&power_domains->lock);  }  static bool chv_pipe_power_well_enabled(struct drm_i915_private *dev_priv, @@ -1097,6 +1487,22 @@ static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv,  	chv_set_pipe_power_well(dev_priv, power_well, false);  } +static void +__intel_display_power_get_domain(struct drm_i915_private *dev_priv, +				 enum intel_display_power_domain domain) +{ +	struct i915_power_domains *power_domains = &dev_priv->power_domains; +	struct i915_power_well *power_well; +	int i; + +	for_each_power_well(i, power_well, BIT(domain), power_domains) { +		if (!power_well->count++) +			intel_power_well_enable(dev_priv, power_well); +	} + +	power_domains->domain_use_count[domain]++; +} +  /**   * intel_display_power_get - grab a power domain reference   * @dev_priv: i915 device instance @@ -1112,24 +1518,53 @@ static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv,  void intel_display_power_get(struct drm_i915_private *dev_priv,  			     enum intel_display_power_domain domain)  { -	struct i915_power_domains *power_domains; -	struct i915_power_well *power_well; -	int i; +	struct i915_power_domains *power_domains = &dev_priv->power_domains;  	intel_runtime_pm_get(dev_priv); -	power_domains = &dev_priv->power_domains; +	mutex_lock(&power_domains->lock); + +	__intel_display_power_get_domain(dev_priv, domain); + +	mutex_unlock(&power_domains->lock); +} + +/** + * intel_display_power_get_if_enabled - grab a reference for an enabled display power domain + * @dev_priv: i915 device instance + * @domain: power domain to reference + * + * This function grabs a power domain reference for @domain and ensures that the + * power domain and all its parents are powered up. Therefore users should only + * grab a reference to the innermost power domain they need. + * + * Any power domain reference obtained by this function must have a symmetric + * call to intel_display_power_put() to release the reference again. + */ +bool intel_display_power_get_if_enabled(struct drm_i915_private *dev_priv, +					enum intel_display_power_domain domain) +{ +	struct i915_power_domains *power_domains = &dev_priv->power_domains; +	bool is_enabled; + +	if (!intel_runtime_pm_get_if_in_use(dev_priv)) +		return false;  	mutex_lock(&power_domains->lock); -	for_each_power_well(i, power_well, BIT(domain), power_domains) { -		if (!power_well->count++) -			intel_power_well_enable(dev_priv, power_well); +	if (__intel_display_power_is_enabled(dev_priv, domain)) { +		__intel_display_power_get_domain(dev_priv, domain); +		is_enabled = true; +	} else { +		is_enabled = false;  	} -	power_domains->domain_use_count[domain]++; -  	mutex_unlock(&power_domains->lock); + +	if (!is_enabled) +		intel_runtime_pm_put(dev_priv); + +	return is_enabled;  }  /** @@ -1152,13 +1587,17 @@ void intel_display_power_put(struct drm_i915_private *dev_priv,  	mutex_lock(&power_domains->lock); -	WARN_ON(!power_domains->domain_use_count[domain]); +	WARN(!power_domains->domain_use_count[domain], +	     "Use count on domain %s is already zero\n", +	     intel_display_power_domain_str(domain));  	power_domains->domain_use_count[domain]--;  	for_each_power_well_rev(i, power_well, BIT(domain), power_domains) { -		WARN_ON(!power_well->count); +		WARN(!power_well->count, +		     "Use count on power well %s is already zero", +		     power_well->name); -		if (!--power_well->count && i915.disable_power_well) +		if (!--power_well->count)  			intel_power_well_disable(dev_priv, power_well);  	} @@ -1167,25 +1606,20 @@ void intel_display_power_put(struct drm_i915_private *dev_priv,  	intel_runtime_pm_put(dev_priv);  } -#define POWER_DOMAIN_MASK (BIT(POWER_DOMAIN_NUM) - 1) -  #define HSW_ALWAYS_ON_POWER_DOMAINS (			\  	BIT(POWER_DOMAIN_PIPE_A) |			\  	BIT(POWER_DOMAIN_TRANSCODER_EDP) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_A_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_A_4_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_D_2_LANES) |		\ -	BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_A_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_B_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |		\ +	BIT(POWER_DOMAIN_PORT_DDI_D_LANES) |		\  	BIT(POWER_DOMAIN_PORT_CRT) |			\  	BIT(POWER_DOMAIN_PLLS) |			\  	BIT(POWER_DOMAIN_AUX_A) |			\  	BIT(POWER_DOMAIN_AUX_B) |			\  	BIT(POWER_DOMAIN_AUX_C) |			\  	BIT(POWER_DOMAIN_AUX_D) |			\ +	BIT(POWER_DOMAIN_GMBUS) |			\  	BIT(POWER_DOMAIN_INIT))  #define HSW_DISPLAY_POWER_DOMAINS (				\  	(POWER_DOMAIN_MASK & ~HSW_ALWAYS_ON_POWER_DOMAINS) |	\ @@ -1202,49 +1636,42 @@ void intel_display_power_put(struct drm_i915_private *dev_priv,  #define VLV_DISPLAY_POWER_DOMAINS	POWER_DOMAIN_MASK  #define VLV_DPIO_CMN_BC_POWER_DOMAINS (		\ -	BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) |	\ -	BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) |	\ -	BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) |	\ -	BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) |	\ +	BIT(POWER_DOMAIN_PORT_DDI_B_LANES) |	\ +	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |	\  	BIT(POWER_DOMAIN_PORT_CRT) |		\  	BIT(POWER_DOMAIN_AUX_B) |		\  	BIT(POWER_DOMAIN_AUX_C) |		\  	BIT(POWER_DOMAIN_INIT))  #define VLV_DPIO_TX_B_LANES_01_POWER_DOMAINS (	\ -	BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) |	\ -	BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) |	\ +	BIT(POWER_DOMAIN_PORT_DDI_B_LANES) |	\  	BIT(POWER_DOMAIN_AUX_B) |		\  	BIT(POWER_DOMAIN_INIT))  #define VLV_DPIO_TX_B_LANES_23_POWER_DOMAINS (	\ -	BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) |	\ +	BIT(POWER_DOMAIN_PORT_DDI_B_LANES) |	\  	BIT(POWER_DOMAIN_AUX_B) |		\  	BIT(POWER_DOMAIN_INIT))  #define VLV_DPIO_TX_C_LANES_01_POWER_DOMAINS (	\ -	BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) |	\ -	BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) |	\ +	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |	\  	BIT(POWER_DOMAIN_AUX_C) |		\  	BIT(POWER_DOMAIN_INIT))  #define VLV_DPIO_TX_C_LANES_23_POWER_DOMAINS (	\ -	BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) |	\ +	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |	\  	BIT(POWER_DOMAIN_AUX_C) |		\  	BIT(POWER_DOMAIN_INIT))  #define CHV_DPIO_CMN_BC_POWER_DOMAINS (		\ -	BIT(POWER_DOMAIN_PORT_DDI_B_2_LANES) |	\ -	BIT(POWER_DOMAIN_PORT_DDI_B_4_LANES) |	\ -	BIT(POWER_DOMAIN_PORT_DDI_C_2_LANES) |	\ -	BIT(POWER_DOMAIN_PORT_DDI_C_4_LANES) |	\ +	BIT(POWER_DOMAIN_PORT_DDI_B_LANES) |	\ +	BIT(POWER_DOMAIN_PORT_DDI_C_LANES) |	\  	BIT(POWER_DOMAIN_AUX_B) |		\  	BIT(POWER_DOMAIN_AUX_C) |		\  	BIT(POWER_DOMAIN_INIT))  #define CHV_DPIO_CMN_D_POWER_DOMAINS (		\ -	BIT(POWER_DOMAIN_PORT_DDI_D_2_LANES) |	\ -	BIT(POWER_DOMAIN_PORT_DDI_D_4_LANES) |	\ +	BIT(POWER_DOMAIN_PORT_DDI_D_LANES) |	\  	BIT(POWER_DOMAIN_AUX_D) |		\  	BIT(POWER_DOMAIN_INIT)) @@ -1292,6 +1719,13 @@ static const struct i915_power_well_ops skl_power_well_ops = {  	.is_enabled = skl_power_well_enabled,  }; +static const struct i915_power_well_ops gen9_dc_off_power_well_ops = { +	.sync_hw = gen9_dc_off_power_well_sync_hw, +	.enable = gen9_dc_off_power_well_enable, +	.disable = gen9_dc_off_power_well_disable, +	.is_enabled = gen9_dc_off_power_well_enabled, +}; +  static struct i915_power_well hsw_power_wells[] = {  	{  		.name = "always-on", @@ -1347,6 +1781,7 @@ static struct i915_power_well vlv_power_wells[] = {  		.always_on = 1,  		.domains = VLV_ALWAYS_ON_POWER_DOMAINS,  		.ops = &i9xx_always_on_power_well_ops, +		.data = PUNIT_POWER_WELL_ALWAYS_ON,  	},  	{  		.name = "display", @@ -1430,21 +1865,6 @@ static struct i915_power_well chv_power_wells[] = {  	},  }; -static struct i915_power_well *lookup_power_well(struct drm_i915_private *dev_priv, -						 int power_well_id) -{ -	struct i915_power_domains *power_domains = &dev_priv->power_domains; -	struct i915_power_well *power_well; -	int i; - -	for_each_power_well(i, power_well, POWER_DOMAIN_MASK, power_domains) { -		if (power_well->data == power_well_id) -			return power_well; -	} - -	return NULL; -} -  bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv,  				    int power_well_id)  { @@ -1463,20 +1883,29 @@ static struct i915_power_well skl_power_wells[] = {  		.always_on = 1,  		.domains = SKL_DISPLAY_ALWAYS_ON_POWER_DOMAINS,  		.ops = &i9xx_always_on_power_well_ops, +		.data = SKL_DISP_PW_ALWAYS_ON,  	},  	{  		.name = "power well 1", -		.domains = SKL_DISPLAY_POWERWELL_1_POWER_DOMAINS, +		/* Handled by the DMC firmware */ +		.domains = 0,  		.ops = &skl_power_well_ops,  		.data = SKL_DISP_PW_1,  	},  	{  		.name = "MISC IO power well", -		.domains = SKL_DISPLAY_MISC_IO_POWER_DOMAINS, +		/* Handled by the DMC firmware */ +		.domains = 0,  		.ops = &skl_power_well_ops,  		.data = SKL_DISP_PW_MISC_IO,  	},  	{ +		.name = "DC off", +		.domains = SKL_DISPLAY_DC_OFF_POWER_DOMAINS, +		.ops = &gen9_dc_off_power_well_ops, +		.data = SKL_DISP_PW_DC_OFF, +	}, +	{  		.name = "power well 2",  		.domains = SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS,  		.ops = &skl_power_well_ops, @@ -1508,6 +1937,34 @@ static struct i915_power_well skl_power_wells[] = {  	},  }; +void skl_pw1_misc_io_init(struct drm_i915_private *dev_priv) +{ +	struct i915_power_well *well; + +	if (!IS_SKYLAKE(dev_priv)) +		return; + +	well = lookup_power_well(dev_priv, SKL_DISP_PW_1); +	intel_power_well_enable(dev_priv, well); + +	well = lookup_power_well(dev_priv, SKL_DISP_PW_MISC_IO); +	intel_power_well_enable(dev_priv, well); +} + +void skl_pw1_misc_io_fini(struct drm_i915_private *dev_priv) +{ +	struct i915_power_well *well; + +	if (!IS_SKYLAKE(dev_priv)) +		return; + +	well = lookup_power_well(dev_priv, SKL_DISP_PW_1); +	intel_power_well_disable(dev_priv, well); + +	well = lookup_power_well(dev_priv, SKL_DISP_PW_MISC_IO); +	intel_power_well_disable(dev_priv, well); +} +  static struct i915_power_well bxt_power_wells[] = {  	{  		.name = "always-on", @@ -1522,13 +1979,34 @@ static struct i915_power_well bxt_power_wells[] = {  		.data = SKL_DISP_PW_1,  	},  	{ +		.name = "DC off", +		.domains = BXT_DISPLAY_DC_OFF_POWER_DOMAINS, +		.ops = &gen9_dc_off_power_well_ops, +		.data = SKL_DISP_PW_DC_OFF, +	}, +	{  		.name = "power well 2",  		.domains = BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS,  		.ops = &skl_power_well_ops,  		.data = SKL_DISP_PW_2, -	} +	},  }; +static int +sanitize_disable_power_well_option(const struct drm_i915_private *dev_priv, +				   int disable_power_well) +{ +	if (disable_power_well >= 0) +		return !!disable_power_well; + +	if (IS_BROXTON(dev_priv)) { +		DRM_DEBUG_KMS("Disabling display power well support\n"); +		return 0; +	} + +	return 1; +} +  #define set_power_wells(power_domains, __power_wells) ({		\  	(power_domains)->power_wells = (__power_wells);			\  	(power_domains)->power_well_count = ARRAY_SIZE(__power_wells);	\ @@ -1545,6 +2023,11 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv)  {  	struct i915_power_domains *power_domains = &dev_priv->power_domains; +	i915.disable_power_well = sanitize_disable_power_well_option(dev_priv, +						     i915.disable_power_well); + +	BUILD_BUG_ON(POWER_DOMAIN_NUM > 31); +  	mutex_init(&power_domains->lock);  	/* @@ -1555,7 +2038,7 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv)  		set_power_wells(power_domains, hsw_power_wells);  	} else if (IS_BROADWELL(dev_priv->dev)) {  		set_power_wells(power_domains, bdw_power_wells); -	} else if (IS_SKYLAKE(dev_priv->dev)) { +	} else if (IS_SKYLAKE(dev_priv->dev) || IS_KABYLAKE(dev_priv->dev)) {  		set_power_wells(power_domains, skl_power_wells);  	} else if (IS_BROXTON(dev_priv->dev)) {  		set_power_wells(power_domains, bxt_power_wells); @@ -1570,22 +2053,6 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv)  	return 0;  } -static void intel_runtime_pm_disable(struct drm_i915_private *dev_priv) -{ -	struct drm_device *dev = dev_priv->dev; -	struct device *device = &dev->pdev->dev; - -	if (!HAS_RUNTIME_PM(dev)) -		return; - -	if (!intel_enable_rc6(dev)) -		return; - -	/* Make sure we're not suspended first. */ -	pm_runtime_get_sync(device); -	pm_runtime_disable(device); -} -  /**   * intel_power_domains_fini - finalizes the power domain structures   * @dev_priv: i915 device instance @@ -1596,15 +2063,32 @@ static void intel_runtime_pm_disable(struct drm_i915_private *dev_priv)   */  void intel_power_domains_fini(struct drm_i915_private *dev_priv)  { -	intel_runtime_pm_disable(dev_priv); +	struct device *device = &dev_priv->dev->pdev->dev; -	/* The i915.ko module is still not prepared to be loaded when +	/* +	 * The i915.ko module is still not prepared to be loaded when  	 * the power well is not enabled, so just enable it in case -	 * we're going to unload/reload. */ +	 * we're going to unload/reload. +	 * The following also reacquires the RPM reference the core passed +	 * to the driver during loading, which is dropped in +	 * intel_runtime_pm_enable(). We have to hand back the control of the +	 * device to the core with this reference held. +	 */  	intel_display_set_init_power(dev_priv, true); + +	/* Remove the refcount we took to keep power well support disabled. */ +	if (!i915.disable_power_well) +		intel_display_power_put(dev_priv, POWER_DOMAIN_INIT); + +	/* +	 * Remove the refcount we took in intel_runtime_pm_enable() in case +	 * the platform doesn't support runtime PM. +	 */ +	if (!HAS_RUNTIME_PM(dev_priv)) +		pm_runtime_put(device);  } -static void intel_power_domains_resume(struct drm_i915_private *dev_priv) +static void intel_power_domains_sync_hw(struct drm_i915_private *dev_priv)  {  	struct i915_power_domains *power_domains = &dev_priv->power_domains;  	struct i915_power_well *power_well; @@ -1619,6 +2103,47 @@ static void intel_power_domains_resume(struct drm_i915_private *dev_priv)  	mutex_unlock(&power_domains->lock);  } +static void skl_display_core_init(struct drm_i915_private *dev_priv, +				  bool resume) +{ +	struct i915_power_domains *power_domains = &dev_priv->power_domains; +	uint32_t val; + +	gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); + +	/* enable PCH reset handshake */ +	val = I915_READ(HSW_NDE_RSTWRN_OPT); +	I915_WRITE(HSW_NDE_RSTWRN_OPT, val | RESET_PCH_HANDSHAKE_ENABLE); + +	/* enable PG1 and Misc I/O */ +	mutex_lock(&power_domains->lock); +	skl_pw1_misc_io_init(dev_priv); +	mutex_unlock(&power_domains->lock); + +	if (!resume) +		return; + +	skl_init_cdclk(dev_priv); + +	if (dev_priv->csr.dmc_payload) +		intel_csr_load_program(dev_priv); +} + +static void skl_display_core_uninit(struct drm_i915_private *dev_priv) +{ +	struct i915_power_domains *power_domains = &dev_priv->power_domains; + +	gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); + +	skl_uninit_cdclk(dev_priv); + +	/* The spec doesn't call for removing the reset handshake flag */ +	/* disable PG1 and Misc I/O */ +	mutex_lock(&power_domains->lock); +	skl_pw1_misc_io_fini(dev_priv); +	mutex_unlock(&power_domains->lock); +} +  static void chv_phy_control_init(struct drm_i915_private *dev_priv)  {  	struct i915_power_well *cmn_bc = @@ -1630,19 +2155,80 @@ static void chv_phy_control_init(struct drm_i915_private *dev_priv)  	 * DISPLAY_PHY_CONTROL can get corrupted if read. As a  	 * workaround never ever read DISPLAY_PHY_CONTROL, and  	 * instead maintain a shadow copy ourselves. Use the actual -	 * power well state to reconstruct the expected initial -	 * value. +	 * power well state and lane status to reconstruct the +	 * expected initial value.  	 */  	dev_priv->chv_phy_control =  		PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY0) |  		PHY_LDO_SEQ_DELAY(PHY_LDO_DELAY_600NS, DPIO_PHY1) | -		PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH0) | -		PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY0, DPIO_CH1) | -		PHY_CH_POWER_MODE(PHY_CH_SU_PSR, DPIO_PHY1, DPIO_CH0); -	if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) +		PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH0) | +		PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY0, DPIO_CH1) | +		PHY_CH_POWER_MODE(PHY_CH_DEEP_PSR, DPIO_PHY1, DPIO_CH0); + +	/* +	 * If all lanes are disabled we leave the override disabled +	 * with all power down bits cleared to match the state we +	 * would use after disabling the port. Otherwise enable the +	 * override and set the lane powerdown bits accding to the +	 * current lane status. +	 */ +	if (cmn_bc->ops->is_enabled(dev_priv, cmn_bc)) { +		uint32_t status = I915_READ(DPLL(PIPE_A)); +		unsigned int mask; + +		mask = status & DPLL_PORTB_READY_MASK; +		if (mask == 0xf) +			mask = 0x0; +		else +			dev_priv->chv_phy_control |= +				PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH0); + +		dev_priv->chv_phy_control |= +			PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH0); + +		mask = (status & DPLL_PORTC_READY_MASK) >> 4; +		if (mask == 0xf) +			mask = 0x0; +		else +			dev_priv->chv_phy_control |= +				PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY0, DPIO_CH1); + +		dev_priv->chv_phy_control |= +			PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY0, DPIO_CH1); +  		dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY0); -	if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) + +		dev_priv->chv_phy_assert[DPIO_PHY0] = false; +	} else { +		dev_priv->chv_phy_assert[DPIO_PHY0] = true; +	} + +	if (cmn_d->ops->is_enabled(dev_priv, cmn_d)) { +		uint32_t status = I915_READ(DPIO_PHY_STATUS); +		unsigned int mask; + +		mask = status & DPLL_PORTD_READY_MASK; + +		if (mask == 0xf) +			mask = 0x0; +		else +			dev_priv->chv_phy_control |= +				PHY_CH_POWER_DOWN_OVRD_EN(DPIO_PHY1, DPIO_CH0); + +		dev_priv->chv_phy_control |= +			PHY_CH_POWER_DOWN_OVRD(mask, DPIO_PHY1, DPIO_CH0); +  		dev_priv->chv_phy_control |= PHY_COM_LANE_RESET_DEASSERT(DPIO_PHY1); + +		dev_priv->chv_phy_assert[DPIO_PHY1] = false; +	} else { +		dev_priv->chv_phy_assert[DPIO_PHY1] = true; +	} + +	I915_WRITE(DISPLAY_PHY_CONTROL, dev_priv->chv_phy_control); + +	DRM_DEBUG_KMS("Initial PHY_CONTROL=0x%08x\n", +		      dev_priv->chv_phy_control);  }  static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv) @@ -1680,15 +2266,19 @@ static void vlv_cmnlane_wa(struct drm_i915_private *dev_priv)   * This function initializes the hardware power domain state and enables all   * power domains using intel_display_set_init_power().   */ -void intel_power_domains_init_hw(struct drm_i915_private *dev_priv) +void intel_power_domains_init_hw(struct drm_i915_private *dev_priv, bool resume)  {  	struct drm_device *dev = dev_priv->dev;  	struct i915_power_domains *power_domains = &dev_priv->power_domains;  	power_domains->initializing = true; -	if (IS_CHERRYVIEW(dev)) { +	if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) { +		skl_display_core_init(dev_priv, resume); +	} else if (IS_CHERRYVIEW(dev)) { +		mutex_lock(&power_domains->lock);  		chv_phy_control_init(dev_priv); +		mutex_unlock(&power_domains->lock);  	} else if (IS_VALLEYVIEW(dev)) {  		mutex_lock(&power_domains->lock);  		vlv_cmnlane_wa(dev_priv); @@ -1697,60 +2287,87 @@ void intel_power_domains_init_hw(struct drm_i915_private *dev_priv)  	/* For now, we need the power well to be always enabled. */  	intel_display_set_init_power(dev_priv, true); -	intel_power_domains_resume(dev_priv); +	/* Disable power support if the user asked so. */ +	if (!i915.disable_power_well) +		intel_display_power_get(dev_priv, POWER_DOMAIN_INIT); +	intel_power_domains_sync_hw(dev_priv);  	power_domains->initializing = false;  }  /** - * intel_aux_display_runtime_get - grab an auxiliary power domain reference + * intel_power_domains_suspend - suspend power domain state   * @dev_priv: i915 device instance   * - * This function grabs a power domain reference for the auxiliary power domain - * (for access to the GMBUS and DP AUX blocks) and ensures that it and all its - * parents are powered up. Therefore users should only grab a reference to the - * innermost power domain they need. - * - * Any power domain reference obtained by this function must have a symmetric - * call to intel_aux_display_runtime_put() to release the reference again. + * This function prepares the hardware power domain state before entering + * system suspend. It must be paired with intel_power_domains_init_hw().   */ -void intel_aux_display_runtime_get(struct drm_i915_private *dev_priv) +void intel_power_domains_suspend(struct drm_i915_private *dev_priv)  { -	intel_runtime_pm_get(dev_priv); +	/* +	 * Even if power well support was disabled we still want to disable +	 * power wells while we are system suspended. +	 */ +	if (!i915.disable_power_well) +		intel_display_power_put(dev_priv, POWER_DOMAIN_INIT); + +	if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) +		skl_display_core_uninit(dev_priv);  }  /** - * intel_aux_display_runtime_put - release an auxiliary power domain reference + * intel_runtime_pm_get - grab a runtime pm reference   * @dev_priv: i915 device instance   * - * This function drops the auxiliary power domain reference obtained by - * intel_aux_display_runtime_get() and might power down the corresponding - * hardware block right away if this is the last reference. + * This function grabs a device-level runtime pm reference (mostly used for GEM + * code to ensure the GTT or GT is on) and ensures that it is powered up. + * + * Any runtime pm reference obtained by this function must have a symmetric + * call to intel_runtime_pm_put() to release the reference again.   */ -void intel_aux_display_runtime_put(struct drm_i915_private *dev_priv) +void intel_runtime_pm_get(struct drm_i915_private *dev_priv)  { -	intel_runtime_pm_put(dev_priv); +	struct drm_device *dev = dev_priv->dev; +	struct device *device = &dev->pdev->dev; + +	pm_runtime_get_sync(device); + +	atomic_inc(&dev_priv->pm.wakeref_count); +	assert_rpm_wakelock_held(dev_priv);  }  /** - * intel_runtime_pm_get - grab a runtime pm reference + * intel_runtime_pm_get_if_in_use - grab a runtime pm reference if device in use   * @dev_priv: i915 device instance   * - * This function grabs a device-level runtime pm reference (mostly used for GEM - * code to ensure the GTT or GT is on) and ensures that it is powered up. + * This function grabs a device-level runtime pm reference if the device is + * already in use and ensures that it is powered up.   *   * Any runtime pm reference obtained by this function must have a symmetric   * call to intel_runtime_pm_put() to release the reference again.   */ -void intel_runtime_pm_get(struct drm_i915_private *dev_priv) +bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv)  {  	struct drm_device *dev = dev_priv->dev;  	struct device *device = &dev->pdev->dev; -	if (!HAS_RUNTIME_PM(dev)) -		return; +	if (IS_ENABLED(CONFIG_PM)) { +		int ret = pm_runtime_get_if_in_use(device); -	pm_runtime_get_sync(device); -	WARN(dev_priv->pm.suspended, "Device still suspended.\n"); +		/* +		 * In cases runtime PM is disabled by the RPM core and we get +		 * an -EINVAL return value we are not supposed to call this +		 * function, since the power state is undefined. This applies +		 * atm to the late/early system suspend/resume handlers. +		 */ +		WARN_ON_ONCE(ret < 0); +		if (ret <= 0) +			return false; +	} + +	atomic_inc(&dev_priv->pm.wakeref_count); +	assert_rpm_wakelock_held(dev_priv); + +	return true;  }  /** @@ -1775,11 +2392,10 @@ void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv)  	struct drm_device *dev = dev_priv->dev;  	struct device *device = &dev->pdev->dev; -	if (!HAS_RUNTIME_PM(dev)) -		return; - -	WARN(dev_priv->pm.suspended, "Getting nosync-ref while suspended.\n"); +	assert_rpm_wakelock_held(dev_priv);  	pm_runtime_get_noresume(device); + +	atomic_inc(&dev_priv->pm.wakeref_count);  }  /** @@ -1795,8 +2411,9 @@ void intel_runtime_pm_put(struct drm_i915_private *dev_priv)  	struct drm_device *dev = dev_priv->dev;  	struct device *device = &dev->pdev->dev; -	if (!HAS_RUNTIME_PM(dev)) -		return; +	assert_rpm_wakelock_held(dev_priv); +	if (atomic_dec_and_test(&dev_priv->pm.wakeref_count)) +		atomic_inc(&dev_priv->pm.atomic_seq);  	pm_runtime_mark_last_busy(device);  	pm_runtime_put_autosuspend(device); @@ -1817,24 +2434,27 @@ void intel_runtime_pm_enable(struct drm_i915_private *dev_priv)  	struct drm_device *dev = dev_priv->dev;  	struct device *device = &dev->pdev->dev; -	if (!HAS_RUNTIME_PM(dev)) -		return; - -	pm_runtime_set_active(device); +	pm_runtime_set_autosuspend_delay(device, 10000); /* 10s */ +	pm_runtime_mark_last_busy(device);  	/* -	 * RPM depends on RC6 to save restore the GT HW context, so make RC6 a -	 * requirement. +	 * Take a permanent reference to disable the RPM functionality and drop +	 * it only when unloading the driver. Use the low level get/put helpers, +	 * so the driver's own RPM reference tracking asserts also work on +	 * platforms without RPM support.  	 */ -	if (!intel_enable_rc6(dev)) { -		DRM_INFO("RC6 disabled, disabling runtime PM support\n"); -		return; +	if (!HAS_RUNTIME_PM(dev)) { +		pm_runtime_dont_use_autosuspend(device); +		pm_runtime_get_sync(device); +	} else { +		pm_runtime_use_autosuspend(device);  	} -	pm_runtime_set_autosuspend_delay(device, 10000); /* 10s */ -	pm_runtime_mark_last_busy(device); -	pm_runtime_use_autosuspend(device); - +	/* +	 * The core calls the driver load handler with an RPM reference held. +	 * We drop that here and will reacquire it during unloading in +	 * intel_power_domains_fini(). +	 */  	pm_runtime_put_autosuspend(device);  }  |