/* Copyright (C) 2003 Cherry George Mathew 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. */ #include #include #include #include #include #include #include #include #include #include "pvcard.h" #include "pvcl.h" #include "pvproc.h" #include #include /* const declarations and static allocations */ /* card ids ends with vendor id as marker. Number of cards is defined \ * as MAX_CARDS. Will add PCI_DEVICE_ID_CIRRUS_5465 Later. */ const int clgd54xx_card_id[]={PCI_DEVICE_ID_CIRRUS_5480,\ PCI_DEVICE_ID_CIRRUS_5446,\ PCI_VENDOR_ID_CIRRUS}; /* Decided against kmalloc for the moment. Would probably be efficient * if we dealt with stuff like multiple cards, etc. not for the moment, * though. */ /* Default max screen size hardcoded to ???? */ static struct video_buffer vbuf_s = { }; static struct video_picture vpict_s; /* TODO: Chroma keying.....Default _no_ chromakeying bcos xawtv has no * support for it with V4L1. Someday.... Sigh ......*/ static struct video_window vwin_s = { /* chromakey: GD_CHROMA_KEY, */ /* flags: VIDEO_WINDOW_CHROMAKEY */ }; /* Tuner properties hardcoded. */ static struct video_tuner vtun_s = { name: "Television", rangelow: 0, rangehigh: 0x7FFFFFFF, flags: VIDEO_TUNER_PAL, mode: VIDEO_MODE_PAL }; /* Single channel support at the moment. S-Video and Composite ignored. */ static struct video_channel vchan_s = { name: "Television", tuners: 1, flags: VIDEO_VC_TUNER | VIDEO_VC_AUDIO, type: VIDEO_TYPE_TV, norm: VIDEO_MODE_PAL }; static struct gd_status_t dstat_s = { vbuf_p: &vbuf_s, vwin_p: &vwin_s, vtun_p: &vtun_s, vchan_p: &vchan_s, vpict_p: &vpict_s, }; /* Initializing a new adapter named clgd54xx. Right now, we'll make do with a static declaration. Not good enough for driving multiple cards. */ static struct clgd54xx_card clgd54xx_card_info = { spun_lock: SPIN_LOCK_UNLOCKED, drv_stat_p: &dstat_s, model: PVCLPP_COMBO }; static struct i2c_algo_bit_data clgd54xx_bitbang_adap = { data: &clgd54xx_card_info, setsda: gd54xx_setsda, setscl: gd54xx_setscl, getsda: gd54xx_getsda, getscl: gd54xx_getscl, udelay: 16, mdelay: 10, timeout: 200, }; static struct i2c_adapter clgd54xx_adap = { name: "clgd54xx", /* Now make i2c_adapter->algo_data point to our * i2c_algo_bit_data , ie; link adapter and algo. */ id: I2C_ALGO_BIT | I2C_HW_B_BT848, algo_data: &clgd54xx_bitbang_adap, }; /* variable declarations and initializations */ unsigned int debug = 0; unsigned int re_entry = 0; /* Adapter - Low level functions */ /* Note: * 5480 and 5446B can use either MMIO or PIO * 5446A must use PIO * 5465 must use MMIO */ unsigned io_readb(unsigned port) { vprintk("io_io_read on port %p\n", (char *) port); return inb(port); } void io_writeb(unsigned data, unsigned port) { vprintk("io_io_write on port %p\n", (char *) port); outb(data, port); } /*************** TODO: IMPLEMENT PCI MEM MAPPING ***************** unsigned m_io_readb_proc(unsigned port) { vprintk("m_io_read on port %p\n", (char *) port); return readb(port); } void m_io_writeb_proc(unsigned data, unsigned port) { vprintk("m_io_write on port %p\n", (char *) port); writeb(data, port); } *************************************************************************/ void gd_write_sr(struct clgd54xx_card *card_p,unsigned char datum,unsigned reg) { spin_lock_irqsave(&card_p->spun_lock, card_p->spinflags); io_writeb(reg, card_p->gd_io_base + GD_SR_OFFSET); io_writeb(datum, card_p->gd_io_base + GD_SR_OFFSET + 1); spin_unlock_irqrestore(&card_p->spun_lock, card_p->spinflags); } void gd_write_gr(struct clgd54xx_card *card_p, unsigned char datum,unsigned reg) { spin_lock_irqsave(&card_p->spun_lock,card_p->spinflags); io_writeb(reg, card_p->gd_io_base + GD_GR_OFFSET); io_writeb(datum, card_p->gd_io_base + GD_GR_OFFSET + 1); spin_unlock_irqrestore(&card_p->spun_lock,card_p->spinflags); } void gd_write_cr(struct clgd54xx_card *card_p, unsigned char datum,unsigned reg) { spin_lock_irqsave(&card_p->spun_lock, card_p->spinflags); io_writeb(reg, card_p->gd_io_base + GD_CR_OFFSET); io_writeb(datum, card_p->gd_io_base + GD_CR_OFFSET + 1); spin_unlock_irqrestore(&card_p->spun_lock,card_p->spinflags); } unsigned gd_read_sr(struct clgd54xx_card *card_p, unsigned reg) { unsigned value; spin_lock_irqsave(&card_p->spun_lock,card_p->spinflags); io_writeb(reg, card_p->gd_io_base + GD_SR_OFFSET); value = io_readb(card_p->gd_io_base + GD_SR_OFFSET + 1); spin_unlock_irqrestore(&card_p->spun_lock,card_p->spinflags); return value; } unsigned gd_read_gr(struct clgd54xx_card *card_p, unsigned reg) { unsigned value; spin_lock_irqsave(&card_p->spun_lock, card_p->spinflags); io_writeb(reg, card_p->gd_io_base + GD_GR_OFFSET); value = io_readb(card_p->gd_io_base + GD_GR_OFFSET + 1); spin_unlock_irqrestore(&card_p->spun_lock,card_p->spinflags); return value; } unsigned gd_read_cr(struct clgd54xx_card *card_p, unsigned reg) { unsigned value; spin_lock_irqsave(&card_p->spun_lock, card_p->spinflags); io_writeb(reg, card_p->gd_io_base + GD_CR_OFFSET); value = io_readb(card_p->gd_io_base + GD_CR_OFFSET + 1); spin_unlock_irqrestore(&card_p->spun_lock, card_p->spinflags); return value; } void gd54xx_setsda (void *bit_adap_dat, int state) { struct clgd54xx_card *data=bit_adap_dat; /* Switch on I2C interface */ set_bit(6, &data->i2c_state); /* Set/Clear bit */ state ? set_bit(1, &(data->i2c_state)) : clear_bit(1, &(data->i2c_state)); gd_write_sr(data, data->i2c_state, 0x8); } void gd54xx_setscl (void *bit_adap_dat, int state) { struct clgd54xx_card *data=bit_adap_dat; set_bit(6, &data->i2c_state); state ? set_bit(0, &(data->i2c_state)) : clear_bit(0, &(data->i2c_state)); gd_write_sr(data, data->i2c_state, 0x8); } int gd54xx_getsda (void *bit_adap_dat) { struct clgd54xx_card *data=bit_adap_dat; return (((data->i2c_state = gd_read_sr(data, 0x8)) >>7)&0x1); return 0; } int gd54xx_getscl (void *bit_adap_dat) { struct clgd54xx_card *data=bit_adap_dat; return (((data->i2c_state = gd_read_sr(data, 0x8)) >>2)&0x1); return 0; } /* Adapter functions - high level */ int i2c_clgd54xx_init_adapter(struct clgd54xx_card *card_p, struct i2c_adapter * adap, struct i2c_algo_bit_data * bitadap) { /* Let's initialize some of the data structures. * First plug in card adapter and card bit bang algo * into card info. */ card_p->clgd54xx_adapter_p = adap; card_p->clgd54xx_bitbang_adapter_p = bitadap; return i2c_bit_add_bus(card_p->clgd54xx_adapter_p); } int i2c_clgd54xx_cleanup_adapter(struct clgd54xx_card *card_p){ return i2c_bit_del_bus(card_p->clgd54xx_adapter_p); } int i2c_clgd54xx_probe_card(struct clgd54xx_card *card_p) { card_p->gd_io_base=0; /* Memory Mapped IO not supported at the moment. * This driver won't work for multiple cards. */ return 0; } int i2c_clgd54xx_find_card(struct clgd54xx_card *card_p){ struct pci_dev *dev = NULL; int loop_count=0; do{ if ((dev = pci_find_device(clgd54xx_card_id[MAX_CARDS],clgd54xx_card_id[loop_count], dev))) { printk(KERN_INFO "pvcl: Found %s\n", dev->name); card_p->clgd54xx_pci_dev_p = dev; card_p->clgd54xx_pci_dev_id = clgd54xx_card_id[loop_count]; printk(KERN_INFO "pvcl: Detected %dMB video ram.\n", gd_count_ram(card_p)); return 0; } loop_count++; } while(clgd54xx_card_id[loop_count]!=clgd54xx_card_id[MAX_CARDS]); printk(KERN_WARNING "pvcl: Sorry. Could not find a Cirrus Logic Chip.\n"); return -ENODEV; } /* * gd_count_ram() is hacked from gd5480.c from the xtv package. * xtv is copyrighted to Itai Nahshon . * Used with permission from the author. */ int gd_count_ram(struct clgd54xx_card *card_p) { int videoram = 1; int SR0F, SR17; unsigned short chip_type = card_p->clgd54xx_pci_dev_id; switch (chip_type) { case PCI_DEVICE_ID_CIRRUS_5446: videoram = 1; SR0F = gd_read_sr(card_p, 0x0F); SR17 = gd_read_sr(card_p, 0x17); if ((SR0F & 0x18) == 0x18) { if(SR0F & 0x80) { if(SR17 & 0x80) videoram = 2; else if(SR17 & 0x02) videoram = 3; else videoram = 4; } else { if((SR17 & 80) == 0) videoram = 2; } } break; case PCI_DEVICE_ID_CIRRUS_5480: videoram = 1; SR0F = gd_read_sr(card_p, 0x0F); if ((SR0F & 0x18) == 0x18) { /* 2 or 4 MB */ videoram = 2; if (SR0F & 0x80) /* Second bank enable */ videoram = 4; } break; case PCI_DEVICE_ID_CIRRUS_5465: videoram = 4; break; } card_p->vram = videoram; return videoram; } int __init i2c_clgd54xx_init(struct clgd54xx_card *card_p, struct i2c_adapter *adap, struct i2c_algo_bit_data *bitadap) { int ret_val; /* This adapter is non-re-entrant at the moment */ if(re_entry) return -EBUSY; re_entry = 1; if( (ret_val = i2c_clgd54xx_find_card(card_p)) ){ return ret_val; } if( (ret_val = i2c_clgd54xx_probe_card(card_p)) ){ return ret_val; } if( (ret_val = i2c_clgd54xx_init_adapter(card_p, adap, bitadap)) ){ return ret_val; } return ret_val; } int __init i2c_clgd54xx_cleanup(struct clgd54xx_card *card_p) { re_entry=0; return i2c_clgd54xx_cleanup_adapter( card_p ); } /* i2c client support */ void do_client_ioctl(struct file *file, unsigned int cmd, void *arg) { struct clgd54xx_card *card_p = file->private_data; struct i2c_adapter *adap = card_p->clgd54xx_adapter_p; int i; for (i = 0; i < I2C_CLIENT_MAX; i++) { if (NULL == adap->clients[i]) continue; if (NULL == adap->clients[i]->driver->command) continue; adap->clients[i]->driver->command( adap->clients[i],cmd,arg); } } /* VGA support functions. */ /* gd_bit_copy() - used for copying data to/from VGA registers */ void gd_bit_copy(unsigned long * dest, int dest_start, unsigned long * src, int src_start, int src_stop) { for(; src_start<= src_stop; src_start++, dest_start++) { if(test_bit(src_start, src)) set_bit(dest_start, dest); else clear_bit(dest_start, dest); } } void gd_enable_window(struct clgd54xx_card * card_p) { unsigned long CR3E, CR50, CR51, CR58, CR5C; CR3E = gd_read_cr(card_p, 0x3e); CR50 = gd_read_cr(card_p, 0x50); CR51 = gd_read_cr(card_p, 0x51); CR58 = gd_read_cr(card_p, 0x58); CR5C = gd_read_cr(card_p, 0x5c); /* Disable Teletext CR5C[7] = 0 */ clear_bit(7, &CR5C); /* Capture all frames CR50[7], CR50[2], CR58[6] = 0,0,0 */ clear_bit(7, &CR50); clear_bit(2, &CR50); clear_bit(6, &CR58); /* Set Capture input to VPORT CR50[1:0] = 11 */ set_bit(0, &CR50); /* Falling edge of HREF ends */ set_bit(1, &CR50); /* capture line. */ /* Enable Capture CR51[3] = 1 */ set_bit(3, &CR51); /* Enable Video Window CR3E[0] = 1 */ set_bit(0, &CR3E); gd_write_cr(card_p, CR5C, 0x5c); gd_write_cr(card_p, CR58, 0x58); gd_write_cr(card_p, CR51, 0x51); gd_write_cr(card_p, CR50, 0x50); gd_write_cr(card_p, CR3E, 0x3e); } void gd_disable_window(struct clgd54xx_card * card_p) { unsigned long CR3E, CR50, CR51; CR3E = gd_read_cr(card_p, 0x3e); CR50 = gd_read_cr(card_p, 0x50); CR51 = gd_read_cr(card_p, 0x51); /* Disable Capture CR51[3] = 0 */ clear_bit(3, &CR51); /* Reset Capture input to standard feature connector CR50[1:0] = 11 */ clear_bit(0, &CR50); /* Falling edge of HREF ends capture */ clear_bit(1, &CR50); /* Disable Video Window CR3E[0] = 0 */ clear_bit(0, &CR3E); gd_write_cr(card_p, CR51, 0x51); gd_write_cr(card_p, CR50, 0x50); gd_write_cr(card_p, CR3E, 0x3e); } void gd_set_vbuf1(struct clgd54xx_card * card_p, unsigned long ptr) { unsigned long CR3A, CR3B, CR3C, CR5D; CR3A = gd_read_cr(card_p, 0x3a); CR3B = gd_read_cr(card_p, 0x3b); CR3C = gd_read_cr(card_p, 0x3c); CR5D = gd_read_cr(card_p, 0x5d); /* CR5D[3:2] = ptr[1:0] */ gd_bit_copy( &CR5D, 2, &ptr, 0, 1); /* CR3A = ptr[9:2] */ gd_bit_copy( &CR3A, 0, &ptr, 2, 9); /* CR3B = ptr[17:10] */ gd_bit_copy(&CR3B, 0, &ptr, 10, 17); /* CR3C[3:0] = ptr[21:18] */ gd_bit_copy(&CR3C, 0, &ptr, 18, 21); gd_write_cr(card_p, CR3A, 0x3a); gd_write_cr(card_p, CR3B, 0x3b); gd_write_cr(card_p, CR3C, 0x3c); gd_write_cr(card_p, CR5D, 0x5d); } void gd_set_vbuf2(struct clgd54xx_card * card_p, unsigned long ptr) { unsigned long CR59, CR5A, CR58, CR5D; CR59 = gd_read_cr(card_p, 0x59); CR5A = gd_read_cr(card_p, 0x5a); CR58 = gd_read_cr(card_p, 0x58); CR5D = gd_read_cr(card_p, 0x5d); /* CR5D[3:2] = ptr[1:0] */ gd_bit_copy(&CR5D, 2, &ptr, 0, 1); /* CR59 = ptr[9:2] */ gd_bit_copy(&CR59, 0, &ptr, 2, 9); /* CR5A = ptr[17:10] */ gd_bit_copy(&CR5A, 0, &ptr, 10, 17); /* CR58[3:0] = ptr[21:18] */ gd_bit_copy(&CR58, 0, &ptr, 18, 21); gd_write_cr(card_p, CR59, 0x59); gd_write_cr(card_p, CR5A, 0x5a); gd_write_cr(card_p, CR58, 0x58); gd_write_cr(card_p, CR5D, 0x5d); } unsigned long gd_get_vbuf1(struct clgd54xx_card * card_p) { unsigned long CR3A, CR3B, CR3C, CR5D; unsigned long ptr; CR3A = gd_read_cr(card_p, 0x3a); CR3B = gd_read_cr(card_p, 0x3b); CR3C = gd_read_cr(card_p, 0x3c); CR5D = gd_read_cr(card_p, 0x5d); /* ptr[1:0] = CR5D[3:2] */ ptr = 0; gd_bit_copy( &ptr, 0, &CR5D, 2, 3); /* ptr[9:2] = CR3A */ gd_bit_copy( &ptr, 2, &CR3A, 0, 7); /* ptr[17:10] = CR3B */ gd_bit_copy(&ptr, 10, &CR3B, 0, 7); /* ptr[21:18] = CR3C[3:0] */ gd_bit_copy(&ptr, 18, &CR3C, 0, 3); return ptr; } unsigned long gd_get_vbuf2(struct clgd54xx_card * card_p) { unsigned long CR59, CR5A, CR58, CR5D; unsigned long ptr; CR59 = gd_read_cr(card_p, 0x59); CR5A = gd_read_cr(card_p, 0x5a); CR58 = gd_read_cr(card_p, 0x58); CR5D = gd_read_cr(card_p, 0x5d); /* ptr[1:0] = CR5D[3:2] */ ptr = 0; gd_bit_copy(&ptr, 0, &CR5D, 2, 3); /* ptr[9:2] = CR59 */ gd_bit_copy(&ptr, 2, &CR59, 0, 7); /* ptr[17:10] = CR5A */ gd_bit_copy(&ptr, 10, &CR5A, 0, 7); /* ptr[21:18] = CR58[3:0] */ gd_bit_copy(&ptr, 18, &CR58, 0, 3); return ptr; } void gd_set_pitch(struct clgd54xx_card * card_p, unsigned long offset) { unsigned long CR3C, CR3D; CR3C = gd_read_cr(card_p, 0x3c); CR3D = gd_read_cr(card_p, 0x3d); /* CR3C[5] = offset[11], CR3D = offset[10:3] */ gd_bit_copy(&CR3C, 5, &offset ,11, 11); gd_bit_copy(&CR3D, 0, &offset, 3, 10); gd_write_cr(card_p, CR3C, 0x3c); gd_write_cr(card_p, CR3D, 0x3d); } unsigned long gd_get_pitch(struct clgd54xx_card * card_p) { unsigned long CR3C, CR3D; unsigned long offset ; CR3C = gd_read_cr(card_p, 0x3c); CR3D = gd_read_cr(card_p, 0x3d); /* offset[11] = CR3C[5] , offset[10:3] = CR3D */ offset = 0; gd_bit_copy(&offset, 11, &CR3C, 5, 5); gd_bit_copy(&offset, 3, &CR3D, 0, 7); return offset; } void gd_init_video( struct clgd54xx_card * card_p ) { unsigned long temp_reg; unsigned long CR31, CR32, CR3E, CR3F, CR50, CR51, CR56, CR57, CR58, CR5C, CR5E; /* unsigned long GRC, GRD; */ CR31 = gd_read_cr(card_p, 0x31); CR32 = gd_read_cr(card_p, 0x32); CR3E = gd_read_cr(card_p, 0x3e); CR3F = gd_read_cr(card_p, 0x3f); CR50 = gd_read_cr(card_p, 0x50); CR51 = gd_read_cr(card_p, 0x51); CR56 = gd_read_cr(card_p, 0x56); CR57 = gd_read_cr(card_p, 0x57); CR58 = gd_read_cr(card_p, 0x58); /* Dependancy only on maxheight */ CR5C = gd_read_cr(card_p, 0x5c); CR5E = gd_read_cr(card_p, 0x5e); /* GRC = gd_read_gr(card_p, 0x0c); */ /* GRD = gd_read_gr(card_p, 0x0d); */ /* Set the maximum scanlines to 512, we don't want * wanton clipping. */ temp_reg = 0xff; gd_bit_copy(&CR57, 0, &temp_reg, 0, 7); set_bit(5, &CR58); /* Luminance only capture disabled */ /* CR5C[5] = 0 */ clear_bit(5, &CR5C); /* Count lines upto capture, set to zero */ /* CR56[4:0] = 0 */ temp_reg = 0; gd_bit_copy(&CR56, 0, &temp_reg, 0, 4); /* Non - Interlaced capture */ /* CR50[6] = 0 */ clear_bit(6, &CR50); /* 8bits, VPort width */ /* CR50[4] = 0; */ clear_bit(4, &CR50); /* Double clock capture */ /* CR50[3] = 1; */ set_bit(3, &CR50); /* Video Capture (Not Teletext) */ /* CR5C[7] = 0 */ clear_bit(7, &CR5C); /* Capture Data Format - YUV 4:2:2 */ /* CR51[2:0] = 000 */ clear_bit(0, &CR51); clear_bit(1, &CR51); clear_bit(2, &CR51); /* Display Data Format - YUV 4:2:2 */ /* CR3E[3:1] = 000 */ /* CR3F[4] = 0 */ clear_bit(1, &CR3E); clear_bit(2, &CR3E); clear_bit(3, &CR3E); clear_bit(4, &CR3F); /* Double Buffer Control - Autoswitch */ /* CR5E[5:4] = 10 */ clear_bit(4, &CR5E); set_bit(5, &CR5E); /* Set Zoom = 1:1 ie., ZoomX = ZoomY = 0 */ /* ZoomX = CR31, ZoomY = CR32 */ CR31 = CR32 = 0; /* Also, Zoom mode set to 1; ie; line replication, * in anticipation of chromakeying.. */ set_bit(4, &CR3E); /* Occlusion set to Colour Key, tagged compare (16 bit). */ /* CR1D[5:3] = 011 */ /* set_bit(3, &CR1D); */ /* clear_bit(4, &CR1D); */ /* clear_bit(5, &CR1D); */ /* Set the Chroma key. */ /* GRC = 0; */ /* GRD = GD_CHROMA_KEY; */ /* Switch on Chromakeying */ /* set_bit(7, &CR3E); */ /* gd_write_gr(card_p, GRC, 0x0c); */ /* gd_write_gr(card_p, GRD, 0x0d); */ gd_write_cr(card_p, CR50, 0x50); gd_write_cr(card_p, CR51, 0x51); gd_write_cr(card_p, CR56, 0x56); gd_write_cr(card_p, CR57, 0x57); gd_write_cr(card_p, CR58, 0x58); /* May be safely removed with Mx ht.*/ gd_write_cr(card_p, CR5C, 0x5c); gd_write_cr(card_p, CR5E, 0x5e); gd_write_cr(card_p, CR31, 0x31); gd_write_cr(card_p, CR32, 0x32); gd_write_cr(card_p, CR3E, 0x3e); gd_write_cr(card_p, CR3F, 0x3f); } void gd_get_window(struct clgd54xx_card * card_p, struct video_window *winstruct, struct video_buffer * vbuf) { int depth; unsigned long R1SZ, R1Adjust, R2SZ, R2Adjust, R2DSZ, WVS, WVE, WAO; unsigned long CR33, CR34, CR35, CR36, CR37, CR38, CR39, CR5D; depth = (vbuf->depth == 32 ? 24 : vbuf->depth); CR33 = gd_read_cr(card_p, 0x33); CR34 = gd_read_cr(card_p, 0x34); CR35 = gd_read_cr(card_p, 0x35); CR36 = gd_read_cr(card_p, 0x36); CR37 = gd_read_cr(card_p, 0x37); CR38 = gd_read_cr(card_p, 0x38); CR39 = gd_read_cr(card_p, 0x39); CR5D = gd_read_cr(card_p, 0x5d); /* R1SZ = CR36[1:0] CR33 */ R1SZ = 0; gd_bit_copy(&R1SZ, 8, &CR36, 0, 1); gd_bit_copy(&R1SZ, 0, &CR33, 0, 7); /* R1Adjust = CR5D[1:0] */ R1Adjust = 0; gd_bit_copy(&R1Adjust, 0, &CR5D, 0, 1); /* R2SZ = CR36[3:2] CR34 */ R2SZ = 0; gd_bit_copy(&R2SZ, 8, &CR36, 2, 3); gd_bit_copy(&R2SZ, 0, &CR34, 0, 7); /* R2Adjust = CR5D[5:4] */ R2Adjust = 0; gd_bit_copy(&R2Adjust, 0, &CR5D, 4, 5); /* R2DSZ = CR36[5:4] CR35 */ R2DSZ = 0; gd_bit_copy(&R2DSZ, 8, &CR36, 4, 5); gd_bit_copy(&R2DSZ, 0, &CR35, 0, 7); /* WVS = CR39[1:0] CR37 */ WVS = 0; gd_bit_copy(&WVS, 8, &CR39, 0, 1); gd_bit_copy(&WVS, 0, &CR37, 0, 7); /* WVE = CR39[3:2] CR38 */ WVE = 0; gd_bit_copy(&WVE, 8, &CR39, 2, 3); gd_bit_copy(&WVE, 0, &CR38, 0, 7); WAO = 0; WAO = gd_get_pitch(card_p); tprintk("gd_get_window() hardware registers read:\n depth = %d bpp \n R1SZ = %ld\n R1Adjust = %ld\n R2SZ = %ld\n R2Adjust = %ld\n R2DSZ = %ld\n WVS=%ld\n WVE=%ld\n WAO=%ld\n", depth, R1SZ, R1Adjust, R2SZ, R2Adjust, R2DSZ, WVS, WVE, WAO); winstruct->x = R1SZ * 32 / depth + R1Adjust * 8 / depth; winstruct->width = R2SZ * 32 / depth + R2Adjust * 8 / depth; winstruct->y = WVS; winstruct->height = WVE - WVS; } void gd_set_window(struct clgd54xx_card * card_p, struct video_window *winasked, struct video_window *wingiven, struct video_buffer *vbuf) { int depth; unsigned long R1SZ, R1Adjust, R2SZ, R2Adjust, R2DSZ, WVS, WVE, WAO; unsigned ZOOMY, CR32; unsigned long CR33, CR34, CR35, CR36, CR37, CR38, CR39, CR5D; depth = (vbuf->depth == 32 ? 24 : vbuf->depth); /* Safety Checks and clipping, for window borders. */ winasked->x = winasked->x < 0 ? 0 : winasked->x; winasked->y = winasked->y < 0 ? 0 : winasked->y; winasked->width = (vbuf->width - winasked->x) < winasked->width ? (vbuf->width - winasked->x) : winasked->width; winasked->height = (vbuf->height - winasked->y) < winasked->height ? (vbuf->height - winasked->y) : winasked->height; R1SZ = (winasked->x * depth) / 32; R1Adjust = (winasked->x * depth - R1SZ * 32) / 8; R2SZ = (winasked->width * depth) / 32; R2Adjust = (winasked->width * depth - R2SZ * 32) / 8; /*ugly hack. only tried on 24bpp and 16bpp eqn is. vbpp/gbpp * .5 */ R2DSZ = R2SZ * 16 / depth + R2Adjust * 4 / depth; WVS = winasked->y; WVE = WVS + winasked->height; WAO = R2SZ * 4; /* Zoom treatment. Doing zoom just for Vlines. * Notes: Occlusion, ie; chromakeying and line interpolation * (in the case of zooming) are mutually exclusive. */ if(wingiven->height < winasked->height) { ZOOMY = (wingiven->height * 256) / winasked->height - 5; CR32 = ZOOMY; } else CR32 = 0; /* Don't want old Zoom values lying about. */ /* CR36[1:0] CR33 = R1SZ */ gd_bit_copy(&CR36, 0, &R1SZ, 8, 9); gd_bit_copy(&CR33, 0, &R1SZ, 0, 7); /* CR5D[1:0] = R1Adjust */ gd_bit_copy(&CR5D, 0, &R1Adjust, 0, 1); /* CR36[3:2] CR34 = R2SZ */ gd_bit_copy(&CR36, 2, &R2SZ, 8, 9); gd_bit_copy(&CR34, 0, &R2SZ, 0, 7); /* CR5D[5:4] = R2Adjust */ gd_bit_copy(&CR5D, 4, &R2Adjust, 0, 1); /* CR36[5:4] CR35 = R2DSZ */ gd_bit_copy(&CR36, 4, &R2DSZ, 8, 9); gd_bit_copy(&CR35, 0, &R2DSZ, 0, 7); /* CR39[1:0] CR37 = WVS */ gd_bit_copy(&CR39, 0, &WVS, 8, 9); gd_bit_copy(&CR37, 0, &WVS, 0, 7); /* CR39[3:2] CR38 = WVE */ gd_bit_copy(&CR39, 2, &WVE, 8, 9); gd_bit_copy(&CR38, 0, &WVE, 0, 7); /* CR36[5:4] CR35 = R2DSZ */ gd_bit_copy(&CR36, 4, &R2DSZ, 8, 9); gd_bit_copy(&CR35, 0, &R2DSZ, 0, 7); gd_set_pitch(card_p, WAO); tprintk("gd_set_window() hardware registers set to:\n R1SZ = %ld\n R1Adjust = %ld\n R2SZ = %ld\n R2Adjust = %ld\n R2DSZ = %ld\n WVS=%ld\n WVE=%ld\n WAO=%ld\n", R1SZ, R1Adjust, R2SZ, R2Adjust, R2DSZ, WVS, WVE, WAO); gd_write_cr(card_p, CR5D, 0x5d); gd_write_cr(card_p, CR39, 0x39); gd_write_cr(card_p, CR38, 0x38); gd_write_cr(card_p, CR37, 0x37); gd_write_cr(card_p, CR36, 0x36); gd_write_cr(card_p, CR35, 0x35); gd_write_cr(card_p, CR34, 0x34); gd_write_cr(card_p, CR33, 0x33); gd_write_cr(card_p, CR32, 0x32); } /* returns clipped windows, according to available ram, with * video buffer starting as close to the end of video ram as possible. */ long gd_window_init(struct clgd54xx_card * card_p) { int scrn_depth; unsigned long vbuf1_ptr, vbuf2_ptr, scrn_ht, scrn_wt, vram; vram = card_p->vram = gd_count_ram(card_p); scrn_ht = card_p->drv_stat_p->vbuf_p->height; scrn_wt = card_p->drv_stat_p->vbuf_p->width; scrn_depth = card_p->drv_stat_p->vbuf_p->depth; scrn_depth = scrn_depth == 32 ? 24 :scrn_depth; vbuf1_ptr = vram * 1048576 - 16384 - scrn_ht * scrn_wt * 2; /* Eeeks!!! No check for video data spilling over into framebuffer * space. OK folk. Get ready for full screen video! if(vbuf1_ptr < (scrn_ht * scrn_wt * scrn_depth / 8)) return -1; */ vbuf2_ptr = vbuf1_ptr; gd_init_video(card_p); printk("Marker \n"); gd_set_vbuf1(card_p, vbuf1_ptr); gd_set_vbuf2(card_p, vbuf2_ptr); return vbuf2_ptr; } /* V4L Driver Callback functions */ static int pvcl_open(struct inode *inode, struct file *file) { unsigned int minor = minor(inode->i_rdev); struct clgd54xx_card * card_p; file->private_data = &clgd54xx_card_info; card_p = file->private_data; do_client_ioctl(file, VPROC_INIT, &card_p->model); dprintk("pvcl_open called on minor %d\n", minor); return 0; } static int pvcl_release(struct inode *inode, struct file *file) { file->private_data = NULL; dprintk("pvcl_release called.\n"); return 0; } static int pvcl_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { struct clgd54xx_card * card_p = file->private_data; switch(cmd) { case VIDIOCGCAP: { struct video_capability *cap = arg; struct video_buffer *bufstat = card_p->drv_stat_p->vbuf_p; sprintf(cap->name, "pvcl"); cap->type = VID_TYPE_TUNER | VID_TYPE_OVERLAY | VID_TYPE_FRAMERAM | VID_TYPE_SCALES; cap->channels = 1; /* Just one channel for now. */ cap->audios = 1; /* Max Screen Size reported by X11 via v4l-conf */ cap->maxwidth = bufstat->width; cap->maxheight = bufstat->height; cap->minwidth = 32; /* Hardcoded vpx 3225D specific */ cap->minheight = 24; /* Hard Code PAL for the moment. */ do_client_ioctl(file, VPROC_SET_CAP_MODE, &card_p->drv_stat_p->vtun_p->mode); dprintk("VIDIOCGCAP called\n"); return 0; } case VIDIOCGFBUF: { struct video_buffer *buf = arg; struct video_buffer *bufstat = card_p->drv_stat_p->vbuf_p; memcpy(buf, bufstat, sizeof(*buf)); dprintk("VIDIOCGFBUF called. \n"); return 0; } case VIDIOCSFBUF: { struct video_buffer *buf = arg; struct video_buffer *bufstat = card_p->drv_stat_p->vbuf_p; /* HEEEEEEEEEEEEELOOOOOOOOOOOOOOOO, Careful. Security hole here!!!!!!!!!!! */ /* Security check disabled for now. if(!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) return -EPERM; */ memcpy(bufstat, buf, sizeof(*bufstat)); dprintk("VIDIOCSFBUF called. \n"); printk(KERN_INFO "client reports:\n screen size: %dx%d @ %dbpp. %d bytesperline.\n.", bufstat->width, bufstat->height, bufstat->depth, bufstat->bytesperline); dprintk("Capture Buffer set to 0x%p", bufstat->base); /* Now that we've got the Screen Parms, * we can inititialize the window */ gd_window_init(card_p); return 0; } case VIDIOCCAPTURE: { int *on = arg; struct video_buffer *bufstat = card_p->drv_stat_p->vbuf_p; if (*on) { /* verify args */ if (NULL == bufstat->base) return -EINVAL; if (bufstat->width <32 || bufstat->height <24 || bufstat->width >800 || bufstat->height >600) return -EINVAL; } if(*on) { /* Tell vpx to switch on. `on' is dummy here.*/ do_client_ioctl(file, VPROC_START_CAPTURE, on); gd_enable_window(card_p); dprintk("VIDIOCCAPTURE: video window switched on. \n"); return 0; } else { /* Tell vpx to stop capture. `on' is dummy.*/ do_client_ioctl(file, VPROC_STOP_CAPTURE, on); gd_disable_window(card_p); dprintk("VIDIOCCAPTURE: video window switched off. \n"); return 0; } } case VIDIOCGWIN: { struct video_window *winarg = arg; struct video_window *winstat = card_p->drv_stat_p->vwin_p; memcpy(winarg, winstat, sizeof(*winarg)); /* TODO: Need to get actual window parms from * hardware. Use gd_get_video_window() */ dprintk("VIDIOCGWIN called. \n"); return 0; } case VIDIOCSWIN: { struct video_window *winarg = arg; struct video_window *winstat = card_p->drv_stat_p->vwin_p; struct video_buffer *bufstat = card_p->drv_stat_p->vbuf_p; memcpy(winstat, winarg, sizeof(*winstat)); tprintk("VIDIOCSWIN recieved:\n \t position: %d, %d \n \t dimensions: %d, %d \n", winstat->x, winstat->y, winstat->width, winstat->height); do_client_ioctl(file, VPROC_SET_WINDOW, winarg); /* gd_set_window(card_p, app_wants, vproc_gives....) */ gd_set_window(card_p, winstat, winarg, bufstat); tprintk("gd_set_window() recieved:\n \t position: %d, %d \n \t dimensions: %d, %d \n", winstat->x, winstat->y, winstat->width, winstat->height); gd_get_window(card_p, winstat, bufstat); tprintk("gd_get_window returned: \n \t position: %d, %d \n \t dimensions: %d, %d \n", winstat->x, winstat->y, winstat->width, winstat->height); dprintk("VIDIOCSWIN called. \n"); return 0; } /* Wrappers for tuner.c - hacked from bttv-driver.c which is copyrighted to : Copyright (C) 1996,97,98 Ralph Metzler , Marcus Metzler , & (c) 1999-2002 Gerd Knorr */ case VIDIOCGTUNER: { struct video_tuner *v = arg; struct video_tuner *vstat = card_p->drv_stat_p->vtun_p; if (v->tuner) /* Only tuner 0 */ return -EINVAL; do_client_ioctl(file,cmd,v); /* tuner.c returns v->signal, ie; signal strength. */ memcpy(v, vstat, sizeof(*v)); dprintk("VIDIOCGTUNER has been called. \n"); return 0; } case VIDIOCSTUNER: { struct video_tuner *v = arg; struct video_tuner *vstat = card_p->drv_stat_p->vtun_p; if (v->tuner) /* Only tuner 0 */ return -EINVAL; if (v->mode > VIDEO_MODE_PAL) return -EINVAL; do_client_ioctl(file,cmd,v); vstat->mode = v->mode; dprintk("VIDIOCSTUNER has been called. \n"); return 0; } case VIDIOCGFREQ: { unsigned long *freq = arg; *freq = card_p->drv_stat_p->freq; dprintk("VIDIOCGFREQ called.\n"); return 0; } case VIDIOCSFREQ: { unsigned long *freq = arg; do_client_ioctl(file,cmd,freq); card_p->drv_stat_p->freq=*freq; dprintk("VIDIOCSFREQ called.\n"); return 0; } case VIDIOCSCHAN: { struct video_channel *v = arg; struct video_tuner *vstat = card_p->drv_stat_p->vtun_p; if (v->channel > 0) return -EINVAL; if(v->norm > VIDEO_MODE_PAL) return -EINVAL; /* TODO: need to implement tv norms. * Currently hardcoded * to PAL. See struct video_channel * vchan_s decl above. */ do_client_ioctl(file,cmd,v); memcpy(vstat, v , sizeof(*v)); dprintk("VIDIOCSCHAN has been called. \n"); return 0; } case VIDIOCGCHAN: { struct video_channel *v = arg; struct video_channel *chanstat = card_p->drv_stat_p->vchan_p; memcpy(v, chanstat, sizeof(*v)); dprintk("VIDIOCGCHAN has been called. \n"); return 0; } /* End bttv-driver.c wrappers. */ case VIDIOCGPICT: { struct video_picture *vpict = arg; struct video_picture *vpictstat = card_p->drv_stat_p->vpict_p; memcpy(vpict, vpictstat, sizeof(*vpict)); dprintk("VIDIOCGPICT has been called. \n"); } case VIDIOCSPICT: { struct video_picture *vpict = arg; struct video_picture *vpictstat = card_p->drv_stat_p->vpict_p; memcpy(vpictstat, vpict, sizeof(*vpict)); do_client_ioctl(file, VPROC_SET_PICTURE, vpictstat); dprintk("VIDIOCGPICT has been called. \n"); } } return -ENOIOCTLCMD; } static int pvcl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { return video_usercopy(inode, file, cmd, arg, pvcl_do_ioctl); } static ssize_t pvcl_read(struct file *file, char *data, size_t count, loff_t *ppos) { return 0; } static struct file_operations pvcl_fops = { owner: THIS_MODULE, open: pvcl_open, release: pvcl_release, ioctl: pvcl_ioctl, llseek: no_llseek, read: pvcl_read }; static struct video_device pvcl_video_dev = { name: "PVCL ver 0.1", type: VID_TYPE_TUNER | VID_TYPE_OVERLAY | VID_TYPE_FRAMERAM | VID_TYPE_SCALES, fops: &pvcl_fops, minor: -1 }; /* Device Registration */ static int __devinit pvcl_register_video(struct video_device * video_dev ) { if(video_register_device(video_dev, VFL_TYPE_GRABBER, 0)<0) return -1; printk(KERN_INFO "pvcl: registered device video\n"); return 0; } static void __devexit pvcl_unregister_video(struct video_device * video_dev) { video_unregister_device(video_dev); } static int pvcl_init_module(void) { int retval; if( (retval = pvcl_register_video( &pvcl_video_dev)) ) return retval; if( (retval = i2c_clgd54xx_init(&clgd54xx_card_info, &clgd54xx_adap, &clgd54xx_bitbang_adap)) ) return retval; switch(debug) { case 1: { printk(KERN_INFO "pvcl: Functional Debug messages enabled.\n"); break; } case 2: { printk(KERN_INFO "pvcl: Debug messages enabled per function.\n"); break; } case 3: printk(KERN_INFO "pvcl: WARNING: Verbose Debugging enabled - kernel printk buffers may overflow.\n"); break; } return retval; } static void pvcl_cleanup_module(void) { i2c_clgd54xx_cleanup(&clgd54xx_card_info); pvcl_unregister_video(&pvcl_video_dev); } module_init(pvcl_init_module); module_exit(pvcl_cleanup_module); MODULE_PARM(debug,"i"); MODULE_PARM_DESC(debug,"debug messages, default 0, is don't debug."); MODULE_DESCRIPTION("pvcl - v4l driver module for the pixelview combo tv plus"); MODULE_AUTHOR("Cherry George Mathew"); MODULE_LICENSE("GPL"); /* * Local variables: * c-basic-offset: 8 * End: */