SmartAudio/lichee/linux-4.9/drivers/media/platform/sunxi-eve/eve.c

444 lines
12 KiB
C
Executable File

/*
* drivers\media\platform\eve.c
* (C) Copyright 2016-2017
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* xiongyi<xiongyi@allwinnertech.com>
*
* Driver for embedded vision engine(EVE).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
*/
#include "eve.h"
#include <linux/signal.h>
#define EVE_INPUT_CLK 24
#define EVE_TIME_DEFAULT 0x80002701
#define EVE_REG_LEN 0x40
#define BIT_HIGH_8 0xff00
#define BIT0 0x1
#define BIT1 0x2
#define BIT2 0x4
/* volatile deleted here*/
static unsigned int *eve_regs_init;
static unsigned int *eve_regs_temp;
static struct fasync_struct *async_queue;
static unsigned long eve_end_flag, eve_int_flag;
static unsigned long eve_sig_sel;
static unsigned char if_error, if_run;
static unsigned long eve_irq_id;
static unsigned int startTime[5] = {0, 0, 0, 0, 0};
static struct dentry *debugfsP;
static struct clk *eve_clk;
static unsigned long time_reg_value = EVE_TIME_DEFAULT;
static void eve_open_clk(struct eve_register *regP, int en)
{
struct eve_register reg = *regP;
unsigned long rate;
if (reg.value == 0x0)
reg.value = time_reg_value;
else
time_reg_value = reg.value;
rate = EVE_INPUT_CLK * (1 + ((reg.value&BIT_HIGH_8)>>8));
rate /= 1 + ((reg.value&BIT1)>>1);
rate /= 1 + (reg.value&BIT0);
if (clk_set_rate(eve_clk, rate*1000000) < 0)
printk(KERN_INFO "[EVE]: Set clk failed, use default!\n");
if (en > 0) {
if (clk_prepare_enable(eve_clk) < 0) {
printk(KERN_INFO "[EVE]: Open clk failed!\n");
return;
}
sunxi_periph_reset_assert(eve_clk);
sunxi_periph_reset_deassert(eve_clk);
if_error = 1;
writel(0x0000001f, eve_regs_init);
writel(0x0000001c, eve_regs_init + 0x01);
writel(0x00000000, eve_regs_init + 0x01);
mdelay(5);
writel(0x00000020, eve_regs_init + 0x01);
writel(0x00000000, eve_regs_init + 0x01);
}
}
static void eve_close_clk(void)
{
sunxi_periph_reset_assert(eve_clk);
sunxi_periph_reset_deassert(eve_clk);
if_error = 1;
writel(0x0000001f, eve_regs_init);
writel(0x0000001c, eve_regs_init + 0x01);
writel(0x00000000, eve_regs_init + 0x01);
mdelay(5);
writel(0x00000020, eve_regs_init + 0x01);
writel(0x00000000, eve_regs_init + 0x01);
clk_disable_unprepare(eve_clk);
}
static void eve_show_regs(void)
{
int i = 0;
unsigned long rData = 0;
for (i = 0; i < EVE_REG_LEN; i++) {
rData = readl(eve_regs_init + i);
printk(KERN_INFO "Addr = %#010x, Value = %#010x\n",
(unsigned int)(eve_regs_init + i), (unsigned int)rData);
}
}
static void eve_save_regs(void)
{
int i = 0;
unsigned long rData = 0;
printk(KERN_INFO "[EVE]: Save!\n");
eve_show_regs();
eve_regs_temp = (unsigned int *)kmalloc(EVE_REG_LEN*sizeof(unsigned int), \
GFP_KERNEL);
if (IS_ERR_OR_NULL(eve_regs_temp)) {
printk(KERN_INFO "[EVE]: Error: malloc failed!\n");
return;
}
for (i = 1; i < EVE_REG_LEN; i++) {
rData = readl(eve_regs_init + i);
eve_regs_temp[i] = rData;
}
}
static void eve_recover_regs(void)
{
int i = 0;
if (IS_ERR_OR_NULL(eve_regs_temp))
return;
for (i = 1; i < EVE_REG_LEN; i++)
writel(eve_regs_temp[i], eve_regs_init + i);
kfree(eve_regs_temp);
eve_regs_temp = NULL;
printk(KERN_INFO "[EVE]: recover!\n");
eve_show_regs();
}
static irqreturn_t eve_interrupt(unsigned long data)
{
unsigned long now_int, int_mask;
int_mask = readl(eve_regs_init + 0x01);
int_mask = ((int_mask&0x03)<<3) | ((int_mask&0x1C00)>>10);
now_int = readl(eve_regs_init);
writel(now_int, eve_regs_init);
eve_int_flag |= (now_int & int_mask);
if (eve_int_flag > 0x07) {
writel(0x3f, eve_regs_init);
printk(KERN_INFO "[EVE]: Time out!\n");
eve_int_flag = 0;
eve_save_regs();
sunxi_periph_reset_assert(eve_clk);
sunxi_periph_reset_deassert(eve_clk);
eve_recover_regs();
goto SAFE_RETURN;
} else
if_error = 0;
if ((((eve_int_flag & 0x04) > 0) ||
(eve_int_flag >= eve_end_flag)) && async_queue) {
startTime[eve_int_flag&BIT0]++;
startTime[eve_int_flag&BIT1]++;
startTime[eve_int_flag&BIT2]++;
eve_sig_sel = eve_int_flag;
eve_int_flag &= (~eve_sig_sel);
eve_end_flag &= (~eve_sig_sel);
SAFE_RETURN:
kill_fasync(&async_queue, SIGIO, POLL_IN);
}
return IRQ_HANDLED;
}
static int eve_fasync(int fd, struct file *filp, int mode)
{
return fasync_helper(fd, filp, mode, &async_queue);
}
static int evedev_open(struct inode *node, struct file *filp)
{
if_run = 1;
return 0;
}
static int evedev_close(struct inode *node, struct file *filp)
{
if_run = 0;
return eve_fasync(-1, filp, 0);
}
static int eve_write(struct file *filp,
const char __user *buf,
size_t count,
loff_t *ppos)
{
if (copy_from_user(&eve_end_flag,
(void __user *)buf,
sizeof(unsigned long)))
return -EFAULT;
eve_end_flag &= 0x07;
eve_sig_sel = 0x00;
return 0;
}
static int eve_read(struct file *filp,
char __user *buf,
size_t count,
loff_t *ppos)
{
if (copy_to_user((char *)buf, &eve_sig_sel, sizeof(unsigned long)))
return -EFAULT;
return 0;
}
static long eve_ioctl(struct file *filp,
unsigned int cmd,
unsigned long arg)
{
struct eve_register reg;
if (unlikely(copy_from_user(&reg, (void __user *)arg,
sizeof(struct eve_register)))) {
return -EFAULT;
}
BACK_POINT:
if (likely(cmd == EVE_WRITE_REGISTER)) {
writel(reg.value, eve_regs_init + (reg.addr>>2));
return 0;
} else if (cmd == EVE_READ_RESNUM) {
return readl(eve_regs_init + (reg.addr>>2));
}
switch (cmd) {
case EVE_READ_REGISTER:
return readl(eve_regs_init + (reg.addr>>2));
case EVE_OPEN_CLK:
eve_open_clk(&reg, 1);
break;
case EVE_SYS_RESET:
sunxi_periph_reset_assert(eve_clk);
sunxi_periph_reset_deassert(eve_clk);
break;
case EVE_MOD_RESET:
if_error = 1;
writel(0x0000001f, eve_regs_init);
writel(0x0000001c, eve_regs_init + 0x01);
writel(0x00000000, eve_regs_init + 0x01);
mdelay(5);
writel(0x00000020, eve_regs_init + 0x01);
writel(0x00000000, eve_regs_init + 0x01);
goto BACK_POINT;
case EVE_CLOSE_CLK:
eve_close_clk();
break;
case EVE_PLL_SET:
eve_open_clk(&reg, -1);
break;
default:
break;
}
return 0;
}
static int sunxi_eve_show(struct seq_file *m, void *v)
{
unsigned long rdata1, rdata2, rdata3, rdata4;
seq_printf(m, "[sunxi-eve] IP Version: %#010x\n",
readl(eve_regs_init + (0x0c>>2)));
seq_printf(m, "----------------MODULE STATUS---------------\n");
/*
seq_printf(m, " DMA SF BOOST Time out IRQ\n");
seq_printf(m, " %ld %ld %ld %d %ld\n\n",
-1*((eve_int_flag&0x10)>>4) + 1*((eve_int_flag&0x4)>>2),
-1*((eve_int_flag&0x8)>>3) + 1*((eve_int_flag&0x2)>>1),
-1*((eve_int_flag&0x8)>>3) + 1*(eve_int_flag&0x1),
(if_error == 2)?1 : 0,
eve_sig_sel);
*/
seq_printf(m, "----------------MODULE PARAM---------------\n");
seq_printf(m, " eve_end_flag \n");
seq_printf(m, " %#010lx\n\n", eve_end_flag);
seq_printf(m, "-------------MODULE INT TIMEs-------------\n");
seq_printf(m, " DMA SF BOOST\n");
seq_printf(m, " %d %d %d\n\n",
startTime[4], startTime[2], startTime[1]);
seq_printf(m, "---------------TASK SIZE INFO--------------\n");
seq_printf(m, " Module Name Input ROI Output Channel Layels\n");
rdata1 = readl(eve_regs_init + (0x10>>2));
rdata2 = readl(eve_regs_init + (0x14>>2));
rdata3 = readl(eve_regs_init + (0x18>>2));
seq_printf(m, " <DMA> %ld x %ld %ld x %ld %ld x %ld %d %d\n",
((rdata1&0xffff0000)>>16), rdata1&0xffff,
((rdata2&0xffff0000)>>16), rdata2&0xffff,
((rdata3&0xffff0000)>>16), rdata3&0xffff,
2, 1);
rdata1 = readl(eve_regs_init + (0x38>>2));
rdata2 = readl(eve_regs_init + (0x3c>>2));
rdata3 = readl(eve_regs_init + (0x40>>2));
rdata4 = readl(eve_regs_init + (0x80>>2));
seq_printf(m, " <SF> %ld x %ld %ld x %ld %ld x %ld %ld %ld\n",
((rdata1&0xffff0000)>>16), rdata1&0xffff,
((rdata2&0xffff0000)>>16), rdata2&0xffff,
((rdata3&0xffff0000)>>16), rdata3&0xffff,
rdata4&0x7, ((rdata4&0x3f00)>>8));
rdata1 = readl(eve_regs_init + (0x88>>2));
rdata4 = readl(eve_regs_init + (0xa4>>2));
seq_printf(m, " <BOOST> %ld x %ld - %ld %ld\n",
((rdata1&0xffff0000)>>16), rdata1&0xffff,
((rdata4&0x1c)>>2), ((rdata4&0x3f0000)>>16)+1);
return 0;
}
static int sunxi_eve_open(struct inode *inode,
struct file *file)
{
return single_open(file, sunxi_eve_show, NULL);
}
static int eve_suspend(struct platform_device *pdev,
pm_message_t state){
printk(KERN_INFO "[EVE]: Time for sleep!\n");
if (if_run > 0) {
eve_save_regs();
eve_close_clk();
}
return 0;
}
static int eve_resume(struct platform_device *pdev)
{
struct eve_register reg;
printk(KERN_INFO "[EVE]: Weak up!\n");
if (if_run > 0) {
reg.addr = 0x030010c0;
reg.value = 0x0;
eve_open_clk(&reg, 1);
eve_recover_regs();
kill_fasync(&async_queue, SIGIO, POLL_IN);
}
return 0;
}
static const struct file_operations eve_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = eve_ioctl,
.fasync = eve_fasync,
.read = eve_read,
.write = eve_write,
.open = evedev_open,
.release = evedev_close
};
static const struct file_operations eve_proc_fops = {
.owner = THIS_MODULE,
.open = sunxi_eve_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static struct miscdevice eve_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &eve_fops
};
static struct of_device_id eve_of_match[] = {
{ .compatible = "allwinner,sunxi-aie-eve", },
{}
};
MODULE_DEVICE_TABLE(of, eve_of_match);
static struct platform_driver eve_pm_driver = {
.driver = {
.name = "EVE_PM",
.owner = THIS_MODULE,
.of_match_table = eve_of_match,
},
.suspend = eve_suspend,
.resume = eve_resume,
};
static int __init eve_init(void)
{
struct device_node *node;
int r = misc_register(&eve_dev);
if (r >= 0)
printk(KERN_INFO "EVE device had been registered! %d\n", r);
else
printk(KERN_INFO "EVE device register failed! %d\n", r);
r = platform_driver_probe(&eve_pm_driver, NULL);
if (r >= 0)
printk(KERN_INFO "EVE pm driver had been registered! %d\n", r);
else
printk(KERN_INFO "EVE pm driver register failed! %d\n", r);
node = of_find_compatible_node(NULL, NULL,
"allwinner,sunxi-aie-eve");
eve_irq_id = irq_of_parse_and_map(node, 0);
eve_clk = of_clk_get(node, 1);
if ((!eve_clk) || IS_ERR(eve_clk)) {
printk(KERN_INFO "[EVE]: Error, can't find clk!\n");
return -EFAULT;
}
r = request_irq(eve_irq_id, (irq_handler_t)eve_interrupt, 0,
DEVICE_NAME, NULL);
if (r < 0) {
printk(KERN_INFO "Request EVE Irq error! return %d\n", r);
return -EFAULT;
}
printk(KERN_INFO "Request EVE Irq success! return %d, irq_id = %ld\n",
r, eve_irq_id);
eve_regs_init = (unsigned int *)of_iomap(node, 0);
printk(KERN_INFO "[EVE] remap from %#010x to %#010lx\n", 0x01500000,
(unsigned long)eve_regs_init);
startTime[0] = 0; startTime[1] = 0;
startTime[2] = 0; startTime[3] = 0; startTime[4] = 0;
debugfsP = debugfs_create_file("sunxi-eve", 0444, NULL, NULL,
&eve_proc_fops);
if (IS_ERR_OR_NULL(debugfsP))
printk(KERN_INFO "Warning: Fail to create debugfs node!\n");
eve_end_flag = 0;
if_run = 0;
return 0;
}
static void __exit eve_exit(void)
{
free_irq(eve_irq_id, NULL);
iounmap(eve_regs_init);
clk_put(eve_clk);
platform_driver_unregister(&eve_pm_driver);
misc_deregister(&eve_dev);
printk(KERN_INFO "EVE device has been removed!\n");
if (!IS_ERR_OR_NULL(debugfsP))
debugfs_remove(debugfsP);
};
module_init(eve_init);
module_exit(eve_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("XiongYi<xiongyi@allwinnertech.com>");
MODULE_DESCRIPTION("EVE device driver");
MODULE_VERSION("1.0");
MODULE_ALIAS("platform:EVE(AW1721)");