Browse Source

drm/vc4: Use runtime PM to power cycle the device when the GPU hangs.

This gets us functional GPU reset again, like we had until a refactor
at merge time.  Tested with a little patch to stuff in a broken binner
job every 100 frames.

Signed-off-by: Eric Anholt <eric@anholt.net>
Eric Anholt 9 năm trước cách đây
mục cha
commit
36cb6253f9

+ 5 - 1
drivers/gpu/drm/vc4/vc4_drv.h

@@ -91,6 +91,11 @@ struct vc4_dev {
 	struct vc4_bo *overflow_mem;
 	struct vc4_bo *overflow_mem;
 	struct work_struct overflow_mem_work;
 	struct work_struct overflow_mem_work;
 
 
+	int power_refcount;
+
+	/* Mutex controlling the power refcount. */
+	struct mutex power_lock;
+
 	struct {
 	struct {
 		struct timer_list timer;
 		struct timer_list timer;
 		struct work_struct reset_work;
 		struct work_struct reset_work;
@@ -439,7 +444,6 @@ void vc4_plane_async_set_fb(struct drm_plane *plane,
 extern struct platform_driver vc4_v3d_driver;
 extern struct platform_driver vc4_v3d_driver;
 int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused);
 int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused);
 int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused);
 int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused);
-int vc4_v3d_set_power(struct vc4_dev *vc4, bool on);
 
 
 /* vc4_validate.c */
 /* vc4_validate.c */
 int
 int

+ 21 - 5
drivers/gpu/drm/vc4/vc4_gem.c

@@ -229,8 +229,16 @@ vc4_reset(struct drm_device *dev)
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 
 
 	DRM_INFO("Resetting GPU.\n");
 	DRM_INFO("Resetting GPU.\n");
-	vc4_v3d_set_power(vc4, false);
-	vc4_v3d_set_power(vc4, true);
+
+	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);
+	}
+	mutex_unlock(&vc4->power_lock);
 
 
 	vc4_irq_reset(dev);
 	vc4_irq_reset(dev);
 
 
@@ -641,7 +649,10 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
 	}
 	}
 	mutex_unlock(&dev->struct_mutex);
 	mutex_unlock(&dev->struct_mutex);
 
 
-	pm_runtime_put(&vc4->v3d->pdev->dev);
+	mutex_lock(&vc4->power_lock);
+	if (--vc4->power_refcount == 0)
+		pm_runtime_put(&vc4->v3d->pdev->dev);
+	mutex_unlock(&vc4->power_lock);
 
 
 	kfree(exec);
 	kfree(exec);
 }
 }
@@ -783,7 +794,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct drm_vc4_submit_cl *args = data;
 	struct drm_vc4_submit_cl *args = data;
 	struct vc4_exec_info *exec;
 	struct vc4_exec_info *exec;
-	int ret;
+	int ret = 0;
 
 
 	if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) {
 	if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) {
 		DRM_ERROR("Unknown flags: 0x%02x\n", args->flags);
 		DRM_ERROR("Unknown flags: 0x%02x\n", args->flags);
@@ -796,7 +807,10 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
-	ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev);
+	mutex_lock(&vc4->power_lock);
+	if (vc4->power_refcount++ == 0)
+		ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev);
+	mutex_unlock(&vc4->power_lock);
 	if (ret < 0) {
 	if (ret < 0) {
 		kfree(exec);
 		kfree(exec);
 		return ret;
 		return ret;
@@ -856,6 +870,8 @@ vc4_gem_init(struct drm_device *dev)
 		    (unsigned long)dev);
 		    (unsigned long)dev);
 
 
 	INIT_WORK(&vc4->job_done_work, vc4_job_done_work);
 	INIT_WORK(&vc4->job_done_work, vc4_job_done_work);
+
+	mutex_init(&vc4->power_lock);
 }
 }
 
 
 void
 void

+ 0 - 12
drivers/gpu/drm/vc4/vc4_v3d.c

@@ -145,18 +145,6 @@ int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused)
 }
 }
 #endif /* CONFIG_DEBUG_FS */
 #endif /* CONFIG_DEBUG_FS */
 
 
-int
-vc4_v3d_set_power(struct vc4_dev *vc4, bool on)
-{
-	/* XXX: This interface is needed for GPU reset, and the way to
-	 * do it is to turn our power domain off and back on.  We
-	 * can't just reset from within the driver, because the reset
-	 * bits are in the power domain's register area, and get set
-	 * during the poweron process.
-	 */
-	return 0;
-}
-
 static void vc4_v3d_init_hw(struct drm_device *dev)
 static void vc4_v3d_init_hw(struct drm_device *dev)
 {
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_dev *vc4 = to_vc4_dev(dev);