linux/drivers/gpu/drm/vc4/vc4_irq.c
<<
>>
Prefs
   1/*
   2 * Copyright © 2014 Broadcom
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice (including the next
  12 * paragraph) shall be included in all copies or substantial portions of the
  13 * Software.
  14 *
  15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21 * IN THE SOFTWARE.
  22 */
  23
  24/**
  25 * DOC: Interrupt management for the V3D engine
  26 *
  27 * We have an interrupt status register (V3D_INTCTL) which reports
  28 * interrupts, and where writing 1 bits clears those interrupts.
  29 * There are also a pair of interrupt registers
  30 * (V3D_INTENA/V3D_INTDIS) where writing a 1 to their bits enables or
  31 * disables that specific interrupt, and 0s written are ignored
  32 * (reading either one returns the set of enabled interrupts).
  33 *
  34 * When we take a binning flush done interrupt, we need to submit the
  35 * next frame for binning and move the finished frame to the render
  36 * thread.
  37 *
  38 * When we take a render frame interrupt, we need to wake the
  39 * processes waiting for some frame to be done, and get the next frame
  40 * submitted ASAP (so the hardware doesn't sit idle when there's work
  41 * to do).
  42 *
  43 * When we take the binner out of memory interrupt, we need to
  44 * allocate some new memory and pass it to the binner so that the
  45 * current job can make progress.
  46 */
  47
  48#include "vc4_drv.h"
  49#include "vc4_regs.h"
  50
  51#define V3D_DRIVER_IRQS (V3D_INT_OUTOMEM | \
  52                         V3D_INT_FLDONE | \
  53                         V3D_INT_FRDONE)
  54
  55DECLARE_WAIT_QUEUE_HEAD(render_wait);
  56
  57static void
  58vc4_overflow_mem_work(struct work_struct *work)
  59{
  60        struct vc4_dev *vc4 =
  61                container_of(work, struct vc4_dev, overflow_mem_work);
  62        struct vc4_bo *bo = vc4->bin_bo;
  63        int bin_bo_slot;
  64        struct vc4_exec_info *exec;
  65        unsigned long irqflags;
  66
  67        bin_bo_slot = vc4_v3d_get_bin_slot(vc4);
  68        if (bin_bo_slot < 0) {
  69                DRM_ERROR("Couldn't allocate binner overflow mem\n");
  70                return;
  71        }
  72
  73        spin_lock_irqsave(&vc4->job_lock, irqflags);
  74
  75        if (vc4->bin_alloc_overflow) {
  76                /* If we had overflow memory allocated previously,
  77                 * then that chunk will free when the current bin job
  78                 * is done.  If we don't have a bin job running, then
  79                 * the chunk will be done whenever the list of render
  80                 * jobs has drained.
  81                 */
  82                exec = vc4_first_bin_job(vc4);
  83                if (!exec)
  84                        exec = vc4_last_render_job(vc4);
  85                if (exec) {
  86                        exec->bin_slots |= vc4->bin_alloc_overflow;
  87                } else {
  88                        /* There's nothing queued in the hardware, so
  89                         * the old slot is free immediately.
  90                         */
  91                        vc4->bin_alloc_used &= ~vc4->bin_alloc_overflow;
  92                }
  93        }
  94        vc4->bin_alloc_overflow = BIT(bin_bo_slot);
  95
  96        V3D_WRITE(V3D_BPOA, bo->base.paddr + bin_bo_slot * vc4->bin_alloc_size);
  97        V3D_WRITE(V3D_BPOS, bo->base.base.size);
  98        V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
  99        V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
 100        spin_unlock_irqrestore(&vc4->job_lock, irqflags);
 101}
 102
 103static void
 104vc4_irq_finish_bin_job(struct drm_device *dev)
 105{
 106        struct vc4_dev *vc4 = to_vc4_dev(dev);
 107        struct vc4_exec_info *next, *exec = vc4_first_bin_job(vc4);
 108
 109        if (!exec)
 110                return;
 111
 112        vc4_move_job_to_render(dev, exec);
 113        next = vc4_first_bin_job(vc4);
 114
 115        /* Only submit the next job in the bin list if it matches the perfmon
 116         * attached to the one that just finished (or if both jobs don't have
 117         * perfmon attached to them).
 118         */
 119        if (next && next->perfmon == exec->perfmon)
 120                vc4_submit_next_bin_job(dev);
 121}
 122
 123static void
 124vc4_cancel_bin_job(struct drm_device *dev)
 125{
 126        struct vc4_dev *vc4 = to_vc4_dev(dev);
 127        struct vc4_exec_info *exec = vc4_first_bin_job(vc4);
 128
 129        if (!exec)
 130                return;
 131
 132        /* Stop the perfmon so that the next bin job can be started. */
 133        if (exec->perfmon)
 134                vc4_perfmon_stop(vc4, exec->perfmon, false);
 135
 136        list_move_tail(&exec->head, &vc4->bin_job_list);
 137        vc4_submit_next_bin_job(dev);
 138}
 139
 140static void
 141vc4_irq_finish_render_job(struct drm_device *dev)
 142{
 143        struct vc4_dev *vc4 = to_vc4_dev(dev);
 144        struct vc4_exec_info *exec = vc4_first_render_job(vc4);
 145        struct vc4_exec_info *nextbin, *nextrender;
 146
 147        if (!exec)
 148                return;
 149
 150        vc4->finished_seqno++;
 151        list_move_tail(&exec->head, &vc4->job_done_list);
 152
 153        nextbin = vc4_first_bin_job(vc4);
 154        nextrender = vc4_first_render_job(vc4);
 155
 156        /* Only stop the perfmon if following jobs in the queue don't expect it
 157         * to be enabled.
 158         */
 159        if (exec->perfmon && !nextrender &&
 160            (!nextbin || nextbin->perfmon != exec->perfmon))
 161                vc4_perfmon_stop(vc4, exec->perfmon, true);
 162
 163        /* If there's a render job waiting, start it. If this is not the case
 164         * we may have to unblock the binner if it's been stalled because of
 165         * perfmon (this can be checked by comparing the perfmon attached to
 166         * the finished renderjob to the one attached to the next bin job: if
 167         * they don't match, this means the binner is stalled and should be
 168         * restarted).
 169         */
 170        if (nextrender)
 171                vc4_submit_next_render_job(dev);
 172        else if (nextbin && nextbin->perfmon != exec->perfmon)
 173                vc4_submit_next_bin_job(dev);
 174
 175        if (exec->fence) {
 176                dma_fence_signal_locked(exec->fence);
 177                dma_fence_put(exec->fence);
 178                exec->fence = NULL;
 179        }
 180
 181        wake_up_all(&vc4->job_wait_queue);
 182        schedule_work(&vc4->job_done_work);
 183}
 184
 185irqreturn_t
 186vc4_irq(int irq, void *arg)
 187{
 188        struct drm_device *dev = arg;
 189        struct vc4_dev *vc4 = to_vc4_dev(dev);
 190        uint32_t intctl;
 191        irqreturn_t status = IRQ_NONE;
 192
 193        barrier();
 194        intctl = V3D_READ(V3D_INTCTL);
 195
 196        /* Acknowledge the interrupts we're handling here. The binner
 197         * last flush / render frame done interrupt will be cleared,
 198         * while OUTOMEM will stay high until the underlying cause is
 199         * cleared.
 200         */
 201        V3D_WRITE(V3D_INTCTL, intctl);
 202
 203        if (intctl & V3D_INT_OUTOMEM) {
 204                /* Disable OUTOMEM until the work is done. */
 205                V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM);
 206                schedule_work(&vc4->overflow_mem_work);
 207                status = IRQ_HANDLED;
 208        }
 209
 210        if (intctl & V3D_INT_FLDONE) {
 211                spin_lock(&vc4->job_lock);
 212                vc4_irq_finish_bin_job(dev);
 213                spin_unlock(&vc4->job_lock);
 214                status = IRQ_HANDLED;
 215        }
 216
 217        if (intctl & V3D_INT_FRDONE) {
 218                spin_lock(&vc4->job_lock);
 219                vc4_irq_finish_render_job(dev);
 220                spin_unlock(&vc4->job_lock);
 221                status = IRQ_HANDLED;
 222        }
 223
 224        return status;
 225}
 226
 227void
 228vc4_irq_preinstall(struct drm_device *dev)
 229{
 230        struct vc4_dev *vc4 = to_vc4_dev(dev);
 231
 232        init_waitqueue_head(&vc4->job_wait_queue);
 233        INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work);
 234
 235        /* Clear any pending interrupts someone might have left around
 236         * for us.
 237         */
 238        V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
 239}
 240
 241int
 242vc4_irq_postinstall(struct drm_device *dev)
 243{
 244        struct vc4_dev *vc4 = to_vc4_dev(dev);
 245
 246        /* Enable both the render done and out of memory interrupts. */
 247        V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS);
 248
 249        return 0;
 250}
 251
 252void
 253vc4_irq_uninstall(struct drm_device *dev)
 254{
 255        struct vc4_dev *vc4 = to_vc4_dev(dev);
 256
 257        /* Disable sending interrupts for our driver's IRQs. */
 258        V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS);
 259
 260        /* Clear any pending interrupts we might have left. */
 261        V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
 262
 263        /* Finish any interrupt handler still in flight. */
 264        disable_irq(dev->irq);
 265
 266        cancel_work_sync(&vc4->overflow_mem_work);
 267}
 268
 269/** Reinitializes interrupt registers when a GPU reset is performed. */
 270void vc4_irq_reset(struct drm_device *dev)
 271{
 272        struct vc4_dev *vc4 = to_vc4_dev(dev);
 273        unsigned long irqflags;
 274
 275        /* Acknowledge any stale IRQs. */
 276        V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
 277
 278        /*
 279         * Turn all our interrupts on.  Binner out of memory is the
 280         * only one we expect to trigger at this point, since we've
 281         * just come from poweron and haven't supplied any overflow
 282         * memory yet.
 283         */
 284        V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS);
 285
 286        spin_lock_irqsave(&vc4->job_lock, irqflags);
 287        vc4_cancel_bin_job(dev);
 288        vc4_irq_finish_render_job(dev);
 289        spin_unlock_irqrestore(&vc4->job_lock, irqflags);
 290}
 291