drm/i915: wait for actual vblank, not just 20ms
Waiting for a hard coded 20ms isn't always enough to make sure a vblank period has actually occurred, so add code to make sure we really have passed through a vblank period (or that the pipe is off when disabling). This prevents problems with mode setting and link training, and seems to fix a bug like https://bugs.freedesktop.org/show_bug.cgi?id=29278, but on an HP 8440p instead. Hopefully also fixes https://bugs.freedesktop.org/show_bug.cgi?id=29141. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Eric Anholt <eric@anholt.net>
This commit is contained in:
		
					parent
					
						
							
								d240f20f54
							
						
					
				
			
			
				commit
				
					
						9d0498a2bf
					
				
			
		
					 7 changed files with 69 additions and 30 deletions
				
			
		|  | @ -2081,6 +2081,7 @@ | |||
| #define PIPE_DITHER_TYPE_ST01		(1 << 2) | ||||
| /* Pipe A */ | ||||
| #define PIPEADSL		0x70000 | ||||
| #define   DSL_LINEMASK	       	0x00000fff | ||||
| #define PIPEACONF		0x70008 | ||||
| #define   PIPEACONF_ENABLE	(1<<31) | ||||
| #define   PIPEACONF_DISABLE	0 | ||||
|  |  | |||
|  | @ -328,7 +328,7 @@ intel_crt_load_detect(struct drm_crtc *crtc, struct intel_encoder *intel_encoder | |||
| 		I915_WRITE(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER); | ||||
| 		/* Wait for next Vblank to substitue
 | ||||
| 		 * border color for Color info */ | ||||
| 		intel_wait_for_vblank(dev); | ||||
| 		intel_wait_for_vblank(dev, pipe); | ||||
| 		st00 = I915_READ8(VGA_MSR_WRITE); | ||||
| 		status = ((st00 & (1 << 4)) != 0) ? | ||||
| 			connector_status_connected : | ||||
|  |  | |||
|  | @ -977,14 +977,54 @@ intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc, | |||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| intel_wait_for_vblank(struct drm_device *dev) | ||||
| /**
 | ||||
|  * intel_wait_for_vblank - wait for vblank on a given pipe | ||||
|  * @dev: drm device | ||||
|  * @pipe: pipe to wait for | ||||
|  * | ||||
|  * Wait for vblank to occur on a given pipe.  Needed for various bits of | ||||
|  * mode setting code. | ||||
|  */ | ||||
| void intel_wait_for_vblank(struct drm_device *dev, int pipe) | ||||
| { | ||||
| 	/* Wait for 20ms, i.e. one cycle at 50hz. */ | ||||
| 	if (in_dbg_master()) | ||||
| 		mdelay(20); /* The kernel debugger cannot call msleep() */ | ||||
| 	else | ||||
| 		msleep(20); | ||||
| 	struct drm_i915_private *dev_priv = dev->dev_private; | ||||
| 	int pipestat_reg = (pipe == 0 ? PIPEASTAT : PIPEBSTAT); | ||||
| 
 | ||||
| 	/* Wait for vblank interrupt bit to set */ | ||||
| 	if (wait_for((I915_READ(pipestat_reg) & | ||||
| 		      PIPE_VBLANK_INTERRUPT_STATUS) == 0, | ||||
| 		     50, 0)) | ||||
| 		DRM_DEBUG_KMS("vblank wait timed out\n"); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * intel_wait_for_vblank_off - wait for vblank after disabling a pipe | ||||
|  * @dev: drm device | ||||
|  * @pipe: pipe to wait for | ||||
|  * | ||||
|  * After disabling a pipe, we can't wait for vblank in the usual way, | ||||
|  * spinning on the vblank interrupt status bit, since we won't actually | ||||
|  * see an interrupt when the pipe is disabled. | ||||
|  * | ||||
|  * So this function waits for the display line value to settle (it | ||||
|  * usually ends up stopping at the start of the next frame). | ||||
|  */ | ||||
| void intel_wait_for_vblank_off(struct drm_device *dev, int pipe) | ||||
| { | ||||
| 	struct drm_i915_private *dev_priv = dev->dev_private; | ||||
| 	int pipedsl_reg = (pipe == 0 ? PIPEADSL : PIPEBDSL); | ||||
| 	unsigned long timeout = jiffies + msecs_to_jiffies(100); | ||||
| 	u32 last_line; | ||||
| 
 | ||||
| 	/* Wait for the display line to settle */ | ||||
| 	do { | ||||
| 		last_line = I915_READ(pipedsl_reg) & DSL_LINEMASK; | ||||
| 		mdelay(5); | ||||
| 	} while (((I915_READ(pipedsl_reg) & DSL_LINEMASK) != last_line) && | ||||
| 		 time_after(timeout, jiffies)); | ||||
| 
 | ||||
| 	if (time_after(jiffies, timeout)) | ||||
| 		DRM_DEBUG_KMS("vblank wait timed out\n"); | ||||
| } | ||||
| 
 | ||||
| /* Parameters have changed, update FBC info */ | ||||
|  | @ -1057,8 +1097,6 @@ void i8xx_disable_fbc(struct drm_device *dev) | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	intel_wait_for_vblank(dev); | ||||
| 
 | ||||
| 	DRM_DEBUG_KMS("disabled FBC\n"); | ||||
| } | ||||
| 
 | ||||
|  | @ -1115,7 +1153,6 @@ void g4x_disable_fbc(struct drm_device *dev) | |||
| 	dpfc_ctl = I915_READ(DPFC_CONTROL); | ||||
| 	dpfc_ctl &= ~DPFC_CTL_EN; | ||||
| 	I915_WRITE(DPFC_CONTROL, dpfc_ctl); | ||||
| 	intel_wait_for_vblank(dev); | ||||
| 
 | ||||
| 	DRM_DEBUG_KMS("disabled FBC\n"); | ||||
| } | ||||
|  | @ -1176,7 +1213,6 @@ void ironlake_disable_fbc(struct drm_device *dev) | |||
| 	dpfc_ctl = I915_READ(ILK_DPFC_CONTROL); | ||||
| 	dpfc_ctl &= ~DPFC_CTL_EN; | ||||
| 	I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl); | ||||
| 	intel_wait_for_vblank(dev); | ||||
| 
 | ||||
