#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
-#include <linux/timer.h>
+#include <linux/wakelock.h>
#include <asm/uaccess.h>
#include <mach/irqs.h>
-#include <plat/vpu_service.h>
#include <mach/pmu.h>
#include <mach/cru.h>
+#include <plat/vpu_service.h>
+#include <plat/cpu.h>
+
+typedef enum {
+ VPU_DEC_ID_9190 = 0x6731,
+ VPU_ID_8270 = 0x8270,
+ VPU_ID_4831 = 0x4831,
+} VPU_HW_ID;
+
+typedef enum {
+ VPU_DEC_TYPE_9190 = 0,
+ VPU_ENC_TYPE_8270 = 0x100,
+ VPU_ENC_TYPE_4831 ,
+} VPU_HW_TYPE_E;
+
+typedef enum VPU_FREQ {
+ VPU_FREQ_200M,
+ VPU_FREQ_266M,
+ VPU_FREQ_300M,
+ VPU_FREQ_400M,
+ VPU_FREQ_DEFAULT,
+ VPU_FREQ_BUT,
+} VPU_FREQ;
+
+typedef struct {
+ VPU_HW_ID hw_id;
+ unsigned long hw_addr;
+ unsigned long enc_offset;
+ unsigned long enc_reg_num;
+ unsigned long enc_io_size;
+ unsigned long dec_offset;
+ unsigned long dec_reg_num;
+ unsigned long dec_io_size;
+} VPU_HW_INFO_E;
+
+#define VPU_SERVICE_SHOW_TIME 0
+
+#if VPU_SERVICE_SHOW_TIME
+static struct timeval enc_start, enc_end;
+static struct timeval dec_start, dec_end;
+static struct timeval pp_start, pp_end;
+#endif
+
+#define MHZ (1000*1000)
+
+#define VCODEC_PHYS (0x10104000)
+
+#define REG_NUM_9190_DEC (60)
+#define REG_NUM_9190_PP (41)
+#define REG_NUM_9190_DEC_PP (REG_NUM_9190_DEC+REG_NUM_9190_PP)
+
+#define REG_NUM_DEC_PP (REG_NUM_9190_DEC+REG_NUM_9190_PP)
+
+#define REG_NUM_ENC_8270 (96)
+#define REG_SIZE_ENC_8270 (0x200)
+#define REG_NUM_ENC_4831 (164)
+#define REG_SIZE_ENC_4831 (0x400)
+
+#define SIZE_REG(reg) ((reg)*4)
+
+VPU_HW_INFO_E vpu_hw_set[] = {
+ [0] = {
+ .hw_id = VPU_ID_8270,
+ .hw_addr = VCODEC_PHYS,
+ .enc_offset = 0x0,
+ .enc_reg_num = REG_NUM_ENC_8270,
+ .enc_io_size = REG_NUM_ENC_8270 * 4,
+ .dec_offset = REG_SIZE_ENC_8270,
+ .dec_reg_num = REG_NUM_9190_DEC_PP,
+ .dec_io_size = REG_NUM_9190_DEC_PP * 4,
+ },
+ [1] = {
+ .hw_id = VPU_ID_4831,
+ .hw_addr = VCODEC_PHYS,
+ .enc_offset = 0x0,
+ .enc_reg_num = REG_NUM_ENC_4831,
+ .enc_io_size = REG_NUM_ENC_4831 * 4,
+ .dec_offset = REG_SIZE_ENC_4831,
+ .dec_reg_num = REG_NUM_9190_DEC_PP,
+ .dec_io_size = REG_NUM_9190_DEC_PP * 4,
+ },
+};
+
#define DEC_INTERRUPT_REGISTER 1
#define PP_INTERRUPT_REGISTER 60
#define PP_INTERRUPT_BIT 0x100
#define ENC_INTERRUPT_BIT 0x1
-#define REG_NUM_DEC (60)
-#define REG_NUM_PP (41)
-#if defined(CONFIG_ARCH_RK29) || defined(CONFIG_ARCH_RK2928)
-#define REG_NUM_ENC (96)
-#elif defined(CONFIG_ARCH_RK30)
-#define REG_NUM_ENC (164)
-#endif
-#define REG_NUM_DEC_PP (REG_NUM_DEC+REG_NUM_PP)
-#define SIZE_REG(reg) ((reg)*4)
-
-#define DEC_IO_SIZE ((100 + 1) * 4) /* bytes */
-#if defined(CONFIG_ARCH_RK29) || defined(CONFIG_ARCH_RK2928)
-#define ENC_IO_SIZE (96 * 4) /* bytes */
-#elif defined(CONFIG_ARCH_RK30)
-#define ENC_IO_SIZE (164 * 4) /* bytes */
-#endif
-#define REG_NUM_DEC_PP (REG_NUM_DEC+REG_NUM_PP)
-static const u16 dec_hw_ids[] = { 0x8190, 0x8170, 0x9170, 0x9190, 0x6731 };
-#if defined(CONFIG_ARCH_RK29) || defined(CONFIG_ARCH_RK2928)
-static const u16 enc_hw_ids[] = { 0x6280, 0x7280, 0x8270 };
-#define DEC_PHY_OFFSET 0x200
-#elif defined(CONFIG_ARCH_RK30)
-static const u16 enc_hw_ids[] = { 0x6280, 0x7280, 0x8270, 0x8290, 0x4831 };
-#define DEC_PHY_OFFSET 0x400
-#define RK29_VCODEC_PHYS RK30_VCODEC_PHYS
-#endif
-
#define VPU_REG_EN_ENC 14
#define VPU_REG_ENC_GATE 2
#define VPU_REG_ENC_GATE_BIT (1<<4)
#define VPU_REG_DEC_PP_GATE 61
#define VPU_REG_DEC_PP_GATE_BIT (1<<8)
-
/**
* struct for process session which connect to vpu
*
*
* @author ChenHengming (2011-5-4)
*/
-#define VPU_REG_NUM_MAX (((VPU_REG_NUM_ENC)>(VPU_REG_NUM_DEC_PP))?(VPU_REG_NUM_ENC):(VPU_REG_NUM_DEC_PP))
typedef struct vpu_reg {
VPU_CLIENT_TYPE type;
+ VPU_FREQ freq;
vpu_session *session;
struct list_head session_link; /* link to vpu service session */
struct list_head status_link; /* link to register set list */
unsigned long size;
- unsigned long reg[VPU_REG_NUM_MAX];
+ unsigned long *reg;
} vpu_reg;
typedef struct vpu_device {
+ atomic_t irq_count_codec;
+ atomic_t irq_count_pp;
unsigned long iobaseaddr;
unsigned int iosize;
volatile u32 *hwregs;
} vpu_device;
typedef struct vpu_service_info {
- spinlock_t lock;
- spinlock_t lock_power;
- struct timer_list timer; /* timer for power off */
+ struct wake_lock wake_lock;
+ struct delayed_work power_off_work;
+ struct mutex lock;
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 */
vpu_reg *reg_resev;
VPUHwDecConfig_t dec_config;
VPUHwEncConfig_t enc_config;
+ VPU_HW_INFO_E *hw_info;
+ unsigned long reg_size;
+ bool auto_freq;
+ atomic_t freq_status;
} vpu_service_info;
typedef struct vpu_request
} vpu_request;
static struct clk *pd_video;
-static struct clk *clk_vpu; /* for power on notify */
static struct clk *aclk_vepu;
static struct clk *hclk_vepu;
static struct clk *aclk_ddr_vepu;
static vpu_device dec_dev;
static vpu_device enc_dev;
-#define POWER_OFF_DELAY 4*HZ /* 4s */
-#define TIMEOUT_DELAY 2*HZ /* 2s */
+#define VPU_POWER_OFF_DELAY 4*HZ /* 4s */
+#define VPU_TIMEOUT_DELAY 2*HZ /* 2s */
static void vpu_get_clk(void)
{
pd_video = clk_get(NULL, "pd_video");
- clk_vpu = clk_get(NULL, "vpu");
+ if (IS_ERR(pd_video)) {
+ pr_err("failed on clk_get pd_video\n");
+ }
aclk_vepu = clk_get(NULL, "aclk_vepu");
+ if (IS_ERR(aclk_vepu)) {
+ pr_err("failed on clk_get aclk_vepu\n");
+ }
hclk_vepu = clk_get(NULL, "hclk_vepu");
+ if (IS_ERR(hclk_vepu)) {
+ pr_err("failed on clk_get hclk_vepu\n");
+ }
aclk_ddr_vepu = clk_get(NULL, "aclk_ddr_vepu");
+ if (IS_ERR(aclk_ddr_vepu)) {
+ pr_err("failed on clk_get aclk_ddr_vepu\n");
+ }
hclk_cpu_vcodec = clk_get(NULL, "hclk_cpu_vcodec");
+ if (IS_ERR(hclk_cpu_vcodec)) {
+ pr_err("failed on clk_get hclk_cpu_vcodec\n");
+ }
}
static void vpu_put_clk(void)
{
clk_put(pd_video);
- clk_put(clk_vpu);
clk_put(aclk_vepu);
clk_put(hclk_vepu);
clk_put(aclk_ddr_vepu);
static void vpu_service_power_off(void)
{
int total_running;
-
- spin_lock_bh(&service.lock_power);
if (!service.enabled) {
- spin_unlock_bh(&service.lock_power);
return;
}
}
printk("vpu: power off...");
-#if defined(CONFIG_ARCH_RK29)
+#ifdef CONFIG_ARCH_RK29
pmu_set_power_domain(PD_VCODEC, false);
#else
clk_disable(pd_video);
clk_disable(aclk_ddr_vepu);
clk_disable(hclk_vepu);
clk_disable(aclk_vepu);
- clk_disable(clk_vpu);
+ wake_unlock(&service.wake_lock);
printk("done\n");
- spin_unlock_bh(&service.lock_power);
}
-static void vpu_service_power_off_work_func(unsigned long data)
+static inline void vpu_queue_power_off_work(void)
{
- printk("delayed ");
- vpu_service_power_off();
+ queue_delayed_work(system_nrt_wq, &service.power_off_work, VPU_POWER_OFF_DELAY);
}
-static void vpu_service_power_maintain(void)
+static void vpu_power_off_work(struct work_struct *work)
{
- if (service.enabled) {
- mod_timer(&service.timer, jiffies + POWER_OFF_DELAY);
+ if (mutex_trylock(&service.lock)) {
+ vpu_service_power_off();
+ mutex_unlock(&service.lock);
} else {
- pr_err("error: maintain power when power is off!\n");
+ /* Come back later if the device is busy... */
+ vpu_queue_power_off_work();
}
}
static void vpu_service_power_on(void)
{
- clk_enable(clk_vpu); /* notify vpu on without lock. */
+ static ktime_t last;
+ ktime_t now = ktime_get();
+ if (ktime_to_ns(ktime_sub(now, last)) > NSEC_PER_SEC) {
+ cancel_delayed_work_sync(&service.power_off_work);
+ vpu_queue_power_off_work();
+ last = now;
+ }
+ if (service.enabled)
+ return ;
- spin_lock_bh(&service.lock_power);
- if (!service.enabled) {
- service.enabled = true;
- printk("vpu: power on\n");
-
- clk_enable(clk_vpu);
- clk_enable(aclk_vepu);
- clk_enable(hclk_vepu);
- clk_enable(hclk_cpu_vcodec);
- udelay(10);
-#if defined(CONFIG_ARCH_RK29)
- pmu_set_power_domain(PD_VCODEC, true);
+ service.enabled = true;
+ printk("vpu: power on\n");
+
+ clk_enable(aclk_vepu);
+ clk_enable(hclk_vepu);
+ clk_enable(hclk_cpu_vcodec);
+ udelay(10);
+#ifdef CONFIG_ARCH_RK29
+ pmu_set_power_domain(PD_VCODEC, true);
#else
- clk_enable(pd_video);
+ clk_enable(pd_video);
#endif
- udelay(10);
- clk_enable(aclk_ddr_vepu);
- mod_timer(&service.timer, jiffies + POWER_OFF_DELAY);
- spin_unlock_bh(&service.lock_power);
- } else {
- spin_unlock_bh(&service.lock_power);
- vpu_service_power_maintain();
- }
+ udelay(10);
+ clk_enable(aclk_ddr_vepu);
+ wake_lock(&service.wake_lock);
+}
+
+static inline bool reg_check_rmvb_wmv(vpu_reg *reg)
+{
+ unsigned long type = (reg->reg[3] & 0xF0000000) >> 28;
+ return ((type == 8) || (type == 4));
+}
- clk_disable(clk_vpu);
+static inline bool reg_check_interlace(vpu_reg *reg)
+{
+ unsigned long type = (reg->reg[3] & (1 << 23));
+ return (type > 0);
}
static vpu_reg *reg_init(vpu_session *session, void __user *src, unsigned long size)
{
- unsigned long flag;
- vpu_reg *reg = kmalloc(sizeof(vpu_reg), GFP_KERNEL);
+ vpu_reg *reg = kmalloc(sizeof(vpu_reg)+service.reg_size, GFP_KERNEL);
if (NULL == reg) {
pr_err("error: kmalloc fail in reg_init\n");
return NULL;
reg->session = session;
reg->type = session->type;
reg->size = size;
+ reg->freq = VPU_FREQ_DEFAULT;
+ reg->reg = (unsigned long *)®[1];
INIT_LIST_HEAD(®->session_link);
INIT_LIST_HEAD(®->status_link);
return NULL;
}
- spin_lock_irqsave(&service.lock, flag);
+ mutex_lock(&service.lock);
list_add_tail(®->status_link, &service.waiting);
list_add_tail(®->session_link, &session->waiting);
- spin_unlock_irqrestore(&service.lock, flag);
+ mutex_unlock(&service.lock);
+
+ if (service.auto_freq) {
+ if (reg->type == VPU_DEC || reg->type == VPU_DEC_PP) {
+ if (reg_check_rmvb_wmv(reg)) {
+ reg->freq = VPU_FREQ_200M;
+ } else {
+ if (reg_check_interlace(reg)) {
+ reg->freq = VPU_FREQ_400M;
+ }
+ }
+ }
+ if (reg->type == VPU_PP) {
+ reg->freq = VPU_FREQ_400M;
+ }
+ }
return reg;
}
switch (reg->type) {
case VPU_ENC : {
service.reg_codec = NULL;
- reg_copy_from_hw(reg, enc_dev.hwregs, REG_NUM_ENC);
+ reg_copy_from_hw(reg, enc_dev.hwregs, service.hw_info->enc_reg_num);
break;
}
case VPU_DEC : {
service.reg_codec = NULL;
- reg_copy_from_hw(reg, dec_dev.hwregs, REG_NUM_DEC);
+ reg_copy_from_hw(reg, dec_dev.hwregs, REG_NUM_9190_DEC);
break;
}
case VPU_PP : {
service.reg_pproc = NULL;
- reg_copy_from_hw(reg, dec_dev.hwregs + PP_INTERRUPT_REGISTER, REG_NUM_PP);
+ reg_copy_from_hw(reg, dec_dev.hwregs + PP_INTERRUPT_REGISTER, REG_NUM_9190_PP);
dec_dev.hwregs[PP_INTERRUPT_REGISTER] = 0;
break;
}
case VPU_DEC_PP : {
service.reg_codec = NULL;
service.reg_pproc = NULL;
- reg_copy_from_hw(reg, dec_dev.hwregs, REG_NUM_DEC_PP);
+ reg_copy_from_hw(reg, dec_dev.hwregs, REG_NUM_9190_DEC_PP);
dec_dev.hwregs[PP_INTERRUPT_REGISTER] = 0;
break;
}
wake_up_interruptible_sync(®->session->wait);
}
-void reg_copy_to_hw(vpu_reg *reg)
+static void vpu_service_set_freq(vpu_reg *reg)
+{
+ VPU_FREQ curr = atomic_read(&service.freq_status);
+ if (curr == reg->freq) {
+ return ;
+ }
+ atomic_set(&service.freq_status, reg->freq);
+ switch (reg->freq) {
+ case VPU_FREQ_200M : {
+ clk_set_rate(aclk_vepu, 200*MHZ);
+ //printk("default: 200M\n");
+ } break;
+ case VPU_FREQ_266M : {
+ clk_set_rate(aclk_vepu, 266*MHZ);
+ //printk("default: 266M\n");
+ } break;
+ case VPU_FREQ_300M : {
+ clk_set_rate(aclk_vepu, 300*MHZ);
+ //printk("default: 300M\n");
+ } break;
+ case VPU_FREQ_400M : {
+ clk_set_rate(aclk_vepu, 400*MHZ);
+ //printk("default: 400M\n");
+ } break;
+ default : {
+ clk_set_rate(aclk_vepu, 300*MHZ);
+ //printk("default: 300M\n");
+ } break;
+ }
+}
+
+static void reg_copy_to_hw(vpu_reg *reg)
{
int i;
u32 *src = (u32 *)®->reg[0];
atomic_add(1, &service.total_running);
atomic_add(1, ®->session->task_running);
+ if (service.auto_freq) {
+ vpu_service_set_freq(reg);
+ }
switch (reg->type) {
case VPU_ENC : {
+ int enc_count = service.hw_info->enc_reg_num;
u32 *dst = (u32 *)enc_dev.hwregs;
#if defined(CONFIG_ARCH_RK30)
cru_set_soft_reset(SOFT_RST_CPU_VCODEC, true);
for (i = 0; i < VPU_REG_EN_ENC; i++)
dst[i] = src[i];
- for (i = VPU_REG_EN_ENC + 1; i < REG_NUM_ENC; i++)
+ for (i = VPU_REG_EN_ENC + 1; i < enc_count; i++)
dst[i] = src[i];
dsb();
dst[VPU_REG_ENC_GATE] = src[VPU_REG_ENC_GATE] | VPU_REG_ENC_GATE_BIT;
dst[VPU_REG_EN_ENC] = src[VPU_REG_EN_ENC];
+
+#if VPU_SERVICE_SHOW_TIME
+ do_gettimeofday(&enc_start);
+#endif
+
} break;
case VPU_DEC : {
u32 *dst = (u32 *)dec_dev.hwregs;
service.reg_codec = reg;
- for (i = REG_NUM_DEC - 1; i > VPU_REG_DEC_GATE; i--)
+ for (i = REG_NUM_9190_DEC - 1; i > VPU_REG_DEC_GATE; i--)
dst[i] = src[i];
dsb();
dst[VPU_REG_DEC_GATE] = src[VPU_REG_DEC_GATE] | VPU_REG_DEC_GATE_BIT;
dst[VPU_REG_EN_DEC] = src[VPU_REG_EN_DEC];
+
+#if VPU_SERVICE_SHOW_TIME
+ do_gettimeofday(&dec_start);
+#endif
+
} break;
case VPU_PP : {
u32 *dst = (u32 *)dec_dev.hwregs + PP_INTERRUPT_REGISTER;
dst[VPU_REG_PP_GATE] = src[VPU_REG_PP_GATE] | VPU_REG_PP_GATE_BIT;
- for (i = VPU_REG_PP_GATE + 1; i < REG_NUM_PP; i++)
+ for (i = VPU_REG_PP_GATE + 1; i < REG_NUM_9190_PP; i++)
dst[i] = src[i];
dsb();
dst[VPU_REG_EN_PP] = src[VPU_REG_EN_PP];
+
+#if VPU_SERVICE_SHOW_TIME
+ do_gettimeofday(&pp_start);
+#endif
+
} break;
case VPU_DEC_PP : {
u32 *dst = (u32 *)dec_dev.hwregs;
service.reg_codec = reg;
service.reg_pproc = reg;
- for (i = VPU_REG_EN_DEC_PP + 1; i < REG_NUM_DEC_PP; i++)
+ for (i = VPU_REG_EN_DEC_PP + 1; i < REG_NUM_9190_DEC_PP; i++)
dst[i] = src[i];
dst[VPU_REG_EN_DEC_PP] = src[VPU_REG_EN_DEC_PP] | 0x2;
dst[VPU_REG_DEC_PP_GATE] = src[VPU_REG_DEC_PP_GATE] | VPU_REG_PP_GATE_BIT;
dst[VPU_REG_DEC_GATE] = src[VPU_REG_DEC_GATE] | VPU_REG_DEC_GATE_BIT;
dst[VPU_REG_EN_DEC] = src[VPU_REG_EN_DEC];
+
+#if VPU_SERVICE_SHOW_TIME
+ do_gettimeofday(&dec_start);
+#endif
+
} break;
default : {
pr_err("error: unsupport session type %d", reg->type);
static void try_set_reg(void)
{
- unsigned long flag;
// first get reg from reg list
- spin_lock_irqsave(&service.lock, flag);
if (!list_empty(&service.waiting)) {
int can_set = 0;
vpu_reg *reg = list_entry(service.waiting.next, vpu_reg, status_link);
- vpu_service_power_maintain();
+ vpu_service_power_on();
+
switch (reg->type) {
case VPU_ENC : {
if ((NULL == service.reg_codec) && (NULL == service.reg_pproc))
- can_set = 1;
+ can_set = 1;
} break;
case VPU_DEC : {
if (NULL == service.reg_codec)
can_set = 1;
+ if (service.auto_freq && (NULL != service.reg_pproc)) {
+ can_set = 0;
+ }
} break;
case VPU_PP : {
if (NULL == service.reg_codec) {
} else {
if ((VPU_DEC == service.reg_codec->type) && (NULL == service.reg_pproc))
can_set = 1;
+ // can not charge frequency when vpu is working
+ if (service.auto_freq) {
+ can_set = 0;
+ }
}
} break;
case VPU_DEC_PP : {
reg_copy_to_hw(reg);
}
}
- spin_unlock_irqrestore(&service.lock, flag);
}
static int return_reg(vpu_reg *reg, u32 __user *dst)
int ret = 0;
switch (reg->type) {
case VPU_ENC : {
- if (copy_to_user(dst, ®->reg[0], SIZE_REG(REG_NUM_ENC)))
+ if (copy_to_user(dst, ®->reg[0], service.hw_info->enc_io_size))
ret = -EFAULT;
break;
}
case VPU_DEC : {
- if (copy_to_user(dst, ®->reg[0], SIZE_REG(REG_NUM_DEC)))
+ if (copy_to_user(dst, ®->reg[0], SIZE_REG(REG_NUM_9190_DEC)))
ret = -EFAULT;
break;
}
case VPU_PP : {
- if (copy_to_user(dst, ®->reg[0], SIZE_REG(REG_NUM_PP)))
+ if (copy_to_user(dst, ®->reg[0], SIZE_REG(REG_NUM_9190_PP)))
ret = -EFAULT;
break;
}
case VPU_DEC_PP : {
- if (copy_to_user(dst, ®->reg[0], SIZE_REG(REG_NUM_DEC_PP)))
+ if (copy_to_user(dst, ®->reg[0], SIZE_REG(REG_NUM_9190_DEC_PP)))
ret = -EFAULT;
break;
}
if (NULL == reg) {
return -EFAULT;
} else {
- vpu_service_power_on();
+ mutex_lock(&service.lock);
try_set_reg();
+ mutex_unlock(&service.lock);
}
break;
case VPU_IOC_GET_REG : {
vpu_request req;
vpu_reg *reg;
- unsigned long flag;
if (copy_from_user(&req, (void __user *)arg, sizeof(vpu_request))) {
pr_err("error: VPU_IOC_GET_REG copy_from_user failed\n");
return -EFAULT;
} else {
- int ret = wait_event_interruptible_timeout(session->wait, !list_empty(&session->done), TIMEOUT_DELAY);
+ int ret = wait_event_interruptible_timeout(session->wait, !list_empty(&session->done), VPU_TIMEOUT_DELAY);
if (!list_empty(&session->done)) {
if (ret < 0) {
pr_err("warning: pid %d wait task sucess but wait_evernt ret %d\n", session->pid, ret);
ret = -ETIMEDOUT;
}
}
- spin_lock_irqsave(&service.lock, flag);
if (ret < 0) {
int task_running = atomic_read(&session->task_running);
+ mutex_lock(&service.lock);
vpu_service_dump();
if (task_running) {
atomic_set(&session->task_running, 0);
printk("done\n");
}
vpu_service_session_clear(session);
- spin_unlock_irqrestore(&service.lock, flag);
+ mutex_unlock(&service.lock);
return ret;
}
- spin_unlock_irqrestore(&service.lock, flag);
}
- spin_lock_irqsave(&service.lock, flag);
+ mutex_lock(&service.lock);
reg = list_entry(session->done.next, vpu_reg, session_link);
return_reg(reg, (u32 __user *)req.req);
- spin_unlock_irqrestore(&service.lock, flag);
+ mutex_unlock(&service.lock);
break;
}
default : {
return 0;
}
-static int vpu_service_check_hw_id(struct vpu_device * dev, const u16 *hwids, size_t num)
+static int vpu_service_check_hw(vpu_service_info *p, unsigned long hw_addr)
{
- u32 hwid = readl(dev->hwregs);
- pr_info("HW ID = 0x%08x\n", hwid);
-
- hwid = (hwid >> 16) & 0xFFFF; /* product version only */
-
- while (num--) {
- if (hwid == hwids[num]) {
- pr_info("Compatible HW found at 0x%08lx\n", dev->iobaseaddr);
- return 1;
+ int ret = -EINVAL, i = 0;
+ volatile u32 *tmp = (volatile u32 *)ioremap_nocache(hw_addr, 0x4);
+ u32 enc_id = *tmp;
+ enc_id = (enc_id >> 16) & 0xFFFF;
+ pr_info("checking hw id %x\n", enc_id);
+ p->hw_info = NULL;
+ for (i = 0; i < ARRAY_SIZE(vpu_hw_set); i++) {
+ if (enc_id == vpu_hw_set[i].hw_id) {
+ p->hw_info = &vpu_hw_set[i];
+ ret = 0;
+ break;
}
}
-
- pr_info("No Compatible HW found at 0x%08lx\n", dev->iobaseaddr);
- return 0;
+ iounmap((void *)tmp);
+ return ret;
}
static void vpu_service_release_io(void)
{
- if (dec_dev.hwregs)
+ if (dec_dev.hwregs) {
iounmap((void *)dec_dev.hwregs);
- release_mem_region(dec_dev.iobaseaddr, dec_dev.iosize);
+ dec_dev.hwregs = NULL;
+ }
+ if (dec_dev.iobaseaddr) {
+ release_mem_region(dec_dev.iobaseaddr, dec_dev.iosize);
+ dec_dev.iobaseaddr = 0;
+ dec_dev.iosize = 0;
+ }
- if (enc_dev.hwregs)
+ if (enc_dev.hwregs) {
iounmap((void *)enc_dev.hwregs);
- release_mem_region(enc_dev.iobaseaddr, enc_dev.iosize);
+ enc_dev.hwregs = NULL;
+ }
+ if (enc_dev.iobaseaddr) {
+ release_mem_region(enc_dev.iobaseaddr, enc_dev.iosize);
+ enc_dev.iobaseaddr = 0;
+ enc_dev.iosize = 0;
+ }
}
static int vpu_service_reserve_io(void)
goto err;
}
- /* check for correct HW */
- if (!vpu_service_check_hw_id(&dec_dev, dec_hw_ids, ARRAY_SIZE(dec_hw_ids))) {
- goto err;
- }
-
iobaseaddr = enc_dev.iobaseaddr;
iosize = enc_dev.iosize;
- if (!request_mem_region(iobaseaddr, iosize, "hx280enc")) {
+ if (!request_mem_region(iobaseaddr, iosize, "vepu_io")) {
pr_info("failed to reserve enc HW regs\n");
goto err;
}
goto err;
}
- /* check for correct HW */
- if (!vpu_service_check_hw_id(&enc_dev, enc_hw_ids, ARRAY_SIZE(enc_hw_ids))) {
- goto err;
- }
return 0;
err:
- vpu_service_release_io();
return -EBUSY;
}
static int vpu_service_open(struct inode *inode, struct file *filp)
{
- unsigned long flag;
vpu_session *session = (vpu_session *)kmalloc(sizeof(vpu_session), GFP_KERNEL);
if (NULL == session) {
pr_err("error: unable to allocate memory for vpu_session.");
INIT_LIST_HEAD(&session->list_session);
init_waitqueue_head(&session->wait);
atomic_set(&session->task_running, 0);
- spin_lock_irqsave(&service.lock, flag);
+ mutex_lock(&service.lock);
list_add_tail(&session->list_session, &service.session);
filp->private_data = (void *)session;
- spin_unlock_irqrestore(&service.lock, flag);
+ mutex_unlock(&service.lock);
pr_debug("dev opened\n");
return nonseekable_open(inode, filp);
static int vpu_service_release(struct inode *inode, struct file *filp)
{
int task_running;
- unsigned long flag;
vpu_session *session = (vpu_session *)filp->private_data;
if (NULL == session)
return -EINVAL;
}
wake_up_interruptible_sync(&session->wait);
- spin_lock_irqsave(&service.lock, flag);
+ mutex_lock(&service.lock);
/* remove this filp from the asynchronusly notified filp's */
- //vpu_service_fasync(-1, filp, 0);
list_del_init(&session->list_session);
vpu_service_session_clear(session);
kfree(session);
filp->private_data = NULL;
- spin_unlock_irqrestore(&service.lock, flag);
+ mutex_unlock(&service.lock);
pr_debug("dev closed\n");
return 0;
.fops = &vpu_service_fops,
};
-static void vpu_service_shutdown(struct platform_device *pdev)
-{
- pr_cont("shutdown...");
- del_timer(&service.timer);
- vpu_service_power_off();
- pr_cont("done\n");
-}
-
-static int vpu_service_suspend(struct platform_device *pdev, pm_message_t state)
-{
- bool enabled;
- pr_info("suspend...");
- del_timer(&service.timer);
- enabled = service.enabled;
- vpu_service_power_off();
- service.enabled = enabled;
- return 0;
-}
-
-static int vpu_service_resume(struct platform_device *pdev)
-{
- pr_info("resume...");
- if (service.enabled) {
- service.enabled = false;
- vpu_service_power_on();
- try_set_reg();
- }
- return 0;
-}
-
static struct platform_device vpu_service_device = {
.name = "vpu_service",
.id = -1,
.name = "vpu_service",
.owner = THIS_MODULE,
},
- .shutdown = vpu_service_shutdown,
- .suspend = vpu_service_suspend,
- .resume = vpu_service_resume,
};
static void get_hw_info(void)
enc->jpegEnabled = (configReg >> 25) & 1;
enc->vsEnabled = (configReg >> 24) & 1;
enc->rgbEnabled = (configReg >> 28) & 1;
- enc->busType = (configReg >> 20) & 15;
- enc->synthesisLanguage = (configReg >> 16) & 15;
- enc->busWidth = (configReg >> 12) & 15;
+ //enc->busType = (configReg >> 20) & 15;
+ //enc->synthesisLanguage = (configReg >> 16) & 15;
+ //enc->busWidth = (configReg >> 12) & 15;
+ enc->reg_size = service.reg_size;
+ enc->reserv[0] = enc->reserv[1] = 0;
+
+ service.auto_freq = soc_is_rk2928g() || soc_is_rk2928l() || soc_is_rk2926();
+ if (service.auto_freq) {
+ printk("vpu_service set to auto frequency mode\n");
+ atomic_set(&service.freq_status, VPU_FREQ_BUT);
+ }
}
-static irqreturn_t vdpu_isr(int irq, void *dev_id)
+static irqreturn_t vdpu_irq(int irq, void *dev_id)
{
vpu_device *dev = (vpu_device *) dev_id;
- u32 irq_status_dec = readl(dev->hwregs + DEC_INTERRUPT_REGISTER);
- u32 irq_status_pp = readl(dev->hwregs + PP_INTERRUPT_REGISTER);
+ u32 irq_status = readl(dev->hwregs + DEC_INTERRUPT_REGISTER);
- pr_debug("vdpu_isr dec %x pp %x\n", irq_status_dec, irq_status_pp);
+ pr_debug("vdpu_irq\n");
- if (irq_status_dec & DEC_INTERRUPT_BIT) {
- irq_status_dec = readl(dev->hwregs + DEC_INTERRUPT_REGISTER);
- if ((irq_status_dec & 0x40001) == 0x40001)
+ if (irq_status & DEC_INTERRUPT_BIT) {
+ pr_debug("vdpu_isr dec %x\n", irq_status);
+ if ((irq_status & 0x40001) == 0x40001)
{
do {
- irq_status_dec = readl(dev->hwregs + DEC_INTERRUPT_REGISTER);
- } while ((irq_status_dec & 0x40001) == 0x40001);
+ irq_status = readl(dev->hwregs + DEC_INTERRUPT_REGISTER);
+ } while ((irq_status & 0x40001) == 0x40001);
}
/* clear dec IRQ */
- writel(irq_status_dec & (~DEC_INTERRUPT_BIT), dev->hwregs + DEC_INTERRUPT_REGISTER);
- pr_debug("DEC IRQ received!\n");
- spin_lock(&service.lock);
+ writel(irq_status & (~DEC_INTERRUPT_BIT), dev->hwregs + DEC_INTERRUPT_REGISTER);
+ atomic_add(1, &dev->irq_count_codec);
+ }
+
+ irq_status = readl(dev->hwregs + PP_INTERRUPT_REGISTER);
+ if (irq_status & PP_INTERRUPT_BIT) {
+ pr_debug("vdpu_isr pp %x\n", irq_status);
+ /* clear pp IRQ */
+ writel(irq_status & (~DEC_INTERRUPT_BIT), dev->hwregs + PP_INTERRUPT_REGISTER);
+ atomic_add(1, &dev->irq_count_pp);
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t vdpu_isr(int irq, void *dev_id)
+{
+ vpu_device *dev = (vpu_device *) dev_id;
+
+ mutex_lock(&service.lock);
+ if (atomic_read(&dev->irq_count_codec)) {
+#if VPU_SERVICE_SHOW_TIME
+ do_gettimeofday(&dec_end);
+ printk("dec task: %ld ms\n",
+ (dec_end.tv_sec - dec_start.tv_sec) * 1000 +
+ (dec_end.tv_usec - dec_start.tv_usec) / 1000);
+#endif
+ atomic_sub(1, &dev->irq_count_codec);
if (NULL == service.reg_codec) {
pr_err("error: dec isr with no task waiting\n");
} else {
reg_from_run_to_done(service.reg_codec);
}
- spin_unlock(&service.lock);
}
- if (irq_status_pp & PP_INTERRUPT_BIT) {
- /* clear pp IRQ */
- writel(irq_status_pp & (~DEC_INTERRUPT_BIT), dev->hwregs + PP_INTERRUPT_REGISTER);
- pr_debug("PP IRQ received!\n");
- spin_lock(&service.lock);
+ if (atomic_read(&dev->irq_count_pp)) {
+
+#if VPU_SERVICE_SHOW_TIME
+ do_gettimeofday(&pp_end);
+ printk("pp task: %ld ms\n",
+ (pp_end.tv_sec - pp_start.tv_sec) * 1000 +
+ (pp_end.tv_usec - pp_start.tv_usec) / 1000);
+#endif
+
+ atomic_sub(1, &dev->irq_count_pp);
if (NULL == service.reg_pproc) {
pr_err("error: pp isr with no task waiting\n");
} else {
reg_from_run_to_done(service.reg_pproc);
}
- spin_unlock(&service.lock);
}
try_set_reg();
+ mutex_unlock(&service.lock);
return IRQ_HANDLED;
}
-static irqreturn_t vepu_isr(int irq, void *dev_id)
+static irqreturn_t vepu_irq(int irq, void *dev_id)
{
struct vpu_device *dev = (struct vpu_device *) dev_id;
u32 irq_status = readl(dev->hwregs + ENC_INTERRUPT_REGISTER);
- pr_debug("enc_isr\n");
+ pr_debug("vepu_irq irq status %x\n", irq_status);
+
+#if VPU_SERVICE_SHOW_TIME
+ do_gettimeofday(&enc_end);
+ printk("enc task: %ld ms\n",
+ (enc_end.tv_sec - enc_start.tv_sec) * 1000 +
+ (enc_end.tv_usec - enc_start.tv_usec) / 1000);
+#endif
if (likely(irq_status & ENC_INTERRUPT_BIT)) {
/* clear enc IRQ */
writel(irq_status & (~ENC_INTERRUPT_BIT), dev->hwregs + ENC_INTERRUPT_REGISTER);
- pr_debug("ENC IRQ received!\n");
- spin_lock(&service.lock);
+ atomic_add(1, &dev->irq_count_codec);
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t vepu_isr(int irq, void *dev_id)
+{
+ struct vpu_device *dev = (struct vpu_device *) dev_id;
+
+ mutex_lock(&service.lock);
+ if (atomic_read(&dev->irq_count_codec)) {
+ atomic_sub(1, &dev->irq_count_codec);
if (NULL == service.reg_codec) {
pr_err("error: enc isr with no task waiting\n");
} else {
reg_from_run_to_done(service.reg_codec);
}
- spin_unlock(&service.lock);
}
try_set_reg();
+ mutex_unlock(&service.lock);
return IRQ_HANDLED;
}
{
int ret;
- pr_debug("baseaddr = 0x%08x vdpu irq = %d vepu irq = %d\n", RK29_VCODEC_PHYS, IRQ_VDPU, IRQ_VEPU);
-
- dec_dev.iobaseaddr = RK29_VCODEC_PHYS + DEC_PHY_OFFSET;
- dec_dev.iosize = DEC_IO_SIZE;
- enc_dev.iobaseaddr = RK29_VCODEC_PHYS;
- enc_dev.iosize = ENC_IO_SIZE;
+ pr_debug("baseaddr = 0x%08x vdpu irq = %d vepu irq = %d\n", VCODEC_PHYS, IRQ_VDPU, IRQ_VEPU);
+ wake_lock_init(&service.wake_lock, WAKE_LOCK_SUSPEND, "vpu");
INIT_LIST_HEAD(&service.waiting);
INIT_LIST_HEAD(&service.running);
INIT_LIST_HEAD(&service.done);
INIT_LIST_HEAD(&service.session);
- spin_lock_init(&service.lock);
- spin_lock_init(&service.lock_power);
+ mutex_init(&service.lock);
service.reg_codec = NULL;
service.reg_pproc = NULL;
atomic_set(&service.total_running, 0);
service.enabled = false;
vpu_get_clk();
- init_timer(&service.timer);
- service.timer.expires = jiffies + POWER_OFF_DELAY;
- service.timer.function = vpu_service_power_off_work_func;
+
+ INIT_DELAYED_WORK(&service.power_off_work, vpu_power_off_work);
+
vpu_service_power_on();
+ ret = vpu_service_check_hw(&service, VCODEC_PHYS);
+ if (ret < 0) {
+ pr_err("error: hw info check faild\n");
+ goto err_hw_id_check;
+ }
+
+ atomic_set(&dec_dev.irq_count_codec, 0);
+ atomic_set(&dec_dev.irq_count_pp, 0);
+ dec_dev.iobaseaddr = service.hw_info->hw_addr + service.hw_info->dec_offset;
+ dec_dev.iosize = service.hw_info->dec_io_size;
+ atomic_set(&enc_dev.irq_count_codec, 0);
+ atomic_set(&enc_dev.irq_count_pp, 0);
+ enc_dev.iobaseaddr = service.hw_info->hw_addr + service.hw_info->enc_offset;
+ enc_dev.iosize = service.hw_info->enc_io_size;;
+ service.reg_size = max(dec_dev.iosize, enc_dev.iosize);
ret = vpu_service_reserve_io();
if (ret < 0) {
}
/* get the IRQ line */
- ret = request_irq(IRQ_VDPU, vdpu_isr, IRQF_SHARED, "vdpu", (void *)&dec_dev);
+ ret = request_threaded_irq(IRQ_VDPU, vdpu_irq, vdpu_isr, IRQF_SHARED, "vdpu", (void *)&dec_dev);
if (ret) {
pr_err("error: can't request vdpu irq %d\n", IRQ_VDPU);
goto err_req_vdpu_irq;
}
- ret = request_irq(IRQ_VEPU, vepu_isr, IRQF_SHARED, "vepu", (void *)&enc_dev);
+ ret = request_threaded_irq(IRQ_VEPU, vepu_irq, vepu_isr, IRQF_SHARED, "vepu", (void *)&enc_dev);
if (ret) {
pr_err("error: can't request vepu irq %d\n", IRQ_VEPU);
goto err_req_vepu_irq;
err_req_vdpu_irq:
pr_info("init failed\n");
err_reserve_io:
- vpu_service_power_off();
vpu_service_release_io();
+err_hw_id_check:
+ vpu_service_power_off();
vpu_put_clk();
+ wake_lock_destroy(&service.wake_lock);
pr_info("init failed\n");
return ret;
}
+static void __exit vpu_service_proc_release(void);
static void __exit vpu_service_exit(void)
{
- del_timer(&service.timer);
+ vpu_service_proc_release();
vpu_service_power_off();
platform_device_unregister(&vpu_service_device);
platform_driver_unregister(&vpu_service_driver);
misc_deregister(&vpu_service_misc_device);
free_irq(IRQ_VEPU, (void *)&enc_dev);
free_irq(IRQ_VDPU, (void *)&dec_dev);
+ vpu_service_release_io();
vpu_put_clk();
+ wake_lock_destroy(&service.wake_lock);
}
module_init(vpu_service_init);
static int proc_vpu_service_show(struct seq_file *s, void *v)
{
unsigned int i, n;
- unsigned long flag;
vpu_reg *reg, *reg_tmp;
vpu_session *session, *session_tmp;
+ mutex_lock(&service.lock);
vpu_service_power_on();
seq_printf(s, "\nENC Registers:\n");
n = enc_dev.iosize >> 2;
}
seq_printf(s, "\nvpu service status:\n");
- spin_lock_irqsave(&service.lock, flag);
list_for_each_entry_safe(session, session_tmp, &service.session, list_session) {
seq_printf(s, "session pid %d type %d:\n", session->pid, session->type);
//seq_printf(s, "waiting reg set %d\n");
seq_printf(s, "done register set\n");
}
}
- spin_unlock_irqrestore(&service.lock, flag);
+ mutex_unlock(&service.lock);
return 0;
}
return 0;
}
+static void __exit vpu_service_proc_release(void)
+{
+ remove_proc_entry("vpu_service", NULL);
+}
#endif /* CONFIG_PROC_FS */