rk29: vpu_service: fix bug when isr timeout and taskrunning count error
[firefly-linux-kernel-4.4.55.git] / arch / arm / mach-rk29 / vpu_service.c
index b0d08b29967dcadf526a94d50e63e12cc56b8daf..1ccc145ddf9dd5d624ac31df8747799bb52e0484 100644 (file)
@@ -34,7 +34,7 @@
 #include <linux/mm.h>
 #include <linux/poll.h>
 #include <linux/platform_device.h>
-#include <linux/workqueue.h>
+#include <linux/timer.h>
 
 #include <asm/uaccess.h>
 
@@ -120,6 +120,7 @@ typedef struct vpu_device {
 
 typedef struct vpu_service_info {
        spinlock_t              lock;
+       struct timer_list       timer;                  /* timer for power off */
        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 */
@@ -147,9 +148,8 @@ static vpu_service_info service;
 static vpu_device      dec_dev;
 static vpu_device      enc_dev;
 
-static void vpu_service_power_off_work_func(struct work_struct *work);
-static DECLARE_DELAYED_WORK(vpu_service_power_off_work, vpu_service_power_off_work_func);
-#define POWER_OFF_DELAY        3*HZ /* 3s */
+#define POWER_OFF_DELAY        4*HZ /* 4s */
+#define TIMEOUT_DELAY  2*HZ /* 2s */
 
 static void vpu_get_clk(void)
 {
@@ -167,47 +167,55 @@ static void vpu_put_clk(void)
        clk_put(hclk_cpu_vcodec);
 }
 
-static void vpu_service_power_on(void)
-{
-       if (service.enabled)
-               return;
-
-       printk("vpu: power on\n");
-       clk_enable(aclk_vepu);
-       clk_enable(hclk_vepu);
-       clk_enable(hclk_cpu_vcodec);
-       udelay(10);
-       pmu_set_power_domain(PD_VCODEC, true);
-       udelay(10);
-       clk_enable(aclk_ddr_vepu);
-       service.enabled = true;
-}
-
 static void vpu_service_power_off(void)
 {
        if (!service.enabled)
                return;
 
-       while(atomic_read(&service.task_running))
+       service.enabled = false;
+       printk("vpu: power off\n");
+
+       while(atomic_read(&service.task_running)) {
+               pr_alert("power off when task running!!\n");
                udelay(10);
+       }
 
-       printk("vpu: power off\n");
        pmu_set_power_domain(PD_VCODEC, false);
        udelay(10);
        clk_disable(hclk_cpu_vcodec);
        clk_disable(aclk_ddr_vepu);
        clk_disable(hclk_vepu);
        clk_disable(aclk_vepu);
-
-       service.enabled = false;
 }
 
-static void vpu_service_power_off_work_func(struct work_struct *work)
+static void vpu_service_power_off_work_func(unsigned long data)
 {
-       pr_debug("work\n");
+       printk("vpu: delayed power off work\n");
        vpu_service_power_off();
 }
 
+static void vpu_service_power_on(void)
+{
+       if (service.enabled) {
+               mod_timer(&service.timer, jiffies + POWER_OFF_DELAY);
+               return;
+       }
+       service.enabled = true;
+       printk("vpu: power on\n");
+
+       clk_enable(aclk_vepu);
+       clk_enable(hclk_vepu);
+       clk_enable(hclk_cpu_vcodec);
+       udelay(10);
+       pmu_set_power_domain(PD_VCODEC, true);
+       udelay(10);
+       clk_enable(aclk_ddr_vepu);
+       init_timer(&service.timer);
+       service.timer.expires = jiffies + POWER_OFF_DELAY;
+       service.timer.function = vpu_service_power_off_work_func;
+       add_timer(&service.timer);
+}
+
 static vpu_reg *reg_init(vpu_session *session, void __user *src, unsigned long size)
 {
        unsigned long flag;
@@ -253,8 +261,6 @@ static void reg_from_wait_to_run(vpu_reg *reg)
 
        list_del_init(&reg->session_link);
        list_add_tail(&reg->session_link, &reg->session->running);
-
-       atomic_add(1, &service.task_running);
 }
 
 static void reg_copy_from_hw(vpu_reg *reg, volatile u32 *src, u32 count)
@@ -303,7 +309,6 @@ static void reg_from_run_to_done(vpu_reg *reg)
                break;
        }
        }
-       atomic_sub(1, &service.task_running);
        wake_up_interruptible_sync(&reg->session->wait);
        spin_unlock(&service.lock);
 }