| 	DRM_DEBUG_KMS("disabled FBC\n"); | ||||
| } | ||||
|  | @ -1475,7 +1511,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, | |||
| 	if ((IS_I965G(dev) || plane == 0)) | ||||
| 		intel_update_fbc(crtc, &crtc->mode); | ||||
| 
 | ||||
| 	intel_wait_for_vblank(dev); | ||||
| 	intel_wait_for_vblank(dev, intel_crtc->pipe); | ||||
| 	intel_increase_pllclock(crtc, true); | ||||
| 
 | ||||
| 	return 0; | ||||
|  | @ -1593,7 +1629,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, | |||
| 	if ((IS_I965G(dev) || plane == 0)) | ||||
| 		intel_update_fbc(crtc, &crtc->mode); | ||||
| 
 | ||||
| 	intel_wait_for_vblank(dev); | ||||
| 	intel_wait_for_vblank(dev, pipe); | ||||
| 
 | ||||
| 	if (old_fb) { | ||||
| 		intel_fb = to_intel_framebuffer(old_fb); | ||||
|  | @ -2343,10 +2379,8 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) | |||
| 			I915_READ(dspbase_reg); | ||||
| 		} | ||||
| 
 | ||||
| 		if (!IS_I9XX(dev)) { | ||||
| 		/* Wait for vblank for the disable to take effect */ | ||||
| 			intel_wait_for_vblank(dev); | ||||
| 		} | ||||
| 		intel_wait_for_vblank_off(dev, pipe); | ||||
| 
 | ||||
