diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_crt.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_crt.c | 157 | 
1 files changed, 149 insertions, 8 deletions
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 9bdd959260a..79acc4f4c1f 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -161,7 +161,7 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)  	hotplug_en &= CRT_FORCE_HOTPLUG_MASK;  	hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; -	if (IS_GM45(dev)) +	if (IS_G4X(dev))  		hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64;  	hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; @@ -198,9 +198,142 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector)  	return intel_ddc_probe(intel_output);  } +static enum drm_connector_status +intel_crt_load_detect(struct drm_crtc *crtc, struct intel_output *intel_output) +{ +	struct drm_encoder *encoder = &intel_output->enc; +	struct drm_device *dev = encoder->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 save_bclrpat; +	uint32_t save_vtotal; +	uint32_t vtotal, vactive; +	uint32_t vsample; +	uint32_t vblank, vblank_start, vblank_end; +	uint32_t dsl; +	uint32_t bclrpat_reg; +	uint32_t vtotal_reg; +	uint32_t vblank_reg; +	uint32_t vsync_reg; +	uint32_t pipeconf_reg; +	uint32_t pipe_dsl_reg; +	uint8_t	st00; +	enum drm_connector_status status; + +	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; +	} + +	save_bclrpat = I915_READ(bclrpat_reg); +	save_vtotal = I915_READ(vtotal_reg); +	vblank = I915_READ(vblank_reg); + +	vtotal = ((save_vtotal >> 16) & 0xfff) + 1; +	vactive = (save_vtotal & 0x7ff) + 1; + +	vblank_start = (vblank & 0xfff) + 1; +	vblank_end = ((vblank >> 16) & 0xfff) + 1; + +	/* Set the border color to purple. */ +	I915_WRITE(bclrpat_reg, 0x500050); + +	if (IS_I9XX(dev)) { +		uint32_t pipeconf = I915_READ(pipeconf_reg); +		I915_WRITE(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER); +		/* Wait for next Vblank to substitue +		 * border color for Color info */ +		intel_wait_for_vblank(dev); +		st00 = I915_READ8(VGA_MSR_WRITE); +		status = ((st00 & (1 << 4)) != 0) ? +			connector_status_connected : +			connector_status_disconnected; + +		I915_WRITE(pipeconf_reg, pipeconf); +	} else { +		bool restore_vblank = false; +		int count, detect; + +		/* +		* If there isn't any border, add some. +		* Yes, this will flicker +		*/ +		if (vblank_start <= vactive && vblank_end >= vtotal) { +			uint32_t vsync = I915_READ(vsync_reg); +			uint32_t vsync_start = (vsync & 0xffff) + 1; + +			vblank_start = vsync_start; +			I915_WRITE(vblank_reg, +				   (vblank_start - 1) | +				   ((vblank_end - 1) << 16)); +			restore_vblank = true; +		} +		/* sample in the vertical border, selecting the larger one */ +		if (vblank_start - vactive >= vtotal - vblank_end) +			vsample = (vblank_start + vactive) >> 1; +		else +			vsample = (vtotal + vblank_end) >> 1; + +		/* +		 * Wait for the border to be displayed +		 */ +		while (I915_READ(pipe_dsl_reg) >= vactive) +			; +		while ((dsl = I915_READ(pipe_dsl_reg)) <= vsample) +			; +		/* +		 * Watch ST00 for an entire scanline +		 */ +		detect = 0; +		count = 0; +		do { +			count++; +			/* Read the ST00 VGA status register */ +			st00 = I915_READ8(VGA_MSR_WRITE); +			if (st00 & (1 << 4)) +				detect++; +		} while ((I915_READ(pipe_dsl_reg) == dsl)); + +		/* restore vblank if necessary */ +		if (restore_vblank) +			I915_WRITE(vblank_reg, vblank); +		/* +		 * If more than 3/4 of the scanline detected a monitor, +		 * then it is assumed to be present. This works even on i830, +		 * where there isn't any way to force the border color across +		 * the screen +		 */ +		status = detect * 4 > count * 3 ? +			 connector_status_connected : +			 connector_status_disconnected; +	} + +	/* Restore previous settings */ +	I915_WRITE(bclrpat_reg, save_bclrpat); + +	return status; +} +  static enum drm_connector_status intel_crt_detect(struct drm_connector *connector)  {  	struct drm_device *dev = connector->dev; +	struct intel_output *intel_output = to_intel_output(connector); +	struct drm_encoder *encoder = &intel_output->enc; +	struct drm_crtc *crtc; +	int dpms_mode; +	enum drm_connector_status status;  	if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {  		if (intel_crt_detect_hotplug(connector)) @@ -212,8 +345,20 @@ static enum drm_connector_status intel_crt_detect(struct drm_connector *connecto  	if (intel_crt_detect_ddc(connector))  		return connector_status_connected; -	/* TODO use load detect */ -	return connector_status_unknown; +	/* for pre-945g platforms use load detect */ +	if (encoder->crtc && encoder->crtc->enabled) { +		status = intel_crt_load_detect(encoder->crtc, intel_output); +	} else { +		crtc = intel_get_load_detect_pipe(intel_output, +						  NULL, &dpms_mode); +		if (crtc) { +			status = intel_crt_load_detect(crtc, intel_output); +			intel_release_load_detect_pipe(intel_output, dpms_mode); +		} else +			status = connector_status_unknown; +	} + +	return status;  }  static void intel_crt_destroy(struct drm_connector *connector) @@ -236,11 +381,6 @@ static int intel_crt_set_property(struct drm_connector *connector,  				  struct drm_property *property,  				  uint64_t value)  { -	struct drm_device *dev = connector->dev; - -	if (property == dev->mode_config.dpms_property && connector->encoder) -		intel_crt_dpms(connector->encoder, (uint32_t)(value & 0xf)); -  	return 0;  } @@ -257,6 +397,7 @@ static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = {  };  static const struct drm_connector_funcs intel_crt_connector_funcs = { +	.dpms = drm_helper_connector_dpms,  	.detect = intel_crt_detect,  	.fill_modes = drm_helper_probe_single_connector_modes,  	.destroy = intel_crt_destroy,  |