📄 fr400cc_vdc.c
字号:
static ssize_t
vdc_read_que(struct rbuf_que *q,
wait_queue_head_t *waitq,
struct semaphore *sem,
struct file *file,
char *buffer, size_t count, loff_t *ppos)
{
int sz, retval;
unsigned long flags;
struct fr400vdc_read read_pack;
sz = sizeof(struct fr400vdc_read);
if(count < sz){
return -EINVAL;
}
down(sem);
local_irq_save(flags);
local_irq_disable();
if((file->f_flags & O_NONBLOCK) == 0){
while(q->num == 0){
#if defined(CONFIG_MB93091_CB30) || defined(CONFIG_FR400PDK2_BOARD)
interruptible_sleep_on(waitq);
#else
sleep_on(waitq);
#endif
local_irq_disable();
}
}
spin_unlock(&fr400vdc_lock);
local_irq_restore(flags);
retval = 0;
while(q->num > 0){
if(count < sz) break;
if(!rbuf_que_get(q, &read_pack)) break;
if(copy_to_user(buffer, &read_pack, sz)){
retval = -EFAULT;
break;
}
buffer += sz;
count -= sz;
retval += sz;
}
up(sem);
return retval == 0 ? -EAGAIN : retval;
}
static ssize_t
vdc_read(struct file *file,
char *buffer, size_t count, loff_t *ppos)
{
return vdc_read_que(&rbuf_vdc, &vdc_waitq, &vdc_sem,
file, buffer, count, ppos);
}
static unsigned int
vdc_poll_que(struct rbuf_que *q, wait_queue_head_t *waitq,
struct file *file, poll_table *wait)
{
unsigned long flags;
int comp;
spin_lock_irqsave(&fr400vdc_lock, flags);
comp = (q->num > 0);
spin_unlock_irqrestore(&fr400vdc_lock, flags);
if(comp){
return POLLIN | POLLRDNORM;
}
poll_wait(file, waitq, wait);
spin_lock_irqsave(&fr400vdc_lock, flags);
comp = (q->num > 0);
spin_unlock_irqrestore(&fr400vdc_lock, flags);
if(comp){
return POLLIN | POLLRDNORM;
}
return 0;
}
static unsigned int
vdc_poll(struct file *file, poll_table *wait)
{
return vdc_poll_que(&rbuf_vdc, &vdc_waitq, file, wait);
}
static int
vdc_release(struct inode *inode, struct file *file)
{
int retval;
lock_kernel();
vdc_stop2(0);
if((retval = dma_close()) < 0){
unlock_kernel();
return retval;
}
irq_close();
if(vdc_count > 0) vdc_count--;
unlock_kernel();
#if 0 /* debug */
printk("intr cnt %d (vsync %d, under %d), dma intr cnt %d\n",
vdc_intr_cnt,
vdc_intr_cnt_vsync,
vdc_intr_cnt_under,
vdc_dma_intr_cnt);
#endif
return 0;
}
static int
vdc_mmap(struct file *file, struct vm_area_struct *vma)
{
int ret;
ret = -EINVAL;
lock_kernel();
if(vma->vm_start == 0 &&
vma->vm_end <= sizeof(mmap_area) &&
vma->vm_offset == 0){
ret = (int)mmap_area;
}
unlock_kernel();
return ret;
}
static int
vdc_start(unsigned long arg)
#if defined(CONFIG_MB93091_CB30) || defined(CONFIG_FR400PDK2_BOARD)
{
return vdc_start_inner(arg, 0);
}
static int
vdc_start_inner(unsigned long arg, int from_intr)
#endif
{
int ch;
unsigned v;
#if defined(CONFIG_MB93091_CB30) || defined(CONFIG_FR400PDK2_BOARD)
unsigned long flags = 0;
#else
unsigned long flags;
#endif
#if defined(CONFIG_MB93091_CB30) || defined(CONFIG_FR400PDK2_BOARD)
if(!from_intr){
#endif
spin_lock_irqsave(&fr400vdc_lock, flags);
#if defined(CONFIG_MB93091_CB30) || defined(CONFIG_FR400PDK2_BOARD)
}
#endif
vdc_stop2(0);
ch = VDC_DMA_CH;
#if VDC_CFG_MOD
v = (vdc_config.dma_mode << CCFR_CM_SHIFT) |
(vdc_config.dma_ats << CCFR_ATS_SHIFT) |
(vdc_config.dma_rs << CCFR_RS_SHIFT);
#else
v = (CCFR_CM_2D << CCFR_CM_SHIFT) |
(2 << CCFR_ATS_SHIFT) |
(CCFR_RS_EXT << CCFR_RS_SHIFT);
#endif
dmac_reg_write(ch, CCFR, v);
#if defined(CONFIG_MB93091_CB30) || defined(CONFIG_FR400PDK2_BOARD)
if(!from_intr){
frm_count = 0;
rbuf_que_clear(&rbuf_vdc);
}
#else
frm_count = 0;
rbuf_que_clear(&rbuf_vdc);
#endif
#if defined(CONFIG_MB93091_CB30) || defined(CONFIG_FR400PDK2_BOARD)
if(!from_intr){
#endif
if(Q_EMP){ /* no data */
spin_unlock_irqrestore(&fr400vdc_lock, flags);
printk("vdc: no data\n");
return -1; /* !!! */
}
play_idx = Q_RD;
Q_INC_R;
if(Q_TERM(play_idx)){
spin_unlock_irqrestore(&fr400vdc_lock, flags);
printk("vdc: no data (trem?)\n");
return -1; /* !!! */
}
#if defined(CONFIG_MB93091_CB30) || defined(CONFIG_FR400PDK2_BOARD)
}
#endif
vdc_dfi = 0;
dma_set_2d(ch, area_get(play_idx));
vdc_go(0);
#if defined(CONFIG_MB93091_CB30) || defined(CONFIG_FR400PDK2_BOARD)
if(!from_intr){
#endif
spin_unlock_irqrestore(&fr400vdc_lock, flags);
#if defined(CONFIG_MB93091_CB30) || defined(CONFIG_FR400PDK2_BOARD)
}
#endif
return 0;
}
static int
vdc_stop(void)
{
if(vdc_config.stop_immidiate){
vdc_stop2(0);
}else{
Q_TERM_SET;
}
return 0;
}
static int
vdc_set_data(unsigned long arg)
{
int buf_idx;
buf_idx = arg;
if(Q_FULL) return -1; /* !!! */
Q_WR(buf_idx);
Q_INC_W;
return 0;
}
static int
regio_offset_chk(unsigned offset)
{
return (
(0x000 <= offset && offset < 0x100) ||
(0x140 <= offset && offset < 0x180) ||
(0x1c0 <= offset && offset < 0x1e0) ) ? 0 : -1;
}
static int
vdc_regio_get(struct fr400cc_vdc_regio *regio_u)
{
struct fr400cc_vdc_regio regio;
if(copy_from_user(®io, regio_u, sizeof(regio))) return -EFAULT;
if((regio.reg_offset & 3) != 0) return -EINVAL;
if(regio_offset_chk(regio.reg_offset) != 0) return -EINVAL;
regio.value = read_fr400cc_register(VDC_BASE + regio.reg_offset);
if(copy_to_user(regio_u, ®io, sizeof(regio))) return -EFAULT;
return 0;
}
static int
vdc_regio_set(struct fr400cc_vdc_regio *regio_u)
{
struct fr400cc_vdc_regio regio;
if(copy_from_user(®io, regio_u, sizeof(regio))) return -EFAULT;
if((regio.reg_offset & 3) != 0) return -EINVAL;
if(regio_offset_chk(regio.reg_offset) != 0) return -EINVAL;
write_fr400cc_register(VDC_BASE + regio.reg_offset, regio.value);
return 0;
}
static int
vdc_test(unsigned long arg)
{
int retval, ch;
unsigned v;
unsigned long flags;
v = read_fr400cc_register(VDC_RS);
printk("vdc: test rs=%08x\n", v);
ch = VDC_DMA_CH;
v = dmac_reg_read(ch, PIX);
printk("vdc: pix=%d\n", v);
v = dmac_reg_read(ch, SIX);
printk("vdc: six=%d\n", v);
v = dmac_reg_read(ch, BCL);
printk("vdc: bc=%d\n", v);
retval = 0;
spin_lock_irqsave(&fr400vdc_lock, flags);
if(copy_to_user((int *)arg,
&frm_count,
sizeof(frm_count))){
retval = -EFAULT;
}
spin_unlock_irqrestore(&fr400vdc_lock, flags);
return retval;
}
static int
vdc_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int retval;
retval = 0;
switch(cmd){
case VDCIOCGCFG:
if(copy_to_user((struct fr400cc_vdc_config *)arg,
&vdc_config,
sizeof(vdc_config))){
retval = -EFAULT;
}
break;
case VDCIOCSCFG:
if(copy_from_user(&vdc_config,
(struct fr400cc_vdc_config *)arg,
sizeof(vdc_config))){
retval = -EFAULT;
}
#if UNDERRUN_NORESET
underrun_reset = (vdc_config.skipbf & 4) ? 1 : 0;
vdc_config.skipbf &= ~4;
#endif
buf_size_adjust();
break;
case VDCIOCSTART:
retval = vdc_start(arg);
break;
case VDCIOCSTOP:
retval = vdc_stop();
break;
case VDCIOCSDAT:
retval = vdc_set_data(arg);
break;
#ifdef CONFIG_FR400CC_VDC_VSYNC_REPORT
case VDCIOCVSSTART:
rbuf_que_clear(&rbuf_vsync);
vdc_go(1);
break;
case VDCIOCVSSTOP:
vdc_stop2(1);
break;
#endif /* CONFIG_FR400CC_VDC_VSYNC_REPORT */
case VDCIOCGREGIO:
retval = vdc_regio_get((struct fr400cc_vdc_regio *)arg);
break;
case VDCIOCSREGIO:
retval = vdc_regio_set((struct fr400cc_vdc_regio *)arg);
break;
case VDCIOCTEST:
retval = vdc_test(arg);
break;
#ifdef FR400CC_VDC_DEBUG_IOCTL
case __VDCIOCGREGIO:
{
struct __fr400cc_vdc_regio regio;
if (copy_from_user(®io, (struct __fr400cc_vdc_regio *)arg,
sizeof(struct __fr400cc_vdc_regio))){
retval = -EFAULT;
break;
}
regio.value = read_fr400cc_register(regio.reg_no);
if (copy_to_user((struct __fr400cc_vdc_regio *)arg,
®io,
sizeof(struct __fr400cc_vdc_regio)))
retval = -EFAULT;
}
break;
case __VDCIOCSREGIO:
{
struct __fr400cc_vdc_regio regio;
if (copy_from_user(®io, (struct __fr400cc_vdc_regio *)arg,
sizeof(struct __fr400cc_vdc_regio))){
retval = -EFAULT;
break;
}
write_fr400cc_register(regio.reg_no, regio.value);
}
break;
#endif
default:
retval = -ENOIOCTLCMD;
break;
}
return retval;
}
static struct file_operations fr400cc_vdc_fops={
owner: THIS_MODULE,
open: vdc_open,
read: vdc_read,
poll: vdc_poll,
release: vdc_release,
mmap: vdc_mmap,
ioctl: vdc_ioctl,
};
static struct miscdevice fr400cc_vdc_miscdev={
FR400CC_VDC_MINOR,
"fr400cc vdc",
&fr400cc_vdc_fops
};
#ifdef CONFIG_FR400CC_VDC_VSYNC_REPORT
static int
vdc_vsync_open(struct inode *inode, struct file *file)
{
lock_kernel();
if(vdc_vsync_count){
unlock_kernel();
return -EBUSY;
}
if(vdc_count <= 0){
vdc_config = default_config;
rbuf_que_clear(&rbuf_vsync);
}
if(irq_open() != 0){
printk("vdc vsync: open, request_irq NG.\n");
dma_close();
unlock_kernel();
return -1;
}
#ifdef CONFIG_FR400CC_VDC_VSYNC_AUTO
vdc_go(1);
#endif
vdc_vsync_count++;
unlock_kernel();
return 0;
}
static int
vdc_vsync_release(struct inode *inode, struct file *file)
{
lock_kernel();
#ifdef CONFIG_FR400CC_VDC_VSYNC_AUTO
vdc_stop2(1);
#endif
irq_close();
if(vdc_vsync_count > 0) vdc_vsync_count--;
unlock_kernel();
return 0;
}
static unsigned int
vdc_vsync_poll(struct file *file, poll_table *wait)
{
return vdc_poll_que(&rbuf_vsync, &vdc_vsync_waitq,
file, wait);
}
static ssize_t
vdc_vsync_read(struct file *file,
char *buffer, size_t count, loff_t *ppos)
{
return vdc_read_que(&rbuf_vsync, &vdc_vsync_waitq,
&vdc_vsync_sem,
file, buffer, count, ppos);
}
static struct file_operations fr400cc_vdc_vsync_fops={
owner: THIS_MODULE,
open: vdc_vsync_open,
read: vdc_vsync_read,
poll: vdc_vsync_poll,
release: vdc_vsync_release,
};
static struct miscdevice fr400cc_vdc_vsync_miscdev={
FR400CC_VDC_VSYNC_MINOR,
"fr400cc vdc vsync",
&fr400cc_vdc_vsync_fops
};
#endif /* CONFIG_FR400CC_VDC_VSYNC_REPORT */
static void
vdc_irq_setup(void)
{
unsigned v, vdc_pat;
int i, n;
vdc_kirq = kirqs[VDC_FR400CC_IRQ];
vdc_pat = (1<<16); /* E0 */
n = sizeof(IQSRs) / sizeof(IQSRs[0]);
for(i=0; i<n; i++){
v = read_fr400cc_register(IQSRs[i]);
if(i == VDC_FR400CC_IRQ) v |= vdc_pat;
else v &= ~ vdc_pat;
write_fr400cc_register(IQSRs[i], v);
}
}
static void
vdc_dma_setup(void)
{
unsigned v, vdc_pat;
int i, n;
vdc_pat = (1<<16); /* E0 */
n = sizeof(DQSRs) / sizeof(DQSRs[0]);
for(i=0; i<n; i++){
v = read_fr400cc_register(DQSRs[i]);
if(i == VDC_DMA_CH) v |= vdc_pat;
else v &= ~ vdc_pat;
write_fr400cc_register(DQSRs[i], v);
}
}
static int __init fr400cc_vdc_init(void)
{
#if defined(CONFIG_MB93091_CB30) || defined(CONFIG_FR400PDK2_BOARD)
unsigned v, mask;
v = read_fr400cc_register(LBUS_LBSER);
v |= 1<<23; /* E7 GPIO */
v |= 1<<16; /* E0 VDC */
write_fr400cc_register(LBUS_LBSER, v);
v = read_fr400cc_register(GPIO_SOR0);
mask = ((1<<8)-1)<<16;
v &= ~mask;
v |= mask;
write_fr400cc_register(GPIO_SOR0, v);
v = read_fr400cc_register(GPIO_SOR1);
mask = ((1<<8)-1)<<8;
v &= ~mask;
v |= mask;
write_fr400cc_register(GPIO_SOR1, v);
#endif
init_waitqueue_head(&vdc_waitq);
misc_register(&fr400cc_vdc_miscdev);
write_fr400cc_register(LBUS_STSR0, LBUS_STSR_EN);
/* VDC DMA burst enable */
vdc_irq_setup();
vdc_dma_setup();
printk("FR400 Companion Chip VDC driver (irq=%d dreq=%d)\n",
vdc_kirq, VDC_DMA_CH);
vdc_reset();
#ifdef CONFIG_FR400CC_VDC_VSYNC_REPORT
init_waitqueue_head(&vdc_vsync_waitq);
misc_register(&fr400cc_vdc_vsync_miscdev);
printk("FR400 Companion Chip VDC VSYNC report driver\n");
#endif
return 0;
}
__initcall(fr400cc_vdc_init);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -