diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
| -rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 136 | 
1 files changed, 71 insertions, 65 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index febddc2952f..ab023ca73b4 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -451,6 +451,31 @@ static void snb_gt_irq_handler(struct drm_device *dev,  	}  } +static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, +				u32 pm_iir) +{ +	unsigned long flags; + +	/* +	 * IIR bits should never already be set because IMR should +	 * prevent an interrupt from being shown in IIR. The warning +	 * displays a case where we've unsafely cleared +	 * dev_priv->pm_iir. Although missing an interrupt of the same +	 * type is not a problem, it displays a problem in the logic. +	 * +	 * The mask bit in IMR is cleared by rps_work. +	 */ + +	spin_lock_irqsave(&dev_priv->rps_lock, flags); +	WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n"); +	dev_priv->pm_iir |= pm_iir; +	I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir); +	POSTING_READ(GEN6_PMIMR); +	spin_unlock_irqrestore(&dev_priv->rps_lock, flags); + +	queue_work(dev_priv->wq, &dev_priv->rps_work); +} +  static irqreturn_t valleyview_irq_handler(DRM_IRQ_ARGS)  {  	struct drm_device *dev = (struct drm_device *) arg; @@ -532,16 +557,8 @@ static irqreturn_t valleyview_irq_handler(DRM_IRQ_ARGS)  		if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)  			blc_event = true; -		if (pm_iir & GEN6_PM_DEFERRED_EVENTS) { -			unsigned long flags; -			spin_lock_irqsave(&dev_priv->rps_lock, flags); -			WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n"); -			dev_priv->pm_iir |= pm_iir; -			I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir); -			POSTING_READ(GEN6_PMIMR); -			spin_unlock_irqrestore(&dev_priv->rps_lock, flags); -			queue_work(dev_priv->wq, &dev_priv->rps_work); -		} +		if (pm_iir & GEN6_PM_DEFERRED_EVENTS) +			gen6_queue_rps_work(dev_priv, pm_iir);  		I915_WRITE(GTIIR, gt_iir);  		I915_WRITE(GEN6_PMIIR, pm_iir); @@ -655,16 +672,8 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS)  		pch_irq_handler(dev);  	} -	if (pm_iir & GEN6_PM_DEFERRED_EVENTS) { -		unsigned long flags; -		spin_lock_irqsave(&dev_priv->rps_lock, flags); -		WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n"); -		dev_priv->pm_iir |= pm_iir; -		I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir); -		POSTING_READ(GEN6_PMIMR); -		spin_unlock_irqrestore(&dev_priv->rps_lock, flags); -		queue_work(dev_priv->wq, &dev_priv->rps_work); -	} +	if (pm_iir & GEN6_PM_DEFERRED_EVENTS) +		gen6_queue_rps_work(dev_priv, pm_iir);  	/* should clear PCH hotplug event before clear CPU irq */  	I915_WRITE(SDEIIR, pch_iir); @@ -764,25 +773,8 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)  		i915_handle_rps_change(dev);  	} -	if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS) { -		/* -		 * IIR bits should never already be set because IMR should -		 * prevent an interrupt from being shown in IIR. The warning -		 * displays a case where we've unsafely cleared -		 * dev_priv->pm_iir. Although missing an interrupt of the same -		 * type is not a problem, it displays a problem in the logic. -		 * -		 * The mask bit in IMR is cleared by rps_work. -		 */ -		unsigned long flags; -		spin_lock_irqsave(&dev_priv->rps_lock, flags); -		WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n"); -		dev_priv->pm_iir |= pm_iir; -		I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir); -		POSTING_READ(GEN6_PMIMR); -		spin_unlock_irqrestore(&dev_priv->rps_lock, flags); -		queue_work(dev_priv->wq, &dev_priv->rps_work); -	} +	if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS) +		gen6_queue_rps_work(dev_priv, pm_iir);  	/* should clear PCH hotplug event before clear CPU irq */  	I915_WRITE(SDEIIR, pch_iir); @@ -1376,7 +1368,8 @@ static void i915_pageflip_stall_check(struct drm_device *dev, int pipe)  	obj = work->pending_flip_obj;  	if (INTEL_INFO(dev)->gen >= 4) {  		int dspsurf = DSPSURF(intel_crtc->plane); -		stall_detected = I915_READ(dspsurf) == obj->gtt_offset; +		stall_detected = I915_HI_DISPBASE(I915_READ(dspsurf)) == +					obj->gtt_offset;  	} else {  		int dspaddr = DSPADDR(intel_crtc->plane);  		stall_detected = I915_READ(dspaddr) == (obj->gtt_offset + @@ -1875,6 +1868,36 @@ static bool kick_ring(struct intel_ring_buffer *ring)  	return false;  } +static bool i915_hangcheck_hung(struct drm_device *dev) +{ +	drm_i915_private_t *dev_priv = dev->dev_private; + +	if (dev_priv->hangcheck_count++ > 1) { +		DRM_ERROR("Hangcheck timer elapsed... GPU hung\n"); +		i915_handle_error(dev, true); + +		if (!IS_GEN2(dev)) { +			/* Is the chip hanging on a WAIT_FOR_EVENT? +			 * If so we can simply poke the RB_WAIT bit +			 * and break the hang. This should work on +			 * all but the second generation chipsets. +			 */ +			if (kick_ring(&dev_priv->ring[RCS])) +				return false; + +			if (HAS_BSD(dev) && kick_ring(&dev_priv->ring[VCS])) +				return false; + +			if (HAS_BLT(dev) && kick_ring(&dev_priv->ring[BCS])) +				return false; +		} + +		return true; +	} + +	return false; +} +  /**   * This is called when the chip hasn't reported back with completed   * batchbuffers in a long time. The first time this is called we simply record @@ -1895,9 +1918,14 @@ void i915_hangcheck_elapsed(unsigned long data)  	if (i915_hangcheck_ring_idle(&dev_priv->ring[RCS], &err) &&  	    i915_hangcheck_ring_idle(&dev_priv->ring[VCS], &err) &&  	    i915_hangcheck_ring_idle(&dev_priv->ring[BCS], &err)) { -		dev_priv->hangcheck_count = 0; -		if (err) +		if (err) { +			if (i915_hangcheck_hung(dev)) +				return; +  			goto repeat; +		} + +		dev_priv->hangcheck_count = 0;  		return;  	} @@ -1919,30 +1947,8 @@ void i915_hangcheck_elapsed(unsigned long data)  	    dev_priv->last_acthd_blt == acthd_blt &&  	    dev_priv->last_instdone == instdone &&  	    dev_priv->last_instdone1 == instdone1) { -		if (dev_priv->hangcheck_count++ > 1) { -			DRM_ERROR("Hangcheck timer elapsed... GPU hung\n"); -			i915_handle_error(dev, true); - -			if (!IS_GEN2(dev)) { -				/* Is the chip hanging on a WAIT_FOR_EVENT? -				 * If so we can simply poke the RB_WAIT bit -				 * and break the hang. This should work on -				 * all but the second generation chipsets. -				 */ -				if (kick_ring(&dev_priv->ring[RCS])) -					goto repeat; - -				if (HAS_BSD(dev) && -				    kick_ring(&dev_priv->ring[VCS])) -					goto repeat; - -				if (HAS_BLT(dev) && -				    kick_ring(&dev_priv->ring[BCS])) -					goto repeat; -			} - +		if (i915_hangcheck_hung(dev))  			return; -		}  	} else {  		dev_priv->hangcheck_count = 0;  |