diff options
Diffstat (limited to 'drivers/gpu/drm/imagination/pvr_device.c')
-rw-r--r-- | drivers/gpu/drm/imagination/pvr_device.c | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/drivers/gpu/drm/imagination/pvr_device.c b/drivers/gpu/drm/imagination/pvr_device.c index e16282325178..1be14cdbdace 100644 --- a/drivers/gpu/drm/imagination/pvr_device.c +++ b/drivers/gpu/drm/imagination/pvr_device.c @@ -114,6 +114,87 @@ static int pvr_device_clk_init(struct pvr_device *pvr_dev) return 0; } +static irqreturn_t pvr_device_irq_thread_handler(int irq, void *data) +{ + struct pvr_device *pvr_dev = data; + irqreturn_t ret = IRQ_NONE; + + /* We are in the threaded handler, we can keep dequeuing events until we + * don't see any. This should allow us to reduce the number of interrupts + * when the GPU is receiving a massive amount of short jobs. + */ + while (pvr_fw_irq_pending(pvr_dev)) { + pvr_fw_irq_clear(pvr_dev); + + if (pvr_dev->fw_dev.booted) { + pvr_fwccb_process(pvr_dev); + pvr_kccb_wake_up_waiters(pvr_dev); + } + + pm_runtime_mark_last_busy(from_pvr_device(pvr_dev)->dev); + + ret = IRQ_HANDLED; + } + + /* Unmask FW irqs before returning, so new interrupts can be received. */ + pvr_fw_irq_enable(pvr_dev); + return ret; +} + +static irqreturn_t pvr_device_irq_handler(int irq, void *data) +{ + struct pvr_device *pvr_dev = data; + + if (!pvr_fw_irq_pending(pvr_dev)) + return IRQ_NONE; /* Spurious IRQ - ignore. */ + + /* Mask the FW interrupts before waking up the thread. Will be unmasked + * when the thread handler is done processing events. + */ + pvr_fw_irq_disable(pvr_dev); + return IRQ_WAKE_THREAD; +} + +/** + * pvr_device_irq_init() - Initialise IRQ required by a PowerVR device + * @pvr_dev: Target PowerVR device. + * + * Returns: + * * 0 on success, + * * Any error returned by platform_get_irq_byname(), or + * * Any error returned by request_irq(). + */ +static int +pvr_device_irq_init(struct pvr_device *pvr_dev) +{ + struct drm_device *drm_dev = from_pvr_device(pvr_dev); + struct platform_device *plat_dev = to_platform_device(drm_dev->dev); + + init_waitqueue_head(&pvr_dev->kccb.rtn_q); + + pvr_dev->irq = platform_get_irq(plat_dev, 0); + if (pvr_dev->irq < 0) + return pvr_dev->irq; + + /* Clear any pending events before requesting the IRQ line. */ + pvr_fw_irq_clear(pvr_dev); + pvr_fw_irq_enable(pvr_dev); + + return request_threaded_irq(pvr_dev->irq, pvr_device_irq_handler, + pvr_device_irq_thread_handler, + IRQF_SHARED, "gpu", pvr_dev); +} + +/** + * pvr_device_irq_fini() - Deinitialise IRQ required by a PowerVR device + * @pvr_dev: Target PowerVR device. + */ +static void +pvr_device_irq_fini(struct pvr_device *pvr_dev) +{ + free_irq(pvr_dev->irq, pvr_dev); +} + /** * pvr_build_firmware_filename() - Construct a PowerVR firmware filename * @pvr_dev: Target PowerVR device. @@ -324,7 +405,19 @@ pvr_device_gpu_init(struct pvr_device *pvr_dev) return PTR_ERR(pvr_dev->kernel_vm_ctx); } + err = pvr_fw_init(pvr_dev); + if (err) + goto err_vm_ctx_put; + return 0; + +err_vm_ctx_put: + if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) { + pvr_vm_context_put(pvr_dev->kernel_vm_ctx); + pvr_dev->kernel_vm_ctx = NULL; + } + + return err; } /** @@ -334,6 +427,8 @@ pvr_device_gpu_init(struct pvr_device *pvr_dev) static void pvr_device_gpu_fini(struct pvr_device *pvr_dev) { + pvr_fw_fini(pvr_dev); + if (pvr_dev->fw_dev.processor_type != PVR_FW_PROCESSOR_TYPE_MIPS) { WARN_ON(!pvr_vm_context_put(pvr_dev->kernel_vm_ctx)); pvr_dev->kernel_vm_ctx = NULL; @@ -386,10 +481,17 @@ pvr_device_init(struct pvr_device *pvr_dev) if (err) goto err_pm_runtime_put; + err = pvr_device_irq_init(pvr_dev); + if (err) + goto err_device_gpu_fini; + pm_runtime_put(dev); return 0; +err_device_gpu_fini: + pvr_device_gpu_fini(pvr_dev); + err_pm_runtime_put: pm_runtime_put_sync_suspend(dev); @@ -407,6 +509,7 @@ pvr_device_fini(struct pvr_device *pvr_dev) * Deinitialization stages are performed in reverse order compared to * the initialization stages in pvr_device_init(). */ + pvr_device_irq_fini(pvr_dev); pvr_device_gpu_fini(pvr_dev); } |