swarm_saa7114h.c

来自「linux-2.4.29操作系统的源码」· C语言 代码 · 共 1,696 行 · 第 1/3 页

C
1,696
字号
/*    saa7114h - Philips SAA7114H video decoder driver   Copyright (C) 2001,2002,2003 Broadcom Corporation   From saa7111.c:     Copyright (C) 1998 Dave Perks <dperks@ibm.net>   From cpia.c:     (C) Copyright 1999-2000 Peter Pregler     (C) Copyright 1999-2000 Scott J. Bertin     (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>   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. *//* * Important note: this driver is reasonably functional, and has been * tested with the "camserv" v4l application.  But it primarily a * proof-of-concept, and example for setting up FIFO-mode. */#include <linux/init.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/ctype.h>#include <linux/fs.h>#include <linux/vmalloc.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/pci.h>#include <linux/signal.h>#include <linux/proc_fs.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/page.h>#include <linux/sched.h>#include <asm/segment.h>#include <linux/types.h>#include <linux/wrapper.h>#include <linux/smp_lock.h>#include <asm/hardirq.h>#include <linux/i2c.h>#include <linux/videodev.h>#include <linux/version.h>#include <asm/uaccess.h>#include <linux/i2c-algo-sibyte.h>#include <asm/sibyte/64bit.h>#include <asm/sibyte/sb1250_regs.h>#include <asm/sibyte/sb1250_int.h>#include <asm/sibyte/sb1250_mac.h>#include <asm/sibyte/sb1250_dma.h>#define SAA_BRIGHTNESS	 0x0a#define SAA_CONTRAST	 0x0b#define SAA_SATURATION	 0x0c#define SAA_HUE		 0x0d#define DECODER_STATUS	 0x1f#define SLICER_STATUS_0	 0x60#define SLICER_STATUS_1	 0x61#define SLICER_STATUS_2	 0x62#define SCALER_STATUS	 0x8f#define NUM_FRAME	 2#define MAX_HORIZ	 720#define MAX_VERT	 480#define MIN_HORIZ	 180#define MIN_VERT	 120#define MAX_PER_PIXEL	 3#define MAX_FRAME_SIZE	 (MAX_HORIZ*MAX_VERT*MAX_PER_PIXEL)#define MAX_MMAP_SIZE	 (PAGE_ALIGN(MAX_FRAME_SIZE*NUM_FRAME))#define RAW_PER_PIXEL	 2#define RAW_LINE_PAD	 8#define RAW_LINE_SIZE	 (((MAX_HORIZ*RAW_PER_PIXEL)+RAW_LINE_PAD+0x1f) & ~0x1f)#define RAW_FRAME_SIZE	 (RAW_LINE_SIZE*MAX_VERT)#define NUM_DESCR	 64#define INTR_PKT_CNT	 8/* Extensions to videodev.h IOCTL definitions */#define VIDIOREADREG	_IOR('v', 50, int)#define VIDIOWRITEREG	_IOW('v', 50, int)#define VIDIOGRABFRAME	_IOR('v', 51, int)#define VIDIOSHOWEAV	_IOR('v', 52, int)#define IF_NAME "saa7114h"#define MAC2_CSR(r)	   (KSEG1 + A_MAC_REGISTER(2, r))#define MAC2_DMARX0_CSR(r) (KSEG1 + A_MAC_DMA_REGISTER(2, DMA_RX, 0, r))/* Options */#define DMA_DEINTERLACE	 1#define LAZY_READ	 1#define NULL_DMA	 0/* Debug filters */#define DBG_NULL	 0x0000#define DBG_IO		 0x0001#define DBG_DESCR	 0x0002#define DBG_INTR	 0x0004#define DBG_CONVERT	 0x0008#define DBG_FRAMING	 0x0010#define DBG_REGISTER	 0x0020#define DBG_CALL	 0x0040#define DBG_FRAMING_LOUD 0x0080/* XXXKW make this settable through /proc... */#define DEBUG_LVL	 (DBG_NULL)#if DEBUG_LVL#define DBG(l, p) do { if (DEBUG_LVL & l) p; } while (0)#else#define DBG(l, p)#endif/* ----------------------------------------------------------------------- */enum {	FRAME_READY,		/* Ready to grab into */	FRAME_GRABBING,		/* In the process of being grabbed into */	FRAME_DONE,		/* Finished grabbing, but not been synced yet */	FRAME_UNUSED,		/* Unused (belongs to driver, but can't be used) */};struct saa_frame {	uint8_t		 *data;	uint8_t		 *pos;	int		  width;	int		  height;	uint32_t	  size;	volatile int	  state;	wait_queue_head_t read_wait;};typedef struct fifo_descr_s {	uint64_t descr_a;	uint64_t descr_b;} fifo_descr_t;typedef unsigned long paddr_t;typedef struct fifo_s {	unsigned	 ringsz;	fifo_descr_t	*descrtab;	fifo_descr_t	*descrtab_end;	fifo_descr_t	*next_descr;	paddr_t		 descrtab_phys;	void		*dma_buf;	    /* DMA buffer */} fifo_t;struct saa7114h {	struct i2c_client    *client;	struct video_device  *vd;	struct video_window   vw;	struct video_picture  vp;	uint8_t		      reg[256];	fifo_t		 ff;	void		*frame_buf; /* hold frames for the client */	struct saa_frame frame[NUM_FRAME]; /* point into frame_buf */	int		 hwframe;	int		 swframe;	uint16_t depth;	uint16_t palette;	uint8_t	 bright;	uint8_t	 contrast;	uint8_t	 hue;	uint8_t	 sat;	struct proc_dir_entry *proc_entry;	struct semaphore       param_lock;	struct semaphore       busy_lock;	int	dma_enable;	int	opened;	int	irq;	int	interlaced;};static int saa7114h_probe(struct i2c_adapter *adap);static int saa7114h_detach(struct i2c_client *device);struct i2c_driver i2c_driver_saa7114h ={	name:		"saa7114h",		/* name */	id:		I2C_DRIVERID_SAA7114H,	/* ID */	flags:		I2C_DF_NOTIFY,		/* XXXKW do I care? */	attach_adapter: saa7114h_probe,	detach_client:	saa7114h_detach};/* ----------------------------------------------------------------------- * VM assist for MMAPed space * ----------------------------------------------------------------------- *//* Given PGD from the address space's page table, return the kernel * virtual mapping of the physical memory mapped at ADR. */static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr){	unsigned long ret = 0UL;	pmd_t *pmd;	pte_t *ptep, pte;	if (!pgd_none(*pgd)) {		pmd = pmd_offset(pgd, adr);		if (!pmd_none(*pmd)) {			ptep = pte_offset(pmd, adr);			pte = *ptep;			if (pte_present(pte)) {				ret = (unsigned long) page_address(pte_page(pte));				ret |= (adr & (PAGE_SIZE-1));			}		}	}	return ret;}/* Here we want the physical address of the memory. * This is used when initializing the contents of the * area and marking the pages as reserved. */static inline unsigned long kvirt_to_pa(unsigned long adr){	unsigned long va, kva, ret;	va = VMALLOC_VMADDR(adr);	kva = uvirt_to_kva(pgd_offset_k(va), va);	ret = __pa(kva);	return ret;}static void *rvmalloc(unsigned long size){	void *mem;	unsigned long adr, page;	/* Round it off to PAGE_SIZE */	size += (PAGE_SIZE - 1);	size &= ~(PAGE_SIZE - 1);	mem = vmalloc_32(size);	if (!mem)		return NULL;	memset(mem, 0, size); /* Clear the ram out, no junk to the user */	adr = (unsigned long) mem;	while (size > 0) {		page = kvirt_to_pa(adr);		mem_map_reserve(virt_to_page(__va(page)));		adr += PAGE_SIZE;		if (size > PAGE_SIZE)			size -= PAGE_SIZE;		else			size = 0;	}	return mem;}static void rvfree(void *mem, unsigned long size){	unsigned long adr, page;	if (!mem)		return;	size += (PAGE_SIZE - 1);	size &= ~(PAGE_SIZE - 1);	adr = (unsigned long) mem;	while (size > 0) {		page = kvirt_to_pa(adr);		mem_map_unreserve(virt_to_page(__va(page)));		adr += PAGE_SIZE;		if (size > PAGE_SIZE)			size -= PAGE_SIZE;		else			size = 0;	}	vfree(mem);}/* ----------------------------------------------------------------------- * Control interface (i2c) * ----------------------------------------------------------------------- */static int saa7114h_reg_read(struct saa7114h *dev, unsigned char subaddr){	return i2c_smbus_read_byte_data(dev->client, subaddr);}static int saa7114h_reg_write(struct saa7114h *dev, unsigned char subaddr, int data){	return i2c_smbus_write_byte_data(dev->client, subaddr, data & 0xff);}static int saa7114h_reg_init(struct saa7114h *dev, unsigned const char *data, unsigned int len){	int rc = 0;	int val;	while (len && !rc) {		dev->reg[data[0]] = data[1];		rc = saa7114h_reg_write(dev, data[0], data[1]);		if (!rc && (data[0] != 0)) {			val = saa7114h_reg_read(dev, data[0]);			if ((val < 0) || (val != data[1])) {				printk(KERN_ERR				       IF_NAME ": init readback mismatch reg %02x = %02x (should be %02x)\n",				       data[0], val, data[1]);			}		}		len -= 2;		data += 2;	}	return rc;}/* ----------------------------------------------------------------------- * /proc interface * ----------------------------------------------------------------------- */#ifdef CONFIG_PROC_FSstatic struct proc_dir_entry *saa7114h_proc_root=NULL;static int decoder_read_proc(char *page, char **start, off_t off,			     int count, int *eof, void *data){	char *out = page;	int len, status;	struct saa7114h *decoder = data;	out += sprintf(out, "  SWARM saa7114h\n------------------\n");	status = saa7114h_reg_read(decoder, DECODER_STATUS);	out += sprintf(out, "  Decoder status = %02x\n", status);	if (status & 0x80)		out += sprintf(out, "	 interlaced\n");	if (status & 0x40)		out += sprintf(out, "	 not locked\n");	if (status & 0x02)		out += sprintf(out, "	 Macrovision detected\n");	if (status & 0x01)		out += sprintf(out, "	 color\n");	out += sprintf(out, "  Brightness = %02x\n", decoder->bright);	out += sprintf(out, "  Contrast	  = %02x\n", decoder->contrast);	out += sprintf(out, "  Saturation = %02x\n", decoder->sat);	out += sprintf(out, "  Hue	  = %02x\n\n", decoder->hue);	out += sprintf(out, "  Scaler status  = %02x\n", 		       (int)saa7114h_reg_read(decoder, SCALER_STATUS));	len = out - page;	len -= off;	if (len < count) {		*eof = 1;		if (len <= 0) return 0;	} else		len = count;	*start = page + off;	return len;}static int decoder_write_proc(struct file *file, const char *buffer,			       unsigned long count, void *data){	struct saa7114h *d = data;	int retval;	unsigned int cmd, reg, reg_val;		if (down_interruptible(&d->param_lock))		return -ERESTARTSYS;#define VALUE \	({ \		char *_p; \		unsigned long int _ret; \		while (count && isspace(*buffer)) { \			buffer++; \			count--; \		} \		_ret = simple_strtoul(buffer, &_p, 16); \		if (_p == buffer) \			retval = -EINVAL; \		else { \			count -= _p - buffer; \			buffer = _p; \		} \		_ret; \	})		retval = 0;	while (count && !retval) {		cmd = VALUE;		if (retval)			break;		switch (cmd) {		case 1:			reg = VALUE;			if (retval)				break;			reg_val = VALUE;			if (retval)				break;			printk(IF_NAME ": write reg %x <- %x\n", reg, reg_val);			if (saa7114h_reg_write(d, reg, reg_val) == -1)				retval = -EINVAL;			break;		case 2:			reg = VALUE;			if (retval)				break;			reg_val = saa7114h_reg_read(d, reg);			if (reg_val == -1)				retval = -EINVAL;			else				printk(IF_NAME ": read reg %x -> %x\n", reg, reg_val);			break;		default:			break;		}	}	up(&d->param_lock);		return retval;}static void create_proc_decoder(struct saa7114h *decoder){	char name[8];	struct proc_dir_entry *ent;		if (!saa7114h_proc_root || !decoder)		return;	sprintf(name, "video%d", decoder->vd->minor);		ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, saa7114h_proc_root);	if (!ent) {		printk(KERN_INFO IF_NAME ": Unable to initialize /proc/saa7114h/%s\n", name);		return;	}	ent->data = decoder;	ent->read_proc = decoder_read_proc;	ent->write_proc = decoder_write_proc;	ent->size = 3626;	/* XXXKW ??? */	decoder->proc_entry = ent;}static void destroy_proc_decoder(struct saa7114h *decoder){	char name[7];		if (!decoder || !decoder->proc_entry)		return;		sprintf(name, "video%d", decoder->vd->minor);	remove_proc_entry(name, saa7114h_proc_root);	decoder->proc_entry = NULL;}static void proc_saa7114h_create(void){	saa7114h_proc_root = create_proc_entry("saa7114h", S_IFDIR, 0);	if (saa7114h_proc_root)		saa7114h_proc_root->owner = THIS_MODULE;	else		printk(KERN_INFO IF_NAME ": Unable to initialize /proc/saa7114h\n");}static void proc_saa7114h_destroy(void){	remove_proc_entry("saa7114h", 0);}#endif /* CONFIG_PROC_FS *//* ----------------------------------------------------------------------- * Initialization * ----------------------------------------------------------------------- */static int dma_setup(struct saa7114h *d){	int i;	void *curbuf;	/* Reset the port */	out64(M_MAC_PORT_RESET, MAC2_CSR(R_MAC_ENABLE));	in64(MAC2_CSR(R_MAC_ENABLE));	/* Zero everything out, disable filters */	out64(0, MAC2_CSR(R_MAC_TXD_CTL));	out64(M_MAC_ALLPKT_EN, MAC2_CSR(R_MAC_ADFILTER_CFG));	out64(V_MAC_RX_RD_THRSH(4) | V_MAC_RX_RL_THRSH(4),	      MAC2_CSR(R_MAC_THRSH_CFG));	for (i=0; i<MAC_CHMAP_COUNT; i++) {		out64(0, MAC2_CSR(R_MAC_CHLO0_BASE+(i*8)));		out64(0, MAC2_CSR(R_MAC_CHUP0_BASE+(i*8)));	}	for (i=0; i<MAC_HASH_COUNT; i++) {		out64(0, MAC2_CSR(R_MAC_HASH_BASE+(i*8)));	}	for (i=0; i<MAC_ADDR_COUNT; i++) {		out64(0, MAC2_CSR(R_MAC_ADDR_BASE+(i*8)));	}	 		out64(V_MAC_MAX_FRAMESZ(16*1024) | V_MAC_MIN_FRAMESZ(0),	      MAC2_CSR(R_MAC_FRAMECFG));	/* Select bypass mode */	out64((M_MAC_BYPASS_SEL | V_MAC_BYPASS_CFG(K_MAC_BYPASS_EOP) | 	       M_MAC_FC_SEL | M_MAC_SS_EN | V_MAC_SPEED_SEL_1000MBPS),	      MAC2_CSR(R_MAC_CFG));	/* Set up the descriptor table */	d->ff.descrtab = kmalloc(NUM_DESCR * sizeof(fifo_descr_t), GFP_KERNEL);	d->ff.descrtab_phys = __pa(d->ff.descrtab);	d->ff.descrtab_end = d->ff.descrtab + NUM_DESCR;	d->ff.next_descr = d->ff.descrtab;	d->ff.ringsz = NUM_DESCR;#if 0	/* XXXKW this won't work because the physical may not be	   contiguous; how do I handle a bigger alloc then? */	d->ff.dma_buf = rvmalloc(RAW_LINE_SIZE*NUM_DESCR);	printk(KERN_DEBUG IF_NAME ": DMA buffer allocated (%p)\n",	       d->ff.dma_buf);#else	d->ff.dma_buf = kmalloc(RAW_LINE_SIZE*NUM_DESCR, GFP_KERNEL);#endif	if (!d->ff.dma_buf) {		printk(KERN_ERR IF_NAME ": couldn't allocate DMA buffer\n");		return -ENOMEM;	}	memset(d->ff.dma_buf, 0, RAW_LINE_SIZE*NUM_DESCR);	for (i=0, curbuf=d->ff.dma_buf; i<d->ff.ringsz; i++, curbuf+=RAW_LINE_SIZE) {		d->ff.descrtab[i].descr_a = (__pa(curbuf) |					     V_DMA_DSCRA_A_SIZE(RAW_LINE_SIZE >> 5));		d->ff.descrtab[i].descr_b = 0;	}	out64(V_DMA_INT_PKTCNT(INTR_PKT_CNT) | M_DMA_EOP_INT_EN |	      V_DMA_RINGSZ(d->ff.ringsz) | M_DMA_TDX_EN,	      MAC2_DMARX0_CSR(R_MAC_DMA_CONFIG0));

⌨️ 快捷键说明

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