#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>
typedef struct vpu_service_info {
spinlock_t lock;
- struct workqueue_struct *workqueue;
+ 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 */
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 */
static void vpu_get_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;
+ 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;
((VPU_PP == reg->type) && (NULL == service.reg_pproc)) ||
((VPU_ENC == reg->type) && (NULL == service.reg_codec))) {
reg_from_wait_to_run(reg);
- 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);
- queue_delayed_work(service.workqueue, &vpu_service_power_off_work, POWER_OFF_DELAY);
}
+ spin_unlock_irqrestore(&service.lock, flag);
}
static int return_reg(vpu_reg *reg, u32 __user *dst)
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");
}
{
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;
service.reg_pproc = NULL;
atomic_set(&service.task_running, 0);
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();
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");
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();
- 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);
+ del_timer(&service.timer);
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);
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;
}
}
spin_unlock_irqrestore(&service.lock, flag);
- queue_delayed_work(service.workqueue, &vpu_service_power_off_work, POWER_OFF_DELAY);
return 0;
}