📄 saa7111-new.c
字号:
/* saa7111 - Philips SAA7111A video decoder driver version 0.0.4 - Quick conversion to the new I2C driver by Jose Ignacio Gijon - Colour detection for SECAM by Joerg Heckenbach - Compatibility with ov511 driver and minor improvements by Mark McClelland Copyright (C) 1998 Dave Perks <dperks@ibm.net> 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/config.h>#include <linux/version.h>#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/i2c.h>#include <linux/videodev.h>#include <linux/video_decoder.h>#include "compat.h"#define DEBUG(x) /* Debug driver */MODULE_DESCRIPTION("Philips SAA7111A video decoder driver");MODULE_AUTHOR("Dave Perks, Jose Ignacio Gijon, Joerg Heckenbach, Mark McClelland");#if defined(MODULE_LICENSE) /* Introduced in ~2.4.10 */MODULE_LICENSE("GPL");#endif/* Number of I2C-accessible registers */#define NUM_REGS 32/* Addresses to scan */#define I2C_SAA7111A 0x48static unsigned short normal_i2c[] = { I2C_SAA7111A >> 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};/* Prototypes */static struct i2c_driver driver;static struct i2c_client client_template;/* ----------------------------------------------------------------------- */struct saa7111 { unsigned char reg[NUM_REGS]; int norm; int input; int enable; int bright; int contrast; int hue; int sat;};/* ----------------------------------------------------------------------- */static int saa7111_write(struct i2c_client *client, unsigned char subaddr, unsigned char data){ struct saa7111 *decoder = i2c_get_clientdata(client); if (i2c_smbus_write_byte_data(client, subaddr, data) < 0) { printk(KERN_WARNING "saa7111: I/O error, trying (write 0x%02X 0x%02X)\n", subaddr, data); return -EIO; } decoder->reg[subaddr] = data; return 0;}static int saa7111_write_block(struct i2c_client *client, unsigned const char *data, unsigned int start, unsigned int len){ struct saa7111 *decoder = i2c_get_clientdata(client); unsigned int subaddr, i; i = 1; subaddr = start; while (i < len) { if (i2c_smbus_write_byte_data(client, subaddr, data[i]) < 0) { printk(KERN_WARNING "saa7111: write error\n"); return -EIO; } decoder->reg[subaddr] = data[i]; ++subaddr; i += 2; } return 0;}static int saa7111_read(struct i2c_client *client, unsigned char subaddr){ int ret; ret = i2c_smbus_read_byte_data(client, subaddr); if (ret < 0) { printk(KERN_WARNING "saa7111: read error\n"); return -EIO; } return ret;}/* ----------------------------------------------------------------------- */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 68)static int saa7111_attach(struct i2c_adapter *adap, int addr, int kind)#elsestatic int saa7111_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind)#endif{ int i; struct saa7111 *decoder; struct i2c_client *client; static const unsigned char init[] = { 0x00, 0x00, /* 00 - ID byte */ 0x01, 0x00, /* 01 - reserved */ /*front end */// 0x02, 0xd0, /* 02 - FUSE=3, GUDL=2, MODE=0 */ 0x02, 0xc0, /* 02 - FUSE=3, GUDL=2, MODE=0 */ 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */ 0x04, 0x00, /* 04 - GAI1=256 */ 0x05, 0x00, /* 05 - GAI2=256 */ /* decoder */// WARNING: This comment isn't correct (refers to 06:f3, 07:13 I think)// 0x06, 0xEC, /* 06 - HSB at 13(50Hz) / 17(60Hz) pixels after end of last line */// 0x07, 0xE0, /* 07 - HSS at 113(50Hz) / 117(60Hz) pixels after end of last line */ 0x06, 0xEC, /* 06 - HSB at 13(50Hz) / 17(60Hz) pixels after end of last line */ 0x07, 0x00, /* 07 - HSS at 113(50Hz) / 117(60Hz) pixels after end of last line */ 0x08, 0xc8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1, HPLL=0, VNOI=0 */ 0x09, 0x01, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0, UPTCV=0, APER=1 */ 0x0a, 0x80, /* 0a - BRIG=128 */ 0x0b, 0x47, /* 0b - CONT=1.109 */ 0x0c, 0x40, /* 0c - SATN=1.0 */ 0x0d, 0x00, /* 0d - HUE=0 */ 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */ 0x0f, 0x00, /* 0f - reserved */// 0x10, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ 0x10, 0x44, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */// 0x11, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */ 0x11, 0x0c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */ 0x12, 0x00, /* 12 - output control 2 */ 0x13, 0x00, /* 13 - output control 3 */ 0x14, 0x00, /* 14 - reserved */ 0x15, 0x00, /* 15 - VBI */ 0x16, 0x00, /* 16 - VBI */ 0x17, 0x00, /* 17 - VBI */ }; client = kmalloc(sizeof *client, GFP_KERNEL); if (!client) return -ENOMEM; memcpy(client, &client_template, sizeof *client); client->adapter = adap; client->addr = addr; strcpy(i2c_clientname(client), "saa7111"); decoder = kmalloc(sizeof *decoder, GFP_KERNEL); i2c_set_clientdata(client, decoder); if (!decoder) { kfree(client); return -ENOMEM; } memset(decoder, 0, sizeof *decoder); decoder->norm = VIDEO_MODE_NTSC; decoder->input = 0; decoder->enable = 1; decoder->bright = 32768; decoder->contrast = 32768; // FIXME: This doesn't match the actual setting decoder->hue = 32768; decoder->sat = 32768; i = saa7111_write_block(client, init, 0, sizeof(init)); if (i < 0) { printk(KERN_ERR "saa7111_attach: init status %d\n", i); return i; } else { printk(KERN_INFO "saa7111_attach: chip version 0x%02X\n", saa7111_read(client, 0x00)); }#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) MOD_INC_USE_COUNT;#endif printk(KERN_INFO "saa7111: init\n"); i2c_attach_client(client); return 0;}static int saa7111_probe(struct i2c_adapter *adap){ return i2c_probe(adap, &addr_data, saa7111_attach);}static int saa7111_detach(struct i2c_client *client){ struct saa7111 *s = i2c_get_clientdata(client); i2c_detach_client(client); kfree(s); kfree(client);#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) MOD_DEC_USE_COUNT;#endif return 0;}static int saa7111_command(struct i2c_client *client, unsigned int cmd, void *arg){ struct saa7111 *decoder = i2c_get_clientdata(client); switch (cmd) {#if defined(DECODER_DUMP) case DECODER_DUMP: { int i; for (i = 0; i < NUM_REGS; i += NUM_REGS/2) { int j; printk(KERN_DEBUG "saa7111: 0x%02X", i); for (j = 0; j < NUM_REGS/2; ++j) { printk(" 0x%02X", saa7111_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 = saa7111_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 saa7111_write(client, 0x08, (decoder->reg[0x08] & 0x3F) | 0x00); //Colour standard PAL/SECAM saa7111_write(client, 0x0E, (decoder->reg[0x0E] & 0x8F) | 0x50); break; case VIDEO_MODE_NTSC: //Field selection 60Hz saa7111_write(client, 0x08, (decoder->reg[0x08] & 0x3f) | 0x40); //Colour standard PAL_BGHI/NTSC_MJ saa7111_write(client, 0x0E, (decoder->reg[0x0E] & 0x8F) | 0x00); break; case VIDEO_MODE_PAL: //Field selection 50Hz saa7111_write(client, 0x08, (decoder->reg[0x08] & 0x3f) | 0x00); //Colour standard PAL_BGHI/NTSC_MJ saa7111_write(client, 0x0E, (decoder->reg[0x0E] & 0x8F) | 0x00); break; case VIDEO_MODE_AUTO: //Field selection auto saa7111_write(client, 0x08, (decoder->reg[0x08] & 0x3f) | 0x80); //Colour standard PAL_BGHI/NTSC_MJ saa7111_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 */ saa7111_write(client, 0x02, (decoder->reg[0x02] & 0xf8) | decoder->input); /* bypass chrominance trap for modes 4..7 */ saa7111_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) { saa7111_write(client, 0x02, (decoder-> reg[0x02] & 0xf8) | decoder->input); saa7111_write(client, 0x08, (decoder-> reg[0x08] & 0xfb)); saa7111_write(client, 0x11, (decoder-> reg[0x11] & 0xf3) | 0x0c); } else { saa7111_write(client, 0x02, (decoder-> reg[0x02] & 0xf8)); saa7111_write(client, 0x08, (decoder-> reg[0x08] & 0xfb) | 0x04); saa7111_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; saa7111_write(client, 0x0a, decoder->bright >> 8); } if (decoder->contrast != pic->contrast) { /* We want 0 to 127 we get 0-65535 */ decoder->contrast = pic->contrast; saa7111_write(client, 0x0b, decoder->contrast >> 9); } if (decoder->sat != pic->colour) { /* We want 0 to 127 we get 0-65535 */ decoder->sat = pic->colour; saa7111_write(client, 0x0c, decoder->sat >> 9); } if (decoder->hue != pic->hue) { /* We want -128 to 127 we get 0-65535 */ decoder->hue = pic->hue; saa7111_write(client, 0x0d , (decoder->hue - 32768) >> 8); } } break; case VIDIOCGPICT: { struct video_picture *pic = arg; pic->brightness = decoder->bright; pic->contrast = decoder->contrast; pic->colour = decoder->sat; pic->hue = decoder->hue; pic->whiteness = 32768; } break; default: return -EINVAL; } return 0;}/* ----------------------------------------------------------------------- */static struct i2c_driver driver = { .name = "i2c SAA7111A Driver", .id = I2C_DRIVERID_SAA7111A, .flags = I2C_DF_NOTIFY, .attach_adapter = saa7111_probe, .detach_client = saa7111_detach, .command = saa7111_command,};static struct i2c_client client_template = { I2C_DEVNAME("(unset)"), .id = -1, .driver = &driver};static int __init saa7111_init(void){#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)EXPORT_NO_SYMBOLS;#endif return i2c_add_driver(&driver);}static void __exit saa7111_exit(void){ i2c_del_driver(&driver);}module_init(saa7111_init);module_exit(saa7111_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -