diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_sprite.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_sprite.c | 488 | 
1 files changed, 349 insertions, 139 deletions
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index ad6ec4b3900..9a17b4e92ef 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -37,6 +37,106 @@  #include <drm/i915_drm.h>  #include "i915_drv.h" +static int usecs_to_scanlines(const struct drm_display_mode *mode, int usecs) +{ +	/* paranoia */ +	if (!mode->crtc_htotal) +		return 1; + +	return DIV_ROUND_UP(usecs * mode->crtc_clock, 1000 * mode->crtc_htotal); +} + +static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t *start_vbl_count) +{ +	struct drm_device *dev = crtc->base.dev; +	const struct drm_display_mode *mode = &crtc->config.adjusted_mode; +	enum pipe pipe = crtc->pipe; +	long timeout = msecs_to_jiffies_timeout(1); +	int scanline, min, max, vblank_start; +	DEFINE_WAIT(wait); + +	WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex)); + +	vblank_start = mode->crtc_vblank_start; +	if (mode->flags & DRM_MODE_FLAG_INTERLACE) +		vblank_start = DIV_ROUND_UP(vblank_start, 2); + +	/* FIXME needs to be calibrated sensibly */ +	min = vblank_start - usecs_to_scanlines(mode, 100); +	max = vblank_start - 1; + +	if (min <= 0 || max <= 0) +		return false; + +	if (WARN_ON(drm_vblank_get(dev, pipe))) +		return false; + +	local_irq_disable(); + +	trace_i915_pipe_update_start(crtc, min, max); + +	for (;;) { +		/* +		 * prepare_to_wait() has a memory barrier, which guarantees +		 * other CPUs can see the task state update by the time we +		 * read the scanline. +		 */ +		prepare_to_wait(&crtc->vbl_wait, &wait, TASK_UNINTERRUPTIBLE); + +		scanline = intel_get_crtc_scanline(crtc); +		if (scanline < min || scanline > max) +			break; + +		if (timeout <= 0) { +			DRM_ERROR("Potential atomic update failure on pipe %c\n", +				  pipe_name(crtc->pipe)); +			break; +		} + +		local_irq_enable(); + +		timeout = schedule_timeout(timeout); + +		local_irq_disable(); +	} + +	finish_wait(&crtc->vbl_wait, &wait); + +	drm_vblank_put(dev, pipe); + +	*start_vbl_count = dev->driver->get_vblank_counter(dev, pipe); + +	trace_i915_pipe_update_vblank_evaded(crtc, min, max, *start_vbl_count); + +	return true; +} + +static void intel_pipe_update_end(struct intel_crtc *crtc, u32 start_vbl_count) +{ +	struct drm_device *dev = crtc->base.dev; +	enum pipe pipe = crtc->pipe; +	u32 end_vbl_count = dev->driver->get_vblank_counter(dev, pipe); + +	trace_i915_pipe_update_end(crtc, end_vbl_count); + +	local_irq_enable(); + +	if (start_vbl_count != end_vbl_count) +		DRM_ERROR("Atomic update failure on pipe %c (start=%u end=%u)\n", +			  pipe_name(pipe), start_vbl_count, end_vbl_count); +} + +static void intel_update_primary_plane(struct intel_crtc *crtc) +{ +	struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; +	int reg = DSPCNTR(crtc->plane); + +	if (crtc->primary_enabled) +		I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE); +	else +		I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE); +} +  static void  vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,  		 struct drm_framebuffer *fb, @@ -48,11 +148,14 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,  	struct drm_device *dev = dplane->dev;  	struct drm_i915_private *dev_priv = dev->dev_private;  	struct intel_plane *intel_plane = to_intel_plane(dplane); +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);  	int pipe = intel_plane->pipe;  	int plane = intel_plane->plane;  	u32 sprctl;  	unsigned long sprsurf_offset, linear_offset;  	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); +	u32 start_vbl_count; +	bool atomic_update;  	sprctl = I915_READ(SPCNTR(pipe, plane)); @@ -104,6 +207,12 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,  		break;  	} +	/* +	 * Enable gamma to match primary/cursor plane behaviour. +	 * FIXME should be user controllable via propertiesa. +	 */ +	sprctl |= SP_GAMMA_ENABLE; +  	if (obj->tiling_mode != I915_TILING_NONE)  		sprctl |= SP_TILED; @@ -118,9 +227,6 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,  	crtc_w--;  	crtc_h--; -	I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]); -	I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x); -  	linear_offset = y * fb->pitches[0] + x * pixel_size;  	sprsurf_offset = intel_gen4_compute_page_offset(&x, &y,  							obj->tiling_mode, @@ -128,6 +234,13 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,  							fb->pitches[0]);  	linear_offset -= sprsurf_offset; +	atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + +	intel_update_primary_plane(intel_crtc); + +	I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]); +	I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x); +  	if (obj->tiling_mode != I915_TILING_NONE)  		I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x);  	else @@ -135,9 +248,13 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,  	I915_WRITE(SPSIZE(pipe, plane), (crtc_h << 16) | crtc_w);  	I915_WRITE(SPCNTR(pipe, plane), sprctl); -	I915_MODIFY_DISPBASE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) + -			     sprsurf_offset); -	POSTING_READ(SPSURF(pipe, plane)); +	I915_WRITE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) + +		   sprsurf_offset); + +	intel_flush_primary_plane(dev_priv, intel_crtc->plane); + +	if (atomic_update) +		intel_pipe_update_end(intel_crtc, start_vbl_count);  }  static void @@ -146,14 +263,25 @@ vlv_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc)  	struct drm_device *dev = dplane->dev;  	struct drm_i915_private *dev_priv = dev->dev_private;  	struct intel_plane *intel_plane = to_intel_plane(dplane); +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);  	int pipe = intel_plane->pipe;  	int plane = intel_plane->plane; +	u32 start_vbl_count; +	bool atomic_update; + +	atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + +	intel_update_primary_plane(intel_crtc);  	I915_WRITE(SPCNTR(pipe, plane), I915_READ(SPCNTR(pipe, plane)) &  		   ~SP_ENABLE);  	/* Activate double buffered register update */ -	I915_MODIFY_DISPBASE(SPSURF(pipe, plane), 0); -	POSTING_READ(SPSURF(pipe, plane)); +	I915_WRITE(SPSURF(pipe, plane), 0); + +	intel_flush_primary_plane(dev_priv, intel_crtc->plane); + +	if (atomic_update) +		intel_pipe_update_end(intel_crtc, start_vbl_count);  	intel_update_sprite_watermarks(dplane, crtc, 0, 0, false, false);  } @@ -220,11 +348,13 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  	struct drm_device *dev = plane->dev;  	struct drm_i915_private *dev_priv = dev->dev_private;  	struct intel_plane *intel_plane = to_intel_plane(plane); +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);  	int pipe = intel_plane->pipe;  	u32 sprctl, sprscale = 0;  	unsigned long sprsurf_offset, linear_offset;  	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); -	bool scaling_was_enabled = dev_priv->sprite_scaling_enabled; +	u32 start_vbl_count; +	bool atomic_update;  	sprctl = I915_READ(SPRCTL(pipe)); @@ -257,17 +387,23 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  		BUG();  	} +	/* +	 * Enable gamma to match primary/cursor plane behaviour. +	 * FIXME should be user controllable via propertiesa. +	 */ +	sprctl |= SPRITE_GAMMA_ENABLE; +  	if (obj->tiling_mode != I915_TILING_NONE)  		sprctl |= SPRITE_TILED; -	if (IS_HASWELL(dev)) +	if (IS_HASWELL(dev) || IS_BROADWELL(dev))  		sprctl &= ~SPRITE_TRICKLE_FEED_DISABLE;  	else  		sprctl |= SPRITE_TRICKLE_FEED_DISABLE;  	sprctl |= SPRITE_ENABLE; -	if (IS_HASWELL(dev)) +	if (IS_HASWELL(dev) || IS_BROADWELL(dev))  		sprctl |= SPRITE_PIPE_CSC_ENABLE;  	intel_update_sprite_watermarks(plane, crtc, src_w, pixel_size, true, @@ -279,24 +415,8 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  	crtc_w--;  	crtc_h--; -	/* -	 * IVB workaround: must disable low power watermarks for at least -	 * one frame before enabling scaling.  LP watermarks can be re-enabled -	 * when scaling is disabled. -	 */ -	if (crtc_w != src_w || crtc_h != src_h) { -		dev_priv->sprite_scaling_enabled |= 1 << pipe; - -		if (!scaling_was_enabled) { -			intel_update_watermarks(dev); -			intel_wait_for_vblank(dev, pipe); -		} +	if (crtc_w != src_w || crtc_h != src_h)  		sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h; -	} else -		dev_priv->sprite_scaling_enabled &= ~(1 << pipe); - -	I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); -	I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);  	linear_offset = y * fb->pitches[0] + x * pixel_size;  	sprsurf_offset = @@ -304,9 +424,16 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  					       pixel_size, fb->pitches[0]);  	linear_offset -= sprsurf_offset; +	atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + +	intel_update_primary_plane(intel_crtc); + +	I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); +	I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x); +  	/* HSW consolidates SPRTILEOFF and SPRLINOFF into a single SPROFFSET  	 * register */ -	if (IS_HASWELL(dev)) +	if (IS_HASWELL(dev) || IS_BROADWELL(dev))  		I915_WRITE(SPROFFSET(pipe), (y << 16) | x);  	else if (obj->tiling_mode != I915_TILING_NONE)  		I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x); @@ -317,13 +444,13 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  	if (intel_plane->can_scale)  		I915_WRITE(SPRSCALE(pipe), sprscale);  	I915_WRITE(SPRCTL(pipe), sprctl); -	I915_MODIFY_DISPBASE(SPRSURF(pipe), -			     i915_gem_obj_ggtt_offset(obj) + sprsurf_offset); -	POSTING_READ(SPRSURF(pipe)); +	I915_WRITE(SPRSURF(pipe), +		   i915_gem_obj_ggtt_offset(obj) + sprsurf_offset); -	/* potentially re-enable LP watermarks */ -	if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled) -		intel_update_watermarks(dev); +	intel_flush_primary_plane(dev_priv, intel_crtc->plane); + +	if (atomic_update) +		intel_pipe_update_end(intel_crtc, start_vbl_count);  }  static void @@ -332,24 +459,34 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)  	struct drm_device *dev = plane->dev;  	struct drm_i915_private *dev_priv = dev->dev_private;  	struct intel_plane *intel_plane = to_intel_plane(plane); +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);  	int pipe = intel_plane->pipe; -	bool scaling_was_enabled = dev_priv->sprite_scaling_enabled; +	u32 start_vbl_count; +	bool atomic_update; + +	atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + +	intel_update_primary_plane(intel_crtc);  	I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE);  	/* Can't leave the scaler enabled... */  	if (intel_plane->can_scale)  		I915_WRITE(SPRSCALE(pipe), 0);  	/* Activate double buffered register update */ -	I915_MODIFY_DISPBASE(SPRSURF(pipe), 0); -	POSTING_READ(SPRSURF(pipe)); +	I915_WRITE(SPRSURF(pipe), 0); -	dev_priv->sprite_scaling_enabled &= ~(1 << pipe); +	intel_flush_primary_plane(dev_priv, intel_crtc->plane); -	intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false); +	if (atomic_update) +		intel_pipe_update_end(intel_crtc, start_vbl_count); + +	/* +	 * Avoid underruns when disabling the sprite. +	 * FIXME remove once watermark updates are done properly. +	 */ +	intel_wait_for_vblank(dev, pipe); -	/* potentially re-enable LP watermarks */ -	if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled) -		intel_update_watermarks(dev); +	intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false);  }  static int @@ -417,10 +554,13 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  	struct drm_device *dev = plane->dev;  	struct drm_i915_private *dev_priv = dev->dev_private;  	struct intel_plane *intel_plane = to_intel_plane(plane); +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);  	int pipe = intel_plane->pipe;  	unsigned long dvssurf_offset, linear_offset;  	u32 dvscntr, dvsscale;  	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); +	u32 start_vbl_count; +	bool atomic_update;  	dvscntr = I915_READ(DVSCNTR(pipe)); @@ -453,6 +593,12 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  		BUG();  	} +	/* +	 * Enable gamma to match primary/cursor plane behaviour. +	 * FIXME should be user controllable via propertiesa. +	 */ +	dvscntr |= DVS_GAMMA_ENABLE; +  	if (obj->tiling_mode != I915_TILING_NONE)  		dvscntr |= DVS_TILED; @@ -470,18 +616,22 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  	crtc_h--;  	dvsscale = 0; -	if (IS_GEN5(dev) || crtc_w != src_w || crtc_h != src_h) +	if (crtc_w != src_w || crtc_h != src_h)  		dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h; -	I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]); -	I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x); -  	linear_offset = y * fb->pitches[0] + x * pixel_size;  	dvssurf_offset =  		intel_gen4_compute_page_offset(&x, &y, obj->tiling_mode,  					       pixel_size, fb->pitches[0]);  	linear_offset -= dvssurf_offset; +	atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + +	intel_update_primary_plane(intel_crtc); + +	I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]); +	I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x); +  	if (obj->tiling_mode != I915_TILING_NONE)  		I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x);  	else @@ -490,9 +640,13 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  	I915_WRITE(DVSSIZE(pipe), (crtc_h << 16) | crtc_w);  	I915_WRITE(DVSSCALE(pipe), dvsscale);  	I915_WRITE(DVSCNTR(pipe), dvscntr); -	I915_MODIFY_DISPBASE(DVSSURF(pipe), -			     i915_gem_obj_ggtt_offset(obj) + dvssurf_offset); -	POSTING_READ(DVSSURF(pipe)); +	I915_WRITE(DVSSURF(pipe), +		   i915_gem_obj_ggtt_offset(obj) + dvssurf_offset); + +	intel_flush_primary_plane(dev_priv, intel_crtc->plane); + +	if (atomic_update) +		intel_pipe_update_end(intel_crtc, start_vbl_count);  }  static void @@ -501,50 +655,81 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)  	struct drm_device *dev = plane->dev;  	struct drm_i915_private *dev_priv = dev->dev_private;  	struct intel_plane *intel_plane = to_intel_plane(plane); +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);  	int pipe = intel_plane->pipe; +	u32 start_vbl_count; +	bool atomic_update; + +	atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count); + +	intel_update_primary_plane(intel_crtc);  	I915_WRITE(DVSCNTR(pipe), I915_READ(DVSCNTR(pipe)) & ~DVS_ENABLE);  	/* Disable the scaler */  	I915_WRITE(DVSSCALE(pipe), 0);  	/* Flush double buffered register updates */ -	I915_MODIFY_DISPBASE(DVSSURF(pipe), 0); -	POSTING_READ(DVSSURF(pipe)); +	I915_WRITE(DVSSURF(pipe), 0); + +	intel_flush_primary_plane(dev_priv, intel_crtc->plane); + +	if (atomic_update) +		intel_pipe_update_end(intel_crtc, start_vbl_count); + +	/* +	 * Avoid underruns when disabling the sprite. +	 * FIXME remove once watermark updates are done properly. +	 */ +	intel_wait_for_vblank(dev, pipe);  	intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false);  }  static void -intel_enable_primary(struct drm_crtc *crtc) +intel_post_enable_primary(struct drm_crtc *crtc)  {  	struct drm_device *dev = crtc->dev; -	struct drm_i915_private *dev_priv = dev->dev_private;  	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); -	int reg = DSPCNTR(intel_crtc->plane); -	if (!intel_crtc->primary_disabled) -		return; +	/* +	 * BDW signals flip done immediately if the plane +	 * is disabled, even if the plane enable is already +	 * armed to occur at the next vblank :( +	 */ +	if (IS_BROADWELL(dev)) +		intel_wait_for_vblank(dev, intel_crtc->pipe); -	intel_crtc->primary_disabled = false; -	intel_update_fbc(dev); +	/* +	 * FIXME IPS should be fine as long as one plane is +	 * enabled, but in practice it seems to have problems +	 * when going from primary only to sprite only and vice +	 * versa. +	 */ +	hsw_enable_ips(intel_crtc); -	I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE); +	mutex_lock(&dev->struct_mutex); +	intel_update_fbc(dev); +	mutex_unlock(&dev->struct_mutex);  }  static void -intel_disable_primary(struct drm_crtc *crtc) +intel_pre_disable_primary(struct drm_crtc *crtc)  {  	struct drm_device *dev = crtc->dev;  	struct drm_i915_private *dev_priv = dev->dev_private;  	struct intel_crtc *intel_crtc = to_intel_crtc(crtc); -	int reg = DSPCNTR(intel_crtc->plane); - -	if (intel_crtc->primary_disabled) -		return; -	I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE); +	mutex_lock(&dev->struct_mutex); +	if (dev_priv->fbc.plane == intel_crtc->plane) +		intel_disable_fbc(dev); +	mutex_unlock(&dev->struct_mutex); -	intel_crtc->primary_disabled = true; -	intel_update_fbc(dev); +	/* +	 * FIXME IPS should be fine as long as one plane is +	 * enabled, but in practice it seems to have problems +	 * when going from primary only to sprite only and vice +	 * versa. +	 */ +	hsw_disable_ips(intel_crtc);  }  static int @@ -615,6 +800,15 @@ format_is_yuv(uint32_t format)  	}  } +static bool colorkey_enabled(struct intel_plane *intel_plane) +{ +	struct drm_intel_sprite_colorkey key; + +	intel_plane->get_colorkey(&intel_plane->base, &key); + +	return key.flags != I915_SET_COLORKEY_NONE; +} +  static int  intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  		   struct drm_framebuffer *fb, int crtc_x, int crtc_y, @@ -623,16 +817,13 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  		   uint32_t src_w, uint32_t src_h)  {  	struct drm_device *dev = plane->dev; -	struct drm_i915_private *dev_priv = dev->dev_private;  	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);  	struct intel_plane *intel_plane = to_intel_plane(plane); -	struct intel_framebuffer *intel_fb; -	struct drm_i915_gem_object *obj, *old_obj; -	int pipe = intel_plane->pipe; -	enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, -								      pipe); -	int ret = 0; -	bool disable_primary = false; +	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); +	struct drm_i915_gem_object *obj = intel_fb->obj; +	struct drm_i915_gem_object *old_obj = intel_plane->obj; +	int ret; +	bool primary_enabled;  	bool visible;  	int hscale, vscale;  	int max_scale, min_scale; @@ -652,29 +843,23 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  		.y2 = crtc_y + crtc_h,  	};  	const struct drm_rect clip = { -		.x2 = crtc->mode.hdisplay, -		.y2 = crtc->mode.vdisplay, +		.x2 = intel_crtc->active ? intel_crtc->config.pipe_src_w : 0, +		.y2 = intel_crtc->active ? intel_crtc->config.pipe_src_h : 0, +	}; +	const struct { +		int crtc_x, crtc_y; +		unsigned int crtc_w, crtc_h; +		uint32_t src_x, src_y, src_w, src_h; +	} orig = { +		.crtc_x = crtc_x, +		.crtc_y = crtc_y, +		.crtc_w = crtc_w, +		.crtc_h = crtc_h, +		.src_x = src_x, +		.src_y = src_y, +		.src_w = src_w, +		.src_h = src_h,  	}; - -	intel_fb = to_intel_framebuffer(fb); -	obj = intel_fb->obj; - -	old_obj = intel_plane->obj; - -	intel_plane->crtc_x = crtc_x; -	intel_plane->crtc_y = crtc_y; -	intel_plane->crtc_w = crtc_w; -	intel_plane->crtc_h = crtc_h; -	intel_plane->src_x = src_x; -	intel_plane->src_y = src_y; -	intel_plane->src_w = src_w; -	intel_plane->src_h = src_h; - -	/* Pipe must be running... */ -	if (!(I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE)) { -		DRM_DEBUG_KMS("Pipe disabled\n"); -		return -EINVAL; -	}  	/* Don't modify another pipe's plane */  	if (intel_plane->pipe != intel_crtc->pipe) { @@ -809,8 +994,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  	 * If the sprite is completely covering the primary plane,  	 * we can disable the primary and save power.  	 */ -	disable_primary = drm_rect_equals(&dst, &clip); -	WARN_ON(disable_primary && !visible); +	primary_enabled = !drm_rect_equals(&dst, &clip) || colorkey_enabled(intel_plane); +	WARN_ON(!primary_enabled && !visible && intel_crtc->active);  	mutex_lock(&dev->struct_mutex); @@ -820,27 +1005,43 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  	 * the sprite planes only require 128KiB alignment and 32 PTE padding.  	 */  	ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); -	if (ret) -		goto out_unlock; +	mutex_unlock(&dev->struct_mutex); + +	if (ret) +		return ret; + +	intel_plane->crtc_x = orig.crtc_x; +	intel_plane->crtc_y = orig.crtc_y; +	intel_plane->crtc_w = orig.crtc_w; +	intel_plane->crtc_h = orig.crtc_h; +	intel_plane->src_x = orig.src_x; +	intel_plane->src_y = orig.src_y; +	intel_plane->src_w = orig.src_w; +	intel_plane->src_h = orig.src_h;  	intel_plane->obj = obj; -	/* -	 * Be sure to re-enable the primary before the sprite is no longer -	 * covering it fully. -	 */ -	if (!disable_primary) -		intel_enable_primary(crtc); +	if (intel_crtc->active) { +		bool primary_was_enabled = intel_crtc->primary_enabled; -	if (visible) -		intel_plane->update_plane(plane, crtc, fb, obj, -					  crtc_x, crtc_y, crtc_w, crtc_h, -					  src_x, src_y, src_w, src_h); -	else -		intel_plane->disable_plane(plane, crtc); +		intel_crtc->primary_enabled = primary_enabled; -	if (disable_primary) -		intel_disable_primary(crtc); +		if (primary_was_enabled != primary_enabled) +			intel_crtc_wait_for_pending_flips(crtc); + +		if (primary_was_enabled && !primary_enabled) +			intel_pre_disable_primary(crtc); + +		if (visible) +			intel_plane->update_plane(plane, crtc, fb, obj, +						  crtc_x, crtc_y, crtc_w, crtc_h, +						  src_x, src_y, src_w, src_h); +		else +			intel_plane->disable_plane(plane, crtc); + +		if (!primary_was_enabled && primary_enabled) +			intel_post_enable_primary(crtc); +	}  	/* Unpin old obj after new one is active to avoid ugliness */  	if (old_obj) { @@ -850,17 +1051,15 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,  		 * wait for vblank to avoid ugliness, we only need to  		 * do the pin & ref bookkeeping.  		 */ -		if (old_obj != obj) { -			mutex_unlock(&dev->struct_mutex); -			intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe); -			mutex_lock(&dev->struct_mutex); -		} +		if (old_obj != obj && intel_crtc->active) +			intel_wait_for_vblank(dev, intel_crtc->pipe); + +		mutex_lock(&dev->struct_mutex);  		intel_unpin_fb_obj(old_obj); +		mutex_unlock(&dev->struct_mutex);  	} -out_unlock: -	mutex_unlock(&dev->struct_mutex); -	return ret; +	return 0;  }  static int @@ -868,7 +1067,7 @@ intel_disable_plane(struct drm_plane *plane)  {  	struct drm_device *dev = plane->dev;  	struct intel_plane *intel_plane = to_intel_plane(plane); -	int ret = 0; +	struct intel_crtc *intel_crtc;  	if (!plane->fb)  		return 0; @@ -876,21 +1075,31 @@ intel_disable_plane(struct drm_plane *plane)  	if (WARN_ON(!plane->crtc))  		return -EINVAL; -	intel_enable_primary(plane->crtc); -	intel_plane->disable_plane(plane, plane->crtc); +	intel_crtc = to_intel_crtc(plane->crtc); -	if (!intel_plane->obj) -		goto out; +	if (intel_crtc->active) { +		bool primary_was_enabled = intel_crtc->primary_enabled; -	intel_wait_for_vblank(dev, intel_plane->pipe); +		intel_crtc->primary_enabled = true; -	mutex_lock(&dev->struct_mutex); -	intel_unpin_fb_obj(intel_plane->obj); -	intel_plane->obj = NULL; -	mutex_unlock(&dev->struct_mutex); -out: +		intel_plane->disable_plane(plane, plane->crtc); -	return ret; +		if (!primary_was_enabled && intel_crtc->primary_enabled) +			intel_post_enable_primary(plane->crtc); +	} + +	if (intel_plane->obj) { +		if (intel_crtc->active) +			intel_wait_for_vblank(dev, intel_plane->pipe); + +		mutex_lock(&dev->struct_mutex); +		intel_unpin_fb_obj(intel_plane->obj); +		mutex_unlock(&dev->struct_mutex); + +		intel_plane->obj = NULL; +	} + +	return 0;  }  static void intel_destroy_plane(struct drm_plane *plane) @@ -921,7 +1130,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,  	obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE);  	if (!obj) { -		ret = -EINVAL; +		ret = -ENOENT;  		goto out_unlock;  	} @@ -950,7 +1159,7 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,  	obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE);  	if (!obj) { -		ret = -EINVAL; +		ret = -ENOENT;  		goto out_unlock;  	} @@ -1034,7 +1243,7 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane)  	if (INTEL_INFO(dev)->gen < 5)  		return -ENODEV; -	intel_plane = kzalloc(sizeof(struct intel_plane), GFP_KERNEL); +	intel_plane = kzalloc(sizeof(*intel_plane), GFP_KERNEL);  	if (!intel_plane)  		return -ENOMEM; @@ -1058,6 +1267,7 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane)  		break;  	case 7: +	case 8:  		if (IS_IVYBRIDGE(dev)) {  			intel_plane->can_scale = true;  			intel_plane->max_downscale = 2;  | 
