diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_runtime_pm.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_runtime_pm.c | 210 | 
1 files changed, 193 insertions, 17 deletions
diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index d758da6156a8..53ea564f971e 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -94,6 +94,8 @@ intel_display_power_domain_str(enum intel_display_power_domain domain)  		return "PORT_DDI_D_LANES";  	case POWER_DOMAIN_PORT_DDI_E_LANES:  		return "PORT_DDI_E_LANES"; +	case POWER_DOMAIN_PORT_DDI_F_LANES: +		return "PORT_DDI_F_LANES";  	case POWER_DOMAIN_PORT_DDI_A_IO:  		return "PORT_DDI_A_IO";  	case POWER_DOMAIN_PORT_DDI_B_IO: @@ -104,6 +106,8 @@ intel_display_power_domain_str(enum intel_display_power_domain domain)  		return "PORT_DDI_D_IO";  	case POWER_DOMAIN_PORT_DDI_E_IO:  		return "PORT_DDI_E_IO"; +	case POWER_DOMAIN_PORT_DDI_F_IO: +		return "PORT_DDI_F_IO";  	case POWER_DOMAIN_PORT_DSI:  		return "PORT_DSI";  	case POWER_DOMAIN_PORT_CRT: @@ -124,6 +128,10 @@ intel_display_power_domain_str(enum intel_display_power_domain domain)  		return "AUX_C";  	case POWER_DOMAIN_AUX_D:  		return "AUX_D"; +	case POWER_DOMAIN_AUX_F: +		return "AUX_F"; +	case POWER_DOMAIN_AUX_IO_A: +		return "AUX_IO_A";  	case POWER_DOMAIN_GMBUS:  		return "GMBUS";  	case POWER_DOMAIN_INIT: @@ -390,6 +398,15 @@ static void hsw_power_well_enable(struct drm_i915_private *dev_priv,  	I915_WRITE(HSW_PWR_WELL_CTL_DRIVER(id), val | HSW_PWR_WELL_CTL_REQ(id));  	hsw_wait_for_power_well_enable(dev_priv, power_well); +	/* Display WA #1178: cnl */ +	if (IS_CANNONLAKE(dev_priv) && +	    (id == CNL_DISP_PW_AUX_B || id == CNL_DISP_PW_AUX_C || +	     id == CNL_DISP_PW_AUX_D || id == CNL_DISP_PW_AUX_F)) { +		val = I915_READ(CNL_AUX_ANAOVRD1(id)); +		val |= CNL_AUX_ANAOVRD1_ENABLE | CNL_AUX_ANAOVRD1_LDO_BYPASS; +		I915_WRITE(CNL_AUX_ANAOVRD1(id), val); +	} +  	if (wait_fuses)  		gen9_wait_for_power_well_fuses(dev_priv, pg); @@ -1816,9 +1833,11 @@ void intel_display_power_put(struct drm_i915_private *dev_priv,  	BIT_ULL(POWER_DOMAIN_PORT_DDI_B_LANES) |		\  	BIT_ULL(POWER_DOMAIN_PORT_DDI_C_LANES) |		\  	BIT_ULL(POWER_DOMAIN_PORT_DDI_D_LANES) |		\ +	BIT_ULL(POWER_DOMAIN_PORT_DDI_F_LANES) |		\  	BIT_ULL(POWER_DOMAIN_AUX_B) |                       \  	BIT_ULL(POWER_DOMAIN_AUX_C) |			\  	BIT_ULL(POWER_DOMAIN_AUX_D) |			\ +	BIT_ULL(POWER_DOMAIN_AUX_F) |			\  	BIT_ULL(POWER_DOMAIN_AUDIO) |			\  	BIT_ULL(POWER_DOMAIN_VGA) |				\  	BIT_ULL(POWER_DOMAIN_INIT)) @@ -1836,6 +1855,7 @@ void intel_display_power_put(struct drm_i915_private *dev_priv,  	BIT_ULL(POWER_DOMAIN_INIT))  #define CNL_DISPLAY_AUX_A_POWER_DOMAINS (		\  	BIT_ULL(POWER_DOMAIN_AUX_A) |			\ +	BIT_ULL(POWER_DOMAIN_AUX_IO_A) |		\  	BIT_ULL(POWER_DOMAIN_INIT))  #define CNL_DISPLAY_AUX_B_POWER_DOMAINS (		\  	BIT_ULL(POWER_DOMAIN_AUX_B) |			\ @@ -1846,8 +1866,15 @@ void intel_display_power_put(struct drm_i915_private *dev_priv,  #define CNL_DISPLAY_AUX_D_POWER_DOMAINS (		\  	BIT_ULL(POWER_DOMAIN_AUX_D) |			\  	BIT_ULL(POWER_DOMAIN_INIT)) +#define CNL_DISPLAY_AUX_F_POWER_DOMAINS (		\ +	BIT_ULL(POWER_DOMAIN_AUX_F) |			\ +	BIT_ULL(POWER_DOMAIN_INIT)) +#define CNL_DISPLAY_DDI_F_IO_POWER_DOMAINS (		\ +	BIT_ULL(POWER_DOMAIN_PORT_DDI_F_IO) |		\ +	BIT_ULL(POWER_DOMAIN_INIT))  #define CNL_DISPLAY_DC_OFF_POWER_DOMAINS (		\  	CNL_DISPLAY_POWERWELL_2_POWER_DOMAINS |		\ +	BIT_ULL(POWER_DOMAIN_GT_IRQ) |			\  	BIT_ULL(POWER_DOMAIN_MODESET) |			\  	BIT_ULL(POWER_DOMAIN_AUX_A) |			\  	BIT_ULL(POWER_DOMAIN_INIT)) @@ -2395,6 +2422,18 @@ static struct i915_power_well cnl_power_wells[] = {  		.ops = &hsw_power_well_ops,  		.id = SKL_DISP_PW_DDI_D,  	}, +	{ +		.name = "DDI F IO power well", +		.domains = CNL_DISPLAY_DDI_F_IO_POWER_DOMAINS, +		.ops = &hsw_power_well_ops, +		.id = CNL_DISP_PW_DDI_F, +	}, +	{ +		.name = "AUX F", +		.domains = CNL_DISPLAY_AUX_F_POWER_DOMAINS, +		.ops = &hsw_power_well_ops, +		.id = CNL_DISP_PW_AUX_F, +	},  };  static int @@ -2510,6 +2549,16 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv)  		set_power_wells(power_domains, skl_power_wells);  	} else if (IS_CANNONLAKE(dev_priv)) {  		set_power_wells(power_domains, cnl_power_wells); + +		/* +		 * DDI and Aux IO are getting enabled for all ports +		 * regardless the presence or use. So, in order to avoid +		 * timeouts, lets remove them from the list +		 * for the SKUs without port F. +		 */ +		if (!IS_CNL_WITH_PORT_F(dev_priv)) +			power_domains->power_well_count -= 2; +  	} else if (IS_BROXTON(dev_priv)) {  		set_power_wells(power_domains, bxt_power_wells);  	} else if (IS_GEMINILAKE(dev_priv)) { @@ -2600,6 +2649,48 @@ static void gen9_dbuf_disable(struct drm_i915_private *dev_priv)  		DRM_ERROR("DBuf power disable timeout!\n");  } +/* + * TODO: we shouldn't always enable DBUF_CTL_S2, we should only enable it when + * needed and keep it disabled as much as possible. + */ +static void icl_dbuf_enable(struct drm_i915_private *dev_priv) +{ +	I915_WRITE(DBUF_CTL_S1, I915_READ(DBUF_CTL_S1) | DBUF_POWER_REQUEST); +	I915_WRITE(DBUF_CTL_S2, I915_READ(DBUF_CTL_S2) | DBUF_POWER_REQUEST); +	POSTING_READ(DBUF_CTL_S2); + +	udelay(10); + +	if (!(I915_READ(DBUF_CTL_S1) & DBUF_POWER_STATE) || +	    !(I915_READ(DBUF_CTL_S2) & DBUF_POWER_STATE)) +		DRM_ERROR("DBuf power enable timeout\n"); +} + +static void icl_dbuf_disable(struct drm_i915_private *dev_priv) +{ +	I915_WRITE(DBUF_CTL_S1, I915_READ(DBUF_CTL_S1) & ~DBUF_POWER_REQUEST); +	I915_WRITE(DBUF_CTL_S2, I915_READ(DBUF_CTL_S2) & ~DBUF_POWER_REQUEST); +	POSTING_READ(DBUF_CTL_S2); + +	udelay(10); + +	if ((I915_READ(DBUF_CTL_S1) & DBUF_POWER_STATE) || +	    (I915_READ(DBUF_CTL_S2) & DBUF_POWER_STATE)) +		DRM_ERROR("DBuf power disable timeout!\n"); +} + +static void icl_mbus_init(struct drm_i915_private *dev_priv) +{ +	uint32_t val; + +	val = MBUS_ABOX_BT_CREDIT_POOL1(16) | +	      MBUS_ABOX_BT_CREDIT_POOL2(16) | +	      MBUS_ABOX_B_CREDIT(1) | +	      MBUS_ABOX_BW_CREDIT(1); + +	I915_WRITE(MBUS_ABOX_CTL, val); +} +  static void skl_display_core_init(struct drm_i915_private *dev_priv,  				   bool resume)  { @@ -2748,12 +2839,19 @@ static const struct cnl_procmon {  		{ .dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1, },  }; -static void cnl_set_procmon_ref_values(struct drm_i915_private *dev_priv) +/* + * CNL has just one set of registers, while ICL has two sets: one for port A and + * the other for port B. The CNL registers are equivalent to the ICL port A + * registers, that's why we call the ICL macros even though the function has CNL + * on its name. + */ +static void cnl_set_procmon_ref_values(struct drm_i915_private *dev_priv, +				       enum port port)  {  	const struct cnl_procmon *procmon;  	u32 val; -	val = I915_READ(CNL_PORT_COMP_DW3); +	val = I915_READ(ICL_PORT_COMP_DW3(port));  	switch (val & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) {  	default:  		MISSING_CASE(val); @@ -2774,13 +2872,13 @@ static void cnl_set_procmon_ref_values(struct drm_i915_private *dev_priv)  		break;  	} -	val = I915_READ(CNL_PORT_COMP_DW1); +	val = I915_READ(ICL_PORT_COMP_DW1(port));  	val &= ~((0xff << 16) | 0xff);  	val |= procmon->dw1; -	I915_WRITE(CNL_PORT_COMP_DW1, val); +	I915_WRITE(ICL_PORT_COMP_DW1(port), val); -	I915_WRITE(CNL_PORT_COMP_DW9, procmon->dw9); -	I915_WRITE(CNL_PORT_COMP_DW10, procmon->dw10); +	I915_WRITE(ICL_PORT_COMP_DW9(port), procmon->dw9); +	I915_WRITE(ICL_PORT_COMP_DW10(port), procmon->dw10);  }  static void cnl_display_core_init(struct drm_i915_private *dev_priv, bool resume) @@ -2801,7 +2899,8 @@ static void cnl_display_core_init(struct drm_i915_private *dev_priv, bool resume  	val &= ~CNL_COMP_PWR_DOWN;  	I915_WRITE(CHICKEN_MISC_2, val); -	cnl_set_procmon_ref_values(dev_priv); +	/* Dummy PORT_A to get the correct CNL register from the ICL macro */ +	cnl_set_procmon_ref_values(dev_priv, PORT_A);  	val = I915_READ(CNL_PORT_COMP_DW0);  	val |= COMP_INIT; @@ -2865,6 +2964,80 @@ static void cnl_display_core_uninit(struct drm_i915_private *dev_priv)  	I915_WRITE(CHICKEN_MISC_2, val);  } +static void icl_display_core_init(struct drm_i915_private *dev_priv, +				  bool resume) +{ +	enum port port; +	u32 val; + +	gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); + +	/* 1. Enable PCH reset handshake. */ +	val = I915_READ(HSW_NDE_RSTWRN_OPT); +	val |= RESET_PCH_HANDSHAKE_ENABLE; +	I915_WRITE(HSW_NDE_RSTWRN_OPT, val); + +	for (port = PORT_A; port <= PORT_B; port++) { +		/* 2. Enable DDI combo PHY comp. */ +		val = I915_READ(ICL_PHY_MISC(port)); +		val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN; +		I915_WRITE(ICL_PHY_MISC(port), val); + +		cnl_set_procmon_ref_values(dev_priv, port); + +		val = I915_READ(ICL_PORT_COMP_DW0(port)); +		val |= COMP_INIT; +		I915_WRITE(ICL_PORT_COMP_DW0(port), val); + +		/* 3. Set power down enable. */ +		val = I915_READ(ICL_PORT_CL_DW5(port)); +		val |= CL_POWER_DOWN_ENABLE; +		I915_WRITE(ICL_PORT_CL_DW5(port), val); +	} + +	/* 4. Enable power well 1 (PG1) and aux IO power. */ +	/* FIXME: ICL power wells code not here yet. */ + +	/* 5. Enable CDCLK. */ +	icl_init_cdclk(dev_priv); + +	/* 6. Enable DBUF. */ +	icl_dbuf_enable(dev_priv); + +	/* 7. Setup MBUS. */ +	icl_mbus_init(dev_priv); + +	/* 8. CHICKEN_DCPR_1 */ +	I915_WRITE(GEN8_CHICKEN_DCPR_1, I915_READ(GEN8_CHICKEN_DCPR_1) | +					CNL_DDI_CLOCK_REG_ACCESS_ON); +} + +static void icl_display_core_uninit(struct drm_i915_private *dev_priv) +{ +	enum port port; +	u32 val; + +	gen9_set_dc_state(dev_priv, DC_STATE_DISABLE); + +	/* 1. Disable all display engine functions -> aready done */ + +	/* 2. Disable DBUF */ +	icl_dbuf_disable(dev_priv); + +	/* 3. Disable CD clock */ +	icl_uninit_cdclk(dev_priv); + +	/* 4. Disable Power Well 1 (PG1) and Aux IO Power */ +	/* FIXME: ICL power wells code not here yet. */ + +	/* 5. Disable Comp */ +	for (port = PORT_A; port <= PORT_B; port++) { +		val = I915_READ(ICL_PHY_MISC(port)); +		val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN; +		I915_WRITE(ICL_PHY_MISC(port), val); +	} +} +  static void chv_phy_control_init(struct drm_i915_private *dev_priv)  {  	struct i915_power_well *cmn_bc = @@ -2997,7 +3170,9 @@ void intel_power_domains_init_hw(struct drm_i915_private *dev_priv, bool resume)  	power_domains->initializing = true; -	if (IS_CANNONLAKE(dev_priv)) { +	if (IS_ICELAKE(dev_priv)) { +		icl_display_core_init(dev_priv, resume); +	} else if (IS_CANNONLAKE(dev_priv)) {  		cnl_display_core_init(dev_priv, resume);  	} else if (IS_GEN9_BC(dev_priv)) {  		skl_display_core_init(dev_priv, resume); @@ -3038,7 +3213,9 @@ void intel_power_domains_suspend(struct drm_i915_private *dev_priv)  	if (!i915_modparams.disable_power_well)  		intel_display_power_put(dev_priv, POWER_DOMAIN_INIT); -	if (IS_CANNONLAKE(dev_priv)) +	if (IS_ICELAKE(dev_priv)) +		icl_display_core_uninit(dev_priv); +	else if (IS_CANNONLAKE(dev_priv))  		cnl_display_core_uninit(dev_priv);  	else if (IS_GEN9_BC(dev_priv))  		skl_display_core_uninit(dev_priv); @@ -3154,18 +3331,19 @@ void intel_runtime_pm_get(struct drm_i915_private *dev_priv)   * @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. + * already in use and ensures that it is powered up. It is illegal to try + * and access the HW should intel_runtime_pm_get_if_in_use() report failure.   *   * Any runtime pm reference obtained by this function must have a symmetric   * call to intel_runtime_pm_put() to release the reference again. + * + * Returns: True if the wakeref was acquired, or False otherwise.   */  bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv)  { -	struct pci_dev *pdev = dev_priv->drm.pdev; -	struct device *kdev = &pdev->dev; -  	if (IS_ENABLED(CONFIG_PM)) { -		int ret = pm_runtime_get_if_in_use(kdev); +		struct pci_dev *pdev = dev_priv->drm.pdev; +		struct device *kdev = &pdev->dev;  		/*  		 * In cases runtime PM is disabled by the RPM core and we get @@ -3173,9 +3351,7 @@ bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv)  		 * function, since the power state is undefined. This applies  		 * atm to the late/early system suspend/resume handlers.  		 */ -		WARN_ONCE(ret < 0, -			  "pm_runtime_get_if_in_use() failed: %d\n", ret); -		if (ret <= 0) +		if (pm_runtime_get_if_in_use(kdev) <= 0)  			return false;  	}  |