diff options
| -rw-r--r-- | drivers/gpu/drm/drm_drv.c | 2 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_irq.c | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_dma.c | 7 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 11 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_gem.c | 74 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 293 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_opregion.c | 18 | ||||
| -rw-r--r-- | drivers/gpu/drm/i915/i915_suspend.c | 6 | ||||
| -rw-r--r-- | drivers/gpu/drm/mga/mga_dma.c | 8 | ||||
| -rw-r--r-- | drivers/gpu/drm/mga/mga_irq.c | 5 | ||||
| -rw-r--r-- | drivers/gpu/drm/r128/r128_drv.c | 6 | ||||
| -rw-r--r-- | drivers/gpu/drm/r128/r128_drv.h | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/r128/r128_irq.c | 2 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_cp.c | 6 | ||||
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_irq.c | 5 | ||||
| -rw-r--r-- | drivers/gpu/drm/via/via_irq.c | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/via/via_map.c | 11 | ||||
| -rw-r--r-- | include/drm/drmP.h | 1 | 
18 files changed, 262 insertions, 199 deletions
| diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3ab1e9cc4692..996097acb5e7 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -305,6 +305,8 @@ static void drm_cleanup(struct drm_device * dev)  		return;  	} +	drm_vblank_cleanup(dev); +  	drm_lastclose(dev);  	if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 15c8dabc3e97..1e787f894b3c 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -94,7 +94,7 @@ static void vblank_disable_fn(unsigned long arg)  	}  } -static void drm_vblank_cleanup(struct drm_device *dev) +void drm_vblank_cleanup(struct drm_device *dev)  {  	/* Bail if the driver didn't call drm_vblank_init() */  	if (dev->num_crtcs == 0) @@ -278,8 +278,6 @@ int drm_irq_uninstall(struct drm_device * dev)  	free_irq(dev->pdev->irq, dev); -	drm_vblank_cleanup(dev); -  	return 0;  }  EXPORT_SYMBOL(drm_irq_uninstall); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0d215e38606a..ba89b42f790a 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -856,6 +856,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)  	spin_lock_init(&dev_priv->user_irq_lock); +	ret = drm_vblank_init(dev, I915_NUM_PIPE); + +	if (ret) { +		(void) i915_driver_unload(dev); +		return ret; +	} +  	return ret;  } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ef1c0b8f8d07..0a4f39b9a0ec 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -47,6 +47,8 @@ enum pipe {  	PIPE_B,  }; +#define I915_NUM_PIPE	2 +  /* Interface history:   *   * 1.1: Original. @@ -132,6 +134,7 @@ typedef struct drm_i915_private {  	int user_irq_refcount;  	/** Cached value of IMR to avoid reads in updating the bitfield */  	u32 irq_mask_reg; +	u32 pipestat[2];  	int tex_lru_log_granularity;  	int allow_batchbuffer; @@ -147,6 +150,7 @@ typedef struct drm_i915_private {  	u32 saveDSPBCNTR;  	u32 saveDSPARB;  	u32 saveRENDERSTANDBY; +	u32 saveHWS;  	u32 savePIPEACONF;  	u32 savePIPEBCONF;  	u32 savePIPEASRC; @@ -446,6 +450,13 @@ extern int i915_vblank_swap(struct drm_device *dev, void *data,  			    struct drm_file *file_priv);  extern void i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask); +void +i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); + +void +i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); + +  /* i915_mem.c */  extern int i915_mem_alloc(struct drm_device *dev, void *data,  			  struct drm_file *file_priv); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6b4a2bd20640..54bb0d0e49b8 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -31,6 +31,8 @@  #include "i915_drv.h"  #include <linux/swap.h> +#define I915_GEM_GPU_DOMAINS	(~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) +  static int  i915_gem_object_set_domain(struct drm_gem_object *obj,  			    uint32_t read_domains, @@ -83,20 +85,14 @@ int  i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,  			    struct drm_file *file_priv)  { -	drm_i915_private_t *dev_priv = dev->dev_private;  	struct drm_i915_gem_get_aperture *args = data; -	struct drm_i915_gem_object *obj_priv;  	if (!(dev->driver->driver_features & DRIVER_GEM))  		return -ENODEV;  	args->aper_size = dev->gtt_total; -	args->aper_available_size = args->aper_size; - -	list_for_each_entry(obj_priv, &dev_priv->mm.active_list, list) { -		if (obj_priv->pin_count > 0) -			args->aper_available_size -= obj_priv->obj->size; -	} +	args->aper_available_size = (args->aper_size - +				     atomic_read(&dev->pin_memory));  	return 0;  } @@ -1870,17 +1866,6 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,  	for (i = 0; i < args->buffer_count; i++) {  		struct drm_gem_object *obj = object_list[i]; -		struct drm_i915_gem_object *obj_priv = obj->driver_private; - -		if (obj_priv->gtt_space == NULL) { -			/* We evicted the buffer in the process of validating -			 * our set of buffers in.  We could try to recover by -			 * kicking them everything out and trying again from -			 * the start. -			 */ -			ret = -ENOMEM; -			goto err; -		}  		/* make sure all previous memory operations have passed */  		ret = i915_gem_object_set_domain(obj, @@ -2299,29 +2284,52 @@ i915_gem_idle(struct drm_device *dev)  	i915_gem_retire_requests(dev); -	/* Active and flushing should now be empty as we've -	 * waited for a sequence higher than any pending execbuffer -	 */ -	BUG_ON(!list_empty(&dev_priv->mm.active_list)); -	BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); +	if (!dev_priv->mm.wedged) { +		/* Active and flushing should now be empty as we've +		 * waited for a sequence higher than any pending execbuffer +		 */ +		WARN_ON(!list_empty(&dev_priv->mm.active_list)); +		WARN_ON(!list_empty(&dev_priv->mm.flushing_list)); +		/* Request should now be empty as we've also waited +		 * for the last request in the list +		 */ +		WARN_ON(!list_empty(&dev_priv->mm.request_list)); +	} -	/* Request should now be empty as we've also waited -	 * for the last request in the list +	/* Empty the active and flushing lists to inactive.  If there's +	 * anything left at this point, it means that we're wedged and +	 * nothing good's going to happen by leaving them there.  So strip +	 * the GPU domains and just stuff them onto inactive.  	 */ -	BUG_ON(!list_empty(&dev_priv->mm.request_list)); +	while (!list_empty(&dev_priv->mm.active_list)) { +		struct drm_i915_gem_object *obj_priv; -	/* Move all buffers out of the GTT. */ +		obj_priv = list_first_entry(&dev_priv->mm.active_list, +					    struct drm_i915_gem_object, +					    list); +		obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; +		i915_gem_object_move_to_inactive(obj_priv->obj); +	} + +	while (!list_empty(&dev_priv->mm.flushing_list)) { +		struct drm_i915_gem_object *obj_priv; + +		obj_priv = list_first_entry(&dev_priv->mm.active_list, +					    struct drm_i915_gem_object, +					    list); +		obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; +		i915_gem_object_move_to_inactive(obj_priv->obj); +	} + + +	/* Move all inactive buffers out of the GTT. */  	ret = i915_gem_evict_from_list(dev, &dev_priv->mm.inactive_list); +	WARN_ON(!list_empty(&dev_priv->mm.inactive_list));  	if (ret) {  		mutex_unlock(&dev->struct_mutex);  		return ret;  	} -	BUG_ON(!list_empty(&dev_priv->mm.active_list)); -	BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); -	BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); -	BUG_ON(!list_empty(&dev_priv->mm.request_list)); -  	i915_gem_cleanup_ringbuffer(dev);  	mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 82752d6177a4..fe3d9cc72bf5 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -33,11 +33,23 @@  #define MAX_NOPID ((u32)~0) -/** These are the interrupts used by the driver */ -#define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT |		\ -				    I915_ASLE_INTERRUPT |		\ -				    I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ -				    I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) +/** + * Interrupts that are always left unmasked. + * + * Since pipe events are edge-triggered from the PIPESTAT register to IIR, + * we leave them always unmasked in IMR and then control enabling them through + * PIPESTAT alone. + */ +#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \ +				   I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |  \ +				   I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) + +/** Interrupts that we mask and unmask at runtime. */ +#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) + +/** These are all of the interrupts used by the driver */ +#define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \ +				    I915_INTERRUPT_ENABLE_VAR)  void  i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask) @@ -59,6 +71,41 @@ i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask)  	}  } +static inline u32 +i915_pipestat(int pipe) +{ +	if (pipe == 0) +		return PIPEASTAT; +	if (pipe == 1) +		return PIPEBSTAT; +	BUG_ON(1); +} + +void +i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) +{ +	if ((dev_priv->pipestat[pipe] & mask) != mask) { +		u32 reg = i915_pipestat(pipe); + +		dev_priv->pipestat[pipe] |= mask; +		/* Enable the interrupt, clear any pending status */ +		I915_WRITE(reg, dev_priv->pipestat[pipe] | (mask >> 16)); +		(void) I915_READ(reg); +	} +} + +void +i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) +{ +	if ((dev_priv->pipestat[pipe] & mask) != 0) { +		u32 reg = i915_pipestat(pipe); + +		dev_priv->pipestat[pipe] &= ~mask; +		I915_WRITE(reg, dev_priv->pipestat[pipe]); +		(void) I915_READ(reg); +	} +} +  /**   * i915_pipe_enabled - check if a pipe is enabled   * @dev: DRM device @@ -121,80 +168,102 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)  {  	struct drm_device *dev = (struct drm_device *) arg;  	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; -	u32 iir; +	u32 iir, new_iir;  	u32 pipea_stats, pipeb_stats; +	u32 vblank_status; +	u32 vblank_enable;  	int vblank = 0; +	unsigned long irqflags; +	int irq_received; +	int ret = IRQ_NONE;  	atomic_inc(&dev_priv->irq_received); -	if (dev->pdev->msi_enabled) -		I915_WRITE(IMR, ~0);  	iir = I915_READ(IIR); -	if (iir == 0) { -		if (dev->pdev->msi_enabled) { -			I915_WRITE(IMR, dev_priv->irq_mask_reg); -			(void) I915_READ(IMR); -		} -		return IRQ_NONE; +	if (IS_I965G(dev)) { +		vblank_status = I915_START_VBLANK_INTERRUPT_STATUS; +		vblank_enable = PIPE_START_VBLANK_INTERRUPT_ENABLE; +	} else { +		vblank_status = I915_VBLANK_INTERRUPT_STATUS; +		vblank_enable = I915_VBLANK_INTERRUPT_ENABLE;  	} -	/* -	 * Clear the PIPE(A|B)STAT regs before the IIR otherwise -	 * we may get extra interrupts. -	 */ -	if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { +	for (;;) { +		irq_received = iir != 0; + +		/* Can't rely on pipestat interrupt bit in iir as it might +		 * have been cleared after the pipestat interrupt was received. +		 * It doesn't set the bit in iir again, but it still produces +		 * interrupts (for non-MSI). +		 */ +		spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);  		pipea_stats = I915_READ(PIPEASTAT); -		if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)) -			pipea_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | -					 PIPE_VBLANK_INTERRUPT_ENABLE); -		else if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| -					PIPE_VBLANK_INTERRUPT_STATUS)) { +		pipeb_stats = I915_READ(PIPEBSTAT); +		/* +		 * Clear the PIPE(A|B)STAT regs before the IIR +		 */ +		if (pipea_stats & 0x8000ffff) { +			I915_WRITE(PIPEASTAT, pipea_stats); +			irq_received = 1; +		} + +		if (pipeb_stats & 0x8000ffff) { +			I915_WRITE(PIPEBSTAT, pipeb_stats); +			irq_received = 1; +		} +		spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); + +		if (!irq_received) +			break; + +		ret = IRQ_HANDLED; + +		I915_WRITE(IIR, iir); +		new_iir = I915_READ(IIR); /* Flush posted writes */ + +		if (dev_priv->sarea_priv) +			dev_priv->sarea_priv->last_dispatch = +				READ_BREADCRUMB(dev_priv); + +		if (iir & I915_USER_INTERRUPT) { +			dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); +			DRM_WAKEUP(&dev_priv->irq_queue); +		} + +		if (pipea_stats & vblank_status) {  			vblank++;  			drm_handle_vblank(dev, 0);  		} -		I915_WRITE(PIPEASTAT, pipea_stats); -	} -	if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { -		pipeb_stats = I915_READ(PIPEBSTAT); -		/* Ack the event */ -		I915_WRITE(PIPEBSTAT, pipeb_stats); - -		/* The vblank interrupt gets enabled even if we didn't ask for -		   it, so make sure it's shut down again */ -		if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)) -			pipeb_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | -					 PIPE_VBLANK_INTERRUPT_ENABLE); -		else if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| -					PIPE_VBLANK_INTERRUPT_STATUS)) { +		if (pipeb_stats & vblank_status) {  			vblank++;  			drm_handle_vblank(dev, 1);  		} -		if (pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) +		if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) || +		    (iir & I915_ASLE_INTERRUPT))  			opregion_asle_intr(dev); -		I915_WRITE(PIPEBSTAT, pipeb_stats); -	} - -	I915_WRITE(IIR, iir); -	if (dev->pdev->msi_enabled) -		I915_WRITE(IMR, dev_priv->irq_mask_reg); -	(void) I915_READ(IIR); /* Flush posted writes */ - -	if (dev_priv->sarea_priv) -		dev_priv->sarea_priv->last_dispatch = -			READ_BREADCRUMB(dev_priv); -	if (iir & I915_USER_INTERRUPT) { -		dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); -		DRM_WAKEUP(&dev_priv->irq_queue); +		/* With MSI, interrupts are only generated when iir +		 * transitions from zero to nonzero.  If another bit got +		 * set while we were handling the existing iir bits, then +		 * we would never get another interrupt. +		 * +		 * This is fine on non-MSI as well, as if we hit this path +		 * we avoid exiting the interrupt handler only to generate +		 * another one. +		 * +		 * Note that for MSI this could cause a stray interrupt report +		 * if an interrupt landed in the time between writing IIR and +		 * the posting read.  This should be rare enough to never +		 * trigger the 99% of 100,000 interrupts test for disabling +		 * stray interrupts. +		 */ +		iir = new_iir;  	} -	if (iir & I915_ASLE_INTERRUPT) -		opregion_asle_intr(dev); - -	return IRQ_HANDLED; +	return ret;  }  static int i915_emit_irq(struct drm_device * dev) @@ -330,48 +399,16 @@ int i915_irq_wait(struct drm_device *dev, void *data,  int i915_enable_vblank(struct drm_device *dev, int pipe)  {  	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; -	u32	pipestat_reg = 0; -	u32	pipestat; -	u32	interrupt = 0;  	unsigned long irqflags; -	switch (pipe) { -	case 0: -		pipestat_reg = PIPEASTAT; -		interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; -		break; -	case 1: -		pipestat_reg = PIPEBSTAT; -		interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; -		break; -	default: -		DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", -			  pipe); -		return 0; -	} -  	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); -	/* Enabling vblank events in IMR comes before PIPESTAT write, or -	 * there's a race where the PIPESTAT vblank bit gets set to 1, so -	 * the OR of enabled PIPESTAT bits goes to 1, so the PIPExEVENT in -	 * ISR flashes to 1, but the IIR bit doesn't get set to 1 because -	 * IMR masks it.  It doesn't ever get set after we clear the masking -	 * in IMR because the ISR bit is edge, not level-triggered, on the -	 * OR of PIPESTAT bits. -	 */ -	i915_enable_irq(dev_priv, interrupt); -	pipestat = I915_READ(pipestat_reg);  	if (IS_I965G(dev)) -		pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE; +		i915_enable_pipestat(dev_priv, pipe, +				     PIPE_START_VBLANK_INTERRUPT_ENABLE);  	else -		pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE; -	/* Clear any stale interrupt status */ -	pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | -		     PIPE_VBLANK_INTERRUPT_STATUS); -	I915_WRITE(pipestat_reg, pipestat); -	(void) I915_READ(pipestat_reg);	/* Posting read */ +		i915_enable_pipestat(dev_priv, pipe, +				     PIPE_VBLANK_INTERRUPT_ENABLE);  	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); -  	return 0;  } @@ -381,37 +418,12 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)  void i915_disable_vblank(struct drm_device *dev, int pipe)  {  	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; -	u32	pipestat_reg = 0; -	u32	pipestat; -	u32	interrupt = 0;  	unsigned long irqflags; -	switch (pipe) { -	case 0: -		pipestat_reg = PIPEASTAT; -		interrupt = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; -		break; -	case 1: -		pipestat_reg = PIPEBSTAT; -		interrupt = I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; -		break; -	default: -		DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", -			  pipe); -		return; -		break; -	} -  	spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); -	i915_disable_irq(dev_priv, interrupt); -	pipestat = I915_READ(pipestat_reg); -	pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | -		      PIPE_VBLANK_INTERRUPT_ENABLE); -	/* Clear any stale interrupt status */ -	pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | -		     PIPE_VBLANK_INTERRUPT_STATUS); -	I915_WRITE(pipestat_reg, pipestat); -	(void) I915_READ(pipestat_reg);	/* Posting read */ +	i915_disable_pipestat(dev_priv, pipe, +			      PIPE_VBLANK_INTERRUPT_ENABLE | +			      PIPE_START_VBLANK_INTERRUPT_ENABLE);  	spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);  } @@ -476,32 +488,35 @@ void i915_driver_irq_preinstall(struct drm_device * dev)  	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;  	I915_WRITE(HWSTAM, 0xeffe); +	I915_WRITE(PIPEASTAT, 0); +	I915_WRITE(PIPEBSTAT, 0);  	I915_WRITE(IMR, 0xffffffff);  	I915_WRITE(IER, 0x0); +	(void) I915_READ(IER);  }  int i915_driver_irq_postinstall(struct drm_device *dev)  {  	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; -	int ret, num_pipes = 2; - -	/* Set initial unmasked IRQs to just the selected vblank pipes. */ -	dev_priv->irq_mask_reg = ~0; - -	ret = drm_vblank_init(dev, num_pipes); -	if (ret) -		return ret;  	dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; -	dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; -	dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT;  	dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ -	dev_priv->irq_mask_reg &= I915_INTERRUPT_ENABLE_MASK; +	/* Unmask the interrupts that we always want on. */ +	dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX; + +	dev_priv->pipestat[0] = 0; +	dev_priv->pipestat[1] = 0; + +	/* Disable pipe interrupt enables, clear pending pipe status */ +	I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); +	I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); +	/* Clear pending interrupt status */ +	I915_WRITE(IIR, I915_READ(IIR)); -	I915_WRITE(IMR, dev_priv->irq_mask_reg);  	I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); +	I915_WRITE(IMR, dev_priv->irq_mask_reg);  	(void) I915_READ(IER);  	opregion_enable_asle(dev); @@ -513,7 +528,6 @@ int i915_driver_irq_postinstall(struct drm_device *dev)  void i915_driver_irq_uninstall(struct drm_device * dev)  {  	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; -	u32 temp;  	if (!dev_priv)  		return; @@ -521,13 +535,12 @@ void i915_driver_irq_uninstall(struct drm_device * dev)  	dev_priv->vblank_pipe = 0;  	I915_WRITE(HWSTAM, 0xffffffff); +	I915_WRITE(PIPEASTAT, 0); +	I915_WRITE(PIPEBSTAT, 0);  	I915_WRITE(IMR, 0xffffffff);  	I915_WRITE(IER, 0x0); -	temp = I915_READ(PIPEASTAT); -	I915_WRITE(PIPEASTAT, temp); -	temp = I915_READ(PIPEBSTAT); -	I915_WRITE(PIPEBSTAT, temp); -	temp = I915_READ(IIR); -	I915_WRITE(IIR, temp); +	I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); +	I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); +	I915_WRITE(IIR, I915_READ(IIR));  } diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c index 1787a0c7e3ab..13ae731a33db 100644 --- a/drivers/gpu/drm/i915/i915_opregion.c +++ b/drivers/gpu/drm/i915/i915_opregion.c @@ -235,17 +235,15 @@ void opregion_enable_asle(struct drm_device *dev)  	struct opregion_asle *asle = dev_priv->opregion.asle;  	if (asle) { -		u32 pipeb_stats = I915_READ(PIPEBSTAT);  		if (IS_MOBILE(dev)) { -			/* Many devices trigger events with a write to the -			   legacy backlight controller, so we need to ensure -			   that it's able to generate interrupts */ -			I915_WRITE(PIPEBSTAT, pipeb_stats |= -				   I915_LEGACY_BLC_EVENT_ENABLE); -			i915_enable_irq(dev_priv, I915_ASLE_INTERRUPT | -					I915_DISPLAY_PIPE_B_EVENT_INTERRUPT); -		} else -			i915_enable_irq(dev_priv, I915_ASLE_INTERRUPT); +			unsigned long irqflags; + +			spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); +			i915_enable_pipestat(dev_priv, 1, +					     I915_LEGACY_BLC_EVENT_ENABLE); +			spin_unlock_irqrestore(&dev_priv->user_irq_lock, +					       irqflags); +		}  		asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN |  			ASLE_PFMB_EN; diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 5ddc6e595c0c..5d84027ee8f3 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -244,6 +244,9 @@ int i915_save_state(struct drm_device *dev)  	if (IS_I965G(dev) && IS_MOBILE(dev))  		dev_priv->saveRENDERSTANDBY = I915_READ(MCHBAR_RENDER_STANDBY); +	/* Hardware status page */ +	dev_priv->saveHWS = I915_READ(HWS_PGA); +  	/* Display arbitration control */  	dev_priv->saveDSPARB = I915_READ(DSPARB); @@ -373,6 +376,9 @@ int i915_restore_state(struct drm_device *dev)  	if (IS_I965G(dev) && IS_MOBILE(dev))  		I915_WRITE(MCHBAR_RENDER_STANDBY, dev_priv->saveRENDERSTANDBY); +	/* Hardware status page */ +	I915_WRITE(HWS_PGA, dev_priv->saveHWS); +  	/* Display arbitration */  	I915_WRITE(DSPARB, dev_priv->saveDSPARB); diff --git a/drivers/gpu/drm/mga/mga_dma.c b/drivers/gpu/drm/mga/mga_dma.c index c1d12dbfa8d8..b49c5ff29585 100644 --- a/drivers/gpu/drm/mga/mga_dma.c +++ b/drivers/gpu/drm/mga/mga_dma.c @@ -396,6 +396,7 @@ int mga_freelist_put(struct drm_device * dev, struct drm_buf * buf)  int mga_driver_load(struct drm_device * dev, unsigned long flags)  {  	drm_mga_private_t *dev_priv; +	int ret;  	dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER);  	if (!dev_priv) @@ -415,6 +416,13 @@ int mga_driver_load(struct drm_device * dev, unsigned long flags)  	dev->types[7] = _DRM_STAT_PRIMARY;  	dev->types[8] = _DRM_STAT_SECONDARY; +	ret = drm_vblank_init(dev, 1); + +	if (ret) { +		(void) mga_driver_unload(dev); +		return ret; +	} +  	return 0;  } diff --git a/drivers/gpu/drm/mga/mga_irq.c b/drivers/gpu/drm/mga/mga_irq.c index bab42f41188b..daa6041a483a 100644 --- a/drivers/gpu/drm/mga/mga_irq.c +++ b/drivers/gpu/drm/mga/mga_irq.c @@ -152,11 +152,6 @@ void mga_driver_irq_preinstall(struct drm_device * dev)  int mga_driver_irq_postinstall(struct drm_device *dev)  {  	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; -	int ret; - -	ret = drm_vblank_init(dev, 1); -	if (ret) -		return ret;  	DRM_INIT_WAITQUEUE(&dev_priv->fence_queue); diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c index 3265d53ba91f..601f4c0e5da5 100644 --- a/drivers/gpu/drm/r128/r128_drv.c +++ b/drivers/gpu/drm/r128/r128_drv.c @@ -45,6 +45,7 @@ static struct drm_driver driver = {  	    DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG |  	    DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,  	.dev_priv_size = sizeof(drm_r128_buf_priv_t), +	.load = r128_driver_load,  	.preclose = r128_driver_preclose,  	.lastclose = r128_driver_lastclose,  	.get_vblank_counter = r128_get_vblank_counter, @@ -84,6 +85,11 @@ static struct drm_driver driver = {  	.patchlevel = DRIVER_PATCHLEVEL,  }; +int r128_driver_load(struct drm_device * dev, unsigned long flags) +{ +	return drm_vblank_init(dev, 1); +} +  static int __init r128_init(void)  {  	driver.num_ioctls = r128_max_ioctl; diff --git a/drivers/gpu/drm/r128/r128_drv.h b/drivers/gpu/drm/r128/r128_drv.h index 5898b274279d..797a26c42dab 100644 --- a/drivers/gpu/drm/r128/r128_drv.h +++ b/drivers/gpu/drm/r128/r128_drv.h @@ -159,6 +159,7 @@ extern void r128_driver_irq_preinstall(struct drm_device * dev);  extern int r128_driver_irq_postinstall(struct drm_device *dev);  extern void r128_driver_irq_uninstall(struct drm_device * dev);  extern void r128_driver_lastclose(struct drm_device * dev); +extern int r128_driver_load(struct drm_device * dev, unsigned long flags);  extern void r128_driver_preclose(struct drm_device * dev,  				 struct drm_file *file_priv); diff --git a/drivers/gpu/drm/r128/r128_irq.c b/drivers/gpu/drm/r128/r128_irq.c index d7349012a680..69810fb8ac49 100644 --- a/drivers/gpu/drm/r128/r128_irq.c +++ b/drivers/gpu/drm/r128/r128_irq.c @@ -102,7 +102,7 @@ void r128_driver_irq_preinstall(struct drm_device * dev)  int r128_driver_irq_postinstall(struct drm_device *dev)  { -	return drm_vblank_init(dev, 1); +	return 0;  }  void r128_driver_irq_uninstall(struct drm_device * dev) diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index abdc1ae38467..dcebb4bee7aa 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -1757,6 +1757,12 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags)  	if (ret != 0)  		return ret; +	ret = drm_vblank_init(dev, 2); +	if (ret) { +		radeon_driver_unload(dev); +		return ret; +	} +  	DRM_DEBUG("%s card detected\n",  		  ((dev_priv->flags & RADEON_IS_AGP) ? "AGP" : (((dev_priv->flags & RADEON_IS_PCIE) ? "PCIE" : "PCI"))));  	return ret; diff --git a/drivers/gpu/drm/radeon/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c index 5079f7054a2f..97c0599fdb1e 100644 --- a/drivers/gpu/drm/radeon/radeon_irq.c +++ b/drivers/gpu/drm/radeon/radeon_irq.c @@ -337,15 +337,10 @@ int radeon_driver_irq_postinstall(struct drm_device *dev)  {  	drm_radeon_private_t *dev_priv =  	    (drm_radeon_private_t *) dev->dev_private; -	int ret;  	atomic_set(&dev_priv->swi_emitted, 0);  	DRM_INIT_WAITQUEUE(&dev_priv->swi_queue); -	ret = drm_vblank_init(dev, 2); -	if (ret) -		return ret; -  	dev->max_vblank_count = 0x001fffff;  	radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1); diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c index 665d319b927b..c248c1d37268 100644 --- a/drivers/gpu/drm/via/via_irq.c +++ b/drivers/gpu/drm/via/via_irq.c @@ -314,7 +314,6 @@ int via_driver_irq_postinstall(struct drm_device *dev)  	if (!dev_priv)  		return -EINVAL; -	drm_vblank_init(dev, 1);  	status = VIA_READ(VIA_REG_INTERRUPT);  	VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL  		  | dev_priv->irq_enable_mask); diff --git a/drivers/gpu/drm/via/via_map.c b/drivers/gpu/drm/via/via_map.c index a967556be014..2c4f0b485792 100644 --- a/drivers/gpu/drm/via/via_map.c +++ b/drivers/gpu/drm/via/via_map.c @@ -107,8 +107,17 @@ int via_driver_load(struct drm_device *dev, unsigned long chipset)  	ret = drm_sman_init(&dev_priv->sman, 2, 12, 8);  	if (ret) {  		drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); +		return ret;  	} -	return ret; + +	ret = drm_vblank_init(dev, 1); +	if (ret) { +		drm_sman_takedown(&dev_priv->sman); +		drm_free(dev_priv, sizeof(drm_via_private_t), DRM_MEM_DRIVER); +		return ret; +	} + +	return 0;  }  int via_driver_unload(struct drm_device *dev) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 28c7f1679d49..d5e8e5c89548 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1151,6 +1151,7 @@ extern u32 drm_vblank_count(struct drm_device *dev, int crtc);  extern void drm_handle_vblank(struct drm_device *dev, int crtc);  extern int drm_vblank_get(struct drm_device *dev, int crtc);  extern void drm_vblank_put(struct drm_device *dev, int crtc); +extern void drm_vblank_cleanup(struct drm_device *dev);  /* Modesetting support */  extern int drm_modeset_ctl(struct drm_device *dev, void *data,  			   struct drm_file *file_priv); | 
