multimedia/device/mpp/extdrv/piris/piris.c

420 lines
9.1 KiB
C

/* isp/piris.c
*
* Copyright (c) 2006 Hisilicon Co., Ltd.
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program;
*
* History:
* 23-march-2011 create this file
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/fcntl.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/workqueue.h>
#include <linux/hrtimer.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#include "piris.h"
#include "piris_ext.h"
#define PIRISI_ADRESS_BASE 0x20210000 // Piris use GPIO13
void __iomem* reg_pirisI_base_va = 0;
#define HI_IO_PIRISI_ADDRESS(x) (reg_pirisI_base_va + ((x)-(PIRISI_ADRESS_BASE)))
#define PIRIS_CFG_REG HI_IO_PIRISI_ADDRESS(PIRISI_ADRESS_BASE + 0x007C)
#define PIRIS_CTRL_REG HI_IO_PIRISI_ADDRESS(PIRISI_ADRESS_BASE + 0x0400)
#define PIRIS_WRITE_REG(Addr, Value) ((*(volatile unsigned int *)(Addr)) = (Value))
#define PIRIS_READ_REG(Addr) (*(volatile unsigned int *)(Addr))
#define MAX(a, b) (a > b? a : b)
#define MIN(a, b) (a < b? a : b)
#define MAX_MOTOR_PAHSE 4
#define MAX_STEPS 92
#define PIRIS_PPS 100
static const unsigned char motor_phase_tbl[MAX_MOTOR_PAHSE] = { 0x0, 0x1, 0x2, 0x3};
typedef struct hiPIRIS_DEV
{
int src_pos;
int dest_pos;
unsigned int pps;
int phase;
const unsigned char* phase_tbl;
struct semaphore sem;
struct timer_list timer;
} PIRIS_DEV;
static PIRIS_DEV* p_piris_dev;
DECLARE_COMPLETION(piris_comp);
int piris_gpio_update(int* pPirisPos)
{
p_piris_dev->dest_pos = *pPirisPos;
p_piris_dev->pps = PIRIS_PPS;
p_piris_dev->pps = MAX(MIN(p_piris_dev->pps, HZ), 1);
p_piris_dev->timer.expires = jiffies + HZ / p_piris_dev->pps;
/* whether piris timer already at the kerbel timer pending list */
if (p_piris_dev->timer.entry.next != NULL)
{
return -1;
}
add_timer(&p_piris_dev->timer);
return 0;
}
/* first go to the full open iris step, set the full open as origin */
int piris_origin_set(PIRIS_DATA_S* pstPirisData)
{
int piris_pos;
piris_pos = pstPirisData->CurPos;
piris_gpio_update(&piris_pos);
// wait for piris origin done
init_completion(&piris_comp);
wait_for_completion(&piris_comp);
if (pstPirisData->ZeroIsMax == 1)
{
p_piris_dev->src_pos = 0;
p_piris_dev->dest_pos = 0;
}
else
{
p_piris_dev->src_pos = pstPirisData->TotalStep - 1;
p_piris_dev->dest_pos = pstPirisData->TotalStep - 1;
}
return 0;
}
/* go to the full close iris step */
int piris_close_set(PIRIS_DATA_S* pstPirisData)
{
int piris_pos;
piris_pos = pstPirisData->CurPos;
piris_gpio_update(&piris_pos);
// wait for piris origin done
init_completion(&piris_comp);
wait_for_completion(&piris_comp);
if (pstPirisData->ZeroIsMax == 1)
{
p_piris_dev->src_pos = pstPirisData->TotalStep - 1;
p_piris_dev->dest_pos = pstPirisData->TotalStep - 1;
}
else
{
p_piris_dev->src_pos = 0;
p_piris_dev->dest_pos = 0;
}
return 0;
}
/* file operation */
int piris_open(struct inode* inode, struct file* file)
{
file->private_data = p_piris_dev;
return 0 ;
}
int piris_close(struct inode* inode, struct file* file)
{
return 0;
}
#if 0
static int PIRIS_DRV_Disable(void)
{
PIRIS_WRITE_REG(PIRIS_CTRL_REG, 0x1F);
PIRIS_WRITE_REG(PIRIS_CFG_REG, 0x10);
return 0;
}
#endif
int PIRIS_DRV_Write(unsigned char bits)
{
switch (bits)
{
case 0:
PIRIS_WRITE_REG(PIRIS_CTRL_REG, 0x1F);
PIRIS_WRITE_REG(PIRIS_CFG_REG , 0x15);
break;
case 1:
PIRIS_WRITE_REG(PIRIS_CTRL_REG, 0x1F);
PIRIS_WRITE_REG(PIRIS_CFG_REG , 0x16);
break;
case 2:
PIRIS_WRITE_REG(PIRIS_CTRL_REG, 0x1F);
PIRIS_WRITE_REG(PIRIS_CFG_REG , 0x1A);
break;
case 3:
PIRIS_WRITE_REG(PIRIS_CTRL_REG, 0x1F);
PIRIS_WRITE_REG(PIRIS_CFG_REG , 0x19);
break;
default:
break;
}
return 0;
}
static long piris_ioctl(struct file* file, unsigned int cmd, unsigned long arg)
{
int __user* pPirisPos;
PIRIS_DATA_S __user* argp;
PIRIS_STATUS_E __user* pPirisStatus;
PIRIS_DEV* pstPirisDev = (PIRIS_DEV*) file->private_data;
int err = 0;
if (_IOC_TYPE(cmd) != PIRIS_IOC_MAGIC)
{
return -ENOTTY;
}
if (_IOC_NR(cmd) > PIRIS_IOC_MAXNR)
{
return -ENOTTY;
}
if (_IOC_DIR(cmd) & _IOC_READ)
{
err = !access_ok(VERIFY_WRITE, (void __user*)arg, _IOC_SIZE(cmd));
}
else if (_IOC_DIR(cmd) & _IOC_WRITE)
{
err = !access_ok(VERIFY_READ, (void __user*)arg, _IOC_SIZE(cmd));
}
if (err)
{
return -EFAULT;
}
// lock pstPirisDev
if (down_interruptible(&pstPirisDev->sem))
{
return -ERESTARTSYS;
}
switch (cmd)
{
case PIRIS_SET_ACT_ARGS:
pPirisPos = (int __user*)arg;
piris_gpio_update(pPirisPos);
break;
case PIRIS_SET_ORGIN:
argp = (PIRIS_DATA_S __user*)arg;
piris_origin_set(argp);
break;
case PIRIS_SET_CLOSE:
argp = (PIRIS_DATA_S __user*)arg;
piris_close_set(argp);
break;
case PIRIS_GET_STATUS:
pPirisStatus = (PIRIS_STATUS_E __user*)arg;
if (pstPirisDev->dest_pos != pstPirisDev->src_pos)
{
*pPirisStatus = PIRIS_BUSY;
}
else
{
*pPirisStatus = PIRIS_IDLE;
//PIRIS_DRV_Disable();
}
break;
default: /* redundant, as cmd was checked against MAXNR */
break;
}
// unlock pstPirisDev
up(&pstPirisDev->sem);
return 0 ;
}
static struct file_operations piris_fops =
{
.owner = THIS_MODULE,
.unlocked_ioctl = piris_ioctl ,
.open = piris_open ,
.release = piris_close ,
};
static struct miscdevice gstPirisDev =
{
.minor = MISC_DYNAMIC_MINOR,
.name = "piris" ,
.fops = &piris_fops,
};
void piris_timer_cb(unsigned long arg)
{
int sign = 1;
unsigned char bits;
PIRIS_DEV* pstPirisDev = (PIRIS_DEV*)arg;
if (pstPirisDev->src_pos == pstPirisDev->dest_pos)
{
return ;
}
sign = (pstPirisDev->dest_pos - pstPirisDev->src_pos < 0) ? -1 : 1;
pstPirisDev->src_pos += sign;
// close iris: 0->1->2->3->0; open iris: 3->2->1->0->3
pstPirisDev->phase = (pstPirisDev->phase + MAX_MOTOR_PAHSE + sign) % MAX_MOTOR_PAHSE;
bits = pstPirisDev->phase_tbl[pstPirisDev->phase];
PIRIS_DRV_Write(bits);
if (pstPirisDev->dest_pos == pstPirisDev->src_pos)
{
complete(&piris_comp);
}
pstPirisDev->timer.expires = jiffies + HZ / pstPirisDev->pps;
add_timer(&pstPirisDev->timer);
//printk("%s, pos :%d @ pps:%d\n", __FUNCTION__, pstPirisDev->src_pos, pstPirisDev->pps);
return ;
}
static int hi_piris_isp_register(void)
{
ISP_PIRIS_CALLBACK_S stPirisCb = {0};
stPirisCb.pfn_piris_gpio_update = (HI_S32 (*)(HI_S32))piris_gpio_update;
if (CKFN_ISP_RegisterPirisCallBack())
{
CALL_ISP_RegisterPirisCallBack(0, &stPirisCb);
}
else
{
printk("register piris_gpio_write_callback to isp failed, hi_piris init is failed!\n");
return -1;
}
return 0;
}
/* module init and exit */
static int __init piris_init(void)
{
int ret;
p_piris_dev = kmalloc(sizeof(PIRIS_DEV), GFP_KERNEL);
if (!p_piris_dev)
{
return -1;
}
memset(p_piris_dev, 0x0, sizeof(PIRIS_DEV));
sema_init(&p_piris_dev->sem, 1);
init_completion(&piris_comp);
// init timer
init_timer(&p_piris_dev->timer);
p_piris_dev->timer.function = piris_timer_cb;
p_piris_dev->timer.data = (unsigned long)p_piris_dev;
p_piris_dev->timer.expires = jiffies + HZ; /* one second */
p_piris_dev->phase_tbl = motor_phase_tbl;
reg_pirisI_base_va = ioremap_nocache(PIRISI_ADRESS_BASE, 0x10000);
ret = misc_register(&gstPirisDev);
hi_piris_isp_register();
if (ret != 0)
{
printk("register piris device failed with %#x!\n", ret);
return -1;
}
return 0;
}
static void __exit piris_exit(void)
{
del_timer(&p_piris_dev->timer);
kfree(p_piris_dev);
misc_deregister(&gstPirisDev);
}
module_init(piris_init);
module_exit(piris_exit);
MODULE_DESCRIPTION("piris driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("hisilicon");