| 		/* Don't disable pipe A or pipe A PLLs if needed */ | ||||
| 		if (pipeconf_reg == PIPEACONF && | ||||
|  | @ -2361,7 +2395,7 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) | |||
| 		} | ||||
| 
 | ||||
| 		/* Wait for vblank for the disable to take effect. */ | ||||
| 		intel_wait_for_vblank(dev); | ||||
| 		intel_wait_for_vblank_off(dev, pipe); | ||||
| 
 | ||||
| 		temp = I915_READ(dpll_reg); | ||||
| 		if ((temp & DPLL_VCO_ENABLE) != 0) { | ||||
|  | @ -4096,7 +4130,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, | |||
| 	I915_WRITE(pipeconf_reg, pipeconf); | ||||
| 	I915_READ(pipeconf_reg); | ||||
| 
 | ||||
| 	intel_wait_for_vblank(dev); | ||||
| 	intel_wait_for_vblank(dev, pipe); | ||||
| 
 | ||||
| 	if (IS_IRONLAKE(dev)) { | ||||
| 		/* enable address swizzle for tiling buffer */ | ||||
|  | @ -4508,7 +4542,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, | |||
| 		encoder_funcs->commit(encoder); | ||||
| 	} | ||||
| 	/* let the connector get through one full cycle before testing */ | ||||
| 	intel_wait_for_vblank(dev); | ||||
| 	intel_wait_for_vblank(dev, intel_crtc->pipe); | ||||
| 
 | ||||
| 	return crtc; | ||||
| } | ||||
|  | @ -4713,7 +4747,7 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule) | |||
| 		dpll &= ~DISPLAY_RATE_SELECT_FPA1; | ||||
| 		I915_WRITE(dpll_reg, dpll); | ||||
| 		dpll = I915_READ(dpll_reg); | ||||
| 		intel_wait_for_vblank(dev); | ||||
| 		intel_wait_for_vblank(dev, pipe); | ||||
| 		dpll = I915_READ(dpll_reg); | ||||
| 		if (dpll & DISPLAY_RATE_SELECT_FPA1) | ||||
| 			DRM_DEBUG_DRIVER("failed to upclock LVDS!\n"); | ||||
|  | @ -4757,7 +4791,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc) | |||
| 		dpll |= DISPLAY_RATE_SELECT_FPA1; | ||||
| 		I915_WRITE(dpll_reg, dpll); | ||||
| 		dpll = I915_READ(dpll_reg); | ||||
| 		intel_wait_for_vblank(dev); | ||||
| 		intel_wait_for_vblank(dev, pipe); | ||||
| 		dpll = I915_READ(dpll_reg); | ||||
| 		if (!(dpll & DISPLAY_RATE_SELECT_FPA1)) | ||||
| 			DRM_DEBUG_DRIVER("failed to downclock LVDS!\n"); | ||||
|  |  | |||
|  | @ -1145,12 +1145,13 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, | |||
| { | ||||
| 	struct drm_device *dev = intel_dp->base.enc.dev; | ||||
| 	struct drm_i915_private *dev_priv = dev->dev_private; | ||||
| 	struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.enc.crtc); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	I915_WRITE(intel_dp->output_reg, dp_reg_value); | ||||
| 	POSTING_READ(intel_dp->output_reg); | ||||
| 	if (first) | ||||
| 		intel_wait_for_vblank(dev); | ||||
| 		intel_wait_for_vblank(dev, intel_crtc->pipe); | ||||
| 
 | ||||
