drm/i915: report Gen5+ CPU and PCH FIFO underruns

In this commit we enable both CPU and PCH FIFO underrun reporting and
start reporting them. We follow a few rules:
  - after we receive one of these errors, we mask the interrupt, so
    we won't get an "interrupt storm" and we also won't flood dmesg;
  - at each mode set we enable the interrupts again, so we'll see each
    message at most once per mode set;
  - in the specific places where we need to ignore the errors, we
    completely mask the interrupts.

The downside of this patch is that since we're completely disabling
(masking) the interrupts instead of just not printing error messages,
we will mask more than just what we want on IVB/HSW CPU interrupts
(due to GEN7_ERR_INT) and on CPT/PPT/LPT PCHs (due to SERR_INT). So
when we decide to mask PCH FIFO underruns for pipe A on CPT, we'll
also be masking PCH FIFO underruns for pipe B, because both are
reported by SERR_INT, which has to be either completely enabled or
completely disabled (in othe words, there's no way to disable/enable
specific bits of GEN7_ERR_INT and SERR_INT).

V2: Rename some functions and variables, downgrade messages to
DRM_DEBUG_DRIVER and rebase.

Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
This commit is contained in:
Paulo Zanoni 2013-04-12 17:57:57 -03:00 committed by Daniel Vetter
parent 89b667f86a
commit 8664281b64
4 changed files with 342 additions and 11 deletions

View File

