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;
  63        int bin_bo_slot;
  64        struct vc4_exec_info *exec;
  65        unsigned long irqflags;
  66
  67        mutex_lock(&vc4->bin_bo_lock);
  68
  69        if (!vc4->bin_bo)
  70                goto complete;
  71
  72        bo = vc4->bin_bo;
  73
  74        bin_bo_slot = vc4_v3d_get_bin_slot(vc4);
  75        if (bin_bo_slot < 0) {
  76                DRM_ERROR("Couldn't allocate binner overflow mem\n");
  77                goto complete;
  78        }
  79
  80        spin_lock_irqsave(&vc4->job_lock, irqflags);
  81
  82        if (vc4->bin_alloc_overflow) {
  83                /* If we had overflow memory allocated previously,
  84                 * then that chunk will free when the current bin job
  85                 * is done.  If we don't have a bin job running, then
  86                 * the chunk will be done whenever the list of render
  87                 * jobs has drained.
  88                 */
  89                exec = vc4_first_bin_job(vc4);
  90                if (!exec)
  91                        exec = vc4_last_render_job(vc4);
  92                if (exec) {
  93                        exec->bin_slots |= vc4->bin_alloc_overflow;
  94                } else {
  95                        /* There's nothing queued in the hardware, so
  96                         * the old slot is free immediately.
  97                         */
  98                        vc4->bin_alloc_used &= ~vc4->bin_alloc_overflow;
  99                }
 100        }
 101        vc4->bin_alloc_overflow = BIT(bin_bo_slot);
 102
 103        V3D_WRITE(V3D_BPOA, bo->base.paddr + bin_bo_slot * vc4->bin_alloc_size);
 104        V3D_WRITE(V3D_BPOS, bo->base.base.size);
 105        V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
 106        V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
 107        spin_unlock_irqrestore(&vc4->job_lock, irqflags);
 108
 109complete:
 110        mutex_unlock(&vc4->bin_bo_lock);
 111}
 112
 113static void
 114vc4_irq_finish_bin_job(struct drm_device *dev)
 115{
 116        struct vc4_dev *vc4 = to_vc4_dev(dev);
 117        struct vc4_exec_info *next, *exec = vc4_first_bin_job(vc4);
 118
 119        if (!exec)
 120                return;
 121
 122        vc4_move_job_to_render(dev, exec);
 123        next = vc4_first_bin_job(vc4);
 124
 125        /* Only submit the next job in the bin list if it matches the perfmon
 126         * attached to the one that just finished (or if both jobs don't have
 127         * perfmon attached to them).
 128         */
 129        if (next && next->perfmon == exec->perfmon)
 130                vc4_submit_next_bin_job(dev);
 131}
 132
 133static void
 134vc4_cancel_bin_job(struct drm_device *dev)
 135{
 136        struct vc4_dev *vc4 = to_vc4_dev(dev);
 137        struct vc4_exec_info *exec = vc4_first_bin_job(vc4);
 138
 139        if (!exec)
 140                return;
 141
 142        /* Stop the perfmon so that the next bin job can be started. */
 143        if (exec->perfmon)
 144                vc4_perfmon_stop(vc4, exec->perfmon, false);
 145
 146        list_move_tail(&exec->head, &vc4->bin_job_list);
 147        vc4_submit_next_bin_job(dev);
 148}
 149
 150static void
 151vc4_irq_finish_render_job(struct drm_device *dev)
 152{
 153        struct vc4_dev *vc4 = to_vc4_dev(dev);
 154        struct vc4_exec_info *exec = vc4_first_render_job(vc4);
 155        struct vc4_exec_info *nextbin, *nextrender;
 156
 157        if (!exec)
 158                return;
 159
 160        vc4->finished_seqno++;
 161        list_move_tail(&exec->head, &vc4->job_done_list);
 162
 163        nextbin = vc4_first_bin_job(vc4);
 164        nextrender = vc4_first_render_job(vc4);
 165
 166        /* Only stop the perfmon if following jobs in the queue don't expect it
 167         * to be enabled.
 168         */
 169        if (exec->perfmon && !nextrender &&
 170            (!nextbin || nextbin->perfmon != exec->perfmon))
 171                vc4_perfmon_stop(vc4, exec->perfmon, true);
 172
 173        /* If there's a render job waiting, start it. If this is not the case
 174         * we may have to unblock the binner if it's been stalled because of
 175         * perfmon (this can be checked by comparing the perfmon attached to
 176         * the finished renderjob to the one attached to the next bin job: if
 177         * they don't match, this means the binner is stalled and should be
 178         * restarted).
 179         */
 180        if (nextrender)
 181                vc4_submit_next_render_job(dev);
 182        else if (nextbin && nextbin->perfmon != exec->perfmon)
 183                vc4_submit_next_bin_job(dev);
 184
 185        if (exec->fence) {
 186                dma_fence_signal_locked(exec->fence);
 187                dma_fence_put(exec->fence);
 188                exec->fence = NULL;
 189        }
 190
 191        wake_up_all(&vc4->job_wait_queue);
 192        schedule_work(&vc4->job_done_work);
 193}
 194
 195irqreturn_t
 196vc4_irq(int irq, void *arg)
 197{
 198        struct drm_device *dev = arg;
 199        struct vc4_dev *vc4 = to_vc4_dev(dev);
 200        uint32_t intctl;
 201        irqreturn_t status = IRQ_NONE;
 202
 203        barrier();
 204        intctl = V3D_READ(V3D_INTCTL);
 205
 206        /* Acknowledge the interrupts we're handling here. The binner
 207         * last flush / render frame done interrupt will be cleared,
 208         * while OUTOMEM will stay high until the underlying cause is
 209         * cleared.
 210         */
 211        V3D_WRITE(V3D_INTCTL, intctl);
 212
 213        if (intctl & V3D_INT_OUTOMEM) {
 214                /* Disable OUTOMEM until the work is done. */
 215                V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM);
 216                schedule_work(&vc4->overflow_mem_work);
 217                status = IRQ_HANDLED;
 218        }
 219
 220        if (intctl & V3D_INT_FLDONE) {
 221                spin_lock(&vc4->job_lock);
 222                vc4_irq_finish_bin_job(dev);
 223                spin_unlock(&vc4->job_lock);
 224                status = IRQ_HANDLED;
 225        }
 226
 227        if (intctl & V3D_INT_FRDONE) {
 228                spin_lock(&vc4->job_lock);
 229                vc4_irq_finish_render_job(dev);
 230                spin_unlock(&vc4->job_lock);
 231                status = IRQ_HANDLED;
 232        }
 233
 234        return status;
 235}
 236
 237void
 238vc4_irq_preinstall(struct drm_device *dev)
 239{
 240        struct vc4_dev *vc4 = to_vc4_dev(dev);
 241
 242        if (!vc4->v3d)
 243                return;
 244
 245        init_waitqueue_head(&vc4->job_wait_queue);
 246        INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work);
 247
 248        /* Clear any pending interrupts someone might have left around
 249         * for us.
 250         */
 251        V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
 252}
 253
 254int
 255vc4_irq_postinstall(struct drm_device *dev)
 256{
 257        struct vc4_dev *vc4 = to_vc4_dev(dev);
 258
 259        if (!vc4->v3d)
 260                return 0;
 261
 262        /* Enable the render done interrupts. The out-of-memory interrupt is
 263         * enabled as soon as we have a binner BO allocated.
 264         */
 265        V3D_WRITE(V3D_INTENA, V3D_INT_FLDONE | V3D_INT_FRDONE);
 266
 267        return 0;
 268}
 269
 270void
 271vc4_irq_uninstall(struct drm_device *dev)
 272{
 273        struct vc4_dev *vc4 = to_vc4_dev(dev);
 274
 275        if (!vc4->v3d)
 276                return;
 277
 278        /* Disable sending interrupts for our driver's IRQs. */
 279        V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS);
 280
 281        /* Clear any pending interrupts we might have left. */
 282        V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
 283
 284        /* Finish any interrupt handler still in flight. */
 285        disable_irq(dev->irq);
 286
 287        cancel_work_sync(&vc4->overflow_mem_work);
 288}
 289
 290/** Reinitializes interrupt registers when a GPU reset is performed. */
 291void vc4_irq_reset(struct drm_device *dev)
 292{
 293        struct vc4_dev *vc4 = to_vc4_dev(dev);
 294        unsigned long irqflags;
 295
 296        /* Acknowledge any stale IRQs. */
 297        V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
 298
 299        /*
 300         * Turn all our interrupts on.  Binner out of memory is the
 301         * only one we expect to trigger at this point, since we've
 302         * just come from poweron and haven't supplied any overflow
 303         * memory yet.
 304         */
 305        V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS);
 306
 307        spin_lock_irqsave(&vc4->job_lock, irqflags);
 308        vc4_cancel_bin_job(dev);
 309        vc4_irq_finish_render_job(dev);
 310        spin_unlock_irqrestore(&vc4->job_lock, irqflags);
 311}
 312