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 + -
显示快捷键?