@ -112,6 +112,213 @@ ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
}
}
static bool ivb_can_enable_err_int(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *crtc;
enum pipe pipe;
for_each_pipe(pipe) {
crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
if (crtc->cpu_fifo_underrun_disabled)
return false;
}
return true;
}
static bool cpt_can_enable_serr_int(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
enum pipe pipe;
struct intel_crtc *crtc;
for_each_pipe(pipe) {
crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
if (crtc->pch_fifo_underrun_disabled)
return false;
}
return true;
}
static void ironlake_set_fifo_underrun_reporting(struct drm_device *dev,
enum pipe pipe, bool enable)
{
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t bit = (pipe == PIPE_A) ? DE_PIPEA_FIFO_UNDERRUN :
DE_PIPEB_FIFO_UNDERRUN;
if (enable)
ironlake_enable_display_irq(dev_priv, bit);
else
ironlake_disable_display_irq(dev_priv, bit);
}
static void ivybridge_set_fifo_underrun_reporting(struct drm_device *dev,
bool enable)
{
struct drm_i915_private *dev_priv = dev->dev_private;
if (enable) {
if (!ivb_can_enable_err_int(dev))
return;
I915_WRITE(GEN7_ERR_INT, ERR_INT_FIFO_UNDERRUN_A |
ERR_INT_FIFO_UNDERRUN_B |
ERR_INT_FIFO_UNDERRUN_C);
ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
} else {
ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
}
}
static void ibx_set_fifo_underrun_reporting(struct intel_crtc *crtc,
bool enable)
{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t bit = (crtc->pipe == PIPE_A) ? SDE_TRANSA_FIFO_UNDER :
SDE_TRANSB_FIFO_UNDER;
if (enable)
I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~bit);
else
I915_WRITE(SDEIMR, I915_READ(SDEIMR) | bit);
POSTING_READ(SDEIMR);
}
static void cpt_set_fifo_underrun_reporting(struct drm_device *dev,
enum transcoder pch_transcoder,
bool enable)
{
struct drm_i915_private *dev_priv = dev->dev_private;
if (enable) {
if (!cpt_can_enable_serr_int(dev))
return;
I915_WRITE(SERR_INT, SERR_INT_TRANS_A_FIFO_UNDERRUN |
SERR_INT_TRANS_B_FIFO_UNDERRUN |
SERR_INT_TRANS_C_FIFO_UNDERRUN);
I915_WRITE(SDEIMR, I915_READ(SDEIMR) & ~SDE_ERROR_CPT);
} else {
I915_WRITE(SDEIMR, I915_READ(SDEIMR) | SDE_ERROR_CPT);
}
POSTING_READ(SDEIMR);
}
/**
* intel_set_cpu_fifo_underrun_reporting - enable/disable FIFO underrun messages
* @dev: drm device
* @pipe: pipe
* @enable: true if we want to report FIFO underrun errors, false otherwise
*
* This function makes us disable or enable CPU fifo underruns for a specific
* pipe. Notice that on some Gens (e.g. IVB, HSW), disabling FIFO underrun
* reporting for one pipe may also disable all the other CPU error interruts for
* the other pipes, due to the fact that there's just one interrupt mask/enable
* bit for all the pipes.
*
* Returns the previous state of underrun reporting.
*/
bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
enum pipe pipe, bool enable)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
unsigned long flags;
bool ret;
spin_lock_irqsave(&dev_priv->irq_lock, flags);
ret = !intel_crtc->cpu_fifo_underrun_disabled;
if (enable == ret)
goto done;
intel_crtc->cpu_fifo_underrun_disabled = !enable;
if (IS_GEN5(dev) || IS_GEN6(dev))
ironlake_set_fifo_underrun_reporting(dev, pipe, enable);
else if (IS_GEN7(dev))
ivybridge_set_fifo_underrun_reporting(dev, enable);
done:
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
return ret;
}
/**
* intel_set_pch_fifo_underrun_reporting - enable/disable FIFO underrun messages
* @dev: drm device
* @pch_transcoder: the PCH transcoder (same as pipe on IVB and older)
* @enable: true if we want to report FIFO underrun errors, false otherwise
*
* This function makes us disable or enable PCH fifo underruns for a specific
* PCH transcoder. Notice that on some PCHs (e.g. CPT/PPT), disabling FIFO
* underrun reporting for one transcoder may also disable all the other PCH
* error interruts for the other transcoders, due to the fact that there's just
* one interrupt mask/enable bit for all the transcoders.
*
* Returns the previous state of underrun reporting.
*/
bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
enum transcoder pch_transcoder,
bool enable)
{
struct drm_i915_private *dev_priv = dev->dev_private;
enum pipe p;
struct drm_crtc *crtc;
struct intel_crtc *intel_crtc;
unsigned long flags;
bool ret;
if (HAS_PCH_LPT(dev)) {
crtc = NULL;
for_each_pipe(p) {
struct drm_crtc *c = dev_priv->pipe_to_crtc_mapping[p];
if (intel_pipe_has_type(c, INTEL_OUTPUT_ANALOG)) {
crtc = c;
break;
}
}
if (!crtc) {
DRM_ERROR("PCH FIFO underrun, but no CRTC using the PCH found\n");
return false;
}
} else {
crtc = dev_priv->pipe_to_crtc_mapping[pch_transcoder];
}
intel_crtc = to_intel_crtc(crtc);
spin_lock_irqsave(&dev_priv->irq_lock, flags);
ret = !intel_crtc->pch_fifo_underrun_disabled;
if (enable == ret)
goto done;
intel_crtc->pch_fifo_underrun_disabled = !enable;
if (HAS_PCH_IBX(dev))
ibx_set_fifo_underrun_reporting(intel_crtc, enable);
else
cpt_set_fifo_underrun_reporting(dev, pch_transcoder, enable);
done:
spin_unlock_irqrestore(&dev_priv->irq_lock, flags);
return ret;
}
void
i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
{
@ -800,10 +1007,58 @@ static void ibx_irq_handler(struct drm_device *dev, u32 pch_iir)
if (pch_iir & (SDE_TRANSB_CRC_ERR | SDE_TRANSA_CRC_ERR))
DRM_DEBUG_DRIVER("PCH transcoder CRC error interrupt\n");
if (pch_iir & SDE_TRANSB_FIFO_UNDER)
DRM_DEBUG_DRIVER("PCH transcoder B underrun interrupt\n");
if (pch_iir & SDE_TRANSA_FIFO_UNDER)
DRM_DEBUG_DRIVER("PCH transcoder A underrun interrupt\n");
if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A,
false))
DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n");
if (pch_iir & SDE_TRANSB_FIFO_UNDER)
if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B,
false))
DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n");
}
static void ivb_err_int_handler(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 err_int = I915_READ(GEN7_ERR_INT);
if (err_int & ERR_INT_FIFO_UNDERRUN_A)
if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false))
DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n");
if (err_int & ERR_INT_FIFO_UNDERRUN_B)
if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false))
DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n");
if (err_int & ERR_INT_FIFO_UNDERRUN_C)
if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_C, false))
DRM_DEBUG_DRIVER("Pipe C FIFO underrun\n");
I915_WRITE(GEN7_ERR_INT, err_int);
}
static void cpt_serr_int_handler(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 serr_int = I915_READ(SERR_INT);
if (serr_int & SERR_INT_TRANS_A_FIFO_UNDERRUN)
if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A,
false))
DRM_DEBUG_DRIVER("PCH transcoder A FIFO underrun\n");
if (serr_int & SERR_INT_TRANS_B_FIFO_UNDERRUN)
if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_B,
false))
DRM_DEBUG_DRIVER("PCH transcoder B FIFO underrun\n");
if (serr_int & SERR_INT_TRANS_C_FIFO_UNDERRUN)
if (intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_C,
false))
DRM_DEBUG_DRIVER("PCH transcoder C FIFO underrun\n");
I915_WRITE(SERR_INT, serr_int);
}
static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
@ -841,6 +1096,9 @@ static void cpt_irq_handler(struct drm_device *dev, u32 pch_iir)
DRM_DEBUG_DRIVER(" pipe %c FDI IIR: 0x%08x\n",
pipe_name(pipe),
I915_READ(FDI_RX_IIR(pipe)));
if (pch_iir & SDE_ERROR_CPT)
cpt_serr_int_handler(dev);
}
static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
@ -853,6 +1111,14 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
atomic_inc(&dev_priv->irq_received);
/* We get interrupts on unclaimed registers, so check for this before we
* do any I915_{READ,WRITE}. */
if (IS_HASWELL(dev) &&
(I915_READ_NOTRACE(FPGA_DBG) & FPGA_DBG_RM_NOCLAIM)) {
DRM_ERROR("Unclaimed register before interrupt\n");
I915_WRITE_NOTRACE(FPGA_DBG, FPGA_DBG_RM_NOCLAIM);
}
/* disable master interrupt before clearing iir */
de_ier = I915_READ(DEIER);
I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
@ -868,6 +1134,12 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
POSTING_READ(SDEIER);
}
/* On Haswell, also mask ERR_INT because we don't want to risk
* generating "unclaimed register" interrupts from inside the interrupt
* handler. */
if (IS_HASWELL(dev))
ironlake_disable_display_irq(dev_priv, DE_ERR_INT_IVB);
gt_iir = I915_READ(GTIIR);
if (gt_iir) {
snb_gt_irq_handler(dev, dev_priv, gt_iir);
@ -877,6 +1149,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
de_iir = I915_READ(DEIIR);
if (de_iir) {
if (de_iir & DE_ERR_INT_IVB)
ivb_err_int_handler(dev);
if (de_iir & DE_AUX_CHANNEL_A_IVB)
dp_aux_irq_handler(dev);
@ -914,6 +1189,9 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg)
ret = IRQ_HANDLED;
}
if (IS_HASWELL(dev) && ivb_can_enable_err_int(dev))
ironlake_enable_display_irq(dev_priv, DE_ERR_INT_IVB);
I915_WRITE(DEIER, de_ier);
POSTING_READ(DEIER);
if (!HAS_PCH_NOP(dev)) {
@ -983,6 +1261,14 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg)
if (de_iir & DE_PIPEB_VBLANK)
drm_handle_vblank(dev, 1);
if (de_iir & DE_PIPEA_FIFO_UNDERRUN)
if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_A, false))
DRM_DEBUG_DRIVER("Pipe A FIFO underrun\n");
if (de_iir & DE_PIPEB_FIFO_UNDERRUN)
if (intel_set_cpu_fifo_underrun_reporting(dev, PIPE_B, false))
DRM_DEBUG_DRIVER("Pipe B FIFO underrun\n");
if (de_iir & DE_PLANEA_FLIP_DONE) {
intel_prepare_page_flip(dev, 0);
intel_finish_page_flip_plane(dev, 0);
@ -2208,10 +2494,14 @@ static void ibx_irq_postinstall(struct drm_device *dev)
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u32 mask;
if (HAS_PCH_IBX(dev))
mask = SDE_GMBUS | SDE_AUX_MASK;
else
mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
if (HAS_PCH_IBX(dev)) {
mask = SDE_GMBUS | SDE_AUX_MASK | SDE_TRANSB_FIFO_UNDER |
SDE_TRANSA_FIFO_UNDER;
} else {
mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT | SDE_ERROR_CPT;
I915_WRITE(SERR_INT, I915_READ(SERR_INT));
}
if (HAS_PCH_NOP(dev))
return;
@ -2226,7 +2516,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
/* enable kind of interrupts always enabled */
u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
DE_AUX_CHANNEL_A;
DE_AUX_CHANNEL_A | DE_PIPEB_FIFO_UNDERRUN |
DE_PIPEA_FIFO_UNDERRUN;
u32 render_irqs;
dev_priv->irq_mask = ~display_mask;
@ -2276,12 +2567,14 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
DE_PLANEC_FLIP_DONE_IVB |
DE_PLANEB_FLIP_DONE_IVB |
DE_PLANEA_FLIP_DONE_IVB |
DE_AUX_CHANNEL_A_IVB;
DE_AUX_CHANNEL_A_IVB |
DE_ERR_INT_IVB;
u32 render_irqs;
dev_priv->irq_mask = ~display_mask;
/* should always can generate irq */
I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
I915_WRITE(DEIIR, I915_READ(DEIIR));
I915_WRITE(DEIMR, dev_priv->irq_mask);
I915_WRITE(DEIER,
@ -2409,6 +2702,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
I915_WRITE(DEIMR, 0xffffffff);
I915_WRITE(DEIER, 0x0);
I915_WRITE(DEIIR, I915_READ(DEIIR));
if (IS_GEN7(dev))
I915_WRITE(GEN7_ERR_INT, I915_READ(GEN7_ERR_INT));
I915_WRITE(GTIMR, 0xffffffff);
I915_WRITE(GTIER, 0x0);
@ -2420,6 +2715,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
I915_WRITE(SDEIMR, 0xffffffff);
I915_WRITE(SDEIER, 0x0);
I915_WRITE(SDEIIR, I915_READ(SDEIIR));
if (HAS_PCH_CPT(dev) || HAS_PCH_LPT(dev))
I915_WRITE(SERR_INT, I915_READ(SERR_INT));
}
static void i8xx_irq_preinstall(struct drm_device * dev)

View File

@ -641,6 +641,9 @@
#define ERROR_GEN6 0x040a0
#define GEN7_ERR_INT 0x44040
#define ERR_INT_MMIO_UNCLAIMED (1<<13)
#define ERR_INT_FIFO_UNDERRUN_C (1<<6)
#define ERR_INT_FIFO_UNDERRUN_B (1<<3)
#define ERR_INT_FIFO_UNDERRUN_A (1<<0)
#define FPGA_DBG 0x42300
#define FPGA_DBG_RM_NOCLAIM (1<<31)
@ -3622,7 +3625,7 @@
#define DE_PIPEA_FIFO_UNDERRUN (1 << 0)
/* More Ivybridge lolz */
#define DE_ERR_DEBUG_IVB (1<<30)
#define DE_ERR_INT_IVB (1<<30)
#define DE_GSE_IVB (1<<29)
#define DE_PCH_EVENT_IVB (1<<28)
#define DE_DP_A_HOTPLUG_IVB (1<<27)
@ -3781,6 +3784,7 @@
SDE_PORTC_HOTPLUG_CPT | \
SDE_PORTB_HOTPLUG_CPT)
#define SDE_GMBUS_CPT (1 << 17)
#define SDE_ERROR_CPT (1 << 16)
#define SDE_AUDIO_CP_REQ_C_CPT (1 << 10)
#define SDE_AUDIO_CP_CHG_C_CPT (1 << 9)
#define SDE_FDI_RXC_CPT (1 << 8)
@ -3805,6 +3809,11 @@
#define SDEIIR 0xc4008
#define SDEIER 0xc400c
#define SERR_INT 0xc4040
#define SERR_INT_TRANS_C_FIFO_UNDERRUN (1<<6)
#define SERR_INT_TRANS_B_FIFO_UNDERRUN (1<<3)
#define SERR_INT_TRANS_A_FIFO_UNDERRUN (1<<0)
/* digital port hotplug */
#define PCH_PORT_HOTPLUG 0xc4030 /* SHOTPLUG_CTL */
#define PORTD_HOTPLUG_ENABLE (1 << 20)

