summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2015-07-08 17:41:56 -0700
committerEric Anholt <eric@anholt.net>2015-07-17 12:12:56 -0700
commit7419dc13de9825228d594148d14175c3a58a5d75 (patch)
tree3c08694dc54731e08d2c39bd3dbdf98b686c59cc
parent7f09bbd2ea2ea6892678f136f2c920f95a9f6303 (diff)
downloadlinux-7419dc13de9825228d594148d14175c3a58a5d75.tar.gz
drm/vc4: Add an interrupt handler for the HVS to log errors.
This should help in modesetting debugging, for example if we were to program a PV width/height that didn't match the scaler. Signed-off-by: Eric Anholt <eric@anholt.net>
-rw-r--r--drivers/gpu/drm/vc4/vc4_hvs.c56
-rw-r--r--drivers/gpu/drm/vc4/vc4_regs.h95
2 files changed, 151 insertions, 0 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 4ba37162f39c..6d00440b3e26 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -68,6 +68,34 @@ vc4_hvs_dump_state(struct drm_device *dev)
}
}
+static irqreturn_t vc4_hvs_irq_handler(int irq, void *data)
+{
+ struct vc4_dev *vc4 = data;
+ u32 stat = HVS_READ(SCALER_DISPSTAT);
+ u32 irq_bits = stat & (SCALER_DISPSTAT_IRQSCL |
+ SCALER_DISPSTAT_IRQDISP0 |
+ SCALER_DISPSTAT_IRQDISP1 |
+ SCALER_DISPSTAT_IRQDISP2 |
+ SCALER_DISPSTAT_IRQSLVWR |
+ SCALER_DISPSTAT_IRQSLVRD);
+ irqreturn_t ret = IRQ_NONE;
+
+ if (irq_bits != 0) {
+ DRM_ERROR("HVS reported error: SCALER_DISPSTAT = 0x%08x\n",
+ stat);
+
+ /* Mask out whichever of the interrupt enable generated our
+ * interrupt, so that the error only appears once for this
+ * type of error
+ */
+ HVS_WRITE(SCALER_DISPCTRL,
+ HVS_READ(SCALER_DISPCTRL) & ~irq_bits);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
#ifdef CONFIG_DEBUG_FS
int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused)
{
@@ -92,6 +120,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
struct drm_device *drm = dev_get_drvdata(master);
struct vc4_dev *vc4 = drm->dev_private;
struct vc4_hvs *hvs = NULL;
+ int ret;
hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);
if (!hvs)
@@ -106,6 +135,30 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
hvs->dlist = hvs->regs + SCALER_DLIST_START;
vc4->hvs = hvs;
+
+ /* Disable any interrupts set by the firmware. */
+ HVS_WRITE(SCALER_DISPCTRL, SCALER_DISPCTRL_ENABLE);
+ /* Clear any previous interrupts. */
+ HVS_WRITE(SCALER_DISPSTAT, SCALER_DISPSTAT_IRQSCL);
+
+ ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+ vc4_hvs_irq_handler, 0, "vc4 hvs", vc4);
+ if (ret)
+ return ret;
+
+ /* Turn on a bunch of interrupts for catching HW setup errors. */
+ HVS_WRITE(SCALER_DISPCTRL,
+ SCALER_DISPCTRL_ENABLE |
+ SCALER_DISPCTRL_DSP0EISLUR |
+ SCALER_DISPCTRL_DSP1EISLUR |
+ SCALER_DISPCTRL_DSP2EISLUR |
+ SCALER_DISPCTRL_SLVRDEIRQ |
+ SCALER_DISPCTRL_SLVWREIRQ |
+ SCALER_DISPCTRL_DMAEIRQ |
+ SCALER_DISPCTRL_DISP0EIRQ |
+ SCALER_DISPCTRL_DISP1EIRQ |
+ SCALER_DISPCTRL_DISP2EIRQ);
+
return 0;
}
@@ -115,6 +168,9 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master,
struct drm_device *drm = dev_get_drvdata(master);
struct vc4_dev *vc4 = drm->dev_private;
+ HVS_WRITE(SCALER_DISPCTRL, SCALER_DISPCTRL_ENABLE);
+ HVS_WRITE(SCALER_DISPSTAT, SCALER_DISPSTAT_IRQSCL);
+
vc4->hvs = NULL;
}
diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h
index ff6197cde4f4..2282804ae101 100644
--- a/drivers/gpu/drm/vc4/vc4_regs.h
+++ b/drivers/gpu/drm/vc4/vc4_regs.h
@@ -233,8 +233,103 @@
#define SCALER_DISPCTRL 0x00000000
/* Global register for clock gating the HVS */
# define SCALER_DISPCTRL_ENABLE (1 << 31)
+# define SCALER_DISPCTRL_DSP2EISLUR (1 << 15)
+# define SCALER_DISPCTRL_DSP1EISLUR (1 << 14)
+/*
+ * Enables Display 0 short line and underrun contribution to
+ * SCALER_DISPSTAT_IRQDISP0. Note that short frame contributions are
+ * always enabled.
+ */
+# define SCALER_DISPCTRL_DSP0EISLUR (1 << 13)
+# define SCALER_DISPCTRL_DSP2EIEOLN (1 << 12)
+# define SCALER_DISPCTRL_DSP2EIEOF (1 << 11)
+# define SCALER_DISPCTRL_DSP1EIEOLN (1 << 10)
+# define SCALER_DISPCTRL_DSP1EIEOF (1 << 9)
+/*
+ * Enables Display 0 end-of-line-N contribution to
+ * SCALER_DISPSTAT_IRQDISP0
+ */
+# define SCALER_DISPCTRL_DSP0EIEOLN (1 << 8)
+/* Enables Display 0 EOF contribution to SCALER_DISPSTAT_IRQDISP0 */
+# define SCALER_DISPCTRL_DSP0EIEOF (1 << 7)
+
+# define SCALER_DISPCTRL_SLVRDEIRQ (1 << 6)
+# define SCALER_DISPCTRL_SLVWREIRQ (1 << 5)
+# define SCALER_DISPCTRL_DMAEIRQ (1 << 4)
+# define SCALER_DISPCTRL_DISP2EIRQ (1 << 3)
+# define SCALER_DISPCTRL_DISP1EIRQ (1 << 2)
+/*
+ * Enables interrupt generation on the enabled EOF/EOLN/EISLUR
+ * bits and short frames..
+ */
+# define SCALER_DISPCTRL_DISP0EIRQ (1 << 1)
+/* Enables interrupt generation on scaler profiler interrupt. */
+# define SCALER_DISPCTRL_SCLEIRQ (1 << 0)
#define SCALER_DISPSTAT 0x00000004
+# define SCALER_DISPSTAT_COBLOW2 (1 << 29)
+# define SCALER_DISPSTAT_EOLN2 (1 << 28)
+# define SCALER_DISPSTAT_ESFRAME2 (1 << 27)
+# define SCALER_DISPSTAT_ESLINE2 (1 << 26)
+# define SCALER_DISPSTAT_EUFLOW2 (1 << 25)
+# define SCALER_DISPSTAT_EOF2 (1 << 24)
+
+# define SCALER_DISPSTAT_COBLOW1 (1 << 21)
+# define SCALER_DISPSTAT_EOLN1 (1 << 20)
+# define SCALER_DISPSTAT_ESFRAME1 (1 << 19)
+# define SCALER_DISPSTAT_ESLINE1 (1 << 18)
+# define SCALER_DISPSTAT_EUFLOW1 (1 << 17)
+# define SCALER_DISPSTAT_EOF1 (1 << 16)
+
+# define SCALER_DISPSTAT_RESP_MASK VC4_MASK(15, 14)
+# define SCALER_DISPSTAT_RESP_SHIFT 14
+# define SCALER_DISPSTAT_RESP_OKAY 0
+# define SCALER_DISPSTAT_RESP_EXOKAY 1
+# define SCALER_DISPSTAT_RESP_SLVERR 2
+# define SCALER_DISPSTAT_RESP_DECERR 3
+
+# define SCALER_DISPSTAT_COBLOW0 (1 << 13)
+/* Set when the DISPEOLN line is done compositing. */
+# define SCALER_DISPSTAT_EOLN0 (1 << 12)
+/*
+ * Set when VSTART is seen but there are still pixels in the current
+ * output line.
+ */
+# define SCALER_DISPSTAT_ESFRAME0 (1 << 11)
+/*
+ * Set when HSTART is seen but there are still pixels in the current
+ * output line.
+ */
+# define SCALER_DISPSTAT_ESLINE0 (1 << 10)
+/*
+ * Set when the the downstream tries to read from the display FIFO
+ * while it's empty.
+ */
+# define SCALER_DISPSTAT_EUFLOW0 (1 << 9)
+/* Set when the display mode changes from RUN to EOF */
+# define SCALER_DISPSTAT_EOF0 (1 << 8)
+
+/* Set on AXI invalid DMA ID error. */
+# define SCALER_DISPSTAT_DMA_ERROR (1 << 7)
+/* Set on AXI slave read decode error */
+# define SCALER_DISPSTAT_IRQSLVRD (1 << 6)
+/* Set on AXI slave write decode error */
+# define SCALER_DISPSTAT_IRQSLVWR (1 << 5)
+/*
+ * Set when SCALER_DISPSTAT_DMA_ERROR is set, or
+ * SCALER_DISPSTAT_RESP_ERROR is not SCALER_DISPSTAT_RESP_OKAY.
+ */
+# define SCALER_DISPSTAT_IRQDMA (1 << 4)
+# define SCALER_DISPSTAT_IRQDISP2 (1 << 3)
+# define SCALER_DISPSTAT_IRQDISP1 (1 << 2)
+/*
+ * Set when any of the EOF/EOLN/ESFRAME/ESLINE bits are set and their
+ * corresponding interrupt bit is enabled in DISPCTRL.
+ */
+# define SCALER_DISPSTAT_IRQDISP0 (1 << 1)
+/* On read, the profiler interrupt. On write, clear *all* interrupt bits. */
+# define SCALER_DISPSTAT_IRQSCL (1 << 0)
+
#define SCALER_DISPID 0x00000008
#define SCALER_DISPECTRL 0x0000000c
#define SCALER_DISPPROF 0x00000010