URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [media/] [video/] [swarm_saa7114h.c] - Rev 1774
Go to most recent revision | Compare with Previous | Blame | View Log
/* 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_FS static 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)); out64(M_DMA_L2CA, MAC2_DMARX0_CSR(R_MAC_DMA_CONFIG1)); out64(d->ff.descrtab_phys, MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_BASE)); /* Enable interrupts and DMA */ out64(M_MAC_INT_EOP_COUNT<<S_MAC_RX_CH0, MAC2_CSR(R_MAC_INT_MASK)); out64(M_MAC_RXDMA_EN0 | M_MAC_BYP_RX_ENABLE, MAC2_CSR(R_MAC_ENABLE)); return 0; } /* ----------------------------------------------------------------------- * v4linux helpers - color conversion, etc (taken from cpia.c) * ----------------------------------------------------------------------- */ #define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) static void yuvconvert_inplace(uint8_t *data, uint32_t in_uyvy, int out_fmt, int mmap) { int y, u, v, r, g, b, y1; uint8_t *src, *dst; if (out_fmt == VIDEO_PALETTE_RGB24) { src = (uint8_t *)((int)data + in_uyvy); dst = (uint8_t *)((int)data + in_uyvy + (in_uyvy >> 1)); DBG(DBG_CONVERT, printk(KERN_DEBUG "inplace: %p %p %p\n", data, src, dst)); while (src > data) { if ((int)(src-data) < 4) break; //printk("freaky %p %p\n", src, data); y1 = (*(--src) - 16) * 76310; v = *(--src) - 128; y = (*(--src) - 16) * 76310; u = *(--src) - 128; r = 104635 * v; g = -25690 * u + -53294 * v; b = 132278 * u; /* XXXKW what on earth is up with mmap? */ if (mmap) { *(--dst) = LIMIT(r+y1); *(--dst) = LIMIT(g+y1); *(--dst) = LIMIT(b+y1); *(--dst) = LIMIT(r+y); *(--dst) = LIMIT(g+y); *(--dst) = LIMIT(b+y); } else { *(--dst) = LIMIT(b+y1); *(--dst) = LIMIT(g+y1); *(--dst) = LIMIT(r+y1); *(--dst) = LIMIT(b+y); *(--dst) = LIMIT(g+y); *(--dst) = LIMIT(r+y); } } } } static int saa7114h_get_cparams(struct saa7114h *decoder) { /* XXX check for error code */ decoder->bright = saa7114h_reg_read(decoder, SAA_BRIGHTNESS); decoder->contrast = saa7114h_reg_read(decoder, SAA_CONTRAST); decoder->sat = saa7114h_reg_read(decoder, SAA_SATURATION); decoder->hue = saa7114h_reg_read(decoder, SAA_HUE); decoder->vp.brightness = (uint16_t)decoder->bright << 8; decoder->vp.contrast = (uint16_t)decoder->contrast << 9; decoder->vp.colour = decoder->sat << 9; decoder->vp.hue = ((int16_t)decoder->hue + 128) << 8; return 0; } static int saa7114h_set_cparams(struct saa7114h *decoder) { decoder->bright = decoder->vp.brightness >> 8; decoder->contrast = decoder->vp.contrast >> 9; decoder->sat = decoder->vp.colour >> 9; decoder->hue = (uint8_t)((int8_t)(decoder->vp.hue >> 8) - 128); return (saa7114h_reg_write(decoder, SAA_BRIGHTNESS, decoder->bright) || saa7114h_reg_write(decoder, SAA_CONTRAST, decoder->contrast) || saa7114h_reg_write(decoder, SAA_SATURATION, decoder->sat) || saa7114h_reg_write(decoder, SAA_HUE, decoder->hue)); } /* ----------------------------------------------------------------------- * Custom IOCTL support * ----------------------------------------------------------------------- */ unsigned char eav[625][2]; static int grab_frame(struct saa7114h *d, void *user_buf, int print_eav) { int cur_idx = 0; int to_go = 625; int delta; int i, len, eav_val, sav_val; int started = 0; uint8_t *buf; fifo_descr_t *cur_d; int swptr = d->ff.next_descr - d->ff.descrtab; int hwptr; DBG(DBG_CALL, printk(IF_NAME ": grabbing frame\n")); /* Check for Macrovision -- if it's on, DMA won't happen */ if (saa7114h_reg_read(d, DECODER_STATUS) & 0x2) return -EACCES; out64(d->ff.ringsz, MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)); do { hwptr = (unsigned) (((in64(MAC2_DMARX0_CSR(R_MAC_DMA_CUR_DSCRADDR)) & M_DMA_CURDSCR_ADDR) - d->ff.descrtab_phys) / sizeof(fifo_descr_t)); delta = (hwptr + d->ff.ringsz - swptr) % d->ff.ringsz; if (delta == 0) { #if 0 uint64_t val = in64(MAC2_DMARX0_CSR(R_MAC_STATUS)); printk("mac status: %08x%08x\n", (u32)(val >> 32), (u32)(val&0xffffffff)); #endif } for (i=0; i<delta; i++) { cur_d = d->ff.next_descr; if (++d->ff.next_descr == d->ff.descrtab_end) d->ff.next_descr = d->ff.descrtab; if (!(cur_d->descr_a & M_DMA_ETHRX_SOP)) { printk("bogus RX\n"); continue; } cur_d->descr_a &= ~M_DMA_ETHRX_SOP; len = G_DMA_DSCRB_PKT_SIZE(cur_d->descr_b); buf = (uint8_t *)__va(cur_d->descr_a & M_DMA_DSCRA_A_ADDR); if (len != (d->vw.width*RAW_PER_PIXEL)+RAW_LINE_PAD) { printk("funny size %d\n", len); continue; } eav_val = buf[1]; sav_val = buf[5]; if (eav_val == 0xf1) { /* end of field 2, V-blank */ if (started) { started = 0; delta = to_go = 0; /* just let DMA finish in background */ } else { started = 1; } } if (started) { eav[cur_idx][0] = eav_val; eav[cur_idx++][1] = sav_val; if (copy_to_user(user_buf, &buf[6], 1440)) return -EFAULT; user_buf += 1440; } } swptr = hwptr; if (delta) { if (started) to_go -= delta; if (delta > to_go) delta = to_go; out64(delta, MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)); } } while (to_go); if (print_eav) { for (i=0; i<cur_idx; i++) { printk("%3d: %02x | %02x\n", i, eav[i][0], eav[i][1]); } } return cur_idx; } /* ----------------------------------------------------------------------- * Interrupt handler * ----------------------------------------------------------------------- */ unsigned long int_count = 0; static void saa7114h_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct saa7114h *d = dev_id; uint64_t status_val; fifo_descr_t *cur_d; int i, delta, len; uint8_t *buf, eav_val; int swptr = d->ff.next_descr - d->ff.descrtab; int hwptr; status_val = in64(MAC2_CSR(R_MAC_STATUS)); /* Process finished decsriptors */ hwptr = (unsigned) (((in64(MAC2_DMARX0_CSR(R_MAC_DMA_CUR_DSCRADDR)) & M_DMA_CURDSCR_ADDR) - d->ff.descrtab_phys) / sizeof(fifo_descr_t)); delta = (hwptr + d->ff.ringsz - swptr) % d->ff.ringsz; if (!delta) { if (status_val & M_MAC_INT_EOP_SEEN<<S_MAC_RX_CH0) { /* Must have wrapped since the last interrupt */ delta = d->ff.ringsz; } else { /* XXXKW why would this happen? */ return; } } for (i=0; i<delta; i++) { cur_d = d->ff.next_descr; if (++d->ff.next_descr == d->ff.descrtab_end) d->ff.next_descr = d->ff.descrtab; if (!(cur_d->descr_a & M_DMA_ETHRX_SOP)) { printk(KERN_DEBUG "bogus RX\n"); continue; } cur_d->descr_a &= ~M_DMA_ETHRX_SOP; if (!d->dma_enable) continue; len = G_DMA_DSCRB_PKT_SIZE(cur_d->descr_b); buf = (uint8_t *)__va(cur_d->descr_a & M_DMA_DSCRA_A_ADDR); if (len != (d->vw.width*RAW_PER_PIXEL)+RAW_LINE_PAD) { printk(KERN_DEBUG "funny size %d\n", len); // continue; } len -= RAW_LINE_PAD; eav_val = buf[1]; DBG(DBG_FRAMING_LOUD, printk(KERN_DEBUG "eav: %02x len: %d\n", eav_val, len)); if (eav_val == 0xf1) { /* end of field 2, V-blank: start-of-frame */ switch (d->frame[d->hwframe].state) { case FRAME_UNUSED: DBG(DBG_FRAMING, printk(KERN_ERR "capture to unused frame %d\n", d->hwframe)); break; case FRAME_READY: DBG(DBG_FRAMING, printk(KERN_DEBUG "frame started %d\n", d->hwframe)); /* start this frame (skip eav/sav) */ memcpy(d->frame[d->hwframe].pos, &buf[6], len); #if DMA_DEINTERLACE if (!d->interlaced) memcpy(d->frame[d->hwframe].pos-len, &buf[6], len); d->frame[d->hwframe].pos += len*2; #else d->frame[d->hwframe].pos += len; #endif d->frame[d->hwframe].state = FRAME_GRABBING; /* XXXKW check pos overflow */ break; case FRAME_GRABBING: /* kick over to new frame */ d->frame[d->hwframe].size = d->frame[d->hwframe].pos - d->frame[d->hwframe].data; d->frame[d->hwframe].state = FRAME_DONE; DBG(DBG_FRAMING, printk(KERN_DEBUG "frame finished %d\n", d->frame[d->hwframe].size)); /* wake up a waiting reader */ DBG(DBG_IO, printk(KERN_DEBUG "wakeup\n")); wake_up(&d->frame[d->hwframe].read_wait); d->hwframe = (d->hwframe + 1) % NUM_FRAME; if (d->frame[d->hwframe].state == FRAME_READY) { /* start this frame */ DBG(DBG_FRAMING, printk(KERN_DEBUG "frame bumped %d\n", d->hwframe)); memcpy(d->frame[d->hwframe].pos, &buf[6], len); #if DMA_DEINTERLACE if (!d->interlaced) memcpy(d->frame[d->hwframe].pos-len, &buf[6], len); d->frame[d->hwframe].pos += len*2; #else d->frame[d->hwframe].pos += len; #endif d->frame[d->hwframe].state = FRAME_GRABBING; } else { /* drop on the floor, note that we've stopped DMA'ing */ DBG(DBG_FRAMING, printk(KERN_DEBUG "frame capture halted\n")); d->dma_enable = 0; } break; case FRAME_DONE: /* drop on the floor (must be waiting for sw) */ DBG(DBG_FRAMING, printk(KERN_DEBUG "frame capture halted\n")); d->dma_enable = 0; break; } } else { switch (d->frame[d->hwframe].state) { case FRAME_UNUSED: DBG(DBG_FRAMING, printk(KERN_ERR "capture to unused frame %d\n", d->hwframe)); break; case FRAME_READY: /* drop on the floor (must have dropped something) */ DBG(DBG_FRAMING_LOUD, printk(KERN_DEBUG "missed SOF\n")); break; case FRAME_DONE: /* drop on the floor (must be waiting for sw) */ DBG(DBG_FRAMING, printk(KERN_DEBUG "frame overflow\n")); d->dma_enable = 0; break; case FRAME_GRABBING: #if DMA_DEINTERLACE if (eav_val == 0xb6) { d->frame[d->hwframe].pos = d->frame[d->hwframe].data; } memcpy(d->frame[d->hwframe].pos, &buf[6], len); if (!d->interlaced) memcpy(d->frame[d->hwframe].pos-len, &buf[6], len); d->frame[d->hwframe].pos += len*2; #else memcpy(d->frame[d->hwframe].pos, &buf[6], len); d->frame[d->hwframe].pos += len; #endif /* XXXKW check pos overflow */ break; } } } if (d->dma_enable) { out64(delta, MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)); DBG(DBG_DESCR, printk(KERN_DEBUG IF_NAME ": interrupt adds %d -> %d descrs\n", delta, (int)in64(MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)))); } } /* ----------------------------------------------------------------------- * /dev/video interface * ----------------------------------------------------------------------- */ static int saa7114h_open(struct video_device *vd, int nb) { struct saa7114h *d = vd->priv; uint32_t status; if (!d || d->opened) return -EBUSY; d->opened = 1; DBG(DBG_CALL, printk(KERN_DEBUG IF_NAME ": open\n")); /* XXKW Should check this periodically!? */ status = saa7114h_reg_read(d, DECODER_STATUS); d->interlaced = ((status & 0x80) != 0); #if !NULL_DMA if (d->dma_enable) { printk(IF_NAME ": open found DMA on?!\n"); #if LAZY_READ } #else } else { int descr; d->dma_enable = 1; DBG(DBG_DESCR, printk(IF_NAME ": open enabling DMA\n")); /* Force capture to start into frame buffer 0 */ descr = in64(MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)); DBG(DBG_DESCR, printk(IF_NAME ": open adds %d -> %d descrs\n", d->ff.ringsz-desc, descr)); out64(d->ff.ringsz-descr, MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)); } #endif #endif return 0; } static void saa7114h_release(struct video_device *vd) { struct saa7114h *d = vd->priv; DBG(DBG_CALL, printk(KERN_DEBUG IF_NAME ": release\n")); d->opened = 0; d->dma_enable = 0; /* XXXKW do a clean drain of outstanding DMAs? toss leftover buffer contents to avoid stale pictures? */ return; } static long saa7114h_read(struct video_device *vd, char *buf, unsigned long count, int noblock) { struct saa7114h *d = vd->priv; int descr, status; if (!d) return -ENODEV; /* XXKW Should check this periodically!? */ status = saa7114h_reg_read(d, DECODER_STATUS); // d->interlaced = ((status & 0x80) != 0); #if !NULL_DMA #if LAZY_READ if (!d->dma_enable) { DBG(DBG_DESCR, printk(KERN_DEBUG IF_NAME ": enabling DMA\n")); /* Give the buffer to the DMA engine (force ptr reset) */ d->swframe = d->hwframe; d->frame[d->swframe].state = FRAME_READY; #if DMA_DEINTERLACE d->frame[d->swframe].pos = d->frame[d->swframe].data+d->vw.width*RAW_PER_PIXEL; #else d->frame[d->swframe].pos = d->frame[d->swframe].data; #endif /* Fire up the DMA engine again if it stopped */ d->dma_enable = 1; descr = in64(MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)); out64(d->ff.ringsz-descr, MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)); } #endif #endif /* XXXKW mmap/read mixture could break the swframe sequence */ if (d->frame[d->swframe].state != FRAME_DONE) { if (noblock) return -EAGAIN; else { DBG(DBG_IO, printk(KERN_DEBUG IF_NAME ": sleeping for frame\n")); interruptible_sleep_on(&d->frame[d->swframe].read_wait); DBG(DBG_IO, printk(KERN_DEBUG IF_NAME ": awakened\n")); if (signal_pending(current)) return -ERESTARTSYS; } } if (count < d->frame[d->swframe].size) return -EFAULT; count = d->frame[d->swframe].size; yuvconvert_inplace(d->frame[d->swframe].data, d->frame[d->swframe].size, d->vp.palette, 0); copy_to_user(buf, d->frame[d->swframe].data, d->frame[d->swframe].size); d->swframe = (d->swframe + 1) % NUM_FRAME; /* XXXKW doesn't do format conversion!!! */ #if !NULL_DMA #if !LAZY_READ /* XXXKW Fire up the DMA engine again if it stopped ??? */ if (!d->dma_enable) { DBG(DBG_DESCR, printk(KERN_DEBUG IF_NAME ": enabling DMA\n")); /* Fire up the DMA engine again if it stopped */ d->dma_enable = 1; descr = in64(MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)); out64(d->ff.ringsz-descr, MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)); } #endif #endif return count; } static int saa7114h_ioctl(struct video_device *vd, unsigned int cmd, void *arg) { struct saa7114h *d = vd->priv; int val, reg, retval = 0; if (!d) return -ENODEV; switch (cmd) { case VIDIOCGCHAN: { struct video_channel v; if (copy_from_user(&v, arg, sizeof(v))) { retval = -EFAULT; break; } if (v.channel != 0) { retval = -EINVAL; break; } v.channel = 0; strcpy(v.name, "Camera"); v.tuners = 0; v.flags = 0; v.type = VIDEO_TYPE_CAMERA; v.norm = 0; if (copy_to_user(arg, &v, sizeof(v))) retval = -EFAULT; break; } case VIDIOCSCHAN: { int v; if (copy_from_user(&v, arg, sizeof(v))) retval = -EFAULT; if (retval == 0 && v != 0) retval = -EINVAL; break; } case VIDIOCGCAP: { struct video_capability b; strcpy(b.name, "Philips SAA7114H Decoder"); b.type = VID_TYPE_CAPTURE /* | VID_TYPE_TELETEXT */ | VID_TYPE_SCALES; b.channels = 1; b.audios = 0; b.maxwidth = MAX_HORIZ; b.maxheight = MAX_VERT; /* XXXKW find real values */ b.minwidth = 48; b.minheight = 48; if (copy_to_user(arg, &b, sizeof(b))) retval = -EFAULT; break; } /* image properties */ case VIDIOCGPICT: if (copy_to_user(arg, &d->vp, sizeof(struct video_picture))) retval = -EFAULT; break; case VIDIOCSPICT: { struct video_picture vp; /* copy_from_user */ if (copy_from_user(&vp, arg, sizeof(vp))) { retval = -EFAULT; break; } down(&d->param_lock); /* brightness, colour, contrast need not check 0-65535 */ memcpy( &d->vp, &vp, sizeof(vp) ); /* update cam->params.colourParams */ saa7114h_set_cparams(d); up(&d->param_lock); break; } /* get/set capture window */ case VIDIOCGWIN: if (copy_to_user(arg, &d->vw, sizeof(struct video_window))) retval = -EFAULT; break; case VIDIOCSWIN: { /* copy_from_user, check validity, copy to internal structure */ struct video_window vw; if (copy_from_user(&vw, arg, sizeof(vw))) { retval = -EFAULT; break; } if (vw.clipcount != 0) { /* clipping not supported */ retval = -EINVAL; break; } if (vw.clips != NULL) { /* clipping not supported */ retval = -EINVAL; break; } if ((vw.width > MAX_HORIZ || vw.width < MIN_HORIZ) || (vw.height > MAX_VERT || vw.height < MIN_VERT)) { retval = -EINVAL; break; } /* we set the video window to something smaller or equal to what * is requested by the user??? */ down(&d->param_lock); if (vw.width != d->vw.width || vw.height != d->vw.height) { uint32_t scale_factor; /* XXXKW base percentage on input stream, not MAX? */ /* Assert scaler reset */ saa7114h_reg_write(d, 0x88, 0x98); /* Vertical scaling */ scale_factor = (MAX_VERT*1024) / vw.height; saa7114h_reg_write(d, 0x9e, vw.height & 0xff); saa7114h_reg_write(d, 0x9f, (vw.height >> 8) & 0xf); saa7114h_reg_write(d, 0xb0, scale_factor & 0xff); saa7114h_reg_write(d, 0xb1, (scale_factor >> 8) & 0xff); saa7114h_reg_write(d, 0xb2, scale_factor & 0xff); saa7114h_reg_write(d, 0xb3, (scale_factor >> 8) & 0xff); /* Horizontal scaling */ scale_factor = (MAX_HORIZ*1024) / vw.width; saa7114h_reg_write(d, 0x9c, vw.width & 0xff); saa7114h_reg_write(d, 0x9d, (vw.width >> 8) & 0xf); saa7114h_reg_write(d, 0xa8, scale_factor & 0xff); saa7114h_reg_write(d, 0xa9, (scale_factor >> 8) & 0xff); saa7114h_reg_write(d, 0xac, (scale_factor >> 1) & 0xff); saa7114h_reg_write(d, 0xad, (scale_factor >> 9) & 0xff); #if 0 /* prescaler saa7114h_reg_write(d, 0xa0, 2); saa7114h_reg_write(d, 0xa1, 1); saa7114h_reg_write(d, 0xa2, 1); */ #endif /* Release scaler reset */ saa7114h_reg_write(d, 0x88, 0xb8); d->vw.width = vw.width; d->vw.height = vw.height; } up(&d->param_lock); break; } /* mmap interface */ case VIDIOCGMBUF: { struct video_mbuf vm; int i; memset(&vm, 0, sizeof(vm)); vm.size = MAX_FRAME_SIZE*NUM_FRAME; vm.frames = NUM_FRAME; for (i = 0; i < NUM_FRAME; i++) vm.offsets[i] = MAX_FRAME_SIZE * i; if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) retval = -EFAULT; break; } case VIDIOCMCAPTURE: { struct video_mmap vm; int descr, status; if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) { retval = -EFAULT; break; } if (vm.frame<0||vm.frame>NUM_FRAME) { retval = -EINVAL; break; } DBG(DBG_CALL, printk(KERN_DEBUG IF_NAME ":ioctl MCAPTURE %d\n", vm.frame)); d->vp.palette = vm.format; /* XXXKW set depth? */ /* XXXKW match/update for vm.width, vm.height */ /* XXKW Should check this periodically!? */ status = saa7114h_reg_read(d, DECODER_STATUS); // d->interlaced = ((status & 0x80) != 0); /* Give the buffer to the DMA engine */ /* XXXKW vm.frame vs d->swframe!! mmap/read mismatch */ #if DMA_DEINTERLACE d->frame[vm.frame].pos = d->frame[vm.frame].data + d->vw.width*RAW_PER_PIXEL; #else d->frame[vm.frame].pos = d->frame[vm.frame].data; #endif #if !NULL_DMA d->frame[vm.frame].state = FRAME_READY; /* Fire up the DMA engine again if it stopped */ if (!d->dma_enable) { d->dma_enable = 1; d->hwframe = d->swframe = vm.frame; descr = in64(MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)); DBG(DBG_DESCR, printk(KERN_DEBUG IF_NAME ": capture adds %d -> %d descrs\n", d->ff.ringsz-descr, descr)); out64(d->ff.ringsz-descr, MAC2_DMARX0_CSR(R_MAC_DMA_DSCR_CNT)); } #endif break; } case VIDIOCSYNC: { int frame; if (copy_from_user((void *)&frame, arg, sizeof(int))) { retval = -EFAULT; break; } if (frame<0 || frame >= NUM_FRAME) { retval = -EINVAL; break; } DBG(DBG_CALL, printk(KERN_DEBUG IF_NAME ":ioctl CSYNC %d\n", frame)); switch (d->frame[frame].state) { case FRAME_UNUSED: DBG(DBG_IO, printk(KERN_ERR IF_NAME ":sync to unused frame %d\n", frame)); retval = -EINVAL; break; case FRAME_READY: case FRAME_GRABBING: DBG(DBG_IO, printk(KERN_DEBUG IF_NAME ": sleeping for frame %d\n", frame)); interruptible_sleep_on(&d->frame[frame].read_wait); DBG(DBG_IO, printk(KERN_DEBUG IF_NAME ": awakened\n")); if (signal_pending(current)) return -ERESTARTSYS; case FRAME_DONE: #if !NULL_DMA yuvconvert_inplace(d->frame[frame].data, d->frame[frame].size, d->vp.palette, 1); d->frame[frame].state = FRAME_UNUSED; #endif DBG(DBG_IO, printk(KERN_DEBUG IF_NAME ": sync finished %d\n", frame)); break; } break; } case VIDIOREADREG: reg = *(int *)arg; DBG(DBG_REGISTER, printk(KERN_DEBUG IF_NAME ": read of %02x\n", reg)); if ((reg > 0xEF) || (reg < 0)) return -EINVAL; val = saa7114h_reg_read((struct saa7114h *)vd->priv, reg); if (val == -1) return -EIO; *(int *)arg = val; break; case VIDIOWRITEREG: if (copy_from_user(®, arg, sizeof(int)) || copy_from_user(&val, arg+sizeof(int), sizeof(int))) return -EFAULT; DBG(DBG_REGISTER, printk(KERN_DEBUG IF_NAME ": write of %02x <- %02x\n", reg, val)); if ((reg > 0xEF) || (reg < 0)) return -EINVAL; val = saa7114h_reg_write((struct saa7114h *)vd->priv, reg, val); if (val == -1) return -EIO; break; case VIDIOGRABFRAME: return grab_frame((struct saa7114h *)vd->priv, arg, 0); case VIDIOSHOWEAV: return grab_frame((struct saa7114h *)vd->priv, arg, 1); default: retval = -EINVAL; break; } return retval; } static int saa7114h_mmap(struct video_device *vd, const char *adr, unsigned long size) { struct saa7114h *d = vd->priv; unsigned long start = (unsigned long)adr; unsigned long page, pos; if (!d) return -ENODEV; if (size > MAX_MMAP_SIZE) { printk("mmap: bad size %lu > %lu\n", size, MAX_MMAP_SIZE); return -EINVAL; } /* make this _really_ smp-safe */ if (down_interruptible(&d->busy_lock)) return -EINTR; pos = (unsigned long)(d->frame_buf); while (size > 0) { page = kvirt_to_pa(pos); if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) { up(&d->busy_lock); return -EAGAIN; } start += PAGE_SIZE; pos += PAGE_SIZE; if (size > PAGE_SIZE) size -= PAGE_SIZE; else size = 0; } up(&d->busy_lock); return 0; } /* ----------------------------------------------------------------------- * Device probing and initialization * ----------------------------------------------------------------------- */ /* Default values to program into SAA7114H */ static const unsigned char reg_init[] = { 0x00, 0x00, /* 00 - ID byte */ /*front end */ 0x01, 0x08, /* 01 - Horizontal increment -> recommended delay */ 0x02, 0xC4, /* 02 - AI Control 1 (CVBS AI23) */ 0x03, 0x10, /* 03 - AI Control 2 */ 0x04, 0x90, /* 04 - AI Control 3 (Gain ch 1) */ 0x05, 0x90, /* 05 - AI Control 4 (Gain ch 2) */ /* decoder */ 0x06, 0xEB, /* 06 - Horiz sync start */ 0x07, 0xE0, /* 07 - Horiz sync stop */ 0x08, 0x98, /* 08 - Sync control */ 0x09, 0x40, /* 09 - L Control */ 0x0a, 0x80, /* 0a - L Brightness */ 0x0b, 0x44, /* 0b - L Contrast */ 0x0c, 0x40, /* 0c - C Saturation */ 0x0d, 0x00, /* 0d - C Hue */ 0x0e, 0x89, /* 0e - C Control 1 */ 0x0f, 0x0f, /* 0f - C Gain (??? 0x2A recommended) */ 0x10, 0x0E, /* 10 - C Control 2 */ 0x11, 0x00, /* 11 - Mode/Delay */ 0x12, 0x00, /* 12 - RT signal control */ 0x13, 0x00, /* 13 - RT/X output */ 0x14, 0x00, /* 14 - Analog, Compat */ 0x15, 0x11, /* 15 - VGATE start */ 0x16, 0xFE, /* 16 - VGATE stop */ 0x17, 0x40, /* 17 - Misc VGATE (disable LLC2) */ 0x18, 0x40, /* 18 - Raw data gain - 128 */ 0x19, 0x80, /* 19 - Raw data offset - 0 */ /* Global settings */ 0x88, 0x98, /* 88 - AI1x on, AI2x off; decoder/slicer off; ACLK gen off */ 0x83, 0x00, /* 83 - X-port output disabled */ 0x84, 0xF0, /* 84 - I-port V/G output framing, IGP1=0=IGP0=0 */ 0x85, 0x00, /* 85 - I-port default polarities, X-port signals */ 0x86, 0x40, /* 86 - more IGP1/0, FIFO level, only video transmitted */ 0x87, 0x01, /* 87 - ICK default, IDQ default, I-port output enabled */ /* Task A: scaler input config and output format */ 0x90, 0x00, /* 90 - Task handling */ 0x91, 0x08, /* 91 - Scalar input and format */ 0x92, 0x10, /* 92 - Reference signal def */ 0x93, 0x80, /* 93 - I-port output */ /* Task B */ 0xc0, 0x42, /* 90 - Task handling */ 0xc1, 0x08, /* 91 - Scalar input and format */ 0xc2, 0x10, /* 92 - Reference signal def */ 0xc3, 0x80, /* 93 - I-port output */ /* Input and Output windows */ 0x94, 0x10, /* - */ 0x95, 0x00, /* - */ 0x96, 0xD0, /* - */ 0x97, 0x02, /* - */ 0x98, 0x0A, /* - */ 0x99, 0x00, /* - */ 0x9a, 0xF2, /* - */ 0x9b, 0x00, /* - */ 0x9c, 0xD0, /* - */ 0x9d, 0x02, /* - */ 0xc4, 0x10, /* - */ 0xc5, 0x00, /* - */ 0xc6, 0xD0, /* - */ 0xc7, 0x02, /* - */ 0xc8, 0x0A, /* - */ 0xc9, 0x00, /* - */ 0xca, 0xF2, /* - */ 0xcb, 0x00, /* - */ 0xcc, 0xD0, /* - */ 0xcd, 0x02, /* - */ 0x9e, 0xf0, /* - */ 0x9f, 0x00, /* - */ 0xce, 0xf0, /* - */ 0xcf, 0x00, /* - */ /* Prefiltering and prescaling */ 0xa0, 0x01, /* - */ 0xa1, 0x00, /* - */ 0xa2, 0x00, /* - */ 0xa4, 0x80, /* - */ 0xa5, 0x40, /* - */ 0xa6, 0x40, /* - */ 0xd4, 0x80, /* - */ 0xd5, 0x40, /* - */ 0xd6, 0x40, /* - */ /* Horizontal phase scaling */ 0xa8, 0x00, /* - */ 0xa9, 0x04, /* - */ 0xaa, 0x00, /* - */ 0xd8, 0x00, /* - */ 0xd9, 0x04, /* - */ 0xda, 0x00, /* - */ 0xac, 0x00, /* - */ 0xad, 0x02, /* - */ 0xae, 0x00, /* - */ 0xdc, 0x00, /* - */ 0xdd, 0x02, /* - */ 0xde, 0x00, /* - */ /* Vertical phase scaling */ 0xb0, 0x00, /* - */ 0xb1, 0x04, /* - */ 0xb2, 0x00, /* - */ 0xb3, 0x04, /* - */ 0xe0, 0x00, /* - */ 0xe1, 0x04, /* - */ 0xe2, 0x00, /* - */ 0xe3, 0x04, /* - */ 0xb4, 0x00, /* b4 - vscale mode control */ 0xe4, 0x00, /* b4 - vscale mode control */ /* Task enables */ 0x80, 0x10, /* 80 - LLC->ICLK, dq->IDQ, scaler->F/V timing, task enables */ /* Reset the slicer */ 0x88, 0xb8, /* 88 - AI1x on, AI2x off; decoder/slicer on; ACLK gen off */ }; static int saa7114h_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind) { struct i2c_client *client; struct video_device *vd; struct saa7114h *decoder; int err; int val, i; client = kmalloc(sizeof(*client), GFP_KERNEL); if (client == NULL) return -ENOMEM; client->adapter = adap; client->addr = addr; client->driver = &i2c_driver_saa7114h; strcpy(client->name, IF_NAME); decoder = kmalloc(sizeof(*decoder), GFP_KERNEL); if (decoder == NULL) { kfree(client); return -ENOMEM; } memset(decoder, 0, sizeof(struct saa7114h)); decoder->client = client; decoder->dma_enable = 0; decoder->palette = VIDEO_PALETTE_UYVY; decoder->depth = 16; decoder->vw.width = MAX_HORIZ; decoder->vw.height = MAX_VERT; decoder->frame_buf = rvmalloc(MAX_FRAME_SIZE*NUM_FRAME); if (!decoder->frame_buf) { kfree(decoder); kfree(client); return -ENOMEM; } /* XXXKW use clear_page? */ memset(decoder->frame_buf, 0, MAX_FRAME_SIZE*NUM_FRAME); printk("saa7114h_attach: frame_buf = (fb=%8p / %08lx)\n", decoder->frame_buf, kvirt_to_pa((int)decoder->frame_buf)); for (i=0; i<NUM_FRAME; i++) { decoder->frame[i].data = decoder->frame_buf+i*MAX_FRAME_SIZE; #if NULL_DMA decoder->frame[i].state = FRAME_DONE; #else decoder->frame[i].state = FRAME_UNUSED; #endif init_waitqueue_head(&decoder->frame[i].read_wait); } decoder->irq = K_INT_MAC_2; if (request_irq (decoder->irq, saa7114h_interrupt, 0, "Philips SAA7114h", decoder)) { rvfree(decoder->frame_buf, MAX_FRAME_SIZE*NUM_FRAME); kfree(decoder); kfree(client); return -ENOMEM; } init_MUTEX(&decoder->param_lock); init_MUTEX(&decoder->busy_lock); if ((err = i2c_attach_client(client)) < 0) { kfree(client); kfree(decoder); return err; } if (saa7114h_reg_init(decoder, reg_init, sizeof(reg_init)) || saa7114h_get_cparams(decoder)) { i2c_detach_client(client); kfree(client); kfree(decoder); return -ENODEV; } vd = kmalloc(sizeof(*vd), GFP_KERNEL); memset(vd, 0, sizeof(*vd)); if (vd == NULL) { i2c_detach_client(client); kfree(client); kfree(decoder); return -ENOMEM; } vd->priv = decoder; strcpy(vd->name, IF_NAME); vd->type = VID_TYPE_CAPTURE; vd->hardware = VID_HARDWARE_SAA7114H; vd->open = saa7114h_open; vd->close = saa7114h_release; vd->read = saa7114h_read; vd->ioctl = saa7114h_ioctl; vd->mmap = saa7114h_mmap; if ((err = video_register_device(vd, VFL_TYPE_GRABBER, -1)) < 0) { i2c_detach_client(client); kfree(client); kfree(decoder); kfree(vd); return err; } client->data = vd; decoder->vd = vd; /* Turn on the ITRDY - preserve the GENO pin for syncser */ val = in64(KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)); out64(M_MAC_MDIO_OUT | (val & M_MAC_GENC), KSEG1 + A_MAC_REGISTER(2, R_MAC_MDIO)); if ((err = dma_setup(decoder))) { i2c_detach_client(client); kfree(client); kfree(decoder); kfree(vd); return err; } printk("saa7114h_attach successful\n"); #ifdef CONFIG_PROC_FS proc_saa7114h_create(); create_proc_decoder(vd->priv); #endif MOD_INC_USE_COUNT; return 0; } /* Addresses to scan */ static unsigned short normal_i2c[] = {I2C_CLIENT_END}; static unsigned short normal_i2c_range[] = {0x20, 0x21, I2C_CLIENT_END}; static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; static struct i2c_client_address_data addr_data = { normal_i2c, normal_i2c_range, probe, probe_range, ignore, ignore_range, force }; static int saa7114h_probe(struct i2c_adapter *adap) { /* Look for this device on the given adapter (bus) */ if (adap->id == (I2C_ALGO_SIBYTE | I2C_HW_SIBYTE)) return i2c_probe(adap, &addr_data, &saa7114h_attach); else return 0; } static int saa7114h_detach(struct i2c_client *device) { #if 0 kfree(device->data); MOD_DEC_USE_COUNT; #endif #ifdef CONFIG_PROC_FS destroy_proc_decoder(((struct video_device *)device->data)->priv); proc_saa7114h_destroy(); #endif return 0; } /* ----------------------------------------------------------------------- */ static int __init swarm_7114h_init(void) { return i2c_add_driver(&i2c_driver_saa7114h); } static void __exit swarm_7114h_cleanup(void) { } MODULE_AUTHOR("Kip Walker, Broadcom Corp."); MODULE_DESCRIPTION("Philips SAA7114H Driver for Broadcom SWARM board"); module_init(swarm_7114h_init); module_exit(swarm_7114h_cleanup);
Go to most recent revision | Compare with Previous | Blame | View Log