@@ -312,7 +317,7 @@ void reg_copy_to_hw(vpu_reg *reg)
 {
        int i;
        u32 *src = (u32 *)&reg->reg[0];
-
+       atomic_add(1, &service.task_running);
        switch (reg->type) {
        case VPU_ENC : {
                u32 *dst = (u32 *)enc_dev.hwregs;
@@ -373,6 +378,7 @@ void reg_copy_to_hw(vpu_reg *reg)
        } break;
        default : {
                pr_err("unsupport session type %d", reg->type);
+               atomic_sub(1, &service.task_running);
                break;
        }
        }
@@ -391,15 +397,11 @@ 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);
                        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);
        }
+       spin_unlock_irqrestore(&service.lock, flag);
 }
 
 static int return_reg(vpu_reg *reg, u32 __user *dst)
@@ -493,7 +495,7 @@ static long vpu_service_ioctl(struct file *filp, unsigned int cmd, unsigned long
                        pr_err("VPU_IOC_GET_REG copy_from_user failed\n");
                        return -EFAULT;
                } else {
-                       int ret = wait_event_interruptible_timeout(session->wait, !list_empty(&session->done), HZ);
+                       int ret = wait_event_interruptible_timeout(session->wait, !list_empty(&session->done), TIMEOUT_DELAY);
                        if (unlikely(ret < 0)) {
                                pr_err("pid %d wait task ret %d\n", session->pid, ret);
                                return ret;
@@ -674,7 +676,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);
+       del_timer(&service.timer);
        vpu_service_power_off();
        pr_cont("done\n");
 }
@@ -683,7 +685,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);
+       del_timer(&service.timer);
        enabled = service.enabled;
        vpu_service_power_off();
        service.enabled = enabled;
@@ -909,6 +911,7 @@ static irqreturn_t vdpu_isr(int irq, void *dev_id)
                /* clear dec IRQ */
                writel(irq_status_dec & (~DEC_INTERRUPT_BIT), dev->hwregs + DEC_INTERRUPT_REGISTER);
                pr_debug("DEC IRQ received!\n");
+               atomic_sub(1, &service.task_running);
                if (NULL == service.reg_codec) {
                        pr_err("dec isr with no task waiting\n");
                } else {
@@ -920,7 +923,7 @@ static irqreturn_t vdpu_isr(int irq, void *dev_id)
                /* clear pp IRQ */
                writel(irq_status_pp & (~DEC_INTERRUPT_BIT), dev->hwregs + PP_INTERRUPT_REGISTER);
                pr_debug("PP IRQ received!\n");
-
+               atomic_sub(1, &service.task_running);
                if (NULL == service.reg_pproc) {
                        pr_err("pp isr with no task waiting\n");
                } else {
@@ -942,7 +945,7 @@ static irqreturn_t vepu_isr(int irq, void *dev_id)
                /* clear enc IRQ */
                writel(irq_status & (~ENC_INTERRUPT_BIT), dev->hwregs + ENC_INTERRUPT_REGISTER);
                pr_debug("ENC IRQ received!\n");
-
+               atomic_sub(1, &service.task_running);
                if (NULL == service.reg_codec) {
                        pr_err("enc isr with no task waiting\n");
                } else {
@@ -972,7 +975,7 @@ 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;
 
        vpu_get_clk();
        vpu_service_power_on();
@@ -1005,6 +1008,7 @@ static int __init vpu_service_init(void)
        platform_device_register(&vpu_service_device);
        platform_driver_probe(&vpu_service_driver, NULL);
        get_hw_info();
+       del_timer(&service.timer);
        vpu_service_power_off();
        pr_info("init success\n");
 
@@ -1017,6 +1021,7 @@ err_req_vepu_irq:
 err_req_vdpu_irq:
        pr_info("init failed\n");
 err_reserve_io:
+       del_timer(&service.timer);
        vpu_service_power_off();
        vpu_service_release_io();
        vpu_put_clk();
@@ -1026,7 +1031,7 @@ err_reserve_io:
 
 static void __exit vpu_service_exit(void)
 {
-       __cancel_delayed_work(&vpu_service_power_off_work);
+       del_timer(&service.timer);
        vpu_service_power_off();
        platform_device_unregister(&vpu_service_device);
        platform_driver_unregister(&vpu_service_driver);
@@ -1051,7 +1056,6 @@ static int proc_vpu_service_show(struct seq_file *s, void *v)
        vpu_reg *reg, *reg_tmp;
        vpu_session *session, *session_tmp;
 
-       cancel_delayed_work_sync(&vpu_service_power_off_work);
        vpu_service_power_on();
        seq_printf(s, "\nENC Registers:\n");
        n = enc_dev.iosize >> 2;
@@ -1080,7 +1084,6 @@ 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);
 
        return 0;
 }