diff options
author | Eric Anholt <eric@anholt.net> | 2014-10-16 10:48:30 +0100 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2015-06-04 14:15:24 -0700 |
commit | 631c7adff119fc2fedb09e64a9e2057501648d59 (patch) | |
tree | a00001277a58c59be7bf829b986d7edde7bf3bbd | |
parent | 3c5db5e5e5f77d6a589b3a11f18d6a5e7744fec7 (diff) | |
download | linux-631c7adff119fc2fedb09e64a9e2057501648d59.tar.gz |
drm/vc4: Use interrupts for binner overflow memory allocation handling.
This avoids some of the checks in the spinloop waiting for frame done,
and avoids the race I had documented, as far as I know.
Note that this reportedly requires "mask_gpu_interrupt0=0x400" in
config.txt to keep the VPU firmware from trying to do things based on
our interrupts.
v2: Rebase on 3.19 DRM core APIs.
v3: Rebase on the KMS work.
Signed-off-by: Eric Anholt <eric@anholt.net>
-rw-r--r-- | drivers/gpu/drm/vc4/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.c | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_drv.h | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_gem.c | 79 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_irq.c | 129 | ||||
-rw-r--r-- | drivers/gpu/drm/vc4/vc4_v3d.c | 8 |
6 files changed, 174 insertions, 71 deletions
diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index 09dad06a75b9..0abf23d62f9e 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -11,6 +11,7 @@ vc4-y := \ vc4_gem.o \ vc4_hdmi.o \ vc4_hvs.o \ + vc4_irq.o \ vc4_plane.o \ vc4_v3d.o \ vc4_validate.o \ diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index ba1145a98e38..433be8c09efa 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -58,6 +58,8 @@ vc4_drm_load(struct drm_device *dev, unsigned long flags) if (!vc4) return -ENOMEM; + INIT_LIST_HEAD(&vc4->overflow_list); + vc4->firmware_node = of_parse_phandle(dev->dev->of_node, "firmware", 0); if (!vc4->firmware_node) { DRM_ERROR("Failed to parse firmware node.\n"); @@ -114,11 +116,17 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { static struct drm_driver vc4_drm_driver = { .driver_features = (DRIVER_MODESET | DRIVER_GEM | + DRIVER_HAVE_IRQ | DRIVER_PRIME), .load = vc4_drm_load, .unload = vc4_drm_unload, .set_busid = drm_platform_set_busid, + .irq_handler = vc4_irq, + .irq_preinstall = vc4_irq_preinstall, + .irq_postinstall = vc4_irq_postinstall, + .irq_uninstall = vc4_irq_uninstall, + .enable_vblank = vc4_enable_vblank, .disable_vblank = vc4_disable_vblank, .get_vblank_counter = drm_vblank_count, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index bf0d749a312f..8b745c3bac40 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -18,6 +18,13 @@ struct vc4_dev { struct vc4_hvs *hvs; struct vc4_crtc *crtc[3]; struct vc4_v3d *v3d; + + /* List of struct vc4_list_bo_entry allocated to accomodate + * binner overflow. These will be freed when the exec is + * done. + */ + struct list_head overflow_list; + struct work_struct overflow_mem_work; }; static inline struct vc4_dev * @@ -122,12 +129,6 @@ struct exec_info { */ struct drm_gem_cma_object *exec_bo; - /* List of struct vc4_list_bo_entry allocated to accomodate - * binner overflow. These will be freed when the exec is - * done. - */ - struct list_head overflow_list; - /** * This tracks the per-shader-record state (packet 64) that * determines the length of the shader record and the offset @@ -283,6 +284,13 @@ struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev, struct drm_encoder *encoder); int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); +/* vc4_irq.c */ +irqreturn_t vc4_irq(int irq, void *arg); +void vc4_irq_preinstall(struct drm_device *dev); +int vc4_irq_postinstall(struct drm_device *dev); +void vc4_irq_uninstall(struct drm_device *dev); +void vc4_irq_reset(struct drm_device *dev); + /* vc4_hvs.c */ void vc4_hvs_register(void); void vc4_hvs_unregister(void); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 9dc661ee3798..3c38e41bd41f 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -38,6 +38,8 @@ vc4_reset(struct drm_device *dev) DRM_INFO("Resetting GPU.\n"); vc4_v3d_set_power(vc4, false); vc4_v3d_set_power(vc4, true); + + vc4_irq_reset(dev); } static void @@ -69,77 +71,16 @@ thread_stopped(struct drm_device *dev, uint32_t thread) } static int -try_adding_overflow_memory(struct drm_device *dev, struct exec_info *exec) -{ - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_bo_list_entry *entry = kmalloc(sizeof(*entry), GFP_KERNEL); - int ret; - - if (!entry) - return -ENOMEM; - - entry->bo = drm_gem_cma_create(dev, 256 * 1024); - if (IS_ERR(entry->bo)) { - int ret = PTR_ERR(entry->bo); - DRM_ERROR("Couldn't allocate binner overflow mem\n"); - kfree(entry); - return ret; - } - - list_add_tail(&entry->head, &exec->overflow_list); - - V3D_WRITE(V3D_BPOA, entry->bo->paddr); - V3D_WRITE(V3D_BPOS, entry->bo->base.size); - - /* Wait for the hardware to ack our supplied memory before - * continuing. There's an ugly race here where if the - * hardware gets the request and continues on to overflow - * again before we read the reg, we'll time out. - */ - ret = wait_for((V3D_READ(V3D_PCS) & V3D_BMOOM) == 0, 1000); - if (ret) { - DRM_ERROR("Timed out waiting for hardware to ack " - "overflow memory\n"); - return ret; - } - - return 0; -} - -static bool -vc4_job_finished(struct drm_device *dev, struct exec_info *exec) -{ - struct vc4_dev *vc4 = to_vc4_dev(dev); - bool stopped = thread_stopped(dev, 1); - - /* If the thread is merely paused waiting for overflow memory, - * hand some to it so we can make progress. - */ - if (V3D_READ(V3D_PCS) & V3D_BMOOM) { - int ret = try_adding_overflow_memory(dev, exec); - return ret != 0; - } - - return stopped; -} - -static int wait_for_job(struct drm_device *dev, struct exec_info *exec) { - struct vc4_dev *vc4 = to_vc4_dev(dev); int ret; - ret = wait_for(vc4_job_finished(dev, exec), 1000); + ret = wait_for(thread_stopped(dev, 1), 1000); if (ret) { DRM_ERROR("timeout waiting for render thread idle\n"); return ret; } - if (V3D_READ(V3D_PCS) & V3D_BMOOM) { - DRM_ERROR("binner oom and stopped.\n"); - return -EINVAL; - } - return 0; } @@ -383,13 +324,13 @@ int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct vc4_dev *vc4 = to_vc4_dev(dev); struct exec_info exec; int ret; int i; memset(&exec, 0, sizeof(exec)); exec.args = data; - INIT_LIST_HEAD(&exec.overflow_list); mutex_lock(&dev->struct_mutex); @@ -414,10 +355,18 @@ fail: kfree(exec.bo); } - while (!list_empty(&exec.overflow_list)) { + /* Release all but the very last overflow list entry (which is + * still sitting in the BPOS/BPOA registers for use by the + * next job). + */ + while (true) { struct vc4_bo_list_entry *entry = - list_first_entry(&exec.overflow_list, + list_first_entry(&vc4->overflow_list, struct vc4_bo_list_entry, head); + + if (entry->head.next == &vc4->overflow_list) + break; + drm_gem_object_unreference(&entry->bo->base); list_del(&entry->head); kfree(entry); diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c new file mode 100644 index 000000000000..dfc302606762 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -0,0 +1,129 @@ +/* + * Copyright © 2014 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "vc4_drv.h" +#include "vc4_regs.h" + +#define V3D_DRIVER_IRQS V3D_INT_OUTOMEM + +static void +vc4_overflow_mem_work(struct work_struct *work) +{ + struct vc4_dev *vc4 = + container_of(work, struct vc4_dev, overflow_mem_work); + struct drm_device *dev = vc4->dev; + struct vc4_bo_list_entry *entry = kmalloc(sizeof(*entry), GFP_KERNEL); + + if (!entry) { + DRM_ERROR("Couldn't allocate binner overflow mem record\n"); + return; + } + + entry->bo = drm_gem_cma_create(dev, 256 * 1024); + if (IS_ERR(entry->bo)) { + DRM_ERROR("Couldn't allocate binner overflow mem\n"); + kfree(entry); + return; + } + + list_add_tail(&entry->head, &vc4->overflow_list); + + V3D_WRITE(V3D_BPOA, entry->bo->paddr); + V3D_WRITE(V3D_BPOS, entry->bo->base.size); + V3D_WRITE(V3D_INTDIS, 0); + V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); +} + +irqreturn_t +vc4_irq(int irq, void *arg) +{ + struct drm_device *dev = arg; + struct vc4_dev *vc4 = to_vc4_dev(dev); + uint32_t intctl; + + barrier(); + intctl = V3D_READ(V3D_INTCTL); + V3D_WRITE(V3D_INTCTL, intctl); + + if (intctl & V3D_INT_OUTOMEM) { + V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM); + schedule_work(&vc4->overflow_mem_work); + } + + return intctl ? IRQ_HANDLED : IRQ_NONE; +} + +void +vc4_irq_preinstall(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work); + + /* Clear any pending interrupts someone might have left around + * for us. + */ + V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); +} + +int +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. + */ + V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); + + /* No interrupts disabled. */ + V3D_WRITE(V3D_INTDIS, 0); + + return 0; +} + +void +vc4_irq_uninstall(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + cancel_work_sync(&vc4->overflow_mem_work); + + V3D_WRITE(V3D_INTENA, 0); + V3D_WRITE(V3D_INTDIS, 0); + + /* Clear any pending interrupts we might have left. */ + V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); +} + +/** Reinitializes interrupt registers when a GPU reset is performed. */ +void vc4_irq_reset(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); + V3D_WRITE(V3D_INTDIS, 0); + V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); +} diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index 5a18cf41558a..9321fa2a2f23 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -202,6 +202,12 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) vc4_v3d_init_hw(drm); + ret = drm_irq_install(drm, platform_get_irq(pdev, 0)); + if (ret) { + DRM_ERROR("Failed to install IRQ handler\n"); + return ret; + } + return 0; } @@ -211,6 +217,8 @@ static void vc4_v3d_unbind(struct device *dev, struct device *master, struct drm_device *drm = dev_get_drvdata(master); struct vc4_dev *vc4 = to_vc4_dev(drm); + drm_irq_uninstall(drm); + vc4->v3d = NULL; } |