diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_runtime_pm.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_runtime_pm.c | 153 | 
1 files changed, 139 insertions, 14 deletions
| diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index ddbdbffe829a..4f43d9b32e66 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -470,6 +470,43 @@ static void gen9_set_dc_state_debugmask_memory_up(  	}  } +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; @@ -494,10 +531,18 @@ static void gen9_set_dc_state(struct drm_i915_private *dev_priv, uint32_t state)  	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; -	I915_WRITE(DC_STATE_EN, val); -	POSTING_READ(DC_STATE_EN); + +	gen9_write_dc_state(dev_priv, val); + +	dev_priv->csr.dc_state = val & mask;  }  void bxt_enable_dc9(struct drm_i915_private *dev_priv) @@ -1442,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 @@ -1457,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;  }  /** @@ -2213,15 +2303,15 @@ void intel_power_domains_init_hw(struct drm_i915_private *dev_priv, bool resume)   */  void intel_power_domains_suspend(struct drm_i915_private *dev_priv)  { -	if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) -		skl_display_core_uninit(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);  }  /** @@ -2246,6 +2336,41 @@ void intel_runtime_pm_get(struct drm_i915_private *dev_priv)  }  /** + * 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 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. + */ +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 (IS_ENABLED(CONFIG_PM)) { +		int ret = pm_runtime_get_if_in_use(device); + +		/* +		 * 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; +} + +/**   * intel_runtime_pm_get_noresume - grab a runtime pm reference   * @dev_priv: i915 device instance   * | 
