diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_crt.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_crt.c | 732 | 
1 files changed, 523 insertions, 209 deletions
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index c55c7704335..5a045d3bd77 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -24,51 +24,233 @@   *	Eric Anholt <eric@anholt.net>   */ +#include <linux/dmi.h>  #include <linux/i2c.h>  #include <linux/slab.h> -#include "drmP.h" -#include "drm.h" -#include "drm_crtc.h" -#include "drm_crtc_helper.h" +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h>  #include "intel_drv.h" -#include "i915_drm.h" +#include <drm/i915_drm.h>  #include "i915_drv.h" -static void intel_crt_dpms(struct drm_encoder *encoder, int mode) +/* Here's the desired hotplug mode */ +#define ADPA_HOTPLUG_BITS (ADPA_CRT_HOTPLUG_PERIOD_128 |		\ +			   ADPA_CRT_HOTPLUG_WARMUP_10MS |		\ +			   ADPA_CRT_HOTPLUG_SAMPLE_4S |			\ +			   ADPA_CRT_HOTPLUG_VOLTAGE_50 |		\ +			   ADPA_CRT_HOTPLUG_VOLREF_325MV |		\ +			   ADPA_CRT_HOTPLUG_ENABLE) + +struct intel_crt { +	struct intel_encoder base; +	/* DPMS state is stored in the connector, which we need in the +	 * encoder's enable/disable callbacks */ +	struct intel_connector *connector; +	bool force_hotplug_required; +	u32 adpa_reg; +}; + +static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder) +{ +	return container_of(encoder, struct intel_crt, base); +} + +static struct intel_crt *intel_attached_crt(struct drm_connector *connector) +{ +	return intel_encoder_to_crt(intel_attached_encoder(connector)); +} + +static bool intel_crt_get_hw_state(struct intel_encoder *encoder, +				   enum pipe *pipe)  { -	struct drm_device *dev = encoder->dev; +	struct drm_device *dev = encoder->base.dev;  	struct drm_i915_private *dev_priv = dev->dev_private; -	u32 temp, reg; +	struct intel_crt *crt = intel_encoder_to_crt(encoder); +	enum intel_display_power_domain power_domain; +	u32 tmp; + +	power_domain = intel_display_port_power_domain(encoder); +	if (!intel_display_power_enabled(dev_priv, power_domain)) +		return false; + +	tmp = I915_READ(crt->adpa_reg); + +	if (!(tmp & ADPA_DAC_ENABLE)) +		return false; + +	if (HAS_PCH_CPT(dev)) +		*pipe = PORT_TO_PIPE_CPT(tmp); +	else +		*pipe = PORT_TO_PIPE(tmp); + +	return true; +} + +static unsigned int intel_crt_get_flags(struct intel_encoder *encoder) +{ +	struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; +	struct intel_crt *crt = intel_encoder_to_crt(encoder); +	u32 tmp, flags = 0; + +	tmp = I915_READ(crt->adpa_reg); + +	if (tmp & ADPA_HSYNC_ACTIVE_HIGH) +		flags |= DRM_MODE_FLAG_PHSYNC; +	else +		flags |= DRM_MODE_FLAG_NHSYNC; + +	if (tmp & ADPA_VSYNC_ACTIVE_HIGH) +		flags |= DRM_MODE_FLAG_PVSYNC; +	else +		flags |= DRM_MODE_FLAG_NVSYNC; + +	return flags; +} + +static void intel_crt_get_config(struct intel_encoder *encoder, +				 struct intel_crtc_config *pipe_config) +{ +	struct drm_device *dev = encoder->base.dev; +	int dotclock; + +	pipe_config->adjusted_mode.flags |= intel_crt_get_flags(encoder); + +	dotclock = pipe_config->port_clock;  	if (HAS_PCH_SPLIT(dev)) -		reg = PCH_ADPA; +		ironlake_check_encoder_dotclock(pipe_config, dotclock); + +	pipe_config->adjusted_mode.crtc_clock = dotclock; +} + +static void hsw_crt_get_config(struct intel_encoder *encoder, +			       struct intel_crtc_config *pipe_config) +{ +	intel_ddi_get_config(encoder, pipe_config); + +	pipe_config->adjusted_mode.flags &= ~(DRM_MODE_FLAG_PHSYNC | +					      DRM_MODE_FLAG_NHSYNC | +					      DRM_MODE_FLAG_PVSYNC | +					      DRM_MODE_FLAG_NVSYNC); +	pipe_config->adjusted_mode.flags |= intel_crt_get_flags(encoder); +} + +/* Note: The caller is required to filter out dpms modes not supported by the + * platform. */ +static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode) +{ +	struct drm_device *dev = encoder->base.dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crt *crt = intel_encoder_to_crt(encoder); +	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); +	struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode; +	u32 adpa; + +	if (INTEL_INFO(dev)->gen >= 5) +		adpa = ADPA_HOTPLUG_BITS;  	else -		reg = ADPA; +		adpa = 0; -	temp = I915_READ(reg); -	temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); -	temp &= ~ADPA_DAC_ENABLE; +	if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) +		adpa |= ADPA_HSYNC_ACTIVE_HIGH; +	if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) +		adpa |= ADPA_VSYNC_ACTIVE_HIGH; -	switch(mode) { +	/* For CPT allow 3 pipe config, for others just use A or B */ +	if (HAS_PCH_LPT(dev)) +		; /* Those bits don't exist here */ +	else if (HAS_PCH_CPT(dev)) +		adpa |= PORT_TRANS_SEL_CPT(crtc->pipe); +	else if (crtc->pipe == 0) +		adpa |= ADPA_PIPE_A_SELECT; +	else +		adpa |= ADPA_PIPE_B_SELECT; + +	if (!HAS_PCH_SPLIT(dev)) +		I915_WRITE(BCLRPAT(crtc->pipe), 0); + +	switch (mode) {  	case DRM_MODE_DPMS_ON: -		temp |= ADPA_DAC_ENABLE; +		adpa |= ADPA_DAC_ENABLE;  		break;  	case DRM_MODE_DPMS_STANDBY: -		temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; +		adpa |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;  		break;  	case DRM_MODE_DPMS_SUSPEND: -		temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; +		adpa |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;  		break;  	case DRM_MODE_DPMS_OFF: -		temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; +		adpa |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;  		break;  	} -	I915_WRITE(reg, temp); +	I915_WRITE(crt->adpa_reg, adpa); +} + +static void intel_disable_crt(struct intel_encoder *encoder) +{ +	intel_crt_set_dpms(encoder, DRM_MODE_DPMS_OFF);  } -static int intel_crt_mode_valid(struct drm_connector *connector, -				struct drm_display_mode *mode) +static void intel_enable_crt(struct intel_encoder *encoder) +{ +	struct intel_crt *crt = intel_encoder_to_crt(encoder); + +	intel_crt_set_dpms(encoder, crt->connector->base.dpms); +} + +/* Special dpms function to support cloning between dvo/sdvo/crt. */ +static void intel_crt_dpms(struct drm_connector *connector, int mode) +{ +	struct drm_device *dev = connector->dev; +	struct intel_encoder *encoder = intel_attached_encoder(connector); +	struct drm_crtc *crtc; +	int old_dpms; + +	/* PCH platforms and VLV only support on/off. */ +	if (INTEL_INFO(dev)->gen >= 5 && mode != DRM_MODE_DPMS_ON) +		mode = DRM_MODE_DPMS_OFF; + +	if (mode == connector->dpms) +		return; + +	old_dpms = connector->dpms; +	connector->dpms = mode; + +	/* Only need to change hw state when actually enabled */ +	crtc = encoder->base.crtc; +	if (!crtc) { +		encoder->connectors_active = false; +		return; +	} + +	/* We need the pipe to run for anything but OFF. */ +	if (mode == DRM_MODE_DPMS_OFF) +		encoder->connectors_active = false; +	else +		encoder->connectors_active = true; + +	/* We call connector dpms manually below in case pipe dpms doesn't +	 * change due to cloning. */ +	if (mode < old_dpms) { +		/* From off to on, enable the pipe first. */ +		intel_crtc_update_dpms(crtc); + +		intel_crt_set_dpms(encoder, mode); +	} else { +		intel_crt_set_dpms(encoder, mode); + +		intel_crtc_update_dpms(crtc); +	} + +	intel_modeset_check_state(connector->dev); +} + +static enum drm_mode_status +intel_crt_mode_valid(struct drm_connector *connector, +		     struct drm_display_mode *mode)  {  	struct drm_device *dev = connector->dev; @@ -86,125 +268,109 @@ static int intel_crt_mode_valid(struct drm_connector *connector,  	if (mode->clock > max_clock)  		return MODE_CLOCK_HIGH; +	/* The FDI receiver on LPT only supports 8bpc and only has 2 lanes. */ +	if (HAS_PCH_LPT(dev) && +	    (ironlake_get_lanes_required(mode->clock, 270000, 24) > 2)) +		return MODE_CLOCK_HIGH; +  	return MODE_OK;  } -static bool intel_crt_mode_fixup(struct drm_encoder *encoder, -				 struct drm_display_mode *mode, -				 struct drm_display_mode *adjusted_mode) +static bool intel_crt_compute_config(struct intel_encoder *encoder, +				     struct intel_crtc_config *pipe_config)  { +	struct drm_device *dev = encoder->base.dev; + +	if (HAS_PCH_SPLIT(dev)) +		pipe_config->has_pch_encoder = true; + +	/* LPT FDI RX only supports 8bpc. */ +	if (HAS_PCH_LPT(dev)) +		pipe_config->pipe_bpp = 24; + +	/* FDI must always be 2.7 GHz */ +	if (HAS_DDI(dev)) +		pipe_config->port_clock = 135000 * 2; +  	return true;  } -static void intel_crt_mode_set(struct drm_encoder *encoder, -			       struct drm_display_mode *mode, -			       struct drm_display_mode *adjusted_mode) +static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)  { - -	struct drm_device *dev = encoder->dev; -	struct drm_crtc *crtc = encoder->crtc; -	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); +	struct drm_device *dev = connector->dev; +	struct intel_crt *crt = intel_attached_crt(connector);  	struct drm_i915_private *dev_priv = dev->dev_private; -	int dpll_md_reg; -	u32 adpa, dpll_md; -	u32 adpa_reg; +	u32 adpa; +	bool ret; -	if (intel_crtc->pipe == 0) -		dpll_md_reg = DPLL_A_MD; -	else -		dpll_md_reg = DPLL_B_MD; +	/* The first time through, trigger an explicit detection cycle */ +	if (crt->force_hotplug_required) { +		bool turn_off_dac = HAS_PCH_SPLIT(dev); +		u32 save_adpa; -	if (HAS_PCH_SPLIT(dev)) -		adpa_reg = PCH_ADPA; -	else -		adpa_reg = ADPA; +		crt->force_hotplug_required = 0; -	/* -	 * Disable separate mode multiplier used when cloning SDVO to CRT -	 * XXX this needs to be adjusted when we really are cloning -	 */ -	if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) { -		dpll_md = I915_READ(dpll_md_reg); -		I915_WRITE(dpll_md_reg, -			   dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); -	} +		save_adpa = adpa = I915_READ(crt->adpa_reg); +		DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa); -	adpa = 0; -	if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) -		adpa |= ADPA_HSYNC_ACTIVE_HIGH; -	if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) -		adpa |= ADPA_VSYNC_ACTIVE_HIGH; +		adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER; +		if (turn_off_dac) +			adpa &= ~ADPA_DAC_ENABLE; -	if (intel_crtc->pipe == 0) { -		if (HAS_PCH_CPT(dev)) -			adpa |= PORT_TRANS_A_SEL_CPT; -		else -			adpa |= ADPA_PIPE_A_SELECT; -		if (!HAS_PCH_SPLIT(dev)) -			I915_WRITE(BCLRPAT_A, 0); -	} else { -		if (HAS_PCH_CPT(dev)) -			adpa |= PORT_TRANS_B_SEL_CPT; -		else -			adpa |= ADPA_PIPE_B_SELECT; -		if (!HAS_PCH_SPLIT(dev)) -			I915_WRITE(BCLRPAT_B, 0); +		I915_WRITE(crt->adpa_reg, adpa); + +		if (wait_for((I915_READ(crt->adpa_reg) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, +			     1000)) +			DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER"); + +		if (turn_off_dac) { +			I915_WRITE(crt->adpa_reg, save_adpa); +			POSTING_READ(crt->adpa_reg); +		}  	} -	I915_WRITE(adpa_reg, adpa); +	/* Check the status to see if both blue and green are on now */ +	adpa = I915_READ(crt->adpa_reg); +	if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0) +		ret = true; +	else +		ret = false; +	DRM_DEBUG_KMS("ironlake hotplug adpa=0x%x, result %d\n", adpa, ret); + +	return ret;  } -static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector) +static bool valleyview_crt_detect_hotplug(struct drm_connector *connector)  {  	struct drm_device *dev = connector->dev; +	struct intel_crt *crt = intel_attached_crt(connector);  	struct drm_i915_private *dev_priv = dev->dev_private; -	u32 adpa, temp; +	u32 adpa;  	bool ret; -	bool turn_off_dac = false; - -	temp = adpa = I915_READ(PCH_ADPA); - -	if (HAS_PCH_SPLIT(dev)) -		turn_off_dac = true; - -	adpa &= ~ADPA_CRT_HOTPLUG_MASK; -	if (turn_off_dac) -		adpa &= ~ADPA_DAC_ENABLE; +	u32 save_adpa; -	/* disable HPD first */ -	I915_WRITE(PCH_ADPA, adpa); -	(void)I915_READ(PCH_ADPA); +	save_adpa = adpa = I915_READ(crt->adpa_reg); +	DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa); -	adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 | -			ADPA_CRT_HOTPLUG_WARMUP_10MS | -			ADPA_CRT_HOTPLUG_SAMPLE_4S | -			ADPA_CRT_HOTPLUG_VOLTAGE_50 | /* default */ -			ADPA_CRT_HOTPLUG_VOLREF_325MV | -			ADPA_CRT_HOTPLUG_ENABLE | -			ADPA_CRT_HOTPLUG_FORCE_TRIGGER); +	adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER; -	DRM_DEBUG_KMS("pch crt adpa 0x%x", adpa); -	I915_WRITE(PCH_ADPA, adpa); +	I915_WRITE(crt->adpa_reg, adpa); -	if (wait_for((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, -		     1000)) +	if (wait_for((I915_READ(crt->adpa_reg) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, +		     1000)) {  		DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER"); - -	if (turn_off_dac) { -		/* Make sure hotplug is enabled */ -		I915_WRITE(PCH_ADPA, temp | ADPA_CRT_HOTPLUG_ENABLE); -		(void)I915_READ(PCH_ADPA); +		I915_WRITE(crt->adpa_reg, save_adpa);  	}  	/* Check the status to see if both blue and green are on now */ -	adpa = I915_READ(PCH_ADPA); -	adpa &= ADPA_CRT_HOTPLUG_MONITOR_MASK; -	if ((adpa == ADPA_CRT_HOTPLUG_MONITOR_COLOR) || -		(adpa == ADPA_CRT_HOTPLUG_MONITOR_MONO)) +	adpa = I915_READ(crt->adpa_reg); +	if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0)  		ret = true;  	else  		ret = false; +	DRM_DEBUG_KMS("valleyview hotplug adpa=0x%x, result %d\n", adpa, ret); +  	return ret;  } @@ -227,6 +393,9 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)  	if (HAS_PCH_SPLIT(dev))  		return intel_ironlake_crt_detect_hotplug(connector); +	if (IS_VALLEYVIEW(dev)) +		return valleyview_crt_detect_hotplug(connector); +  	/*  	 * On 4 series desktop, CRT detect sequence need to be done twice  	 * to get a reliable result. @@ -262,51 +431,81 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)  	return ret;  } -static bool intel_crt_ddc_probe(struct drm_i915_private *dev_priv, int ddc_bus) +static struct edid *intel_crt_get_edid(struct drm_connector *connector, +				struct i2c_adapter *i2c)  { -	u8 buf; -	struct i2c_msg msgs[] = { -		{ -			.addr = 0xA0, -			.flags = 0, -			.len = 1, -			.buf = &buf, -		}, -	}; -	/* DDC monitor detect: Does it ACK a write to 0xA0? */ -	return i2c_transfer(&dev_priv->gmbus[ddc_bus].adapter, msgs, 1) == 1; +	struct edid *edid; + +	edid = drm_get_edid(connector, i2c); + +	if (!edid && !intel_gmbus_is_forced_bit(i2c)) { +		DRM_DEBUG_KMS("CRT GMBUS EDID read failed, retry using GPIO bit-banging\n"); +		intel_gmbus_force_bit(i2c, true); +		edid = drm_get_edid(connector, i2c); +		intel_gmbus_force_bit(i2c, false); +	} + +	return edid;  } -static bool intel_crt_detect_ddc(struct drm_encoder *encoder) +/* local version of intel_ddc_get_modes() to use intel_crt_get_edid() */ +static int intel_crt_ddc_get_modes(struct drm_connector *connector, +				struct i2c_adapter *adapter)  { -	struct intel_encoder *intel_encoder = to_intel_encoder(encoder); -	struct drm_i915_private *dev_priv = encoder->dev->dev_private; +	struct edid *edid; +	int ret; -	/* CRT should always be at 0, but check anyway */ -	if (intel_encoder->type != INTEL_OUTPUT_ANALOG) -		return false; +	edid = intel_crt_get_edid(connector, adapter); +	if (!edid) +		return 0; -	if (intel_crt_ddc_probe(dev_priv, dev_priv->crt_ddc_pin)) { -		DRM_DEBUG_KMS("CRT detected via DDC:0xa0\n"); -		return true; -	} +	ret = intel_connector_update_modes(connector, edid); +	kfree(edid); + +	return ret; +} -	if (intel_ddc_probe(intel_encoder, dev_priv->crt_ddc_pin)) { -		DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n"); -		return true; +static bool intel_crt_detect_ddc(struct drm_connector *connector) +{ +	struct intel_crt *crt = intel_attached_crt(connector); +	struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private; +	struct edid *edid; +	struct i2c_adapter *i2c; + +	BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG); + +	i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin); +	edid = intel_crt_get_edid(connector, i2c); + +	if (edid) { +		bool is_digital = edid->input & DRM_EDID_INPUT_DIGITAL; + +		/* +		 * This may be a DVI-I connector with a shared DDC +		 * link between analog and digital outputs, so we +		 * have to check the EDID input spec of the attached device. +		 */ +		if (!is_digital) { +			DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n"); +			return true; +		} + +		DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n"); +	} else { +		DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [no valid EDID found]\n");  	} +	kfree(edid); +  	return false;  }  static enum drm_connector_status -intel_crt_load_detect(struct drm_crtc *crtc, struct intel_encoder *intel_encoder) +intel_crt_load_detect(struct intel_crt *crt)  { -	struct drm_encoder *encoder = &intel_encoder->base; -	struct drm_device *dev = encoder->dev; +	struct drm_device *dev = crt->base.base.dev;  	struct drm_i915_private *dev_priv = dev->dev_private; -	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); -	uint32_t pipe = intel_crtc->pipe; +	uint32_t pipe = to_intel_crtc(crt->base.base.crtc)->pipe;  	uint32_t save_bclrpat;  	uint32_t save_vtotal;  	uint32_t vtotal, vactive; @@ -324,21 +523,12 @@ intel_crt_load_detect(struct drm_crtc *crtc, struct intel_encoder *intel_encoder  	DRM_DEBUG_KMS("starting load-detect on CRT\n"); -	if (pipe == 0) { -		bclrpat_reg = BCLRPAT_A; -		vtotal_reg = VTOTAL_A; -		vblank_reg = VBLANK_A; -		vsync_reg = VSYNC_A; -		pipeconf_reg = PIPEACONF; -		pipe_dsl_reg = PIPEADSL; -	} else { -		bclrpat_reg = BCLRPAT_B; -		vtotal_reg = VTOTAL_B; -		vblank_reg = VBLANK_B; -		vsync_reg = VSYNC_B; -		pipeconf_reg = PIPEBCONF; -		pipe_dsl_reg = PIPEBDSL; -	} +	bclrpat_reg = BCLRPAT(pipe); +	vtotal_reg = VTOTAL(pipe); +	vblank_reg = VBLANK(pipe); +	vsync_reg = VSYNC(pipe); +	pipeconf_reg = PIPECONF(pipe); +	pipe_dsl_reg = PIPEDSL(pipe);  	save_bclrpat = I915_READ(bclrpat_reg);  	save_vtotal = I915_READ(vtotal_reg); @@ -434,48 +624,74 @@ static enum drm_connector_status  intel_crt_detect(struct drm_connector *connector, bool force)  {  	struct drm_device *dev = connector->dev; -	struct intel_encoder *encoder = intel_attached_encoder(connector); -	struct drm_crtc *crtc; -	int dpms_mode; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crt *crt = intel_attached_crt(connector); +	struct intel_encoder *intel_encoder = &crt->base; +	enum intel_display_power_domain power_domain;  	enum drm_connector_status status; +	struct intel_load_detect_pipe tmp; +	struct drm_modeset_acquire_ctx ctx; + +	intel_runtime_pm_get(dev_priv); + +	DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n", +		      connector->base.id, connector->name, +		      force); + +	power_domain = intel_display_port_power_domain(intel_encoder); +	intel_display_power_get(dev_priv, power_domain);  	if (I915_HAS_HOTPLUG(dev)) { +		/* We can not rely on the HPD pin always being correctly wired +		 * up, for example many KVM do not pass it through, and so +		 * only trust an assertion that the monitor is connected. +		 */  		if (intel_crt_detect_hotplug(connector)) {  			DRM_DEBUG_KMS("CRT detected via hotplug\n"); -			return connector_status_connected; +			status = connector_status_connected; +			goto out;  		} else -			return connector_status_disconnected; +			DRM_DEBUG_KMS("CRT not detected via hotplug\n");  	} -	if (intel_crt_detect_ddc(&encoder->base)) -		return connector_status_connected; +	if (intel_crt_detect_ddc(connector)) { +		status = connector_status_connected; +		goto out; +	} -	if (!force) -		return connector->status; +	/* Load detection is broken on HPD capable machines. Whoever wants a +	 * broken monitor (without edid) to work behind a broken kvm (that fails +	 * to have the right resistors for HP detection) needs to fix this up. +	 * For now just bail out. */ +	if (I915_HAS_HOTPLUG(dev)) { +		status = connector_status_disconnected; +		goto out; +	} -	/* for pre-945g platforms use load detect */ -	if (encoder->base.crtc && encoder->base.crtc->enabled) { -		status = intel_crt_load_detect(encoder->base.crtc, encoder); -	} else { -		crtc = intel_get_load_detect_pipe(encoder, connector, -						  NULL, &dpms_mode); -		if (crtc) { -			if (intel_crt_detect_ddc(&encoder->base)) -				status = connector_status_connected; -			else -				status = intel_crt_load_detect(crtc, encoder); -			intel_release_load_detect_pipe(encoder, -						       connector, dpms_mode); -		} else -			status = connector_status_unknown; +	if (!force) { +		status = connector->status; +		goto out;  	} +	/* for pre-945g platforms use load detect */ +	if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) { +		if (intel_crt_detect_ddc(connector)) +			status = connector_status_connected; +		else +			status = intel_crt_load_detect(crt); +		intel_release_load_detect_pipe(connector, &tmp, &ctx); +	} else +		status = connector_status_unknown; + +out: +	intel_display_power_put(dev_priv, power_domain); +	intel_runtime_pm_put(dev_priv); +  	return status;  }  static void intel_crt_destroy(struct drm_connector *connector)  { -	drm_sysfs_connector_remove(connector);  	drm_connector_cleanup(connector);  	kfree(connector);  } @@ -484,16 +700,28 @@ static int intel_crt_get_modes(struct drm_connector *connector)  {  	struct drm_device *dev = connector->dev;  	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crt *crt = intel_attached_crt(connector); +	struct intel_encoder *intel_encoder = &crt->base; +	enum intel_display_power_domain power_domain;  	int ret; +	struct i2c_adapter *i2c; -	ret = intel_ddc_get_modes(connector, -				 &dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter); +	power_domain = intel_display_port_power_domain(intel_encoder); +	intel_display_power_get(dev_priv, power_domain); + +	i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin); +	ret = intel_crt_ddc_get_modes(connector, i2c);  	if (ret || !IS_G4X(dev)) -		return ret; +		goto out;  	/* Try to probe digital port for output in DVI-I -> VGA mode. */ -	return intel_ddc_get_modes(connector, -				   &dev_priv->gmbus[GMBUS_PORT_DPB].adapter); +	i2c = intel_gmbus_get_adapter(dev_priv, GMBUS_PORT_DPB); +	ret = intel_crt_ddc_get_modes(connector, i2c); + +out: +	intel_display_power_put(dev_priv, power_domain); + +	return ret;  }  static int intel_crt_set_property(struct drm_connector *connector, @@ -503,20 +731,34 @@ static int intel_crt_set_property(struct drm_connector *connector,  	return 0;  } +static void intel_crt_reset(struct drm_connector *connector) +{ +	struct drm_device *dev = connector->dev; +	struct drm_i915_private *dev_priv = dev->dev_private; +	struct intel_crt *crt = intel_attached_crt(connector); + +	if (INTEL_INFO(dev)->gen >= 5) { +		u32 adpa; + +		adpa = I915_READ(crt->adpa_reg); +		adpa &= ~ADPA_CRT_HOTPLUG_MASK; +		adpa |= ADPA_HOTPLUG_BITS; +		I915_WRITE(crt->adpa_reg, adpa); +		POSTING_READ(crt->adpa_reg); + +		DRM_DEBUG_KMS("pch crt adpa set to 0x%x\n", adpa); +		crt->force_hotplug_required = 1; +	} + +} +  /*   * Routines for controlling stuff on the analog port   */ -static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = { -	.dpms = intel_crt_dpms, -	.mode_fixup = intel_crt_mode_fixup, -	.prepare = intel_encoder_prepare, -	.commit = intel_encoder_commit, -	.mode_set = intel_crt_mode_set, -}; -  static const struct drm_connector_funcs intel_crt_connector_funcs = { -	.dpms = drm_helper_connector_dpms, +	.reset = intel_crt_reset, +	.dpms = intel_crt_dpms,  	.detect = intel_crt_detect,  	.fill_modes = drm_helper_probe_single_connector_modes,  	.destroy = intel_crt_destroy, @@ -533,49 +775,121 @@ static const struct drm_encoder_funcs intel_crt_enc_funcs = {  	.destroy = intel_encoder_destroy,  }; +static int __init intel_no_crt_dmi_callback(const struct dmi_system_id *id) +{ +	DRM_INFO("Skipping CRT initialization for %s\n", id->ident); +	return 1; +} + +static const struct dmi_system_id intel_no_crt[] = { +	{ +		.callback = intel_no_crt_dmi_callback, +		.ident = "ACER ZGB", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "ACER"), +			DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), +		}, +	}, +	{ +		.callback = intel_no_crt_dmi_callback, +		.ident = "DELL XPS 8700", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), +			DMI_MATCH(DMI_PRODUCT_NAME, "XPS 8700"), +		}, +	}, +	{ } +}; +  void intel_crt_init(struct drm_device *dev)  {  	struct drm_connector *connector; -	struct intel_encoder *intel_encoder; +	struct intel_crt *crt;  	struct intel_connector *intel_connector;  	struct drm_i915_private *dev_priv = dev->dev_private; -	intel_encoder = kzalloc(sizeof(struct intel_encoder), GFP_KERNEL); -	if (!intel_encoder) +	/* Skip machines without VGA that falsely report hotplug events */ +	if (dmi_check_system(intel_no_crt))  		return; -	intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL); +	crt = kzalloc(sizeof(struct intel_crt), GFP_KERNEL); +	if (!crt) +		return; + +	intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);  	if (!intel_connector) { -		kfree(intel_encoder); +		kfree(crt);  		return;  	}  	connector = &intel_connector->base; +	crt->connector = intel_connector;  	drm_connector_init(dev, &intel_connector->base,  			   &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); -	drm_encoder_init(dev, &intel_encoder->base, &intel_crt_enc_funcs, +	drm_encoder_init(dev, &crt->base.base, &intel_crt_enc_funcs,  			 DRM_MODE_ENCODER_DAC); -	intel_connector_attach_encoder(intel_connector, intel_encoder); +	intel_connector_attach_encoder(intel_connector, &crt->base); + +	crt->base.type = INTEL_OUTPUT_ANALOG; +	crt->base.cloneable = (1 << INTEL_OUTPUT_DVO) | (1 << INTEL_OUTPUT_HDMI); +	if (IS_I830(dev)) +		crt->base.crtc_mask = (1 << 0); +	else +		crt->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); -	intel_encoder->type = INTEL_OUTPUT_ANALOG; -	intel_encoder->clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT) | -				   (1 << INTEL_ANALOG_CLONE_BIT) | -				   (1 << INTEL_SDVO_LVDS_CLONE_BIT); -	intel_encoder->crtc_mask = (1 << 0) | (1 << 1); -	connector->interlace_allowed = 1; +	if (IS_GEN2(dev)) +		connector->interlace_allowed = 0; +	else +		connector->interlace_allowed = 1;  	connector->doublescan_allowed = 0; -	drm_encoder_helper_add(&intel_encoder->base, &intel_crt_helper_funcs); +	if (HAS_PCH_SPLIT(dev)) +		crt->adpa_reg = PCH_ADPA; +	else if (IS_VALLEYVIEW(dev)) +		crt->adpa_reg = VLV_ADPA; +	else +		crt->adpa_reg = ADPA; + +	crt->base.compute_config = intel_crt_compute_config; +	crt->base.disable = intel_disable_crt; +	crt->base.enable = intel_enable_crt; +	if (I915_HAS_HOTPLUG(dev)) +		crt->base.hpd_pin = HPD_CRT; +	if (HAS_DDI(dev)) { +		crt->base.get_config = hsw_crt_get_config; +		crt->base.get_hw_state = intel_ddi_get_hw_state; +	} else { +		crt->base.get_config = intel_crt_get_config; +		crt->base.get_hw_state = intel_crt_get_hw_state; +	} +	intel_connector->get_hw_state = intel_connector_get_hw_state; +	intel_connector->unregister = intel_connector_unregister; +  	drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);  	drm_sysfs_connector_add(connector); -	if (I915_HAS_HOTPLUG(dev)) -		connector->polled = DRM_CONNECTOR_POLL_HPD; -	else -		connector->polled = DRM_CONNECTOR_POLL_CONNECT; +	if (!I915_HAS_HOTPLUG(dev)) +		intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT; + +	/* +	 * Configure the automatic hotplug detection stuff +	 */ +	crt->force_hotplug_required = 0; + +	/* +	 * TODO: find a proper way to discover whether we need to set the the +	 * polarity and link reversal bits or not, instead of relying on the +	 * BIOS. +	 */ +	if (HAS_PCH_LPT(dev)) { +		u32 fdi_config = FDI_RX_POLARITY_REVERSED_LPT | +				 FDI_RX_LINK_REVERSAL_OVERRIDE; + +		dev_priv->fdi_rx_config = I915_READ(_FDI_RXA_CTL) & fdi_config; +	} -	dev_priv->hotplug_supported_mask |= CRT_HOTPLUG_INT_STATUS; +	intel_crt_reset(connector);  }  | 
