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

📄 planb.c

📁 microwindows移植到S3C44B0的源码
💻 C
📖 第 1 页 / 共 5 页
字号:
/*     planb - v4l-compatible frame grabber driver for the PlanB hardware    PlanB is used in the 7x00/8x00 series of PowerMacintosh    Computers as video input DMA controller.    Copyright (C) 1998 - 2002  Michel Lanners <mailto:mlan@cpu.lu>    Based largely on the old bttv driver by Ralph Metzler    Additional debugging and coding by Takashi Oe <mailto:toe@unlserve.unl.edu>    For more information, see <http://www.cpu.lu/~mlan/planb.html>    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; if not, write to the Free Software    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*//* $Id: planb.c,v 2.11 2002/04/03 15:57:57 mlan Exp mlan $ */#include <linux/version.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/slab.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/vmalloc.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/poll.h>#include <linux/wrapper.h>#include <linux/tqueue.h>#include <linux/videodev.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/prom.h>#include <asm/dbdma.h>#include <asm/pgtable.h>#include <asm/page.h>#include <asm/irq.h>#include <asm/semaphore.h>/* Define these to get general / interrupt debugging */#undef DEBUG#undef IDEBUG//#define DEBUG#ifdef DEBUG#define DBG(x...) printk(KERN_DEBUG ## x)#else#define DBG(x...)#endif#ifdef IDEBUG#define IDBG(x...) printk(KERN_DEBUG ## x)#else#define IDBG(x...)#endif#include "planb.h"#include "saa7196.h"static struct planb planbs;static volatile struct planb_registers *planb_regs;static int def_norm = PLANB_DEF_NORM;	/* default norm */static int video_nr = -1;static int vbi_nr = -1;MODULE_PARM(def_norm, "i");MODULE_PARM_DESC(def_norm, "Default startup norm (0=PAL, 1=NTSC, 2=SECAM)");MODULE_PARM(video_nr,"i");MODULE_PARM(vbi_nr,"i");MODULE_DESCRIPTION("planb - v4l driver module for Apple PlanB video in");MODULE_AUTHOR("Michel Lanners & Takashi Oe - see: http://www.cpu.lu/planb.html");MODULE_LICENSE("GPL");/* ------------------ PlanB Exported Functions ------------------ */static long planb_write(struct video_device *, const char *, unsigned long, int);static long planb_read(struct video_device *, char *, unsigned long, int);static int planb_open(struct video_device *, int);static void planb_close(struct video_device *);static int planb_ioctl(struct video_device *, unsigned int, void *);static int planb_mmap(struct video_device *, const char *, unsigned long);static void planb_irq(int, void *, struct pt_regs *);static int planb_vbi_open(struct video_device *, int);static void planb_vbi_close(struct video_device *);static long planb_vbi_read(struct video_device *, char *, unsigned long, int);static unsigned int planb_vbi_poll(struct video_device *, struct file *,	poll_table *);static int planb_vbi_ioctl(struct video_device *, unsigned int, void *);static void release_planb(void);static int __init init_planbs(void);static void __exit exit_planbs(void);/* ------------------ PlanB Internal Functions ------------------ */static int planb_prepare_open(struct planb *);static int planb_prepare_vbi(struct planb *);static int planb_prepare_video(struct planb *);static void planb_prepare_close(struct planb *);static void planb_close_vbi(struct planb *);static void planb_close_video(struct planb *);static void saa_write_reg(unsigned char, unsigned char);static unsigned char saa_status(int, struct planb *);static void saa_set(unsigned char, unsigned char, struct planb *);static void saa_init_regs(struct planb *);static int grabbuf_alloc(struct planb *);static int vgrab(struct planb *, struct video_mmap *);static void add_clip(struct planb *, struct video_clip *);static void fill_cmd_buff(struct planb *);static void cmd_buff(struct planb *);static dbdma_cmd_ptr setup_grab_cmd(int, struct planb *);static void overlay_start(struct planb *);static void overlay_stop(struct planb *);static inline void tab_cmd_dbdma(dbdma_cmd_ptr, unsigned short, unsigned int);static inline void tab_cmd_store(dbdma_cmd_ptr, unsigned int, unsigned int);static inline void tab_cmd_gen(dbdma_cmd_ptr, unsigned short, unsigned short,	unsigned int, unsigned int);static int init_planb(struct planb *);static int find_planb(void);static void planb_pre_capture(int, struct planb *);static dbdma_cmd_ptr cmd_geo_setup(dbdma_cmd_ptr, int, int, int, int, int,	struct planb *);static inline void planb_dbdma_stop(dbdma_regs_ptr);static inline void planb_dbdma_restart(dbdma_regs_ptr);static void saa_geo_setup(int, int, int, int, struct planb *);static inline int overlay_is_active(struct planb *);/*******************************//* Memory management functions *//*******************************//* I know this is not the right way to allocate memory. Whoever knows * the right way to allocate a huge buffer for DMA that can be mapped * to user space, please tell me... or better, fix the code and send * patches. * *					Michel Lanners (mlan@cpu.lu) *//* FIXME: As subsequent calls to __get_free_pages don't necessarily return * contiguous pages, we need to do horrible things later on when setting * up DMA, to make sure a single DMA transfer doesn't cross a page boundary. * At least, I hope it's done right later on ;-) ...... * Anyway, there should be a way to get hold of a large buffer of contiguous * pages for DMA.... */static int grabbuf_alloc(struct planb *pb){	int i, npage;	npage = MAX_GBUFFERS * ((PLANB_MAX_FBUF / PAGE_SIZE + 1)#ifndef PLANB_GSCANLINE		+ MAX_LNUM#endif /* PLANB_GSCANLINE */		);	if ((pb->rawbuf = (unsigned char**) kmalloc (npage				* sizeof(unsigned long), GFP_KERNEL)) == 0)		return -ENOMEM;	for (i = 0; i < npage; i++) {		pb->rawbuf[i] = (unsigned char *)__get_free_pages(GFP_KERNEL |								GFP_DMA, 0);		if (!pb->rawbuf[i])			break;		mem_map_reserve(virt_to_page(pb->rawbuf[i]));	}	if (i-- < npage) {		DBG("PlanB: init_grab: grab buffer not allocated\n");		for (; i > 0; i--) {			mem_map_unreserve(virt_to_page(pb->rawbuf[i]));			free_pages((unsigned long)pb->rawbuf[i], 0);		}		kfree(pb->rawbuf);		return -ENOBUFS;	}	pb->rawbuf_nchunks = npage;	return 0;}/*****************************//* Hardware access functions *//*****************************/static void saa_write_reg(unsigned char addr, unsigned char val){	planb_regs->saa_addr = addr; eieio();	planb_regs->saa_regval = val; eieio();	return;}/* return  status byte 0 or 1: */static unsigned char saa_status(int byte, struct planb *pb){	saa_regs[pb->win.norm][SAA7196_STDC] =		(saa_regs[pb->win.norm][SAA7196_STDC] & ~2) | ((byte & 1) << 1);	saa_write_reg (SAA7196_STDC, saa_regs[pb->win.norm][SAA7196_STDC]);	/* Let's wait 30msec for this one */	current->state = TASK_INTERRUPTIBLE;	schedule_timeout(30 * HZ / 1000);	return (unsigned char)in_8 (&planb_regs->saa_status);}static void saa_set(unsigned char addr, unsigned char val, struct planb *pb){	if(saa_regs[pb->win.norm][addr] != val) {		saa_regs[pb->win.norm][addr] = val;		saa_write_reg (addr, val);	}	return;}static void saa_init_regs(struct planb *pb){	int i;	for (i = 0; i < SAA7196_NUMREGS; i++)		saa_write_reg (i, saa_regs[pb->win.norm][i]);}static void saa_geo_setup(int width, int height, int interlace,	int fmt, struct planb *pb){	int ht, norm = pb->win.norm;	/* bits FS0, FS1 according to format spec */	saa_regs[norm][SAA7196_FMTS] &= ~0x3;	saa_regs[norm][SAA7196_FMTS] |= (palette2fmt[fmt].saa_fmt & 0x3);	ht = (interlace ? height / 2 : height);	saa_regs[norm][SAA7196_OUTPIX] = (unsigned char) (width & 0x00ff);	saa_regs[norm][SAA7196_HFILT] = (saa_regs[norm][SAA7196_HFILT] & ~0x3)						| (width >> 8 & 0x3);	saa_regs[norm][SAA7196_OUTLINE] = (unsigned char) (ht & 0xff);	saa_regs[norm][SAA7196_VYP] = (saa_regs[norm][SAA7196_VYP] & ~0x3)						| (ht >> 8 & 0x3);	/* feed both fields if interlaced, or else feed only even fields */	saa_regs[norm][SAA7196_FMTS] = (interlace) ?					(saa_regs[norm][SAA7196_FMTS] & ~0x60)					: (saa_regs[norm][SAA7196_FMTS] | 0x60);	/* transparent mode; extended format enabled */	saa_regs[norm][SAA7196_DPATH] |= 0x3;	/* bits LLV, MCT according to format spec */	saa_regs[norm][SAA7196_DPATH] &= ~0x30;	saa_regs[norm][SAA7196_DPATH] |= (palette2fmt[fmt].saa_fmt & 0x30);}/***************************//* DBDMA support functions *//***************************/static inline void planb_dbdma_restart(dbdma_regs_ptr ch){	writel(PLANB_CLR(RUN), &ch->control);	writel(PLANB_SET(RUN|WAKE) | PLANB_CLR(PAUSE), &ch->control);}static inline void planb_dbdma_stop(dbdma_regs_ptr ch){	int i = 0;	writel(PLANB_CLR(RUN) | PLANB_SET(FLUSH), &ch->control);	while((readl(&ch->status) == (ACTIVE | FLUSH)) && (i < 999)) {		IDBG("PlanB: waiting for DMA to stop\n");		i++;	}}static inline void tab_cmd_dbdma(dbdma_cmd_ptr ch, unsigned short command,	unsigned int cmd_dep){	st_le16(&ch->command, command);	st_le16(&ch->req_count, 0);	st_le32(&ch->phy_addr, 0);	st_le32(&ch->cmd_dep, cmd_dep);	/* really clears res_count & xfer_status */	st_le32((unsigned int *)&ch->res_count, 0);}static inline void tab_cmd_store(dbdma_cmd_ptr ch, unsigned int phy_addr,	unsigned int cmd_dep){	st_le16(&ch->command, STORE_WORD | KEY_SYSTEM);	st_le16(&ch->req_count, 4);	st_le32(&ch->phy_addr, phy_addr);	st_le32(&ch->cmd_dep, cmd_dep);	st_le32((unsigned int *)&ch->res_count, 0);}static inline void tab_cmd_gen(dbdma_cmd_ptr ch, unsigned short command,	unsigned short req_count, unsigned int phy_addr, unsigned int cmd_dep){	st_le16(&ch->command, command);	st_le16(&ch->req_count, req_count);	st_le32(&ch->phy_addr, phy_addr);	st_le32(&ch->cmd_dep, cmd_dep);	st_le32((unsigned int *)&ch->res_count, 0);}static dbdma_cmd_ptr cmd_geo_setup(dbdma_cmd_ptr c1, int width, int height,	int interlace, int fmt, int clip, struct planb *pb){	int norm = pb->win.norm;	saa_geo_setup(width, height, interlace, fmt, pb);	/* if the number of DBDMA commands here (14) changes, lots of	 * things need to be corrected accordingly... */	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),								SAA7196_FMTS);	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),						saa_regs[norm][SAA7196_FMTS]);	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),								SAA7196_DPATH);	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),						saa_regs[norm][SAA7196_DPATH]);	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->even),			palette2fmt[fmt].pb_fmt | ((clip)? PLANB_CLIPMASK: 0));	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->odd),			palette2fmt[fmt].pb_fmt | ((clip)? PLANB_CLIPMASK: 0));	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),								SAA7196_OUTPIX);	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),						saa_regs[norm][SAA7196_OUTPIX]);	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),								SAA7196_HFILT);	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),						saa_regs[norm][SAA7196_HFILT]);	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),							SAA7196_OUTLINE);	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),					saa_regs[norm][SAA7196_OUTLINE]);	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),								SAA7196_VYP);	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),						saa_regs[norm][SAA7196_VYP]);	return c1;}/******************************//* misc. supporting functions *//******************************/static inline void planb_lock(struct planb *pb){	DBG("PlanB: planb_lock\n");	down(&pb->lock);}static inline void planb_unlock(struct planb *pb){	DBG("PlanB: planb_unlock\n");	up(&pb->lock);}/***************//* Driver Core *//***************//* number of entries in the circular DBDMA command buffer * initial stop, odd/even vbi, odd/even video, branch back */#define NUMJUMPS 6static int planb_prepare_open(struct planb *pb){	dbdma_cmd_ptr	c;	int		size, i;	size = (NUMJUMPS + 1) * sizeof(struct dbdma_cmd);	if((pb->jump_raw = kmalloc (size, GFP_KERNEL|GFP_DMA)) == 0)		return -ENOMEM;	memset(pb->jump_raw, 0, size);	c = pb->jumpbuf = (dbdma_cmd_ptr) DBDMA_ALIGN (pb->jump_raw);	/* circular DBDMA command buffer, to hold jumps to transfer commands */	tab_cmd_dbdma(c++, DBDMA_STOP, 0);	for (i=1; i<NUMJUMPS-1; i++)		tab_cmd_dbdma(c++, DBDMA_NOP, 0);	tab_cmd_dbdma(c, DBDMA_NOP|BR_ALWAYS,		(unsigned int)pb->jumpbuf);	DBG("PlanB: planb_prepare_open, jumpbuffer at 0x%08x, length %d.\n",		(unsigned int)pb->jumpbuf, size); 	return 0;}#define VBIDUMMY 40	/* must be even !! */static int planb_prepare_vbi(struct planb *pb){	int	size;	/* allocate VBI comand buffer memory	   (2 fields * VBI_MAXLINES + 40 handling + alignment) */	size = (2*VBI_MAXLINES + VBIDUMMY + 1) * sizeof(struct dbdma_cmd);	if ((pb->vbi_raw = kmalloc (size, GFP_KERNEL|GFP_DMA)) == 0)		return -ENOMEM;	memset (pb->vbi_raw, 0, size);	size = (VBI_MAXLINES + VBIDUMMY/2) * sizeof(struct dbdma_cmd);	pb->vbi_cbo.start = (dbdma_cmd_ptr) DBDMA_ALIGN (pb->vbi_raw);	pb->vbi_cbo.size = pb->vbi_cbe.size = size;	pb->vbi_cbe.start = pb->vbi_cbo.start + pb->vbi_cbo.size;	pb->vbi_cbo.jumpaddr = pb->jumpbuf + 1;	pb->vbi_cbe.jumpaddr = pb->jumpbuf + 3;	DBG("PlanB: planb_prepare_vbi, dbdma cmd_buf at 0x%08x, length %d.\n",		(unsigned int)pb->vbi_cbo.start, 2*size); 	return 0;}static int planb_prepare_video(struct planb *pb){	int	i, size;	/* FIXME: This is stressing kmalloc to its limits...		  We really should allocate smaller chunks. */	/* allocate memory for two plus alpha command buffers (size: max lines,	   plus 40 commands handling, plus 1 alignment), plus dummy command buf,	   plus clipmask buffer, plus frame grabbing status */	size = (pb->tab_size * (2 + MAX_GBUFFERS * TAB_FACTOR)		    + MAX_GBUFFERS * PLANB_DUMMY + 1) * sizeof(struct dbdma_cmd)		+ (PLANB_MAXLINES * ((PLANB_MAXPIXELS + 7) & ~7)) / 8		+ MAX_GBUFFERS * sizeof(unsigned int);	if ((pb->vid_raw = kmalloc (size, GFP_KERNEL|GFP_DMA)) == 0)		return -ENOMEM;	memset (pb->vid_raw, 0, size);	pb->vid_cbo.start = (dbdma_cmd_ptr) DBDMA_ALIGN (pb->vid_raw);	pb->vid_cbo.size = pb->vid_cbe.size = pb->tab_size/2;	pb->vid_cbe.start = pb->vid_cbo.start + pb->vid_cbo.size;	pb->vid_cbo.jumpaddr = pb->jumpbuf + 2;	pb->vid_cbe.jumpaddr = pb->jumpbuf + 4;	pb->overlay_last1 = pb->vid_cbo.start;	pb->vid_cbo.bus = virt_to_bus(pb->vid_cbo.start);	pb->vid_cbe.bus = virt_to_bus(pb->vid_cbe.start);	pb->clip_cbo.start = pb->vid_cbe.start + pb->vid_cbe.size;	pb->clip_cbo.size = pb->clip_cbe.size = pb->tab_size/2;	pb->clip_cbe.start = pb->clip_cbo.start + pb->clip_cbo.size;	pb->overlay_last2 = pb->clip_cbo.start;	pb->clip_cbo.bus = virt_to_bus(pb->clip_cbo.start);	pb->clip_cbe.bus = virt_to_bus(pb->clip_cbe.start);	pb->gbuf[0].cap_cmd = pb->clip_cbe.start + pb->clip_cbe.size;	pb->gbuf[0].pre_cmd = pb->gbuf[0].cap_cmd + pb->tab_size * TAB_FACTOR;	for (i = 1; i < MAX_GBUFFERS; i++) {		pb->gbuf[i].cap_cmd = pb->gbuf[i-1].pre_cmd + PLANB_DUMMY;		pb->gbuf[i].pre_cmd = pb->gbuf[i].cap_cmd +						pb->tab_size * TAB_FACTOR;	}	pb->gbuf[0].status = (volatile unsigned int *)			(pb->gbuf[MAX_GBUFFERS-1].pre_cmd + PLANB_DUMMY);	for (i = 1; i < MAX_GBUFFERS; i++)		pb->gbuf[i].status = pb->gbuf[i-1].status;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -