diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 649032174dbd..695bc6a29d30 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -212,14 +212,9 @@ struct vc4_dev { struct work_struct overflow_mem_work; - int power_refcount; - /* Set to true when the load tracker is active. */ bool load_tracker_enabled; - /* Mutex controlling the power refcount. */ - struct mutex power_lock; - struct { struct timer_list timer; struct work_struct reset_work; @@ -294,6 +289,13 @@ struct vc4_v3d { struct platform_device *pdev; void __iomem *regs; struct clk *clk; + + /* Reset line for the V3D block, used to recover from a GPU hang. + * NULL if the device tree does not describe one, in which case the + * GPU cannot be reset. + */ + struct reset_control *reset; + struct debugfs_regset32 regset; }; @@ -1056,6 +1058,7 @@ int vc4_v3d_bin_bo_get(struct vc4_dev *vc4, bool *used); void vc4_v3d_bin_bo_put(struct vc4_dev *vc4); int vc4_v3d_pm_get(struct vc4_dev *vc4); void vc4_v3d_pm_put(struct vc4_dev *vc4); +void vc4_v3d_init_hw(struct drm_device *dev); int vc4_v3d_debugfs_init(struct drm_minor *minor); /* vc4_validate.c */ diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index e231c906709c..3212b9167620 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include @@ -292,19 +292,22 @@ err_free_state: static void vc4_reset(struct drm_device *dev) { - struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_v3d *v3d = to_vc4_dev(dev)->v3d; + int ret; - DRM_INFO("Resetting GPU.\n"); + vc4_irq_disable(dev); - mutex_lock(&vc4->power_lock); - if (vc4->power_refcount) { - /* Power the device off and back on the by dropping the - * reference on runtime PM. - */ - pm_runtime_put_sync_suspend(&vc4->v3d->pdev->dev); - pm_runtime_get_sync(&vc4->v3d->pdev->dev); + if (v3d->reset) { + drm_info(dev, "Resetting GPU.\n"); + + ret = reset_control_reset(v3d->reset); + if (ret) + drm_err(dev, "Failed to reset the GPU: %d\n", ret); + + vc4_v3d_init_hw(dev); + } else { + drm_info_once(dev, "No reset line; GPU state is not reset.\n"); } - mutex_unlock(&vc4->power_lock); vc4_irq_reset(dev); @@ -320,10 +323,19 @@ vc4_reset_work(struct work_struct *work) { struct vc4_dev *vc4 = container_of(work, struct vc4_dev, hangcheck.reset_work); + int ret; + + /* Make sure the device is not suspended during the reset. */ + ret = vc4_v3d_pm_get(vc4); + if (ret) { + drm_err(&vc4->base, "Failed to resume V3D for GPU reset: %d\n", ret); + return; + } vc4_save_hang_state(&vc4->base); - vc4_reset(&vc4->base); + + vc4_v3d_pm_put(vc4); } static void @@ -1177,10 +1189,6 @@ int vc4_gem_init(struct drm_device *dev) INIT_WORK(&vc4->job_done_work, vc4_job_done_work); - ret = drmm_mutex_init(dev, &vc4->power_lock); - if (ret) - return ret; - INIT_LIST_HEAD(&vc4->purgeable.list); ret = drmm_mutex_init(dev, &vc4->purgeable.lock); diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index 7877d493d80e..3a3ea1e62dcb 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -336,10 +336,9 @@ void vc4_irq_reset(struct drm_device *dev) V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); /* - * 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. + * Turn all our interrupts on. Binner out of memory is the only + * one we expect to trigger at this point, since the reset cleared + * the overflow memory address and none has been supplied yet. */ 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 a86739873e05..b40d98c9d1d2 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -122,29 +123,13 @@ static int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) return 0; } -/* - * Wraps pm_runtime_get_sync() in a refcount, so that we can reliably - * get the pm_runtime refcount to 0 in vc4_reset(). - */ int vc4_v3d_pm_get(struct vc4_dev *vc4) { if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) return -ENODEV; - mutex_lock(&vc4->power_lock); - if (vc4->power_refcount++ == 0) { - int ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); - - if (ret < 0) { - vc4->power_refcount--; - mutex_unlock(&vc4->power_lock); - return ret; - } - } - mutex_unlock(&vc4->power_lock); - - return 0; + return pm_runtime_resume_and_get(&vc4->v3d->pdev->dev); } void @@ -153,15 +138,10 @@ vc4_v3d_pm_put(struct vc4_dev *vc4) if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) return; - mutex_lock(&vc4->power_lock); - if (--vc4->power_refcount == 0) { - pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev); - pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); - } - mutex_unlock(&vc4->power_lock); + pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); } -static void vc4_v3d_init_hw(struct drm_device *dev) +void vc4_v3d_init_hw(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); @@ -447,6 +427,14 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) if (IS_ERR(v3d->clk)) return dev_err_probe(dev, PTR_ERR(v3d->clk), "Failed to get V3D clock\n"); + v3d->reset = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(v3d->reset)) + return dev_err_probe(dev, PTR_ERR(v3d->reset), + "Failed to get reset control\n"); + + if (!v3d->reset) + drm_warn(drm, "No V3D reset line in the device tree"); + ret = platform_get_irq(pdev, 0); if (ret < 0) return ret;