From a0986f8d6d64b2014c9dbaa412a7ab6a84a77331 Mon Sep 17 00:00:00 2001
From: =?utf8?q?=E9=99=88=E6=81=92=E6=98=8E?= <chm@rock-chips.com>
Date: Wed, 20 Jul 2011 16:07:00 +0800
Subject: [PATCH] rk29: vpu_service: fix bug when delay work and cancel work
 come at the same time

---
 arch/arm/mach-rk29/vpu_service.c | 28 +++++++++++++++++++++-------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-rk29/vpu_service.c b/arch/arm/mach-rk29/vpu_service.c
index b0d08b29967d..d5465cb221aa 100644
--- a/arch/arm/mach-rk29/vpu_service.c
+++ b/arch/arm/mach-rk29/vpu_service.c
@@ -120,6 +120,7 @@ typedef struct vpu_device {
 
 typedef struct vpu_service_info {
 	spinlock_t		lock;
+	struct workqueue_struct	*workqueue;
 	struct list_head	waiting;		/* link to link_reg in struct vpu_reg */
 	struct list_head	running;		/* link to link_reg in struct vpu_reg */
 	struct list_head	done;			/* link to link_reg in struct vpu_reg */
@@ -391,14 +392,20 @@ static void try_set_reg(void)
        		    ((VPU_PP  == reg->type) && (NULL == service.reg_pproc)) ||
        		    ((VPU_ENC == reg->type) && (NULL == service.reg_codec))) {
 			reg_from_wait_to_run(reg);
-			__cancel_delayed_work(&vpu_service_power_off_work);
+			if (!cancel_delayed_work(&vpu_service_power_off_work)) {
+				if (!in_interrupt()) {
+					flush_delayed_work(&vpu_service_power_off_work);
+				} else {
+					pr_err("try_set_reg in inturrpt but cancel power off failed\n");
+				}
+			}
 			vpu_service_power_on();
 			reg_copy_to_hw(reg);
 		}
 		spin_unlock_irqrestore(&service.lock, flag);
 	} else {
 		spin_unlock_irqrestore(&service.lock, flag);
-		schedule_delayed_work(&vpu_service_power_off_work, POWER_OFF_DELAY);
+		queue_delayed_work(service.workqueue, &vpu_service_power_off_work, POWER_OFF_DELAY);
 	}
 }
 
@@ -674,7 +681,7 @@ static struct miscdevice vpu_service_misc_device = {
 static void vpu_service_shutdown(struct platform_device *pdev)
 {
 	pr_cont("shutdown...");
-	__cancel_delayed_work(&vpu_service_power_off_work);
+	cancel_delayed_work(&vpu_service_power_off_work);
 	vpu_service_power_off();
 	pr_cont("done\n");
 }
@@ -683,7 +690,7 @@ static int vpu_service_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	bool enabled;
 	pr_info("suspend...");
-	__cancel_delayed_work(&vpu_service_power_off_work);
+	cancel_delayed_work(&vpu_service_power_off_work);
 	enabled = service.enabled;
 	vpu_service_power_off();
 	service.enabled = enabled;
@@ -972,7 +979,12 @@ static int __init vpu_service_init(void)
 	service.reg_codec	= NULL;
 	service.reg_pproc	= NULL;
 	atomic_set(&service.task_running, 0);
-	service.enabled = false;
+	service.enabled		= false;
+	service.workqueue	= create_singlethread_workqueue("vpu_service");
+	if (!service.workqueue) {
+		pr_err("create_singlethread_workqueue failed\n");
+		return -ENOMEM;
+	}
 
 	vpu_get_clk();
 	vpu_service_power_on();
@@ -1020,14 +1032,16 @@ err_reserve_io:
 	vpu_service_power_off();
 	vpu_service_release_io();
 	vpu_put_clk();
+	destroy_workqueue(service.workqueue);
 	pr_info("init failed\n");
 	return ret;
 }
 
 static void __exit vpu_service_exit(void)
 {
-	__cancel_delayed_work(&vpu_service_power_off_work);
+	cancel_delayed_work(&vpu_service_power_off_work);
 	vpu_service_power_off();
+	destroy_workqueue(service.workqueue);
 	platform_device_unregister(&vpu_service_device);
 	platform_driver_unregister(&vpu_service_driver);
 	misc_deregister(&vpu_service_misc_device);
@@ -1080,7 +1094,7 @@ static int proc_vpu_service_show(struct seq_file *s, void *v)
 		}
 	}
 	spin_unlock_irqrestore(&service.lock, flag);
-	schedule_delayed_work(&vpu_service_power_off_work, POWER_OFF_DELAY);
+	queue_delayed_work(service.workqueue, &vpu_service_power_off_work, POWER_OFF_DELAY);
 
 	return 0;
 }
-- 
2.34.1