Chris, I have two versions of the saa7113 driver I've been playing with. One I wrote for usbvision-0.3.0, and a modified version of the the one that came with usbvision-0.3.3. I've attached both, 'saa7113.c' is the one I modified from the kfir/saa7111 driver, and 'saa7113-new.c' is the modified driver that came with usbvision-0.3.3. They are pretty much the same except for the init data. I included both because my original has comments for which mode options I selected for each register. > I modified the saa7111-new.c driver that was in the usbvision tar ball. > I changed the address from 0x48 to 0x4a. That fixed the NAK I was > getting from the saa driver. I also imported the init parameters from > the saa7113 driver that comes with kfir, and now I get a string of null > packets when I try to capture an image. So, some progress. The null packets are an indication that the NT1004 chip isn't receiving the valid input from the saa7113 decoder chip. This is what led me to the output control registers 0x11, 0x12 and 0x13. I found by setting 0x12 to output a horizontal reference signal on RTS0 that I could get frames to be captured. I infered this to mean the NT1004 uses this output to know when to start capturing frames. I don't know if its necessary to change the NT1004 setup in the usbvision driver for this though, I'm still experimenting. > I am testing using a Belkin USBvideobus II (0x050d/0x0208) and a WinTV > usb (0x0573/0x4d22). My version of usbvision fails to recognize the > WinTV because in usbvision_probe there is a test for num configurations > = 1 and my WinTV has 4. I hacked it to skip this test, and it gets a bit > further but still not far enough. Most of my testing has been with the > Belkin. I am still working on it... Yes my WinTv also has mutiple configurations so I disabled the test as well. I don't think it should matter because the default configuration should work. My understanding is the other configurations are for enabling audio transfer channels, which WinTv's made before Janurary 2002 don't support, and for reducing the maximum packet size for the usb transfers to reduce the load placed on the bus by the capture device. Cameron
/* saa7113 - Philips SAA7113 video decoder driver version 0.0.1 - Adapted from the SAA7111 driver Copyright (C) 1998 Dave Perks <dperks@xxxxxxx> 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 <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> #include <linux/string.h> #include <linux/timer.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/slab.h> #include <linux/poll.h> #include <linux/i2c.h> #include <linux/types.h> #include <linux/videodev.h> #include <linux/video_decoder.h> #define DEBUG(x) /* Debug driver */ /* Addresses to scan */ #define I2C_SAA7113 0x4A static unsigned short normal_i2c[] = { I2C_SAA7113 >> 1, I2C_CLIENT_END }; static unsigned short normal_i2c_range[] = { 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 struct i2c_driver driver; static struct i2c_client client_template; /* ----------------------------------------------------------------------- */ struct saa7113 { // struct i2c_bus *bus; // int addr; unsigned char reg[32]; int norm; int input; int enable; int bright; int contrast; int hue; int sat; }; /* ----------------------------------------------------------------------- */ static int saa7113_write(struct i2c_client *client, unsigned char subaddr, unsigned char data) { unsigned char buffer[2]; buffer[0] = subaddr; buffer[1] = data; if (2 != i2c_master_send(client, buffer, 2)) { printk(KERN_WARNING "saa7113: I/O error, trying (write %d 0x%x)\n", subaddr, data); return -1; } return 0; } static int saa7113_write_block(struct i2c_client *client, unsigned const char *data, unsigned int len) { if (len != i2c_master_send(client, data, len)) { printk(KERN_WARNING "saa7113: I/O error, trying write\n"); return -1; } return 0; } static int saa7113_read(struct i2c_client *client, unsigned char subaddr) { unsigned char buffer; if (1 != i2c_master_send(client, &subaddr, 1)) { printk(KERN_WARNING "saa7113: I/O error, trying (write %d)\n", subaddr); return -1; } if (1 != i2c_master_recv(client, &buffer, 1)) { printk(KERN_WARNING "saa7113: I/O error, trying (read)\n"); return -1; } //printk(KERN_WARNING "saa7113_read 0x%02X\n", buffer); return buffer; } static void saa7113_human_status(struct i2c_client *client) { int oldsb; int status; oldsb = saa7113_read(client, 0x13); status = saa7113_read(client, 0x1f); if(status & 0x80) { printk(KERN_INFO "saa7113: status - interlaced\n"); } else { printk(KERN_INFO "saa7113: status - non-interlaced\n"); } if(oldsb & 0x10) { if(status & 0x40) { printk(KERN_INFO "saa7113: status - horz. freq. locked\n"); } else { printk(KERN_INFO "saa7113: status - horz. freq. locked\n"); } } else { } if(status & 0x20) { printk(KERN_INFO "saa7113: status - field. freq. 60Hz\n"); } else { printk(KERN_INFO "saa7113: status - field. freq. 50Hz\n"); } if(status & 0x10) { printk(KERN_INFO "saa7113: status - luminance gain max-top\n"); } if(status & 0x08) { printk(KERN_INFO "saa7113: status - luminance gain max-bottom\n"); } if(status & 0x04) { printk(KERN_INFO "saa7113: status - white-peak loop active\n"); } if(oldsb & 0x10) { if(status & 0x02) { printk(KERN_INFO "saa7113: status - slow time source active in WIPA\n"); } } else { } if(oldsb & 0x10) { if(status & 0x01) { printk(KERN_INFO "saa7113: status - colour signal detected\n"); } } status = saa7113_read(client, 0x62); printk(KERN_INFO "saa7113: status - slicer mode %d\n", status & 0x0F); } /* ----------------------------------------------------------------------- */ static int saa7113_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind) { int i; struct saa7113 *decoder; struct i2c_client *client; static const unsigned char init[] = { 0x00, 0x00, /* 00 - ID byte */ 0x01, 0x08, /* 01 - IDEL=8 */ /*front end */ 0x02, 0xd0, /* 02 - FUSE= 3, GUDL=2, MODE=0 <-- come back and look at mode*/ 0x03, 0x00, /* 03 - HLNRS=0, VBSL=0, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=0, GAI2=0 */ 0x04, 0x00, /* 04 - GAI1=0 */ 0x05, 0x00, /* 05 - GAI2=0 */ /* decoder */ 0x06, 0xEC, /* 06 - HSB=EC(-30) */ 0x07, 0xE0, /* 07 - HSS=E0(-32) */ 0x08, 0x88, /* 08 - AUFD=1, FSEL=0, FOET=0, HTC=1, HPLL=0, VNOI=0 || NO EXFIL!! */ 0x09, 0x01, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0, UPTCV=0, APER=1 */ 0x0a, 0x80, /* 0a - BRIG=128 */ 0x0b, 0x47, /* 0b - CONT=47(1.109) */ 0x0c, 0x40, /* 0c - SATN=40(1.0) */ 0x0d, 0x00, /* 0d - HUE=0 */ 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */ 0x0f, 0x2A, /* 0f - ACGC=0, CGAIN=42 (not used) */ //0x10, 0x08, /* 10 - OFTS=0, HDEL=0, VLRN=1, YDEL=0 ????? OFTS??*/ 0x10, 0x08, /* 10 - OFTS=1, HDEL=0, VLRN=1, YDEL=0 ????? OFTS??*/ //0x11, 0x0c, /* 11 - GPSW1=0, CM99=0, GPSW0=0, HLSEL=0, OEYC=1, OERT=1, VIPB=0, COLO=0 */ 0x11, 0x1e, /* 11 - GPSW1=0, CM99=0, GPSW0=0, HLSEL=0, OEYC=1, OERT=1, VIPB=0, COLO=0 */ //0x12, 0x6A, /* 12 - RTS1=6(PLIN), RTS0=10 (ODD)*/ //0x12, 0xB7, /* 12 - getting somewhere */ 0x12, 0xE7, /* 12 - RTS1=11(VS), RTS0=8 (HS)*/ 0x13, 0x10, /* 13 - OLDSB=1 */ 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, /* vbi slicer */ 0x40, 0x02, 0x41, 0xFF, 0x42, 0xFF, 0x43, 0xFF, 0x44, 0xFF, 0x45, 0xFF, 0x46, 0xFF, 0x47, 0xFF, 0x48, 0xFF, 0x49, 0xFF, 0x4A, 0xFF, 0x4B, 0xFF, 0x4C, 0xFF, 0x4D, 0xFF, 0x4E, 0xFF, 0x4F, 0xFF, 0x50, 0xFF, 0x51, 0xFF, 0x52, 0xFF, 0x53, 0xFF, 0x54, 0xFF, 0x55, 0xFF, 0x56, 0xFF, 0x57, 0xFF, 0x58, 0x00, 0x59, 0x54, 0x5A, 0x07, 0x5B, 0x83, 0x5C, 0x00, 0x5D, 0x00, 0x5E, 0x00 }; printk(KERN_INFO "saa7113_attach: addr=0x%x flags=0x%x kind=0x%x\n", addr, flags, kind); client = kmalloc(sizeof *client, GFP_KERNEL); if (!client) return -ENOMEM; memcpy(client, &client_template, sizeof(struct i2c_client)); client->adapter = adap; client->addr = addr; strcpy(client->name, "saa7113"); client->data = decoder = kmalloc(sizeof *decoder, GFP_KERNEL); if (!decoder) return -ENOMEM; memset(decoder, 0, sizeof *decoder); decoder->norm = VIDEO_MODE_AUTO; decoder->input = 0; decoder->enable = 1; decoder->bright = 32768; decoder->contrast = 32768; decoder->hue = 32768; decoder->sat = 32768; // printk(KERN_INFO "%s_attach: chip version %x\n", client->name, // saa7113_read(client, 0x00)); //saa7113_write(client, 0x00, 0x00); // printk(KERN_INFO "%s_attach: chip version %x\n", client->name, // saa7113_read(client, 0x00)); i = saa7113_write_block(client, init, sizeof(init)); //i = -1; if (i < 0) { printk(KERN_ERR "%s_attach: init status %d\n", client->name, i); return(-1); } else { printk(KERN_INFO "%s_attach: chip version %x\n", client->name, saa7113_read(client, 0x00)); } MOD_INC_USE_COUNT; //printk(KERN_INFO "saa7113: init (status 0x%02X)\n", saa7113_read(client, 0x1F)); saa7113_human_status(client); //for(i = 0; i < 0x64;i++) //{ // printk(KERN_INFO "saa7113_init: reg[%02x] = 0x%02X\n", i, saa7113_read(client, i)); //} i2c_attach_client(client); return 0; } static int saa7113_probe(struct i2c_adapter *adap) { return i2c_probe(adap, &addr_data, saa7113_attach); } static int saa7113_detach(struct i2c_client *client) { struct saa7113 *s = client->data; i2c_detach_client(client); kfree(s); kfree(client); MOD_DEC_USE_COUNT; return 0; } static int saa7113_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct saa7113 *decoder = client->data; switch (cmd) { #if defined(DECODER_DUMP) case DECODER_DUMP: { int i; for (i = 0; i < 32; i += 16) { int j; printk(KERN_INFO "%s: %03x", client->name, i); for (j = 0; j < 16; ++j) { printk(" %02x", saa7113_read(client, i + j)); } printk("\n"); } } break; #endif /* defined(DECODER_DUMP) */ case DECODER_GET_CAPABILITIES: { struct video_decoder_capability *cap = arg; cap->flags = VIDEO_DECODER_PAL | VIDEO_DECODER_SECAM | VIDEO_DECODER_NTSC | VIDEO_DECODER_AUTO | VIDEO_DECODER_CCIR; cap->inputs = 8; cap->outputs = 1; } break; case DECODER_GET_STATUS: { int *iarg = arg; int status; int res; status = saa7113_read(client, 0x1f); res = 0; if ((status & (1 << 6)) == 0) { res |= DECODER_STATUS_GOOD; } switch (decoder->norm) { case VIDEO_MODE_NTSC: res |= DECODER_STATUS_NTSC; break; case VIDEO_MODE_PAL: res |= DECODER_STATUS_PAL; break; case VIDEO_MODE_SECAM: res |= DECODER_STATUS_SECAM; break; default: case VIDEO_MODE_AUTO: if ((status & (1 << 5)) != 0) { res |= DECODER_STATUS_NTSC; } else { res |= DECODER_STATUS_PAL; } break; } if ((status & (1 << 0)) != 0) { res |= DECODER_STATUS_COLOR; } *iarg = res; } break; case DECODER_SET_NORM: { int *iarg = arg; /* switch (*iarg) { case VIDEO_MODE_SECAM: //Field selection 50Hz saa7113_write(client, 0x08, (decoder->reg[0x08] & 0x3F) | 0x00); //Colour standard PAL/SECAM saa7113_write(client, 0x0E, (decoder->reg[0x0E] & 0x8F) | 0x50); break; case VIDEO_MODE_NTSC: //Field selection 60Hz saa7113_write(client, 0x08, (decoder->reg[0x08] & 0x3f) | 0x40); //Colour standard PAL_BGHI/NTSC_MJ saa7113_write(client, 0x0E, (decoder->reg[0x0E] & 0x8F) | 0x00); break; case VIDEO_MODE_PAL: //Field selection 50Hz saa7113_write(client, 0x08, (decoder->reg[0x08] & 0x3f) | 0x00); //Colour standard PAL_BGHI/NTSC_MJ saa7113_write(client, 0x0E, (decoder->reg[0x0E] & 0x8F) | 0x00); break; case VIDEO_MODE_AUTO: //Field selection auto saa7113_write(client, 0x08, (decoder->reg[0x08] & 0x3f) | 0x80); //Colour standard PAL_BGHI/NTSC_MJ saa7113_write(client, 0x0E, (decoder->reg[0x0E] & 0x8F) | 0x00); break; default: return -EINVAL; } */ decoder->norm = *iarg; } break; case DECODER_SET_INPUT: { int *iarg = arg; if (*iarg < 0 || *iarg > 7) { return -EINVAL; } //if (decoder->input != *iarg) { // decoder->input = *iarg; // /* select mode */ // saa7113_write(client, 0x02, // (decoder->reg[0x02] & 0xf8) | // decoder->input); // /* bypass chrominance trap for modes 4..7 */ // saa7113_write(client, 0x09, // (decoder->reg[0x09] & 0x7f) | // ((decoder->input > 3) ? 0x80 : 0)); //} } break; case DECODER_SET_OUTPUT: { int *iarg = arg; /* not much choice of outputs */ if (*iarg != 0) { return -EINVAL; } } break; case DECODER_ENABLE_OUTPUT: { int *iarg = arg; int enable = (*iarg != 0); if (decoder->enable != enable) { decoder->enable = enable; //RJ:If output should be disabled (for playing videos), we also need a open PLL. // The input is set to 0 (where no input source is connected), although this // is not necessary. // // If output should be enabled, we have to reverse the above. if (decoder->enable) { saa7113_write(client, 0x02, (decoder-> reg[0x02] & 0xf8) | decoder->input); saa7113_write(client, 0x08, (decoder-> reg[0x08] & 0xfb)); saa7113_write(client, 0x11, (decoder-> reg[0x11] & 0xf3) | 0x0c); } else { saa7113_write(client, 0x02, (decoder-> reg[0x02] & 0xf8)); saa7113_write(client, 0x08, (decoder-> reg[0x08] & 0xfb) | 0x04); saa7113_write(client, 0x11, (decoder-> reg[0x11] & 0xf3)); } } } break; case DECODER_SET_PICTURE: { struct video_picture *pic = arg; //if (decoder->bright != pic->brightness) { // /* We want 0 to 255 we get 0-65535 */ // decoder->bright = pic->brightness; // saa7113_write(client, 0x0a, // decoder->bright >> 8); //} //if (decoder->contrast != pic->contrast) { // /* We want 0 to 127 we get 0-65535 */ // decoder->contrast = pic->contrast; // saa7113_write(client, 0x0b, // decoder->contrast >> 9); //} //if (decoder->sat != pic->colour) { // /* We want 0 to 127 we get 0-65535 */ // decoder->sat = pic->colour; // saa7113_write(client, 0x0c, // decoder->sat >> 9); //} //if (decoder->hue != pic->hue) { // /* We want -128 to 127 we get 0-65535 */ // decoder->hue = pic->hue; // saa7113_write(client, 0x0d , // (decoder->hue - 32768) >> 8); //} } break; default: return -EINVAL; } return 0; } /* ----------------------------------------------------------------------- */ static struct i2c_driver driver = { "i2c saa7113 driver", I2C_DRIVERID_SAA7113, /* ????? */ I2C_DF_NOTIFY, saa7113_probe, saa7113_detach, saa7113_command, }; static struct i2c_client client_template = { "(unset)", /* name */ -1, 0, 0, NULL, &driver }; EXPORT_NO_SYMBOLS; #ifdef MODULE int init_module(void) #else int saa7113_init(void) #endif { i2c_add_driver(&driver); return 0; } #ifdef MODULE void cleanup_module(void) { i2c_del_driver(&driver); } #endif
/* SAA7113 - Philips video decoder driver Copyright (C) 2000 Ralph Metzler <ralph@xxxxxxxxxxxxxx> for Convergence Integrated Media GmbH based on the SAAA7110 and SAA7111 drivers by: Copyright (C) 1998 Pauline Middelink <middelin@xxxxxxxxxxx> Copyright (C) 1998 Dave Perks <dperks@xxxxxxx> 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 <linux/errno.h> #include <linux/fs.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/sched.h> #include <linux/types.h> #include <linux/wrapper.h> #include <linux/video_decoder.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/poll.h> #include <linux/version.h> #include <linux/i2c.h> #include <linux/videodev.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/page.h> #include <asm/segment.h> #include "saa7113-new.h" MODULE_AUTHOR("Ralph Metzler"); MODULE_DESCRIPTION("Philips SAA7113 VideoStream Decoder Device Driver for Linux"); MODULE_LICENSE("GPL"); #define I2C_SAA7113 0x4a /* or 0x48 */ #define DEBUG(x) /* remove when no long debugging */ #define I2C_DELAY 10 /* 10 us or 100khz */ static struct i2c_client client_template; struct saa7113 { struct i2c_client *client; int addr; unsigned char reg[0x62]; int norm; int input; int enable; int bright; int contrast; int hue; int sat; }; static int writereg(struct i2c_client *client, unsigned char reg, unsigned char data) { int ret; unsigned char msg[] = {0x1f, 0x00}; msg[0]=reg; msg[1]=data; ret=i2c_master_send(client, msg, 2); if (ret!=2) printk("writereg error\n"); ((struct saa7113 *) client->data)->reg[reg]=data; return ret; } static int writeregs(struct i2c_client *client, const unsigned char *regs) { unsigned char reg, data; while (*regs!=0xff) { reg =*(regs++); data=*(regs++); if (writereg(client, reg, data)<0) return -1; } return 0; } static u8 readreg(struct i2c_client *client, unsigned char reg) { struct i2c_adapter *adap=client->adapter; unsigned char mm1[] = {0x1e}; unsigned char mm2[] = {0x00}; struct i2c_msg msgs[2]; msgs[0].flags=0; msgs[1].flags=I2C_M_RD; msgs[0].addr=msgs[1].addr=client->addr; mm1[0]=reg; msgs[0].len=1; msgs[1].len=1; msgs[0].buf=mm1; msgs[1].buf=mm2; i2c_transfer(adap, msgs, 2); return mm2[0]; } static const unsigned char init_saa7113[] = { 0x01, 0x08, 0x02, 0xc0, /* c7 s-video */ 0x03, 0x23, 0x04, 0x00, 0x05, 0x00, 0x06, 0xeb, 0x07, 0xe0, 0x08, 0x88, 0x09, 0x00, 0x0a, 0x80, 0x0b, 0x47, 0x0c, 0x40, 0x0d, 0x00, 0x0e, 0x01, 0x0f, 0xaa, 0x10, 0x00, 0x11, 0x1C, //0x12, 0x01, //0x12, 0xe7, 0x12, 0xa7, 0x13, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x0a, 0x5b, 0x83, 0x5e, 0x00, 0xff }; void init(struct i2c_client *client) { struct saa7113 *decoder=(struct saa7113 *) client->data; decoder->addr = client->addr; decoder->norm = VIDEO_MODE_AUTO; decoder->input = 0; decoder->enable = 1; decoder->bright = 32768; decoder->contrast = 32768; decoder->hue = 32768; decoder->sat = 32768; decoder->client=client; writeregs(client, init_saa7113); printk("saa7113: status=%02x\n", readreg(client, 0x1f)); } int attach_adapter(struct i2c_adapter *adap) { struct saa7113 *decoder; struct i2c_client *client; u8 version; client_template.adapter=adap; if (i2c_master_send(&client_template,NULL,0)) return -1; client_template.adapter=adap; version=readreg(&client_template, 0x00); printk("saa7113: version=%02x\n", version); if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) return -ENOMEM; memcpy(client, &client_template, sizeof(struct i2c_client)); client->data=decoder=kmalloc(sizeof(struct saa7113),GFP_KERNEL); if (decoder==NULL) { kfree(client); return -ENOMEM; } memset(decoder, 0, sizeof(struct saa7113)); printk("saa7113: attaching SAA7113 at 0x%02x\n", (client->addr)<<1); i2c_attach_client(client); init(client); printk("saa7113: attached to adapter %s\n", adap->name); return 0; } /* ----------------------------------------------------------------------- */ int detach_client(struct i2c_client *client) { i2c_detach_client(client); kfree(client->data); kfree(client); return 0; } static int saa7113_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct saa7113 *decoder= (struct saa7113 *) client->data; int v; switch (cmd) { case DECODER_GET_CAPABILITIES: { struct video_decoder_capability *dc = arg; dc->flags = VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC | VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO | VIDEO_DECODER_CCIR; dc->inputs = 2; dc->outputs = 1; } break; case DECODER_GET_STATUS: { int *iarg = arg; int status; int res; status = readreg(client, 0x1f); res = 0; if ((status & (1 << 6)) == 0) { res |= DECODER_STATUS_GOOD; } switch (decoder->norm) { case VIDEO_MODE_NTSC: res |= DECODER_STATUS_NTSC; break; case VIDEO_MODE_PAL: res |= DECODER_STATUS_PAL; break; default: case VIDEO_MODE_AUTO: if ((status & (1 << 5)) != 0) { res |= DECODER_STATUS_NTSC; } else { res |= DECODER_STATUS_PAL; } break; } if ((status & (1 << 0)) != 0) { res |= DECODER_STATUS_COLOR; } *iarg = res; } break; case DECODER_SET_NORM: v = *(int*)arg; switch (v) { case VIDEO_MODE_NTSC: writereg(client, 0x08, (decoder->reg[0x08] & 0x3f) | 0x40); break; case VIDEO_MODE_PAL: writereg(client, 0x08, (decoder->reg[0x08] & 0x3f) | 0x00); break; case VIDEO_MODE_AUTO: writereg(client, 0x08, (decoder->reg[0x08] & 0x3f) | 0x80); break; default: return -EINVAL; } decoder->norm = v; break; case DECODER_SET_INPUT: { int *iarg = arg; if (*iarg < 0 || *iarg > 7) { return -EINVAL; } if (decoder->input != *iarg) { decoder->input = *iarg; /* select mode */ writereg(client, 0x02, (decoder->reg[0x02] & 0xf8) | decoder->input); /* bypass chrominance trap for modes 4..7 */ writereg(client, 0x09, (decoder->reg[0x09] & 0x7f) | ((decoder->input > 3) ? 0x80 : 0)); } } break; case DECODER_SET_OUTPUT: v = *(int*)arg; /* not much choice of outputs */ if (v != 0) return -EINVAL; break; case DECODER_ENABLE_OUTPUT: { int *iarg = arg; int enable = (*iarg != 0); if (decoder->enable != enable) { decoder->enable = enable; // RJ: If output should be disabled (for playing videos), we also need a open PLL. // The input is set to 0 (where no input source is connected), although this // is not necessary. // // If output should be enabled, we have to reverse the above. if (decoder->enable) { writereg(client, 0x02, (decoder->reg[0x02] & 0xf8) | decoder->input); writereg(client, 0x08, (decoder->reg[0x08] & 0xfb)); writereg(client, 0x11, (decoder->reg[0x11] & 0xf3) | 0x0c); } else { writereg(client, 0x02, (decoder->reg[0x02] & 0xf8)); writereg(client, 0x08, (decoder->reg[0x08] & 0xfb) | 0x04); writereg(client, 0x11, (decoder->reg[0x11] & 0xf3)); } } } break; case DECODER_SET_PICTURE: { struct video_picture *pic = arg; if (decoder->bright != pic->brightness) { /* We want 0 to 255 we get 0-65535 */ decoder->bright = pic->brightness; writereg(client, 0x0a, decoder->bright >> 8); } if (decoder->contrast != pic->contrast) { /* We want 0 to 127 we get 0-65535 */ decoder->contrast = pic->contrast; writereg(client, 0x0b, decoder->contrast >> 9); } if (decoder->sat != pic->colour) { /* We want 0 to 127 we get 0-65535 */ decoder->sat = pic->colour; writereg(client, 0x0c, decoder->sat >> 9); } if (decoder->hue != pic->hue) { /* We want -128 to 127 we get 0-65535 */ decoder->hue = pic->hue; writereg(client, 0x0d, (decoder->hue - 32768) >> 8); } } break; default: printk(KERN_INFO "unknown saa7113_command??(%d)\n",cmd); return -EINVAL; } return 0; } void inc_use (struct i2c_client *client) { #ifdef MODULE MOD_INC_USE_COUNT; #endif } void dec_use (struct i2c_client *client) { #ifdef MODULE MOD_DEC_USE_COUNT; #endif } static struct i2c_driver saa7113_driver = { "SAA7113", I2C_DRIVERID_SAA7113, I2C_DF_NOTIFY, attach_adapter, detach_client, saa7113_command, inc_use, dec_use, }; static struct i2c_client client_template = { "SAA7113", I2C_DRIVERID_SAA7113, 0, (0x4a >> 1), NULL, &saa7113_driver, NULL }; EXPORT_NO_SYMBOLS; #ifdef MODULE int init_module(void) #else int saa7113_init(void) #endif { int res; if ((res = i2c_add_driver(&saa7113_driver))) { printk("saa7113: Driver registration failed, module not inserted.\n"); return res; } printk("saa7113: init_module\n"); return 0; } #ifdef MODULE void cleanup_module(void) { int res; if ((res = i2c_del_driver(&saa7113_driver))) { printk("saa7113: Driver deregistration failed, " "module not removed.\n"); } } #endif