View File

@ -3346,6 +3346,10 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
return;
intel_crtc->active = true;
intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
intel_set_pch_fifo_underrun_reporting(dev, pipe, true);
intel_update_watermarks(dev);
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
@ -3437,6 +3441,11 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
return;
intel_crtc->active = true;
intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
if (intel_crtc->config.has_pch_encoder)
intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true);
intel_update_watermarks(dev);
if (intel_crtc->config.has_pch_encoder)
@ -3523,6 +3532,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
if (dev_priv->cfb_plane == plane)
intel_disable_fbc(dev);
intel_set_pch_fifo_underrun_reporting(dev, pipe, false);
intel_disable_pipe(dev_priv, pipe);
/* Disable PF */
@ -3536,6 +3546,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
ironlake_fdi_disable(crtc);
ironlake_disable_pch_transcoder(dev_priv, pipe);
intel_set_pch_fifo_underrun_reporting(dev, pipe, true);
if (HAS_PCH_CPT(dev)) {
/* disable TRANS_DP_CTL */
@ -3602,6 +3613,8 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
if (dev_priv->cfb_plane == plane)
intel_disable_fbc(dev);
if (intel_crtc->config.has_pch_encoder)
intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, false);
intel_disable_pipe(dev_priv, pipe);
intel_ddi_disable_transcoder_func(dev_priv, cpu_transcoder);
@ -3622,6 +3635,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
if (intel_crtc->config.has_pch_encoder) {
lpt_disable_pch_transcoder(dev_priv);
intel_set_pch_fifo_underrun_reporting(dev, TRANSCODER_A, true);
intel_ddi_fdi_disable(crtc);
}

View File

@ -265,6 +265,10 @@ struct intel_crtc {
/* reset counter value when the last flip was submitted */
unsigned int reset_counter;
/* Access to these should be protected by dev_priv->irq_lock. */
bool cpu_fifo_underrun_disabled;
bool pch_fifo_underrun_disabled;
};
struct intel_plane {
@ -487,6 +491,7 @@ int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
extern void intel_attach_force_audio_property(struct drm_connector *connector);
extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector);
extern bool intel_pipe_has_type(struct drm_crtc *crtc, int type);
extern void intel_crt_init(struct drm_device *dev);
extern void intel_hdmi_init(struct drm_device *dev,
int hdmi_reg, enum port port);
@ -743,5 +748,11 @@ intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector);
extern void intel_ddi_fdi_disable(struct drm_crtc *crtc);
extern void intel_display_handle_reset(struct drm_device *dev);
extern bool intel_set_cpu_fifo_underrun_reporting(struct drm_device *dev,
enum pipe pipe,
bool enable);
extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
enum transcoder pch_transcoder,
bool enable);
#endif /* __INTEL_DRV_H__ */