⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fr400cc_vdc.c

📁 一个linux内核编程文件
💻 C
📖 第 1 页 / 共 3 页
字号:
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(&regio, 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, &regio, 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(&regio, 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(&regio, (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,
				 &regio,
				 sizeof(struct __fr400cc_vdc_regio)))
			retval = -EFAULT;
	    }
	    break;

	case __VDCIOCSREGIO:
	    {
		struct __fr400cc_vdc_regio regio;

		if (copy_from_user(&regio, (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 + -