| 	intel_dp_aux_native_write_1(intel_dp, | ||||
| 				    DP_TRAINING_PATTERN_SET, | ||||
|  |  | |||
|  | @ -219,7 +219,8 @@ extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, | |||
| 						    struct drm_crtc *crtc); | ||||
| int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, | ||||
| 				struct drm_file *file_priv); | ||||
| extern void intel_wait_for_vblank(struct drm_device *dev); | ||||
| extern void intel_wait_for_vblank_off(struct drm_device *dev, int pipe); | ||||
| extern void intel_wait_for_vblank(struct drm_device *dev, int pipe); | ||||
| extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe); | ||||
| extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, | ||||
| 						   struct drm_connector *connector, | ||||
|  |  | |||
|  | @ -1218,6 +1218,7 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) | |||
| 	struct drm_device *dev = encoder->dev; | ||||
| 	struct drm_i915_private *dev_priv = dev->dev_private; | ||||
| 	struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); | ||||
| 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); | ||||
| 	u32 temp; | ||||
| 
 | ||||
| 	if (mode != DRM_MODE_DPMS_ON) { | ||||
|  | @ -1240,7 +1241,7 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) | |||
| 		if ((temp & SDVO_ENABLE) == 0) | ||||
| 			intel_sdvo_write_sdvox(intel_sdvo, temp | SDVO_ENABLE); | ||||
| 		for (i = 0; i < 2; i++) | ||||
| 		  intel_wait_for_vblank(dev); | ||||
| 			intel_wait_for_vblank(dev, intel_crtc->pipe); | ||||
| 
 | ||||
| 		status = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2); | ||||
| 		/* Warn if the device reported failure to sync.
 | ||||
|  |  | |||
|  | @ -1158,11 +1158,11 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, | |||
| 
 | ||||
| 		/* Wait for vblank for the disable to take effect */ | ||||
| 		if (!IS_I9XX(dev)) | ||||
| 			intel_wait_for_vblank(dev); | ||||
| 			intel_wait_for_vblank(dev, intel_crtc->pipe); | ||||
| 
 | ||||
| 		I915_WRITE(pipeconf_reg, pipeconf & ~PIPEACONF_ENABLE); | ||||
| 		/* Wait for vblank for the disable to take effect. */ | ||||
| 		intel_wait_for_vblank(dev); | ||||
| 		intel_wait_for_vblank(dev, intel_crtc->pipe); | ||||
| 
 | ||||
| 		/* Filter ctl must be set before TV_WIN_SIZE */ | ||||
| 		I915_WRITE(TV_FILTER_CTL_1, TV_AUTO_SCALE); | ||||
|  | @ -1231,6 +1231,7 @@ intel_tv_detect_type (struct intel_tv *intel_tv) | |||
| 	struct drm_encoder *encoder = &intel_tv->base.enc; | ||||
| 	struct drm_device *dev = encoder->dev; | ||||
| 	struct drm_i915_private *dev_priv = dev->dev_private; | ||||
| 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); | ||||
| 	unsigned long irqflags; | ||||
| 	u32 tv_ctl, save_tv_ctl; | ||||
| 	u32 tv_dac, save_tv_dac; | ||||
|  | @ -1267,11 +1268,11 @@ intel_tv_detect_type (struct intel_tv *intel_tv) | |||
| 		   DAC_C_0_7_V); | ||||
| 	I915_WRITE(TV_CTL, tv_ctl); | ||||
| 	I915_WRITE(TV_DAC, tv_dac); | ||||
| 	intel_wait_for_vblank(dev); | ||||
| 	intel_wait_for_vblank(dev, intel_crtc->pipe); | ||||
| 	tv_dac = I915_READ(TV_DAC); | ||||
| 	I915_WRITE(TV_DAC, save_tv_dac); | ||||
| 	I915_WRITE(TV_CTL, save_tv_ctl); | ||||
| 	intel_wait_for_vblank(dev); | ||||
| 	intel_wait_for_vblank(dev, intel_crtc->pipe); | ||||
| 	/*
 | ||||
| 	 *  A B C | ||||
| 	 *  0 1 1 Composite | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jesse Barnes
				Jesse Barnes