summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2015-06-16 16:18:38 -0700
committerEric Anholt <eric@anholt.net>2015-06-17 17:20:19 -0700
commitfefaa64b6faf8bd7bac610758cefe3c405962fe1 (patch)
treeafccec90b8e3ca17b9fa8ec11be7987d2364a4bd
parent8d102f7a302db39539ce780875f88ea62270818b (diff)
downloadlinux-fefaa64b6faf8bd7bac610758cefe3c405962fe1.tar.gz
drm/vc4: Clean up V3D interrupt handling, and document how it works.
My surprise about having to set INTENA for OUTOMEM when I hadn't cleared anything in it now makes sense, having found that there is just one set of underlying enable bits behind the INTENA/INTDIS registers. Signed-off-by: Eric Anholt <eric@anholt.net>
-rw-r--r--drivers/gpu/drm/vc4/vc4_irq.c50
1 files changed, 37 insertions, 13 deletions
diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c
index b5a1a273f83f..2d0b91a9bd93 100644
--- a/drivers/gpu/drm/vc4/vc4_irq.c
+++ b/drivers/gpu/drm/vc4/vc4_irq.c
@@ -21,6 +21,25 @@
* IN THE SOFTWARE.
*/
+/** DOC: Interrupt management for the V3D engine.
+ *
+ * We have an interrupt status register (V3D_INTCTL) which reports
+ * interrupts, and where writing 1 bits clears those interrupts.
+ * There are also a pair of interrupt registers
+ * (V3D_INTENA/V3D_INTDIS) where writing a 1 to their bits enables or
+ * disables that specific interrupt, and 0s written are ignored
+ * (reading either one returns the set of enabled interrupts).
+ *
+ * When we take a render frame interrupt, we need to wake the
+ * processes waiting for some frame to be done, and get the next frame
+ * submitted ASAP (so the hardware doesn't sit idle when there's work
+ * to do).
+ *
+ * When we take the binner out of memory interrupt, we need to
+ * allocate some new memory and pass it to the binner so that the
+ * current job can make progress.
+ */
+
#include "vc4_drv.h"
#include "vc4_regs.h"
@@ -77,9 +96,8 @@ vc4_overflow_mem_work(struct work_struct *work)
V3D_WRITE(V3D_BPOA, bo->base.paddr);
V3D_WRITE(V3D_BPOS, bo->base.base.size);
- V3D_WRITE(V3D_INTDIS, 0);
- V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS);
V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
+ V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
}
static void
@@ -109,9 +127,15 @@ vc4_irq(int irq, void *arg)
barrier();
intctl = V3D_READ(V3D_INTCTL);
+
+ /* Acknowledge the interrupts we're handling here. The render
+ * frame done interrupt will be cleared, while OUTOMEM will
+ * stay high until the underlying cause is cleared.
+ */
V3D_WRITE(V3D_INTCTL, intctl);
if (intctl & V3D_INT_OUTOMEM) {
+ /* Disable OUTOMEM until the work is done. */
V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM);
schedule_work(&vc4->overflow_mem_work);
status = IRQ_HANDLED;
@@ -146,16 +170,9 @@ vc4_irq_postinstall(struct drm_device *dev)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
- /* Enable both the bin and render done interrupts, as well as
- * out of memory. Eventually, we'll have the bin use internal
- * semaphores with render to sync between the two, but for now
- * we're driving that from the ARM.
- */
+ /* Enable both the render done and out of memory interrupts. */
V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS);
- /* No interrupts disabled. */
- V3D_WRITE(V3D_INTDIS, 0);
-
return 0;
}
@@ -164,8 +181,8 @@ vc4_irq_uninstall(struct drm_device *dev)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
- V3D_WRITE(V3D_INTENA, 0);
- V3D_WRITE(V3D_INTDIS, 0);
+ /* Disable sending interrupts for our driver's IRQs. */
+ V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS);
/* Clear any pending interrupts we might have left. */
V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
@@ -179,8 +196,15 @@ void vc4_irq_reset(struct drm_device *dev)
struct vc4_dev *vc4 = to_vc4_dev(dev);
unsigned long irqflags;
+ /* Acknowledge any stale IRQs. */
V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
- V3D_WRITE(V3D_INTDIS, 0);
+
+ /*
+ * Turn all our interrupts on. Binner out of memory is the
+ * only one we expect to trigger at this point, since we've
+ * just come from poweron and haven't supplied any overflow
+ * memory yet.
+ */
V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS);
spin_lock_irqsave(&vc4->job_lock, irqflags);