diff options
author | Dave Kleikamp <shaggy@austin.ibm.com> | 2006-01-24 14:34:47 -0600 |
---|---|---|
committer | Dave Kleikamp <shaggy@austin.ibm.com> | 2006-01-24 14:34:47 -0600 |
commit | 0a0fc0ddbe732779366ab6b1b879f62195e65967 (patch) | |
tree | 7b42490a676cf39ae0691b6859ecf7fd410f229b /drivers/media/video | |
parent | 4d5dbd0945d9e0833dd7964a3d6ee33157f7cc7a (diff) | |
parent | 3ee68c4af3fd7228c1be63254b9f884614f9ebb2 (diff) | |
download | kernel-crypto-0a0fc0ddbe732779366ab6b1b879f62195e65967.tar.gz kernel-crypto-0a0fc0ddbe732779366ab6b1b879f62195e65967.tar.xz kernel-crypto-0a0fc0ddbe732779366ab6b1b879f62195e65967.zip |
Merge with /home/shaggy/git/linus-clean/
Diffstat (limited to 'drivers/media/video')
130 files changed, 14969 insertions, 5577 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 199b0118885..d82c8a30ba4 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -7,6 +7,15 @@ menu "Video For Linux" comment "Video Adapters" +config VIDEO_ADV_DEBUG + bool "Enable advanced debug functionality" + depends on VIDEO_DEV + default n + ---help--- + Say Y here to enable advanced debugging functionality on some + V4L devices. + In doubt, say N. + config VIDEO_BT848 tristate "BT848 Video For Linux" depends on VIDEO_DEV && PCI && I2C @@ -26,15 +35,12 @@ config VIDEO_BT848 module will be called bttv. config VIDEO_BT848_DVB - tristate "DVB/ATSC Support for bt878 based TV cards" + bool "DVB/ATSC Support for bt878 based TV cards" depends on VIDEO_BT848 && DVB_CORE select DVB_BT8XX ---help--- This adds support for DVB/ATSC cards based on the BT878 chip. - To compile this driver as a module, choose M here: the - module will be called dvb-bt8xx. - config VIDEO_SAA6588 tristate "SAA6588 Radio Chip RDS decoder support on BT848 cards" depends on VIDEO_DEV && I2C && VIDEO_BT848 @@ -177,7 +183,7 @@ config VIDEO_STRADIS help Say Y here to enable support for the Stradis 4:2:2 MPEG-2 video driver for PCI. There is a product page at - <http://www.stradis.com/decoder.html>. + <http://www.stradis.com/>. config VIDEO_ZORAN tristate "Zoran ZR36057/36067 Video For Linux" @@ -191,7 +197,7 @@ config VIDEO_ZORAN To compile this driver as a module, choose M here: the module will be called zr36067. - + config VIDEO_ZORAN_BUZ tristate "Iomega Buz support" depends on VIDEO_ZORAN @@ -207,8 +213,8 @@ config VIDEO_ZORAN_DC10 config VIDEO_ZORAN_DC30 tristate "Pinnacle/Miro DC30(+) support" - depends on VIDEO_ZORAN - help + depends on VIDEO_ZORAN + help Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback card. This also supports really old DC10 cards based on the zr36050 MJPEG codec and zr36016 VFE. @@ -263,7 +269,7 @@ config VIDEO_MXB ---help--- This is a video4linux driver for the 'Multimedia eXtension Board' TV card by Siemens-Nixdorf. - + To compile this driver as a module, choose M here: the module will be called mxb. @@ -277,7 +283,7 @@ config VIDEO_DPC for SAA7146 bases boards, so if you have some unsupported saa7146 based, analog video card, chances are good that it will work with this skeleton driver. - + To compile this driver as a module, choose M here: the module will be called dpc7146. @@ -288,7 +294,7 @@ config VIDEO_HEXIUM_ORION ---help--- This is a video4linux driver for the Hexium HV-PCI6 and Orion frame grabber cards by Hexium. - + To compile this driver as a module, choose M here: the module will be called hexium_orion. @@ -300,7 +306,7 @@ config VIDEO_HEXIUM_GEMINI This is a video4linux driver for the Hexium Gemini frame grabber card by Hexium. Please note that the Gemini Dual card is *not* fully supported. - + To compile this driver as a module, choose M here: the module will be called hexium_gemini. @@ -333,4 +339,18 @@ config VIDEO_M32R_AR_M64278 Say Y here to use the Renesas M64278E-800 camera module, which supports VGA(640x480 pixcels) size of images. +config VIDEO_AUDIO_DECODER + tristate "Add support for additional audio chipsets" + depends on VIDEO_DEV && I2C && EXPERIMENTAL + ---help--- + Say Y here to compile drivers for WM8775 and CS53L32A audio + decoders. + +config VIDEO_DECODER + tristate "Add support for additional video chipsets" + depends on VIDEO_DEV && I2C && EXPERIMENTAL + ---help--- + Say Y here to compile drivers for SAA7115, SAA7127 and CX25840 + video decoders. + endmenu diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 3ac46599240..faf728366c4 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -3,15 +3,20 @@ # bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ - bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o + bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \ + bttv-input.o zoran-objs := zr36120.o zr36120_i2c.o zr36120_mem.o zr36067-objs := zoran_procfs.o zoran_device.o \ zoran_driver.o zoran_card.o -tuner-objs := tuner-core.o tuner-simple.o mt20xx.o tda8290.o tea5767.o -obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o +tuner-objs := tuner-core.o tuner-types.o tuner-simple.o \ + mt20xx.o tda8290.o tea5767.o + +msp3400-objs := msp3400-driver.o msp3400-kthreads.o + +obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o compat_ioctl32.o obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \ - tda7432.o tda9875.o ir-kbd-i2c.o ir-kbd-gpio.o + tda7432.o tda9875.o ir-kbd-i2c.o obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o obj-$(CONFIG_VIDEO_ZR36120) += zoran.o @@ -36,10 +41,11 @@ obj-$(CONFIG_VIDEO_CPIA) += cpia.o obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o obj-$(CONFIG_VIDEO_MEYE) += meye.o -obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ +obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_EM28XX) += saa711x.o tvp5150.o +obj-$(CONFIG_VIDEO_AUDIO_DECODER) += wm8775.o cs53l32a.o obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/ obj-$(CONFIG_VIDEO_MXB) += saa7111.o tuner.o tda9840.o tea6415c.o tea6420.o mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o @@ -55,4 +61,6 @@ obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o +obj-$(CONFIG_VIDEO_DECODER) += saa7115.o cx25840/ saa7127.o + EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c index 1ca2b67aedf..4ce07ae62da 100644 --- a/drivers/media/video/adv7170.c +++ b/drivers/media/video/adv7170.c @@ -413,14 +413,12 @@ adv7170_detect_client (struct i2c_adapter *adapter, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_adv7170; - client->flags = I2C_CLIENT_ALLOW_USE; if ((client->addr == I2C_ADV7170 >> 1) || (client->addr == (I2C_ADV7170 >> 1) + 1)) { dname = adv7170_name; @@ -434,12 +432,11 @@ adv7170_detect_client (struct i2c_adapter *adapter, } strlcpy(I2C_NAME(client), dname, sizeof(I2C_NAME(client))); - encoder = kmalloc(sizeof(struct adv7170), GFP_KERNEL); + encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL); if (encoder == NULL) { kfree(client); return -ENOMEM; } - memset(encoder, 0, sizeof(struct adv7170)); encoder->norm = VIDEO_MODE_NTSC; encoder->input = 0; encoder->enable = 1; @@ -498,11 +495,11 @@ adv7170_detach_client (struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_driver i2c_driver_adv7170 = { - .owner = THIS_MODULE, - .name = "adv7170", /* name */ + .driver = { + .name = "adv7170", /* name */ + }, .id = I2C_DRIVERID_ADV7170, - .flags = I2C_DF_NOTIFY, .attach_adapter = adv7170_attach_adapter, .detach_client = adv7170_detach_client, diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c index 173bca1e029..4e218f22b21 100644 --- a/drivers/media/video/adv7175.c +++ b/drivers/media/video/adv7175.c @@ -463,14 +463,12 @@ adv7175_detect_client (struct i2c_adapter *adapter, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_adv7175; - client->flags = I2C_CLIENT_ALLOW_USE; if ((client->addr == I2C_ADV7175 >> 1) || (client->addr == (I2C_ADV7175 >> 1) + 1)) { dname = adv7175_name; @@ -484,12 +482,11 @@ adv7175_detect_client (struct i2c_adapter *adapter, } strlcpy(I2C_NAME(client), dname, sizeof(I2C_NAME(client))); - encoder = kmalloc(sizeof(struct adv7175), GFP_KERNEL); + encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL); if (encoder == NULL) { kfree(client); return -ENOMEM; } - memset(encoder, 0, sizeof(struct adv7175)); encoder->norm = VIDEO_MODE_PAL; encoder->input = 0; encoder->enable = 1; @@ -548,11 +545,11 @@ adv7175_detach_client (struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_driver i2c_driver_adv7175 = { - .owner = THIS_MODULE, - .name = "adv7175", /* name */ + .driver = { + .name = "adv7175", /* name */ + }, .id = I2C_DRIVERID_ADV7175, - .flags = I2C_DF_NOTIFY, .attach_adapter = adv7175_attach_adapter, .detach_client = adv7175_detach_client, diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c index 881cdcb1875..994b75fe165 100644 --- a/drivers/media/video/arv.c +++ b/drivers/media/video/arv.c @@ -129,9 +129,9 @@ static unsigned char yuv[MAX_AR_FRAME_BYTES]; static int freq = DEFAULT_FREQ; /* BCLK: available 50 or 70 (MHz) */ static int vga = 0; /* default mode(0:QVGA mode, other:VGA mode) */ static int vga_interlace = 0; /* 0 is normal mode for, else interlace mode */ -MODULE_PARM(freq, "i"); -MODULE_PARM(vga, "i"); -MODULE_PARM(vga_interlace, "i"); +module_param(freq, int, 0); +module_param(vga, int, 0); +module_param(vga_interlace, int, 0); static int ar_initialize(struct video_device *dev); @@ -749,6 +749,7 @@ static struct file_operations ar_fops = { .release = video_exclusive_release, .read = ar_read, .ioctl = ar_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index 3ee0afca76a..d6447791d0e 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -528,22 +528,18 @@ bt819_detect_client (struct i2c_adapter *adapter, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_bt819; - client->flags = I2C_CLIENT_ALLOW_USE; - decoder = kmalloc(sizeof(struct bt819), GFP_KERNEL); + decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL); if (decoder == NULL) { kfree(client); return -ENOMEM; } - - memset(decoder, 0, sizeof(struct bt819)); decoder->norm = VIDEO_MODE_NTSC; decoder->input = 0; decoder->enable = 1; @@ -623,11 +619,11 @@ bt819_detach_client (struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_driver i2c_driver_bt819 = { - .owner = THIS_MODULE, - .name = "bt819", + .driver = { + .name = "bt819", + }, .id = I2C_DRIVERID_BT819, - .flags = I2C_DF_NOTIFY, .attach_adapter = bt819_attach_adapter, .detach_client = bt819_detach_client, diff --git a/drivers/media/video/bt832.c b/drivers/media/video/bt832.c index e4063950ae5..cc54b62f460 100644 --- a/drivers/media/video/bt832.c +++ b/drivers/media/video/bt832.c @@ -30,8 +30,9 @@ #include <linux/init.h> #include <linux/errno.h> #include <linux/slab.h> - #include <media/audiochip.h> +#include <media/v4l2-common.h> + #include "bttv.h" #include "bt832.h" @@ -42,9 +43,10 @@ static unsigned short normal_i2c[] = { I2C_BT832_ALT1>>1, I2C_BT832_ALT2>>1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; -/* ---------------------------------------------------------------------- */ +int debug; /* debug output */ +module_param(debug, int, 0644); -#define dprintk if (debug) printk +/* ---------------------------------------------------------------------- */ static int bt832_detach(struct i2c_client *client); @@ -61,23 +63,26 @@ int bt832_hexdump(struct i2c_client *i2c_client_s, unsigned char *buf) int i,rc; buf[0]=0x80; // start at register 0 with auto-increment if (1 != (rc = i2c_master_send(i2c_client_s,buf,1))) - printk("bt832: i2c i/o error: rc == %d (should be 1)\n",rc); + v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 1)\n",rc); for(i=0;i<65;i++) buf[i]=0; if (65 != (rc=i2c_master_recv(i2c_client_s,buf,65))) - printk("bt832: i2c i/o error: rc == %d (should be 65)\n",rc); + v4l_err(i2c_client_s,"i2c i/o error: rc == %d (should be 65)\n",rc); // Note: On READ the first byte is the current index // (e.g. 0x80, what we just wrote) - if(1) { + if(debug>1) { int i; - printk("BT832 hexdump:\n"); + v4l_dbg(2, debug,i2c_client_s,"hexdump:"); for(i=1;i<65;i++) { if(i!=1) { - if(((i-1)%8)==0) printk(" "); - if(((i-1)%16)==0) printk("\n"); + if(((i-1)%8)==0) printk(" "); + if(((i-1)%16)==0) { + printk("\n"); + v4l_dbg(2, debug,i2c_client_s,"hexdump:"); + } } printk(" %02x",buf[i]); } @@ -96,56 +101,56 @@ int bt832_init(struct i2c_client *i2c_client_s) bt832_hexdump(i2c_client_s,buf); if(buf[0x40] != 0x31) { - printk("bt832: this i2c chip is no bt832 (id=%02x). Detaching.\n",buf[0x40]); + v4l_err(i2c_client_s,"This i2c chip is no bt832 (id=%02x). Detaching.\n",buf[0x40]); kfree(buf); return 0; } - printk("Write 0 tp VPSTATUS\n"); + v4l_err(i2c_client_s,"Write 0 tp VPSTATUS\n"); buf[0]=BT832_VP_STATUS; // Reg.52 buf[1]= 0x00; if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - printk("bt832: i2c i/o error VPS: rc == %d (should be 2)\n",rc); + v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc); bt832_hexdump(i2c_client_s,buf); // Leave low power mode: - printk("Bt832: leave low power mode.\n"); + v4l_err(i2c_client_s,"leave low power mode.\n"); buf[0]=BT832_CAM_SETUP0; //0x39 57 buf[1]=0x08; if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - printk("bt832: i2c i/o error LLPM: rc == %d (should be 2)\n",rc); + v4l_err(i2c_client_s,"i2c i/o error LLPM: rc == %d (should be 2)\n",rc); bt832_hexdump(i2c_client_s,buf); - printk("Write 0 tp VPSTATUS\n"); + v4l_info(i2c_client_s,"Write 0 tp VPSTATUS\n"); buf[0]=BT832_VP_STATUS; // Reg.52 buf[1]= 0x00; if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - printk("bt832: i2c i/o error VPS: rc == %d (should be 2)\n",rc); + v4l_err(i2c_client_s,"i2c i/o error VPS: rc == %d (should be 2)\n",rc); bt832_hexdump(i2c_client_s,buf); // Enable Output - printk("Enable Output\n"); + v4l_info(i2c_client_s,"Enable Output\n"); buf[0]=BT832_VP_CONTROL1; // Reg.40 buf[1]= 0x27 & (~0x01); // Default | !skip if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - printk("bt832: i2c i/o error EO: rc == %d (should be 2)\n",rc); + v4l_err(i2c_client_s,"i2c i/o error EO: rc == %d (should be 2)\n",rc); bt832_hexdump(i2c_client_s,buf); // for testing (even works when no camera attached) - printk("bt832: *** Generate NTSC M Bars *****\n"); + v4l_info(i2c_client_s,"*** Generate NTSC M Bars *****\n"); buf[0]=BT832_VP_TESTCONTROL0; // Reg. 42 buf[1]=3; // Generate NTSC System M bars, Generate Frame timing internally if (2 != (rc = i2c_master_send(i2c_client_s,buf,2))) - printk("bt832: i2c i/o error MBAR: rc == %d (should be 2)\n",rc); + v4l_info(i2c_client_s,"i2c i/o error MBAR: rc == %d (should be 2)\n",rc); - printk("Bt832: Camera Present: %s\n", + v4l_info(i2c_client_s,"Camera Present: %s\n", (buf[1+BT832_CAM_STATUS] & BT832_56_CAMERA_PRESENT) ? "yes":"no"); bt832_hexdump(i2c_client_s,buf); @@ -159,20 +164,18 @@ static int bt832_attach(struct i2c_adapter *adap, int addr, int kind) { struct bt832 *t; - printk("bt832_attach\n"); - client_template.adapter = adap; client_template.addr = addr; - printk("bt832: chip found @ 0x%x\n", addr<<1); - - if (NULL == (t = kmalloc(sizeof(*t), GFP_KERNEL))) + if (NULL == (t = kzalloc(sizeof(*t), GFP_KERNEL))) return -ENOMEM; - memset(t,0,sizeof(*t)); t->client = client_template; i2c_set_clientdata(&t->client, t); i2c_attach_client(&t->client); + v4l_info(&t->client,"chip found @ 0x%x\n", addr<<1); + + if(! bt832_init(&t->client)) { bt832_detach(&t->client); return -1; @@ -183,13 +186,8 @@ static int bt832_attach(struct i2c_adapter *adap, int addr, int kind) static int bt832_probe(struct i2c_adapter *adap) { -#ifdef I2C_CLASS_TV_ANALOG if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, bt832_attach); -#else - if (adap->id == I2C_HW_B_BT848) - return i2c_probe(adap, &addr_data, bt832_attach); -#endif return 0; } @@ -197,7 +195,7 @@ static int bt832_detach(struct i2c_client *client) { struct bt832 *t = i2c_get_clientdata(client); - printk("bt832: detach.\n"); + v4l_info(&t->client,"dettach\n"); i2c_detach_client(client); kfree(t); return 0; @@ -208,7 +206,8 @@ bt832_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct bt832 *t = i2c_get_clientdata(client); - printk("bt832: command %x\n",cmd); + if (debug>1) + v4l_i2c_print_ioctl(&t->client,cmd); switch (cmd) { case BT832_HEXDUMP: { @@ -219,7 +218,7 @@ bt832_command(struct i2c_client *client, unsigned int cmd, void *arg) } break; case BT832_REATTACH: - printk("bt832: re-attach\n"); + v4l_info(&t->client,"re-attach\n"); i2c_del_driver(&driver); i2c_add_driver(&driver); break; @@ -230,19 +229,18 @@ bt832_command(struct i2c_client *client, unsigned int cmd, void *arg) /* ----------------------------------------------------------------------- */ static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "i2c bt832 driver", - .id = -1, /* FIXME */ - .flags = I2C_DF_NOTIFY, - .attach_adapter = bt832_probe, - .detach_client = bt832_detach, - .command = bt832_command, + .driver = { + .name = "bt832", + }, + .id = 0, /* FIXME */ + .attach_adapter = bt832_probe, + .detach_client = bt832_detach, + .command = bt832_command, }; static struct i2c_client client_template = { .name = "bt832", - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, + .driver = &driver, }; diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c index 8eb871d0e85..909b593530e 100644 --- a/drivers/media/video/bt856.c +++ b/drivers/media/video/bt856.c @@ -316,22 +316,19 @@ bt856_detect_client (struct i2c_adapter *adapter, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_bt856; - client->flags = I2C_CLIENT_ALLOW_USE; strlcpy(I2C_NAME(client), "bt856", sizeof(I2C_NAME(client))); - encoder = kmalloc(sizeof(struct bt856), GFP_KERNEL); + encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL); if (encoder == NULL) { kfree(client); return -ENOMEM; } - memset(encoder, 0, sizeof(struct bt856)); encoder->norm = VIDEO_MODE_NTSC; encoder->enable = 1; i2c_set_clientdata(client, encoder); @@ -405,11 +402,11 @@ bt856_detach_client (struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_driver i2c_driver_bt856 = { - .owner = THIS_MODULE, - .name = "bt856", + .driver = { + .name = "bt856", + }, .id = I2C_DRIVERID_BT856, - .flags = I2C_DF_NOTIFY, .attach_adapter = bt856_attach_adapter, .detach_client = bt856_detach_client, diff --git a/drivers/media/video/btcx-risc.c b/drivers/media/video/btcx-risc.c index a48de3c0e3f..b4aca724927 100644 --- a/drivers/media/video/btcx-risc.c +++ b/drivers/media/video/btcx-risc.c @@ -37,7 +37,7 @@ MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers"); MODULE_AUTHOR("Gerd Knorr"); MODULE_LICENSE("GPL"); -static unsigned int debug = 0; +static unsigned int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)"); diff --git a/drivers/media/video/bttv-cards.c b/drivers/media/video/bttv-cards.c index 3413bace443..9749d6ed623 100644 --- a/drivers/media/video/bttv-cards.c +++ b/drivers/media/video/bttv-cards.c @@ -38,6 +38,7 @@ #include <asm/io.h> #include "bttvp.h" +#include <media/v4l2-common.h> /* fwd decl */ static void boot_msp34xx(struct bttv *btv, int pin); @@ -91,8 +92,8 @@ static void identify_by_eeprom(struct bttv *btv, static int __devinit pvr_boot(struct bttv *btv); /* config variables */ -static unsigned int triton1=0; -static unsigned int vsfx=0; +static unsigned int triton1; +static unsigned int vsfx; static unsigned int latency = UNSET; int no_overlay=-1; @@ -105,7 +106,7 @@ static struct bttv *master[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = NULL }; #ifdef MODULE static unsigned int autoload = 1; #else -static unsigned int autoload = 0; +static unsigned int autoload; #endif static unsigned int gpiomask = UNSET; static unsigned int audioall = UNSET; @@ -292,6 +293,9 @@ static struct CARD { /* likely broken, vendor id doesn't match the other magic views ... * { 0xa0fca04f, BTTV_BOARD_MAGICTVIEW063, "Guillemot Maxi TV Video 3" }, */ + /* Duplicate PCI ID, reconfigure for this board during the eeprom read. + * { 0x13eb0070, BTTV_BOARD_HAUPPAUGE_IMPACTVCB, "Hauppauge ImpactVCB" }, */ + /* DVB cards (using pci function .1 for mpeg data xfer) */ { 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" }, { 0x07611461, BTTV_BOARD_AVDVBT_761, "AverMedia AverTV DVB-T 761" }, @@ -317,7 +321,7 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 1, .tuner = 0, .svhs = 2, - .muxsel = { 2, 3, 1, 0}, + .muxsel = { 2, 3, 1, 0 }, .tuner_type = -1, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -329,8 +333,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 2, 0, 0, 0, 10}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 2, 0, 0, 0, 10 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -343,8 +347,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0, 1, 2, 3, 4}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0, 1, 2, 3, 4 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -357,8 +361,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 4, 0, 2, 3, 1}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 4, 0, 2, 3, 1 }, .no_msp34xx = 1, .needs_tvaudio = 1, .tuner_type = TUNER_PHILIPS_NTSC, @@ -376,7 +380,7 @@ struct tvcard bttv_tvcards[] = { .tuner = -1, .svhs = 2, .gpiomask = 0, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 0 }, .needs_tvaudio = 0, .tuner_type = 4, @@ -390,8 +394,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 3, - .muxsel = { 2, 3, 1, 0}, - .audiomux = { 0, 1, 0, 1, 3}, + .muxsel = { 2, 3, 1, 0 }, + .audiomux = { 0, 1, 0, 1, 3 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -403,9 +407,9 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 1, .tuner = 0, .svhs = 3, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .gpiomask = 0x0f, - .audiomux = { 0x0c, 0x04, 0x08, 0x04, 0}, + .audiomux = { 0x0c, 0x04, 0x08, 0x04, 0 }, /* 0x04 for some cards ?? */ .needs_tvaudio = 1, .tuner_type = -1, @@ -421,8 +425,8 @@ struct tvcard bttv_tvcards[] = { .tuner = -1, .svhs = 3, .gpiomask = 0, - .muxsel = { 2, 3, 1, 0, 0}, - .audiomux = {0 }, + .muxsel = { 2, 3, 1, 0, 0 }, + .audiomux = { 0 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -437,8 +441,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0xc00, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0, 0xc00, 0x800, 0x400, 0xc00, 0}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0, 0xc00, 0x800, 0x400, 0xc00, 0 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -452,8 +456,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 3, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 1, 1, 2, 3, 0}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 1, 1, 2, 3, 0 }, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_TEMIC_PAL, @@ -467,8 +471,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x0f, /* old: 7 */ - .muxsel = { 2, 0, 1, 1}, - .audiomux = { 0, 1, 2, 3, 4}, + .muxsel = { 2, 0, 1, 1 }, + .audiomux = { 0, 1, 2, 3, 4 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -482,8 +486,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x3014f, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0x20001,0x10001, 0, 0,10}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0x20001,0x10001, 0, 0,10 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -498,8 +502,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 13, 14, 11, 7, 0, 0}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 13, 14, 11, 7, 0, 0 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -512,8 +516,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 13, 14, 11, 7, 0, 0}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 13, 14, 11, 7, 0, 0 }, .needs_tvaudio = 1, .msp34xx_alt = 1, .pll = PLL_28, @@ -530,8 +534,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0, 2, 1, 3, 4}, /* old: { 0, 1, 2, 3, 4} */ + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0, 2, 1, 3, 4 }, /* old: {0, 1, 2, 3, 4} */ .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -545,8 +549,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1}, - .audiomux = {0 , 0, 1 , 0, 10}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0 , 0, 1 , 0, 10 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -561,7 +565,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x01fe00, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, #if 0 /* old */ .audiomux = { 0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 }, @@ -580,8 +584,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x8300f8, - .muxsel = { 2, 3, 1, 1,0}, - .audiomux = { 0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007}, + .muxsel = { 2, 3, 1, 1,0 }, + .audiomux = { 0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -596,8 +600,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0, - .muxsel = {2, 3, 1, 1}, - .audiomux = {1, 0, 0, 0, 0}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 1, 0, 0, 0, 0 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -610,7 +614,7 @@ struct tvcard bttv_tvcards[] = { .tuner = -1, .svhs = -1, .gpiomask = 0x8dff00, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 0 }, .no_msp34xx = 1, .tuner_type = -1, @@ -625,7 +629,7 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 3, .tuner = 0, .svhs = 2, - .muxsel = {2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .tuner_type = -1, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -637,8 +641,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x1800, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800 }, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, @@ -651,8 +655,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0xc00, - .muxsel = { 2, 3, 1, 1}, - .audiomux = {0, 1, 0x800, 0x400, 0xc00, 0}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0, 1, 0x800, 0x400, 0xc00, 0 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -684,8 +688,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0xe00, - .muxsel = { 2, 3, 1, 1}, - .audiomux = {0x400, 0x400, 0x400, 0x400, 0xc00}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = {0x400, 0x400, 0x400, 0x400, 0xc00 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -700,8 +704,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x1f0fff, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0x20000, 0x30000, 0x10000, 0, 0x40000}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0x20000, 0x30000, 0x10000, 0, 0x40000 }, .needs_tvaudio = 0, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, @@ -715,8 +719,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 3, .gpiomask = 7, - .muxsel = { 2, 0, 1, 1}, - .audiomux = { 0, 1, 2, 3, 4}, + .muxsel = { 2, 0, 1, 1 }, + .audiomux = { 0, 1, 2, 3, 4 }, .needs_tvaudio = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -729,8 +733,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x1800, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800 }, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_SECAM, .tuner_addr = ADDR_UNSET, @@ -745,8 +749,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x1f0fff, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0x20000, 0x30000, 0x10000, 0x00000, 0x40000}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0x20000, 0x30000, 0x10000, 0x00000, 0x40000 }, .needs_tvaudio = 0, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, @@ -808,7 +812,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x1800, /* 0x8dfe00 */ - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 0, 0x0800, 0x1000, 0x1000, 0x1800, 0 }, .pll = PLL_28, .tuner_type = -1, @@ -822,7 +826,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 3, .gpiomask = 1, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 1, 0, 0, 0, 0 }, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -838,7 +842,7 @@ struct tvcard bttv_tvcards[] = { .tuner = -1, .svhs = 2, .gpiomask = 0, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 0 }, .needs_tvaudio = 0, .tuner_type = 4, @@ -852,8 +856,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0xffff00, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0x500, 0, 0x300, 0x900, 0x900}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0x500, 0, 0x300, 0x900, 0x900 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -866,7 +870,7 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 1, .tuner = 0, .svhs = 2, - .muxsel = { 2, 3, 1, 1, 0}, /* TV, CVid, SVid, CVid over SVid connector */ + .muxsel = { 2, 3, 1, 1, 0 }, /* TV, CVid, SVid, CVid over SVid connector */ #if 0 .gpiomask = 0xc33000, .audiomux = { 0x422000,0x1000,0x0000,0x620000,0x800000 }, @@ -902,8 +906,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x1800, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800 }, .pll = PLL_28, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -918,7 +922,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x1800, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800, 0 }, .pll = PLL_28, .tuner_type = -1, @@ -948,7 +952,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x551e00, - .muxsel = { 2, 3, 1, 0}, + .muxsel = { 2, 3, 1, 0 }, .audiomux = { 0x551400, 0x551200, 0, 0, 0x551c00, 0x551200 }, .needs_tvaudio = 1, .pll = PLL_28, @@ -964,8 +968,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x03000F, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 2, 0xd0001, 0, 0, 1}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 2, 0xd0001, 0, 0, 1 }, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = -1, @@ -981,8 +985,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 4, 0, 2, 3, 1}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 4, 0, 2, 3, 1 }, .no_msp34xx = 1, .needs_tvaudio = 1, .tuner_type = TUNER_PHILIPS_NTSC, @@ -998,8 +1002,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 13, 4, 11, 7, 0, 0}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 13, 4, 11, 7, 0, 0 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -1031,8 +1035,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0xe00b, - .muxsel = {2, 3, 1, 1}, - .audiomux = {0xff9ff6, 0xff9ff6, 0xff1ff7, 0, 0xff3ffc}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0xff9ff6, 0xff9ff6, 0xff1ff7, 0, 0xff3ffc }, .no_msp34xx = 1, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -1047,8 +1051,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = -1, .gpiomask = 3, - .muxsel = {2, 3, 1, 1}, - .audiomux = {1, 1, 0, 2, 3}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 1, 1, 0, 2, 3 }, .no_msp34xx = 1, .pll = PLL_NONE, .tuner_type = -1, @@ -1062,8 +1066,8 @@ struct tvcard bttv_tvcards[] = { .tuner = -1, .svhs = 3, .gpiomask = 0, - .muxsel = { 2, 3, 1, 0, 0}, - .audiomux = {0 }, + .muxsel = { 2, 3, 1, 0, 0 }, + .audiomux = { 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = -1, @@ -1077,8 +1081,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0xbcf03f, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0xbc803f, 0xbc903f, 0xbcb03f, 0, 0xbcb03f}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0xbc803f, 0xbc903f, 0xbcb03f, 0, 0xbcb03f }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = 21, @@ -1092,7 +1096,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x70000, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 0x20000, 0x30000, 0x10000, 0, 0x40000, 0x20000 }, .needs_tvaudio = 1, .no_msp34xx = 1, @@ -1111,8 +1115,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1}, - .audiomux = {2,0,0,0,1}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = {2,0,0,0,1 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -1126,8 +1130,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x010f00, - .muxsel = {2, 3, 0, 0}, - .audiomux = {0x10000, 0, 0x10000, 0, 0, 0}, + .muxsel = {2, 3, 0, 0 }, + .audiomux = {0x10000, 0, 0x10000, 0, 0, 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_ALPS_TSHC6_NTSC, @@ -1168,8 +1172,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 0, 1, 1}, - .audiomux = { 0, 1, 2, 3, 4}, + .muxsel = { 2, 0, 1, 1 }, + .audiomux = { 0, 1, 2, 3, 4 }, .pll = PLL_28, .tuner_type = -1 /* TUNER_ALPS_TMDH2_NTSC */, .tuner_addr = ADDR_UNSET, @@ -1185,8 +1189,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 3, .gpiomask = 0x03000F, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 1, 0xd0001, 0, 0, 10}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 1, 0xd0001, 0, 0, 10 }, /* sound path (5 sources): MUX1 (mask 0x03), Enable Pin 0x08 (0=enable, 1=disable) 0= ext. Audio IN @@ -1211,7 +1215,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x1c, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 0, 0, 0x10, 8, 4 }, .needs_tvaudio = 1, .pll = PLL_28, @@ -1232,7 +1236,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x18e0, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 0x0000,0x0800,0x1000,0x1000,0x18e0 }, /* For cards with tda9820/tda9821: 0x0000: Tuner normal stereo @@ -1252,8 +1256,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0xF, - .muxsel = { 2, 3, 1, 0}, - .audiomux = { 2, 0, 0, 0, 10}, + .muxsel = { 2, 3, 1, 0 }, + .audiomux = { 2, 0, 0, 0, 10 }, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_TEMIC_PAL, @@ -1270,7 +1274,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x1800, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 0, 0x800, 0x1000, 0x1000, 0x1800, 0 }, .pll = PLL_28, .tuner_type = 5, @@ -1320,8 +1324,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x03000F, - .muxsel = { 2, 3, 1, 0}, - .audiomux = { 2,0,0,0,1 }, + .muxsel = { 2, 3, 1, 0 }, + .audiomux = { 2, 0, 0, 0, 1 }, .pll = PLL_28, .tuner_type = 0, .tuner_addr = ADDR_UNSET, @@ -1337,8 +1341,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = -1, .gpiomask = 11, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 2, 0, 0, 1, 8}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 2, 0, 0, 1, 8 }, .pll = PLL_35, .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, @@ -1352,7 +1356,7 @@ struct tvcard bttv_tvcards[] = { .tuner = -1, .svhs = 1, .gpiomask = 0xF, - .muxsel = { 2, 2}, + .muxsel = { 2, 2 }, .audiomux = { }, .no_msp34xx = 1, .needs_tvaudio = 0, @@ -1371,8 +1375,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0xFF, - .muxsel = { 2, 3, 1, 0}, - .audiomux = { 1, 0, 4, 4, 9}, + .muxsel = { 2, 3, 1, 0 }, + .audiomux = { 1, 0, 4, 4, 9 }, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -1388,7 +1392,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0xf03f, .muxsel = { 2, 3, 1, 0 }, - .audiomux = { 0xbffe, 0, 0xbfff, 0, 0xbffe}, + .audiomux = { 0xbffe, 0, 0xbfff, 0, 0xbffe }, .pll = PLL_28, .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL, .tuner_addr = ADDR_UNSET, @@ -1404,8 +1408,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = -1, .gpiomask = 1, - .muxsel = { 2, 3, 0, 1}, - .audiomux = { 0, 0, 1, 0, 0}, + .muxsel = { 2, 3, 0, 1 }, + .audiomux = { 0, 0, 1, 0, 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL, @@ -1420,7 +1424,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x18e0, - .muxsel = { 2, 3, 0, 1}, + .muxsel = { 2, 3, 0, 1 }, /* Radio changed from 1e80 to 0x800 to make FlyVideo2000S in .hu happy (gm)*/ /* -dk-???: set mute=0x1800 for tda9874h daughterboard */ @@ -1441,8 +1445,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0xffff00, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0x500, 0x500, 0x300, 0x900, 0x900}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0x500, 0x500, 0x300, 0x900, 0x900 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -1458,8 +1462,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x010f00, - .muxsel = {2, 3, 0, 0}, - .audiomux = {0x10000, 0, 0x10000, 0, 0, 0}, + .muxsel = {2, 3, 0, 0 }, + .audiomux = {0x10000, 0, 0x10000, 0, 0, 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_SHARP_2U5JF5540_NTSC, @@ -1483,7 +1487,7 @@ struct tvcard bttv_tvcards[] = { .audiomux = {0x947fff, 0x987fff,0x947fff,0x947fff, 0x947fff}, /* tvtuner, radio, external,internal, mute, stereo * tuner, Composit, SVid, Composit-on-Svid-adapter */ - .muxsel = { 2, 3 ,0 ,1}, + .muxsel = { 2, 3 ,0 ,1 }, .tuner_type = TUNER_MT2032, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -1511,8 +1515,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 0, 0, 11, 7, 13, 0}, /* TV and Radio with same GPIO ! */ + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0, 0, 11, 7, 13, 0 }, /* TV and Radio with same GPIO ! */ .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = 25, @@ -1533,7 +1537,7 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 0, .tuner = -1, .svhs = 2, - .muxsel = { 2, 3, 1, 1}, /* AV1, AV2, SVHS, CVid adapter on SVHS */ + .muxsel = { 2, 3, 1, 1 }, /* AV1, AV2, SVHS, CVid adapter on SVHS */ .pll = PLL_28, .no_msp34xx = 1, .tuner_type = UNSET, @@ -1579,7 +1583,7 @@ struct tvcard bttv_tvcards[] = { .tuner = -1, .svhs = 4, .gpiomask = 0, - .muxsel = { 2, 3, 1, 0, 0}, + .muxsel = { 2, 3, 1, 0, 0 }, .audiomux = { 0 }, .needs_tvaudio = 0, .tuner_type = -1, @@ -1692,7 +1696,7 @@ struct tvcard bttv_tvcards[] = { .name = "DSP Design TCVIDEO", .video_inputs = 4, .svhs = -1, - .muxsel = { 2, 3, 1, 0}, + .muxsel = { 2, 3, 1, 0 }, .pll = PLL_28, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -1706,7 +1710,7 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 1, .tuner = 0, .svhs = 2, - .muxsel = { 2, 0, 1, 1}, + .muxsel = { 2, 0, 1, 1 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -1723,7 +1727,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x0f0f80, - .muxsel = {2, 3, 1, 0}, + .muxsel = {2, 3, 1, 0 }, .audiomux = {0x030000, 0x010000, 0, 0, 0x020000, 0}, .no_msp34xx = 1, .pll = PLL_28, @@ -1940,7 +1944,7 @@ struct tvcard bttv_tvcards[] = { .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, - .muxsel = { 3, 0, 1, 2}, + .muxsel = { 3, 0, 1, 2 }, .pll = PLL_28, .no_gpioirq = 1, .has_dvb = 1, @@ -1953,7 +1957,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 3, .gpiomask = 2, /* TV, Comp1, Composite over SVID con, SVID */ - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 2, 2, 0, 0, 0 }, .pll = PLL_28, .has_radio = 1, @@ -2070,7 +2074,7 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 1, .tuner = 0, .svhs = 2, - .muxsel = { 2, 3, 1, 1}, /* Tuner, CVid, SVid, CVid over SVid connector */ + .muxsel = { 2, 3, 1, 1 }, /* Tuner, CVid, SVid, CVid over SVid connector */ .gpiomask = 0, .no_tda9875 = 1, .no_tda7432 = 1, @@ -2124,7 +2128,7 @@ struct tvcard bttv_tvcards[] = { .video_inputs = 1, .tuner = -1, .svhs = -1, - .muxsel = { 2, 3, 1, 0}, + .muxsel = { 2, 3, 1, 0 }, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -2133,6 +2137,8 @@ struct tvcard bttv_tvcards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_dvb = 1, + .has_remote = 1, + .gpiomask = 0x1b, .no_gpioirq = 1, }, [BTTV_BOARD_PV143] = { @@ -2160,7 +2166,7 @@ struct tvcard bttv_tvcards[] = { .tuner = -1, /* card has no tuner */ .svhs = 3, .gpiomask = 0x00, - .muxsel = { 2, 3, 1, 0}, + .muxsel = { 2, 3, 1, 0 }, .audiomux = { 0, 0, 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, @@ -2175,7 +2181,7 @@ struct tvcard bttv_tvcards[] = { .tuner = -1, /* card has no tuner */ .svhs = 3, .gpiomask = 0x00, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 0, 0, 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, @@ -2262,7 +2268,7 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 1, .tuner = 0, .svhs = 2, - .muxsel = { 2, 3, 1, 0}, + .muxsel = { 2, 3, 1, 0 }, .tuner_type = TUNER_PHILIPS_ATSC, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -2285,7 +2291,7 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 0, .svhs = 1, .tuner = -1, - .muxsel = { 3, 1, 1, 3}, /* Vid In, SVid In, Vid over SVid in connector */ + .muxsel = { 3, 1, 1, 3 }, /* Vid In, SVid In, Vid over SVid in connector */ .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -2301,8 +2307,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 3, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 1, 1, 1, 1, 0}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 1, 1, 1, 1, 0 }, .needs_tvaudio = 1, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, @@ -2321,7 +2327,7 @@ struct tvcard bttv_tvcards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .pll = PLL_28, - .muxsel = { 2, 2, 2, 2}, + .muxsel = { 2, 2, 2, 2 }, .gpiomask = 0x3F, .muxsel_hook = gvc1100_muxsel, }, @@ -2332,8 +2338,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x008007, - .muxsel = {2, 3, 0, 0}, - .audiomux = {0, 0, 0, 0, 0x000003, 0}, + .muxsel = { 2, 3, 0, 0 }, + .audiomux = { 0, 0, 0, 0, 0x000003, 0 }, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, @@ -2368,7 +2374,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .needs_tvaudio = 0, .gpiomask = 0x68, - .muxsel = { 2, 3, 1}, + .muxsel = { 2, 3, 1 }, .audiomux = { 0x68, 0x68, 0x61, 0x61, 0x00 }, .pll = PLL_28, }, @@ -2427,7 +2433,7 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x0000000f, .muxsel = { 2, 1, 1 }, - .audiomux = { 0x02, 0x00, 0x00, 0x00, 0x00}, + .audiomux = { 0x02, 0x00, 0x00, 0x00, 0x00 }, .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -2460,7 +2466,7 @@ struct tvcard bttv_tvcards[] = { .video_inputs = 2, .tuner = -1, .svhs = 1, - .muxsel = { 3, 1, 2, 0}, /* Comp0, S-Video, ?, ? */ + .muxsel = { 3, 1, 2, 0 }, /* Comp0, S-Video, ?, ? */ .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -2517,8 +2523,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = -1, .gpiomask = 0xFF, - .muxsel = { 2, 3, 1, 1}, - .audiomux = { 2, 0, 0, 0, 10}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 2, 0, 0, 0, 10 }, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, @@ -2551,8 +2557,8 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x3f, - .muxsel = {2, 3, 1, 0}, - .audiomux = {0x31, 0x31, 0x31, 0x31, 0x31, 0x31}, + .muxsel = {2, 3, 1, 0 }, + .audiomux = {0x31, 0x31, 0x31, 0x31, 0x31, 0x31 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_NTSC_M, @@ -2570,12 +2576,12 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 1, .tuner = 0, .svhs = 2, - .muxsel = { 2, 3, 1, 0}, + .muxsel = { 2, 3, 1, 0 }, .tuner_type = TUNER_PHILIPS_NTSC, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .gpiomask = 0x008007, - .audiomux = { 0, 0x000001,0,0, 0}, + .audiomux = { 0, 0x000001,0,0, 0 }, .needs_tvaudio = 1, .has_radio = 1, }, @@ -2666,7 +2672,7 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 1, .tuner = 0, .svhs = 2, - .muxsel = { 2, 3, 1, 0}, + .muxsel = { 2, 3, 1, 0 }, .tuner_type = -1, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -2700,7 +2706,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 0x01fe00, - .muxsel = { 2, 3, 1, 1}, + .muxsel = { 2, 3, 1, 1 }, .audiomux = { 0x001e00, 0, 0x018000, 0x014000, 0x002000, 0 }, .needs_tvaudio = 1, .pll = PLL_28, @@ -2788,15 +2794,71 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1}, - .audiomux = {2,0,0,0,1}, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 2, 0, 0, 0, 1 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = 2, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, + /* ---- card 0x8e ---------------------------------- */ + [BTTV_BOARD_SABRENT_TVFM] = { + .name = "Sabrent TV-FM (bttv version)", + .video_inputs = 3, + .audio_inputs = 1, + .tuner = 0, + .svhs = 2, + .gpiomask = 0x108007, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 100000, 100002, 100002, 100000 }, + .no_msp34xx = 1, + .no_tda9875 = 1, + .no_tda7432 = 1, + .pll = PLL_28, + .tuner_type = TUNER_TNF_5335MF, + .tuner_addr = ADDR_UNSET, + .has_radio = 1, + }, + /* ---- card 0x8f ---------------------------------- */ + [BTTV_BOARD_HAUPPAUGE_IMPACTVCB] = { + .name = "Hauppauge ImpactVCB (bt878)", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = -1, + .svhs = -1, + .gpiomask = 0x0f, /* old: 7 */ + .muxsel = { 0, 1, 3, 2 }, /* Composite 0-3 */ + .no_msp34xx = 1, + .no_tda9875 = 1, + .no_tda7432 = 1, + .tuner_type = -1, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, + [BTTV_BOARD_MACHTV_MAGICTV] = { + /* Julian Calaby <julian.calaby@gmail.com> + * Slightly different from original MachTV definition (0x60) + * FIXME: RegSpy says gpiomask should be "0x001c800f", but it + * stuffs up remote chip. Bug is a pin on the jaecs is not set + * properly (methinks) causing no keyup bits being set */ + + .name = "MagicTV", /* rebranded MachTV */ + .video_inputs = 3, + .audio_inputs = 1, + .tuner = 0, + .svhs = 2, + .gpiomask = 7, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0, 1, 2, 3, 4 }, + .tuner_type = TUNER_TEMIC_4009FR5_PAL, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .pll = PLL_28, + .has_radio = 1, + .has_remote = 1, + }, }; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -2884,7 +2946,7 @@ void __devinit bttv_idcard(struct bttv *btv) */ /* Some Modular Technology cards have an eeprom, but no subsystem ID */ -void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256]) +static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256]) { int type = -1; @@ -3017,26 +3079,33 @@ static void miro_pinnacle_gpio(struct bttv *btv) switch (id) { case 1: info = "PAL / mono"; + btv->tda9887_conf = TDA9887_INTERCARRIER; break; case 2: info = "PAL+SECAM / stereo"; btv->has_radio = 1; + btv->tda9887_conf = TDA9887_QSS; break; case 3: info = "NTSC / stereo"; btv->has_radio = 1; + btv->tda9887_conf = TDA9887_QSS; break; case 4: info = "PAL+SECAM / mono"; + btv->tda9887_conf = TDA9887_QSS; break; case 5: info = "NTSC / mono"; + btv->tda9887_conf = TDA9887_INTERCARRIER; break; case 6: info = "NTSC / stereo"; + btv->tda9887_conf = TDA9887_INTERCARRIER; break; case 7: info = "PAL / stereo"; + btv->tda9887_conf = TDA9887_INTERCARRIER; break; default: info = "oops: unknown card"; @@ -3047,8 +3116,7 @@ static void miro_pinnacle_gpio(struct bttv *btv) printk(KERN_INFO "bttv%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n", btv->c.nr, id, info, btv->has_radio ? "yes" : "no"); - btv->tuner_type = 33; - btv->pinnacle_id = id; + btv->tuner_type = TUNER_MT2032; } } @@ -3350,9 +3418,9 @@ void __devinit bttv_init_card2(struct bttv *btv) bttv_call_i2c_clients(btv, TUNER_SET_TYPE_ADDR, &tun_setup); } - if (btv->pinnacle_id != UNSET) { - bttv_call_i2c_clients(btv, AUDC_CONFIG_PINNACLE, - &btv->pinnacle_id); + if (btv->tda9887_conf) { + bttv_call_i2c_clients(btv, TDA9887_SET_CONFIG, + &btv->tda9887_conf); } btv->svhs = bttv_tvcards[btv->c.type].svhs; @@ -3402,7 +3470,7 @@ void __devinit bttv_init_card2(struct bttv *btv) /* tuner modules */ tda9887 = 0; - if (btv->pinnacle_id != UNSET) + if (btv->tda9887_conf) tda9887 = 1; if (0 == tda9887 && 0 == bttv_tvcards[btv->c.type].has_dvb && bttv_I2CRead(btv, I2C_TDA9887, "TDA9887") >=0) @@ -3449,6 +3517,21 @@ static void __devinit hauppauge_eeprom(struct bttv *btv) tveeprom_hauppauge_analog(&btv->i2c_client, &tv, eeprom_data); btv->tuner_type = tv.tuner_type; btv->has_radio = tv.has_radio; + + printk("bttv%d: Hauppauge eeprom indicates model#%d\n", + btv->c.nr, tv.model); + + /* + * Some of the 878 boards have duplicate PCI IDs. Switch the board + * type based on model #. + */ + if(tv.model == 64900) { + printk("bttv%d: Switching board type from %s to %s\n", + btv->c.nr, + bttv_tvcards[btv->c.type].name, + bttv_tvcards[BTTV_BOARD_HAUPPAUGE_IMPACTVCB].name); + btv->c.type = BTTV_BOARD_HAUPPAUGE_IMPACTVCB; + } } static int terratec_active_radio_upgrade(struct bttv *btv) @@ -3857,7 +3940,7 @@ static void __devinit init_PXC200(struct bttv *btv) * error. ERROR_CPLD_Check_Failed. */ /* ----------------------------------------------------------------------- */ -void +static void init_RTV24 (struct bttv *btv) { uint32_t dataRead = 0; @@ -4081,7 +4164,7 @@ void tea5757_set_freq(struct bttv *btv, unsigned short freq) /* ----------------------------------------------------------------------- */ /* winview */ -void winview_audio(struct bttv *btv, struct video_audio *v, int set) +static void winview_audio(struct bttv *btv, struct video_audio *v, int set) { /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */ int bits_out, loops, vol, data; diff --git a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c index 0005741d551..aa4c4c52188 100644 --- a/drivers/media/video/bttv-driver.c +++ b/drivers/media/video/bttv-driver.c @@ -34,60 +34,60 @@ #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/kdev_t.h> +#include "bttvp.h" +#include <media/v4l2-common.h> + #include <linux/dma-mapping.h> #include <asm/io.h> #include <asm/byteorder.h> -#include "bttvp.h" - #include "rds.h" unsigned int bttv_num; /* number of Bt848s in use */ struct bttv bttvs[BTTV_MAX]; -unsigned int bttv_debug = 0; +unsigned int bttv_debug; unsigned int bttv_verbose = 1; -unsigned int bttv_gpio = 0; +unsigned int bttv_gpio; /* config variables */ #ifdef __BIG_ENDIAN static unsigned int bigendian=1; #else -static unsigned int bigendian=0; +static unsigned int bigendian; #endif static unsigned int radio[BTTV_MAX]; -static unsigned int irq_debug = 0; +static unsigned int irq_debug; static unsigned int gbuffers = 8; static unsigned int gbufsize = 0x208000; static int video_nr = -1; static int radio_nr = -1; static int vbi_nr = -1; -static int debug_latency = 0; +static int debug_latency; -static unsigned int fdsr = 0; +static unsigned int fdsr; /* options */ -static unsigned int combfilter = 0; -static unsigned int lumafilter = 0; +static unsigned int combfilter; +static unsigned int lumafilter; static unsigned int automute = 1; -static unsigned int chroma_agc = 0; +static unsigned int chroma_agc; static unsigned int adc_crush = 1; static unsigned int whitecrush_upper = 0xCF; static unsigned int whitecrush_lower = 0x7F; -static unsigned int vcr_hack = 0; -static unsigned int irq_iswitch = 0; +static unsigned int vcr_hack; +static unsigned int irq_iswitch; static unsigned int uv_ratio = 50; -static unsigned int full_luma_range = 0; -static unsigned int coring = 0; +static unsigned int full_luma_range; +static unsigned int coring; extern int no_overlay; /* API features (turn on/off stuff for testing) */ static unsigned int v4l2 = 1; - /* insmod args */ module_param(bttv_verbose, int, 0644); module_param(bttv_gpio, int, 0644); @@ -210,6 +210,9 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vdelay = 0x20, .vbipack = 255, .sram = 0, + /* ITU-R frame line number of the first VBI line + we can capture, of the first and second field. */ + .vbistart = { 7,320 }, },{ .v4l2_id = V4L2_STD_NTSC_M, .name = "NTSC", @@ -226,6 +229,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vdelay = 0x1a, .vbipack = 144, .sram = 1, + .vbistart = { 10, 273 }, },{ .v4l2_id = V4L2_STD_SECAM, .name = "SECAM", @@ -242,6 +246,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vdelay = 0x20, .vbipack = 255, .sram = 0, /* like PAL, correct? */ + .vbistart = { 7, 320 }, },{ .v4l2_id = V4L2_STD_PAL_Nc, .name = "PAL-Nc", @@ -258,6 +263,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vdelay = 0x1a, .vbipack = 144, .sram = -1, + .vbistart = { 7, 320 }, },{ .v4l2_id = V4L2_STD_PAL_M, .name = "PAL-M", @@ -274,6 +280,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vdelay = 0x1a, .vbipack = 144, .sram = -1, + .vbistart = { 10, 273 }, },{ .v4l2_id = V4L2_STD_PAL_N, .name = "PAL-N", @@ -290,6 +297,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vdelay = 0x20, .vbipack = 144, .sram = -1, + .vbistart = { 7, 320}, },{ .v4l2_id = V4L2_STD_NTSC_M_JP, .name = "NTSC-JP", @@ -306,6 +314,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vdelay = 0x16, .vbipack = 144, .sram = -1, + .vbistart = {10, 273}, },{ /* that one hopefully works with the strange timing * which video recorders produce when playing a NTSC @@ -326,6 +335,7 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vbipack = 255, .vtotal = 524, .sram = -1, + .vbistart = { 10, 273 }, } }; static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms); @@ -674,16 +684,16 @@ int check_alloc_btres(struct bttv *btv, struct bttv_fh *fh, int bit) return 1; /* is it free? */ - down(&btv->reslock); + mutex_lock(&btv->reslock); if (btv->resources & bit) { /* no, someone else uses it */ - up(&btv->reslock); + mutex_unlock(&btv->reslock); return 0; } /* it's free, grab it */ fh->resources |= bit; btv->resources |= bit; - up(&btv->reslock); + mutex_unlock(&btv->reslock); return 1; } @@ -706,10 +716,10 @@ void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits) /* trying to free ressources not allocated by us ... */ printk("bttv: BUG! (btres)\n"); } - down(&btv->reslock); + mutex_lock(&btv->reslock); fh->resources &= ~bits; btv->resources &= ~bits; - up(&btv->reslock); + mutex_unlock(&btv->reslock); } /* ----------------------------------------------------------------------- */ @@ -727,71 +737,71 @@ void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits) static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout) { - unsigned char fl, fh, fi; + unsigned char fl, fh, fi; - /* prevent overflows */ - fin/=4; - fout/=4; + /* prevent overflows */ + fin/=4; + fout/=4; - fout*=12; - fi=fout/fin; + fout*=12; + fi=fout/fin; - fout=(fout%fin)*256; - fh=fout/fin; + fout=(fout%fin)*256; + fh=fout/fin; - fout=(fout%fin)*256; - fl=fout/fin; + fout=(fout%fin)*256; + fl=fout/fin; - btwrite(fl, BT848_PLL_F_LO); - btwrite(fh, BT848_PLL_F_HI); - btwrite(fi|BT848_PLL_X, BT848_PLL_XCI); + btwrite(fl, BT848_PLL_F_LO); + btwrite(fh, BT848_PLL_F_HI); + btwrite(fi|BT848_PLL_X, BT848_PLL_XCI); } static void set_pll(struct bttv *btv) { - int i; + int i; - if (!btv->pll.pll_crystal) - return; + if (!btv->pll.pll_crystal) + return; if (btv->pll.pll_ofreq == btv->pll.pll_current) { dprintk("bttv%d: PLL: no change required\n",btv->c.nr); - return; - } + return; + } - if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) { - /* no PLL needed */ - if (btv->pll.pll_current == 0) - return; + if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) { + /* no PLL needed */ + if (btv->pll.pll_current == 0) + return; bttv_printk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n", - btv->c.nr,btv->pll.pll_ifreq); - btwrite(0x00,BT848_TGCTRL); - btwrite(0x00,BT848_PLL_XCI); - btv->pll.pll_current = 0; - return; - } + btv->c.nr,btv->pll.pll_ifreq); + btwrite(0x00,BT848_TGCTRL); + btwrite(0x00,BT848_PLL_XCI); + btv->pll.pll_current = 0; + return; + } bttv_printk(KERN_INFO "bttv%d: PLL: %d => %d ",btv->c.nr, - btv->pll.pll_ifreq, btv->pll.pll_ofreq); + btv->pll.pll_ifreq, btv->pll.pll_ofreq); set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq); - for (i=0; i<10; i++) { + for (i=0; i<10; i++) { /* Let other people run while the PLL stabilizes */ bttv_printk("."); msleep(10); - if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) { + if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) { btwrite(0,BT848_DSTATUS); - } else { - btwrite(0x08,BT848_TGCTRL); - btv->pll.pll_current = btv->pll.pll_ofreq; + } else { + btwrite(0x08,BT848_TGCTRL); + btv->pll.pll_current = btv->pll.pll_ofreq; bttv_printk(" ok\n"); - return; - } - } - btv->pll.pll_current = -1; + return; + } + } + btv->pll.pll_current = -1; bttv_printk("failed\n"); - return; + return; } /* used to switch between the bt848's analog/digital video capture modes */ @@ -1510,14 +1520,6 @@ static struct videobuf_queue_ops bttv_video_qops = { .buf_release = buffer_release, }; -static const char *v4l1_ioctls[] = { - "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", - "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", - "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", - "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", - "SMICROCODE", "GVBIFMT", "SVBIFMT" }; -#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) - static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) { switch (cmd) { @@ -1534,12 +1536,12 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) case VIDIOCSFREQ: { unsigned long *freq = arg; - down(&btv->lock); + mutex_lock(&btv->lock); btv->freq=*freq; bttv_call_i2c_clients(btv,VIDIOCSFREQ,freq); if (btv->has_matchbox && btv->radio_user) tea5757_set_freq(btv,*freq); - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } @@ -1569,10 +1571,10 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) if (v->mode >= BTTV_TVNORMS) return -EINVAL; - down(&btv->lock); + mutex_lock(&btv->lock); set_tvnorm(btv,v->mode); bttv_call_i2c_clients(btv,cmd,v); - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } @@ -1609,17 +1611,17 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) if (v->norm >= BTTV_TVNORMS) return -EINVAL; - down(&btv->lock); + mutex_lock(&btv->lock); if (channel == btv->input && v->norm == btv->tvnorm) { /* nothing to do */ - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } btv->tvnorm = v->norm; set_input(btv,v->channel); - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } @@ -1632,14 +1634,14 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) v->flags |= VIDEO_AUDIO_MUTABLE; v->mode = VIDEO_SOUND_MONO; - down(&btv->lock); + mutex_lock(&btv->lock); bttv_call_i2c_clients(btv,cmd,v); /* card specific hooks */ if (btv->audio_hook) btv->audio_hook(btv,v,0); - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } case VIDIOCSAUDIO: @@ -1650,7 +1652,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) if (audio >= bttv_tvcards[btv->c.type].audio_inputs) return -EINVAL; - down(&btv->lock); + mutex_lock(&btv->lock); audio_mux(btv, (v->flags&VIDEO_AUDIO_MUTE) ? AUDIO_MUTE : AUDIO_UNMUTE); bttv_call_i2c_clients(btv,cmd,v); @@ -1658,7 +1660,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) if (btv->audio_hook) btv->audio_hook(btv,v,1); - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } @@ -1692,10 +1694,10 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) if (i == BTTV_TVNORMS) return -EINVAL; - down(&btv->lock); + mutex_lock(&btv->lock); set_tvnorm(btv,i); i2c_vidiocschan(btv); - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } case VIDIOC_QUERYSTD: @@ -1720,7 +1722,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) memset(i,0,sizeof(*i)); i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; - i->audioset = 1; + i->audioset = 0; if (i->index == bttv_tvcards[btv->c.type].tuner) { sprintf(i->name, "Television"); i->type = V4L2_INPUT_TYPE_TUNER; @@ -1753,9 +1755,9 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) if (*i > bttv_tvcards[btv->c.type].video_inputs) return -EINVAL; - down(&btv->lock); + mutex_lock(&btv->lock); set_input(btv,*i); - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } @@ -1767,16 +1769,24 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) return -EINVAL; if (0 != t->index) return -EINVAL; - down(&btv->lock); + mutex_lock(&btv->lock); memset(t,0,sizeof(*t)); strcpy(t->name, "Television"); t->type = V4L2_TUNER_ANALOG_TV; - t->rangehigh = 0xffffffffUL; t->capability = V4L2_TUNER_CAP_NORM; t->rxsubchans = V4L2_TUNER_SUB_MONO; if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) t->signal = 0xffff; { + struct video_tuner tuner; + + memset(&tuner, 0, sizeof (tuner)); + tuner.rangehigh = 0xffffffffUL; + bttv_call_i2c_clients(btv, VIDIOCGTUNER, &tuner); + t->rangelow = tuner.rangelow; + t->rangehigh = tuner.rangehigh; + } + { /* Hmmm ... */ struct video_audio va; memset(&va, 0, sizeof(struct video_audio)); @@ -1794,7 +1804,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) } } /* FIXME: fill capability+audmode */ - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } case VIDIOC_S_TUNER: @@ -1805,7 +1815,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) return -EINVAL; if (0 != t->index) return -EINVAL; - down(&btv->lock); + mutex_lock(&btv->lock); { struct video_audio va; memset(&va, 0, sizeof(struct video_audio)); @@ -1822,7 +1832,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) if (btv->audio_hook) btv->audio_hook(btv,&va,1); } - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } @@ -1843,17 +1853,17 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) return -EINVAL; if (unlikely (f->type != V4L2_TUNER_ANALOG_TV)) return -EINVAL; - down(&btv->lock); + mutex_lock(&btv->lock); btv->freq = f->frequency; bttv_call_i2c_clients(btv,VIDIOCSFREQ,&btv->freq); if (btv->has_matchbox && btv->radio_user) tea5757_set_freq(btv,btv->freq); - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } case VIDIOC_LOG_STATUS: { - bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, 0); + bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, NULL); return 0; } @@ -1956,7 +1966,7 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv, } down(&fh->cap.lock); - kfree(fh->ov.clips); + kfree(fh->ov.clips); fh->ov.clips = clips; fh->ov.nclips = n; @@ -2029,19 +2039,33 @@ static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type) return 0; } +static void +pix_format_set_size (struct v4l2_pix_format * f, + const struct bttv_format * fmt, + unsigned int width, + unsigned int height) +{ + f->width = width; + f->height = height; + + if (fmt->flags & FORMAT_FLAGS_PLANAR) { + f->bytesperline = width; /* Y plane */ + f->sizeimage = (width * height * fmt->depth) >> 3; + } else { + f->bytesperline = (width * fmt->depth) >> 3; + f->sizeimage = height * f->bytesperline; + } +} + static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f) { switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: memset(&f->fmt.pix,0,sizeof(struct v4l2_pix_format)); - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; + pix_format_set_size (&f->fmt.pix, fh->fmt, + fh->width, fh->height); f->fmt.pix.field = fh->cap.field; f->fmt.pix.pixelformat = fh->fmt->fourcc; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; return 0; case V4L2_BUF_TYPE_VIDEO_OVERLAY: memset(&f->fmt.win,0,sizeof(struct v4l2_window)); @@ -2106,11 +2130,9 @@ static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv, f->fmt.pix.width = maxw; if (f->fmt.pix.height > maxh) f->fmt.pix.height = maxh; - f->fmt.pix.width &= ~0x03; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; + pix_format_set_size (&f->fmt.pix, fmt, + f->fmt.pix.width & ~3, + f->fmt.pix.height); return 0; } @@ -2186,22 +2208,9 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, unsigned long flags; int retval = 0; - if (bttv_debug > 1) { - switch (_IOC_TYPE(cmd)) { - case 'v': - printk("bttv%d: ioctl 0x%x (v4l1, VIDIOC%s)\n", - btv->c.nr, cmd, (_IOC_NR(cmd) < V4L1_IOCTLS) ? - v4l1_ioctls[_IOC_NR(cmd)] : "???"); - break; - case 'V': - printk("bttv%d: ioctl 0x%x (v4l2, %s)\n", - btv->c.nr, cmd, v4l2_ioctl_names[_IOC_NR(cmd)]); - break; - default: - printk("bttv%d: ioctl 0x%x (???)\n", - btv->c.nr, cmd); - } - } + if (bttv_debug > 1) + v4l_print_ioctl(btv->c.name, cmd); + if (btv->errors) bttv_reinit_bt848(btv); @@ -2278,6 +2287,15 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, retval = -EINVAL; goto fh_unlock_and_return; } + if (fmt->flags & FORMAT_FLAGS_RAW) { + /* VIDIOCMCAPTURE uses gbufsize, not RAW_BPL * + RAW_LINES * 2. F1 is stored at offset 0, F2 + at buffer size / 2. */ + fh->width = RAW_BPL; + fh->height = gbufsize / RAW_BPL; + btv->init.width = RAW_BPL; + btv->init.height = gbufsize / RAW_BPL; + } fh->ovfmt = fmt; fh->fmt = fmt; btv->init.ovfmt = fmt; @@ -2541,10 +2559,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, fmt->count[0] = fmt2.fmt.vbi.count[0]; fmt->start[1] = fmt2.fmt.vbi.start[1]; fmt->count[1] = fmt2.fmt.vbi.count[1]; - if (fmt2.fmt.vbi.flags & VBI_UNSYNC) - fmt->flags |= V4L2_VBI_UNSYNC; - if (fmt2.fmt.vbi.flags & VBI_INTERLACED) - fmt->flags |= V4L2_VBI_INTERLACED; + if (fmt2.fmt.vbi.flags & V4L2_VBI_UNSYNC) + fmt->flags |= VBI_UNSYNC; + if (fmt2.fmt.vbi.flags & V4L2_VBI_INTERLACED) + fmt->flags |= VBI_INTERLACED; return 0; } case VIDIOCSVBIFMT: @@ -2589,9 +2607,11 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, if (0 == v4l2) return -EINVAL; - strcpy(cap->driver,"bttv"); - strlcpy(cap->card,btv->video_dev->name,sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s",pci_name(btv->c.pci)); + memset(cap, 0, sizeof (*cap)); + strlcpy(cap->driver, "bttv", sizeof (cap->driver)); + strlcpy(cap->card, btv->video_dev->name, sizeof (cap->card)); + snprintf(cap->bus_info, sizeof (cap->bus_info), + "PCI:%s", pci_name(btv->c.pci)); cap->version = BTTV_VERSION_CODE; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | @@ -2727,7 +2747,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, fh->ov.w.height = fb->fmt.height; btv->init.ov.w.width = fb->fmt.width; btv->init.ov.w.height = fb->fmt.height; - kfree(fh->ov.clips); + kfree(fh->ov.clips); fh->ov.clips = NULL; fh->ov.nclips = 0; @@ -2952,6 +2972,8 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait) fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR; field = videobuf_next_field(&fh->cap); if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) { + kfree (fh->cap.read_buf); + fh->cap.read_buf = NULL; up(&fh->cap.lock); return POLLERR; } @@ -3087,6 +3109,7 @@ static struct file_operations bttv_fops = .open = bttv_open, .release = bttv_release, .ioctl = bttv_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, .read = bttv_read, .mmap = bttv_mmap, @@ -3133,7 +3156,7 @@ static int radio_open(struct inode *inode, struct file *file) return -ENODEV; dprintk("bttv%d: open called (radio)\n",btv->c.nr); - down(&btv->lock); + mutex_lock(&btv->lock); btv->radio_user++; @@ -3142,7 +3165,7 @@ static int radio_open(struct inode *inode, struct file *file) bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type); audio_mux(btv,AUDIO_RADIO); - up(&btv->lock); + mutex_unlock(&btv->lock); return 0; } @@ -3196,6 +3219,7 @@ static int radio_do_ioctl(struct inode *inode, struct file *file, case VIDIOCSFREQ: case VIDIOCGAUDIO: case VIDIOCSAUDIO: + case VIDIOC_LOG_STATUS: return bttv_common_ioctls(btv,cmd,arg); default: @@ -3667,6 +3691,10 @@ static irqreturn_t bttv_irq(int irq, void *dev_id, struct pt_regs * regs) int handled = 0; btv=(struct bttv *)dev_id; + + if (btv->custom_irq) + handled = btv->custom_irq(btv); + count=0; while (1) { /* get/clear interrupt status bits */ @@ -3701,9 +3729,9 @@ static irqreturn_t bttv_irq(int irq, void *dev_id, struct pt_regs * regs) if (astat&BT848_INT_VSYNC) btv->field_count++; - if (astat & BT848_INT_GPINT) { + if ((astat & BT848_INT_GPINT) && btv->remote) { wake_up(&btv->gpioq); - bttv_gpio_irq(&btv->c); + bttv_input_irq(btv); } if (astat & BT848_INT_I2CDONE) { @@ -3892,8 +3920,8 @@ static int __devinit bttv_probe(struct pci_dev *dev, sprintf(btv->c.name,"bttv%d",btv->c.nr); /* initialize structs / fill in defaults */ - init_MUTEX(&btv->lock); - init_MUTEX(&btv->reslock); + mutex_init(&btv->lock); + mutex_init(&btv->reslock); spin_lock_init(&btv->s_lock); spin_lock_init(&btv->gpio_lock); init_waitqueue_head(&btv->gpioq); @@ -3909,7 +3937,6 @@ static int __devinit bttv_probe(struct pci_dev *dev, btv->i2c_rc = -1; btv->tuner_type = UNSET; - btv->pinnacle_id = UNSET; btv->new_input = UNSET; btv->has_radio=radio[btv->c.nr]; @@ -4028,11 +4055,11 @@ static int __devinit bttv_probe(struct pci_dev *dev, } /* add subdevices */ - if (btv->has_remote) - bttv_sub_add_device(&btv->c, "remote"); if (bttv_tvcards[btv->c.type].has_dvb) bttv_sub_add_device(&btv->c, "dvb"); + bttv_input_init(btv); + /* everything is fine */ bttv_num++; return 0; @@ -4067,6 +4094,7 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev) /* tell gpio modules we are leaving ... */ btv->shutdown=1; wake_up(&btv->gpioq); + bttv_input_fini(btv); bttv_sub_del_devices(&btv->c); /* unregister i2c_bus + input */ @@ -4216,7 +4244,7 @@ static int bttv_init_module(void) bttv_check_chipset(); bus_register(&bttv_sub_bus_type); - return pci_module_init(&bttv_pci_driver); + return pci_register_driver(&bttv_pci_driver); } static void bttv_cleanup_module(void) diff --git a/drivers/media/video/bttv-gpio.c b/drivers/media/video/bttv-gpio.c index 575ce8b8e71..c4d5e2b70c2 100644 --- a/drivers/media/video/bttv-gpio.c +++ b/drivers/media/video/bttv-gpio.c @@ -47,9 +47,29 @@ static int bttv_sub_bus_match(struct device *dev, struct device_driver *drv) return 0; } +static int bttv_sub_probe(struct device *dev) +{ + struct bttv_sub_device *sdev = to_bttv_sub_dev(dev); + struct bttv_sub_driver *sub = to_bttv_sub_drv(dev->driver); + + return sub->probe ? sub->probe(sdev) : -ENODEV; +} + +static int bttv_sub_remove(struct device *dev) +{ + struct bttv_sub_device *sdev = to_bttv_sub_dev(dev); + struct bttv_sub_driver *sub = to_bttv_sub_drv(dev->driver); + + if (sub->remove) + sub->remove(sdev); + return 0; +} + struct bus_type bttv_sub_bus_type = { - .name = "bttv-sub", - .match = &bttv_sub_bus_match, + .name = "bttv-sub", + .match = &bttv_sub_bus_match, + .probe = bttv_sub_probe, + .remove = bttv_sub_remove, }; EXPORT_SYMBOL(bttv_sub_bus_type); @@ -64,10 +84,9 @@ int bttv_sub_add_device(struct bttv_core *core, char *name) struct bttv_sub_device *sub; int err; - sub = kmalloc(sizeof(*sub),GFP_KERNEL); + sub = kzalloc(sizeof(*sub),GFP_KERNEL); if (NULL == sub) return -ENOMEM; - memset(sub,0,sizeof(*sub)); sub->core = core; sub->dev.parent = &core->pci->dev; diff --git a/drivers/media/video/bttv-i2c.c b/drivers/media/video/bttv-i2c.c index 77619eb131f..614c1201855 100644 --- a/drivers/media/video/bttv-i2c.c +++ b/drivers/media/video/bttv-i2c.c @@ -28,10 +28,11 @@ #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/jiffies.h> -#include <asm/io.h> #include "bttvp.h" +#include <media/v4l2-common.h> +#include <linux/jiffies.h> +#include <asm/io.h> static struct i2c_algo_bit_data bttv_i2c_algo_bit_template; static struct i2c_adapter bttv_i2c_adap_sw_template; @@ -40,9 +41,9 @@ static struct i2c_client bttv_i2c_client_template; static int attach_inform(struct i2c_client *client); -static int i2c_debug = 0; -static int i2c_hw = 0; -static int i2c_scan = 0; +static int i2c_debug; +static int i2c_hw; +static int i2c_scan; module_param(i2c_debug, int, 0644); module_param(i2c_hw, int, 0444); module_param(i2c_scan, int, 0444); @@ -105,10 +106,8 @@ static struct i2c_algo_bit_data bttv_i2c_algo_bit_template = { static struct i2c_adapter bttv_i2c_adap_sw_template = { .owner = THIS_MODULE, -#ifdef I2C_CLASS_TV_ANALOG .class = I2C_CLASS_TV_ANALOG, -#endif - .name = "bt848", + .name = "bttv", .id = I2C_HW_B_BT848, .client_register = attach_inform, }; @@ -275,10 +274,8 @@ static struct i2c_algorithm bttv_algo = { }; static struct i2c_adapter bttv_i2c_adap_hw_template = { - .owner = THIS_MODULE, -#ifdef I2C_CLASS_TV_ANALOG + .owner = THIS_MODULE, .class = I2C_CLASS_TV_ANALOG, -#endif .name = "bt878", .id = I2C_HW_B_BT848 /* FIXME */, .algo = &bttv_algo, @@ -300,7 +297,7 @@ static int attach_inform(struct i2c_client *client) if (bttv_debug) printk(KERN_DEBUG "bttv%d: %s i2c attach [addr=0x%x,client=%s]\n", - btv->c.nr,client->driver->name,client->addr, + btv->c.nr, client->driver->driver.name, client->addr, client->name); if (!client->driver->command) return 0; @@ -441,12 +438,10 @@ int __devinit init_bttv_i2c(struct bttv *btv) i2c_set_adapdata(&btv->c.i2c_adap, btv); btv->i2c_client.adapter = &btv->c.i2c_adap; -#ifdef I2C_CLASS_TV_ANALOG if (bttv_tvcards[btv->c.type].no_video) btv->c.i2c_adap.class &= ~I2C_CLASS_TV_ANALOG; if (bttv_tvcards[btv->c.type].has_dvb) btv->c.i2c_adap.class |= I2C_CLASS_TV_DIGITAL; -#endif if (btv->use_i2c_hw) { btv->i2c_rc = i2c_add_adapter(&btv->c.i2c_adap); diff --git a/drivers/media/video/ir-kbd-gpio.c b/drivers/media/video/bttv-input.c index ed81934ef3c..221b36e7f39 100644 --- a/drivers/media/video/ir-kbd-gpio.c +++ b/drivers/media/video/bttv-input.c @@ -24,11 +24,9 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/input.h> -#include <linux/pci.h> - -#include <media/ir-common.h> #include "bttv.h" +#include "bttvp.h" /* ---------------------------------------------------------------------- */ @@ -156,9 +154,6 @@ static IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE] = { /* ---------------------------------------------------------------------- */ -/* Ricardo Cerqueira <v4l@cerqueira.org> */ -/* Weird matching, since the remote has "uncommon" keys */ - static IR_KEYTAB_TYPE ir_codes_conceptronic[IR_KEYTAB_SIZE] = { [ 30 ] = KEY_POWER, // power @@ -221,50 +216,82 @@ static IR_KEYTAB_TYPE ir_codes_conceptronic[IR_KEYTAB_SIZE] = { [ 24 ] = KEY_MUTE // mute/unmute }; -struct IR { - struct bttv_sub_device *sub; - struct input_dev *input; - struct ir_input_state ir; - char name[32]; - char phys[32]; - u32 mask_keycode; - u32 mask_keydown; - u32 mask_keyup; - - int polling; - u32 last_gpio; - struct work_struct work; - struct timer_list timer; +static IR_KEYTAB_TYPE ir_codes_nebula[IR_KEYTAB_SIZE] = { + [0x00] = KEY_KP0, + [0x01] = KEY_KP1, + [0x02] = KEY_KP2, + [0x03] = KEY_KP3, + [0x04] = KEY_KP4, + [0x05] = KEY_KP5, + [0x06] = KEY_KP6, + [0x07] = KEY_KP7, + [0x08] = KEY_KP8, + [0x09] = KEY_KP9, + [0x0a] = KEY_TV, + [0x0b] = KEY_AUX, + [0x0c] = KEY_DVD, + [0x0d] = KEY_POWER, + [0x0e] = KEY_MHP, /* labelled 'Picture' */ + [0x0f] = KEY_AUDIO, + [0x10] = KEY_INFO, + [0x11] = KEY_F13, /* 16:9 */ + [0x12] = KEY_F14, /* 14:9 */ + [0x13] = KEY_EPG, + [0x14] = KEY_EXIT, + [0x15] = KEY_MENU, + [0x16] = KEY_UP, + [0x17] = KEY_DOWN, + [0x18] = KEY_LEFT, + [0x19] = KEY_RIGHT, + [0x1a] = KEY_ENTER, + [0x1b] = KEY_CHANNELUP, + [0x1c] = KEY_CHANNELDOWN, + [0x1d] = KEY_VOLUMEUP, + [0x1e] = KEY_VOLUMEDOWN, + [0x1f] = KEY_RED, + [0x20] = KEY_GREEN, + [0x21] = KEY_YELLOW, + [0x22] = KEY_BLUE, + [0x23] = KEY_SUBTITLE, + [0x24] = KEY_F15, /* AD */ + [0x25] = KEY_TEXT, + [0x26] = KEY_MUTE, + [0x27] = KEY_REWIND, + [0x28] = KEY_STOP, + [0x29] = KEY_PLAY, + [0x2a] = KEY_FASTFORWARD, + [0x2b] = KEY_F16, /* chapter */ + [0x2c] = KEY_PAUSE, + [0x2d] = KEY_PLAY, + [0x2e] = KEY_RECORD, + [0x2f] = KEY_F17, /* picture in picture */ + [0x30] = KEY_KPPLUS, /* zoom in */ + [0x31] = KEY_KPMINUS, /* zoom out */ + [0x32] = KEY_F18, /* capture */ + [0x33] = KEY_F19, /* web */ + [0x34] = KEY_EMAIL, + [0x35] = KEY_PHONE, + [0x36] = KEY_PC }; static int debug; module_param(debug, int, 0644); /* debug level (0,1,2) */ +static int repeat_delay = 500; +module_param(repeat_delay, int, 0644); +static int repeat_period = 33; +module_param(repeat_period, int, 0644); -#define DEVNAME "ir-kbd-gpio" -#define dprintk(fmt, arg...) if (debug) \ - printk(KERN_DEBUG DEVNAME ": " fmt , ## arg) - -static void ir_irq(struct bttv_sub_device *sub); -static int ir_probe(struct device *dev); -static int ir_remove(struct device *dev); - -static struct bttv_sub_driver driver = { - .drv = { - .name = DEVNAME, - .probe = ir_probe, - .remove = ir_remove, - }, - .gpio_irq = ir_irq, -}; +#define DEVNAME "bttv-input" /* ---------------------------------------------------------------------- */ -static void ir_handle_key(struct IR *ir) +static void ir_handle_key(struct bttv *btv) { + struct bttv_ir *ir = btv->remote; u32 gpio,data; /* read gpio value */ - gpio = bttv_gpio_read(ir->sub->core); + gpio = bttv_gpio_read(&btv->c); if (ir->polling) { if (ir->last_gpio == gpio) return; @@ -273,80 +300,231 @@ static void ir_handle_key(struct IR *ir) /* extract data */ data = ir_extract_bits(gpio, ir->mask_keycode); - dprintk(DEVNAME ": irq gpio=0x%x code=%d | %s%s%s\n", + dprintk(KERN_INFO DEVNAME ": irq gpio=0x%x code=%d | %s%s%s\n", gpio, data, ir->polling ? "poll" : "irq", (gpio & ir->mask_keydown) ? " down" : "", (gpio & ir->mask_keyup) ? " up" : ""); - if (ir->mask_keydown) { - /* bit set on keydown */ - if (gpio & ir->mask_keydown) { - ir_input_keydown(ir->input, &ir->ir, data, data); - } else { - ir_input_nokey(ir->input, &ir->ir); - } - - } else if (ir->mask_keyup) { - /* bit cleared on keydown */ - if (0 == (gpio & ir->mask_keyup)) { - ir_input_keydown(ir->input, &ir->ir, data, data); - } else { - ir_input_nokey(ir->input, &ir->ir); - } - + if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || + (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { + ir_input_keydown(ir->dev,&ir->ir,data,data); } else { - /* can't disturgissh keydown/up :-/ */ - ir_input_keydown(ir->input, &ir->ir, data, data); - ir_input_nokey(ir->input, &ir->ir); + ir_input_nokey(ir->dev,&ir->ir); } + } -static void ir_irq(struct bttv_sub_device *sub) +void bttv_input_irq(struct bttv *btv) { - struct IR *ir = dev_get_drvdata(&sub->dev); + struct bttv_ir *ir = btv->remote; if (!ir->polling) - ir_handle_key(ir); + ir_handle_key(btv); } -static void ir_timer(unsigned long data) +static void bttv_input_timer(unsigned long data) { - struct IR *ir = (struct IR*)data; + struct bttv *btv = (struct bttv*)data; + struct bttv_ir *ir = btv->remote; + unsigned long timeout; - schedule_work(&ir->work); + ir_handle_key(btv); + timeout = jiffies + (ir->polling * HZ / 1000); + mod_timer(&ir->timer, timeout); } -static void ir_work(void *data) +/* ---------------------------------------------------------------*/ + +static int rc5_remote_gap = 885; +module_param(rc5_remote_gap, int, 0644); +static int rc5_key_timeout = 200; +module_param(rc5_key_timeout, int, 0644); + +#define RC5_START(x) (((x)>>12)&3) +#define RC5_TOGGLE(x) (((x)>>11)&1) +#define RC5_ADDR(x) (((x)>>6)&31) +#define RC5_INSTR(x) ((x)&63) + +/* decode raw bit pattern to RC5 code */ +static u32 rc5_decode(unsigned int code) { - struct IR *ir = data; - unsigned long timeout; + unsigned int org_code = code; + unsigned int pair; + unsigned int rc5 = 0; + int i; + + code = (code << 1) | 1; + for (i = 0; i < 14; ++i) { + pair = code & 0x3; + code >>= 2; + + rc5 <<= 1; + switch (pair) { + case 0: + case 2: + break; + case 1: + rc5 |= 1; + break; + case 3: + dprintk(KERN_WARNING "bad code: %x\n", org_code); + return 0; + } + } + dprintk(KERN_WARNING "code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " + "instr=%x\n", rc5, org_code, RC5_START(rc5), + RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); + return rc5; +} - ir_handle_key(ir); - timeout = jiffies + (ir->polling * HZ / 1000); - mod_timer(&ir->timer, timeout); +static int bttv_rc5_irq(struct bttv *btv) +{ + struct bttv_ir *ir = btv->remote; + struct timeval tv; + u32 gpio; + u32 gap; + unsigned long current_jiffies, timeout; + + /* read gpio port */ + gpio = bttv_gpio_read(&btv->c); + + /* remote IRQ? */ + if (!(gpio & 0x20)) + return 0; + + /* get time of bit */ + current_jiffies = jiffies; + do_gettimeofday(&tv); + + /* avoid overflow with gap >1s */ + if (tv.tv_sec - ir->base_time.tv_sec > 1) { + gap = 200000; + } else { + gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + + tv.tv_usec - ir->base_time.tv_usec; + } + + /* active code => add bit */ + if (ir->active) { + /* only if in the code (otherwise spurious IRQ or timer + late) */ + if (ir->last_bit < 28) { + ir->last_bit = (gap - rc5_remote_gap / 2) / + rc5_remote_gap; + ir->code |= 1 << ir->last_bit; + } + /* starting new code */ + } else { + ir->active = 1; + ir->code = 0; + ir->base_time = tv; + ir->last_bit = 0; + + timeout = current_jiffies + (500 + 30 * HZ) / 1000; + mod_timer(&ir->timer_end, timeout); + } + + /* toggle GPIO pin 4 to reset the irq */ + bttv_gpio_write(&btv->c, gpio & ~(1 << 4)); + bttv_gpio_write(&btv->c, gpio | (1 << 4)); + return 1; +} + + +static void bttv_rc5_timer_end(unsigned long data) +{ + struct bttv_ir *ir = (struct bttv_ir *)data; + struct timeval tv; + unsigned long current_jiffies, timeout; + u32 gap; + + /* get time */ + current_jiffies = jiffies; + do_gettimeofday(&tv); + + /* avoid overflow with gap >1s */ + if (tv.tv_sec - ir->base_time.tv_sec > 1) { + gap = 200000; + } else { + gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + + tv.tv_usec - ir->base_time.tv_usec; + } + + /* Allow some timmer jitter (RC5 is ~24ms anyway so this is ok) */ + if (gap < 28000) { + dprintk(KERN_WARNING "spurious timer_end\n"); + return; + } + + ir->active = 0; + if (ir->last_bit < 20) { + /* ignore spurious codes (caused by light/other remotes) */ + dprintk(KERN_WARNING "short code: %x\n", ir->code); + } else { + u32 rc5 = rc5_decode(ir->code); + + /* two start bits? */ + if (RC5_START(rc5) != 3) { + dprintk(KERN_WARNING "rc5 start bits invalid: %u\n", RC5_START(rc5)); + + /* right address? */ + } else if (RC5_ADDR(rc5) == 0x0) { + u32 toggle = RC5_TOGGLE(rc5); + u32 instr = RC5_INSTR(rc5); + + /* Good code, decide if repeat/repress */ + if (toggle != RC5_TOGGLE(ir->last_rc5) || + instr != RC5_INSTR(ir->last_rc5)) { + dprintk(KERN_WARNING "instruction %x, toggle %x\n", instr, + toggle); + ir_input_nokey(ir->dev, &ir->ir); + ir_input_keydown(ir->dev, &ir->ir, instr, + instr); + } + + /* Set/reset key-up timer */ + timeout = current_jiffies + (500 + rc5_key_timeout + * HZ) / 1000; + mod_timer(&ir->timer_keyup, timeout); + + /* Save code for repeat test */ + ir->last_rc5 = rc5; + } + } +} + +static void bttv_rc5_timer_keyup(unsigned long data) +{ + struct bttv_ir *ir = (struct bttv_ir *)data; + + dprintk(KERN_DEBUG "key released\n"); + ir_input_nokey(ir->dev, &ir->ir); } /* ---------------------------------------------------------------------- */ -static int ir_probe(struct device *dev) +int bttv_input_init(struct bttv *btv) { - struct bttv_sub_device *sub = to_bttv_sub_dev(dev); - struct IR *ir; - struct input_dev *input_dev; + struct bttv_ir *ir; IR_KEYTAB_TYPE *ir_codes = NULL; + struct input_dev *input_dev; int ir_type = IR_TYPE_OTHER; - ir = kzalloc(sizeof(*ir), GFP_KERNEL); + if (!btv->has_remote) + return -ENODEV; + + ir = kzalloc(sizeof(*ir),GFP_KERNEL); input_dev = input_allocate_device(); if (!ir || !input_dev) { kfree(ir); input_free_device(input_dev); return -ENOMEM; } + memset(ir,0,sizeof(*ir)); /* detect & configure */ - switch (sub->core->type) { + switch (btv->c.type) { case BTTV_BOARD_AVERMEDIA: case BTTV_BOARD_AVPHONE98: case BTTV_BOARD_AVERMEDIA98: @@ -400,85 +578,114 @@ static int ir_probe(struct device *dev) ir->mask_keyup = 0x006000; ir->polling = 50; // ms break; + case BTTV_BOARD_NEBULA_DIGITV: + ir_codes = ir_codes_nebula; + btv->custom_irq = bttv_rc5_irq; + ir->rc5_gpio = 1; + break; + case BTTV_BOARD_MACHTV_MAGICTV: + ir_codes = ir_codes_apac_viewcomp; + ir->mask_keycode = 0x001F00; + ir->mask_keyup = 0x004000; + ir->polling = 50; /* ms */ + break; } if (NULL == ir_codes) { + dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n",btv->c.type); kfree(ir); input_free_device(input_dev); return -ENODEV; } - /* init hardware-specific stuff */ - bttv_gpio_inout(sub->core, ir->mask_keycode | ir->mask_keydown, 0); - ir->sub = sub; + if (ir->rc5_gpio) { + u32 gpio; + /* enable remote irq */ + bttv_gpio_inout(&btv->c, (1 << 4), 1 << 4); + gpio = bttv_gpio_read(&btv->c); + bttv_gpio_write(&btv->c, gpio & ~(1 << 4)); + bttv_gpio_write(&btv->c, gpio | (1 << 4)); + } else { + /* init hardware-specific stuff */ + bttv_gpio_inout(&btv->c, ir->mask_keycode | ir->mask_keydown, 0); + } /* init input device */ + ir->dev = input_dev; + snprintf(ir->name, sizeof(ir->name), "bttv IR (card=%d)", - sub->core->type); + btv->c.type); snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", - pci_name(sub->core->pci)); + pci_name(btv->c.pci)); ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_PCI; input_dev->id.version = 1; - if (sub->core->pci->subsystem_vendor) { - input_dev->id.vendor = sub->core->pci->subsystem_vendor; - input_dev->id.product = sub->core->pci->subsystem_device; + if (btv->c.pci->subsystem_vendor) { + input_dev->id.vendor = btv->c.pci->subsystem_vendor; + input_dev->id.product = btv->c.pci->subsystem_device; } else { - input_dev->id.vendor = sub->core->pci->vendor; - input_dev->id.product = sub->core->pci->device; + input_dev->id.vendor = btv->c.pci->vendor; + input_dev->id.product = btv->c.pci->device; } - input_dev->cdev.dev = &sub->core->pci->dev; + input_dev->cdev.dev = &btv->c.pci->dev; + btv->remote = ir; if (ir->polling) { - INIT_WORK(&ir->work, ir_work, ir); init_timer(&ir->timer); - ir->timer.function = ir_timer; - ir->timer.data = (unsigned long)ir; - schedule_work(&ir->work); + ir->timer.function = bttv_input_timer; + ir->timer.data = (unsigned long)btv; + ir->timer.expires = jiffies + HZ; + add_timer(&ir->timer); + } else if (ir->rc5_gpio) { + /* set timer_end for code completion */ + init_timer(&ir->timer_end); + ir->timer_end.function = bttv_rc5_timer_end; + ir->timer_end.data = (unsigned long)ir; + + init_timer(&ir->timer_keyup); + ir->timer_keyup.function = bttv_rc5_timer_keyup; + ir->timer_keyup.data = (unsigned long)ir; } /* all done */ - dev_set_drvdata(dev, ir); - input_register_device(ir->input); + input_register_device(btv->remote->dev); + printk(DEVNAME ": %s detected at %s\n",ir->name,ir->phys); + + /* the remote isn't as bouncy as a keyboard */ + ir->dev->rep[REP_DELAY] = repeat_delay; + ir->dev->rep[REP_PERIOD] = repeat_period; return 0; } -static int ir_remove(struct device *dev) +void bttv_input_fini(struct bttv *btv) { - struct IR *ir = dev_get_drvdata(dev); + if (btv->remote == NULL) + return; - if (ir->polling) { - del_timer(&ir->timer); + if (btv->remote->polling) { + del_timer_sync(&btv->remote->timer); flush_scheduled_work(); } - input_unregister_device(ir->input); - kfree(ir); - return 0; -} -/* ---------------------------------------------------------------------- */ + if (btv->remote->rc5_gpio) { + u32 gpio; -MODULE_AUTHOR("Gerd Knorr, Pavel Machek"); -MODULE_DESCRIPTION("input driver for bt8x8 gpio IR remote controls"); -MODULE_LICENSE("GPL"); + del_timer_sync(&btv->remote->timer_end); + flush_scheduled_work(); -static int ir_init(void) -{ - return bttv_sub_register(&driver, "remote"); -} + gpio = bttv_gpio_read(&btv->c); + bttv_gpio_write(&btv->c, gpio & ~(1 << 4)); + } -static void ir_fini(void) -{ - bttv_sub_unregister(&driver); + input_unregister_device(btv->remote->dev); + kfree(btv->remote); + btv->remote = NULL; } -module_init(ir_init); -module_exit(ir_fini); - /* * Local variables: diff --git a/drivers/media/video/bttv-vbi.c b/drivers/media/video/bttv-vbi.c index f4f58c60f15..72afdd64b88 100644 --- a/drivers/media/video/bttv-vbi.c +++ b/drivers/media/video/bttv-vbi.c @@ -31,6 +31,12 @@ #include <asm/io.h> #include "bttvp.h" +/* Offset from line sync pulse leading edge (0H) in 1 / sampling_rate: + bt8x8 /HRESET pulse starts at 0H and has length 64 / fCLKx1 (E|O_VTC + HSFMT = 0). VBI_HDELAY (always 0) is an offset from the trailing edge + of /HRESET in 1 / fCLKx1, and the sampling_rate tvnorm->Fsc is fCLKx2. */ +#define VBI_OFFSET ((64 + 0) * 2) + #define VBI_DEFLINES 16 #define VBI_MAXLINES 32 @@ -163,40 +169,30 @@ void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines) void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f) { const struct bttv_tvnorm *tvnorm; - u32 start0,start1; - s32 count0,count1,count; + s64 count0,count1,count; tvnorm = &bttv_tvnorms[fh->btv->tvnorm]; f->type = V4L2_BUF_TYPE_VBI_CAPTURE; f->fmt.vbi.sampling_rate = tvnorm->Fsc; f->fmt.vbi.samples_per_line = 2048; f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - f->fmt.vbi.offset = 244; + f->fmt.vbi.offset = VBI_OFFSET; f->fmt.vbi.flags = 0; - switch (fh->btv->tvnorm) { - case 1: /* NTSC */ - start0 = 10; - start1 = 273; - break; - case 0: /* PAL */ - case 2: /* SECAM */ - default: - start0 = 7; - start1 = 320; - } - count0 = (f->fmt.vbi.start[0] + f->fmt.vbi.count[0]) - start0; - count1 = (f->fmt.vbi.start[1] + f->fmt.vbi.count[1]) - start1; - count = max(count0,count1); - if (count > VBI_MAXLINES) - count = VBI_MAXLINES; - if (count < 1) - count = 1; + /* s64 to prevent overflow. */ + count0 = (s64) f->fmt.vbi.start[0] + f->fmt.vbi.count[0] + - tvnorm->vbistart[0]; + count1 = (s64) f->fmt.vbi.start[1] + f->fmt.vbi.count[1] + - tvnorm->vbistart[1]; + count = clamp (max (count0, count1), 1LL, (s64) VBI_MAXLINES); - f->fmt.vbi.start[0] = start0; - f->fmt.vbi.start[1] = start1; + f->fmt.vbi.start[0] = tvnorm->vbistart[0]; + f->fmt.vbi.start[1] = tvnorm->vbistart[1]; f->fmt.vbi.count[0] = count; f->fmt.vbi.count[1] = count; + + f->fmt.vbi.reserved[0] = 0; + f->fmt.vbi.reserved[1] = 0; } void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f) @@ -209,21 +205,12 @@ void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f) f->fmt.vbi.sampling_rate = tvnorm->Fsc; f->fmt.vbi.samples_per_line = 2048; f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - f->fmt.vbi.offset = 244; + f->fmt.vbi.offset = VBI_OFFSET; + f->fmt.vbi.start[0] = tvnorm->vbistart[0]; + f->fmt.vbi.start[1] = tvnorm->vbistart[1]; f->fmt.vbi.count[0] = fh->lines; f->fmt.vbi.count[1] = fh->lines; f->fmt.vbi.flags = 0; - switch (fh->btv->tvnorm) { - case 1: /* NTSC */ - f->fmt.vbi.start[0] = 10; - f->fmt.vbi.start[1] = 273; - break; - case 0: /* PAL */ - case 2: /* SECAM */ - default: - f->fmt.vbi.start[0] = 7; - f->fmt.vbi.start[1] = 319; - } } /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/bttv.h b/drivers/media/video/bttv.h index 124ea41dada..9908c8e0c95 100644 --- a/drivers/media/video/bttv.h +++ b/drivers/media/video/bttv.h @@ -16,6 +16,8 @@ #include <linux/videodev.h> #include <linux/i2c.h> +#include <media/ir-common.h> +#include <media/ir-kbd-i2c.h> /* ---------------------------------------------------------- */ /* exported by bttv-cards.c */ @@ -162,6 +164,9 @@ #define BTTV_BOARD_PV_M4900 0x8b #define BTTV_BOARD_OSPREY440 0x8c #define BTTV_BOARD_ASOUND_SKYEYE 0x8d +#define BTTV_BOARD_SABRENT_TVFM 0x8e +#define BTTV_BOARD_HAUPPAUGE_IMPACTVCB 0x8f +#define BTTV_BOARD_MACHTV_MAGICTV 0x90 /* i2c address list */ #define I2C_TSA5522 0xc2 @@ -209,6 +214,34 @@ struct bttv_core { struct bttv; + +struct bttv_ir { + struct input_dev *dev; + struct ir_input_state ir; + char name[32]; + char phys[32]; + + /* Usual gpio signalling */ + + u32 mask_keycode; + u32 mask_keydown; + u32 mask_keyup; + u32 polling; + u32 last_gpio; + struct work_struct work; + struct timer_list timer; + + /* RC5 gpio */ + u32 rc5_gpio; + struct timer_list timer_end; /* timer_end for code completion */ + struct timer_list timer_keyup; /* timer_end for key release */ + u32 last_rc5; /* last good rc5 code */ + u32 last_bit; /* last raw bit seen */ + u32 code; /* raw code under construction */ + struct timeval base_time; /* time of last seen code */ + int active; /* building raw code */ +}; + struct tvcard { char *name; @@ -332,6 +365,8 @@ struct bttv_sub_device { struct bttv_sub_driver { struct device_driver drv; char wanted[BUS_ID_SIZE]; + int (*probe)(struct bttv_sub_device *sub); + void (*remove)(struct bttv_sub_device *sub); void (*gpio_irq)(struct bttv_sub_device *sub); }; #define to_bttv_sub_drv(x) container_of((x), struct bttv_sub_driver, drv) @@ -360,6 +395,10 @@ extern int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, unsigned char b2, int both); extern void bttv_readee(struct bttv *btv, unsigned char *eedata, int addr); +extern int bttv_input_init(struct bttv *dev); +extern void bttv_input_fini(struct bttv *dev); +extern void bttv_input_irq(struct bttv *dev); + #endif /* _BTTV_H_ */ /* * Local variables: diff --git a/drivers/media/video/bttvp.h b/drivers/media/video/bttvp.h index 386f546f7d1..9cb72f176f7 100644 --- a/drivers/media/video/bttvp.h +++ b/drivers/media/video/bttvp.h @@ -35,6 +35,7 @@ #include <linux/videodev.h> #include <linux/pci.h> #include <linux/input.h> +#include <linux/mutex.h> #include <asm/scatterlist.h> #include <asm/io.h> @@ -45,6 +46,7 @@ #include <media/tveeprom.h> #include <media/ir-common.h> + #include "bt848.h" #include "bttv.h" #include "btcx-risc.h" @@ -72,6 +74,8 @@ #define UNSET (-1U) +#define clamp(x, low, high) min (max (low, x), high) + /* ---------------------------------------------------------- */ struct bttv_tvnorm { @@ -87,6 +91,9 @@ struct bttv_tvnorm { u8 vbipack; u16 vtotal; int sram; + /* ITU-R frame line number of the first VBI line we can + capture, of the first and second field. */ + u16 vbistart[2]; }; extern const struct bttv_tvnorm bttv_tvnorms[]; @@ -268,11 +275,13 @@ struct bttv { /* card configuration info */ unsigned int cardid; /* pci subsystem id (bt878 based ones) */ unsigned int tuner_type; /* tuner chip type */ - unsigned int pinnacle_id; + unsigned int tda9887_conf; unsigned int svhs; struct bttv_pll_info pll; int triton1; int gpioirq; + int (*custom_irq)(struct bttv *btv); + int use_i2c_hw; /* old gpio interface */ @@ -297,13 +306,13 @@ struct bttv { /* infrared remote */ int has_remote; - struct bttv_input *remote; + struct bttv_ir *remote; /* locking */ spinlock_t s_lock; - struct semaphore lock; + struct mutex lock; int resources; - struct semaphore reslock; + struct mutex reslock; #ifdef VIDIOC_G_PRIORITY struct v4l2_prio_state prio; #endif diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index 0065d0c240d..6bad93ef969 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -875,6 +875,7 @@ static struct file_operations qcam_fops = { .open = video_exclusive_open, .release = video_exclusive_release, .ioctl = qcam_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .read = qcam_read, .llseek = no_llseek, }; diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index 75442ec49f3..9976db4f6da 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -687,6 +687,7 @@ static struct file_operations qcam_fops = { .open = video_exclusive_open, .release = video_exclusive_release, .ioctl = qcam_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .read = qcam_read, .llseek = no_llseek, }; diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/compat_ioctl32.c new file mode 100644 index 00000000000..297c32ab51e --- /dev/null +++ b/drivers/media/video/compat_ioctl32.c @@ -0,0 +1,879 @@ +/* + * ioctl32.c: Conversion between 32bit and 64bit native ioctls. + * Separated from fs stuff by Arnd Bergmann <arnd@arndb.de> + * + * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs + * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) + * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be) + * + * These routines maintain argument size conversion between 32bit and 64bit + * ioctls. + */ + +#include <linux/config.h> +#include <linux/compat.h> +#include <linux/videodev.h> +#include <linux/videodev2.h> +#include <linux/module.h> +#include <linux/smp_lock.h> +#include <media/v4l2-common.h> + +#ifdef CONFIG_COMPAT + + +struct video_tuner32 { + compat_int_t tuner; + char name[32]; + compat_ulong_t rangelow, rangehigh; + u32 flags; /* It is really u32 in videodev.h */ + u16 mode, signal; +}; + +static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up) +{ + if(!access_ok(VERIFY_READ, up, sizeof(struct video_tuner32)) || + get_user(kp->tuner, &up->tuner) || + copy_from_user(kp->name, up->name, 32) || + get_user(kp->rangelow, &up->rangelow) || + get_user(kp->rangehigh, &up->rangehigh) || + get_user(kp->flags, &up->flags) || + get_user(kp->mode, &up->mode) || + get_user(kp->signal, &up->signal)) + return -EFAULT; + return 0; +} + +static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user *up) +{ + if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_tuner32)) || + put_user(kp->tuner, &up->tuner) || + copy_to_user(up->name, kp->name, 32) || + put_user(kp->rangelow, &up->rangelow) || + put_user(kp->rangehigh, &up->rangehigh) || + put_user(kp->flags, &up->flags) || + put_user(kp->mode, &up->mode) || + put_user(kp->signal, &up->signal)) + return -EFAULT; + return 0; +} + +struct video_buffer32 { + compat_caddr_t base; + compat_int_t height, width, depth, bytesperline; +}; + +static int get_video_buffer32(struct video_buffer *kp, struct video_buffer32 __user *up) +{ + u32 tmp; + + if (!access_ok(VERIFY_READ, up, sizeof(struct video_buffer32)) || + get_user(tmp, &up->base) || + get_user(kp->height, &up->height) || + get_user(kp->width, &up->width) || + get_user(kp->depth, &up->depth) || + get_user(kp->bytesperline, &up->bytesperline)) + return -EFAULT; + + /* This is actually a physical address stored + * as a void pointer. + */ + kp->base = (void *)(unsigned long) tmp; + + return 0; +} + +static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 __user *up) +{ + u32 tmp = (u32)((unsigned long)kp->base); + + if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_buffer32)) || + put_user(tmp, &up->base) || + put_user(kp->height, &up->height) || + put_user(kp->width, &up->width) || + put_user(kp->depth, &up->depth) || + put_user(kp->bytesperline, &up->bytesperline)) + return -EFAULT; + return 0; +} + +struct video_clip32 { + s32 x, y, width, height; /* Its really s32 in videodev.h */ + compat_caddr_t next; +}; + +struct video_window32 { + u32 x, y, width, height, chromakey, flags; + compat_caddr_t clips; + compat_int_t clipcount; +}; + +static int native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = -ENOIOCTLCMD; + + if (file->f_op->unlocked_ioctl) + ret = file->f_op->unlocked_ioctl(file, cmd, arg); + else if (file->f_op->ioctl) { + lock_kernel(); + ret = file->f_op->ioctl(file->f_dentry->d_inode, file, cmd, arg); + unlock_kernel(); + } + + return ret; +} + + +/* You get back everything except the clips... */ +static int put_video_window32(struct video_window *kp, struct video_window32 __user *up) +{ + if(!access_ok(VERIFY_WRITE, up, sizeof(struct video_window32)) || + put_user(kp->x, &up->x) || + put_user(kp->y, &up->y) || + put_user(kp->width, &up->width) || + put_user(kp->height, &up->height) || + put_user(kp->chromakey, &up->chromakey) || + put_user(kp->flags, &up->flags) || + put_user(kp->clipcount, &up->clipcount)) + return -EFAULT; + return 0; +} + +struct v4l2_clip32 +{ + struct v4l2_rect c; + compat_caddr_t next; +}; + +struct v4l2_window32 +{ + struct v4l2_rect w; + enum v4l2_field field; + __u32 chromakey; + compat_caddr_t clips; /* actually struct v4l2_clip32 * */ + __u32 clipcount; + compat_caddr_t bitmap; +}; + +static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) || + copy_from_user(&kp->w, &up->w, sizeof(up->w)) || + get_user(kp->field, &up->field) || + get_user(kp->chromakey, &up->chromakey) || + get_user(kp->clipcount, &up->clipcount)) + return -EFAULT; + if (kp->clipcount > 2048) + return -EINVAL; + if (kp->clipcount) { + struct v4l2_clip32 *uclips = compat_ptr(up->clips); + struct v4l2_clip *kclips; + int n = kp->clipcount; + + kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip)); + kp->clips = kclips; + while (--n >= 0) { + if (!access_ok(VERIFY_READ, &uclips->c, sizeof(uclips->c)) || + copy_from_user(&kclips->c, &uclips->c, sizeof(uclips->c))) + return -EFAULT; + kclips->next = n ? kclips + 1 : 0; + uclips += 1; + kclips += 1; + } + } else + kp->clips = 0; + return 0; +} + +static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_window32)) || + copy_to_user(&up->w, &kp->w, sizeof(up->w)) || + put_user(kp->field, &up->field) || + put_user(kp->chromakey, &up->chromakey) || + put_user(kp->clipcount, &up->clipcount)) + return -EFAULT; + return 0; +} + +static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_pix_format)) || + copy_from_user(kp, up, sizeof(struct v4l2_pix_format))) + return -EFAULT; + return 0; +} + +static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_pix_format)) || + copy_to_user(up, kp, sizeof(struct v4l2_pix_format))) + return -EFAULT; + return 0; +} + +static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_vbi_format)) || + copy_from_user(kp, up, sizeof(struct v4l2_vbi_format))) + return -EFAULT; + return 0; +} + +static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_vbi_format)) || + copy_to_user(up, kp, sizeof(struct v4l2_vbi_format))) + return -EFAULT; + return 0; +} + +struct v4l2_format32 +{ + enum v4l2_buf_type type; + union + { + struct v4l2_pix_format pix; // V4L2_BUF_TYPE_VIDEO_CAPTURE + struct v4l2_window32 win; // V4L2_BUF_TYPE_VIDEO_OVERLAY + struct v4l2_vbi_format vbi; // V4L2_BUF_TYPE_VBI_CAPTURE + __u8 raw_data[200]; // user-defined + } fmt; +}; + +static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) || + get_user(kp->type, &up->type)) + return -EFAULT; + switch (kp->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return get_v4l2_window32(&kp->fmt.win, &up->fmt.win); + case V4L2_BUF_TYPE_VBI_CAPTURE: + return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); + default: + printk("compat_ioctl : unexpected VIDIOC_FMT type %d\n", + kp->type); + return -ENXIO; + } +} + +static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +{ + if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) || + put_user(kp->type, &up->type)) + return -EFAULT; + switch (kp->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return put_v4l2_window32(&kp->fmt.win, &up->fmt.win); + case V4L2_BUF_TYPE_VBI_CAPTURE: + return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); + default: + return -ENXIO; + } +} + +static inline int get_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard)) || + copy_from_user(kp, up, sizeof(struct v4l2_standard))) + return -EFAULT; + return 0; + +} + +static inline int put_v4l2_standard(struct v4l2_standard *kp, struct v4l2_standard __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard)) || + copy_to_user(up, kp, sizeof(struct v4l2_standard))) + return -EFAULT; + return 0; +} + +struct v4l2_standard32 +{ + __u32 index; + __u32 id[2]; /* __u64 would get the alignment wrong */ + __u8 name[24]; + struct v4l2_fract frameperiod; /* Frames, not fields */ + __u32 framelines; + __u32 reserved[4]; +}; + +static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up) +{ + /* other fields are not set by the user, nor used by the driver */ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) || + get_user(kp->index, &up->index)) + return -EFAULT; + return 0; +} + +static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up) +{ + if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) || + put_user(kp->index, &up->index) || + copy_to_user(up->id, &kp->id, sizeof(__u64)) || + copy_to_user(up->name, kp->name, 24) || + copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) || + put_user(kp->framelines, &up->framelines) || + copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32))) + return -EFAULT; + return 0; +} + +static inline int get_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_tuner)) || + copy_from_user(kp, up, sizeof(struct v4l2_tuner))) + return -EFAULT; + return 0; + +} + +static inline int put_v4l2_tuner(struct v4l2_tuner *kp, struct v4l2_tuner __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_tuner)) || + copy_to_user(up, kp, sizeof(struct v4l2_tuner))) + return -EFAULT; + return 0; +} + +struct v4l2_buffer32 +{ + __u32 index; + enum v4l2_buf_type type; + __u32 bytesused; + __u32 flags; + enum v4l2_field field; + struct compat_timeval timestamp; + struct v4l2_timecode timecode; + __u32 sequence; + + /* memory location */ + enum v4l2_memory memory; + union { + __u32 offset; + compat_long_t userptr; + } m; + __u32 length; + __u32 input; + __u32 reserved; +}; + +static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up) +{ + + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) || + get_user(kp->index, &up->index) || + get_user(kp->type, &up->type) || + get_user(kp->flags, &up->flags) || + get_user(kp->memory, &up->memory) || + get_user(kp->input, &up->input)) + return -EFAULT; + switch(kp->memory) { + case V4L2_MEMORY_MMAP: + break; + case V4L2_MEMORY_USERPTR: + { + unsigned long tmp = (unsigned long)compat_ptr(up->m.userptr); + + if(get_user(kp->length, &up->length) || + get_user(kp->m.userptr, &tmp)) + return -EFAULT; + } + break; + case V4L2_MEMORY_OVERLAY: + if(get_user(kp->m.offset, &up->m.offset)) + return -EFAULT; + break; + } + return 0; +} + +static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) || + put_user(kp->index, &up->index) || + put_user(kp->type, &up->type) || + put_user(kp->flags, &up->flags) || + put_user(kp->memory, &up->memory) || + put_user(kp->input, &up->input)) + return -EFAULT; + switch(kp->memory) { + case V4L2_MEMORY_MMAP: + if (put_user(kp->length, &up->length) || + put_user(kp->m.offset, &up->m.offset)) + return -EFAULT; + break; + case V4L2_MEMORY_USERPTR: + if (put_user(kp->length, &up->length) || + put_user(kp->m.userptr, &up->m.userptr)) + return -EFAULT; + break; + case V4L2_MEMORY_OVERLAY: + if (put_user(kp->m.offset, &up->m.offset)) + return -EFAULT; + break; + } + if (put_user(kp->bytesused, &up->bytesused) || + put_user(kp->field, &up->field) || + put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) || + put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) || + copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) || + put_user(kp->sequence, &up->sequence) || + put_user(kp->reserved, &up->reserved)) + return -EFAULT; + return 0; +} + +struct v4l2_framebuffer32 +{ + __u32 capability; + __u32 flags; + compat_caddr_t base; + struct v4l2_pix_format fmt; +}; + +static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up) +{ + u32 tmp; + + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) || + get_user(tmp, &up->base) || + get_user(kp->capability, &up->capability) || + get_user(kp->flags, &up->flags)) + return -EFAULT; + kp->base = compat_ptr(tmp); + get_v4l2_pix_format(&kp->fmt, &up->fmt); + return 0; +} + +static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up) +{ + u32 tmp = (u32)((unsigned long)kp->base); + + if(!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) || + put_user(tmp, &up->base) || + put_user(kp->capability, &up->capability) || + put_user(kp->flags, &up->flags)) + return -EFAULT; + put_v4l2_pix_format(&kp->fmt, &up->fmt); + return 0; +} + +static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_input) - 4) || + copy_from_user(kp, up, sizeof(struct v4l2_input) - 4)) + return -EFAULT; + return 0; +} + +static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_input) - 4) || + copy_to_user(up, kp, sizeof(struct v4l2_input) - 4)) + return -EFAULT; + return 0; +} + +static inline int get_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_input)) || + copy_from_user(kp, up, sizeof(struct v4l2_input))) + return -EFAULT; + return 0; +} + +static inline int put_v4l2_input(struct v4l2_input *kp, struct v4l2_input __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_input)) || + copy_to_user(up, kp, sizeof(struct v4l2_input))) + return -EFAULT; + return 0; +} + +#define VIDIOCGTUNER32 _IOWR('v',4, struct video_tuner32) +#define VIDIOCSTUNER32 _IOW('v',5, struct video_tuner32) +#define VIDIOCGWIN32 _IOR('v',9, struct video_window32) +#define VIDIOCSWIN32 _IOW('v',10, struct video_window32) +#define VIDIOCGFBUF32 _IOR('v',11, struct video_buffer32) +#define VIDIOCSFBUF32 _IOW('v',12, struct video_buffer32) +#define VIDIOCGFREQ32 _IOR('v',14, u32) +#define VIDIOCSFREQ32 _IOW('v',15, u32) + +/* VIDIOC_ENUMINPUT32 is VIDIOC_ENUMINPUT minus 4 bytes of padding alignement */ +#define VIDIOC_ENUMINPUT32 VIDIOC_ENUMINPUT - _IOC(0, 0, 0, 4) +#define VIDIOC_G_FMT32 _IOWR ('V', 4, struct v4l2_format32) +#define VIDIOC_S_FMT32 _IOWR ('V', 5, struct v4l2_format32) +#define VIDIOC_QUERYBUF32 _IOWR ('V', 9, struct v4l2_buffer32) +#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32) +#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32) +/* VIDIOC_OVERLAY is now _IOW, but was _IOWR */ +#define VIDIOC_OVERLAY32 _IOWR ('V', 14, compat_int_t) +#define VIDIOC_QBUF32 _IOWR ('V', 15, struct v4l2_buffer32) +#define VIDIOC_DQBUF32 _IOWR ('V', 17, struct v4l2_buffer32) +#define VIDIOC_STREAMON32 _IOW ('V', 18, compat_int_t) +#define VIDIOC_STREAMOFF32 _IOW ('V', 19, compat_int_t) +#define VIDIOC_ENUMSTD32 _IOWR ('V', 25, struct v4l2_standard32) +/* VIDIOC_S_CTRL is now _IOWR, but was _IOW */ +#define VIDIOC_S_CTRL32 _IOW ('V', 28, struct v4l2_control) +#define VIDIOC_G_INPUT32 _IOR ('V', 38, compat_int_t) +#define VIDIOC_S_INPUT32 _IOWR ('V', 39, compat_int_t) +#define VIDIOC_TRY_FMT32 _IOWR ('V', 64, struct v4l2_format32) + +enum { + MaxClips = (~0U-sizeof(struct video_window))/sizeof(struct video_clip) +}; + +static int do_set_window(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct video_window32 __user *up = compat_ptr(arg); + struct video_window __user *vw; + struct video_clip __user *p; + int nclips; + u32 n; + + if (!access_ok(VERIFY_READ, up, sizeof(struct video_window32))) + return -EFAULT; + + if (get_user(nclips, &up->clipcount)) + return -EFAULT; + + /* Peculiar interface... */ + if (nclips < 0) + nclips = VIDEO_CLIPMAP_SIZE; + + if (nclips > MaxClips) + return -ENOMEM; + + vw = compat_alloc_user_space(sizeof(struct video_window) + + nclips * sizeof(struct video_clip)); + + p = nclips ? (struct video_clip __user *)(vw + 1) : NULL; + + if (get_user(n, &up->x) || put_user(n, &vw->x) || + get_user(n, &up->y) || put_user(n, &vw->y) || + get_user(n, &up->width) || put_user(n, &vw->width) || + get_user(n, &up->height) || put_user(n, &vw->height) || + get_user(n, &up->chromakey) || put_user(n, &vw->chromakey) || + get_user(n, &up->flags) || put_user(n, &vw->flags) || + get_user(n, &up->clipcount) || put_user(n, &vw->clipcount) || + get_user(n, &up->clips) || put_user(p, &vw->clips)) + return -EFAULT; + + if (nclips) { + struct video_clip32 __user *u = compat_ptr(n); + int i; + if (!u) + return -EINVAL; + for (i = 0; i < nclips; i++, u++, p++) { + s32 v; + if (!access_ok(VERIFY_READ, u, sizeof(struct video_clip32)) || + !access_ok(VERIFY_WRITE, p, sizeof(struct video_clip32)) || + get_user(v, &u->x) || + put_user(v, &p->x) || + get_user(v, &u->y) || + put_user(v, &p->y) || + get_user(v, &u->width) || + put_user(v, &p->width) || + get_user(v, &u->height) || + put_user(v, &p->height) || + put_user(NULL, &p->next)) + return -EFAULT; + } + } + + return native_ioctl(file, VIDIOCSWIN, (unsigned long)vw); +} + +static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + union { + struct video_tuner vt; + struct video_buffer vb; + struct video_window vw; + struct v4l2_format v2f; + struct v4l2_buffer v2b; + struct v4l2_framebuffer v2fb; + struct v4l2_standard v2s; + struct v4l2_input v2i; + struct v4l2_tuner v2t; + unsigned long vx; + } karg; + void __user *up = compat_ptr(arg); + int compatible_arg = 1; + int err = 0; + int realcmd = cmd; + + /* First, convert the command. */ + switch(cmd) { + case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break; + case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break; + case VIDIOCGWIN32: cmd = VIDIOCGWIN; break; + case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break; + case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; + case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; + case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; + case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; + case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break; + case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break; + case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break; + case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break; + case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; + case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; + case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break; + case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break; + case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; + case VIDIOC_ENUMSTD32: realcmd = VIDIOC_ENUMSTD; break; + case VIDIOC_ENUMINPUT32: realcmd = VIDIOC_ENUMINPUT; break; + case VIDIOC_S_CTRL32: cmd = VIDIOC_S_CTRL; break; + case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; + case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; + case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break; + }; + + switch(cmd) { + case VIDIOCSTUNER: + case VIDIOCGTUNER: + err = get_video_tuner32(&karg.vt, up); + compatible_arg = 0; + + break; + + case VIDIOCSFBUF: + err = get_video_buffer32(&karg.vb, up); + compatible_arg = 0; + break; + + case VIDIOCSFREQ: + case VIDIOC_S_INPUT: + case VIDIOC_OVERLAY: + case VIDIOC_STREAMON: + case VIDIOC_STREAMOFF: + err = get_user(karg.vx, (u32 __user *)up); + compatible_arg = 0; + break; + + case VIDIOC_S_FBUF: + err = get_v4l2_framebuffer32(&karg.v2fb, up); + compatible_arg = 0; + break; + + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + err = get_v4l2_format32(&karg.v2f, up); + compatible_arg = 0; + break; + + case VIDIOC_QUERYBUF: + case VIDIOC_QBUF: + case VIDIOC_DQBUF: + err = get_v4l2_buffer32(&karg.v2b, up); + compatible_arg = 0; + break; + + case VIDIOC_ENUMSTD: + err = get_v4l2_standard(&karg.v2s, up); + compatible_arg = 0; + break; + + case VIDIOC_ENUMSTD32: + err = get_v4l2_standard32(&karg.v2s, up); + compatible_arg = 0; + break; + + case VIDIOC_ENUMINPUT: + err = get_v4l2_input(&karg.v2i, up); + compatible_arg = 0; + break; + + case VIDIOC_ENUMINPUT32: + err = get_v4l2_input32(&karg.v2i, up); + compatible_arg = 0; + break; + + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + err = get_v4l2_tuner(&karg.v2t, up); + compatible_arg = 0; + break; + + case VIDIOCGWIN: + case VIDIOCGFBUF: + case VIDIOCGFREQ: + case VIDIOC_G_FBUF: + case VIDIOC_G_INPUT: + compatible_arg = 0; + }; + + if(err) + goto out; + + if(compatible_arg) + err = native_ioctl(file, realcmd, (unsigned long)up); + else { + mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + err = native_ioctl(file, realcmd, (unsigned long)&karg); + set_fs(old_fs); + } + if(err == 0) { + switch(cmd) { + case VIDIOCGTUNER: + err = put_video_tuner32(&karg.vt, up); + break; + + case VIDIOCGWIN: + err = put_video_window32(&karg.vw, up); + break; + + case VIDIOCGFBUF: + err = put_video_buffer32(&karg.vb, up); + break; + + case VIDIOC_G_FBUF: + err = put_v4l2_framebuffer32(&karg.v2fb, up); + break; + + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + err = put_v4l2_format32(&karg.v2f, up); + break; + + case VIDIOC_QUERYBUF: + case VIDIOC_QBUF: + case VIDIOC_DQBUF: + err = put_v4l2_buffer32(&karg.v2b, up); + break; + + case VIDIOC_ENUMSTD: + err = put_v4l2_standard(&karg.v2s, up); + break; + + case VIDIOC_ENUMSTD32: + err = put_v4l2_standard32(&karg.v2s, up); + break; + + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + err = put_v4l2_tuner(&karg.v2t, up); + break; + + case VIDIOC_ENUMINPUT: + err = put_v4l2_input(&karg.v2i, up); + break; + + case VIDIOC_ENUMINPUT32: + err = put_v4l2_input32(&karg.v2i, up); + break; + + case VIDIOCGFREQ: + case VIDIOC_G_INPUT: + err = put_user(((u32)karg.vx), (u32 __user *)up); + break; + }; + } +out: + return err; +} + +long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = -ENOIOCTLCMD; + + if (!file->f_op->ioctl) + return ret; + + switch (cmd) { + case VIDIOCSWIN32: + ret = do_set_window(file, cmd, arg); + break; + case VIDIOCGTUNER32: + case VIDIOCSTUNER32: + case VIDIOCGWIN32: + case VIDIOCGFBUF32: + case VIDIOCSFBUF32: + case VIDIOCGFREQ32: + case VIDIOCSFREQ32: + case VIDIOC_QUERYCAP: + case VIDIOC_ENUM_FMT: + case VIDIOC_G_FMT32: + case VIDIOC_S_FMT32: + case VIDIOC_REQBUFS: + case VIDIOC_QUERYBUF32: + case VIDIOC_G_FBUF32: + case VIDIOC_S_FBUF32: + case VIDIOC_OVERLAY32: + case VIDIOC_QBUF32: + case VIDIOC_DQBUF32: + case VIDIOC_STREAMON32: + case VIDIOC_STREAMOFF32: + case VIDIOC_G_PARM: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_ENUMSTD: + case VIDIOC_ENUMSTD32: + case VIDIOC_ENUMINPUT: + case VIDIOC_ENUMINPUT32: + case VIDIOC_G_CTRL: + case VIDIOC_S_CTRL32: + case VIDIOC_QUERYCTRL: + case VIDIOC_G_INPUT32: + case VIDIOC_S_INPUT32: + case VIDIOC_TRY_FMT32: + ret = do_video_ioctl(file, cmd, arg); + break; + + /* Little v, the video4linux ioctls (conflict?) */ + case VIDIOCGCAP: + case VIDIOCGCHAN: + case VIDIOCSCHAN: + case VIDIOCGPICT: + case VIDIOCSPICT: + case VIDIOCCAPTURE: + case VIDIOCKEY: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + case VIDIOCSYNC: + case VIDIOCMCAPTURE: + case VIDIOCGMBUF: + case VIDIOCGUNIT: + case VIDIOCGCAPTURE: + case VIDIOCSCAPTURE: + + /* BTTV specific... */ + case _IOW('v', BASE_VIDIOCPRIVATE+0, char [256]): + case _IOR('v', BASE_VIDIOCPRIVATE+1, char [256]): + case _IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int): + case _IOW('v' , BASE_VIDIOCPRIVATE+3, char [16]): /* struct bttv_pll_info */ + case _IOR('v' , BASE_VIDIOCPRIVATE+4, int): + case _IOR('v' , BASE_VIDIOCPRIVATE+5, int): + case _IOR('v' , BASE_VIDIOCPRIVATE+6, int): + case _IOR('v' , BASE_VIDIOCPRIVATE+7, int): + ret = native_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); + break; + default: + v4l_print_ioctl("compat_ioctl32", cmd); + } + return ret; +} +#else +long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} +#endif +EXPORT_SYMBOL_GPL(v4l_compat_ioctl32); + +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index b7ec9bf4508..9f59541155d 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -3807,6 +3807,7 @@ static struct file_operations cpia_fops = { .read = cpia_read, .mmap = cpia_mmap, .ioctl = cpia_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; diff --git a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c index ddf184f95d8..74cff626e04 100644 --- a/drivers/media/video/cpia_pp.c +++ b/drivers/media/video/cpia_pp.c @@ -170,16 +170,9 @@ static size_t cpia_read_nibble (struct parport *port, /* Does the error line indicate end of data? */ if (((i /*& 1*/) == 0) && (parport_read_status(port) & PARPORT_STATUS_ERROR)) { - port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DNA; - DBG("%s: No more nibble data (%d bytes)\n", - port->name, i/2); - - /* Go to reverse idle phase. */ - parport_frob_control (port, - PARPORT_CONTROL_AUTOFD, - PARPORT_CONTROL_AUTOFD); - port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE; - break; + DBG("%s: No more nibble data (%d bytes)\n", + port->name, i/2); + goto end_of_data; } /* Event 7: Set nAutoFd low. */ @@ -227,18 +220,21 @@ static size_t cpia_read_nibble (struct parport *port, byte = nibble; } - i /= 2; /* i is now in bytes */ - if (i == len) { /* Read the last nibble without checking data avail. */ - port = port->physport; - if (parport_read_status (port) & PARPORT_STATUS_ERROR) - port->ieee1284.phase = IEEE1284_PH_HBUSY_DNA; + if (parport_read_status (port) & PARPORT_STATUS_ERROR) { + end_of_data: + /* Go to reverse idle phase. */ + parport_frob_control (port, + PARPORT_CONTROL_AUTOFD, + PARPORT_CONTROL_AUTOFD); + port->physport->ieee1284.phase = IEEE1284_PH_REV_IDLE; + } else - port->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL; + port->physport->ieee1284.phase = IEEE1284_PH_HBUSY_DAVAIL; } - return i; + return i/2; } /* CPiA nonstandard "Nibble Stream" mode (2 nibbles per cycle, instead of 1) @@ -706,12 +702,11 @@ static int cpia_pp_register(struct parport *port) return -ENXIO; } - cam = kmalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); + cam = kzalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); if (cam == NULL) { LOG("failed to allocate camera structure\n"); return -ENOMEM; } - memset(cam,0,sizeof(struct pp_cam_entry)); pdev = parport_register_device(port, "cpia_pp", NULL, NULL, NULL, 0, cam); diff --git a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c index 9774e94d1e7..03275c37c5d 100644 --- a/drivers/media/video/cpia_usb.c +++ b/drivers/media/video/cpia_usb.c @@ -499,14 +499,12 @@ static int cpia_probe(struct usb_interface *intf, printk(KERN_INFO "USB CPiA camera found\n"); - ucpia = kmalloc(sizeof(*ucpia), GFP_KERNEL); + ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL); if (!ucpia) { printk(KERN_ERR "couldn't kmalloc cpia struct\n"); return -ENOMEM; } - memset(ucpia, 0, sizeof(*ucpia)); - ucpia->dev = udev; ucpia->iface = interface->desc.bInterfaceNumber; init_waitqueue_head(&ucpia->wq_stream); @@ -582,7 +580,6 @@ MODULE_LICENSE("GPL"); static struct usb_driver cpia_driver = { - .owner = THIS_MODULE, .name = "cpia", .probe = cpia_probe, .disconnect = cpia_disconnect, diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index 780b352ec11..8739c64785e 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -27,7 +27,7 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev.h> -#include <media/audiochip.h> +#include <media/v4l2-common.h> MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); MODULE_AUTHOR("Martin Vaughan"); @@ -39,20 +39,6 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging messages\n\t\t\t0=Off (default), 1=On"); -#define cs53l32a_dbg(fmt, arg...) \ - do { \ - if (debug) \ - printk(KERN_INFO "%s debug %d-%04x: " fmt, client->driver->name, \ - i2c_adapter_id(client->adapter), client->addr , ## arg); \ - } while (0) - -#define cs53l32a_err(fmt, arg...) do { \ - printk(KERN_ERR "%s %d-%04x: " fmt, client->driver->name, \ - i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0) -#define cs53l32a_info(fmt, arg...) do { \ - printk(KERN_INFO "%s %d-%04x: " fmt, client->driver->name, \ - i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0) - static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; @@ -73,50 +59,59 @@ static int cs53l32a_read(struct i2c_client *client, u8 reg) static int cs53l32a_command(struct i2c_client *client, unsigned int cmd, void *arg) { - int *input = arg; + struct v4l2_audio *input = arg; + struct v4l2_control *ctrl = arg; switch (cmd) { - case AUDC_SET_INPUT: - switch (*input) { - case AUDIO_TUNER: - cs53l32a_write(client, 0x01, 0x01); - break; - case AUDIO_EXTERN: - cs53l32a_write(client, 0x01, 0x21); - break; - case AUDIO_MUTE: - cs53l32a_write(client, 0x03, 0xF0); - break; - case AUDIO_UNMUTE: - cs53l32a_write(client, 0x03, 0x30); - break; - default: - cs53l32a_err("Invalid input %d.\n", *input); + case VIDIOC_S_AUDIO: + /* There are 2 physical inputs, but the second input can be + placed in two modes, the first mode bypasses the PGA (gain), + the second goes through the PGA. Hence there are three + possible inputs to choose from. */ + if (input->index > 2) { + v4l_err(client, "Invalid input %d.\n", input->index); return -EINVAL; } + cs53l32a_write(client, 0x01, 0x01 + (input->index << 4)); + break; + + case VIDIOC_G_AUDIO: + memset(input, 0, sizeof(*input)); + input->index = (cs53l32a_read(client, 0x01) >> 4) & 3; + break; + + case VIDIOC_G_CTRL: + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + ctrl->value = (cs53l32a_read(client, 0x03) & 0xc0) != 0; + break; + } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) + return -EINVAL; + ctrl->value = (s8)cs53l32a_read(client, 0x04); break; case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - if (ctrl->value > 12 || ctrl->value < -90) - return -EINVAL; - cs53l32a_write(client, 0x04, (u8) ctrl->value); - cs53l32a_write(client, 0x05, (u8) ctrl->value); + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + cs53l32a_write(client, 0x03, ctrl->value ? 0xf0 : 0x30); break; } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) + return -EINVAL; + if (ctrl->value > 12 || ctrl->value < -96) + return -EINVAL; + cs53l32a_write(client, 0x04, (u8) ctrl->value); + cs53l32a_write(client, 0x05, (u8) ctrl->value); + break; case VIDIOC_LOG_STATUS: { u8 v = cs53l32a_read(client, 0x01); u8 m = cs53l32a_read(client, 0x03); + s8 vol = cs53l32a_read(client, 0x04); - cs53l32a_info("Input: %s%s\n", - v == 0x21 ? "external line in" : "tuner", + v4l_info(client, "Input: %d%s\n", (v >> 4) & 3, (m & 0xC0) ? " (muted)" : ""); + v4l_info(client, "Volume: %d dB\n", vol); break; } @@ -146,23 +141,21 @@ static int cs53l32a_attach(struct i2c_adapter *adapter, int address, int kind) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver; - client->flags = I2C_CLIENT_ALLOW_USE; snprintf(client->name, sizeof(client->name) - 1, "cs53l32a"); - cs53l32a_info("chip found @ 0x%x (%s)\n", address << 1, adapter->name); + v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name); for (i = 1; i <= 7; i++) { u8 v = cs53l32a_read(client, i); - cs53l32a_dbg("Read Reg %d %02x\n", i, v); + v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v); } /* Set cs53l32a internal register for Adaptec 2010/2410 setup */ @@ -180,7 +173,7 @@ static int cs53l32a_attach(struct i2c_adapter *adapter, int address, int kind) for (i = 1; i <= 7; i++) { u8 v = cs53l32a_read(client, i); - cs53l32a_dbg("Read Reg %d %02x\n", i, v); + v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v); } i2c_attach_client(client); @@ -190,11 +183,7 @@ static int cs53l32a_attach(struct i2c_adapter *adapter, int address, int kind) static int cs53l32a_probe(struct i2c_adapter *adapter) { -#ifdef I2C_CLASS_TV_ANALOG if (adapter->class & I2C_CLASS_TV_ANALOG) -#else - if (adapter->id == I2C_HW_B_BT848) -#endif return i2c_probe(adapter, &addr_data, cs53l32a_attach); return 0; } @@ -216,13 +205,13 @@ static int cs53l32a_detach(struct i2c_client *client) /* i2c implementation */ static struct i2c_driver i2c_driver = { - .name = "cs53l32a", + .driver = { + .name = "cs53l32a", + }, .id = I2C_DRIVERID_CS53L32A, - .flags = I2C_DF_NOTIFY, .attach_adapter = cs53l32a_probe, .detach_client = cs53l32a_detach, .command = cs53l32a_command, - .owner = THIS_MODULE, }; diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile new file mode 100644 index 00000000000..543ebacdc9d --- /dev/null +++ b/drivers/media/video/cx25840/Makefile @@ -0,0 +1,6 @@ +cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \ + cx25840-vbi.o + +obj-$(CONFIG_VIDEO_DECODER) += cx25840.o + +EXTRA_CFLAGS += -I$(src)/.. diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c new file mode 100644 index 00000000000..cb9a7981e40 --- /dev/null +++ b/drivers/media/video/cx25840/cx25840-audio.c @@ -0,0 +1,349 @@ +/* cx25840 audio functions + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <linux/videodev2.h> +#include <linux/i2c.h> +#include <media/audiochip.h> +#include <media/v4l2-common.h> + +#include "cx25840.h" + +static int set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = i2c_get_clientdata(client); + + if (freq != 32000 && freq != 44100 && freq != 48000) + return -EINVAL; + + /* assert soft reset */ + cx25840_and_or(client, 0x810, ~0x1, 0x01); + + /* common for all inputs and rates */ + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ + cx25840_write(client, 0x127, 0x50); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x0f040610); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0xee39bb01); + + /* src3/4/6_ctl = 0x0801f77f */ + cx25840_write4(client, 0x900, 0x7ff70108); + cx25840_write4(client, 0x904, 0x7ff70108); + cx25840_write4(client, 0x90c, 0x7ff70108); + break; + + case 44100: + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x0f040910); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0xd66bec00); + + /* src3/4/6_ctl = 0x08016d59 */ + cx25840_write4(client, 0x900, 0x596d0108); + cx25840_write4(client, 0x904, 0x596d0108); + cx25840_write4(client, 0x90c, 0x596d0108); + break; + + case 48000: + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x0f040a10); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0xe5d69800); + + /* src3/4/6_ctl = 0x08014faa */ + cx25840_write4(client, 0x900, 0xaa4f0108); + cx25840_write4(client, 0x904, 0xaa4f0108); + cx25840_write4(client, 0x90c, 0xaa4f0108); + break; + } + } else { + switch (freq) { + case 32000: + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x0f04081e); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0x69082a01); + + /* src1_ctl = 0x08010000 */ + cx25840_write4(client, 0x8f8, 0x00000108); + + /* src3/4/6_ctl = 0x08020000 */ + cx25840_write4(client, 0x900, 0x00000208); + cx25840_write4(client, 0x904, 0x00000208); + cx25840_write4(client, 0x90c, 0x00000208); + + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */ + cx25840_write(client, 0x127, 0x54); + break; + + case 44100: + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x0f040918); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0xd66bec00); + + /* src1_ctl = 0x08010000 */ + cx25840_write4(client, 0x8f8, 0xcd600108); + + /* src3/4/6_ctl = 0x08020000 */ + cx25840_write4(client, 0x900, 0x85730108); + cx25840_write4(client, 0x904, 0x85730108); + cx25840_write4(client, 0x90c, 0x85730108); + break; + + case 48000: + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x0f040a18); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0xe5d69800); + + /* src1_ctl = 0x08010000 */ + cx25840_write4(client, 0x8f8, 0x00800108); + + /* src3/4/6_ctl = 0x08020000 */ + cx25840_write4(client, 0x900, 0x55550108); + cx25840_write4(client, 0x904, 0x55550108); + cx25840_write4(client, 0x90c, 0x55550108); + break; + } + } + + /* deassert soft reset */ + cx25840_and_or(client, 0x810, ~0x1, 0x00); + + state->audclk_freq = freq; + + return 0; +} + +void cx25840_audio_set_path(struct i2c_client *client) +{ + struct cx25840_state *state = i2c_get_clientdata(client); + + /* stop microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0); + + /* Mute everything to prevent the PFFT! */ + cx25840_write(client, 0x8d3, 0x1f); + + if (state->aud_input == CX25840_AUDIO_SERIAL) { + /* Set Path1 to Serial Audio Input */ + cx25840_write4(client, 0x8d0, 0x12100101); + + /* The microcontroller should not be started for the + * non-tuner inputs: autodetection is specific for + * TV audio. */ + } else { + /* Set Path1 to Analog Demod Main Channel */ + cx25840_write4(client, 0x8d0, 0x7038061f); + + /* When the microcontroller detects the + * audio format, it will unmute the lines */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); + } + + set_audclk_freq(client, state->audclk_freq); +} + +static int get_volume(struct i2c_client *client) +{ + /* Volume runs +18dB to -96dB in 1/2dB steps + * change to fit the msp3400 -114dB to +12dB range */ + + /* check PATH1_VOLUME */ + int vol = 228 - cx25840_read(client, 0x8d4); + vol = (vol / 2) + 23; + return vol << 9; +} + +static void set_volume(struct i2c_client *client, int volume) +{ + /* First convert the volume to msp3400 values (0-127) */ + int vol = volume >> 9; + /* now scale it up to cx25840 values + * -114dB to -96dB maps to 0 + * this should be 19, but in my testing that was 4dB too loud */ + if (vol <= 23) { + vol = 0; + } else { + vol -= 23; + } + + /* PATH1_VOLUME */ + cx25840_write(client, 0x8d4, 228 - (vol * 2)); +} + +static int get_bass(struct i2c_client *client) +{ + /* bass is 49 steps +12dB to -12dB */ + + /* check PATH1_EQ_BASS_VOL */ + int bass = cx25840_read(client, 0x8d9) & 0x3f; + bass = (((48 - bass) * 0xffff) + 47) / 48; + return bass; +} + +static void set_bass(struct i2c_client *client, int bass) +{ + /* PATH1_EQ_BASS_VOL */ + cx25840_and_or(client, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff)); +} + +static int get_treble(struct i2c_client *client) +{ + /* treble is 49 steps +12dB to -12dB */ + + /* check PATH1_EQ_TREBLE_VOL */ + int treble = cx25840_read(client, 0x8db) & 0x3f; + treble = (((48 - treble) * 0xffff) + 47) / 48; + return treble; +} + +static void set_treble(struct i2c_client *client, int treble) +{ + /* PATH1_EQ_TREBLE_VOL */ + cx25840_and_or(client, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff)); +} + +static int get_balance(struct i2c_client *client) +{ + /* balance is 7 bit, 0 to -96dB */ + + /* check PATH1_BAL_LEVEL */ + int balance = cx25840_read(client, 0x8d5) & 0x7f; + /* check PATH1_BAL_LEFT */ + if ((cx25840_read(client, 0x8d5) & 0x80) == 0) + balance = 0x80 - balance; + else + balance = 0x80 + balance; + return balance << 8; +} + +static void set_balance(struct i2c_client *client, int balance) +{ + int bal = balance >> 8; + if (bal > 0x80) { + /* PATH1_BAL_LEFT */ + cx25840_and_or(client, 0x8d5, 0x7f, 0x80); + /* PATH1_BAL_LEVEL */ + cx25840_and_or(client, 0x8d5, ~0x7f, bal & 0x7f); + } else { + /* PATH1_BAL_LEFT */ + cx25840_and_or(client, 0x8d5, 0x7f, 0x00); + /* PATH1_BAL_LEVEL */ + cx25840_and_or(client, 0x8d5, ~0x7f, 0x80 - bal); + } +} + +static int get_mute(struct i2c_client *client) +{ + /* check SRC1_MUTE_EN */ + return cx25840_read(client, 0x8d3) & 0x2 ? 1 : 0; +} + +static void set_mute(struct i2c_client *client, int mute) +{ + struct cx25840_state *state = i2c_get_clientdata(client); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + /* Must turn off microcontroller in order to mute sound. + * Not sure if this is the best method, but it does work. + * If the microcontroller is running, then it will undo any + * changes to the mute register. */ + if (mute) { + /* disable microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + cx25840_write(client, 0x8d3, 0x1f); + } else { + /* enable microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); + } + } else { + /* SRC1_MUTE_EN */ + cx25840_and_or(client, 0x8d3, ~0x2, mute ? 0x02 : 0x00); + } +} + +int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct v4l2_control *ctrl = arg; + + switch (cmd) { + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + return set_audclk_freq(client, *(u32 *)arg); + + case VIDIOC_G_CTRL: + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = get_volume(client); + break; + case V4L2_CID_AUDIO_BASS: + ctrl->value = get_bass(client); + break; + case V4L2_CID_AUDIO_TREBLE: + ctrl->value = get_treble(client); + break; + case V4L2_CID_AUDIO_BALANCE: + ctrl->value = get_balance(client); + break; + case V4L2_CID_AUDIO_MUTE: + ctrl->value = get_mute(client); + break; + default: + return -EINVAL; + } + break; + + case VIDIOC_S_CTRL: + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + set_volume(client, ctrl->value); + break; + case V4L2_CID_AUDIO_BASS: + set_bass(client, ctrl->value); + break; + case V4L2_CID_AUDIO_TREBLE: + set_treble(client, ctrl->value); + break; + case V4L2_CID_AUDIO_BALANCE: + set_balance(client, ctrl->value); + break; + case V4L2_CID_AUDIO_MUTE: + set_mute(client, ctrl->value); + break; + default: + return -EINVAL; + } + break; + + default: + return -EINVAL; + } + + return 0; +} diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c new file mode 100644 index 00000000000..c66c2c1f480 --- /dev/null +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -0,0 +1,1144 @@ +/* cx25840 - Conexant CX25840 audio/video decoder driver + * + * Copyright (C) 2004 Ulf Eklund + * + * Based on the saa7115 driver and on the first verison of Chris Kennedy's + * cx25840 driver. + * + * Changes by Tyler Trafford <tatrafford@comcast.net> + * - cleanup/rewrite for V4L2 API (2005) + * + * VBI support by Hans Verkuil <hverkuil@xs4all.nl>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/videodev2.h> +#include <linux/i2c.h> +#include <media/audiochip.h> +#include <media/v4l2-common.h> + +#include "cx25840.h" + +MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver"); +MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford"); +MODULE_LICENSE("GPL"); + +static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; + + +static int cx25840_debug; + +module_param_named(debug,cx25840_debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]"); + +I2C_CLIENT_INSMOD; + +/* ----------------------------------------------------------------------- */ + +int cx25840_write(struct i2c_client *client, u16 addr, u8 value) +{ + u8 buffer[3]; + buffer[0] = addr >> 8; + buffer[1] = addr & 0xff; + buffer[2] = value; + return i2c_master_send(client, buffer, 3); +} + +int cx25840_write4(struct i2c_client *client, u16 addr, u32 value) +{ + u8 buffer[6]; + buffer[0] = addr >> 8; + buffer[1] = addr & 0xff; + buffer[2] = value >> 24; + buffer[3] = (value >> 16) & 0xff; + buffer[4] = (value >> 8) & 0xff; + buffer[5] = value & 0xff; + return i2c_master_send(client, buffer, 6); +} + +u8 cx25840_read(struct i2c_client * client, u16 addr) +{ + u8 buffer[2]; + buffer[0] = addr >> 8; + buffer[1] = addr & 0xff; + + if (i2c_master_send(client, buffer, 2) < 2) + return 0; + + if (i2c_master_recv(client, buffer, 1) < 1) + return 0; + + return buffer[0]; +} + +u32 cx25840_read4(struct i2c_client * client, u16 addr) +{ + u8 buffer[4]; + buffer[0] = addr >> 8; + buffer[1] = addr & 0xff; + + if (i2c_master_send(client, buffer, 2) < 2) + return 0; + + if (i2c_master_recv(client, buffer, 4) < 4) + return 0; + + return (buffer[0] << 24) | (buffer[1] << 16) | + (buffer[2] << 8) | buffer[3]; +} + +int cx25840_and_or(struct i2c_client *client, u16 addr, u8 and_mask, + u8 or_value) +{ + return cx25840_write(client, addr, + (cx25840_read(client, addr) & and_mask) | + or_value); +} + +/* ----------------------------------------------------------------------- */ + +static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, + enum cx25840_audio_input aud_input); +static void log_status(struct i2c_client *client); + +/* ----------------------------------------------------------------------- */ + +static void init_dll1(struct i2c_client *client) +{ + /* This is the Hauppauge sequence used to + * initialize the Delay Lock Loop 1 (ADC DLL). */ + cx25840_write(client, 0x159, 0x23); + cx25840_write(client, 0x15a, 0x87); + cx25840_write(client, 0x15b, 0x06); + cx25840_write(client, 0x159, 0xe1); + cx25840_write(client, 0x15a, 0x86); + cx25840_write(client, 0x159, 0xe0); + cx25840_write(client, 0x159, 0xe1); + cx25840_write(client, 0x15b, 0x10); +} + +static void init_dll2(struct i2c_client *client) +{ + /* This is the Hauppauge sequence used to + * initialize the Delay Lock Loop 2 (ADC DLL). */ + cx25840_write(client, 0x15d, 0xe3); + cx25840_write(client, 0x15e, 0x86); + cx25840_write(client, 0x15f, 0x06); + cx25840_write(client, 0x15d, 0xe1); + cx25840_write(client, 0x15d, 0xe0); + cx25840_write(client, 0x15d, 0xe1); +} + +static void cx25840_initialize(struct i2c_client *client, int loadfw) +{ + struct cx25840_state *state = i2c_get_clientdata(client); + + /* datasheet startup in numbered steps, refer to page 3-77 */ + /* 2. */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + /* The default of this register should be 4, but I get 0 instead. + * Set this register to 4 manually. */ + cx25840_write(client, 0x000, 0x04); + /* 3. */ + init_dll1(client); + init_dll2(client); + cx25840_write(client, 0x136, 0x0a); + /* 4. */ + cx25840_write(client, 0x13c, 0x01); + cx25840_write(client, 0x13c, 0x00); + /* 5. */ + if (loadfw) + cx25840_loadfw(client); + /* 6. */ + cx25840_write(client, 0x115, 0x8c); + cx25840_write(client, 0x116, 0x07); + cx25840_write(client, 0x118, 0x02); + /* 7. */ + cx25840_write(client, 0x4a5, 0x80); + cx25840_write(client, 0x4a5, 0x00); + cx25840_write(client, 0x402, 0x00); + /* 8. */ + cx25840_write(client, 0x401, 0x18); + cx25840_write(client, 0x4a2, 0x10); + cx25840_write(client, 0x402, 0x04); + /* 10. */ + cx25840_write(client, 0x8d3, 0x1f); + cx25840_write(client, 0x8e3, 0x03); + + cx25840_vbi_setup(client); + + /* trial and error says these are needed to get audio */ + cx25840_write(client, 0x914, 0xa0); + cx25840_write(client, 0x918, 0xa0); + cx25840_write(client, 0x919, 0x01); + + /* stereo prefered */ + cx25840_write(client, 0x809, 0x04); + /* AC97 shift */ + cx25840_write(client, 0x8cf, 0x0f); + + /* (re)set input */ + set_input(client, state->vid_input, state->aud_input); + + /* start microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); +} + +/* ----------------------------------------------------------------------- */ + +static void input_change(struct i2c_client *client) +{ + struct cx25840_state *state = i2c_get_clientdata(client); + v4l2_std_id std = cx25840_get_v4lstd(client); + + /* Note: perhaps V4L2_STD_PAL_M should be handled as V4L2_STD_NTSC + instead of V4L2_STD_PAL. Someone needs to test this. */ + if (std & V4L2_STD_PAL) { + /* Follow tuner change procedure for PAL */ + cx25840_write(client, 0x808, 0xff); + cx25840_write(client, 0x80b, 0x10); + } else if (std & V4L2_STD_SECAM) { + /* Select autodetect for SECAM */ + cx25840_write(client, 0x808, 0xff); + cx25840_write(client, 0x80b, 0x10); + } else if (std & V4L2_STD_NTSC) { + /* NTSC */ + if (state->pvr150_workaround) { + /* Certain Hauppauge PVR150 models have a hardware bug + that causes audio to drop out. For these models the + audio standard must be set explicitly. + To be precise: it affects cards with tuner models + 85, 99 and 112 (model numbers from tveeprom). */ + if (std == V4L2_STD_NTSC_M_JP) { + /* Japan uses EIAJ audio standard */ + cx25840_write(client, 0x808, 0x2f); + } else { + /* Others use the BTSC audio standard */ + cx25840_write(client, 0x808, 0x1f); + } + /* South Korea uses the A2-M (aka Zweiton M) audio + standard, and should set 0x808 to 0x3f, but I don't + know how to detect this. */ + } else if (std == V4L2_STD_NTSC_M_JP) { + /* Japan uses EIAJ audio standard */ + cx25840_write(client, 0x808, 0xf7); + } else { + /* Others use the BTSC audio standard */ + cx25840_write(client, 0x808, 0xf6); + } + /* South Korea uses the A2-M (aka Zweiton M) audio standard, + and should set 0x808 to 0xf8, but I don't know how to + detect this. */ + cx25840_write(client, 0x80b, 0x00); + } + + if (cx25840_read(client, 0x803) & 0x10) { + /* restart audio decoder microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + cx25840_and_or(client, 0x803, ~0x10, 0x10); + } +} + +static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, + enum cx25840_audio_input aud_input) +{ + struct cx25840_state *state = i2c_get_clientdata(client); + u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && + vid_input <= CX25840_COMPOSITE8); + u8 reg; + + v4l_dbg(1, cx25840_debug, client, "decoder set video input %d, audio input %d\n", + vid_input, aud_input); + + if (is_composite) { + reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); + } else { + int luma = vid_input & 0xf0; + int chroma = vid_input & 0xf00; + + if ((vid_input & ~0xff0) || + luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA4 || + chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { + v4l_err(client, "0x%04x is not a valid video input!\n", vid_input); + return -EINVAL; + } + reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4); + if (chroma >= CX25840_SVIDEO_CHROMA7) { + reg &= 0x3f; + reg |= (chroma - CX25840_SVIDEO_CHROMA7) >> 2; + } else { + reg &= 0xcf; + reg |= (chroma - CX25840_SVIDEO_CHROMA4) >> 4; + } + } + + switch (aud_input) { + case CX25840_AUDIO_SERIAL: + /* do nothing, use serial audio input */ + break; + case CX25840_AUDIO4: reg &= ~0x30; break; + case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break; + case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break; + case CX25840_AUDIO7: reg &= ~0xc0; break; + case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; + + default: + v4l_err(client, "0x%04x is not a valid audio input!\n", aud_input); + return -EINVAL; + } + + cx25840_write(client, 0x103, reg); + /* Set INPUT_MODE to Composite (0) or S-Video (1) */ + cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); + /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ + cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); + /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ + if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) + cx25840_and_or(client, 0x102, ~0x4, 4); + else + cx25840_and_or(client, 0x102, ~0x4, 0); + + state->vid_input = vid_input; + state->aud_input = aud_input; + cx25840_audio_set_path(client); + input_change(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int set_v4lstd(struct i2c_client *client, v4l2_std_id std) +{ + u8 fmt=0; /* zero is autodetect */ + + /* First tests should be against specific std */ + if (std & V4L2_STD_NTSC_M_JP) { + fmt=0x2; + } else if (std & V4L2_STD_NTSC_443) { + fmt=0x3; + } else if (std & V4L2_STD_PAL_M) { + fmt=0x5; + } else if (std & V4L2_STD_PAL_N) { + fmt=0x6; + } else if (std & V4L2_STD_PAL_Nc) { + fmt=0x7; + } else if (std & V4L2_STD_PAL_60) { + fmt=0x8; + } else { + /* Then, test against generic ones */ + if (std & V4L2_STD_NTSC) { + fmt=0x1; + } else if (std & V4L2_STD_PAL) { + fmt=0x4; + } else if (std & V4L2_STD_SECAM) { + fmt=0xc; + } + } + + cx25840_and_or(client, 0x400, ~0xf, fmt); + cx25840_vbi_setup(client); + return 0; +} + +v4l2_std_id cx25840_get_v4lstd(struct i2c_client * client) +{ + /* check VID_FMT_SEL first */ + u8 fmt = cx25840_read(client, 0x400) & 0xf; + + if (!fmt) { + /* check AFD_FMT_STAT if set to autodetect */ + fmt = cx25840_read(client, 0x40d) & 0xf; + } + + switch (fmt) { + case 0x1: return V4L2_STD_NTSC_M; + case 0x2: return V4L2_STD_NTSC_M_JP; + case 0x3: return V4L2_STD_NTSC_443; + case 0x4: return V4L2_STD_PAL; + case 0x5: return V4L2_STD_PAL_M; + case 0x6: return V4L2_STD_PAL_N; + case 0x7: return V4L2_STD_PAL_Nc; + case 0x8: return V4L2_STD_PAL_60; + case 0xc: return V4L2_STD_SECAM; + default: return V4L2_STD_UNKNOWN; + } +} + +/* ----------------------------------------------------------------------- */ + +static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct cx25840_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case CX25840_CID_ENABLE_PVR150_WORKAROUND: + state->pvr150_workaround = ctrl->value; + set_input(client, state->vid_input, state->aud_input); + break; + + case V4L2_CID_BRIGHTNESS: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(client, "invalid brightness setting %d\n", + ctrl->value); + return -ERANGE; + } + + cx25840_write(client, 0x414, ctrl->value - 128); + break; + + case V4L2_CID_CONTRAST: + if (ctrl->value < 0 || ctrl->value > 127) { + v4l_err(client, "invalid contrast setting %d\n", + ctrl->value); + return -ERANGE; + } + + cx25840_write(client, 0x415, ctrl->value << 1); + break; + + case V4L2_CID_SATURATION: + if (ctrl->value < 0 || ctrl->value > 127) { + v4l_err(client, "invalid saturation setting %d\n", + ctrl->value); + return -ERANGE; + } + + cx25840_write(client, 0x420, ctrl->value << 1); + cx25840_write(client, 0x421, ctrl->value << 1); + break; + + case V4L2_CID_HUE: + if (ctrl->value < -127 || ctrl->value > 127) { + v4l_err(client, "invalid hue setting %d\n", ctrl->value); + return -ERANGE; + } + + cx25840_write(client, 0x422, ctrl->value); + break; + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_MUTE: + return cx25840_audio(client, VIDIOC_S_CTRL, ctrl); + + default: + return -EINVAL; + } + + return 0; +} + +static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct cx25840_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case CX25840_CID_ENABLE_PVR150_WORKAROUND: + ctrl->value = state->pvr150_workaround; + break; + case V4L2_CID_BRIGHTNESS: + ctrl->value = (s8)cx25840_read(client, 0x414) + 128; + break; + case V4L2_CID_CONTRAST: + ctrl->value = cx25840_read(client, 0x415) >> 1; + break; + case V4L2_CID_SATURATION: + ctrl->value = cx25840_read(client, 0x420) >> 1; + break; + case V4L2_CID_HUE: + ctrl->value = (s8)cx25840_read(client, 0x422); + break; + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_MUTE: + return cx25840_audio(client, VIDIOC_G_CTRL, ctrl); + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +{ + switch (fmt->type) { + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return cx25840_vbi(client, VIDIOC_G_FMT, fmt); + default: + return -EINVAL; + } + + return 0; +} + +static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +{ + struct v4l2_pix_format *pix; + int HSC, VSC, Vsrc, Hsrc, filter, Vlines; + int is_pal = !(cx25840_get_v4lstd(client) & V4L2_STD_NTSC); + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + pix = &(fmt->fmt.pix); + + Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4; + Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4; + + Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4; + Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4; + + Vlines = pix->height + (is_pal ? 4 : 7); + + if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) || + (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { + v4l_err(client, "%dx%d is not a valid size!\n", + pix->width, pix->height); + return -ERANGE; + } + + HSC = (Hsrc * (1 << 20)) / pix->width - (1 << 20); + VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); + VSC &= 0x1fff; + + if (pix->width >= 385) + filter = 0; + else if (pix->width > 192) + filter = 1; + else if (pix->width > 96) + filter = 2; + else + filter = 3; + + v4l_dbg(1, cx25840_debug, client, "decoder set size %dx%d -> scale %ux%u\n", + pix->width, pix->height, HSC, VSC); + + /* HSCALE=HSC */ + cx25840_write(client, 0x418, HSC & 0xff); + cx25840_write(client, 0x419, (HSC >> 8) & 0xff); + cx25840_write(client, 0x41a, HSC >> 16); + /* VSCALE=VSC */ + cx25840_write(client, 0x41c, VSC & 0xff); + cx25840_write(client, 0x41d, VSC >> 8); + /* VS_INTRLACE=1 VFILT=filter */ + cx25840_write(client, 0x41e, 0x8 | filter); + break; + + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + return cx25840_vbi(client, VIDIOC_S_FMT, fmt); + + case V4L2_BUF_TYPE_VBI_CAPTURE: + return cx25840_vbi(client, VIDIOC_S_FMT, fmt); + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct v4l2_queryctrl cx25840_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 128, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64, + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64, + .flags = 0, + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_VOLUME, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 58880, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Balance", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_MUTE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_BASS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Bass", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + }, { + .id = V4L2_CID_AUDIO_TREBLE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Treble", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + }, +}; + +/* ----------------------------------------------------------------------- */ + +static int cx25840_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + struct cx25840_state *state = i2c_get_clientdata(client); + struct v4l2_tuner *vt = arg; + + switch (cmd) { +#ifdef CONFIG_VIDEO_ADV_DEBUG + /* ioctls to allow direct access to the + * cx25840 registers for testing */ + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_CX25840) + return -EINVAL; + reg->val = cx25840_read(client, reg->reg & 0x0fff); + break; + } + + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_CX25840) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); + break; + } +#endif + + case VIDIOC_INT_DECODE_VBI_LINE: + return cx25840_vbi(client, cmd, arg); + + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + return cx25840_audio(client, cmd, arg); + + case VIDIOC_STREAMON: + v4l_dbg(1, cx25840_debug, client, "enable output\n"); + cx25840_write(client, 0x115, 0x8c); + cx25840_write(client, 0x116, 0x07); + break; + + case VIDIOC_STREAMOFF: + v4l_dbg(1, cx25840_debug, client, "disable output\n"); + cx25840_write(client, 0x115, 0x00); + cx25840_write(client, 0x116, 0x00); + break; + + case VIDIOC_LOG_STATUS: + log_status(client); + break; + + case VIDIOC_G_CTRL: + return get_v4lctrl(client, (struct v4l2_control *)arg); + + case VIDIOC_S_CTRL: + return set_v4lctrl(client, (struct v4l2_control *)arg); + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(cx25840_qctrl); i++) + if (qc->id && qc->id == cx25840_qctrl[i].id) { + memcpy(qc, &cx25840_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; + } + + case VIDIOC_G_STD: + *(v4l2_std_id *)arg = cx25840_get_v4lstd(client); + break; + + case VIDIOC_S_STD: + state->radio = 0; + return set_v4lstd(client, *(v4l2_std_id *)arg); + + case AUDC_SET_RADIO: + state->radio = 1; + break; + + case VIDIOC_G_INPUT: + *(int *)arg = state->vid_input; + break; + + case VIDIOC_S_INPUT: + return set_input(client, *(enum cx25840_video_input *)arg, state->aud_input); + + case VIDIOC_S_AUDIO: + { + struct v4l2_audio *input = arg; + + return set_input(client, state->vid_input, input->index); + } + + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *input = arg; + + memset(input, 0, sizeof(*input)); + input->index = state->aud_input; + break; + } + + case VIDIOC_S_FREQUENCY: + input_change(client); + break; + + case VIDIOC_G_TUNER: + { + u8 mode = cx25840_read(client, 0x804); + u8 pref = cx25840_read(client, 0x809) & 0xf; + u8 vpres = cx25840_read(client, 0x80a) & 0x10; + int val = 0; + + if (state->radio) + break; + + vt->capability |= + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + + vt->signal = vpres ? 0xffff : 0x0; + + /* get rxsubchans and audmode */ + if ((mode & 0xf) == 1) + val |= V4L2_TUNER_SUB_STEREO; + else + val |= V4L2_TUNER_SUB_MONO; + + if (mode == 2 || mode == 4) + val |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + + if (mode & 0x10) + val |= V4L2_TUNER_SUB_SAP; + + vt->rxsubchans = val; + + switch (pref) { + case 0: + vt->audmode = V4L2_TUNER_MODE_MONO; + break; + case 1: + case 2: + vt->audmode = V4L2_TUNER_MODE_LANG2; + break; + case 4: + default: + vt->audmode = V4L2_TUNER_MODE_STEREO; + } + break; + } + + case VIDIOC_S_TUNER: + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_LANG1: + /* Force PREF_MODE to MONO */ + cx25840_and_or(client, 0x809, ~0xf, 0x00); + break; + case V4L2_TUNER_MODE_STEREO: + /* Force PREF_MODE to STEREO */ + cx25840_and_or(client, 0x809, ~0xf, 0x04); + break; + case V4L2_TUNER_MODE_LANG2: + /* Force PREF_MODE to LANG2 */ + cx25840_and_or(client, 0x809, ~0xf, 0x01); + break; + } + break; + + case VIDIOC_G_FMT: + return get_v4lfmt(client, (struct v4l2_format *)arg); + + case VIDIOC_S_FMT: + return set_v4lfmt(client, (struct v4l2_format *)arg); + + case VIDIOC_INT_RESET: + cx25840_initialize(client, 0); + break; + + case VIDIOC_INT_G_CHIP_IDENT: + *(enum v4l2_chip_ident *)arg = + V4L2_IDENT_CX25840 + ((cx25840_read(client, 0x100) >> 4) & 0xf); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_cx25840; + +static int cx25840_detect_client(struct i2c_adapter *adapter, int address, + int kind) +{ + struct i2c_client *client; + struct cx25840_state *state; + u16 device_id; + + /* Check if the adapter supports the needed features + * Not until kernel version 2.6.11 did the bit-algo + * correctly report that it would do an I2C-level xfer */ + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_cx25840; + snprintf(client->name, sizeof(client->name) - 1, "cx25840"); + + v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", address << 1); + + device_id = cx25840_read(client, 0x101) << 8; + device_id |= cx25840_read(client, 0x100); + + /* The high byte of the device ID should be + * 0x84 if chip is present */ + if ((device_id & 0xff00) != 0x8400) { + v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); + kfree(client); + return 0; + } + + v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", + (device_id & 0xfff0) >> 4, + (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : 3, + address << 1, adapter->name); + + state = kmalloc(sizeof(struct cx25840_state), GFP_KERNEL); + if (state == NULL) { + kfree(client); + return -ENOMEM; + } + + i2c_set_clientdata(client, state); + memset(state, 0, sizeof(struct cx25840_state)); + state->vid_input = CX25840_COMPOSITE7; + state->aud_input = CX25840_AUDIO8; + state->audclk_freq = 48000; + state->pvr150_workaround = 0; + + cx25840_initialize(client, 1); + + i2c_attach_client(client); + + return 0; +} + +static int cx25840_attach_adapter(struct i2c_adapter *adapter) +{ + if (adapter->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adapter, &addr_data, &cx25840_detect_client); + return 0; +} + +static int cx25840_detach_client(struct i2c_client *client) +{ + struct cx25840_state *state = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(state); + kfree(client); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_cx25840 = { + .driver = { + .name = "cx25840", + }, + .id = I2C_DRIVERID_CX25840, + .attach_adapter = cx25840_attach_adapter, + .detach_client = cx25840_detach_client, + .command = cx25840_command, +}; + + +static int __init m__init(void) +{ + return i2c_add_driver(&i2c_driver_cx25840); +} + +static void __exit m__exit(void) +{ + i2c_del_driver(&i2c_driver_cx25840); +} + +module_init(m__init); +module_exit(m__exit); + +/* ----------------------------------------------------------------------- */ + +static void log_status(struct i2c_client *client) +{ + static const char *const fmt_strs[] = { + "0x0", + "NTSC-M", "NTSC-J", "NTSC-4.43", + "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", + "0x9", "0xA", "0xB", + "SECAM", + "0xD", "0xE", "0xF" + }; + + struct cx25840_state *state = i2c_get_clientdata(client); + u8 microctrl_vidfmt = cx25840_read(client, 0x80a); + u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; + u8 gen_stat1 = cx25840_read(client, 0x40d); + u8 download_ctl = cx25840_read(client, 0x803); + u8 mod_det_stat0 = cx25840_read(client, 0x804); + u8 mod_det_stat1 = cx25840_read(client, 0x805); + u8 audio_config = cx25840_read(client, 0x808); + u8 pref_mode = cx25840_read(client, 0x809); + u8 afc0 = cx25840_read(client, 0x80b); + u8 mute_ctl = cx25840_read(client, 0x8d3); + int vid_input = state->vid_input; + int aud_input = state->aud_input; + char *p; + + v4l_info(client, "Video signal: %spresent\n", + (microctrl_vidfmt & 0x10) ? "" : "not "); + v4l_info(client, "Detected format: %s\n", + fmt_strs[gen_stat1 & 0xf]); + + switch (mod_det_stat0) { + case 0x00: p = "mono"; break; + case 0x01: p = "stereo"; break; + case 0x02: p = "dual"; break; + case 0x04: p = "tri"; break; + case 0x10: p = "mono with SAP"; break; + case 0x11: p = "stereo with SAP"; break; + case 0x12: p = "dual with SAP"; break; + case 0x14: p = "tri with SAP"; break; + case 0xfe: p = "forced mode"; break; + default: p = "not defined"; + } + v4l_info(client, "Detected audio mode: %s\n", p); + + switch (mod_det_stat1) { + case 0x00: p = "not defined"; break; + case 0x01: p = "EIAJ"; break; + case 0x02: p = "A2-M"; break; + case 0x03: p = "A2-BG"; break; + case 0x04: p = "A2-DK1"; break; + case 0x05: p = "A2-DK2"; break; + case 0x06: p = "A2-DK3"; break; + case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; + case 0x08: p = "AM-L"; break; + case 0x09: p = "NICAM-BG"; break; + case 0x0a: p = "NICAM-DK"; break; + case 0x0b: p = "NICAM-I"; break; + case 0x0c: p = "NICAM-L"; break; + case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; + case 0x0e: p = "IF FM Radio"; break; + case 0x0f: p = "BTSC"; break; + case 0x10: p = "high-deviation FM"; break; + case 0x11: p = "very high-deviation FM"; break; + case 0xfd: p = "unknown audio standard"; break; + case 0xfe: p = "forced audio standard"; break; + case 0xff: p = "no detected audio standard"; break; + default: p = "not defined"; + } + v4l_info(client, "Detected audio standard: %s\n", p); + v4l_info(client, "Audio muted: %s\n", + (mute_ctl & 0x2) ? "yes" : "no"); + v4l_info(client, "Audio microcontroller: %s\n", + (download_ctl & 0x10) ? "running" : "stopped"); + + switch (audio_config >> 4) { + case 0x00: p = "undefined"; break; + case 0x01: p = "BTSC"; break; + case 0x02: p = "EIAJ"; break; + case 0x03: p = "A2-M"; break; + case 0x04: p = "A2-BG"; break; + case 0x05: p = "A2-DK1"; break; + case 0x06: p = "A2-DK2"; break; + case 0x07: p = "A2-DK3"; break; + case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; + case 0x09: p = "AM-L"; break; + case 0x0a: p = "NICAM-BG"; break; + case 0x0b: p = "NICAM-DK"; break; + case 0x0c: p = "NICAM-I"; break; + case 0x0d: p = "NICAM-L"; break; + case 0x0e: p = "FM radio"; break; + case 0x0f: p = "automatic detection"; break; + default: p = "undefined"; + } + v4l_info(client, "Configured audio standard: %s\n", p); + + if ((audio_config >> 4) < 0xF) { + switch (audio_config & 0xF) { + case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; + case 0x01: p = "MONO2 (LANGUAGE B)"; break; + case 0x02: p = "MONO3 (STEREO forced MONO)"; break; + case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; + case 0x04: p = "STEREO"; break; + case 0x05: p = "DUAL1 (AB)"; break; + case 0x06: p = "DUAL2 (AC) (FM)"; break; + case 0x07: p = "DUAL3 (BC) (FM)"; break; + case 0x08: p = "DUAL4 (AC) (AM)"; break; + case 0x09: p = "DUAL5 (BC) (AM)"; break; + case 0x0a: p = "SAP"; break; + default: p = "undefined"; + } + v4l_info(client, "Configured audio mode: %s\n", p); + } else { + switch (audio_config & 0xF) { + case 0x00: p = "BG"; break; + case 0x01: p = "DK1"; break; + case 0x02: p = "DK2"; break; + case 0x03: p = "DK3"; break; + case 0x04: p = "I"; break; + case 0x05: p = "L"; break; + case 0x06: p = "BTSC"; break; + case 0x07: p = "EIAJ"; break; + case 0x08: p = "A2-M"; break; + case 0x09: p = "FM Radio"; break; + case 0x0f: p = "automatic standard and mode detection"; break; + default: p = "undefined"; + } + v4l_info(client, "Configured audio system: %s\n", p); + } + + v4l_info(client, "Specified standard: %s\n", + vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); + + if (vid_input >= CX25840_COMPOSITE1 && + vid_input <= CX25840_COMPOSITE8) { + v4l_info(client, "Specified video input: Composite %d\n", + vid_input - CX25840_COMPOSITE1 + 1); + } else { + v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", + (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); + } + if (aud_input) { + v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input); + } else { + v4l_info(client, "Specified audio input: External\n"); + } + + v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq); + + switch (pref_mode & 0xf) { + case 0: p = "mono/language A"; break; + case 1: p = "language B"; break; + case 2: p = "language C"; break; + case 3: p = "analog fallback"; break; + case 4: p = "stereo"; break; + case 5: p = "language AC"; break; + case 6: p = "language BC"; break; + case 7: p = "language AB"; break; + default: p = "undefined"; + } + v4l_info(client, "Preferred audio mode: %s\n", p); + + if ((audio_config & 0xf) == 0xf) { + switch ((afc0 >> 3) & 0x3) { + case 0: p = "system DK"; break; + case 1: p = "system L"; break; + case 2: p = "autodetect"; break; + default: p = "undefined"; + } + v4l_info(client, "Selected 65 MHz format: %s\n", p); + + switch (afc0 & 0x7) { + case 0: p = "chroma"; break; + case 1: p = "BTSC"; break; + case 2: p = "EIAJ"; break; + case 3: p = "A2-M"; break; + case 4: p = "autodetect"; break; + default: p = "undefined"; + } + v4l_info(client, "Selected 45 MHz format: %s\n", p); + } +} diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c new file mode 100644 index 00000000000..e1a7823d82c --- /dev/null +++ b/drivers/media/video/cx25840/cx25840-firmware.c @@ -0,0 +1,175 @@ +/* cx25840 firmware functions + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/firmware.h> +#include <media/v4l2-common.h> + +#include "cx25840.h" + +#define FWFILE "v4l-cx25840.fw" +#define FWSEND 1024 + +#define FWDEV(x) &((x)->adapter->dev) + +static int fastfw = 1; +static char *firmware = FWFILE; + +module_param(fastfw, bool, 0444); +module_param(firmware, charp, 0444); + +MODULE_PARM_DESC(fastfw, "Load firmware fast [0=100MHz 1=333MHz (default)]"); +MODULE_PARM_DESC(firmware, "Firmware image [default: " FWFILE "]"); + +static void set_i2c_delay(struct i2c_client *client, int delay) +{ + struct i2c_algo_bit_data *algod = client->adapter->algo_data; + + /* We aren't guaranteed to be using algo_bit, + * so avoid the null pointer dereference + * and disable the 'fast firmware load' */ + if (algod) { + algod->udelay = delay; + } else { + fastfw = 0; + } +} + +static void start_fw_load(struct i2c_client *client) +{ + /* DL_ADDR_LB=0 DL_ADDR_HB=0 */ + cx25840_write(client, 0x800, 0x00); + cx25840_write(client, 0x801, 0x00); + // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1 + cx25840_write(client, 0x803, 0x0b); + /* AUTO_INC_DIS=1 */ + cx25840_write(client, 0x000, 0x20); + + if (fastfw) + set_i2c_delay(client, 3); +} + +static void end_fw_load(struct i2c_client *client) +{ + if (fastfw) + set_i2c_delay(client, 10); + + /* AUTO_INC_DIS=0 */ + cx25840_write(client, 0x000, 0x00); + /* DL_ENABLE=0 */ + cx25840_write(client, 0x803, 0x03); +} + +static int check_fw_load(struct i2c_client *client, int size) +{ + /* DL_ADDR_HB DL_ADDR_LB */ + int s = cx25840_read(client, 0x801) << 8; + s |= cx25840_read(client, 0x800); + + if (size != s) { + v4l_err(client, "firmware %s load failed\n", firmware); + return -EINVAL; + } + + v4l_info(client, "loaded %s firmware (%d bytes)\n", firmware, size); + return 0; +} + +static int fw_write(struct i2c_client *client, u8 * data, int size) +{ + int sent; + + if ((sent = i2c_master_send(client, data, size)) < size) { + + if (fastfw) { + v4l_err(client, "333MHz i2c firmware load failed\n"); + fastfw = 0; + set_i2c_delay(client, 10); + + if (sent > 2) { + u16 dl_addr = cx25840_read(client, 0x801) << 8; + dl_addr |= cx25840_read(client, 0x800); + dl_addr -= sent - 2; + cx25840_write(client, 0x801, dl_addr >> 8); + cx25840_write(client, 0x800, dl_addr & 0xff); + } + + if (i2c_master_send(client, data, size) < size) { + v4l_err(client, "100MHz i2c firmware load failed\n"); + return -ENOSYS; + } + + } else { + v4l_err(client, "firmware load i2c failure\n"); + return -ENOSYS; + } + + } + + return 0; +} + +int cx25840_loadfw(struct i2c_client *client) +{ + const struct firmware *fw = NULL; + u8 buffer[4], *ptr; + int size, send, retval; + + if (request_firmware(&fw, firmware, FWDEV(client)) != 0) { + v4l_err(client, "unable to open firmware %s\n", firmware); + return -EINVAL; + } + + start_fw_load(client); + + buffer[0] = 0x08; + buffer[1] = 0x02; + buffer[2] = fw->data[0]; + buffer[3] = fw->data[1]; + retval = fw_write(client, buffer, 4); + + if (retval < 0) { + release_firmware(fw); + return retval; + } + + size = fw->size - 2; + ptr = fw->data; + while (size > 0) { + ptr[0] = 0x08; + ptr[1] = 0x02; + send = size > (FWSEND - 2) ? FWSEND : size + 2; + retval = fw_write(client, ptr, send); + + if (retval < 0) { + release_firmware(fw); + return retval; + } + + size -= FWSEND - 2; + ptr += FWSEND - 2; + } + + end_fw_load(client); + + size = fw->size; + release_firmware(fw); + + return check_fw_load(client, size); +} diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c new file mode 100644 index 00000000000..04d879da7d6 --- /dev/null +++ b/drivers/media/video/cx25840/cx25840-vbi.c @@ -0,0 +1,315 @@ +/* cx25840 VBI functions + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <linux/videodev2.h> +#include <linux/i2c.h> +#include <media/v4l2-common.h> + +#include "cx25840.h" + +static int odd_parity(u8 c) +{ + c ^= (c >> 4); + c ^= (c >> 2); + c ^= (c >> 1); + + return c & 1; +} + +static int decode_vps(u8 * dst, u8 * p) +{ + static const u8 biphase_tbl[] = { + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, + 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, + 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, + 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, + 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, + 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, + 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + }; + + u8 c, err = 0; + int i; + + for (i = 0; i < 2 * 13; i += 2) { + err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; + c = (biphase_tbl[p[i + 1]] & 0xf) | + ((biphase_tbl[p[i]] & 0xf) << 4); + dst[i / 2] = c; + } + + return err & 0xf0; +} + +void cx25840_vbi_setup(struct i2c_client *client) +{ + v4l2_std_id std = cx25840_get_v4lstd(client); + + if (std & ~V4L2_STD_NTSC) { + /* datasheet startup, step 8d */ + cx25840_write(client, 0x49f, 0x11); + + cx25840_write(client, 0x470, 0x84); + cx25840_write(client, 0x471, 0x00); + cx25840_write(client, 0x472, 0x2d); + cx25840_write(client, 0x473, 0x5d); + + cx25840_write(client, 0x474, 0x24); + cx25840_write(client, 0x475, 0x40); + cx25840_write(client, 0x476, 0x24); + cx25840_write(client, 0x477, 0x28); + + cx25840_write(client, 0x478, 0x1f); + cx25840_write(client, 0x479, 0x02); + + if (std & V4L2_STD_SECAM) { + cx25840_write(client, 0x47a, 0x80); + cx25840_write(client, 0x47b, 0x00); + cx25840_write(client, 0x47c, 0x5f); + cx25840_write(client, 0x47d, 0x42); + } else { + cx25840_write(client, 0x47a, 0x90); + cx25840_write(client, 0x47b, 0x20); + cx25840_write(client, 0x47c, 0x63); + cx25840_write(client, 0x47d, 0x82); + } + + cx25840_write(client, 0x47e, 0x0a); + cx25840_write(client, 0x47f, 0x01); + } else { + /* datasheet startup, step 8d */ + cx25840_write(client, 0x49f, 0x14); + + cx25840_write(client, 0x470, 0x7a); + cx25840_write(client, 0x471, 0x00); + cx25840_write(client, 0x472, 0x2d); + cx25840_write(client, 0x473, 0x5b); + + cx25840_write(client, 0x474, 0x1a); + cx25840_write(client, 0x475, 0x70); + cx25840_write(client, 0x476, 0x1e); + cx25840_write(client, 0x477, 0x1e); + + cx25840_write(client, 0x478, 0x1f); + cx25840_write(client, 0x479, 0x02); + cx25840_write(client, 0x47a, 0x50); + cx25840_write(client, 0x47b, 0x66); + + cx25840_write(client, 0x47c, 0x1f); + cx25840_write(client, 0x47d, 0x7c); + cx25840_write(client, 0x47e, 0x08); + cx25840_write(client, 0x47f, 0x00); + } +} + +int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct v4l2_format *fmt; + struct v4l2_sliced_vbi_format *svbi; + + switch (cmd) { + case VIDIOC_G_FMT: + { + static u16 lcr2vbi[] = { + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ + 0, V4L2_SLICED_WSS_625, 0, /* 4 */ + V4L2_SLICED_CAPTION_525, /* 6 */ + 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ + 0, 0, 0, 0 + }; + int i; + + fmt = arg; + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + memset(svbi, 0, sizeof(*svbi)); + /* we're done if raw VBI is active */ + if ((cx25840_read(client, 0x404) & 0x10) == 0) + break; + + for (i = 7; i <= 23; i++) { + u8 v = cx25840_read(client, 0x424 + i - 7); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= + svbi->service_lines[0][i] | svbi->service_lines[1][i]; + } + break; + } + + case VIDIOC_S_FMT: + { + int is_pal = !(cx25840_get_v4lstd(client) & V4L2_STD_NTSC); + int vbi_offset = is_pal ? 1 : 0; + int i, x; + u8 lcr[24]; + + fmt = arg; + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + if (svbi->service_set == 0) { + /* raw VBI */ + memset(svbi, 0, sizeof(*svbi)); + + /* Setup VBI */ + cx25840_vbi_setup(client); + + /* VBI Offset */ + cx25840_write(client, 0x47f, vbi_offset); + cx25840_write(client, 0x404, 0x2e); + break; + } + + for (x = 0; x <= 23; x++) + lcr[x] = 0x00; + + /* Setup VBI */ + cx25840_vbi_setup(client); + + /* Sliced VBI */ + cx25840_write(client, 0x404, 0x36); /* Ancillery data */ + cx25840_write(client, 0x406, 0x13); + cx25840_write(client, 0x47f, vbi_offset); + + if (is_pal) { + for (i = 0; i <= 6; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } else { + for (i = 0; i <= 9; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + + for (i = 22; i <= 23; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } + + for (i = 7; i <= 23; i++) { + for (x = 0; x <= 1; x++) { + switch (svbi->service_lines[1-x][i]) { + case V4L2_SLICED_TELETEXT_B: + lcr[i] |= 1 << (4 * x); + break; + case V4L2_SLICED_WSS_625: + lcr[i] |= 4 << (4 * x); + break; + case V4L2_SLICED_CAPTION_525: + lcr[i] |= 6 << (4 * x); + break; + case V4L2_SLICED_VPS: + lcr[i] |= 9 << (4 * x); + break; + } + } + } + + for (x = 1, i = 0x424; i <= 0x434; i++, x++) { + cx25840_write(client, i, lcr[6 + x]); + } + + cx25840_write(client, 0x43c, 0x16); + + if (is_pal) { + cx25840_write(client, 0x474, 0x2a); + } else { + cx25840_write(client, 0x474, 0x1a + 6); + } + break; + } + + case VIDIOC_INT_DECODE_VBI_LINE: + { + struct v4l2_decode_vbi_line *vbi = arg; + u8 *p = vbi->p; + int id1, id2, l, err = 0; + + if (p[0] || p[1] != 0xff || p[2] != 0xff || + (p[3] != 0x55 && p[3] != 0x91)) { + vbi->line = vbi->type = 0; + break; + } + + p += 4; + id1 = p[-1]; + id2 = p[0] & 0xf; + l = p[2] & 0x3f; + l += 5; + p += 4; + + switch (id2) { + case 1: + id2 = V4L2_SLICED_TELETEXT_B; + break; + case 4: + id2 = V4L2_SLICED_WSS_625; + break; + case 6: + id2 = V4L2_SLICED_CAPTION_525; + err = !odd_parity(p[0]) || !odd_parity(p[1]); + break; + case 9: + id2 = V4L2_SLICED_VPS; + if (decode_vps(p, p) != 0) { + err = 1; + } + break; + default: + id2 = 0; + err = 1; + break; + } + + vbi->type = err ? 0 : id2; + vbi->line = err ? 0 : l; + vbi->is_second_field = err ? 0 : (id1 == 0x55); + vbi->p = p; + break; + } + } + + return 0; +} diff --git a/drivers/media/video/cx25840/cx25840.h b/drivers/media/video/cx25840/cx25840.h new file mode 100644 index 00000000000..fd22f30dcc1 --- /dev/null +++ b/drivers/media/video/cx25840/cx25840.h @@ -0,0 +1,106 @@ +/* cx25840 API header + * + * Copyright (C) 2003-2004 Chris Kennedy + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _CX25840_H_ +#define _CX25840_H_ + + +#include <linux/videodev2.h> +#include <linux/i2c.h> + +/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is + present in Hauppauge PVR-150 (and possibly PVR-500) cards that have + certain NTSC tuners (tveeprom tuner model numbers 85, 99 and 112). The + audio autodetect fails on some channels for these models and the workaround + is to select the audio standard explicitly. Many thanks to Hauppauge for + providing this information. */ +#define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0) + +enum cx25840_video_input { + /* Composite video inputs In1-In8 */ + CX25840_COMPOSITE1 = 1, + CX25840_COMPOSITE2, + CX25840_COMPOSITE3, + CX25840_COMPOSITE4, + CX25840_COMPOSITE5, + CX25840_COMPOSITE6, + CX25840_COMPOSITE7, + CX25840_COMPOSITE8, + + /* S-Video inputs consist of one luma input (In1-In4) ORed with one + chroma input (In5-In8) */ + CX25840_SVIDEO_LUMA1 = 0x10, + CX25840_SVIDEO_LUMA2 = 0x20, + CX25840_SVIDEO_LUMA3 = 0x30, + CX25840_SVIDEO_LUMA4 = 0x40, + CX25840_SVIDEO_CHROMA4 = 0x400, + CX25840_SVIDEO_CHROMA5 = 0x500, + CX25840_SVIDEO_CHROMA6 = 0x600, + CX25840_SVIDEO_CHROMA7 = 0x700, + CX25840_SVIDEO_CHROMA8 = 0x800, + + /* S-Video aliases for common luma/chroma combinations */ + CX25840_SVIDEO1 = 0x510, + CX25840_SVIDEO2 = 0x620, + CX25840_SVIDEO3 = 0x730, + CX25840_SVIDEO4 = 0x840, +}; + +enum cx25840_audio_input { + /* Audio inputs: serial or In4-In8 */ + CX25840_AUDIO_SERIAL, + CX25840_AUDIO4 = 4, + CX25840_AUDIO5, + CX25840_AUDIO6, + CX25840_AUDIO7, + CX25840_AUDIO8, +}; + +struct cx25840_state { + int pvr150_workaround; + int radio; + enum cx25840_video_input vid_input; + enum cx25840_audio_input aud_input; + u32 audclk_freq; +}; + +/* ----------------------------------------------------------------------- */ +/* cx25850-core.c */ +int cx25840_write(struct i2c_client *client, u16 addr, u8 value); +int cx25840_write4(struct i2c_client *client, u16 addr, u32 value); +u8 cx25840_read(struct i2c_client *client, u16 addr); +u32 cx25840_read4(struct i2c_client *client, u16 addr); +int cx25840_and_or(struct i2c_client *client, u16 addr, u8 mask, u8 value); +v4l2_std_id cx25840_get_v4lstd(struct i2c_client *client); + +/* ----------------------------------------------------------------------- */ +/* cx25850-firmware.c */ +int cx25840_loadfw(struct i2c_client *client); + +/* ----------------------------------------------------------------------- */ +/* cx25850-audio.c */ +int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg); +void cx25840_audio_set_path(struct i2c_client *client); + +/* ----------------------------------------------------------------------- */ +/* cx25850-vbi.c */ +void cx25840_vbi_setup(struct i2c_client *client); +int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg); + +#endif diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index 41818b6205b..53308911ae6 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -29,6 +29,20 @@ config VIDEO_CX88_DVB You must also select one or more DVB/ATSC demodulators. If you are unsure which you need, choose all of them. +config VIDEO_CX88_ALSA + tristate "ALSA DMA audio support" + depends on VIDEO_CX88 && SND && EXPERIMENTAL + ---help--- + This is a video4linux driver for direct (DMA) audio on + Conexant 2388x based TV cards. + It only works with boards with function 01 enabled. + To check if your board supports, use lspci -n. + If supported, you should see 1471:8801 or 1471:8811 + PCI device. + + To compile this driver as a module, choose M here: the + module will be called cx88-alsa. + config VIDEO_CX88_DVB_ALL_FRONTENDS bool "Build all supported frontends for cx2388x based TV cards" default y @@ -38,6 +52,7 @@ config VIDEO_CX88_DVB_ALL_FRONTENDS select DVB_CX22702 select DVB_LGDT330X select DVB_NXT200X + select DVB_CX24123 ---help--- This builds cx88-dvb with all currently supported frontend demodulators. If you wish to tweak your configuration, and @@ -46,8 +61,8 @@ config VIDEO_CX88_DVB_ALL_FRONTENDS If you are unsure, choose Y. config VIDEO_CX88_DVB_MT352 - tristate "Zarlink MT352 DVB-T Support" - default m + bool "Zarlink MT352 DVB-T Support" + default y depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS select DVB_MT352 ---help--- @@ -55,8 +70,8 @@ config VIDEO_CX88_DVB_MT352 Connexant 2388x chip and the MT352 demodulator. config VIDEO_CX88_DVB_OR51132 - tristate "OR51132 ATSC Support" - default m + bool "OR51132 ATSC Support" + default y depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS select DVB_OR51132 ---help--- @@ -64,8 +79,8 @@ config VIDEO_CX88_DVB_OR51132 Connexant 2388x chip and the OR51132 demodulator. config VIDEO_CX88_DVB_CX22702 - tristate "Conexant CX22702 DVB-T Support" - default m + bool "Conexant CX22702 DVB-T Support" + default y depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS select DVB_CX22702 ---help--- @@ -73,8 +88,8 @@ config VIDEO_CX88_DVB_CX22702 Connexant 2388x chip and the CX22702 demodulator. config VIDEO_CX88_DVB_LGDT330X - tristate "LG Electronics DT3302/DT3303 ATSC Support" - default m + bool "LG Electronics DT3302/DT3303 ATSC Support" + default y depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS select DVB_LGDT330X ---help--- @@ -82,10 +97,19 @@ config VIDEO_CX88_DVB_LGDT330X Connexant 2388x chip and the LGDT3302/LGDT3303 demodulator. config VIDEO_CX88_DVB_NXT200X - tristate "NXT2002/NXT2004 ATSC Support" - default m + bool "NXT2002/NXT2004 ATSC Support" + default y depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS select DVB_NXT200X ---help--- This adds ATSC 8VSB and QAM64/256 support for cards based on the Connexant 2388x chip and the NXT2002/NXT2004 demodulator. + +config VIDEO_CX88_DVB_CX24123 + bool "Conexant CX24123 DVB-S Support" + default y + depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS + select DVB_CX24123 + ---help--- + This adds DVB-S support for cards based on the + Connexant 2388x chip and the CX24123 demodulator. diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile index 0df40b77345..6e5eaa22619 100644 --- a/drivers/media/video/cx88/Makefile +++ b/drivers/media/video/cx88/Makefile @@ -4,26 +4,20 @@ cx8800-objs := cx88-video.o cx88-vbi.o cx8802-objs := cx88-mpeg.o obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o cx8802.o cx88-blackbird.o -obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o +obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o cx88-vp3054-i2c.o +obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o EXTRA_CFLAGS += -I$(src)/.. EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/frontends -ifneq ($(CONFIG_VIDEO_BUF_DVB),n) - EXTRA_CFLAGS += -DHAVE_VIDEO_BUF_DVB=1 -endif -ifneq ($(CONFIG_DVB_CX22702),n) - EXTRA_CFLAGS += -DHAVE_CX22702=1 -endif -ifneq ($(CONFIG_DVB_OR51132),n) - EXTRA_CFLAGS += -DHAVE_OR51132=1 -endif -ifneq ($(CONFIG_DVB_LGDT330X),n) - EXTRA_CFLAGS += -DHAVE_LGDT330X=1 -endif -ifneq ($(CONFIG_DVB_MT352),n) - EXTRA_CFLAGS += -DHAVE_MT352=1 -endif -ifneq ($(CONFIG_DVB_NXT200X),n) - EXTRA_CFLAGS += -DHAVE_NXT200X=1 -endif + +extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1 +extra-cflags-$(CONFIG_DVB_CX22702) += -DHAVE_CX22702=1 +extra-cflags-$(CONFIG_DVB_OR51132) += -DHAVE_OR51132=1 +extra-cflags-$(CONFIG_DVB_LGDT330X) += -DHAVE_LGDT330X=1 +extra-cflags-$(CONFIG_DVB_MT352) += -DHAVE_MT352=1 +extra-cflags-$(CONFIG_DVB_NXT200X) += -DHAVE_NXT200X=1 +extra-cflags-$(CONFIG_DVB_CX24123) += -DHAVE_CX24123=1 +extra-cflags-$(CONFIG_VIDEO_CX88_DVB)+= -DHAVE_VP3054_I2C=1 + +EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c new file mode 100644 index 00000000000..a2e36a1e5f5 --- /dev/null +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -0,0 +1,847 @@ +/* + * + * Support for audio capture + * PCI function #1 of the cx2388x. + * + * (c) 2005,2006 Ricardo Cerqueira <v4l@cerqueira.org> + * (c) 2005 Mauro Carvalho Chehab <mchehab@brturbo.com.br> + * Based on a dummy cx88 module by Gerd Knorr <kraxel@bytesex.org> + * Based on dummy.c by Jaroslav Kysela <perex@suse.cz> + * + * 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/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <asm/delay.h> +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/control.h> +#include <sound/initval.h> + +#include "cx88.h" +#include "cx88-reg.h" + +#define dprintk(level,fmt, arg...) if (debug >= level) \ + printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg) + +#define dprintk_core(level,fmt, arg...) if (debug >= level) \ + printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg) + + +/**************************************************************************** + Data type declarations - Can be moded to a header file later + ****************************************************************************/ + +/* These can be replaced after done */ +#define MIXER_ADDR_LAST MAX_CX88_INPUT + +struct cx88_audio_dev { + struct cx88_core *core; + struct cx88_dmaqueue q; + + /* pci i/o */ + struct pci_dev *pci; + unsigned char pci_rev,pci_lat; + + /* audio controls */ + int irq; + + snd_card_t *card; + + spinlock_t reg_lock; + + unsigned int dma_size; + unsigned int period_size; + unsigned int num_periods; + + struct videobuf_dmabuf dma_risc; + + int mixer_volume[MIXER_ADDR_LAST+1][2]; + int capture_source[MIXER_ADDR_LAST+1][2]; + + long int read_count; + long int read_offset; + + struct cx88_buffer *buf; + + long opened; + snd_pcm_substream_t *substream; + +}; +typedef struct cx88_audio_dev snd_cx88_card_t; + + + +/**************************************************************************** + Module global static vars + ****************************************************************************/ + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; +static snd_card_t *snd_cx88_cards[SNDRV_CARDS]; + +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled."); + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for cx88x capture interface(s)."); + + +/**************************************************************************** + Module macros + ****************************************************************************/ + +MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards"); +MODULE_AUTHOR("Ricardo Cerqueira"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@brturbo.com.br>"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Conexant,23881}," + "{{Conexant,23882}," + "{{Conexant,23883}"); +static unsigned int debug; +module_param(debug,int,0644); +MODULE_PARM_DESC(debug,"enable debug messages"); + +/**************************************************************************** + Module specific funtions + ****************************************************************************/ + +/* + * BOARD Specific: Sets audio DMA + */ + +int _cx88_start_audio_dma(snd_cx88_card_t *chip) +{ + struct cx88_buffer *buf = chip->buf; + struct cx88_core *core=chip->core; + struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25]; + + + dprintk(1, "Starting audio DMA for %i bytes/line and %i (%i) lines at address %08x\n",buf->bpl, chip->num_periods, audio_ch->fifo_size / buf->bpl, audio_ch->fifo_start); + + /* setup fifo + format - out channel */ + cx88_sram_channel_setup(chip->core, &cx88_sram_channels[SRAM_CH25], + buf->bpl, buf->risc.dma); + + /* sets bpl size */ + cx_write(MO_AUDD_LNGTH, buf->bpl); + + /* reset counter */ + cx_write(MO_AUDD_GPCNTRL,GP_COUNT_CONTROL_RESET); + + dprintk(1,"Enabling IRQ, setting mask from 0x%x to 0x%x\n",chip->core->pci_irqmask,(chip->core->pci_irqmask | 0x02)); + /* enable irqs */ + cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | 0x02); + + + /* Enables corresponding bits at AUD_INT_STAT */ + cx_write(MO_AUD_INTMSK, + (1<<16)| + (1<<12)| + (1<<4)| + (1<<0) + ); + + /* start dma */ + cx_set(MO_DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */ + cx_set(MO_AUD_DMACNTRL, 0x11); /* audio downstream FIFO and RISC enable */ + + if (debug) + cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]); + + return 0; +} + +/* + * BOARD Specific: Resets audio DMA + */ +int _cx88_stop_audio_dma(snd_cx88_card_t *chip) +{ + struct cx88_core *core=chip->core; + dprintk(1, "Stopping audio DMA\n"); + + /* stop dma */ + cx_clear(MO_AUD_DMACNTRL, 0x11); + + /* disable irqs */ + cx_clear(MO_PCI_INTMSK, 0x02); + cx_clear(MO_AUD_INTMSK, + (1<<16)| + (1<<12)| + (1<<4)| + (1<<0) + ); + + if (debug) + cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]); + + return 0; +} + +#define MAX_IRQ_LOOP 10 + +/* + * BOARD Specific: IRQ dma bits + */ +static char *cx88_aud_irqs[32] = { + "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */ + NULL, /* reserved */ + "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */ + NULL, /* reserved */ + "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */ + NULL, /* reserved */ + "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */ + NULL, /* reserved */ + "opc_err", "par_err", "rip_err", /* 16-18 */ + "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */ +}; + +/* + * BOARD Specific: Threats IRQ audio specific calls + */ +static void cx8801_aud_irq(snd_cx88_card_t *chip) +{ + struct cx88_core *core = chip->core; + u32 status, mask; + u32 count; + + status = cx_read(MO_AUD_INTSTAT); + mask = cx_read(MO_AUD_INTMSK); + if (0 == (status & mask)) { + spin_unlock(&chip->reg_lock); + return; + } + cx_write(MO_AUD_INTSTAT, status); + if (debug > 1 || (status & mask & ~0xff)) + cx88_print_irqbits(core->name, "irq aud", + cx88_aud_irqs, status, mask); + /* risc op code error */ + if (status & (1 << 16)) { + printk(KERN_WARNING "%s/0: audio risc op code error\n",core->name); + cx_clear(MO_AUD_DMACNTRL, 0x11); + cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]); + } + + /* risc1 downstream */ + if (status & 0x01) { + spin_lock(&chip->reg_lock); + count = cx_read(MO_AUDD_GPCNT); + spin_unlock(&chip->reg_lock); + if (chip->read_count == 0) + chip->read_count += chip->dma_size; + } + + if (chip->read_count >= chip->period_size) { + dprintk(2, "Elapsing period\n"); + snd_pcm_period_elapsed(chip->substream); + } + + dprintk(3,"Leaving audio IRQ handler...\n"); + + /* FIXME: Any other status should deserve a special handling? */ +} + +/* + * BOARD Specific: Handles IRQ calls + */ +static irqreturn_t cx8801_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + snd_cx88_card_t *chip = dev_id; + struct cx88_core *core = chip->core; + u32 status; + int loop, handled = 0; + + for (loop = 0; loop < MAX_IRQ_LOOP; loop++) { + status = cx_read(MO_PCI_INTSTAT) & (core->pci_irqmask | 0x02); + if (0 == status) + goto out; + dprintk( 3, "cx8801_irq\n" ); + dprintk( 3, " loop: %d/%d\n", loop, MAX_IRQ_LOOP ); + dprintk( 3, " status: %d\n", status ); + handled = 1; + cx_write(MO_PCI_INTSTAT, status); + + if (status & 0x02) + { + dprintk( 2, " ALSA IRQ handling\n" ); + cx8801_aud_irq(chip); + } + }; + + if (MAX_IRQ_LOOP == loop) { + dprintk( 0, "clearing mask\n" ); + dprintk(1,"%s/0: irq loop -- clearing mask\n", + core->name); + cx_clear(MO_PCI_INTMSK,0x02); + } + + out: + return IRQ_RETVAL(handled); +} + + +static int dsp_buffer_free(snd_cx88_card_t *chip) +{ + BUG_ON(!chip->dma_size); + + dprintk(2,"Freeing buffer\n"); + videobuf_dma_pci_unmap(chip->pci, &chip->dma_risc); + videobuf_dma_free(&chip->dma_risc); + btcx_riscmem_free(chip->pci,&chip->buf->risc); + kfree(chip->buf); + + chip->dma_size = 0; + + return 0; +} + +/**************************************************************************** + ALSA PCM Interface + ****************************************************************************/ + +/* + * Digital hardware definition + */ +static snd_pcm_hardware_t snd_cx88_digital_hw = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (2*2048), + .period_bytes_min = 2048, + .period_bytes_max = 2048, + .periods_min = 2, + .periods_max = 2, +}; + +/* + * audio pcm capture runtime free + */ +static void snd_card_cx88_runtime_free(snd_pcm_runtime_t *runtime) +{ +} +/* + * audio pcm capture open callback + */ +static int snd_cx88_pcm_open(snd_pcm_substream_t *substream) +{ + snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int err; + + if (test_and_set_bit(0, &chip->opened)) + return -EBUSY; + + err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + goto _error; + + chip->substream = substream; + + chip->read_count = 0; + chip->read_offset = 0; + + runtime->private_free = snd_card_cx88_runtime_free; + runtime->hw = snd_cx88_digital_hw; + + return 0; +_error: + dprintk(1,"Error opening PCM!\n"); + clear_bit(0, &chip->opened); + smp_mb__after_clear_bit(); + return err; +} + +/* + * audio close callback + */ +static int snd_cx88_close(snd_pcm_substream_t *substream) +{ + snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); + + clear_bit(0, &chip->opened); + smp_mb__after_clear_bit(); + + return 0; +} + +/* + * hw_params callback + */ +static int snd_cx88_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); + struct cx88_buffer *buf; + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + + chip->period_size = params_period_bytes(hw_params); + chip->num_periods = params_periods(hw_params); + chip->dma_size = chip->period_size * params_periods(hw_params); + + BUG_ON(!chip->dma_size); + + dprintk(1,"Setting buffer\n"); + + buf = kmalloc(sizeof(*buf),GFP_KERNEL); + if (NULL == buf) + return -ENOMEM; + memset(buf,0,sizeof(*buf)); + + + buf->vb.memory = V4L2_MEMORY_MMAP; + buf->vb.width = chip->period_size; + buf->vb.height = chip->num_periods; + buf->vb.size = chip->dma_size; + buf->vb.field = V4L2_FIELD_NONE; + + videobuf_dma_init(&buf->vb.dma); + videobuf_dma_init_kernel(&buf->vb.dma,PCI_DMA_FROMDEVICE, + (PAGE_ALIGN(buf->vb.size) >> PAGE_SHIFT)); + + videobuf_dma_pci_map(chip->pci,&buf->vb.dma); + + + cx88_risc_databuffer(chip->pci, &buf->risc, + buf->vb.dma.sglist, + buf->vb.width, buf->vb.height); + + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + buf->vb.state = STATE_PREPARED; + + buf->bpl = chip->period_size; + chip->buf = buf; + chip->dma_risc = buf->vb.dma; + + dprintk(1,"Buffer ready at %u\n",chip->dma_risc.nr_pages); + substream->runtime->dma_area = chip->dma_risc.vmalloc; + return 0; +} + +/* + * hw free callback + */ +static int snd_cx88_hw_free(snd_pcm_substream_t * substream) +{ + + snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + return 0; +} + +/* + * prepare callback + */ +static int snd_cx88_prepare(snd_pcm_substream_t *substream) +{ + return 0; +} + + +/* + * trigger callback + */ +static int snd_cx88_card_trigger(snd_pcm_substream_t *substream, int cmd) +{ + snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); + int err; + + spin_lock(&chip->reg_lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err=_cx88_start_audio_dma(chip); + break; + case SNDRV_PCM_TRIGGER_STOP: + err=_cx88_stop_audio_dma(chip); + break; + default: + err=-EINVAL; + break; + } + + spin_unlock(&chip->reg_lock); + + return err; +} + +/* + * pointer callback + */ +static snd_pcm_uframes_t snd_cx88_pointer(snd_pcm_substream_t *substream) +{ + snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + if (chip->read_count) { + chip->read_count -= snd_pcm_lib_period_bytes(substream); + chip->read_offset += snd_pcm_lib_period_bytes(substream); + if (chip->read_offset == chip->dma_size) + chip->read_offset = 0; + } + + dprintk(2, "Pointer time, will return %li, read %li\n",chip->read_offset,chip->read_count); + return bytes_to_frames(runtime, chip->read_offset); + +} + +/* + * operators + */ +static snd_pcm_ops_t snd_cx88_pcm_ops = { + .open = snd_cx88_pcm_open, + .close = snd_cx88_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cx88_hw_params, + .hw_free = snd_cx88_hw_free, + .prepare = snd_cx88_prepare, + .trigger = snd_cx88_card_trigger, + .pointer = snd_cx88_pointer, +}; + +/* + * create a PCM device + */ +static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, char *name) +{ + int err; + snd_pcm_t *pcm; + + err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); + if (err < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, name); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx88_pcm_ops); + + return 0; +} + +/**************************************************************************** + CONTROL INTERFACE + ****************************************************************************/ +static int snd_cx88_capture_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 1; + info->value.integer.min = 0; + info->value.integer.max = 0x3f; + + return 0; +} + +/* OK - TODO: test it */ +static int snd_cx88_capture_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value) +{ + snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); + struct cx88_core *core=chip->core; + + value->value.integer.value[0] = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f); + + return 0; +} + +/* OK - TODO: test it */ +static int snd_cx88_capture_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *value) +{ + snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); + struct cx88_core *core=chip->core; + int v; + u32 old_control; + + spin_lock_irq(&chip->reg_lock); + old_control = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f); + v = 0x3f - (value->value.integer.value[0] & 0x3f); + cx_andor(AUD_VOL_CTL, 0x3f, v); + spin_unlock_irq(&chip->reg_lock); + + return v != old_control; +} + +static snd_kcontrol_new_t snd_cx88_capture_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .info = snd_cx88_capture_volume_info, + .get = snd_cx88_capture_volume_get, + .put = snd_cx88_capture_volume_put, +}; + + +/**************************************************************************** + Basic Flow for Sound Devices + ****************************************************************************/ + +/* + * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio + * Only boards with eeprom and byte 1 at eeprom=1 have it + */ + +struct pci_device_id cx88_audio_pci_tbl[] = { + {0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, + {0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, + {0, } +}; +MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl); + +/* + * Chip-specific destructor + */ + +static int snd_cx88_free(snd_cx88_card_t *chip) +{ + + if (chip->irq >= 0){ + synchronize_irq(chip->irq); + free_irq(chip->irq, chip); + } + + cx88_core_put(chip->core,chip->pci); + + pci_disable_device(chip->pci); + return 0; +} + +/* + * Component Destructor + */ +static void snd_cx88_dev_free(snd_card_t * card) +{ + snd_cx88_card_t *chip = card->private_data; + + snd_cx88_free(chip); +} + + +/* + * Alsa Constructor - Component probe + */ + +static int devno; +static int __devinit snd_cx88_create(snd_card_t *card, struct pci_dev *pci, + snd_cx88_card_t **rchip) +{ + snd_cx88_card_t *chip; + struct cx88_core *core; + int err; + + *rchip = NULL; + + err = pci_enable_device(pci); + if (err < 0) + return err; + + pci_set_master(pci); + + chip = (snd_cx88_card_t *) card->private_data; + + core = cx88_core_get(pci); + + if (!pci_dma_supported(pci,0xffffffff)) { + dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name); + err = -EIO; + cx88_core_put(core,pci); + return err; + } + + + /* pci init */ + chip->card = card; + chip->pci = pci; + chip->irq = -1; + spin_lock_init(&chip->reg_lock); + + cx88_reset(core); + if (NULL == core) { + err = -EINVAL; + kfree (chip); + return err; + } + chip->core = core; + + /* get irq */ + err = request_irq(chip->pci->irq, cx8801_irq, + SA_SHIRQ | SA_INTERRUPT, chip->core->name, chip); + if (err < 0) { + dprintk(0, "%s: can't get IRQ %d\n", + chip->core->name, chip->pci->irq); + return err; + } + + /* print pci info */ + pci_read_config_byte(pci, PCI_CLASS_REVISION, &chip->pci_rev); + pci_read_config_byte(pci, PCI_LATENCY_TIMER, &chip->pci_lat); + + dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, " + "latency: %d, mmio: 0x%lx\n", core->name, devno, + pci_name(pci), chip->pci_rev, pci->irq, + chip->pci_lat,pci_resource_start(pci,0)); + + chip->irq = pci->irq; + synchronize_irq(chip->irq); + + snd_card_set_dev(card, &pci->dev); + + *rchip = chip; + + return 0; +} + +static int __devinit cx88_audio_initdev(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + snd_card_t *card; + snd_cx88_card_t *chip; + int err; + + if (devno >= SNDRV_CARDS) + return (-ENODEV); + + if (!enable[devno]) { + ++devno; + return (-ENOENT); + } + + card = snd_card_new(index[devno], id[devno], THIS_MODULE, sizeof(snd_cx88_card_t)); + if (!card) + return (-ENOMEM); + + card->private_free = snd_cx88_dev_free; + + err = snd_cx88_create(card, pci, &chip); + if (err < 0) + return (err); + + err = snd_cx88_pcm(chip, 0, "CX88 Digital"); + + if (err < 0) { + snd_card_free(card); + return (err); + } + + err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_capture_volume, chip)); + if (err < 0) { + snd_card_free(card); + return (err); + } + + strcpy (card->driver, "CX88x"); + sprintf(card->shortname, "Conexant CX%x", pci->device); + sprintf(card->longname, "%s at %#lx", + card->shortname, pci_resource_start(pci, 0)); + strcpy (card->mixername, "CX88"); + + dprintk (0, "%s/%i: ALSA support for cx2388x boards\n", + card->driver,devno); + + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return (err); + } + snd_cx88_cards[devno] = card; + + pci_set_drvdata(pci,card); + + devno++; + return 0; +} +/* + * ALSA destructor + */ +static void __devexit cx88_audio_finidev(struct pci_dev *pci) +{ + struct cx88_audio_dev *card = pci_get_drvdata(pci); + + snd_card_free((void *)card); + + pci_set_drvdata(pci, NULL); + + devno--; +} + +/* + * PCI driver definition + */ + +static struct pci_driver cx88_audio_pci_driver = { + .name = "cx88_audio", + .id_table = cx88_audio_pci_tbl, + .probe = cx88_audio_initdev, + .remove = cx88_audio_finidev, +}; + +/**************************************************************************** + LINUX MODULE INIT + ****************************************************************************/ + +/* + * module init + */ +static int cx88_audio_init(void) +{ + printk(KERN_INFO "cx2388x alsa driver version %d.%d.%d loaded\n", + (CX88_VERSION_CODE >> 16) & 0xff, + (CX88_VERSION_CODE >> 8) & 0xff, + CX88_VERSION_CODE & 0xff); +#ifdef SNAPSHOT + printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", + SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); +#endif + return pci_register_driver(&cx88_audio_pci_driver); +} + +/* + * module remove + */ +static void cx88_audio_fini(void) +{ + + pci_unregister_driver(&cx88_audio_pci_driver); +} + +module_init(cx88_audio_init); +module_exit(cx88_audio_fini); + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 4ae3f78cccf..a502a4d6e4a 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -32,10 +32,10 @@ #include <linux/firmware.h> #include "cx88.h" +#include <media/v4l2-common.h> MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards"); -MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>"); -MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); +MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); static unsigned int mpegbufs = 32; @@ -616,6 +616,8 @@ static int blackbird_load_firmware(struct cx8802_dev *dev) retval = request_firmware(&firmware, BLACKBIRD_FIRM_ENC_FILENAME, &dev->pci->dev); + + if (retval != 0) { dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n", BLACKBIRD_FIRM_ENC_FILENAME); @@ -1373,7 +1375,7 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file, struct cx88_core *core = dev->core; if (debug > 1) - cx88_print_ioctl(core->name,cmd); + v4l_print_ioctl(core->name,cmd); switch (cmd) { @@ -1537,10 +1539,9 @@ static int mpeg_open(struct inode *inode, struct file *file) dprintk(1,"open minor=%d\n",minor); /* allocate + initialize per filehandle data */ - fh = kmalloc(sizeof(*fh),GFP_KERNEL); + fh = kzalloc(sizeof(*fh),GFP_KERNEL); if (NULL == fh) return -ENOMEM; - memset(fh,0,sizeof(*fh)); file->private_data = fh; fh->dev = dev; @@ -1676,10 +1677,9 @@ static int __devinit blackbird_probe(struct pci_dev *pci_dev, goto fail_core; err = -ENOMEM; - dev = kmalloc(sizeof(*dev),GFP_KERNEL); + dev = kzalloc(sizeof(*dev),GFP_KERNEL); if (NULL == dev) goto fail_core; - memset(dev,0,sizeof(*dev)); dev->pci = pci_dev; dev->core = core; dev->width = 720; @@ -1687,6 +1687,18 @@ static int __devinit blackbird_probe(struct pci_dev *pci_dev, memcpy(&dev->params,&default_mpeg_params,sizeof(default_mpeg_params)); memcpy(&dev->dnr_params,&default_dnr_params,sizeof(default_dnr_params)); + if (core->board == CX88_BOARD_HAUPPAUGE_ROSLYN) { + + if (core->tuner_formats & V4L2_STD_525_60) { + dev->height = 480; + dev->params.vi_frame_rate = 30; + } else { + dev->height = 576; + dev->params.vi_frame_rate = 25; + } + + } + err = cx8802_init_common(dev); if (0 != err) goto fail_free; diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index f2268631b7c..ad2f565f522 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -567,6 +567,7 @@ struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -610,12 +611,12 @@ struct cx88_board cx88_boards[] = { .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, - .gpio0 = 0xed12, /* internal decoder */ + .gpio0 = 0xed1a, .gpio2 = 0x00ff, },{ .type = CX88_VMUX_DEBUG, .vmux = 0, - .gpio0 = 0xff01, /* mono from tuner chip */ + .gpio0 = 0xff01, },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, @@ -707,10 +708,11 @@ struct cx88_board cx88_boards[] = { }, [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T] = { .name = "DViCO FusionHDTV 3 Gold-T", - .tuner_type = TUNER_THOMSON_DTT7611, + .tuner_type = TUNER_THOMSON_DTT761X, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -895,6 +897,158 @@ struct cx88_board cx88_boards[] = { .gpio3 = 0x0000, }}, }, + [CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1] = { + .name = "Hauppauge Nova-S-Plus DVB-S", + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DVB, + .vmux = 0, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + }}, + .dvb = 1, + }, + [CX88_BOARD_HAUPPAUGE_NOVASE2_S1] = { + .name = "Hauppauge Nova-SE2 DVB-S", + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DVB, + .vmux = 0, + }}, + .dvb = 1, + }, + [CX88_BOARD_KWORLD_DVBS_100] = { + .name = "KWorld DVB-S 100", + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DVB, + .vmux = 0, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + }}, + .dvb = 1, + }, + [CX88_BOARD_HAUPPAUGE_HVR1100] = { + .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid", + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + }}, + /* fixme: Add radio support */ + .dvb = 1, + }, + [CX88_BOARD_HAUPPAUGE_HVR1100LP] = { + .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile)", + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + }}, + /* fixme: Add radio support */ + .dvb = 1, + }, + [CX88_BOARD_DNTV_LIVE_DVB_T_PRO] = { + .name = "digitalnow DNTV Live! DVB-T Pro", + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | + TDA9887_PORT2_ACTIVE, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0xf80808, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0xf80808, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0xf80808, + }}, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0xf80808, + }, + .dvb = 1, + }, + [CX88_BOARD_KWORLD_DVB_T_CX22702] = { + /* Kworld V-stream Xpert DVB-T with Thomson tuner */ + /* DTT 7579 Conexant CX22702-19 Conexant CX2388x */ + /* Manenti Marco <marco_manenti@colman.it> */ + .name = "KWorld/VStream XPert DVB-T with cx22702", + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x0700, + .gpio2 = 0x0101, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x0700, + .gpio2 = 0x0101, + }}, + .dvb = 1, + }, + [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL] = { + .name = "DViCO FusionHDTV DVB-T Dual Digital", + .tuner_type = TUNER_ABSENT, /* No analog tuner */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x000027df, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x000027df, + }}, + .dvb = 1, + }, + }; const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); @@ -1042,6 +1196,59 @@ struct cx88_subid cx88_subids[] = { .subvendor = 0x1461, .subdevice = 0x000a, .card = CX88_BOARD_AVERTV_303, + },{ + .subvendor = 0x0070, + .subdevice = 0x9200, + .card = CX88_BOARD_HAUPPAUGE_NOVASE2_S1, + },{ + .subvendor = 0x0070, + .subdevice = 0x9201, + .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1, + },{ + .subvendor = 0x0070, + .subdevice = 0x9202, + .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1, + },{ + .subvendor = 0x17de, + .subdevice = 0x08b2, + .card = CX88_BOARD_KWORLD_DVBS_100, + },{ + .subvendor = 0x0070, + .subdevice = 0x9400, + .card = CX88_BOARD_HAUPPAUGE_HVR1100, + },{ + .subvendor = 0x0070, + .subdevice = 0x9402, + .card = CX88_BOARD_HAUPPAUGE_HVR1100, + },{ + .subvendor = 0x0070, + .subdevice = 0x9800, + .card = CX88_BOARD_HAUPPAUGE_HVR1100LP, + },{ + .subvendor = 0x0070, + .subdevice = 0x9802, + .card = CX88_BOARD_HAUPPAUGE_HVR1100LP, + },{ + .subvendor = 0x0070, + .subdevice = 0x9001, + .card = CX88_BOARD_HAUPPAUGE_DVB_T1, + },{ + .subvendor = 0x1822, + .subdevice = 0x0025, + .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO, + },{ + .subvendor = 0x17de, + .subdevice = 0x08a1, + .card = CX88_BOARD_KWORLD_DVB_T_CX22702, + },{ + .subvendor = 0x18ac, + .subdevice = 0xdb50, + .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL, + },{ + .subvendor = 0x18ac, + .subdevice = 0xdb11, + .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS, + /* Re-branded DViCO: UltraView DVB-T Plus */ }, }; const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); @@ -1073,51 +1280,39 @@ static void __devinit leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data) core->name, core->tuner_type, eeprom_data[0]); } - -/* ----------------------------------------------------------------------- */ - static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) { struct tveeprom tv; tveeprom_hauppauge_analog(&core->i2c_client, &tv, eeprom_data); core->tuner_type = tv.tuner_type; + core->tuner_formats = tv.tuner_formats; core->has_radio = tv.has_radio; -} - -static int hauppauge_eeprom_dvb(struct cx88_core *core, u8 *ee) -{ - int model; - int tuner; /* Make sure we support the board model */ - model = ee[0x1f] << 24 | ee[0x1e] << 16 | ee[0x1d] << 8 | ee[0x1c]; - switch(model) { - case 90002: - case 90500: - case 90501: + switch (tv.model) + { + case 28552: /* WinTV-PVR 'Roslyn' (No IR) */ + case 90002: /* Nova-T-PCI (9002) */ + case 92001: /* Nova-S-Plus (Video and IR) */ + case 92002: /* Nova-S-Plus (Video and IR) */ + case 90003: /* Nova-T-PCI (9002 No RF out) */ + case 90500: /* Nova-T-PCI (oem) */ + case 90501: /* Nova-T-PCI (oem/IR) */ + case 92000: /* Nova-SE2 (OEM, No Video or IR) */ + case 94009: /* WinTV-HVR1100 (Video and IR Retail) */ + case 94501: /* WinTV-HVR1100 (Video and IR OEM) */ + case 98559: /* WinTV-HVR1100LP (Video no IR, Retail - Low Profile) */ /* known */ break; default: printk("%s: warning: unknown hauppauge model #%d\n", - core->name, model); + core->name, tv.model); break; } - /* Make sure we support the tuner */ - tuner = ee[0x2d]; - switch(tuner) { - case 0x4B: /* dtt 7595 */ - case 0x4C: /* dtt 7592 */ - break; - default: - printk("%s: error: unknown hauppauge tuner 0x%02x\n", - core->name, tuner); - return -ENODEV; - } - printk(KERN_INFO "%s: hauppauge eeprom: model=%d, tuner=%d\n", - core->name, model, tuner); - return 0; + printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", + core->name, tv.model); } /* ----------------------------------------------------------------------- */ @@ -1201,7 +1396,7 @@ void cx88_card_list(struct cx88_core *core, struct pci_dev *pci) void cx88_card_setup(struct cx88_core *core) { - static u8 eeprom[128]; + static u8 eeprom[256]; if (0 == core->i2c_rc) { core->i2c_client.addr = 0xa0 >> 1; @@ -1222,12 +1417,21 @@ void cx88_card_setup(struct cx88_core *core) if (0 == core->i2c_rc) leadtek_eeprom(core,eeprom); break; + case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: + case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: case CX88_BOARD_HAUPPAUGE_DVB_T1: + case CX88_BOARD_HAUPPAUGE_HVR1100: + case CX88_BOARD_HAUPPAUGE_HVR1100LP: if (0 == core->i2c_rc) - hauppauge_eeprom_dvb(core,eeprom); + hauppauge_eeprom(core,eeprom); + break; + case CX88_BOARD_KWORLD_DVBS_100: + cx_write(MO_GP0_IO, 0x000007f8); + cx_write(MO_GP1_IO, 0x00000001); break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: + case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: /* GPIO0:0 is hooked to mt352 reset pin */ cx_set(MO_GP0_IO, 0x00000101); cx_clear(MO_GP0_IO, 0x00000001); @@ -1243,6 +1447,9 @@ void cx88_card_setup(struct cx88_core *core) cx_clear(MO_GP0_IO, 0x00000007); cx_set(MO_GP2_IO, 0x00000101); break; + case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: + cx_write(MO_GP0_IO, 0x00080808); + break; case CX88_BOARD_ATI_HDTVWONDER: if (0 == core->i2c_rc) { /* enable tuner */ diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index eb806af1718..8d6d6a6cf78 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -32,8 +32,10 @@ #include <linux/pci.h> #include <linux/delay.h> #include <linux/videodev2.h> +#include <linux/mutex.h> #include "cx88.h" +#include <media/v4l2-common.h> MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); @@ -74,62 +76,8 @@ MODULE_PARM_DESC(nocomb,"disable comb filter"); static unsigned int cx88_devcount; static LIST_HEAD(cx88_devlist); -static DECLARE_MUTEX(devlist); +static DEFINE_MUTEX(devlist); -/* ------------------------------------------------------------------ */ -/* debug help functions */ - -static const char *v4l1_ioctls[] = { - "0", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", - "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", - "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", - "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", - "SMICROCODE", "GVBIFMT", "SVBIFMT" }; -#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) - -static const char *v4l2_ioctls[] = { - "QUERYCAP", "1", "ENUM_PIXFMT", "ENUM_FBUFFMT", "G_FMT", "S_FMT", - "G_COMP", "S_COMP", "REQBUFS", "QUERYBUF", "G_FBUF", "S_FBUF", - "G_WIN", "S_WIN", "PREVIEW", "QBUF", "16", "DQBUF", "STREAMON", - "STREAMOFF", "G_PERF", "G_PARM", "S_PARM", "G_STD", "S_STD", - "ENUMSTD", "ENUMINPUT", "G_CTRL", "S_CTRL", "G_TUNER", "S_TUNER", - "G_FREQ", "S_FREQ", "G_AUDIO", "S_AUDIO", "35", "QUERYCTRL", - "QUERYMENU", "G_INPUT", "S_INPUT", "ENUMCVT", "41", "42", "43", - "44", "45", "G_OUTPUT", "S_OUTPUT", "ENUMOUTPUT", "G_AUDOUT", - "S_AUDOUT", "ENUMFX", "G_EFFECT", "S_EFFECT", "G_MODULATOR", - "S_MODULATOR" -}; -#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) - -void cx88_print_ioctl(char *name, unsigned int cmd) -{ - char *dir; - - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: dir = "--"; break; - case _IOC_READ: dir = "r-"; break; - case _IOC_WRITE: dir = "-w"; break; - case _IOC_READ | _IOC_WRITE: dir = "rw"; break; - default: dir = "??"; break; - } - switch (_IOC_TYPE(cmd)) { - case 'v': - printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l1, %s, VIDIOC%s)\n", - name, cmd, dir, (_IOC_NR(cmd) < V4L1_IOCTLS) ? - v4l1_ioctls[_IOC_NR(cmd)] : "???"); - break; - case 'V': - printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l2, %s, VIDIOC_%s)\n", - name, cmd, dir, (_IOC_NR(cmd) < V4L2_IOCTLS) ? - v4l2_ioctls[_IOC_NR(cmd)] : "???"); - break; - default: - printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n", - name, cmd, dir, _IOC_NR(cmd)); - } -} - -/* ------------------------------------------------------------------ */ #define NO_SYNC_LINE (-1U) static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist, @@ -291,9 +239,9 @@ cx88_free_buffer(struct pci_dev *pci, struct cx88_buffer *buf) * channel 22 (u video) - 2.0k * channel 23 (v video) - 2.0k * channel 24 (vbi) - 4.0k - * channels 25+26 (audio) - 0.5k + * channels 25+26 (audio) - 4.0k * channel 28 (mpeg) - 4.0k - * TOTAL = 25.5k + * TOTAL = 29.0k * * Every channel has 160 bytes control data (64 bytes instruction * queue and 6 CDT entries), which is close to 2k total. @@ -359,7 +307,7 @@ struct sram_channel cx88_sram_channels[] = { .ctrl_start = 0x180680, .cdt = 0x180680 + 64, .fifo_start = 0x185400, - .fifo_size = 0x000200, + .fifo_size = 0x001000, .ptr1_reg = MO_DMA25_PTR1, .ptr2_reg = MO_DMA25_PTR2, .cnt1_reg = MO_DMA25_CNT1, @@ -371,7 +319,7 @@ struct sram_channel cx88_sram_channels[] = { .ctrl_start = 0x180720, .cdt = 0x180680 + 64, /* same as audio IN */ .fifo_start = 0x185400, /* same as audio IN */ - .fifo_size = 0x000200, /* same as audio IN */ + .fifo_size = 0x001000, /* same as audio IN */ .ptr1_reg = MO_DMA26_PTR1, .ptr2_reg = MO_DMA26_PTR2, .cnt1_reg = MO_DMA26_CNT1, @@ -382,7 +330,7 @@ struct sram_channel cx88_sram_channels[] = { .cmds_start = 0x180200, .ctrl_start = 0x1807C0, .cdt = 0x1807C0 + 64, - .fifo_start = 0x185600, + .fifo_start = 0x186400, .fifo_size = 0x001000, .ptr1_reg = MO_DMA28_PTR1, .ptr2_reg = MO_DMA28_PTR2, @@ -837,6 +785,28 @@ static int set_pll(struct cx88_core *core, int prescale, u32 ofreq) return -1; } +int cx88_start_audio_dma(struct cx88_core *core) +{ + /* setup fifo + format */ + cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0); + cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0); + + cx_write(MO_AUDD_LNGTH, 128); /* fifo bpl size */ + cx_write(MO_AUDR_LNGTH, 128); /* fifo bpl size */ + + /* start dma */ + cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */ + return 0; +} + +int cx88_stop_audio_dma(struct cx88_core *core) +{ + /* stop dma */ + cx_write(MO_AUD_DMACNTRL, 0x0000); + + return 0; +} + static int set_tvaudio(struct cx88_core *core) { struct cx88_tvnorm *norm = core->tvnorm; @@ -877,12 +847,16 @@ static int set_tvaudio(struct cx88_core *core) cx88_set_tvaudio(core); /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */ - cx_write(MO_AUDD_LNGTH, 128); /* fifo size */ - cx_write(MO_AUDR_LNGTH, 128); /* fifo size */ - cx_write(MO_AUD_DMACNTRL, 0x03); /* need audio fifo */ +/* + This should be needed only on cx88-alsa. It seems that some cx88 chips have + bugs and does require DMA enabled for it to work. + */ + cx88_start_audio_dma(core); return 0; } + + int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm) { u32 fsc8; @@ -1063,7 +1037,7 @@ struct cx88_core* cx88_core_get(struct pci_dev *pci) struct list_head *item; int i; - down(&devlist); + mutex_lock(&devlist); list_for_each(item,&cx88_devlist) { core = list_entry(item, struct cx88_core, devlist); if (pci->bus->number != core->pci_bus) @@ -1074,14 +1048,13 @@ struct cx88_core* cx88_core_get(struct pci_dev *pci) if (0 != get_ressources(core,pci)) goto fail_unlock; atomic_inc(&core->refcount); - up(&devlist); + mutex_unlock(&devlist); return core; } - core = kmalloc(sizeof(*core),GFP_KERNEL); + core = kzalloc(sizeof(*core),GFP_KERNEL); if (NULL == core) goto fail_unlock; - memset(core,0,sizeof(*core)); atomic_inc(&core->refcount); core->pci_bus = pci->bus->number; core->pci_slot = PCI_SLOT(pci->devfn); @@ -1150,13 +1123,13 @@ struct cx88_core* cx88_core_get(struct pci_dev *pci) cx88_card_setup(core); cx88_ir_init(core,pci); - up(&devlist); + mutex_unlock(&devlist); return core; fail_free: kfree(core); fail_unlock: - up(&devlist); + mutex_unlock(&devlist); return NULL; } @@ -1168,20 +1141,19 @@ void cx88_core_put(struct cx88_core *core, struct pci_dev *pci) if (!atomic_dec_and_test(&core->refcount)) return; - down(&devlist); + mutex_lock(&devlist); cx88_ir_fini(core); if (0 == core->i2c_rc) i2c_bit_del_bus(&core->i2c_adap); list_del(&core->devlist); iounmap(core->lmmio); cx88_devcount--; - up(&devlist); + mutex_unlock(&devlist); kfree(core); } /* ------------------------------------------------------------------ */ -EXPORT_SYMBOL(cx88_print_ioctl); EXPORT_SYMBOL(cx88_print_irqbits); EXPORT_SYMBOL(cx88_core_irq); @@ -1204,6 +1176,8 @@ EXPORT_SYMBOL(cx88_set_scale); EXPORT_SYMBOL(cx88_vdev_init); EXPORT_SYMBOL(cx88_core_get); EXPORT_SYMBOL(cx88_core_put); +EXPORT_SYMBOL(cx88_start_audio_dma); +EXPORT_SYMBOL(cx88_stop_audio_dma); /* * Local variables: diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 9cce91ec334..e48aa3f6e50 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -3,7 +3,7 @@ * device driver for Conexant 2388x based TV cards * MPEG Transport Stream (DVB) routines * - * (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au> + * (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] * * This program is free software; you can redistribute it and/or modify @@ -31,10 +31,14 @@ #include "cx88.h" #include "dvb-pll.h" +#include <media/v4l2-common.h> #ifdef HAVE_MT352 # include "mt352.h" # include "mt352_priv.h" +# ifdef HAVE_VP3054_I2C +# include "cx88-vp3054-i2c.h" +# endif #endif #ifdef HAVE_CX22702 # include "cx22702.h" @@ -48,6 +52,9 @@ #ifdef HAVE_NXT200X # include "nxt200x.h" #endif +#ifdef HAVE_CX24123 +# include "cx24123.h" +#endif MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); @@ -125,6 +132,27 @@ static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe) return 0; } +static int dvico_dual_demod_init(struct dvb_frontend *fe) +{ + static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x38 }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; + static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + + return 0; +} + static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe) { static u8 clock_config [] = { 0x89, 0x38, 0x39 }; @@ -172,6 +200,98 @@ static struct mt352_config dntv_live_dvbt_config = { .demod_init = dntv_live_dvbt_demod_init, .pll_set = mt352_pll_set, }; + +static struct mt352_config dvico_fusionhdtv_dual = { + .demod_address = 0x0F, + .demod_init = dvico_dual_demod_init, + .pll_set = mt352_pll_set, +}; + +#ifdef HAVE_VP3054_I2C +static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) +{ + static u8 clock_config [] = { 0x89, 0x38, 0x38 }; + static u8 reset [] = { 0x50, 0x80 }; + static u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 }; + static u8 agc_cfg [] = { 0x67, 0x10, 0x20, 0x00, 0xFF, 0xFF, + 0x00, 0xFF, 0x00, 0x40, 0x40 }; + static u8 dntv_extra[] = { 0xB5, 0x7A }; + static u8 capt_range_cfg[] = { 0x75, 0x32 }; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(2000); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + udelay(2000); + mt352_write(fe, dntv_extra, sizeof(dntv_extra)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + + return 0; +} + +static int philips_fmd1216_pll_init(struct dvb_frontend *fe) +{ + struct cx8802_dev *dev= fe->dvb->priv; + + /* this message is to set up ATC and ALC */ + static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0xa0 }; + struct i2c_msg msg = + { .addr = dev->core->pll_addr, .flags = 0, + .buf = fmd1216_init, .len = sizeof(fmd1216_init) }; + int err; + + if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { + if (err < 0) + return err; + else + return -EREMOTEIO; + } + + return 0; +} + +static int dntv_live_dvbt_pro_pll_set(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params, + u8* pllbuf) +{ + struct cx8802_dev *dev= fe->dvb->priv; + struct i2c_msg msg = + { .addr = dev->core->pll_addr, .flags = 0, + .buf = pllbuf+1, .len = 4 }; + int err; + + /* Switch PLL to DVB mode */ + err = philips_fmd1216_pll_init(fe); + if (err) + return err; + + /* Tune PLL */ + pllbuf[0] = dev->core->pll_addr << 1; + dvb_pll_configure(dev->core->pll_desc, pllbuf+1, + params->frequency, + params->u.ofdm.bandwidth); + if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { + printk(KERN_WARNING "cx88-dvb: %s error " + "(addr %02x <- %02x, err = %i)\n", + __FUNCTION__, pllbuf[0], pllbuf[1], err); + if (err < 0) + return err; + else + return -EREMOTEIO; + } + + return 0; +} + +static struct mt352_config dntv_live_dvbt_pro_config = { + .demod_address = 0x0f, + .no_tuner = 1, + .demod_init = dntv_live_dvbt_pro_demod_init, + .pll_set = dntv_live_dvbt_pro_pll_set, +}; +#endif #endif #ifdef HAVE_CX22702 @@ -188,6 +308,12 @@ static struct cx22702_config hauppauge_novat_config = { .pll_address = 0x61, .pll_desc = &dvb_pll_thomson_dtt759x, }; +static struct cx22702_config hauppauge_hvr1100_config = { + .demod_address = 0x63, + .output_mode = CX22702_SERIAL_OUTPUT, + .pll_address = 0x61, + .pll_desc = &dvb_pll_fmd1216me, +}; #endif #ifdef HAVE_OR51132 @@ -314,6 +440,40 @@ static struct nxt200x_config ati_hdtvwonder = { }; #endif +#ifdef HAVE_CX24123 +static int cx24123_set_ts_param(struct dvb_frontend* fe, + int is_punctured) +{ + struct cx8802_dev *dev= fe->dvb->priv; + dev->ts_gen_cntrl = 0x2; + return 0; +} + +static void cx24123_enable_lnb_voltage(struct dvb_frontend* fe, int on) +{ + struct cx8802_dev *dev= fe->dvb->priv; + struct cx88_core *core = dev->core; + + if (on) + cx_write(MO_GP0_IO, 0x000006f9); + else + cx_write(MO_GP0_IO, 0x000006fB); +} + +static struct cx24123_config hauppauge_novas_config = { + .demod_address = 0x55, + .use_isl6421 = 1, + .set_ts_params = cx24123_set_ts_param, +}; + +static struct cx24123_config kworld_dvbs_100_config = { + .demod_address = 0x15, + .use_isl6421 = 0, + .set_ts_params = cx24123_set_ts_param, + .enable_lnb_voltage = cx24123_enable_lnb_voltage, +}; +#endif + static int dvb_register(struct cx8802_dev *dev) { /* init struct videobuf_dvb */ @@ -329,10 +489,16 @@ static int dvb_register(struct cx8802_dev *dev) break; case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: case CX88_BOARD_CONEXANT_DVB_T1: + case CX88_BOARD_KWORLD_DVB_T_CX22702: case CX88_BOARD_WINFAST_DTV1000: dev->dvb.frontend = cx22702_attach(&connexant_refboard_config, &dev->core->i2c_adap); break; + case CX88_BOARD_HAUPPAUGE_HVR1100: + case CX88_BOARD_HAUPPAUGE_HVR1100LP: + dev->dvb.frontend = cx22702_attach(&hauppauge_hvr1100_config, + &dev->core->i2c_adap); + break; #endif #ifdef HAVE_MT352 case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: @@ -355,6 +521,24 @@ static int dvb_register(struct cx8802_dev *dev) dev->dvb.frontend = mt352_attach(&dntv_live_dvbt_config, &dev->core->i2c_adap); break; + case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: +#ifdef HAVE_VP3054_I2C + dev->core->pll_addr = 0x61; + dev->core->pll_desc = &dvb_pll_fmd1216me; + dev->dvb.frontend = mt352_attach(&dntv_live_dvbt_pro_config, + &((struct vp3054_i2c_state *)dev->card_priv)->adap); +#else + printk("%s: built without vp3054 support\n", dev->core->name); +#endif + break; + case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: + /* The tin box says DEE1601, but it seems to be DTT7579 + * compatible, with a slightly different MT352 AGC gain. */ + dev->core->pll_addr = 0x61; + dev->core->pll_desc = &dvb_pll_thomson_dtt7579; + dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv_dual, + &dev->core->i2c_adap); + break; #endif #ifdef HAVE_OR51132 case CX88_BOARD_PCHDTV_HD3000: @@ -393,7 +577,7 @@ static int dvb_register(struct cx8802_dev *dev) cx_set(MO_GP0_IO, 9); mdelay(200); dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_thomson_dtt7611; + dev->core->pll_desc = &dvb_pll_thomson_dtt761x; dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_3_gold, &dev->core->i2c_adap); } @@ -421,6 +605,17 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); break; #endif +#ifdef HAVE_CX24123 + case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: + case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: + dev->dvb.frontend = cx24123_attach(&hauppauge_novas_config, + &dev->core->i2c_adap); + break; + case CX88_BOARD_KWORLD_DVBS_100: + dev->dvb.frontend = cx24123_attach(&kworld_dvbs_100_config, + &dev->core->i2c_adap); + break; +#endif default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->core->name); @@ -439,9 +634,6 @@ static int dvb_register(struct cx8802_dev *dev) /* Put the analog decoder in standby to keep it quiet */ cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); - /* Put the analog decoder in standby to keep it quiet */ - cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); - /* register everything */ return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev); } @@ -465,10 +657,9 @@ static int __devinit dvb_probe(struct pci_dev *pci_dev, goto fail_core; err = -ENOMEM; - dev = kmalloc(sizeof(*dev),GFP_KERNEL); + dev = kzalloc(sizeof(*dev),GFP_KERNEL); if (NULL == dev) goto fail_core; - memset(dev,0,sizeof(*dev)); dev->pci = pci_dev; dev->core = core; @@ -476,6 +667,12 @@ static int __devinit dvb_probe(struct pci_dev *pci_dev, if (0 != err) goto fail_free; +#ifdef HAVE_VP3054_I2C + err = vp3054_i2c_probe(dev); + if (0 != err) + goto fail_free; +#endif + /* dvb stuff */ printk("%s/2: cx2388x based dvb card\n", core->name); videobuf_queue_init(&dev->dvb.dvbq, &dvb_qops, @@ -487,6 +684,9 @@ static int __devinit dvb_probe(struct pci_dev *pci_dev, err = dvb_register(dev); if (0 != err) goto fail_fini; + + /* Maintain a reference to cx88-video can query the 8802 device. */ + core->dvbdev = dev; return 0; fail_fini: @@ -502,9 +702,16 @@ static void __devexit dvb_remove(struct pci_dev *pci_dev) { struct cx8802_dev *dev = pci_get_drvdata(pci_dev); + /* Destroy any 8802 reference. */ + dev->core->dvbdev = NULL; + /* dvb */ videobuf_dvb_unregister(&dev->dvb); +#ifdef HAVE_VP3054_I2C + vp3054_i2c_remove(dev); +#endif + /* common */ cx8802_fini_common(dev); cx88_core_put(dev->core,dev->pci); diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index 9790d412f19..f720901e963 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -30,6 +30,7 @@ #include <asm/io.h> #include "cx88.h" +#include <media/v4l2-common.h> static unsigned int i2c_debug = 0; module_param(i2c_debug, int, 0644); @@ -94,7 +95,7 @@ static int attach_inform(struct i2c_client *client) struct cx88_core *core = i2c_get_adapdata(client->adapter); dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", - client->driver->name, client->addr, client->name); + client->driver->driver.name, client->addr, client->name); if (!client->driver->command) return 0; @@ -135,7 +136,17 @@ void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg) { if (0 != core->i2c_rc) return; - i2c_clients_command(&core->i2c_adap, cmd, arg); + + if (core->dvbdev) { + if (core->dvbdev->dvb.frontend->ops->i2c_gate_ctrl) + core->dvbdev->dvb.frontend->ops->i2c_gate_ctrl(core->dvbdev->dvb.frontend, 1); + + i2c_clients_command(&core->i2c_adap, cmd, arg); + + if (core->dvbdev->dvb.frontend->ops->i2c_gate_ctrl) + core->dvbdev->dvb.frontend->ops->i2c_gate_ctrl(core->dvbdev->dvb.frontend, 0); + } else + i2c_clients_command(&core->i2c_adap, cmd, arg); } static struct i2c_algo_bit_data cx8800_i2c_algo_template = { diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 38b12ebaa49..da2ad5c4b55 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -5,7 +5,7 @@ * * Copyright (c) 2003 Pavel Machek * Copyright (c) 2004 Gerd Knorr - * Copyright (c) 2004 Chris Pascoe + * Copyright (c) 2004, 2005 Chris Pascoe * * 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 @@ -29,9 +29,8 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <media/ir-common.h> - #include "cx88.h" +#include <media/ir-common.h> /* ---------------------------------------------------------------------- */ @@ -258,6 +257,114 @@ static IR_KEYTAB_TYPE ir_codes_cinergy_1400[IR_KEYTAB_SIZE] = { /* ---------------------------------------------------------------------- */ +/* AVERTV STUDIO 303 Remote */ +static IR_KEYTAB_TYPE ir_codes_avertv_303[IR_KEYTAB_SIZE] = { + [ 0x2a ] = KEY_KP1, + [ 0x32 ] = KEY_KP2, + [ 0x3a ] = KEY_KP3, + [ 0x4a ] = KEY_KP4, + [ 0x52 ] = KEY_KP5, + [ 0x5a ] = KEY_KP6, + [ 0x6a ] = KEY_KP7, + [ 0x72 ] = KEY_KP8, + [ 0x7a ] = KEY_KP9, + [ 0x0e ] = KEY_KP0, + + [ 0x02 ] = KEY_POWER, + [ 0x22 ] = KEY_VIDEO, + [ 0x42 ] = KEY_AUDIO, + [ 0x62 ] = KEY_ZOOM, + [ 0x0a ] = KEY_TV, + [ 0x12 ] = KEY_CD, + [ 0x1a ] = KEY_TEXT, + + [ 0x16 ] = KEY_SUBTITLE, + [ 0x1e ] = KEY_REWIND, + [ 0x06 ] = KEY_PRINT, + + [ 0x2e ] = KEY_SEARCH, + [ 0x36 ] = KEY_SLEEP, + [ 0x3e ] = KEY_SHUFFLE, + [ 0x26 ] = KEY_MUTE, + + [ 0x4e ] = KEY_RECORD, + [ 0x56 ] = KEY_PAUSE, + [ 0x5e ] = KEY_STOP, + [ 0x46 ] = KEY_PLAY, + + [ 0x6e ] = KEY_RED, + [ 0x0b ] = KEY_GREEN, + [ 0x66 ] = KEY_YELLOW, + [ 0x03 ] = KEY_BLUE, + + [ 0x76 ] = KEY_LEFT, + [ 0x7e ] = KEY_RIGHT, + [ 0x13 ] = KEY_DOWN, + [ 0x1b ] = KEY_UP, +}; + +/* ---------------------------------------------------------------------- */ + +/* DigitalNow DNTV Live! DVB-T Pro Remote */ +static IR_KEYTAB_TYPE ir_codes_dntv_live_dvbt_pro[IR_KEYTAB_SIZE] = { + [ 0x16 ] = KEY_POWER, + [ 0x5b ] = KEY_HOME, + + [ 0x55 ] = KEY_TV, /* live tv */ + [ 0x58 ] = KEY_TUNER, /* digital Radio */ + [ 0x5a ] = KEY_RADIO, /* FM radio */ + [ 0x59 ] = KEY_DVD, /* dvd menu */ + [ 0x03 ] = KEY_1, + [ 0x01 ] = KEY_2, + [ 0x06 ] = KEY_3, + [ 0x09 ] = KEY_4, + [ 0x1d ] = KEY_5, + [ 0x1f ] = KEY_6, + [ 0x0d ] = KEY_7, + [ 0x19 ] = KEY_8, + [ 0x1b ] = KEY_9, + [ 0x0c ] = KEY_CANCEL, + [ 0x15 ] = KEY_0, + [ 0x4a ] = KEY_CLEAR, + [ 0x13 ] = KEY_BACK, + [ 0x00 ] = KEY_TAB, + [ 0x4b ] = KEY_UP, + [ 0x4e ] = KEY_LEFT, + [ 0x4f ] = KEY_OK, + [ 0x52 ] = KEY_RIGHT, + [ 0x51 ] = KEY_DOWN, + [ 0x1e ] = KEY_VOLUMEUP, + [ 0x0a ] = KEY_VOLUMEDOWN, + [ 0x02 ] = KEY_CHANNELDOWN, + [ 0x05 ] = KEY_CHANNELUP, + [ 0x11 ] = KEY_RECORD, + [ 0x14 ] = KEY_PLAY, + [ 0x4c ] = KEY_PAUSE, + [ 0x1a ] = KEY_STOP, + [ 0x40 ] = KEY_REWIND, + [ 0x12 ] = KEY_FASTFORWARD, + [ 0x41 ] = KEY_PREVIOUSSONG, /* replay |< */ + [ 0x42 ] = KEY_NEXTSONG, /* skip >| */ + [ 0x54 ] = KEY_CAMERA, /* capture */ + [ 0x50 ] = KEY_LANGUAGE, /* sap */ + [ 0x47 ] = KEY_TV2, /* pip */ + [ 0x4d ] = KEY_SCREEN, + [ 0x43 ] = KEY_SUBTITLE, + [ 0x10 ] = KEY_MUTE, + [ 0x49 ] = KEY_AUDIO, /* l/r */ + [ 0x07 ] = KEY_SLEEP, + [ 0x08 ] = KEY_VIDEO, /* a/v */ + [ 0x0e ] = KEY_PREVIOUS, /* recall */ + [ 0x45 ] = KEY_ZOOM, /* zoom + */ + [ 0x46 ] = KEY_ANGLE, /* zoom - */ + [ 0x56 ] = KEY_RED, + [ 0x57 ] = KEY_GREEN, + [ 0x5c ] = KEY_YELLOW, + [ 0x5d ] = KEY_BLUE, +}; + +/* ---------------------------------------------------------------------- */ + struct cx88_IR { struct cx88_core *core; struct input_dev *input; @@ -266,7 +373,7 @@ struct cx88_IR { char phys[32]; /* sample from gpio pin 16 */ - int sampling; + u32 sampling; u32 samples[16]; int scount; unsigned long release; @@ -384,10 +491,13 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: ir_codes = ir_codes_cinergy_1400; ir_type = IR_TYPE_PD; - ir->sampling = 1; + ir->sampling = 0xeb04; /* address */ break; case CX88_BOARD_HAUPPAUGE: case CX88_BOARD_HAUPPAUGE_DVB_T1: + case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: + case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: + case CX88_BOARD_HAUPPAUGE_HVR1100: ir_codes = ir_codes_hauppauge_new; ir_type = IR_TYPE_RC5; ir->sampling = 1; @@ -427,6 +537,19 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->mask_keyup = 0x40; ir->polling = 1; /* ms */ break; + case CX88_BOARD_AVERTV_303: + case CX88_BOARD_AVERTV_STUDIO_303: + ir_codes = ir_codes_avertv_303; + ir->gpio_addr = MO_GP2_IO; + ir->mask_keycode = 0xfb; + ir->mask_keydown = 0x02; + ir->polling = 50; /* ms */ + break; + case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: + ir_codes = ir_codes_dntv_live_dvbt_pro; + ir_type = IR_TYPE_PD; + ir->sampling = 0xff00; /* address */ + break; } if (NULL == ir_codes) { @@ -453,7 +576,6 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) input_dev->id.product = pci->device; } input_dev->cdev.dev = &pci->dev; - /* record handles to ourself */ ir->core = core; core->ir = ir; @@ -485,6 +607,10 @@ int cx88_ir_fini(struct cx88_core *core) if (NULL == ir) return 0; + if (ir->sampling) { + cx_write(MO_DDSCFG_IO, 0x0); + core->pci_irqmask &= ~(1 << 18); + } if (ir->polling) { del_timer(&ir->timer); flush_scheduled_work(); @@ -536,6 +662,7 @@ void cx88_ir_irq(struct cx88_core *core) /* decode it */ switch (core->board) { case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: + case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: ircode = ir_decode_pulsedistance(ir->samples, ir->scount, 1, 4); if (ircode == 0xffffffff) { /* decoding error */ @@ -551,7 +678,7 @@ void cx88_ir_irq(struct cx88_core *core) break; } - if ((ircode & 0xffff) != 0xeb04) { /* wrong address */ + if ((ircode & 0xffff) != (ir->sampling & 0xffff)) { /* wrong address */ ir_dprintk("pulse distance decoded wrong address\n"); break; } @@ -568,6 +695,9 @@ void cx88_ir_irq(struct cx88_core *core) break; case CX88_BOARD_HAUPPAUGE: case CX88_BOARD_HAUPPAUGE_DVB_T1: + case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: + case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: + case CX88_BOARD_HAUPPAUGE_HVR1100: ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7); ir_dprintk("biphase decoded: %x\n", ircode); if ((ircode & 0xfffff000) != 0x3000) @@ -586,7 +716,6 @@ void cx88_ir_irq(struct cx88_core *core) MODULE_AUTHOR("Gerd Knorr, Pavel Machek, Chris Pascoe"); MODULE_DESCRIPTION("input driver for cx88 GPIO-based IR remote controls"); MODULE_LICENSE("GPL"); - /* * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 35e6d0c2b87..c79cc1d2bf8 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -78,6 +78,11 @@ static int cx8802_start_dma(struct cx8802_dev *dev, case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: cx_write(TS_SOP_STAT, 1<<13); break; + case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: + case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: + cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */ + udelay(100); + break; default: cx_write(TS_SOP_STAT, 0x00); break; diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c index 6d9bec1c583..da8d97ce0c4 100644 --- a/drivers/media/video/cx88/cx88-tvaudio.c +++ b/drivers/media/video/cx88/cx88-tvaudio.c @@ -60,6 +60,11 @@ static unsigned int audio_debug = 0; module_param(audio_debug, int, 0644); MODULE_PARM_DESC(audio_debug, "enable debug messages [audio]"); +static unsigned int always_analog = 0; +module_param(always_analog,int,0644); +MODULE_PARM_DESC(always_analog,"force analog audio out"); + + #define dprintk(fmt, arg...) if (audio_debug) \ printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg) @@ -119,13 +124,10 @@ static void set_audio_registers(struct cx88_core *core, const struct rlist *l) static void set_audio_start(struct cx88_core *core, u32 mode) { - // mute + /* mute */ cx_write(AUD_VOL_CTL, (1 << 6)); - // start programming - cx_write(MO_AUD_DMACNTRL, 0x0000); - msleep(100); - //cx_write(AUD_CTL, 0x0000); + /* start programming */ cx_write(AUD_INIT, mode); cx_write(AUD_INIT_LD, 0x0001); cx_write(AUD_SOFT_RESET, 0x0001); @@ -135,25 +137,37 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl) { u32 volume; +#ifndef USING_CX88_ALSA + /* restart dma; This avoids buzz in NICAM and is good in others */ + cx88_stop_audio_dma(core); +#endif + cx_write(AUD_RATE_THRES_DMD, 0x000000C0); +#ifndef USING_CX88_ALSA + cx88_start_audio_dma(core); +#endif + if (cx88_boards[core->board].blackbird) { - // sets sound input from external adc - cx_set(AUD_CTL, EN_I2SIN_ENABLE); - //cx_write(AUD_I2SINPUTCNTL, 0); + /* sets sound input from external adc */ + if (core->board == CX88_BOARD_HAUPPAUGE_ROSLYN) + cx_clear(AUD_CTL, EN_I2SIN_ENABLE); + else + cx_set(AUD_CTL, EN_I2SIN_ENABLE); + cx_write(AUD_I2SINPUTCNTL, 4); cx_write(AUD_BAUDRATE, 1); - // 'pass-thru mode': this enables the i2s output to the mpeg encoder + /* 'pass-thru mode': this enables the i2s output to the mpeg encoder */ cx_set(AUD_CTL, EN_I2SOUT_ENABLE); cx_write(AUD_I2SOUTPUTCNTL, 1); cx_write(AUD_I2SCNTL, 0); - //cx_write(AUD_APB_IN_RATE_ADJ, 0); - } else { + /* cx_write(AUD_APB_IN_RATE_ADJ, 0); */ + } + if ((always_analog) || (!cx88_boards[core->board].blackbird)) { ctl |= EN_DAC_ENABLE; cx_write(AUD_CTL, ctl); } /* finish programming */ cx_write(AUD_SOFT_RESET, 0x0000); - cx_write(MO_AUD_DMACNTRL, 0x0003); /* unmute */ volume = cx_sread(SHADOW_AUD_VOL_CTL); @@ -313,7 +327,6 @@ static void set_audio_standard_NICAM(struct cx88_core *core, u32 mode) {AUD_RATE_ADJ3, 0x00000100}, {AUD_RATE_ADJ4, 0x00000400}, {AUD_RATE_ADJ5, 0x00001000}, - //{ AUD_DMD_RA_DDS, 0x00c0d5ce }, {AUD_ERRLOGPERIOD_R, 0x00000fff}, {AUD_ERRINTRPTTHSHLD1_R, 0x000003ff}, {AUD_ERRINTRPTTHSHLD2_R, 0x000000ff}, @@ -351,12 +364,12 @@ static void set_audio_standard_NICAM(struct cx88_core *core, u32 mode) set_audio_registers(core, nicam_l); break; case WW_I: - dprintk("%s PAL-I NICAM (status: devel)\n", __FUNCTION__); + dprintk("%s PAL-I NICAM (status: known-good)\n", __FUNCTION__); set_audio_registers(core, nicam_bgdki_common); set_audio_registers(core, nicam_i); break; default: - dprintk("%s PAL-BGDK NICAM (status: unknown)\n", __FUNCTION__); + dprintk("%s PAL-BGDK NICAM (status: known-good)\n", __FUNCTION__); set_audio_registers(core, nicam_bgdki_common); set_audio_registers(core, nicam_default); break; @@ -715,8 +728,7 @@ int cx88_detect_nicam(struct cx88_core *core) /* if bit1=1 then nicam is detected */ j += ((cx_read(AUD_NICAM_STATUS2) & 0x02) >> 1); - /* 3x detected: absolutly sure now */ - if (j == 3) { + if (j == 1) { dprintk("nicam is detected.\n"); return 1; } diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 24a48f8a48c..073494ceab0 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -33,6 +33,7 @@ #include <asm/div64.h> #include "cx88.h" +#include <media/v4l2-common.h> /* Include V4L1 specific functions. Should be removed soon */ #include <linux/videodev.h> @@ -240,7 +241,7 @@ static struct cx88_ctrl cx8800_ctls[] = { .minimum = 0, .maximum = 0xff, .step = 1, - .default_value = 0, + .default_value = 0x3f, .type = V4L2_CTRL_TYPE_INTEGER, }, .off = 0, @@ -271,7 +272,7 @@ static struct cx88_ctrl cx8800_ctls[] = { .minimum = 0, .maximum = 0xff, .step = 1, - .default_value = 0, + .default_value = 0x7f, .type = V4L2_CTRL_TYPE_INTEGER, }, .off = 0, @@ -285,6 +286,7 @@ static struct cx88_ctrl cx8800_ctls[] = { .name = "Mute", .minimum = 0, .maximum = 1, + .default_value = 1, .type = V4L2_CTRL_TYPE_BOOLEAN, }, .reg = AUD_VOL_CTL, @@ -298,7 +300,7 @@ static struct cx88_ctrl cx8800_ctls[] = { .minimum = 0, .maximum = 0x3f, .step = 1, - .default_value = 0, + .default_value = 0x1f, .type = V4L2_CTRL_TYPE_INTEGER, }, .reg = AUD_VOL_CTL, @@ -748,10 +750,9 @@ static int video_open(struct inode *inode, struct file *file) minor,radio,v4l2_type_names[type]); /* allocate + initialize per filehandle data */ - fh = kmalloc(sizeof(*fh),GFP_KERNEL); + fh = kzalloc(sizeof(*fh),GFP_KERNEL); if (NULL == fh) return -ENOMEM; - memset(fh,0,sizeof(*fh)); file->private_data = fh; fh->dev = dev; fh->radio = radio; @@ -917,6 +918,9 @@ static int get_control(struct cx88_core *core, struct v4l2_control *ctl) ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift; break; } + printk("get_control id=0x%X reg=0x%02x val=0x%02x (mask 0x%02x)%s\n", + ctl->id, c->reg, ctl->value, + c->mask, c->sreg ? " [shadowed]" : ""); return 0; } @@ -925,13 +929,13 @@ static int set_control(struct cx88_core *core, struct v4l2_control *ctl) { /* struct cx88_core *core = dev->core; */ struct cx88_ctrl *c = NULL; - u32 v_sat_value; - u32 value; + u32 value,mask; int i; - - for (i = 0; i < CX8800_CTLS; i++) - if (cx8800_ctls[i].v.id == ctl->id) + for (i = 0; i < CX8800_CTLS; i++) { + if (cx8800_ctls[i].v.id == ctl->id) { c = &cx8800_ctls[i]; + } + } if (NULL == c) return -EINVAL; @@ -939,6 +943,7 @@ static int set_control(struct cx88_core *core, struct v4l2_control *ctl) ctl->value = c->v.minimum; if (ctl->value > c->v.maximum) ctl->value = c->v.maximum; + mask=c->mask; switch (ctl->id) { case V4L2_CID_AUDIO_BALANCE: value = (ctl->value < 0x40) ? (0x40 - ctl->value) : ctl->value; @@ -948,56 +953,44 @@ static int set_control(struct cx88_core *core, struct v4l2_control *ctl) break; case V4L2_CID_SATURATION: /* special v_sat handling */ - v_sat_value = ctl->value - (0x7f - 0x5a); - if (v_sat_value > 0xff) - v_sat_value = 0xff; - if (v_sat_value < 0x00) - v_sat_value = 0x00; - cx_andor(MO_UV_SATURATION, 0xff00, v_sat_value << 8); - /* fall through to default route for u_sat */ + + value = ((ctl->value - c->off) << c->shift) & c->mask; + + if (core->tvnorm->id & V4L2_STD_SECAM) { + /* For SECAM, both U and V sat should be equal */ + value=value<<8|value; + } else { + /* Keeps U Saturation proportional to V Sat */ + value=(value*0x5a)/0x7f<<8|value; + } + mask=0xffff; + break; default: value = ((ctl->value - c->off) << c->shift) & c->mask; break; } - dprintk(1,"set_control id=0x%X reg=0x%x val=0x%x%s\n", - ctl->id, c->reg, value, c->sreg ? " [shadowed]" : ""); + printk("set_control id=0x%X reg=0x%02x val=0x%02x (mask 0x%02x)%s\n", + ctl->id, c->reg, value, + mask, c->sreg ? " [shadowed]" : ""); if (c->sreg) { - cx_sandor(c->sreg, c->reg, c->mask, value); + cx_sandor(c->sreg, c->reg, mask, value); } else { - cx_andor(c->reg, c->mask, value); + cx_andor(c->reg, mask, value); } return 0; } -/* static void init_controls(struct cx8800_dev *dev) */ static void init_controls(struct cx88_core *core) { - static struct v4l2_control mute = { - .id = V4L2_CID_AUDIO_MUTE, - .value = 1, - }; - static struct v4l2_control volume = { - .id = V4L2_CID_AUDIO_VOLUME, - .value = 0x3f, - }; - static struct v4l2_control hue = { - .id = V4L2_CID_HUE, - .value = 0x80, - }; - static struct v4l2_control contrast = { - .id = V4L2_CID_CONTRAST, - .value = 0x80, - }; - static struct v4l2_control brightness = { - .id = V4L2_CID_BRIGHTNESS, - .value = 0x80, - }; + struct v4l2_control ctrl; + int i; - set_control(core,&mute); - set_control(core,&volume); - set_control(core,&hue); - set_control(core,&contrast); - set_control(core,&brightness); + for (i = 0; i < CX8800_CTLS; i++) { + ctrl.id=cx8800_ctls[i].v.id; + ctrl.value=cx8800_ctls[i].v.default_value + +cx8800_ctls[i].off; + set_control(core, &ctrl); + } } /* ------------------------------------------------------------------ */ @@ -1125,7 +1118,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, int err; if (video_debug > 1) - cx88_print_ioctl(core->name,cmd); + v4l_print_ioctl(core->name,cmd); switch (cmd) { /* --- capabilities ------------------------------------------ */ @@ -1261,7 +1254,7 @@ int cx88_do_ioctl(struct inode *inode, struct file *file, int radio, dprintk( 1, "CORE IOCTL: 0x%x\n", cmd ); if (video_debug > 1) - cx88_print_ioctl(core->name,cmd); + v4l_print_ioctl(core->name,cmd); switch (cmd) { /* ---------- tv norms ---------- */ @@ -1481,7 +1474,7 @@ static int radio_do_ioctl(struct inode *inode, struct file *file, struct cx88_core *core = dev->core; if (video_debug > 1) - cx88_print_ioctl(core->name,cmd); + v4l_print_ioctl(core->name,cmd); switch (cmd) { case VIDIOC_QUERYCAP: @@ -1740,6 +1733,7 @@ static struct file_operations video_fops = .poll = video_poll, .mmap = video_mmap, .ioctl = video_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -1767,6 +1761,7 @@ static struct file_operations radio_fops = .open = video_open, .release = video_release, .ioctl = radio_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -1813,10 +1808,9 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, struct cx88_core *core; int err; - dev = kmalloc(sizeof(*dev),GFP_KERNEL); + dev = kzalloc(sizeof(*dev),GFP_KERNEL); if (NULL == dev) return -ENOMEM; - memset(dev,0,sizeof(*dev)); /* pci init */ dev->pci = pci_dev; @@ -1928,8 +1922,8 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, /* initial device configuration */ down(&core->lock); - init_controls(core); cx88_set_tvnorm(core,tvnorms); + init_controls(core); video_mux(core,0); up(&core->lock); diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.c b/drivers/media/video/cx88/cx88-vp3054-i2c.c new file mode 100644 index 00000000000..751a754a45e --- /dev/null +++ b/drivers/media/video/cx88/cx88-vp3054-i2c.c @@ -0,0 +1,177 @@ +/* + + cx88-vp3054-i2c.c -- support for the secondary I2C bus of the + DNTV Live! DVB-T Pro (VP-3054), wired as: + GPIO[0] -> SCL, GPIO[1] -> SDA + + (c) 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> + + 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/moduleparam.h> +#include <linux/init.h> + +#include <asm/io.h> + +#include "cx88.h" +#include "cx88-vp3054-i2c.h" + + +MODULE_DESCRIPTION("driver for cx2388x VP3054 design"); +MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); +MODULE_LICENSE("GPL"); + +/* ----------------------------------------------------------------------- */ + +static void vp3054_bit_setscl(void *data, int state) +{ + struct cx8802_dev *dev = data; + struct cx88_core *core = dev->core; + struct vp3054_i2c_state *vp3054_i2c = dev->card_priv; + + if (state) { + vp3054_i2c->state |= 0x0001; /* SCL high */ + vp3054_i2c->state &= ~0x0100; /* external pullup */ + } else { + vp3054_i2c->state &= ~0x0001; /* SCL low */ + vp3054_i2c->state |= 0x0100; /* drive pin */ + } + cx_write(MO_GP0_IO, 0x010000 | vp3054_i2c->state); + cx_read(MO_GP0_IO); +} + +static void vp3054_bit_setsda(void *data, int state) +{ + struct cx8802_dev *dev = data; + struct cx88_core *core = dev->core; + struct vp3054_i2c_state *vp3054_i2c = dev->card_priv; + + if (state) { + vp3054_i2c->state |= 0x0002; /* SDA high */ + vp3054_i2c->state &= ~0x0200; /* tristate pin */ + } else { + vp3054_i2c->state &= ~0x0002; /* SDA low */ + vp3054_i2c->state |= 0x0200; /* drive pin */ + } + cx_write(MO_GP0_IO, 0x020000 | vp3054_i2c->state); + cx_read(MO_GP0_IO); +} + +static int vp3054_bit_getscl(void *data) +{ + struct cx8802_dev *dev = data; + struct cx88_core *core = dev->core; + u32 state; + + state = cx_read(MO_GP0_IO); + return (state & 0x01) ? 1 : 0; +} + +static int vp3054_bit_getsda(void *data) +{ + struct cx8802_dev *dev = data; + struct cx88_core *core = dev->core; + u32 state; + + state = cx_read(MO_GP0_IO); + return (state & 0x02) ? 1 : 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_algo_bit_data vp3054_i2c_algo_template = { + .setsda = vp3054_bit_setsda, + .setscl = vp3054_bit_setscl, + .getsda = vp3054_bit_getsda, + .getscl = vp3054_bit_getscl, + .udelay = 16, + .mdelay = 10, + .timeout = 200, +}; + +/* ----------------------------------------------------------------------- */ + +static struct i2c_adapter vp3054_i2c_adap_template = { + .name = "cx2388x", + .owner = THIS_MODULE, + .id = I2C_HW_B_CX2388x, +}; + +static struct i2c_client vp3054_i2c_client_template = { + .name = "VP-3054", +}; + +int vp3054_i2c_probe(struct cx8802_dev *dev) +{ + struct cx88_core *core = dev->core; + struct vp3054_i2c_state *vp3054_i2c; + int rc; + + if (core->board != CX88_BOARD_DNTV_LIVE_DVB_T_PRO) + return 0; + + dev->card_priv = kzalloc(sizeof(*vp3054_i2c), GFP_KERNEL); + if (dev->card_priv == NULL) + return -ENOMEM; + vp3054_i2c = dev->card_priv; + + memcpy(&vp3054_i2c->adap, &vp3054_i2c_adap_template, + sizeof(vp3054_i2c->adap)); + memcpy(&vp3054_i2c->algo, &vp3054_i2c_algo_template, + sizeof(vp3054_i2c->algo)); + memcpy(&vp3054_i2c->client, &vp3054_i2c_client_template, + sizeof(vp3054_i2c->client)); + + vp3054_i2c->adap.class |= I2C_CLASS_TV_DIGITAL; + + vp3054_i2c->adap.dev.parent = &dev->pci->dev; + strlcpy(vp3054_i2c->adap.name, core->name, + sizeof(vp3054_i2c->adap.name)); + vp3054_i2c->algo.data = dev; + i2c_set_adapdata(&vp3054_i2c->adap, dev); + vp3054_i2c->adap.algo_data = &vp3054_i2c->algo; + vp3054_i2c->client.adapter = &vp3054_i2c->adap; + + vp3054_bit_setscl(dev,1); + vp3054_bit_setsda(dev,1); + + rc = i2c_bit_add_bus(&vp3054_i2c->adap); + if (0 != rc) { + printk("%s: vp3054_i2c register FAILED\n", core->name); + + kfree(dev->card_priv); + dev->card_priv = NULL; + } + + return rc; +} + +void vp3054_i2c_remove(struct cx8802_dev *dev) +{ + struct vp3054_i2c_state *vp3054_i2c = dev->card_priv; + + if (vp3054_i2c == NULL || + dev->core->board != CX88_BOARD_DNTV_LIVE_DVB_T_PRO) + return; + + i2c_bit_del_bus(&vp3054_i2c->adap); + kfree(vp3054_i2c); +} + +EXPORT_SYMBOL(vp3054_i2c_probe); +EXPORT_SYMBOL(vp3054_i2c_remove); diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.h b/drivers/media/video/cx88/cx88-vp3054-i2c.h new file mode 100644 index 00000000000..b7a0a04d242 --- /dev/null +++ b/drivers/media/video/cx88/cx88-vp3054-i2c.h @@ -0,0 +1,35 @@ +/* + + cx88-vp3054-i2c.h -- support for the secondary I2C bus of the + DNTV Live! DVB-T Pro (VP-3054), wired as: + GPIO[0] -> SCL, GPIO[1] -> SDA + + (c) 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> + + 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. + +*/ + +/* ----------------------------------------------------------------------- */ +struct vp3054_i2c_state { + struct i2c_adapter adap; + struct i2c_algo_bit_data algo; + struct i2c_client client; + u32 state; +}; + +/* ----------------------------------------------------------------------- */ +int vp3054_i2c_probe(struct cx8802_dev *dev); +void vp3054_i2c_remove(struct cx8802_dev *dev); diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index b19d3a9e229..e9fd55b57fa 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -179,6 +179,14 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_ATI_HDTVWONDER 34 #define CX88_BOARD_WINFAST_DTV1000 35 #define CX88_BOARD_AVERTV_303 36 +#define CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1 37 +#define CX88_BOARD_HAUPPAUGE_NOVASE2_S1 38 +#define CX88_BOARD_KWORLD_DVBS_100 39 +#define CX88_BOARD_HAUPPAUGE_HVR1100 40 +#define CX88_BOARD_HAUPPAUGE_HVR1100LP 41 +#define CX88_BOARD_DNTV_LIVE_DVB_T_PRO 42 +#define CX88_BOARD_KWORLD_DVB_T_CX22702 43 +#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL 44 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, @@ -280,6 +288,9 @@ struct cx88_core { unsigned int tda9887_conf; unsigned int has_radio; + /* Supported V4L _STD_ tuner formats */ + unsigned int tuner_formats; + /* config info -- dvb */ struct dvb_pll_desc *pll_desc; unsigned int pll_addr; @@ -301,6 +312,9 @@ struct cx88_core { /* various v4l controls */ u32 freq; + + /* cx88-video needs to access cx8802 for hybrid tuner pll access. */ + struct cx8802_dev *dvbdev; }; struct cx8800_dev; @@ -412,6 +426,7 @@ struct cx8802_dev { void* fe_handle; int (*fe_release)(void *handle); + void *card_priv; /* for switching modulation types */ unsigned char ts_gen_cntrl; @@ -448,7 +463,6 @@ struct cx8802_dev { extern void cx88_print_irqbits(char *name, char *tag, char **strings, u32 bits, u32 mask); -extern void cx88_print_ioctl(char *name, unsigned int cmd); extern int cx88_core_irq(struct cx88_core *core, u32 status); extern void cx88_wakeup(struct cx88_core *core, @@ -491,6 +505,10 @@ extern struct cx88_core* cx88_core_get(struct pci_dev *pci); extern void cx88_core_put(struct cx88_core *core, struct pci_dev *pci); +extern int cx88_start_audio_dma(struct cx88_core *core); +extern int cx88_stop_audio_dma(struct cx88_core *core); + + /* ----------------------------------------------------------- */ /* cx88-vbi.c */ diff --git a/drivers/media/video/dpc7146.c b/drivers/media/video/dpc7146.c index da9481198c5..2831bdd1205 100644 --- a/drivers/media/video/dpc7146.c +++ b/drivers/media/video/dpc7146.c @@ -94,12 +94,11 @@ static int dpc_probe(struct saa7146_dev* dev) struct i2c_client *client; struct list_head *item; - dpc = (struct dpc*)kmalloc(sizeof(struct dpc), GFP_KERNEL); + dpc = kzalloc(sizeof(struct dpc), GFP_KERNEL); if( NULL == dpc ) { printk("dpc_v4l2.o: dpc_probe: not enough kernel memory.\n"); return -ENOMEM; } - memset(dpc, 0x0, sizeof(struct dpc)); /* FIXME: enable i2c-port pins, video-port-pins video port pins should be enabled here ?! */ diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 57779e63f35..58f7b4194a0 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -30,6 +30,7 @@ #include <media/tuner.h> #include <media/audiochip.h> #include <media/tveeprom.h> +#include <media/v4l2-common.h> #include "msp3400.h" #include "em28xx.h" @@ -261,7 +262,6 @@ void em28xx_card_setup(struct em28xx *dev) /* request some modules */ if (dev->model == EM2820_BOARD_HAUPPAUGE_WINTV_USB_2) { struct tveeprom tv; - struct v4l2_audioout ao; #ifdef CONFIG_MODULES request_module("tveeprom"); request_module("ir-kbd-i2c"); @@ -274,12 +274,8 @@ void em28xx_card_setup(struct em28xx *dev) dev->tuner_type= tv.tuner_type; if (tv.audio_processor == AUDIO_CHIP_MSP34XX) { + dev->i2s_speed=2048000; dev->has_msp34xx=1; - memset (&ao,0,sizeof(ao)); - - ao.index=2; - ao.mode=V4L2_AUDMODE_32BITS; - em28xx_i2c_call_clients(dev, VIDIOC_S_AUDOUT, &ao); } else dev->has_msp34xx=0; } diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index d54bc012748..dff3893f32f 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -32,7 +32,7 @@ /* #define ENABLE_DEBUG_ISOC_FRAMES */ -unsigned int core_debug; +static unsigned int core_debug = 0; module_param(core_debug,int,0644); MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); @@ -41,7 +41,7 @@ MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); printk(KERN_INFO "%s %s :"fmt, \ dev->name, __FUNCTION__ , ##arg); } while (0) -unsigned int reg_debug; +static unsigned int reg_debug = 0; module_param(reg_debug,int,0644); MODULE_PARM_DESC(reg_debug,"enable debug messages [URB reg]"); @@ -50,7 +50,7 @@ MODULE_PARM_DESC(reg_debug,"enable debug messages [URB reg]"); printk(KERN_INFO "%s %s :"fmt, \ dev->name, __FUNCTION__ , ##arg); } while (0) -unsigned int isoc_debug; +static unsigned int isoc_debug = 0; module_param(isoc_debug,int,0644); MODULE_PARM_DESC(isoc_debug,"enable debug messages [isoc transfers]"); @@ -63,100 +63,6 @@ static int alt = EM28XX_PINOUT; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); -/* ------------------------------------------------------------------ */ -/* debug help functions */ - -static const char *v4l1_ioctls[] = { - "0", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", - "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", - "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", - "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", - "SMICROCODE", "GVBIFMT", "SVBIFMT" }; -#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) - -static const char *v4l2_ioctls[] = { - "QUERYCAP", "1", "ENUM_PIXFMT", "ENUM_FBUFFMT", "G_FMT", "S_FMT", - "G_COMP", "S_COMP", "REQBUFS", "QUERYBUF", "G_FBUF", "S_FBUF", - "G_WIN", "S_WIN", "PREVIEW", "QBUF", "16", "DQBUF", "STREAMON", - "STREAMOFF", "G_PERF", "G_PARM", "S_PARM", "G_STD", "S_STD", - "ENUMSTD", "ENUMINPUT", "G_CTRL", "S_CTRL", "G_TUNER", "S_TUNER", - "G_FREQ", "S_FREQ", "G_AUDIO", "S_AUDIO", "35", "QUERYCTRL", - "QUERYMENU", "G_INPUT", "S_INPUT", "ENUMCVT", "41", "42", "43", - "44", "45", "G_OUTPUT", "S_OUTPUT", "ENUMOUTPUT", "G_AUDOUT", - "S_AUDOUT", "ENUMFX", "G_EFFECT", "S_EFFECT", "G_MODULATOR", - "S_MODULATOR" -}; -#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) - -void em28xx_print_ioctl(char *name, unsigned int cmd) -{ - char *dir; - - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: dir = "--"; break; - case _IOC_READ: dir = "r-"; break; - case _IOC_WRITE: dir = "-w"; break; - case _IOC_READ | _IOC_WRITE: dir = "rw"; break; - default: dir = "??"; break; - } - switch (_IOC_TYPE(cmd)) { - case 'v': - printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l1, %s, VIDIOC%s)\n", - name, cmd, dir, (_IOC_NR(cmd) < V4L1_IOCTLS) ? - v4l1_ioctls[_IOC_NR(cmd)] : "???"); - break; - case 'V': - printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l2, %s, VIDIOC_%s)\n", - name, cmd, dir, (_IOC_NR(cmd) < V4L2_IOCTLS) ? - v4l2_ioctls[_IOC_NR(cmd)] : "???"); - break; - default: - printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n", - name, cmd, dir, _IOC_NR(cmd)); - } -} - -static void *rvmalloc(size_t size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - - mem = vmalloc_32((unsigned long)size); - if (!mem) - return NULL; - - memset(mem, 0, size); - - adr = (unsigned long)mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, size_t size) -{ - unsigned long adr; - - if (!mem) - return; - - size = PAGE_ALIGN(size); - - adr = (unsigned long)mem; - while (size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vfree(mem); -} /* * em28xx_request_buffers() @@ -167,14 +73,16 @@ u32 em28xx_request_buffers(struct em28xx *dev, u32 count) const size_t imagesize = PAGE_ALIGN(dev->frame_size); /*needs to be page aligned cause the buffers can be mapped individually! */ void *buff = NULL; u32 i; - em28xx_coredbg("requested %i buffers with size %i", count, imagesize); + em28xx_coredbg("requested %i buffers with size %zi", count, imagesize); if (count > EM28XX_NUM_FRAMES) count = EM28XX_NUM_FRAMES; dev->num_frames = count; while (dev->num_frames > 0) { - if ((buff = rvmalloc(dev->num_frames * imagesize))) + if ((buff = vmalloc_32(dev->num_frames * imagesize))) { + memset(buff, 0, dev->num_frames * imagesize); break; + } dev->num_frames--; } @@ -217,8 +125,7 @@ void em28xx_queue_unusedframes(struct em28xx *dev) void em28xx_release_buffers(struct em28xx *dev) { if (dev->num_frames) { - rvfree(dev->frame[0].bufmem, - dev->num_frames * PAGE_ALIGN(dev->frame[0].buf.length)); + vfree(dev->frame[0].bufmem); dev->num_frames = 0; } } diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index b32d9852f34..0591a705b7a 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -28,6 +28,7 @@ #include <linux/video_decoder.h> #include "em28xx.h" +#include <media/v4l2-common.h> #include <media/tuner.h> /* ----------------------------------------------------------- */ @@ -41,7 +42,7 @@ module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); #define dprintk1(lvl,fmt, args...) if (i2c_debug>=lvl) do {\ - printk(fmt , ##args); } while (0) + printk(fmt, ##args); } while (0) #define dprintk2(lvl,fmt, args...) if (i2c_debug>=lvl) do{ \ printk(KERN_DEBUG "%s at %s: " fmt, \ dev->name, __FUNCTION__ , ##args); } while (0) @@ -486,9 +487,7 @@ static struct i2c_adapter em28xx_adap_template = { .inc_use = inc_use, .dec_use = dec_use, #endif -#ifdef I2C_CLASS_TV_ANALOG .class = I2C_CLASS_TV_ANALOG, -#endif .name = "em28xx", .id = I2C_HW_B_EM28XX, .algo = &em28xx_algo, @@ -497,7 +496,6 @@ static struct i2c_adapter em28xx_adap_template = { static struct i2c_client em28xx_client_template = { .name = "em28xx internal", - .flags = I2C_CLIENT_ALLOW_USE, }; /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 32c49df58ad..30dfa5370c7 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -76,6 +76,58 @@ static IR_KEYTAB_TYPE ir_codes_em_terratec[IR_KEYTAB_SIZE] = { [ 0x40 ] = KEY_ZOOM, }; +static IR_KEYTAB_TYPE ir_codes_em_pinnacle_usb[IR_KEYTAB_SIZE] = { + [ 0x3a ] = KEY_KP0, + [ 0x31 ] = KEY_KP1, + [ 0x32 ] = KEY_KP2, + [ 0x33 ] = KEY_KP3, + [ 0x34 ] = KEY_KP4, + [ 0x35 ] = KEY_KP5, + [ 0x36 ] = KEY_KP6, + [ 0x37 ] = KEY_KP7, + [ 0x38 ] = KEY_KP8, + [ 0x39 ] = KEY_KP9, + + [ 0x2f ] = KEY_POWER, + + [ 0x2e ] = KEY_P, + [ 0x1f ] = KEY_L, + [ 0x2b ] = KEY_I, + + [ 0x2d ] = KEY_ZOOM, + [ 0x1e ] = KEY_ZOOM, + [ 0x1b ] = KEY_VOLUMEUP, + [ 0x0f ] = KEY_VOLUMEDOWN, + [ 0x17 ] = KEY_CHANNELUP, + [ 0x1c ] = KEY_CHANNELDOWN, + [ 0x25 ] = KEY_INFO, + + [ 0x3c ] = KEY_MUTE, + + [ 0x3d ] = KEY_LEFT, + [ 0x3b ] = KEY_RIGHT, + + [ 0x3f ] = KEY_UP, + [ 0x3e ] = KEY_DOWN, + [ 0x1a ] = KEY_PAUSE, + + [ 0x1d ] = KEY_MENU, + [ 0x19 ] = KEY_PLAY, + [ 0x16 ] = KEY_REWIND, + [ 0x13 ] = KEY_FORWARD, + [ 0x15 ] = KEY_PAUSE, + [ 0x0e ] = KEY_REWIND, + [ 0x0d ] = KEY_PLAY, + [ 0x0b ] = KEY_STOP, + [ 0x07 ] = KEY_FORWARD, + [ 0x27 ] = KEY_RECORD, + [ 0x26 ] = KEY_TUNER, + [ 0x29 ] = KEY_TEXT, + [ 0x2a ] = KEY_MEDIA, + [ 0x18 ] = KEY_EPG, + [ 0x27 ] = KEY_RECORD, +}; + /* ----------------------------------------------------------------------- */ static int get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) @@ -120,9 +172,6 @@ static int get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (buf[1]==0xff) return 0; - /* avoid fast reapeating */ - if (buf[1]==ir->old) - return 0; ir->old=buf[1]; /* Rearranges bits to the right order */ @@ -141,6 +190,28 @@ static int get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } +static int get_key_pinnacle_usb(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char buf[3]; + + /* poll IR chip */ + + if (3 != i2c_master_recv(&ir->c,buf,3)) { + dprintk("read error\n"); + return -EIO; + } + + dprintk("key %02x\n", buf[2]&0x3f); + if (buf[0]!=0x00){ + return 0; + } + + *ir_key = buf[2]&0x3f; + *ir_raw = buf[2]&0x3f; + + return 1; +} + /* ----------------------------------------------------------------------- */ void em28xx_set_ir(struct em28xx * dev,struct IR_i2c *ir) { @@ -162,6 +233,9 @@ void em28xx_set_ir(struct em28xx * dev,struct IR_i2c *ir) snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (EM28XX Terratec)"); break; case (EM2820_BOARD_PINNACLE_USB_2): + ir->ir_codes = ir_codes_em_pinnacle_usb; + ir->get_key = get_key_pinnacle_usb; + snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (EM28XX Pinnacle PCTV)"); break; case (EM2820_BOARD_HAUPPAUGE_WINTV_USB_2): ir->ir_codes = ir_codes_hauppauge_new; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 57c1826b928..eea304f7517 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -29,9 +29,11 @@ #include <linux/i2c.h> #include <linux/version.h> #include <linux/video_decoder.h> +#include <linux/mutex.h> #include "em28xx.h" #include <media/tuner.h> +#include <media/v4l2-common.h> #define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \ "Markus Rechberger <mrechberger@gmail.com>, " \ @@ -106,8 +108,32 @@ static const unsigned char saa7114_i2c_init[] = { #define TVNORMS ARRAY_SIZE(tvnorms) /* supported controls */ +/* Common to all boards */ static struct v4l2_queryctrl em28xx_qctrl[] = { { + .id = V4L2_CID_AUDIO_VOLUME, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Volume", + .minimum = 0x0, + .maximum = 0x1f, + .step = 0x1, + .default_value = 0x1f, + .flags = 0, + },{ + .id = V4L2_CID_AUDIO_MUTE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, + } +}; + +/* FIXME: These are specific to saa711x - should be moved to its code */ +static struct v4l2_queryctrl saa711x_qctrl[] = { + { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Brightness", @@ -135,24 +161,6 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { .default_value = 0x10, .flags = 0, },{ - .id = V4L2_CID_AUDIO_VOLUME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Volume", - .minimum = 0x0, - .maximum = 0x1f, - .step = 0x1, - .default_value = 0x1f, - .flags = 0, - },{ - .id = V4L2_CID_AUDIO_MUTE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - .flags = 0, - },{ .id = V4L2_CID_RED_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Red chroma balance", @@ -179,26 +187,16 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { .step = 0x1, .default_value = 0x20, .flags = 0, - } + } }; static struct usb_driver em28xx_usb_driver; -static DECLARE_MUTEX(em28xx_sysfs_lock); +static DEFINE_MUTEX(em28xx_sysfs_lock); static DECLARE_RWSEM(em28xx_disconnect); /********************* v4l2 interface ******************************************/ -static inline unsigned long kvirt_to_pa(unsigned long adr) -{ - unsigned long kva, ret; - - kva = (unsigned long)page_address(vmalloc_to_page((void *)adr)); - kva |= adr & (PAGE_SIZE - 1); - ret = __pa(kva); - return ret; -} - /* * em28xx_config() * inits registers with sane defaults @@ -226,7 +224,7 @@ static int em28xx_config(struct em28xx *dev) * em28xx_config_i2c() * configure i2c attached devices */ -void em28xx_config_i2c(struct em28xx *dev) +static void em28xx_config_i2c(struct em28xx *dev) { struct v4l2_frequency f; struct video_decoder_init em28xx_vdi = {.data = NULL }; @@ -290,6 +288,8 @@ static void video_mux(struct em28xx *dev, int index) em28xx_videodbg("Setting input index=%d, vmux=%d, amux=%d\n",index,input,dev->ctl_ainput); if (dev->has_msp34xx) { + if (dev->i2s_speed) + em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); em28xx_i2c_call_clients(dev, VIDIOC_S_AUDIO, &dev->ctl_ainput); ainput = EM28XX_AUDIO_SRC_TUNER; em28xx_audio_source(dev, ainput); @@ -395,7 +395,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) */ static void em28xx_release_resources(struct em28xx *dev) { - down(&em28xx_sysfs_lock); + mutex_lock(&em28xx_sysfs_lock); em28xx_info("V4L2 device /dev/video%d deregistered\n", dev->vdev->minor); @@ -404,7 +404,7 @@ static void em28xx_release_resources(struct em28xx *dev) /* video_unregister_device(dev->vbi_dev); */ em28xx_i2c_unregister(dev); usb_put_dev(dev->udev); - up(&em28xx_sysfs_lock); + mutex_unlock(&em28xx_sysfs_lock); } /* @@ -616,7 +616,8 @@ static struct vm_operations_struct em28xx_vm_ops = { static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) { unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start, pos, page; + start = vma->vm_start; + void *pos; u32 i; struct em28xx *dev = filp->private_data; @@ -657,12 +658,10 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_flags |= VM_IO; vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ - pos = (unsigned long)dev->frame[i].bufmem; + pos = dev->frame[i].bufmem; while (size > 0) { /* size is page-aligned */ - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, - vma->vm_page_prot)) { - em28xx_videodbg("mmap: rename page map failed\n"); + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + em28xx_videodbg("mmap: vm_insert_page failed\n"); up(&dev->fileop_lock); return -EAGAIN; } @@ -685,7 +684,6 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) */ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) { - s32 tmp; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: ctrl->value = dev->mute; @@ -693,6 +691,16 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_VOLUME: ctrl->value = dev->volume; return 0; + default: + return -EINVAL; + } +} + +/*FIXME: should be moved to saa711x */ +static int saa711x_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) +{ + s32 tmp; + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if ((tmp = em28xx_brightness_get(dev)) < 0) return -EIO; @@ -742,6 +750,15 @@ static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) case V4L2_CID_AUDIO_VOLUME: dev->volume = ctrl->value; return em28xx_audio_analog_set(dev); + default: + return -EINVAL; + } +} + +/*FIXME: should be moved to saa711x */ +static int saa711x_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) +{ + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: return em28xx_brightness_set(dev, ctrl->value); case V4L2_CID_CONTRAST: @@ -1005,14 +1022,34 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp, case VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *qc = arg; - u8 i, n; - n = sizeof(em28xx_qctrl) / sizeof(em28xx_qctrl[0]); - for (i = 0; i < n; i++) - if (qc->id && qc->id == em28xx_qctrl[i].id) { - memcpy(qc, &(em28xx_qctrl[i]), + int i, id=qc->id; + + memset(qc,0,sizeof(*qc)); + qc->id=id; + + if (!dev->has_msp34xx) { + for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { + if (qc->id && qc->id == em28xx_qctrl[i].id) { + memcpy(qc, &(em28xx_qctrl[i]), + sizeof(*qc)); + return 0; + } + } + } + if (dev->decoder == EM28XX_TVP5150) { + em28xx_i2c_call_clients(dev,cmd,qc); + if (qc->type) + return 0; + else + return -EINVAL; + } + for (i = 0; i < ARRAY_SIZE(saa711x_qctrl); i++) { + if (qc->id && qc->id == saa711x_qctrl[i].id) { + memcpy(qc, &(saa711x_qctrl[i]), sizeof(*qc)); return 0; } + } return -EINVAL; } @@ -1020,29 +1057,64 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp, case VIDIOC_G_CTRL: { struct v4l2_control *ctrl = arg; + int retval=-EINVAL; + if (!dev->has_msp34xx) + retval=em28xx_get_ctrl(dev, ctrl); + if (retval==-EINVAL) { + if (dev->decoder == EM28XX_TVP5150) { + em28xx_i2c_call_clients(dev,cmd,arg); + return 0; + } - return em28xx_get_ctrl(dev, ctrl); + return saa711x_get_ctrl(dev, ctrl); + } else return retval; } - case VIDIOC_S_CTRL_OLD: /* ??? */ case VIDIOC_S_CTRL: { struct v4l2_control *ctrl = arg; - u8 i, n; - - - n = sizeof(em28xx_qctrl) / sizeof(em28xx_qctrl[0]); - for (i = 0; i < n; i++) - if (ctrl->id == em28xx_qctrl[i].id) { - if (ctrl->value < - em28xx_qctrl[i].minimum - || ctrl->value > - em28xx_qctrl[i].maximum) - return -ERANGE; + u8 i; + + if (!dev->has_msp34xx){ + for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { + if (ctrl->id == em28xx_qctrl[i].id) { + if (ctrl->value < + em28xx_qctrl[i].minimum + || ctrl->value > + em28xx_qctrl[i].maximum) + return -ERANGE; + return em28xx_set_ctrl(dev, ctrl); + } + } + } - return em28xx_set_ctrl(dev, ctrl); + if (dev->decoder == EM28XX_TVP5150) { + em28xx_i2c_call_clients(dev,cmd,arg); + return 0; + } else if (!dev->has_msp34xx) { + for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { + if (ctrl->id == em28xx_qctrl[i].id) { + if (ctrl->value < + em28xx_qctrl[i].minimum + || ctrl->value > + em28xx_qctrl[i].maximum) + return -ERANGE; + return em28xx_set_ctrl(dev, ctrl); + } } + for (i = 0; i < ARRAY_SIZE(saa711x_qctrl); i++) { + if (ctrl->id == saa711x_qctrl[i].id) { + if (ctrl->value < + saa711x_qctrl[i].minimum + || ctrl->value > + saa711x_qctrl[i].maximum) + return -ERANGE; + return saa711x_set_ctrl(dev, ctrl); + } + } + } + return -EINVAL; } @@ -1198,7 +1270,7 @@ static int em28xx_video_do_ioctl(struct inode *inode, struct file *filp, return -ENODEV; if (video_debug > 1) - em28xx_print_ioctl(dev->name,cmd); + v4l_print_ioctl(dev->name,cmd); switch (cmd) { @@ -1575,6 +1647,8 @@ static struct file_operations em28xx_v4l_fops = { .poll = em28xx_v4l2_poll, .mmap = em28xx_v4l2_mmap, .llseek = no_llseek, + .compat_ioctl = v4l_compat_ioctl32, + }; /******************************** usb interface *****************************************/ @@ -1788,12 +1862,11 @@ static int em28xx_usb_probe(struct usb_interface *interface, } /* allocate memory for our device state and initialize it */ - dev = kmalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { em28xx_err(DRIVER_NAME ": out of memory!\n"); return -ENOMEM; } - memset(dev, 0, sizeof(*dev)); /* compute alternate max packet sizes */ uif = udev->actconfig->interface[0]; @@ -1859,9 +1932,12 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) struct em28xx *dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); +/*FIXME: IR should be disconnected */ + if (!dev) return; + down_write(&em28xx_disconnect); down(&dev->lock); @@ -1895,7 +1971,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) } static struct usb_driver em28xx_usb_driver = { - .owner = THIS_MODULE, .name = "em28xx", .probe = em28xx_usb_probe, .disconnect = em28xx_usb_disconnect, diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 5c7a41ce69f..33de9d846af 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -1,5 +1,5 @@ /* - em28xx-cards.c - driver for Empia EM2800/EM2820/2840 USB video capture devices + em28xx.h - driver for Empia EM2800/EM2820/2840 USB video capture devices Copyright (C) 2005 Markus Rechberger <mrechberger@gmail.com> Ludovico Cavedon <cavedon@sssup.it> @@ -216,6 +216,8 @@ struct em28xx { unsigned int has_msp34xx:1; unsigned int has_tda9887:1; + u32 i2s_speed; /* I2S speed for audio digital stream */ + enum em28xx_decoder decoder; int tuner_type; /* type of the tuner */ @@ -293,8 +295,6 @@ void em28xx_set_ir(struct em28xx * dev,struct IR_i2c *ir); /* Provided by em28xx-core.c */ -void em28xx_print_ioctl(char *name, unsigned int cmd); - u32 em28xx_request_buffers(struct em28xx *dev, u32 count); void em28xx_queue_unusedframes(struct em28xx *dev); void em28xx_release_buffers(struct em28xx *dev); diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index c9b00eafefd..e7bbeb11553 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -240,12 +240,11 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d DEB_EE((".\n")); - hexium = (struct hexium *) kmalloc(sizeof(struct hexium), GFP_KERNEL); + hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL); if (NULL == hexium) { printk("hexium_gemini: not enough kernel memory in hexium_attach().\n"); return -ENOMEM; } - memset(hexium, 0x0, sizeof(struct hexium)); dev->ext_priv = hexium; /* enable i2c-port pins */ diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 42a9414155c..0b6c2096ec6 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -224,12 +224,11 @@ static int hexium_probe(struct saa7146_dev *dev) return -EFAULT; } - hexium = (struct hexium *) kmalloc(sizeof(struct hexium), GFP_KERNEL); + hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL); if (NULL == hexium) { printk("hexium_orion: hexium_probe: not enough kernel memory.\n"); return -ENOMEM; } - memset(hexium, 0x0, sizeof(struct hexium)); /* enable i2c-port pins */ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); diff --git a/drivers/media/video/indycam.c b/drivers/media/video/indycam.c index deeef125eb9..7420b79e987 100644 --- a/drivers/media/video/indycam.c +++ b/drivers/media/video/indycam.c @@ -289,18 +289,15 @@ static int indycam_attach(struct i2c_adapter *adap, int addr, int kind) printk(KERN_INFO "SGI IndyCam driver version %s\n", INDYCAM_MODULE_VERSION); - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (!client) return -ENOMEM; - camera = kmalloc(sizeof(struct indycam), GFP_KERNEL); + camera = kzalloc(sizeof(struct indycam), GFP_KERNEL); if (!camera) { err = -ENOMEM; goto out_free_client; } - memset(client, 0, sizeof(struct i2c_client)); - memset(camera, 0, sizeof(struct indycam)); - client->addr = addr; client->adapter = adap; client->driver = &i2c_driver_indycam; @@ -451,10 +448,10 @@ static int indycam_command(struct i2c_client *client, unsigned int cmd, } static struct i2c_driver i2c_driver_indycam = { - .owner = THIS_MODULE, - .name = "indycam", + .driver = { + .name = "indycam", + }, .id = I2C_DRIVERID_INDYCAM, - .flags = I2C_DF_NOTIFY, .attach_adapter = indycam_probe, .detach_client = indycam_detach, .command = indycam_command, diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 0085567a142..58b0e698282 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -40,6 +40,7 @@ #include <linux/i2c.h> #include <linux/workqueue.h> #include <asm/semaphore.h> + #include <media/ir-common.h> #include <media/ir-kbd-i2c.h> @@ -183,6 +184,58 @@ static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } +/* The new pinnacle PCTV remote (with the colored buttons) + * + * Ricardo Cerqueira <v4l@cerqueira.org> + */ + +int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char b[4]; + unsigned int start = 0,parity = 0,code = 0; + + /* poll IR chip */ + if (4 != i2c_master_recv(&ir->c,b,4)) { + dprintk(2,"read error\n"); + return -EIO; + } + + for (start = 0; start<4; start++) { + if (b[start] == 0x80) { + code=b[(start+3)%4]; + parity=b[(start+2)%4]; + } + } + + /* Empty Request */ + if (parity==0) + return 0; + + /* Repeating... */ + if (ir->old == parity) + return 0; + + + ir->old = parity; + + /* Reduce code value to fit inside IR_KEYTAB_SIZE + * + * this is the only value that results in 42 unique + * codes < 128 + */ + + code %= 0x88; + + *ir_raw = code; + *ir_key = code; + + dprintk(1,"Pinnacle PCTV key %02x\n", code); + + return 1; +} + +EXPORT_SYMBOL_GPL(get_key_pinnacle); + /* ----------------------------------------------------------------------- */ static void ir_key_poll(struct IR_i2c *ir) @@ -225,9 +278,10 @@ static int ir_detach(struct i2c_client *client); static int ir_probe(struct i2c_adapter *adap); static struct i2c_driver driver = { - .name = "ir remote kbd driver", - .id = I2C_DRIVERID_EXP3, /* FIXME */ - .flags = I2C_DF_NOTIFY, + .driver = { + .name = "ir-kbd-i2c", + }, + .id = I2C_DRIVERID_INFRARED, .attach_adapter = ir_probe, .detach_client = ir_detach, }; @@ -244,24 +298,26 @@ static int ir_attach(struct i2c_adapter *adap, int addr, IR_KEYTAB_TYPE *ir_codes = NULL; char *name; int ir_type; - struct IR_i2c *ir; + struct IR_i2c *ir; struct input_dev *input_dev; - ir = kzalloc(sizeof(struct IR_i2c), GFP_KERNEL); + ir = kzalloc(sizeof(struct IR_i2c),GFP_KERNEL); input_dev = input_allocate_device(); if (!ir || !input_dev) { - kfree(ir); input_free_device(input_dev); - return -ENOMEM; + kfree(ir); + return -ENOMEM; } + memset(ir,0,sizeof(*ir)); ir->c = client_template; ir->input = input_dev; - i2c_set_clientdata(&ir->c, ir); ir->c.adapter = adap; ir->c.addr = addr; + i2c_set_clientdata(&ir->c, ir); + switch(addr) { case 0x64: name = "Pixelview"; @@ -308,7 +364,7 @@ static int ir_attach(struct i2c_adapter *adap, int addr, /* register i2c device * At device register, IR codes may be changed to be * board dependent. - */ + */ i2c_attach_client(&ir->c); /* If IR not supported or disabled, unregisters driver */ @@ -324,13 +380,15 @@ static int ir_attach(struct i2c_adapter *adap, int addr, ir->c.dev.bus_id); /* init + register input device */ - ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); - input_dev->id.bustype = BUS_I2C; - input_dev->name = ir->c.name; - input_dev->phys = ir->phys; + ir_input_init(input_dev,&ir->ir,ir_type,ir->ir_codes); + input_dev->id.bustype = BUS_I2C; + input_dev->name = ir->c.name; + input_dev->phys = ir->phys; /* register event device */ input_register_device(ir->input); + printk(DEVNAME ": %s detected at %s [%s]\n", + ir->input->name,ir->input->phys,adap->name); /* start polling via eventd */ INIT_WORK(&ir->work, ir_work, ir); diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 3f2a882bc20..2869464aee0 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -1754,6 +1754,7 @@ static struct file_operations meye_fops = { .release = meye_release, .mmap = meye_mmap, .ioctl = meye_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .poll = meye_poll, .llseek = no_llseek, }; diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c new file mode 100644 index 00000000000..69ed369c2f4 --- /dev/null +++ b/drivers/media/video/msp3400-driver.c @@ -0,0 +1,1274 @@ +/* + * Programming the mspx4xx sound processor family + * + * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org> + * + * what works and what doesn't: + * + * AM-Mono + * Support for Hauppauge cards added (decoding handled by tuner) added by + * Frederic Crozat <fcrozat@mail.dotcom.fr> + * + * FM-Mono + * should work. The stereo modes are backward compatible to FM-mono, + * therefore FM-Mono should be allways available. + * + * FM-Stereo (B/G, used in germany) + * should work, with autodetect + * + * FM-Stereo (satellite) + * should work, no autodetect (i.e. default is mono, but you can + * switch to stereo -- untested) + * + * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) + * should work, with autodetect. Support for NICAM was added by + * Pekka Pietikainen <pp@netppl.fi> + * + * TODO: + * - better SAT support + * + * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch) + * using soundcore instead of OSS + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/videodev.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/audiochip.h> +#include <linux/kthread.h> +#include <linux/suspend.h> +#include "msp3400.h" + +/* ---------------------------------------------------------------------- */ + +MODULE_DESCRIPTION("device driver for msp34xx TV sound processor"); +MODULE_AUTHOR("Gerd Knorr"); +MODULE_LICENSE("GPL"); + +/* module parameters */ +static int opmode = OPMODE_AUTO; +int msp_debug; /* msp_debug output */ +int msp_once; /* no continous stereo monitoring */ +int msp_amsound; /* hard-wire AM sound at 6.5 Hz (france), + the autoscan seems work well only with FM... */ +int msp_standard = 1; /* Override auto detect of audio msp_standard, if needed. */ +int msp_dolby; + +int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual + (msp34xxg only) 0x00a0-0x03c0 */ + +/* read-only */ +module_param(opmode, int, 0444); + +/* read-write */ +module_param_named(once,msp_once, bool, 0644); +module_param_named(debug,msp_debug, int, 0644); +module_param_named(stereo_threshold,msp_stereo_thresh, int, 0644); +module_param_named(standard,msp_standard, int, 0644); +module_param_named(amsound,msp_amsound, bool, 0644); +module_param_named(dolby,msp_dolby, bool, 0644); + +MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect"); +MODULE_PARM_DESC(once, "No continuous stereo monitoring"); +MODULE_PARM_DESC(debug, "Enable debug messages [0-3]"); +MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo"); +MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect"); +MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan"); +MODULE_PARM_DESC(dolby, "Activates Dolby processsing"); + +/* ---------------------------------------------------------------------- */ + +/* control subaddress */ +#define I2C_MSP_CONTROL 0x00 +/* demodulator unit subaddress */ +#define I2C_MSP_DEM 0x10 +/* DSP unit subaddress */ +#define I2C_MSP_DSP 0x12 + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x80 >> 1, 0x88 >> 1, I2C_CLIENT_END }; +I2C_CLIENT_INSMOD; + +/* ----------------------------------------------------------------------- */ +/* functions for talking to the MSP3400C Sound processor */ + +int msp_reset(struct i2c_client *client) +{ + /* reset and read revision code */ + static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 }; + static u8 reset_on[3] = { I2C_MSP_CONTROL, 0x00, 0x00 }; + static u8 write[3] = { I2C_MSP_DSP + 1, 0x00, 0x1e }; + u8 read[2]; + struct i2c_msg reset[2] = { + { client->addr, I2C_M_IGNORE_NAK, 3, reset_off }, + { client->addr, I2C_M_IGNORE_NAK, 3, reset_on }, + }; + struct i2c_msg test[2] = { + { client->addr, 0, 3, write }, + { client->addr, I2C_M_RD, 2, read }, + }; + + v4l_dbg(3, msp_debug, client, "msp_reset\n"); + if (i2c_transfer(client->adapter, &reset[0], 1) != 1 || + i2c_transfer(client->adapter, &reset[1], 1) != 1 || + i2c_transfer(client->adapter, test, 2) != 2) { + v4l_err(client, "chip reset failed\n"); + return -1; + } + return 0; +} + +static int msp_read(struct i2c_client *client, int dev, int addr) +{ + int err, retval; + u8 write[3]; + u8 read[2]; + struct i2c_msg msgs[2] = { + { client->addr, 0, 3, write }, + { client->addr, I2C_M_RD, 2, read } + }; + + write[0] = dev + 1; + write[1] = addr >> 8; + write[2] = addr & 0xff; + + for (err = 0; err < 3; err++) { + if (i2c_transfer(client->adapter, msgs, 2) == 2) + break; + v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err, + dev, addr); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(msecs_to_jiffies(10)); + } + if (err == 3) { + v4l_warn(client, "giving up, resetting chip. Sound will go off, sorry folks :-|\n"); + msp_reset(client); + return -1; + } + retval = read[0] << 8 | read[1]; + v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n", dev, addr, retval); + return retval; +} + +int msp_read_dem(struct i2c_client *client, int addr) +{ + return msp_read(client, I2C_MSP_DEM, addr); +} + +int msp_read_dsp(struct i2c_client *client, int addr) +{ + return msp_read(client, I2C_MSP_DSP, addr); +} + +static int msp_write(struct i2c_client *client, int dev, int addr, int val) +{ + int err; + u8 buffer[5]; + + buffer[0] = dev; + buffer[1] = addr >> 8; + buffer[2] = addr & 0xff; + buffer[3] = val >> 8; + buffer[4] = val & 0xff; + + v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n", dev, addr, val); + for (err = 0; err < 3; err++) { + if (i2c_master_send(client, buffer, 5) == 5) + break; + v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err, + dev, addr); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(msecs_to_jiffies(10)); + } + if (err == 3) { + v4l_warn(client, "giving up, resetting chip. Sound will go off, sorry folks :-|\n"); + msp_reset(client); + return -1; + } + return 0; +} + +int msp_write_dem(struct i2c_client *client, int addr, int val) +{ + return msp_write(client, I2C_MSP_DEM, addr, val); +} + +int msp_write_dsp(struct i2c_client *client, int addr, int val) +{ + return msp_write(client, I2C_MSP_DSP, addr, val); +} + +/* ----------------------------------------------------------------------- * + * bits 9 8 5 - SCART DSP input Select: + * 0 0 0 - SCART 1 to DSP input (reset position) + * 0 1 0 - MONO to DSP input + * 1 0 0 - SCART 2 to DSP input + * 1 1 1 - Mute DSP input + * + * bits 11 10 6 - SCART 1 Output Select: + * 0 0 0 - undefined (reset position) + * 0 1 0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS) + * 1 0 0 - MONO input to SCART 1 Output + * 1 1 0 - SCART 1 DA to SCART 1 Output + * 0 0 1 - SCART 2 DA to SCART 1 Output + * 0 1 1 - SCART 1 Input to SCART 1 Output + * 1 1 1 - Mute SCART 1 Output + * + * bits 13 12 7 - SCART 2 Output Select (for devices with 2 Output SCART): + * 0 0 0 - SCART 1 DA to SCART 2 Output (reset position) + * 0 1 0 - SCART 1 Input to SCART 2 Output + * 1 0 0 - MONO input to SCART 2 Output + * 0 0 1 - SCART 2 DA to SCART 2 Output + * 0 1 1 - SCART 2 Input to SCART 2 Output + * 1 1 0 - Mute SCART 2 Output + * + * Bits 4 to 0 should be zero. + * ----------------------------------------------------------------------- */ + +static int scarts[3][9] = { + /* MASK IN1 IN2 IN1_DA IN2_DA IN3 IN4 MONO MUTE */ + /* SCART DSP Input select */ + { 0x0320, 0x0000, 0x0200, -1, -1, 0x0300, 0x0020, 0x0100, 0x0320 }, + /* SCART1 Output select */ + { 0x0c40, 0x0440, 0x0400, 0x0c00, 0x0040, 0x0000, 0x0840, 0x0800, 0x0c40 }, + /* SCART2 Output select */ + { 0x3080, 0x1000, 0x1080, 0x0000, 0x0080, 0x2080, 0x3080, 0x2000, 0x3000 }, +}; + +static char *scart_names[] = { + "mask", "in1", "in2", "in1 da", "in2 da", "in3", "in4", "mono", "mute" +}; + +void msp_set_scart(struct i2c_client *client, int in, int out) +{ + struct msp_state *state = i2c_get_clientdata(client); + + state->in_scart=in; + + if (in >= 1 && in <= 8 && out >= 0 && out <= 2) { + if (-1 == scarts[out][in]) + return; + + state->acb &= ~scarts[out][SCART_MASK]; + state->acb |= scarts[out][in]; + } else + state->acb = 0xf60; /* Mute Input and SCART 1 Output */ + + v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n", + scart_names[in], out, state->acb); + msp_write_dsp(client, 0x13, state->acb); + + /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */ + msp_write_dem(client, 0x40, state->i2s_mode); +} + +void msp_set_mute(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + + v4l_dbg(1, msp_debug, client, "mute audio\n"); + msp_write_dsp(client, 0x0000, 0); + msp_write_dsp(client, 0x0007, 1); + if (state->has_scart2_out_volume) + msp_write_dsp(client, 0x0040, 1); + if (state->has_headphones) + msp_write_dsp(client, 0x0006, 0); +} + +void msp_set_audio(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + int bal = 0, bass, treble, loudness; + int val = 0; + + if (!state->muted) + val = (state->volume * 0x7f / 65535) << 8; + + v4l_dbg(1, msp_debug, client, "mute=%s volume=%d\n", + state->muted ? "on" : "off", state->volume); + + msp_write_dsp(client, 0x0000, val); + msp_write_dsp(client, 0x0007, state->muted ? 0x1 : (val | 0x1)); + if (state->has_scart2_out_volume) + msp_write_dsp(client, 0x0040, state->muted ? 0x1 : (val | 0x1)); + if (state->has_headphones) + msp_write_dsp(client, 0x0006, val); + if (!state->has_sound_processing) + return; + + if (val) + bal = (u8)((state->balance / 256) - 128); + bass = ((state->bass - 32768) * 0x60 / 65535) << 8; + treble = ((state->treble - 32768) * 0x60 / 65535) << 8; + loudness = state->loudness ? ((5 * 4) << 8) : 0; + + v4l_dbg(1, msp_debug, client, "balance=%d bass=%d treble=%d loudness=%d\n", + state->balance, state->bass, state->treble, state->loudness); + + msp_write_dsp(client, 0x0001, bal << 8); + msp_write_dsp(client, 0x0002, bass); + msp_write_dsp(client, 0x0003, treble); + msp_write_dsp(client, 0x0004, loudness); + if (!state->has_headphones) + return; + msp_write_dsp(client, 0x0030, bal << 8); + msp_write_dsp(client, 0x0031, bass); + msp_write_dsp(client, 0x0032, treble); + msp_write_dsp(client, 0x0033, loudness); +} + +int msp_modus(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + + if (state->radio) { + v4l_dbg(1, msp_debug, client, "video mode selected to Radio\n"); + return 0x0003; + } + + if (state->v4l2_std & V4L2_STD_PAL) { + v4l_dbg(1, msp_debug, client, "video mode selected to PAL\n"); + +#if 1 + /* experimental: not sure this works with all chip versions */ + return 0x7003; +#else + /* previous value, try this if it breaks ... */ + return 0x1003; +#endif + } + if (state->v4l2_std & V4L2_STD_NTSC) { + v4l_dbg(1, msp_debug, client, "video mode selected to NTSC\n"); + return 0x2003; + } + if (state->v4l2_std & V4L2_STD_SECAM) { + v4l_dbg(1, msp_debug, client, "video mode selected to SECAM\n"); + return 0x0003; + } + return 0x0003; +} + +/* ------------------------------------------------------------------------ */ + + +static void msp_wake_thread(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + + if (NULL == state->kthread) + return; + msp_set_mute(client); + state->watch_stereo = 0; + state->restart = 1; + wake_up_interruptible(&state->wq); +} + +int msp_sleep(struct msp_state *state, int timeout) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&state->wq, &wait); + if (!kthread_should_stop()) { + if (timeout < 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } else { + schedule_timeout_interruptible + (msecs_to_jiffies(timeout)); + } + } + + remove_wait_queue(&state->wq, &wait); + try_to_freeze(); + return state->restart; +} + +/* ------------------------------------------------------------------------ */ + +static int msp_mode_v4l2_to_v4l1(int rxsubchans) +{ + int mode = 0; + + if (rxsubchans & V4L2_TUNER_SUB_STEREO) + mode |= VIDEO_SOUND_STEREO; + if (rxsubchans & V4L2_TUNER_SUB_LANG2) + mode |= VIDEO_SOUND_LANG2; + if (rxsubchans & V4L2_TUNER_SUB_LANG1) + mode |= VIDEO_SOUND_LANG1; + if (mode == 0) + mode |= VIDEO_SOUND_MONO; + return mode; +} + +static int msp_mode_v4l1_to_v4l2(int mode) +{ + if (mode & VIDEO_SOUND_STEREO) + return V4L2_TUNER_MODE_STEREO; + if (mode & VIDEO_SOUND_LANG2) + return V4L2_TUNER_MODE_LANG2; + if (mode & VIDEO_SOUND_LANG1) + return V4L2_TUNER_MODE_LANG1; + return V4L2_TUNER_MODE_MONO; +} + +static void msp_any_detect_stereo(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + + switch (state->opmode) { + case OPMODE_MANUAL: + case OPMODE_AUTODETECT: + autodetect_stereo(client); + break; + case OPMODE_AUTOSELECT: + msp34xxg_detect_stereo(client); + break; + } +} + +static struct v4l2_queryctrl msp_qctrl_std[] = { + { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 58880, + .flags = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, +}; + +static struct v4l2_queryctrl msp_qctrl_sound_processing[] = { + { + .id = V4L2_CID_AUDIO_BALANCE, + .name = "Balance", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .flags = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_BASS, + .name = "Bass", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_TREBLE, + .name = "Treble", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_LOUDNESS, + .name = "Loudness", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, +}; + + +static void msp_any_set_audmode(struct i2c_client *client, int audmode) +{ + struct msp_state *state = i2c_get_clientdata(client); + + switch (state->opmode) { + case OPMODE_MANUAL: + case OPMODE_AUTODETECT: + state->watch_stereo = 0; + msp3400c_setstereo(client, audmode); + break; + case OPMODE_AUTOSELECT: + msp34xxg_set_audmode(client, audmode); + break; + } +} + +static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct msp_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = state->volume; + break; + + case V4L2_CID_AUDIO_MUTE: + ctrl->value = state->muted; + break; + + case V4L2_CID_AUDIO_BALANCE: + if (!state->has_sound_processing) + return -EINVAL; + ctrl->value = state->balance; + break; + + case V4L2_CID_AUDIO_BASS: + if (!state->has_sound_processing) + return -EINVAL; + ctrl->value = state->bass; + break; + + case V4L2_CID_AUDIO_TREBLE: + if (!state->has_sound_processing) + return -EINVAL; + ctrl->value = state->treble; + break; + + case V4L2_CID_AUDIO_LOUDNESS: + if (!state->has_sound_processing) + return -EINVAL; + ctrl->value = state->loudness; + break; + + default: + return -EINVAL; + } + return 0; +} + +static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct msp_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + state->volume = ctrl->value; + if (state->volume == 0) + state->balance = 32768; + break; + + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value < 0 || ctrl->value >= 2) + return -ERANGE; + state->muted = ctrl->value; + break; + + case V4L2_CID_AUDIO_BASS: + if (!state->has_sound_processing) + return -EINVAL; + state->bass = ctrl->value; + break; + + case V4L2_CID_AUDIO_TREBLE: + if (!state->has_sound_processing) + return -EINVAL; + state->treble = ctrl->value; + break; + + case V4L2_CID_AUDIO_LOUDNESS: + if (!state->has_sound_processing) + return -EINVAL; + state->loudness = ctrl->value; + break; + + case V4L2_CID_AUDIO_BALANCE: + if (!state->has_sound_processing) + return -EINVAL; + state->balance = ctrl->value; + break; + + default: + return -EINVAL; + } + msp_set_audio(client); + return 0; +} + +static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct msp_state *state = i2c_get_clientdata(client); + u16 *sarg = arg; + int scart = 0; + + if (msp_debug >= 2) + v4l_i2c_print_ioctl(client, cmd); + + switch (cmd) { + case AUDC_SET_INPUT: + if (*sarg == state->input) + break; + state->input = *sarg; + switch (*sarg) { + case AUDIO_RADIO: + /* Hauppauge uses IN2 for the radio */ + state->mode = MSP_MODE_FM_RADIO; + scart = SCART_IN2; + break; + case AUDIO_EXTERN_1: + /* IN1 is often used for external input ... */ + state->mode = MSP_MODE_EXTERN; + scart = SCART_IN1; + break; + case AUDIO_EXTERN_2: + /* ... sometimes it is IN2 through ;) */ + state->mode = MSP_MODE_EXTERN; + scart = SCART_IN2; + break; + case AUDIO_TUNER: + state->mode = -1; + break; + default: + if (*sarg & AUDIO_MUTE) + msp_set_scart(client, SCART_MUTE, 0); + break; + } + if (scart) { + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + state->audmode = V4L2_TUNER_MODE_STEREO; + msp_set_scart(client, scart, 0); + msp_write_dsp(client, 0x000d, 0x1900); + if (state->opmode != OPMODE_AUTOSELECT) + msp3400c_setstereo(client, state->audmode); + } + msp_wake_thread(client); + break; + + case AUDC_SET_RADIO: + if (state->radio) + return 0; + state->radio = 1; + v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); + state->watch_stereo = 0; + switch (state->opmode) { + case OPMODE_MANUAL: + /* set msp3400 to FM radio mode */ + msp3400c_setmode(client, MSP_MODE_FM_RADIO); + msp3400c_setcarrier(client, MSP_CARRIER(10.7), + MSP_CARRIER(10.7)); + msp_set_audio(client); + break; + case OPMODE_AUTODETECT: + case OPMODE_AUTOSELECT: + /* the thread will do for us */ + msp_wake_thread(client); + break; + } + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + + va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_MUTABLE; + if (state->has_sound_processing) + va->flags |= VIDEO_AUDIO_BALANCE | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + if (state->muted) + va->flags |= VIDEO_AUDIO_MUTE; + va->volume = state->volume; + va->balance = state->volume ? state->balance : 32768; + va->bass = state->bass; + va->treble = state->treble; + + if (state->radio) + break; + if (state->opmode == OPMODE_AUTOSELECT) + msp_any_detect_stereo(client); + va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans); + break; + } + + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + + state->muted = (va->flags & VIDEO_AUDIO_MUTE); + state->volume = va->volume; + state->balance = va->balance; + state->bass = va->bass; + state->treble = va->treble; + msp_set_audio(client); + + if (va->mode != 0 && state->radio == 0) + msp_any_set_audmode(client, msp_mode_v4l1_to_v4l2(va->mode)); + break; + } + + case VIDIOCSCHAN: + { + struct video_channel *vc = arg; + int update = 0; + v4l2_std_id std; + + if (state->radio) + update = 1; + state->radio = 0; + if (vc->norm == VIDEO_MODE_PAL) + std = V4L2_STD_PAL; + else if (vc->norm == VIDEO_MODE_SECAM) + std = V4L2_STD_SECAM; + else + std = V4L2_STD_NTSC; + if (std != state->v4l2_std) { + state->v4l2_std = std; + update = 1; + } + if (update) + msp_wake_thread(client); + break; + } + + case VIDIOCSFREQ: + case VIDIOC_S_FREQUENCY: + { + /* new channel -- kick audio carrier scan */ + msp_wake_thread(client); + break; + } + + /* msp34xx specific */ + case MSP_SET_MATRIX: + { + struct msp_matrix *mspm = arg; + + msp_set_scart(client, mspm->input, mspm->output); + break; + } + + /* --- v4l2 ioctls --- */ + case VIDIOC_S_STD: + { + v4l2_std_id *id = arg; + int update = state->radio || state->v4l2_std != *id; + + state->v4l2_std = *id; + state->radio = 0; + if (update) + msp_wake_thread(client); + return 0; + } + + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *i = arg; + + if (i->index != 0) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_TUNER; + switch (i->index) { + case AUDIO_RADIO: + strcpy(i->name, "Radio"); + break; + case AUDIO_EXTERN_1: + strcpy(i->name, "Extern 1"); + break; + case AUDIO_EXTERN_2: + strcpy(i->name, "Extern 2"); + break; + case AUDIO_TUNER: + strcpy(i->name, "Television"); + break; + default: + return -EINVAL; + } + return 0; + } + + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *a = arg; + + memset(a, 0, sizeof(*a)); + + switch (a->index) { + case AUDIO_RADIO: + strcpy(a->name, "Radio"); + break; + case AUDIO_EXTERN_1: + strcpy(a->name, "Extern 1"); + break; + case AUDIO_EXTERN_2: + strcpy(a->name, "Extern 2"); + break; + case AUDIO_TUNER: + strcpy(a->name, "Television"); + break; + default: + return -EINVAL; + } + + msp_any_detect_stereo(client); + if (state->audmode == V4L2_TUNER_MODE_STEREO) { + a->capability = V4L2_AUDCAP_STEREO; + } + + break; + } + + case VIDIOC_S_AUDIO: + { + struct v4l2_audio *sarg = arg; + + switch (sarg->index) { + case AUDIO_RADIO: + /* Hauppauge uses IN2 for the radio */ + state->mode = MSP_MODE_FM_RADIO; + scart = SCART_IN2; + break; + case AUDIO_EXTERN_1: + /* IN1 is often used for external input ... */ + state->mode = MSP_MODE_EXTERN; + scart = SCART_IN1; + break; + case AUDIO_EXTERN_2: + /* ... sometimes it is IN2 through ;) */ + state->mode = MSP_MODE_EXTERN; + scart = SCART_IN2; + break; + case AUDIO_TUNER: + state->mode = -1; + break; + } + if (scart) { + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + state->audmode = V4L2_TUNER_MODE_STEREO; + msp_set_scart(client, scart, 0); + msp_write_dsp(client, 0x000d, 0x1900); + } + if (sarg->capability == V4L2_AUDCAP_STEREO) { + state->audmode = V4L2_TUNER_MODE_STEREO; + } else { + state->audmode &= ~V4L2_TUNER_MODE_STEREO; + } + msp_any_set_audmode(client, state->audmode); + msp_wake_thread(client); + break; + } + + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *vt = arg; + + if (state->radio) + break; + if (state->opmode == OPMODE_AUTOSELECT) + msp_any_detect_stereo(client); + vt->audmode = state->audmode; + vt->rxsubchans = state->rxsubchans; + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + break; + } + + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; + + if (state->radio) + break; + /* only set audmode */ + if (vt->audmode != -1 && vt->audmode != 0) + msp_any_set_audmode(client, vt->audmode); + break; + } + + case VIDIOC_G_AUDOUT: + { + struct v4l2_audioout *a = (struct v4l2_audioout *)arg; + int idx = a->index; + + memset(a, 0, sizeof(*a)); + + switch (idx) { + case 0: + strcpy(a->name, "Scart1 Out"); + break; + case 1: + strcpy(a->name, "Scart2 Out"); + break; + case 2: + strcpy(a->name, "I2S Out"); + break; + default: + return -EINVAL; + } + break; + + } + + case VIDIOC_S_AUDOUT: + { + struct v4l2_audioout *a = (struct v4l2_audioout *)arg; + + if (a->index < 0 || a->index > 2) + return -EINVAL; + + v4l_dbg(1, msp_debug, client, "Setting audio out on msp34xx to input %i\n", a->index); + msp_set_scart(client, state->in_scart, a->index + 1); + + break; + } + + case VIDIOC_INT_I2S_CLOCK_FREQ: + { + u32 *a = (u32 *)arg; + + v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a); + + switch (*a) { + case 1024000: + state->i2s_mode = 0; + break; + case 2048000: + state->i2s_mode = 1; + break; + default: + return -EINVAL; + } + break; + } + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(msp_qctrl_std); i++) + if (qc->id && qc->id == msp_qctrl_std[i].id) { + memcpy(qc, &msp_qctrl_std[i], sizeof(*qc)); + return 0; + } + if (!state->has_sound_processing) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(msp_qctrl_sound_processing); i++) + if (qc->id && qc->id == msp_qctrl_sound_processing[i].id) { + memcpy(qc, &msp_qctrl_sound_processing[i], sizeof(*qc)); + return 0; + } + return -EINVAL; + } + + case VIDIOC_G_CTRL: + return msp_get_ctrl(client, arg); + + case VIDIOC_S_CTRL: + return msp_set_ctrl(client, arg); + + case VIDIOC_LOG_STATUS: + { + const char *p; + + if (state->opmode == OPMODE_AUTOSELECT) + msp_any_detect_stereo(client); + v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", + client->name, state->rev1, state->rev2); + v4l_info(client, "Audio: volume %d%s\n", + state->volume, state->muted ? " (muted)" : ""); + if (state->has_sound_processing) { + v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n", + state->balance, state->bass, state->treble, + state->loudness ? "on" : "off"); + } + switch (state->mode) { + case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; + case MSP_MODE_FM_RADIO: p = "FM Radio"; break; + case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono + FM-stereo"; break; + case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break; + case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break; + case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break; + case MSP_MODE_AM_NICAM: p = "NICAM/AM (L)"; break; + case MSP_MODE_BTSC: p = "BTSC"; break; + case MSP_MODE_EXTERN: p = "External input"; break; + default: p = "unknown"; break; + } + if (state->opmode == OPMODE_MANUAL) { + v4l_info(client, "Mode: %s (%s%s)\n", p, + (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", + (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); + } else { + v4l_info(client, "Mode: %s\n", p); + v4l_info(client, "Standard: %s (%s%s)\n", + msp_standard_std_name(state->std), + (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", + (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); + } + v4l_info(client, "ACB: 0x%04x\n", state->acb); + break; + } + + default: + /* unknown */ + return -EINVAL; + } + return 0; +} + +static int msp_suspend(struct device * dev, pm_message_t state) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + v4l_dbg(1, msp_debug, client, "suspend\n"); + msp_reset(client); + return 0; +} + +static int msp_resume(struct device * dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + v4l_dbg(1, msp_debug, client, "resume\n"); + msp_wake_thread(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver; + +static int msp_attach(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct msp_state *state; + int (*thread_func)(void *data) = NULL; + int msp_hard; + int msp_family; + int msp_revision; + int msp_product, msp_prod_hi, msp_prod_lo; + int msp_rom; + + client = kmalloc(sizeof(*client), GFP_KERNEL); + if (client == NULL) + return -ENOMEM; + memset(client, 0, sizeof(*client)); + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver; + snprintf(client->name, sizeof(client->name) - 1, "msp3400"); + + if (msp_reset(client) == -1) { + v4l_dbg(1, msp_debug, client, "msp3400 not found\n"); + kfree(client); + return -1; + } + + state = kmalloc(sizeof(*state), GFP_KERNEL); + if (state == NULL) { + kfree(client); + return -ENOMEM; + } + i2c_set_clientdata(client, state); + + memset(state, 0, sizeof(*state)); + state->v4l2_std = V4L2_STD_NTSC; + state->volume = 58880; /* 0db gain */ + state->balance = 32768; /* 0db gain */ + state->bass = 32768; + state->treble = 32768; + state->loudness = 0; + state->input = -1; + state->muted = 0; + state->i2s_mode = 0; + init_waitqueue_head(&state->wq); + + state->rev1 = msp_read_dsp(client, 0x1e); + if (state->rev1 != -1) + state->rev2 = msp_read_dsp(client, 0x1f); + v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n", state->rev1, state->rev2); + if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) { + v4l_dbg(1, msp_debug, client, "not an msp3400 (cannot read chip version)\n"); + kfree(state); + kfree(client); + return -1; + } + + msp_set_audio(client); + + msp_family = ((state->rev1 >> 4) & 0x0f) + 3; + msp_product = (state->rev2 >> 8) & 0xff; + msp_prod_hi = msp_product / 10; + msp_prod_lo = msp_product % 10; + msp_revision = (state->rev1 & 0x0f) + '@'; + msp_hard = ((state->rev1 >> 8) & 0xff) + '@'; + msp_rom = state->rev2 & 0x1f; + snprintf(client->name, sizeof(client->name), "MSP%d4%02d%c-%c%d", + msp_family, msp_product, + msp_revision, msp_hard, msp_rom); + + /* Has NICAM support: all mspx41x and mspx45x products have NICAM */ + state->has_nicam = msp_prod_hi == 1 || msp_prod_hi == 5; + /* Has radio support: was added with revision G */ + state->has_radio = msp_revision >= 'G'; + /* Has headphones output: not for stripped down products */ + state->has_headphones = msp_prod_lo < 5; + /* Has scart4 input: not in pre D revisions, not in stripped D revs */ + state->has_scart4 = msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5); + /* Has scart2 and scart3 inputs and scart2 output: not in stripped + down products of the '3' family */ + state->has_scart23_in_scart2_out = msp_family >= 4 || msp_prod_lo < 5; + /* Has scart2 a volume control? Not in pre-D revisions. */ + state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart23_in_scart2_out; + /* Has a configurable i2s out? */ + state->has_i2s_conf = msp_revision >= 'G' && msp_prod_lo < 7; + /* Has subwoofer output: not in pre-D revs and not in stripped down products */ + state->has_subwoofer = msp_revision >= 'D' && msp_prod_lo < 5; + /* Has soundprocessing (bass/treble/balance/loudness/equalizer): not in + stripped down products */ + state->has_sound_processing = msp_prod_lo < 7; + /* Has Virtual Dolby Surround: only in msp34x1 */ + state->has_virtual_dolby_surround = msp_revision == 'G' && msp_prod_lo == 1; + /* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */ + state->has_dolby_pro_logic = msp_revision == 'G' && msp_prod_lo == 2; + + state->opmode = opmode; + if (state->opmode == OPMODE_AUTO) { + /* MSP revision G and up have both autodetect and autoselect */ + if (msp_revision >= 'G') + state->opmode = OPMODE_AUTOSELECT; + /* MSP revision D and up have autodetect */ + else if (msp_revision >= 'D') + state->opmode = OPMODE_AUTODETECT; + else + state->opmode = OPMODE_MANUAL; + } + + /* hello world :-) */ + v4l_info(client, "%s found @ 0x%x (%s)\n", client->name, address << 1, adapter->name); + v4l_info(client, "%s ", client->name); + if (state->has_nicam && state->has_radio) + printk("supports nicam and radio, "); + else if (state->has_nicam) + printk("supports nicam, "); + else if (state->has_radio) + printk("supports radio, "); + printk("mode is "); + + /* version-specific initialization */ + switch (state->opmode) { + case OPMODE_MANUAL: + printk("manual"); + thread_func = msp3400c_thread; + break; + case OPMODE_AUTODETECT: + printk("autodetect"); + thread_func = msp3410d_thread; + break; + case OPMODE_AUTOSELECT: + printk("autodetect and autoselect"); + thread_func = msp34xxg_thread; + break; + } + printk("\n"); + + /* startup control thread if needed */ + if (thread_func) { + state->kthread = kthread_run(thread_func, client, "msp34xx"); + + if (state->kthread == NULL) + v4l_warn(client, "kernel_thread() failed\n"); + msp_wake_thread(client); + } + + /* done */ + i2c_attach_client(client); + + return 0; +} + +static int msp_probe(struct i2c_adapter *adapter) +{ + if (adapter->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adapter, &addr_data, msp_attach); + return 0; +} + +static int msp_detach(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + int err; + + /* shutdown control thread */ + if (state->kthread) { + state->restart = 1; + kthread_stop(state->kthread); + } + msp_reset(client); + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(state); + kfree(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +static struct i2c_driver i2c_driver = { + .id = I2C_DRIVERID_MSP3400, + .attach_adapter = msp_probe, + .detach_client = msp_detach, + .command = msp_command, + .driver = { + .name = "msp3400", + .suspend = msp_suspend, + .resume = msp_resume, + }, +}; + +static int __init msp3400_init_module(void) +{ + return i2c_add_driver(&i2c_driver); +} + +static void __exit msp3400_cleanup_module(void) +{ + i2c_del_driver(&i2c_driver); +} + +module_init(msp3400_init_module); +module_exit(msp3400_cleanup_module); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c new file mode 100644 index 00000000000..2072c3efebb --- /dev/null +++ b/drivers/media/video/msp3400-kthreads.c @@ -0,0 +1,1010 @@ +/* + * Programming the mspx4xx sound processor family + * + * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/videodev.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/audiochip.h> +#include <linux/kthread.h> +#include <linux/suspend.h> +#include "msp3400.h" + +/* this one uses the automatic sound standard detection of newer msp34xx + chip versions */ +static struct { + int retval; + int main, second; + char *name; +} msp_stdlist[] = { + { 0x0000, 0, 0, "could not detect sound standard" }, + { 0x0001, 0, 0, "autodetect start" }, + { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" }, + { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" }, + { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" }, + { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" }, + { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" }, + { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" }, + { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" }, + { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" }, + { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" }, + { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" }, + { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" }, + { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" }, + { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" }, + { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" }, + { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" }, + { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" }, + { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, + { -1, 0, 0, NULL }, /* EOF */ +}; + +static struct msp3400c_init_data_dem { + int fir1[6]; + int fir2[6]; + int cdo1; + int cdo2; + int ad_cv; + int mode_reg; + int dsp_src; + int dsp_matrix; +} msp3400c_init_data[] = { + { /* AM (for carrier detect / msp3400) */ + {75, 19, 36, 35, 39, 40}, + {75, 19, 36, 35, 39, 40}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0500, 0x0020, 0x3000 + },{ /* AM (for carrier detect / msp3410) */ + {-1, -1, -8, 2, 59, 126}, + {-1, -1, -8, 2, 59, 126}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0100, 0x0020, 0x3000 + },{ /* FM Radio */ + {-8, -8, 4, 6, 78, 107}, + {-8, -8, 4, 6, 78, 107}, + MSP_CARRIER(10.7), MSP_CARRIER(10.7), + 0x00d0, 0x0480, 0x0020, 0x3000 + },{ /* Terrestial FM-mono + FM-stereo */ + {3, 18, 27, 48, 66, 72}, + {3, 18, 27, 48, 66, 72}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0480, 0x0030, 0x3000 + },{ /* Sat FM-mono */ + { 1, 9, 14, 24, 33, 37}, + { 3, 18, 27, 48, 66, 72}, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), + 0x00c6, 0x0480, 0x0000, 0x3000 + },{ /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ + {-2, -8, -10, 10, 50, 86}, + {3, 18, 27, 48, 66, 72}, + MSP_CARRIER(5.5), MSP_CARRIER(5.5), + 0x00d0, 0x0040, 0x0120, 0x3000 + },{ /* NICAM/FM -- I (6.0/6.552) */ + {2, 4, -6, -4, 40, 94}, + {3, 18, 27, 48, 66, 72}, + MSP_CARRIER(6.0), MSP_CARRIER(6.0), + 0x00d0, 0x0040, 0x0120, 0x3000 + },{ /* NICAM/AM -- L (6.5/5.85) */ + {-2, -8, -10, 10, 50, 86}, + {-4, -12, -9, 23, 79, 126}, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), + 0x00c6, 0x0140, 0x0120, 0x7c03 + }, +}; + +struct msp3400c_carrier_detect { + int cdo; + char *name; +}; + +static struct msp3400c_carrier_detect msp3400c_carrier_detect_main[] = { + /* main carrier */ + { MSP_CARRIER(4.5), "4.5 NTSC" }, + { MSP_CARRIER(5.5), "5.5 PAL B/G" }, + { MSP_CARRIER(6.0), "6.0 PAL I" }, + { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } +}; + +static struct msp3400c_carrier_detect msp3400c_carrier_detect_55[] = { + /* PAL B/G */ + { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, + { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } +}; + +static struct msp3400c_carrier_detect msp3400c_carrier_detect_65[] = { + /* PAL SAT / SECAM */ + { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, + { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, + { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, + { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, + { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, + { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, +}; + +/* ------------------------------------------------------------------------ */ + +const char *msp_standard_std_name(int std) +{ + int i; + + for (i = 0; msp_stdlist[i].name != NULL; i++) + if (msp_stdlist[i].retval == std) + return msp_stdlist[i].name; + return "unknown"; +} + +void msp3400c_setcarrier(struct i2c_client *client, int cdo1, int cdo2) +{ + msp_write_dem(client, 0x0093, cdo1 & 0xfff); + msp_write_dem(client, 0x009b, cdo1 >> 12); + msp_write_dem(client, 0x00a3, cdo2 & 0xfff); + msp_write_dem(client, 0x00ab, cdo2 >> 12); + msp_write_dem(client, 0x0056, 0); /*LOAD_REG_1/2*/ +} + +void msp3400c_setmode(struct i2c_client *client, int type) +{ + struct msp_state *state = i2c_get_clientdata(client); + int i; + + v4l_dbg(1, msp_debug, client, "setmode: %d\n", type); + state->mode = type; + state->audmode = V4L2_TUNER_MODE_MONO; + state->rxsubchans = V4L2_TUNER_SUB_MONO; + + msp_write_dem(client, 0x00bb, msp3400c_init_data[type].ad_cv); + + for (i = 5; i >= 0; i--) /* fir 1 */ + msp_write_dem(client, 0x0001, msp3400c_init_data[type].fir1[i]); + + msp_write_dem(client, 0x0005, 0x0004); /* fir 2 */ + msp_write_dem(client, 0x0005, 0x0040); + msp_write_dem(client, 0x0005, 0x0000); + for (i = 5; i >= 0; i--) + msp_write_dem(client, 0x0005, msp3400c_init_data[type].fir2[i]); + + msp_write_dem(client, 0x0083, msp3400c_init_data[type].mode_reg); + + msp3400c_setcarrier(client, msp3400c_init_data[type].cdo1, + msp3400c_init_data[type].cdo2); + + msp_write_dem(client, 0x0056, 0); /*LOAD_REG_1/2*/ + + if (msp_dolby) { + msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */ + msp_write_dsp(client, 0x0009, 0x0620); /* I2S2 */ + msp_write_dsp(client, 0x000b, msp3400c_init_data[type].dsp_src); + } else { + msp_write_dsp(client, 0x0008, msp3400c_init_data[type].dsp_src); + msp_write_dsp(client, 0x0009, msp3400c_init_data[type].dsp_src); + msp_write_dsp(client, 0x000b, msp3400c_init_data[type].dsp_src); + } + msp_write_dsp(client, 0x000a, msp3400c_init_data[type].dsp_src); + msp_write_dsp(client, 0x000e, msp3400c_init_data[type].dsp_matrix); + + if (state->has_nicam) { + /* nicam prescale */ + msp_write_dsp(client, 0x0010, 0x5a00); /* was: 0x3000 */ + } +} + +/* turn on/off nicam + stereo */ +void msp3400c_setstereo(struct i2c_client *client, int mode) +{ + static char *strmode[] = { "mono", "stereo", "lang2", "lang1" }; + struct msp_state *state = i2c_get_clientdata(client); + int nicam = 0; /* channel source: FM/AM or nicam */ + int src = 0; + + if (state->opmode == OPMODE_AUTOSELECT) { + /* this method would break everything, let's make sure + * it's never called + */ + v4l_dbg(1, msp_debug, client, "setstereo called with mode=%d instead of set_source (ignored)\n", + mode); + return; + } + + /* switch demodulator */ + switch (state->mode) { + case MSP_MODE_FM_TERRA: + v4l_dbg(1, msp_debug, client, "FM setstereo: %s\n", strmode[mode]); + msp3400c_setcarrier(client, state->second, state->main); + switch (mode) { + case V4L2_TUNER_MODE_STEREO: + msp_write_dsp(client, 0x000e, 0x3001); + break; + case V4L2_TUNER_MODE_MONO: + case V4L2_TUNER_MODE_LANG1: + case V4L2_TUNER_MODE_LANG2: + msp_write_dsp(client, 0x000e, 0x3000); + break; + } + break; + case MSP_MODE_FM_SAT: + v4l_dbg(1, msp_debug, client, "SAT setstereo: %s\n", strmode[mode]); + switch (mode) { + case V4L2_TUNER_MODE_MONO: + msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); + break; + case V4L2_TUNER_MODE_STEREO: + msp3400c_setcarrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); + break; + case V4L2_TUNER_MODE_LANG1: + msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + break; + case V4L2_TUNER_MODE_LANG2: + msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + break; + } + break; + case MSP_MODE_FM_NICAM1: + case MSP_MODE_FM_NICAM2: + case MSP_MODE_AM_NICAM: + v4l_dbg(1, msp_debug, client, "NICAM setstereo: %s\n",strmode[mode]); + msp3400c_setcarrier(client,state->second,state->main); + if (state->nicam_on) + nicam=0x0100; + break; + case MSP_MODE_BTSC: + v4l_dbg(1, msp_debug, client, "BTSC setstereo: %s\n",strmode[mode]); + nicam=0x0300; + break; + case MSP_MODE_EXTERN: + v4l_dbg(1, msp_debug, client, "extern setstereo: %s\n",strmode[mode]); + nicam = 0x0200; + break; + case MSP_MODE_FM_RADIO: + v4l_dbg(1, msp_debug, client, "FM-Radio setstereo: %s\n",strmode[mode]); + break; + default: + v4l_dbg(1, msp_debug, client, "mono setstereo\n"); + return; + } + + /* switch audio */ + switch (mode) { + case V4L2_TUNER_MODE_STEREO: + src = 0x0020 | nicam; + break; + case V4L2_TUNER_MODE_MONO: + if (state->mode == MSP_MODE_AM_NICAM) { + v4l_dbg(1, msp_debug, client, "switching to AM mono\n"); + /* AM mono decoding is handled by tuner, not MSP chip */ + /* SCART switching control register */ + msp_set_scart(client, SCART_MONO, 0); + src = 0x0200; + break; + } + case V4L2_TUNER_MODE_LANG1: + src = 0x0000 | nicam; + break; + case V4L2_TUNER_MODE_LANG2: + src = 0x0010 | nicam; + break; + } + v4l_dbg(1, msp_debug, client, "setstereo final source/matrix = 0x%x\n", src); + + if (msp_dolby) { + msp_write_dsp(client, 0x0008, 0x0520); + msp_write_dsp(client, 0x0009, 0x0620); + msp_write_dsp(client, 0x000a, src); + msp_write_dsp(client, 0x000b, src); + } else { + msp_write_dsp(client, 0x0008, src); + msp_write_dsp(client, 0x0009, src); + msp_write_dsp(client, 0x000a, src); + msp_write_dsp(client, 0x000b, src); + msp_write_dsp(client, 0x000c, src); + if (state->has_scart23_in_scart2_out) + msp_write_dsp(client, 0x0041, src); + } +} + +static void msp3400c_print_mode(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + + if (state->main == state->second) { + v4l_dbg(1, msp_debug, client, "mono sound carrier: %d.%03d MHz\n", + state->main / 910000, (state->main / 910) % 1000); + } else { + v4l_dbg(1, msp_debug, client, "main sound carrier: %d.%03d MHz\n", + state->main / 910000, (state->main / 910) % 1000); + } + if (state->mode == MSP_MODE_FM_NICAM1 || state->mode == MSP_MODE_FM_NICAM2) + v4l_dbg(1, msp_debug, client, "NICAM/FM carrier : %d.%03d MHz\n", + state->second / 910000, (state->second/910) % 1000); + if (state->mode == MSP_MODE_AM_NICAM) + v4l_dbg(1, msp_debug, client, "NICAM/AM carrier : %d.%03d MHz\n", + state->second / 910000, (state->second / 910) % 1000); + if (state->mode == MSP_MODE_FM_TERRA && state->main != state->second) { + v4l_dbg(1, msp_debug, client, "FM-stereo carrier : %d.%03d MHz\n", + state->second / 910000, (state->second / 910) % 1000); + } +} + +/* ----------------------------------------------------------------------- */ + +int autodetect_stereo(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + int val; + int rxsubchans = state->rxsubchans; + int newnicam = state->nicam_on; + int update = 0; + + switch (state->mode) { + case MSP_MODE_FM_TERRA: + val = msp_read_dsp(client, 0x18); + if (val > 32767) + val -= 65536; + v4l_dbg(2, msp_debug, client, "stereo detect register: %d\n", val); + if (val > 4096) { + rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + } else if (val < -4096) { + rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + } else { + rxsubchans = V4L2_TUNER_SUB_MONO; + } + newnicam = 0; + break; + case MSP_MODE_FM_NICAM1: + case MSP_MODE_FM_NICAM2: + case MSP_MODE_AM_NICAM: + val = msp_read_dem(client, 0x23); + v4l_dbg(2, msp_debug, client, "nicam sync=%d, mode=%d\n", + val & 1, (val & 0x1e) >> 1); + + if (val & 1) { + /* nicam synced */ + switch ((val & 0x1e) >> 1) { + case 0: + case 8: + rxsubchans = V4L2_TUNER_SUB_STEREO; + break; + case 1: + case 9: + rxsubchans = V4L2_TUNER_SUB_MONO + | V4L2_TUNER_SUB_LANG1; + break; + case 2: + case 10: + rxsubchans = V4L2_TUNER_SUB_MONO + | V4L2_TUNER_SUB_LANG1 + | V4L2_TUNER_SUB_LANG2; + break; + default: + rxsubchans = V4L2_TUNER_SUB_MONO; + break; + } + newnicam = 1; + } else { + newnicam = 0; + rxsubchans = V4L2_TUNER_SUB_MONO; + } + break; + case MSP_MODE_BTSC: + val = msp_read_dem(client, 0x200); + v4l_dbg(2, msp_debug, client, "status=0x%x (pri=%s, sec=%s, %s%s%s)\n", + val, + (val & 0x0002) ? "no" : "yes", + (val & 0x0004) ? "no" : "yes", + (val & 0x0040) ? "stereo" : "mono", + (val & 0x0080) ? ", nicam 2nd mono" : "", + (val & 0x0100) ? ", bilingual/SAP" : ""); + rxsubchans = V4L2_TUNER_SUB_MONO; + if (val & 0x0040) rxsubchans |= V4L2_TUNER_SUB_STEREO; + if (val & 0x0100) rxsubchans |= V4L2_TUNER_SUB_LANG1; + break; + } + if (rxsubchans != state->rxsubchans) { + update = 1; + v4l_dbg(1, msp_debug, client, "watch: rxsubchans %d => %d\n", + state->rxsubchans,rxsubchans); + state->rxsubchans = rxsubchans; + } + if (newnicam != state->nicam_on) { + update = 1; + v4l_dbg(1, msp_debug, client, "watch: nicam %d => %d\n", + state->nicam_on,newnicam); + state->nicam_on = newnicam; + } + return update; +} + +/* + * A kernel thread for msp3400 control -- we don't want to block the + * in the ioctl while doing the sound carrier & stereo detect + */ +/* stereo/multilang monitoring */ +static void watch_stereo(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + + if (autodetect_stereo(client)) { + if (state->rxsubchans & V4L2_TUNER_SUB_STEREO) + msp3400c_setstereo(client, V4L2_TUNER_MODE_STEREO); + else if (state->rxsubchans & V4L2_TUNER_SUB_LANG1) + msp3400c_setstereo(client, V4L2_TUNER_MODE_LANG1); + else + msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); + } + + if (msp_once) + state->watch_stereo = 0; +} + +int msp3400c_thread(void *data) +{ + struct i2c_client *client = data; + struct msp_state *state = i2c_get_clientdata(client); + struct msp3400c_carrier_detect *cd; + int count, max1,max2,val1,val2, val,this; + + + v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n"); + for (;;) { + v4l_dbg(2, msp_debug, client, "msp3400 thread: sleep\n"); + msp_sleep(state, -1); + v4l_dbg(2, msp_debug, client, "msp3400 thread: wakeup\n"); + + restart: + v4l_dbg(1, msp_debug, client, "thread: restart scan\n"); + state->restart = 0; + if (kthread_should_stop()) + break; + + if (state->radio || MSP_MODE_EXTERN == state->mode) { + /* no carrier scan, just unmute */ + v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n"); + msp_set_audio(client); + continue; + } + + /* mute */ + msp_set_mute(client); + msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); + val1 = val2 = 0; + max1 = max2 = -1; + state->watch_stereo = 0; + + /* some time for the tuner to sync */ + if (msp_sleep(state,200)) + goto restart; + + /* carrier detect pass #1 -- main carrier */ + cd = msp3400c_carrier_detect_main; + count = ARRAY_SIZE(msp3400c_carrier_detect_main); + + if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { + /* autodetect doesn't work well with AM ... */ + max1 = 3; + count = 0; + v4l_dbg(1, msp_debug, client, "AM sound override\n"); + } + + for (this = 0; this < count; this++) { + msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); + if (msp_sleep(state,100)) + goto restart; + val = msp_read_dsp(client, 0x1b); + if (val > 32767) + val -= 65536; + if (val1 < val) + val1 = val, max1 = this; + v4l_dbg(1, msp_debug, client, "carrier1 val: %5d / %s\n", val,cd[this].name); + } + + /* carrier detect pass #2 -- second (stereo) carrier */ + switch (max1) { + case 1: /* 5.5 */ + cd = msp3400c_carrier_detect_55; + count = ARRAY_SIZE(msp3400c_carrier_detect_55); + break; + case 3: /* 6.5 */ + cd = msp3400c_carrier_detect_65; + count = ARRAY_SIZE(msp3400c_carrier_detect_65); + break; + case 0: /* 4.5 */ + case 2: /* 6.0 */ + default: + cd = NULL; + count = 0; + break; + } + + if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { + /* autodetect doesn't work well with AM ... */ + cd = NULL; + count = 0; + max2 = 0; + } + for (this = 0; this < count; this++) { + msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); + if (msp_sleep(state,100)) + goto restart; + val = msp_read_dsp(client, 0x1b); + if (val > 32767) + val -= 65536; + if (val2 < val) + val2 = val, max2 = this; + v4l_dbg(1, msp_debug, client, "carrier2 val: %5d / %s\n", val,cd[this].name); + } + + /* program the msp3400 according to the results */ + state->main = msp3400c_carrier_detect_main[max1].cdo; + switch (max1) { + case 1: /* 5.5 */ + if (max2 == 0) { + /* B/G FM-stereo */ + state->second = msp3400c_carrier_detect_55[max2].cdo; + msp3400c_setmode(client, MSP_MODE_FM_TERRA); + state->nicam_on = 0; + msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); + state->watch_stereo = 1; + } else if (max2 == 1 && state->has_nicam) { + /* B/G NICAM */ + state->second = msp3400c_carrier_detect_55[max2].cdo; + msp3400c_setmode(client, MSP_MODE_FM_NICAM1); + state->nicam_on = 1; + msp3400c_setcarrier(client, state->second, state->main); + state->watch_stereo = 1; + } else { + goto no_second; + } + break; + case 2: /* 6.0 */ + /* PAL I NICAM */ + state->second = MSP_CARRIER(6.552); + msp3400c_setmode(client, MSP_MODE_FM_NICAM2); + state->nicam_on = 1; + msp3400c_setcarrier(client, state->second, state->main); + state->watch_stereo = 1; + break; + case 3: /* 6.5 */ + if (max2 == 1 || max2 == 2) { + /* D/K FM-stereo */ + state->second = msp3400c_carrier_detect_65[max2].cdo; + msp3400c_setmode(client, MSP_MODE_FM_TERRA); + state->nicam_on = 0; + msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); + state->watch_stereo = 1; + } else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) { + /* L NICAM or AM-mono */ + state->second = msp3400c_carrier_detect_65[max2].cdo; + msp3400c_setmode(client, MSP_MODE_AM_NICAM); + state->nicam_on = 0; + msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); + msp3400c_setcarrier(client, state->second, state->main); + /* volume prescale for SCART (AM mono input) */ + msp_write_dsp(client, 0x000d, 0x1900); + state->watch_stereo = 1; + } else if (max2 == 0 && state->has_nicam) { + /* D/K NICAM */ + state->second = msp3400c_carrier_detect_65[max2].cdo; + msp3400c_setmode(client, MSP_MODE_FM_NICAM1); + state->nicam_on = 1; + msp3400c_setcarrier(client, state->second, state->main); + state->watch_stereo = 1; + } else { + goto no_second; + } + break; + case 0: /* 4.5 */ + default: + no_second: + state->second = msp3400c_carrier_detect_main[max1].cdo; + msp3400c_setmode(client, MSP_MODE_FM_TERRA); + state->nicam_on = 0; + msp3400c_setcarrier(client, state->second, state->main); + state->rxsubchans = V4L2_TUNER_SUB_MONO; + msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); + break; + } + + /* unmute */ + msp_set_audio(client); + + if (msp_debug) + msp3400c_print_mode(client); + + /* monitor tv audio mode */ + while (state->watch_stereo) { + if (msp_sleep(state,5000)) + goto restart; + watch_stereo(client); + } + } + v4l_dbg(1, msp_debug, client, "thread: exit\n"); + return 0; +} + + +int msp3410d_thread(void *data) +{ + struct i2c_client *client = data; + struct msp_state *state = i2c_get_clientdata(client); + int val, i, std; + + v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n"); + + for (;;) { + v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n"); + msp_sleep(state,-1); + v4l_dbg(2, msp_debug, client, "msp3410 thread: wakeup\n"); + + restart: + v4l_dbg(1, msp_debug, client, "thread: restart scan\n"); + state->restart = 0; + if (kthread_should_stop()) + break; + + if (state->mode == MSP_MODE_EXTERN) { + /* no carrier scan needed, just unmute */ + v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n"); + msp_set_audio(client); + continue; + } + + /* put into sane state (and mute) */ + msp_reset(client); + + /* some time for the tuner to sync */ + if (msp_sleep(state,200)) + goto restart; + + /* start autodetect */ + if (state->radio) + std = 0x40; + else + std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1; + state->watch_stereo = 0; + + if (msp_debug) + v4l_dbg(1, msp_debug, client, "setting standard: %s (0x%04x)\n", + msp_standard_std_name(std), std); + + if (std != 1) { + /* programmed some specific mode */ + val = std; + } else { + /* triggered autodetect */ + msp_write_dem(client, 0x20, std); + for (;;) { + if (msp_sleep(state, 100)) + goto restart; + + /* check results */ + val = msp_read_dem(client, 0x7e); + if (val < 0x07ff) + break; + v4l_dbg(1, msp_debug, client, "detection still in progress\n"); + } + } + for (i = 0; msp_stdlist[i].name != NULL; i++) + if (msp_stdlist[i].retval == val) + break; + v4l_dbg(1, msp_debug, client, "current standard: %s (0x%04x)\n", + msp_standard_std_name(val), val); + state->main = msp_stdlist[i].main; + state->second = msp_stdlist[i].second; + state->std = val; + + if (msp_amsound && !state->radio && (state->v4l2_std & V4L2_STD_SECAM) && + (val != 0x0009)) { + /* autodetection has failed, let backup */ + v4l_dbg(1, msp_debug, client, "autodetection failed," + " switching to backup standard: %s (0x%04x)\n", + msp_stdlist[8].name ? msp_stdlist[8].name : "unknown",val); + val = 0x0009; + msp_write_dem(client, 0x20, val); + } + + /* set various prescales */ + msp_write_dsp(client, 0x0d, 0x1900); /* scart */ + msp_write_dsp(client, 0x0e, 0x2403); /* FM */ + msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ + + /* set stereo */ + switch (val) { + case 0x0008: /* B/G NICAM */ + case 0x000a: /* I NICAM */ + if (val == 0x0008) + state->mode = MSP_MODE_FM_NICAM1; + else + state->mode = MSP_MODE_FM_NICAM2; + /* just turn on stereo */ + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + state->nicam_on = 1; + state->watch_stereo = 1; + msp3400c_setstereo(client,V4L2_TUNER_MODE_STEREO); + break; + case 0x0009: + state->mode = MSP_MODE_AM_NICAM; + state->rxsubchans = V4L2_TUNER_SUB_MONO; + state->nicam_on = 1; + msp3400c_setstereo(client,V4L2_TUNER_MODE_MONO); + state->watch_stereo = 1; + break; + case 0x0020: /* BTSC */ + /* just turn on stereo */ + state->mode = MSP_MODE_BTSC; + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + state->nicam_on = 0; + state->watch_stereo = 1; + msp3400c_setstereo(client,V4L2_TUNER_MODE_STEREO); + break; + case 0x0040: /* FM radio */ + state->mode = MSP_MODE_FM_RADIO; + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + state->audmode = V4L2_TUNER_MODE_STEREO; + state->nicam_on = 0; + state->watch_stereo = 0; + /* not needed in theory if we have radio, but + short programming enables carrier mute */ + msp3400c_setmode(client, MSP_MODE_FM_RADIO); + msp3400c_setcarrier(client, MSP_CARRIER(10.7), + MSP_CARRIER(10.7)); + /* scart routing */ + msp_set_scart(client,SCART_IN2,0); + /* msp34xx does radio decoding */ + msp_write_dsp(client, 0x08, 0x0020); + msp_write_dsp(client, 0x09, 0x0020); + msp_write_dsp(client, 0x0b, 0x0020); + break; + case 0x0003: + case 0x0004: + case 0x0005: + state->mode = MSP_MODE_FM_TERRA; + state->rxsubchans = V4L2_TUNER_SUB_MONO; + state->audmode = V4L2_TUNER_MODE_MONO; + state->nicam_on = 0; + state->watch_stereo = 1; + break; + } + + /* unmute, restore misc registers */ + msp_set_audio(client); + msp_write_dsp(client, 0x13, state->acb); + if (state->has_i2s_conf) + msp_write_dem(client, 0x40, state->i2s_mode); + + /* monitor tv audio mode */ + while (state->watch_stereo) { + if (msp_sleep(state,5000)) + goto restart; + watch_stereo(client); + } + } + v4l_dbg(1, msp_debug, client, "thread: exit\n"); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* msp34xxG + (autoselect no-thread) */ +/* this one uses both automatic standard detection and automatic sound */ +/* select which are available in the newer G versions */ +/* struct msp: only norm, acb and source are really used in this mode */ + +/* set the same 'source' for the loudspeaker, scart and quasi-peak detector + * the value for source is the same as bit 15:8 of DSP registers 0x08, + * 0x0a and 0x0c: 0=mono, 1=stereo or A|B, 2=SCART, 3=stereo or A, 4=stereo or B + * + * this function replaces msp3400c_setstereo + */ +static void msp34xxg_set_source(struct i2c_client *client, int source) +{ + struct msp_state *state = i2c_get_clientdata(client); + + /* fix matrix mode to stereo and let the msp choose what + * to output according to 'source', as recommended + * for MONO (source==0) downmixing set bit[7:0] to 0x30 + */ + int value = (source & 0x07) << 8 | (source == 0 ? 0x30 : 0x20); + + v4l_dbg(1, msp_debug, client, "set source to %d (0x%x)\n", source, value); + /* Loudspeaker Output */ + msp_write_dsp(client, 0x08, value); + /* SCART1 DA Output */ + msp_write_dsp(client, 0x0a, value); + /* Quasi-peak detector */ + msp_write_dsp(client, 0x0c, value); + /* + * set identification threshold. Personally, I + * I set it to a higher value that the default + * of 0x190 to ignore noisy stereo signals. + * this needs tuning. (recommended range 0x00a0-0x03c0) + * 0x7f0 = forced mono mode + */ + /* a2 threshold for stereo/bilingual */ + msp_write_dem(client, 0x22, msp_stereo_thresh); + state->source = source; +} + +/* (re-)initialize the msp34xxg, according to the current norm in state->norm + * return 0 if it worked, -1 if it failed + */ +static int msp34xxg_reset(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + int modus, std; + + if (msp_reset(client)) + return -1; + + /* make sure that input/output is muted (paranoid mode) */ + /* ACB, mute DSP input, mute SCART 1 */ + if (msp_write_dsp(client, 0x13, 0x0f20)) + return -1; + + if (state->has_i2s_conf) + msp_write_dem(client, 0x40, state->i2s_mode); + + /* step-by-step initialisation, as described in the manual */ + modus = msp_modus(client); + if (state->radio) + std = 0x40; + else + std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1; + modus &= ~0x03; /* STATUS_CHANGE = 0 */ + modus |= 0x01; /* AUTOMATIC_SOUND_DETECTION = 1 */ + if (msp_write_dem(client, 0x30, modus)) + return -1; + if (msp_write_dem(client, 0x20, std)) + return -1; + + /* write the dsps that may have an influence on + standard/audio autodetection right now */ + msp34xxg_set_source(client, state->source); + + /* AM/FM Prescale [15:8] 75khz deviation */ + if (msp_write_dsp(client, 0x0e, 0x3000)) + return -1; + + /* NICAM Prescale 9db gain (as recommended) */ + if (msp_write_dsp(client, 0x10, 0x5a00)) + return -1; + + return 0; +} + +int msp34xxg_thread(void *data) +{ + struct i2c_client *client = data; + struct msp_state *state = i2c_get_clientdata(client); + int val, std, i; + + v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n"); + + state->source = 1; /* default */ + for (;;) { + v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n"); + msp_sleep(state, -1); + v4l_dbg(2, msp_debug, client, "msp34xxg thread: wakeup\n"); + + restart: + v4l_dbg(1, msp_debug, client, "thread: restart scan\n"); + state->restart = 0; + if (kthread_should_stop()) + break; + + /* setup the chip*/ + msp34xxg_reset(client); + std = msp_standard; + if (std != 0x01) + goto unmute; + + /* watch autodetect */ + v4l_dbg(1, msp_debug, client, "triggered autodetect, waiting for result\n"); + for (i = 0; i < 10; i++) { + if (msp_sleep(state, 100)) + goto restart; + + /* check results */ + val = msp_read_dem(client, 0x7e); + if (val < 0x07ff) { + std = val; + break; + } + v4l_dbg(2, msp_debug, client, "detection still in progress\n"); + } + if (std == 1) { + v4l_dbg(1, msp_debug, client, "detection still in progress after 10 tries. giving up.\n"); + continue; + } + + unmute: + state->std = std; + v4l_dbg(1, msp_debug, client, "current standard: %s (0x%04x)\n", + msp_standard_std_name(std), std); + + /* unmute: dispatch sound to scart output, set scart volume */ + msp_set_audio(client); + + /* restore ACB */ + if (msp_write_dsp(client, 0x13, state->acb)) + return -1; + + msp_write_dem(client, 0x40, state->i2s_mode); + } + v4l_dbg(1, msp_debug, client, "thread: exit\n"); + return 0; +} + +void msp34xxg_detect_stereo(struct i2c_client *client) +{ + struct msp_state *state = i2c_get_clientdata(client); + + int status = msp_read_dem(client, 0x0200); + int is_bilingual = status & 0x100; + int is_stereo = status & 0x40; + + state->rxsubchans = 0; + if (is_stereo) + state->rxsubchans |= V4L2_TUNER_SUB_STEREO; + else + state->rxsubchans |= V4L2_TUNER_SUB_MONO; + if (is_bilingual) { + state->rxsubchans |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + /* I'm supposed to check whether it's SAP or not + * and set only LANG2/SAP in this case. Yet, the MSP + * does a lot of work to hide this and handle everything + * the same way. I don't want to work around it so unless + * this is a problem, I'll handle SAP just like lang1/lang2. + */ + } + v4l_dbg(1, msp_debug, client, "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n", + status, is_stereo, is_bilingual, state->rxsubchans); +} + +void msp34xxg_set_audmode(struct i2c_client *client, int audmode) +{ + struct msp_state *state = i2c_get_clientdata(client); + int source; + + switch (audmode) { + case V4L2_TUNER_MODE_MONO: + source = 0; /* mono only */ + break; + case V4L2_TUNER_MODE_STEREO: + source = 1; /* stereo or A|B, see comment in msp34xxg_get_v4l2_stereo() */ + /* problem: that could also mean 2 (scart input) */ + break; + case V4L2_TUNER_MODE_LANG1: + source = 3; /* stereo or A */ + break; + case V4L2_TUNER_MODE_LANG2: + source = 4; /* stereo or B */ + break; + default: + audmode = 0; + source = 1; + break; + } + state->audmode = audmode; + msp34xxg_set_source(client, source); +} + diff --git a/drivers/media/video/msp3400.c b/drivers/media/video/msp3400.c deleted file mode 100644 index a23fb033898..00000000000 --- a/drivers/media/video/msp3400.c +++ /dev/null @@ -1,2225 +0,0 @@ -/* - * programming the msp34* sound processor family - * - * (c) 1997-2001 Gerd Knorr <kraxel@bytesex.org> - * - * what works and what doesn't: - * - * AM-Mono - * Support for Hauppauge cards added (decoding handled by tuner) added by - * Frederic Crozat <fcrozat@mail.dotcom.fr> - * - * FM-Mono - * should work. The stereo modes are backward compatible to FM-mono, - * therefore FM-Mono should be allways available. - * - * FM-Stereo (B/G, used in germany) - * should work, with autodetect - * - * FM-Stereo (satellite) - * should work, no autodetect (i.e. default is mono, but you can - * switch to stereo -- untested) - * - * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) - * should work, with autodetect. Support for NICAM was added by - * Pekka Pietikainen <pp@netppl.fi> - * - * - * TODO: - * - better SAT support - * - * - * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch) - * using soundcore instead of OSS - * - */ - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/moduleparam.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/i2c.h> -#include <linux/videodev.h> -#include <linux/init.h> -#include <linux/smp_lock.h> -#include <linux/kthread.h> -#include <linux/suspend.h> -#include <asm/semaphore.h> -#include <asm/pgtable.h> - -#include <media/audiochip.h> -#include "msp3400.h" - -#define msp3400_dbg(fmt, arg...) \ - do { \ - if (debug) \ - printk(KERN_INFO "%s debug %d-%04x: " fmt, client->driver->name, \ - i2c_adapter_id(client->adapter), client->addr , ## arg); \ - } while (0) - -/* Medium volume debug. */ -#define msp3400_dbg_mediumvol(fmt, arg...) \ - do { \ - if (debug >= 2) \ - printk(KERN_INFO "%s debug %d-%04x: " fmt, client->driver->name, \ - i2c_adapter_id(client->adapter), client->addr , ## arg); \ - } while (0) - -/* High volume debug. Use with care. */ -#define msp3400_dbg_highvol(fmt, arg...) \ - do { \ - if (debug >= 16) \ - printk(KERN_INFO "%s debug %d-%04x: " fmt, client->driver->name, \ - i2c_adapter_id(client->adapter), client->addr , ## arg); \ - } while (0) - -#define msp3400_err(fmt, arg...) do { \ - printk(KERN_ERR "%s %d-%04x: " fmt, client->driver->name, \ - i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0) -#define msp3400_warn(fmt, arg...) do { \ - printk(KERN_WARNING "%s %d-%04x: " fmt, client->driver->name, \ - i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0) -#define msp3400_info(fmt, arg...) do { \ - printk(KERN_INFO "%s %d-%04x: " fmt, client->driver->name, \ - i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0) - -#define OPMODE_AUTO -1 -#define OPMODE_MANUAL 0 -#define OPMODE_SIMPLE 1 /* use short programming (>= msp3410 only) */ -#define OPMODE_SIMPLER 2 /* use shorter programming (>= msp34xxG) */ - -/* insmod parameters */ -static int opmode = OPMODE_AUTO; -static int debug = 0; /* debug output */ -static int once = 0; /* no continous stereo monitoring */ -static int amsound = 0; /* hard-wire AM sound at 6.5 Hz (france), - the autoscan seems work well only with FM... */ -static int standard = 1; /* Override auto detect of audio standard, if needed. */ -static int dolby = 0; - -static int stereo_threshold = 0x190; /* a2 threshold for stereo/bilingual - (msp34xxg only) 0x00a0-0x03c0 */ -#define DFP_COUNT 0x41 -static const int bl_dfp[] = { - 0x00, 0x01, 0x02, 0x03, 0x06, 0x08, 0x09, 0x0a, - 0x0b, 0x0d, 0x0e, 0x10 -}; - -#define IS_MSP34XX_G(msp) ((msp)->opmode==2) - -struct msp3400c { - int rev1,rev2; - - int opmode; - int nicam; - int mode; - int norm; - int stereo; - int nicam_on; - int acb; - int in_scart; - int i2s_mode; - int main, second; /* sound carrier */ - int input; - int source; /* see msp34xxg_set_source */ - - /* v4l2 */ - int audmode; - int rxsubchans; - - int muted; - int left, right; /* volume */ - int bass, treble; - - /* shadow register set */ - int dfp_regs[DFP_COUNT]; - - /* thread */ - struct task_struct *kthread; - wait_queue_head_t wq; - int restart:1; - int watch_stereo:1; -}; - -#define MIN(a,b) (((a)>(b))?(b):(a)) -#define MAX(a,b) (((a)>(b))?(a):(b)) -#define HAVE_NICAM(msp) (((msp->rev2>>8) & 0xff) != 00) -#define HAVE_SIMPLE(msp) ((msp->rev1 & 0xff) >= 'D'-'@') -#define HAVE_SIMPLER(msp) ((msp->rev1 & 0xff) >= 'G'-'@') -#define HAVE_RADIO(msp) ((msp->rev1 & 0xff) >= 'G'-'@') - -#define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ - -/* ---------------------------------------------------------------------- */ - -/* read-only */ -module_param(opmode, int, 0444); - -/* read-write */ -module_param(once, int, 0644); -module_param(debug, int, 0644); -module_param(stereo_threshold, int, 0644); -module_param(standard, int, 0644); -module_param(amsound, int, 0644); -module_param(dolby, int, 0644); - -MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Simple, 2=Simpler"); -MODULE_PARM_DESC(once, "No continuous stereo monitoring"); -MODULE_PARM_DESC(debug, "Enable debug messages"); -MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo"); -MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect"); -MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan"); -MODULE_PARM_DESC(dolby, "Activates Dolby processsing"); - -/* ---------------------------------------------------------------------- */ - -#define I2C_MSP3400C 0x80 -#define I2C_MSP3400C_ALT 0x88 - -#define I2C_MSP3400C_DEM 0x10 -#define I2C_MSP3400C_DFP 0x12 - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { - I2C_MSP3400C >> 1, - I2C_MSP3400C_ALT >> 1, - I2C_CLIENT_END -}; -I2C_CLIENT_INSMOD; - -MODULE_DESCRIPTION("device driver for msp34xx TV sound processor"); -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -/* ----------------------------------------------------------------------- */ -/* functions for talking to the MSP3400C Sound processor */ - -static int msp3400c_reset(struct i2c_client *client) -{ - /* reset and read revision code */ - static char reset_off[3] = { 0x00, 0x80, 0x00 }; - static char reset_on[3] = { 0x00, 0x00, 0x00 }; - static char write[3] = { I2C_MSP3400C_DFP + 1, 0x00, 0x1e }; - char read[2]; - struct i2c_msg reset[2] = { - { client->addr, I2C_M_IGNORE_NAK, 3, reset_off }, - { client->addr, I2C_M_IGNORE_NAK, 3, reset_on }, - }; - struct i2c_msg test[2] = { - { client->addr, 0, 3, write }, - { client->addr, I2C_M_RD, 2, read }, - }; - - msp3400_dbg_highvol("msp3400c_reset\n"); - if ( (1 != i2c_transfer(client->adapter,&reset[0],1)) || - (1 != i2c_transfer(client->adapter,&reset[1],1)) || - (2 != i2c_transfer(client->adapter,test,2)) ) { - msp3400_err("chip reset failed\n"); - return -1; - } - return 0; -} - -static int msp3400c_read(struct i2c_client *client, int dev, int addr) -{ - int err,retval; - - unsigned char write[3]; - unsigned char read[2]; - struct i2c_msg msgs[2] = { - { client->addr, 0, 3, write }, - { client->addr, I2C_M_RD, 2, read } - }; - - write[0] = dev+1; - write[1] = addr >> 8; - write[2] = addr & 0xff; - - for (err = 0; err < 3;) { - if (2 == i2c_transfer(client->adapter,msgs,2)) - break; - err++; - msp3400_warn("I/O error #%d (read 0x%02x/0x%02x)\n", err, - dev, addr); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(msecs_to_jiffies(10)); - } - if (3 == err) { - msp3400_warn("giving up, resetting chip. Sound will go off, sorry folks :-|\n"); - msp3400c_reset(client); - return -1; - } - retval = read[0] << 8 | read[1]; - msp3400_dbg_highvol("msp3400c_read(0x%x, 0x%x): 0x%x\n", dev, addr, retval); - return retval; -} - -static int msp3400c_write(struct i2c_client *client, int dev, int addr, int val) -{ - int err; - unsigned char buffer[5]; - - buffer[0] = dev; - buffer[1] = addr >> 8; - buffer[2] = addr & 0xff; - buffer[3] = val >> 8; - buffer[4] = val & 0xff; - - msp3400_dbg_highvol("msp3400c_write(0x%x, 0x%x, 0x%x)\n", dev, addr, val); - for (err = 0; err < 3;) { - if (5 == i2c_master_send(client, buffer, 5)) - break; - err++; - msp3400_warn("I/O error #%d (write 0x%02x/0x%02x)\n", err, - dev, addr); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(msecs_to_jiffies(10)); - } - if (3 == err) { - msp3400_warn("giving up, reseting chip. Sound will go off, sorry folks :-|\n"); - msp3400c_reset(client); - return -1; - } - return 0; -} - -/* ------------------------------------------------------------------------ */ - -/* This macro is allowed for *constants* only, gcc must calculate it - at compile time. Remember -- no floats in kernel mode */ -#define MSP_CARRIER(freq) ((int)((float)(freq/18.432)*(1<<24))) - -#define MSP_MODE_AM_DETECT 0 -#define MSP_MODE_FM_RADIO 2 -#define MSP_MODE_FM_TERRA 3 -#define MSP_MODE_FM_SAT 4 -#define MSP_MODE_FM_NICAM1 5 -#define MSP_MODE_FM_NICAM2 6 -#define MSP_MODE_AM_NICAM 7 -#define MSP_MODE_BTSC 8 -#define MSP_MODE_EXTERN 9 - -static struct MSP_INIT_DATA_DEM { - int fir1[6]; - int fir2[6]; - int cdo1; - int cdo2; - int ad_cv; - int mode_reg; - int dfp_src; - int dfp_matrix; -} msp_init_data[] = { - { /* AM (for carrier detect / msp3400) */ - {75, 19, 36, 35, 39, 40}, - {75, 19, 36, 35, 39, 40}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0500, 0x0020, 0x3000 - },{ /* AM (for carrier detect / msp3410) */ - {-1, -1, -8, 2, 59, 126}, - {-1, -1, -8, 2, 59, 126}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0100, 0x0020, 0x3000 - },{ /* FM Radio */ - {-8, -8, 4, 6, 78, 107}, - {-8, -8, 4, 6, 78, 107}, - MSP_CARRIER(10.7), MSP_CARRIER(10.7), - 0x00d0, 0x0480, 0x0020, 0x3000 - },{ /* Terrestial FM-mono + FM-stereo */ - {3, 18, 27, 48, 66, 72}, - {3, 18, 27, 48, 66, 72}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0480, 0x0030, 0x3000 - },{ /* Sat FM-mono */ - { 1, 9, 14, 24, 33, 37}, - { 3, 18, 27, 48, 66, 72}, - MSP_CARRIER(6.5), MSP_CARRIER(6.5), - 0x00c6, 0x0480, 0x0000, 0x3000 - },{ /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ - {-2, -8, -10, 10, 50, 86}, - {3, 18, 27, 48, 66, 72}, - MSP_CARRIER(5.5), MSP_CARRIER(5.5), - 0x00d0, 0x0040, 0x0120, 0x3000 - },{ /* NICAM/FM -- I (6.0/6.552) */ - {2, 4, -6, -4, 40, 94}, - {3, 18, 27, 48, 66, 72}, - MSP_CARRIER(6.0), MSP_CARRIER(6.0), - 0x00d0, 0x0040, 0x0120, 0x3000 - },{ /* NICAM/AM -- L (6.5/5.85) */ - {-2, -8, -10, 10, 50, 86}, - {-4, -12, -9, 23, 79, 126}, - MSP_CARRIER(6.5), MSP_CARRIER(6.5), - 0x00c6, 0x0140, 0x0120, 0x7c03 - }, -}; - -struct CARRIER_DETECT { - int cdo; - char *name; -}; - -static struct CARRIER_DETECT carrier_detect_main[] = { - /* main carrier */ - { MSP_CARRIER(4.5), "4.5 NTSC" }, - { MSP_CARRIER(5.5), "5.5 PAL B/G" }, - { MSP_CARRIER(6.0), "6.0 PAL I" }, - { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } -}; - -static struct CARRIER_DETECT carrier_detect_55[] = { - /* PAL B/G */ - { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, - { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } -}; - -static struct CARRIER_DETECT carrier_detect_65[] = { - /* PAL SAT / SECAM */ - { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, - { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, - { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, - { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, - { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, - { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, -}; - -#define CARRIER_COUNT(x) (sizeof(x)/sizeof(struct CARRIER_DETECT)) - -/* ----------------------------------------------------------------------- * - * bits 9 8 5 - SCART DSP input Select: - * 0 0 0 - SCART 1 to DSP input (reset position) - * 0 1 0 - MONO to DSP input - * 1 0 0 - SCART 2 to DSP input - * 1 1 1 - Mute DSP input - * - * bits 11 10 6 - SCART 1 Output Select: - * 0 0 0 - undefined (reset position) - * 0 1 0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS) - * 1 0 0 - MONO input to SCART 1 Output - * 1 1 0 - SCART 1 DA to SCART 1 Output - * 0 0 1 - SCART 2 DA to SCART 1 Output - * 0 1 1 - SCART 1 Input to SCART 1 Output - * 1 1 1 - Mute SCART 1 Output - * - * bits 13 12 7 - SCART 2 Output Select (for devices with 2 Output SCART): - * 0 0 0 - SCART 1 DA to SCART 2 Output (reset position) - * 0 1 0 - SCART 1 Input to SCART 2 Output - * 1 0 0 - MONO input to SCART 2 Output - * 0 0 1 - SCART 2 DA to SCART 2 Output - * 0 1 1 - SCART 2 Input to SCART 2 Output - * 1 1 0 - Mute SCART 2 Output - * - * Bits 4 to 0 should be zero. - * ----------------------------------------------------------------------- */ - -static int scarts[3][9] = { - /* MASK IN1 IN2 IN1_DA IN2_DA IN3 IN4 MONO MUTE */ - /* SCART DSP Input select */ - { 0x0320, 0x0000, 0x0200, -1, -1, 0x0300, 0x0020, 0x0100, 0x0320 }, - /* SCART1 Output select */ - { 0x0c40, 0x0440, 0x0400, 0x0c00, 0x0040, 0x0000, 0x0840, 0x0800, 0x0c40 }, - /* SCART2 Output select */ - { 0x3080, 0x1000, 0x1080, 0x0000, 0x0080, 0x2080, 0x3080, 0x2000, 0x3000 }, -}; - -static char *scart_names[] = { - "mask", "in1", "in2", "in1 da", "in2 da", "in3", "in4", "mono", "mute" -}; - -static void msp3400c_set_scart(struct i2c_client *client, int in, int out) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - - msp->in_scart=in; - - if (in >= 1 && in <= 8 && out >= 0 && out <= 2) { - if (-1 == scarts[out][in]) - return; - - msp->acb &= ~scarts[out][SCART_MASK]; - msp->acb |= scarts[out][in]; - } else - msp->acb = 0xf60; /* Mute Input and SCART 1 Output */ - - msp3400_dbg("scart switch: %s => %d (ACB=0x%04x)\n", - scart_names[in], out, msp->acb); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x13, msp->acb); - - /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */ - msp3400c_write(client,I2C_MSP3400C_DEM, 0x40, msp->i2s_mode); -} - -/* ------------------------------------------------------------------------ */ - -static void msp3400c_setcarrier(struct i2c_client *client, int cdo1, int cdo2) -{ - msp3400c_write(client,I2C_MSP3400C_DEM, 0x0093, cdo1 & 0xfff); - msp3400c_write(client,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12); - msp3400c_write(client,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff); - msp3400c_write(client,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12); - msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ -} - -static void msp3400c_setvolume(struct i2c_client *client, - int muted, int left, int right) - { - int vol = 0, val = 0, balance = 0; - - if (!muted) { - /* 0x7f instead if 0x73 here has sound quality issues, - * probably due to overmodulation + clipping ... */ - vol = (left > right) ? left : right; - val = (vol * 0x73 / 65535) << 8; - } - if (vol > 0) { - balance = ((right - left) * 127) / vol; - } - - msp3400_dbg("setvolume: mute=%s %d:%d v=0x%02x b=0x%02x\n", - muted ? "on" : "off", left, right, val >> 8, balance); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, - muted ? 0x1 : (val | 0x1)); - msp3400c_write(client, I2C_MSP3400C_DFP, 0x0001, balance << 8); -} - -static void msp3400c_setbass(struct i2c_client *client, int bass) -{ - int val = ((bass-32768) * 0x60 / 65535) << 8; - - msp3400_dbg("setbass: %d 0x%02x\n", bass, val >> 8); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */ -} - -static void msp3400c_settreble(struct i2c_client *client, int treble) -{ - int val = ((treble-32768) * 0x60 / 65535) << 8; - - msp3400_dbg("settreble: %d 0x%02x\n",treble, val>>8); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */ -} - -static void msp3400c_setmode(struct i2c_client *client, int type) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - int i; - - msp3400_dbg("setmode: %d\n",type); - msp->mode = type; - msp->audmode = V4L2_TUNER_MODE_MONO; - msp->rxsubchans = V4L2_TUNER_SUB_MONO; - - msp3400c_write(client,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ - msp_init_data[type].ad_cv); - - for (i = 5; i >= 0; i--) /* fir 1 */ - msp3400c_write(client,I2C_MSP3400C_DEM, 0x0001, - msp_init_data[type].fir1[i]); - - msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */ - msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0040); - msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0000); - for (i = 5; i >= 0; i--) - msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, - msp_init_data[type].fir2[i]); - - msp3400c_write(client,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */ - msp_init_data[type].mode_reg); - - msp3400c_setcarrier(client, msp_init_data[type].cdo1, - msp_init_data[type].cdo2); - - msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ - - if (dolby) { - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, - 0x0520); /* I2S1 */ - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, - 0x0620); /* I2S2 */ - msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, - msp_init_data[type].dfp_src); - } else { - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, - msp_init_data[type].dfp_src); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, - msp_init_data[type].dfp_src); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, - msp_init_data[type].dfp_src); - } - msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a, - msp_init_data[type].dfp_src); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, - msp_init_data[type].dfp_matrix); - - if (HAVE_NICAM(msp)) { - /* nicam prescale */ - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x5a00); /* was: 0x3000 */ - } -} - -/* given a bitmask of VIDEO_SOUND_XXX returns the "best" in the bitmask */ -static int best_video_sound(int rxsubchans) -{ - if (rxsubchans & V4L2_TUNER_SUB_STEREO) - return V4L2_TUNER_MODE_STEREO; - if (rxsubchans & V4L2_TUNER_SUB_LANG1) - return V4L2_TUNER_MODE_LANG1; - if (rxsubchans & V4L2_TUNER_SUB_LANG2) - return V4L2_TUNER_MODE_LANG2; - return V4L2_TUNER_MODE_MONO; -} - -/* turn on/off nicam + stereo */ -static void msp3400c_setstereo(struct i2c_client *client, int mode) -{ - static char *strmode[] = { "0", "mono", "stereo", "3", - "lang1", "5", "6", "7", "lang2" - }; - struct msp3400c *msp = i2c_get_clientdata(client); - int nicam = 0; /* channel source: FM/AM or nicam */ - int src = 0; - - if (IS_MSP34XX_G(msp)) { - /* this method would break everything, let's make sure - * it's never called - */ - msp3400_dbg - ("DEBUG WARNING setstereo called with mode=%d instead of set_source (ignored)\n", - mode); - return; - } - - /* switch demodulator */ - switch (msp->mode) { - case MSP_MODE_FM_TERRA: - msp3400_dbg("FM setstereo: %s\n", strmode[mode]); - msp3400c_setcarrier(client,msp->second,msp->main); - switch (mode) { - case V4L2_TUNER_MODE_STEREO: - msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3001); - break; - case V4L2_TUNER_MODE_MONO: - case V4L2_TUNER_MODE_LANG1: - case V4L2_TUNER_MODE_LANG2: - msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3000); - break; - } - break; - case MSP_MODE_FM_SAT: - msp3400_dbg("SAT setstereo: %s\n", strmode[mode]); - switch (mode) { - case V4L2_TUNER_MODE_MONO: - msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); - break; - case V4L2_TUNER_MODE_STEREO: - msp3400c_setcarrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); - break; - case V4L2_TUNER_MODE_LANG1: - msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); - break; - case V4L2_TUNER_MODE_LANG2: - msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); - break; - } - break; - case MSP_MODE_FM_NICAM1: - case MSP_MODE_FM_NICAM2: - case MSP_MODE_AM_NICAM: - msp3400_dbg("NICAM setstereo: %s\n",strmode[mode]); - msp3400c_setcarrier(client,msp->second,msp->main); - if (msp->nicam_on) - nicam=0x0100; - break; - case MSP_MODE_BTSC: - msp3400_dbg("BTSC setstereo: %s\n",strmode[mode]); - nicam=0x0300; - break; - case MSP_MODE_EXTERN: - msp3400_dbg("extern setstereo: %s\n",strmode[mode]); - nicam = 0x0200; - break; - case MSP_MODE_FM_RADIO: - msp3400_dbg("FM-Radio setstereo: %s\n",strmode[mode]); - break; - default: - msp3400_dbg("mono setstereo\n"); - return; - } - - /* switch audio */ - switch (best_video_sound(mode)) { - case V4L2_TUNER_MODE_STEREO: - src = 0x0020 | nicam; - break; - case V4L2_TUNER_MODE_MONO: - if (msp->mode == MSP_MODE_AM_NICAM) { - msp3400_dbg("switching to AM mono\n"); - /* AM mono decoding is handled by tuner, not MSP chip */ - /* SCART switching control register */ - msp3400c_set_scart(client,SCART_MONO,0); - src = 0x0200; - break; - } - case V4L2_TUNER_MODE_LANG1: - src = 0x0000 | nicam; - break; - case V4L2_TUNER_MODE_LANG2: - src = 0x0010 | nicam; - break; - } - msp3400_dbg("setstereo final source/matrix = 0x%x\n", src); - - if (dolby) { - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,0x0520); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,0x0620); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); - } else { - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,src); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,src); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); - } -} - -static void -msp3400c_print_mode(struct i2c_client *client) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - - if (msp->main == msp->second) { - msp3400_dbg("mono sound carrier: %d.%03d MHz\n", - msp->main/910000,(msp->main/910)%1000); - } else { - msp3400_dbg("main sound carrier: %d.%03d MHz\n", - msp->main/910000,(msp->main/910)%1000); - } - if (msp->mode == MSP_MODE_FM_NICAM1 || msp->mode == MSP_MODE_FM_NICAM2) - msp3400_dbg("NICAM/FM carrier : %d.%03d MHz\n", - msp->second/910000,(msp->second/910)%1000); - if (msp->mode == MSP_MODE_AM_NICAM) - msp3400_dbg("NICAM/AM carrier : %d.%03d MHz\n", - msp->second/910000,(msp->second/910)%1000); - if (msp->mode == MSP_MODE_FM_TERRA && - msp->main != msp->second) { - msp3400_dbg("FM-stereo carrier : %d.%03d MHz\n", - msp->second/910000,(msp->second/910)%1000); - } -} - -#define MSP3400_MAX 4 -static struct i2c_client *msps[MSP3400_MAX]; -static void msp3400c_restore_dfp(struct i2c_client *client) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - int i; - - for (i = 0; i < DFP_COUNT; i++) { - if (-1 == msp->dfp_regs[i]) - continue; - msp3400c_write(client, I2C_MSP3400C_DFP, i, msp->dfp_regs[i]); - } -} - -/* if the dfp_regs is set, set what's in there. Otherwise, set the default value */ -static int msp3400c_write_dfp_with_default(struct i2c_client *client, - int addr, int default_value) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - int value = default_value; - if (addr < DFP_COUNT && -1 != msp->dfp_regs[addr]) - value = msp->dfp_regs[addr]; - return msp3400c_write(client, I2C_MSP3400C_DFP, addr, value); -} - -/* ----------------------------------------------------------------------- */ - -struct REGISTER_DUMP { - int addr; - char *name; -}; - -struct REGISTER_DUMP d1[] = { - {0x007e, "autodetect"}, - {0x0023, "C_AD_BITS "}, - {0x0038, "ADD_BITS "}, - {0x003e, "CIB_BITS "}, - {0x0057, "ERROR_RATE"}, -}; - -static int autodetect_stereo(struct i2c_client *client) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - int val; - int rxsubchans = msp->rxsubchans; - int newnicam = msp->nicam_on; - int update = 0; - - switch (msp->mode) { - case MSP_MODE_FM_TERRA: - val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18); - if (val > 32767) - val -= 65536; - msp3400_dbg("stereo detect register: %d\n",val); - if (val > 4096) { - rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; - } else if (val < -4096) { - rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - } else { - rxsubchans = V4L2_TUNER_SUB_MONO; - } - newnicam = 0; - break; - case MSP_MODE_FM_NICAM1: - case MSP_MODE_FM_NICAM2: - case MSP_MODE_AM_NICAM: - val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x23); - msp3400_dbg("nicam sync=%d, mode=%d\n", - val & 1, (val & 0x1e) >> 1); - - if (val & 1) { - /* nicam synced */ - switch ((val & 0x1e) >> 1) { - case 0: - case 8: - rxsubchans = V4L2_TUNER_SUB_STEREO; - break; - case 1: - case 9: - rxsubchans = V4L2_TUNER_SUB_MONO - | V4L2_TUNER_SUB_LANG1; - break; - case 2: - case 10: - rxsubchans = V4L2_TUNER_SUB_MONO - | V4L2_TUNER_SUB_LANG1 - | V4L2_TUNER_SUB_LANG2; - break; - default: - rxsubchans = V4L2_TUNER_SUB_MONO; - break; - } - newnicam=1; - } else { - newnicam = 0; - rxsubchans = V4L2_TUNER_SUB_MONO; - } - break; - case MSP_MODE_BTSC: - val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200); - msp3400_dbg("status=0x%x (pri=%s, sec=%s, %s%s%s)\n", - val, - (val & 0x0002) ? "no" : "yes", - (val & 0x0004) ? "no" : "yes", - (val & 0x0040) ? "stereo" : "mono", - (val & 0x0080) ? ", nicam 2nd mono" : "", - (val & 0x0100) ? ", bilingual/SAP" : ""); - rxsubchans = V4L2_TUNER_SUB_MONO; - if (val & 0x0040) rxsubchans |= V4L2_TUNER_SUB_STEREO; - if (val & 0x0100) rxsubchans |= V4L2_TUNER_SUB_LANG1; - break; - } - if (rxsubchans != msp->rxsubchans) { - update = 1; - msp3400_dbg("watch: rxsubchans %d => %d\n", - msp->rxsubchans,rxsubchans); - msp->rxsubchans = rxsubchans; - } - if (newnicam != msp->nicam_on) { - update = 1; - msp3400_dbg("watch: nicam %d => %d\n", - msp->nicam_on,newnicam); - msp->nicam_on = newnicam; - } - return update; -} - -/* - * A kernel thread for msp3400 control -- we don't want to block the - * in the ioctl while doing the sound carrier & stereo detect - */ - -static int msp34xx_sleep(struct msp3400c *msp, int timeout) -{ - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&msp->wq, &wait); - if (!kthread_should_stop()) { - if (timeout < 0) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } else { - schedule_timeout_interruptible - (msecs_to_jiffies(timeout)); - } - } - - remove_wait_queue(&msp->wq, &wait); - try_to_freeze(); - return msp->restart; -} - -/* stereo/multilang monitoring */ -static void watch_stereo(struct i2c_client *client) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - - if (autodetect_stereo(client)) { - if (msp->stereo & V4L2_TUNER_MODE_STEREO) - msp3400c_setstereo(client, V4L2_TUNER_MODE_STEREO); - else if (msp->stereo & VIDEO_SOUND_LANG1) - msp3400c_setstereo(client, V4L2_TUNER_MODE_LANG1); - else - msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); - } - - if (once) - msp->watch_stereo = 0; -} - -static int msp3400c_thread(void *data) -{ - struct i2c_client *client = data; - struct msp3400c *msp = i2c_get_clientdata(client); - struct CARRIER_DETECT *cd; - int count, max1,max2,val1,val2, val,this; - - msp3400_info("msp3400 daemon started\n"); - for (;;) { - msp3400_dbg_mediumvol("msp3400 thread: sleep\n"); - msp34xx_sleep(msp,-1); - msp3400_dbg_mediumvol("msp3400 thread: wakeup\n"); - - restart: - msp3400_dbg("thread: restart scan\n"); - msp->restart = 0; - if (kthread_should_stop()) - break; - - if (VIDEO_MODE_RADIO == msp->norm || - MSP_MODE_EXTERN == msp->mode) { - /* no carrier scan, just unmute */ - msp3400_info("thread: no carrier scan\n"); - msp3400c_setvolume(client, msp->muted, msp->left, msp->right); - continue; - } - - /* mute */ - msp3400c_setvolume(client, msp->muted, 0, 0); - msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); - val1 = val2 = 0; - max1 = max2 = -1; - msp->watch_stereo = 0; - - /* some time for the tuner to sync */ - if (msp34xx_sleep(msp,200)) - goto restart; - - /* carrier detect pass #1 -- main carrier */ - cd = carrier_detect_main; - count = CARRIER_COUNT(carrier_detect_main); - - if (amsound && (msp->norm == VIDEO_MODE_SECAM)) { - /* autodetect doesn't work well with AM ... */ - max1 = 3; - count = 0; - msp3400_dbg("AM sound override\n"); - } - - for (this = 0; this < count; this++) { - msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); - if (msp34xx_sleep(msp,100)) - goto restart; - val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); - if (val > 32767) - val -= 65536; - if (val1 < val) - val1 = val, max1 = this; - msp3400_dbg("carrier1 val: %5d / %s\n", val,cd[this].name); - } - - /* carrier detect pass #2 -- second (stereo) carrier */ - switch (max1) { - case 1: /* 5.5 */ - cd = carrier_detect_55; - count = CARRIER_COUNT(carrier_detect_55); - break; - case 3: /* 6.5 */ - cd = carrier_detect_65; - count = CARRIER_COUNT(carrier_detect_65); - break; - case 0: /* 4.5 */ - case 2: /* 6.0 */ - default: - cd = NULL; - count = 0; - break; - } - - if (amsound && (msp->norm == VIDEO_MODE_SECAM)) { - /* autodetect doesn't work well with AM ... */ - cd = NULL; - count = 0; - max2 = 0; - } - for (this = 0; this < count; this++) { - msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); - if (msp34xx_sleep(msp,100)) - goto restart; - val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); - if (val > 32767) - val -= 65536; - if (val2 < val) - val2 = val, max2 = this; - msp3400_dbg("carrier2 val: %5d / %s\n", val,cd[this].name); - } - - /* programm the msp3400 according to the results */ - msp->main = carrier_detect_main[max1].cdo; - switch (max1) { - case 1: /* 5.5 */ - if (max2 == 0) { - /* B/G FM-stereo */ - msp->second = carrier_detect_55[max2].cdo; - msp3400c_setmode(client, MSP_MODE_FM_TERRA); - msp->nicam_on = 0; - msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); - msp->watch_stereo = 1; - } else if (max2 == 1 && HAVE_NICAM(msp)) { - /* B/G NICAM */ - msp->second = carrier_detect_55[max2].cdo; - msp3400c_setmode(client, MSP_MODE_FM_NICAM1); - msp->nicam_on = 1; - msp3400c_setcarrier(client, msp->second, msp->main); - msp->watch_stereo = 1; - } else { - goto no_second; - } - break; - case 2: /* 6.0 */ - /* PAL I NICAM */ - msp->second = MSP_CARRIER(6.552); - msp3400c_setmode(client, MSP_MODE_FM_NICAM2); - msp->nicam_on = 1; - msp3400c_setcarrier(client, msp->second, msp->main); - msp->watch_stereo = 1; - break; - case 3: /* 6.5 */ - if (max2 == 1 || max2 == 2) { - /* D/K FM-stereo */ - msp->second = carrier_detect_65[max2].cdo; - msp3400c_setmode(client, MSP_MODE_FM_TERRA); - msp->nicam_on = 0; - msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); - msp->watch_stereo = 1; - } else if (max2 == 0 && - msp->norm == VIDEO_MODE_SECAM) { - /* L NICAM or AM-mono */ - msp->second = carrier_detect_65[max2].cdo; - msp3400c_setmode(client, MSP_MODE_AM_NICAM); - msp->nicam_on = 0; - msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); - msp3400c_setcarrier(client, msp->second, msp->main); - /* volume prescale for SCART (AM mono input) */ - msp3400c_write(client,I2C_MSP3400C_DFP, 0x000d, 0x1900); - msp->watch_stereo = 1; - } else if (max2 == 0 && HAVE_NICAM(msp)) { - /* D/K NICAM */ - msp->second = carrier_detect_65[max2].cdo; - msp3400c_setmode(client, MSP_MODE_FM_NICAM1); - msp->nicam_on = 1; - msp3400c_setcarrier(client, msp->second, msp->main); - msp->watch_stereo = 1; - } else { - goto no_second; - } - break; - case 0: /* 4.5 */ - default: - no_second: - msp->second = carrier_detect_main[max1].cdo; - msp3400c_setmode(client, MSP_MODE_FM_TERRA); - msp->nicam_on = 0; - msp3400c_setcarrier(client, msp->second, msp->main); - msp->rxsubchans = V4L2_TUNER_SUB_MONO; - msp3400c_setstereo(client, V4L2_TUNER_MODE_MONO); - break; - } - - /* unmute */ - msp3400c_setvolume(client, msp->muted, msp->left, msp->right); - msp3400c_restore_dfp(client); - - if (debug) - msp3400c_print_mode(client); - - /* monitor tv audio mode */ - while (msp->watch_stereo) { - if (msp34xx_sleep(msp,5000)) - goto restart; - watch_stereo(client); - } - } - msp3400_dbg("thread: exit\n"); - return 0; -} - -/* ----------------------------------------------------------------------- */ -/* this one uses the automatic sound standard detection of newer */ -/* msp34xx chip versions */ - -static struct MODES { - int retval; - int main, second; - char *name; -} modelist[] = { - { 0x0000, 0, 0, "ERROR" }, - { 0x0001, 0, 0, "autodetect start" }, - { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" }, - { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" }, - { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" }, - { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" }, - { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" }, - { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" }, - { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" }, - { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" }, - { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" }, - { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" }, - { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" }, - { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" }, - { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" }, - { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" }, - { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" }, - { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" }, - { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, - { -1, 0, 0, NULL }, /* EOF */ -}; - -static inline const char *msp34xx_standard_mode_name(int mode) -{ - int i; - for (i = 0; modelist[i].name != NULL; i++) - if (modelist[i].retval == mode) - return modelist[i].name; - return "unknown"; -} - -static int msp34xx_modus(struct i2c_client *client, int norm) -{ - switch (norm) { - case VIDEO_MODE_PAL: - msp3400_dbg("video mode selected to PAL\n"); - -#if 1 - /* experimental: not sure this works with all chip versions */ - return 0x7003; -#else - /* previous value, try this if it breaks ... */ - return 0x1003; -#endif - case VIDEO_MODE_NTSC: /* BTSC */ - msp3400_dbg("video mode selected to NTSC\n"); - return 0x2003; - case VIDEO_MODE_SECAM: - msp3400_dbg("video mode selected to SECAM\n"); - return 0x0003; - case VIDEO_MODE_RADIO: - msp3400_dbg("video mode selected to Radio\n"); - return 0x0003; - case VIDEO_MODE_AUTO: - msp3400_dbg("video mode selected to Auto\n"); - return 0x2003; - default: - return 0x0003; - } -} - -static int msp34xx_standard(int norm) -{ - switch (norm) { - case VIDEO_MODE_PAL: - return 1; - case VIDEO_MODE_NTSC: /* BTSC */ - return 0x0020; - case VIDEO_MODE_SECAM: - return 1; - case VIDEO_MODE_RADIO: - return 0x0040; - default: - return 1; - } -} - -static int msp3410d_thread(void *data) -{ - struct i2c_client *client = data; - struct msp3400c *msp = i2c_get_clientdata(client); - int mode,val,i,std; - - msp3400_info("msp3410 daemon started\n"); - for (;;) { - msp3400_dbg_mediumvol("msp3410 thread: sleep\n"); - msp34xx_sleep(msp,-1); - msp3400_dbg_mediumvol("msp3410 thread: wakeup\n"); - - restart: - msp3400_dbg("thread: restart scan\n"); - msp->restart = 0; - if (kthread_should_stop()) - break; - - if (msp->mode == MSP_MODE_EXTERN) { - /* no carrier scan needed, just unmute */ - msp3400_dbg("thread: no carrier scan\n"); - msp3400c_setvolume(client, msp->muted, msp->left, msp->right); - continue; - } - - /* put into sane state (and mute) */ - msp3400c_reset(client); - - /* some time for the tuner to sync */ - if (msp34xx_sleep(msp,200)) - goto restart; - - /* start autodetect */ - mode = msp34xx_modus(client, msp->norm); - std = msp34xx_standard(msp->norm); - msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode); - msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std); - msp->watch_stereo = 0; - - if (debug) - msp3400_dbg("setting mode: %s (0x%04x)\n", - msp34xx_standard_mode_name(std) ,std); - - if (std != 1) { - /* programmed some specific mode */ - val = std; - } else { - /* triggered autodetect */ - for (;;) { - if (msp34xx_sleep(msp,100)) - goto restart; - - /* check results */ - val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); - if (val < 0x07ff) - break; - msp3400_dbg("detection still in progress\n"); - } - } - for (i = 0; modelist[i].name != NULL; i++) - if (modelist[i].retval == val) - break; - msp3400_dbg("current mode: %s (0x%04x)\n", - modelist[i].name ? modelist[i].name : "unknown", - val); - msp->main = modelist[i].main; - msp->second = modelist[i].second; - - if (amsound && (msp->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) { - /* autodetection has failed, let backup */ - msp3400_dbg("autodetection failed," - " switching to backup mode: %s (0x%04x)\n", - modelist[8].name ? modelist[8].name : "unknown",val); - val = 0x0009; - msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val); - } - - /* set various prescales */ - msp3400c_write(client, I2C_MSP3400C_DFP, 0x0d, 0x1900); /* scart */ - msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */ - msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* nicam */ - - /* set stereo */ - switch (val) { - case 0x0008: /* B/G NICAM */ - case 0x000a: /* I NICAM */ - if (val == 0x0008) - msp->mode = MSP_MODE_FM_NICAM1; - else - msp->mode = MSP_MODE_FM_NICAM2; - /* just turn on stereo */ - msp->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp->nicam_on = 1; - msp->watch_stereo = 1; - msp3400c_setstereo(client,V4L2_TUNER_MODE_STEREO); - break; - case 0x0009: - msp->mode = MSP_MODE_AM_NICAM; - msp->rxsubchans = V4L2_TUNER_SUB_MONO; - msp->nicam_on = 1; - msp3400c_setstereo(client,V4L2_TUNER_MODE_MONO); - msp->watch_stereo = 1; - break; - case 0x0020: /* BTSC */ - /* just turn on stereo */ - msp->mode = MSP_MODE_BTSC; - msp->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp->nicam_on = 0; - msp->watch_stereo = 1; - msp3400c_setstereo(client,V4L2_TUNER_MODE_STEREO); - break; - case 0x0040: /* FM radio */ - msp->mode = MSP_MODE_FM_RADIO; - msp->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp->audmode = V4L2_TUNER_MODE_STEREO; - msp->nicam_on = 0; - msp->watch_stereo = 0; - /* not needed in theory if HAVE_RADIO(), but - short programming enables carrier mute */ - msp3400c_setmode(client,MSP_MODE_FM_RADIO); - msp3400c_setcarrier(client, MSP_CARRIER(10.7), - MSP_CARRIER(10.7)); - /* scart routing */ - msp3400c_set_scart(client,SCART_IN2,0); - /* msp34xx does radio decoding */ - msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0020); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0020); - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0020); - break; - case 0x0003: - case 0x0004: - case 0x0005: - msp->mode = MSP_MODE_FM_TERRA; - msp->rxsubchans = V4L2_TUNER_SUB_MONO; - msp->audmode = V4L2_TUNER_MODE_MONO; - msp->nicam_on = 0; - msp->watch_stereo = 1; - break; - } - - /* unmute, restore misc registers */ - msp3400c_setbass(client, msp->bass); - msp3400c_settreble(client, msp->treble); - msp3400c_setvolume(client, msp->muted, msp->left, msp->right); - msp3400c_write(client, I2C_MSP3400C_DFP, 0x13, msp->acb); - msp3400c_write(client,I2C_MSP3400C_DEM, 0x40, msp->i2s_mode); - msp3400c_restore_dfp(client); - - /* monitor tv audio mode */ - while (msp->watch_stereo) { - if (msp34xx_sleep(msp,5000)) - goto restart; - watch_stereo(client); - } - } - msp3400_dbg("thread: exit\n"); - return 0; -} - -/* ----------------------------------------------------------------------- */ -/* msp34xxG + (simpler no-thread) */ -/* this one uses both automatic standard detection and automatic sound */ -/* select which are available in the newer G versions */ -/* struct msp: only norm, acb and source are really used in this mode */ - -static void msp34xxg_set_source(struct i2c_client *client, int source); - -/* (re-)initialize the msp34xxg, according to the current norm in msp->norm - * return 0 if it worked, -1 if it failed - */ -static int msp34xxg_reset(struct i2c_client *client) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - int modus,std; - - if (msp3400c_reset(client)) - return -1; - - /* make sure that input/output is muted (paranoid mode) */ - if (msp3400c_write(client, - I2C_MSP3400C_DFP, - 0x13, /* ACB */ - 0x0f20 /* mute DSP input, mute SCART 1 */)) - return -1; - - msp3400c_write(client,I2C_MSP3400C_DEM, 0x40, msp->i2s_mode); - - /* step-by-step initialisation, as described in the manual */ - modus = msp34xx_modus(client, msp->norm); - std = msp34xx_standard(msp->norm); - modus &= ~0x03; /* STATUS_CHANGE=0 */ - modus |= 0x01; /* AUTOMATIC_SOUND_DETECTION=1 */ - if (msp3400c_write(client, - I2C_MSP3400C_DEM, - 0x30/*MODUS*/, - modus)) - return -1; - if (msp3400c_write(client, - I2C_MSP3400C_DEM, - 0x20/*standard*/, - std)) - return -1; - - /* write the dfps that may have an influence on - standard/audio autodetection right now */ - msp34xxg_set_source(client, msp->source); - - if (msp3400c_write_dfp_with_default(client, 0x0e, /* AM/FM Prescale */ - 0x3000 - /* default: [15:8] 75khz deviation */ - )) - return -1; - - if (msp3400c_write_dfp_with_default(client, 0x10, /* NICAM Prescale */ - 0x5a00 - /* default: 9db gain (as recommended) */ - )) - return -1; - - return 0; -} - -static int msp34xxg_thread(void *data) -{ - struct i2c_client *client = data; - struct msp3400c *msp = i2c_get_clientdata(client); - int val, std, i; - - msp3400_info("msp34xxg daemon started\n"); - msp->source = 1; /* default */ - for (;;) { - msp3400_dbg_mediumvol("msp34xxg thread: sleep\n"); - msp34xx_sleep(msp,-1); - msp3400_dbg_mediumvol("msp34xxg thread: wakeup\n"); - - restart: - msp3400_dbg("thread: restart scan\n"); - msp->restart = 0; - if (kthread_should_stop()) - break; - - /* setup the chip*/ - msp34xxg_reset(client); - std = standard; - if (std != 0x01) - goto unmute; - - /* watch autodetect */ - msp3400_dbg("triggered autodetect, waiting for result\n"); - for (i = 0; i < 10; i++) { - if (msp34xx_sleep(msp,100)) - goto restart; - - /* check results */ - val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); - if (val < 0x07ff) { - std = val; - break; - } - msp3400_dbg("detection still in progress\n"); - } - if (0x01 == std) { - msp3400_dbg("detection still in progress after 10 tries. giving up.\n"); - continue; - } - - unmute: - msp3400_dbg("current mode: %s (0x%04x)\n", - msp34xx_standard_mode_name(std), std); - - /* unmute: dispatch sound to scart output, set scart volume */ - msp3400_dbg("unmute\n"); - - msp3400c_setbass(client, msp->bass); - msp3400c_settreble(client, msp->treble); - msp3400c_setvolume(client, msp->muted, msp->left, msp->right); - - /* restore ACB */ - if (msp3400c_write(client, - I2C_MSP3400C_DFP, - 0x13, /* ACB */ - msp->acb)) - return -1; - - msp3400c_write(client,I2C_MSP3400C_DEM, 0x40, msp->i2s_mode); - } - msp3400_dbg("thread: exit\n"); - return 0; -} - -/* set the same 'source' for the loudspeaker, scart and quasi-peak detector - * the value for source is the same as bit 15:8 of DFP registers 0x08, - * 0x0a and 0x0c: 0=mono, 1=stereo or A|B, 2=SCART, 3=stereo or A, 4=stereo or B - * - * this function replaces msp3400c_setstereo - */ -static void msp34xxg_set_source(struct i2c_client *client, int source) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - - /* fix matrix mode to stereo and let the msp choose what - * to output according to 'source', as recommended - * for MONO (source==0) downmixing set bit[7:0] to 0x30 - */ - int value = (source&0x07)<<8|(source==0 ? 0x30:0x20); - msp3400_dbg("set source to %d (0x%x)\n", source, value); - msp3400c_write(client, - I2C_MSP3400C_DFP, - 0x08, /* Loudspeaker Output */ - value); - msp3400c_write(client, - I2C_MSP3400C_DFP, - 0x0a, /* SCART1 DA Output */ - value); - msp3400c_write(client, - I2C_MSP3400C_DFP, - 0x0c, /* Quasi-peak detector */ - value); - /* - * set identification threshold. Personally, I - * I set it to a higher value that the default - * of 0x190 to ignore noisy stereo signals. - * this needs tuning. (recommended range 0x00a0-0x03c0) - * 0x7f0 = forced mono mode - */ - msp3400c_write(client, - I2C_MSP3400C_DEM, - 0x22, /* a2 threshold for stereo/bilingual */ - stereo_threshold); - msp->source=source; -} - -static void msp34xxg_detect_stereo(struct i2c_client *client) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - - int status = msp3400c_read(client, - I2C_MSP3400C_DEM, - 0x0200 /* STATUS */); - int is_bilingual = status&0x100; - int is_stereo = status&0x40; - - msp->rxsubchans = 0; - if (is_stereo) - msp->rxsubchans |= V4L2_TUNER_SUB_STEREO; - else - msp->rxsubchans |= V4L2_TUNER_SUB_MONO; - if (is_bilingual) { - msp->rxsubchans |= V4L2_TUNER_SUB_LANG1|V4L2_TUNER_SUB_LANG2; - /* I'm supposed to check whether it's SAP or not - * and set only LANG2/SAP in this case. Yet, the MSP - * does a lot of work to hide this and handle everything - * the same way. I don't want to work around it so unless - * this is a problem, I'll handle SAP just like lang1/lang2. - */ - } - msp3400_dbg("status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n", - status, is_stereo, is_bilingual, msp->rxsubchans); -} - -static void msp34xxg_set_audmode(struct i2c_client *client, int audmode) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - int source; - - switch (audmode) { - case V4L2_TUNER_MODE_MONO: - source=0; /* mono only */ - break; - case V4L2_TUNER_MODE_STEREO: - source=1; /* stereo or A|B, see comment in msp34xxg_get_v4l2_stereo() */ - /* problem: that could also mean 2 (scart input) */ - break; - case V4L2_TUNER_MODE_LANG1: - source=3; /* stereo or A */ - break; - case V4L2_TUNER_MODE_LANG2: - source=4; /* stereo or B */ - break; - default: - audmode = 0; - source = 1; - break; - } - msp->audmode = audmode; - msp34xxg_set_source(client, source); -} - - -/* ----------------------------------------------------------------------- */ - -static int msp_attach(struct i2c_adapter *adap, int addr, int kind); -static int msp_detach(struct i2c_client *client); -static int msp_probe(struct i2c_adapter *adap); -static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg); - -static int msp_suspend(struct device * dev, pm_message_t state); -static int msp_resume(struct device * dev); - -static void msp_wake_thread(struct i2c_client *client); - -static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "msp3400", - .id = I2C_DRIVERID_MSP3400, - .flags = I2C_DF_NOTIFY, - .attach_adapter = msp_probe, - .detach_client = msp_detach, - .command = msp_command, - .driver = { - .suspend = msp_suspend, - .resume = msp_resume, - }, -}; - -static struct i2c_client client_template = -{ - .name = "(unset)", - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, -}; - -static int msp_attach(struct i2c_adapter *adap, int addr, int kind) -{ - struct msp3400c *msp; - struct i2c_client *client = &client_template; - int (*thread_func)(void *data) = NULL; - int i; - - client_template.adapter = adap; - client_template.addr = addr; - - if (-1 == msp3400c_reset(&client_template)) { - msp3400_dbg("no chip found\n"); - return -1; - } - - if (NULL == (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) - return -ENOMEM; - memcpy(client,&client_template,sizeof(struct i2c_client)); - if (NULL == (msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL))) { - kfree(client); - return -ENOMEM; - } - - memset(msp,0,sizeof(struct msp3400c)); - msp->norm = VIDEO_MODE_NTSC; - msp->left = 58880; /* 0db gain */ - msp->right = 58880; /* 0db gain */ - msp->bass = 32768; - msp->treble = 32768; - msp->input = -1; - msp->muted = 0; - msp->i2s_mode = 0; - for (i = 0; i < DFP_COUNT; i++) - msp->dfp_regs[i] = -1; - - i2c_set_clientdata(client, msp); - init_waitqueue_head(&msp->wq); - - if (-1 == msp3400c_reset(client)) { - kfree(msp); - kfree(client); - msp3400_dbg("no chip found\n"); - return -1; - } - - msp->rev1 = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1e); - if (-1 != msp->rev1) - msp->rev2 = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1f); - if ((-1 == msp->rev1) || (0 == msp->rev1 && 0 == msp->rev2)) { - kfree(msp); - kfree(client); - msp3400_dbg("error while reading chip version\n"); - return -1; - } - msp3400_dbg("rev1=0x%04x, rev2=0x%04x\n", msp->rev1, msp->rev2); - - msp3400c_setvolume(client, msp->muted, msp->left, msp->right); - - snprintf(client->name, sizeof(client->name), "MSP%c4%02d%c-%c%d", - ((msp->rev1>>4)&0x0f) + '3', - (msp->rev2>>8)&0xff, (msp->rev1&0x0f)+'@', - ((msp->rev1>>8)&0xff)+'@', msp->rev2&0x1f); - - msp->opmode = opmode; - if (OPMODE_AUTO == msp->opmode) { - if (HAVE_SIMPLER(msp)) - msp->opmode = OPMODE_SIMPLER; - else if (HAVE_SIMPLE(msp)) - msp->opmode = OPMODE_SIMPLE; - else - msp->opmode = OPMODE_MANUAL; - } - - /* hello world :-) */ - msp3400_info("chip=%s", client->name); - if (HAVE_NICAM(msp)) - printk(" +nicam"); - if (HAVE_SIMPLE(msp)) - printk(" +simple"); - if (HAVE_SIMPLER(msp)) - printk(" +simpler"); - if (HAVE_RADIO(msp)) - printk(" +radio"); - - /* version-specific initialization */ - switch (msp->opmode) { - case OPMODE_MANUAL: - printk(" mode=manual"); - thread_func = msp3400c_thread; - break; - case OPMODE_SIMPLE: - printk(" mode=simple"); - thread_func = msp3410d_thread; - break; - case OPMODE_SIMPLER: - printk(" mode=simpler"); - thread_func = msp34xxg_thread; - break; - } - printk("\n"); - - /* startup control thread if needed */ - if (thread_func) { - msp->kthread = kthread_run(thread_func, client, "msp34xx"); - - if (NULL == msp->kthread) - msp3400_warn("kernel_thread() failed\n"); - msp_wake_thread(client); - } - - /* done */ - i2c_attach_client(client); - - /* update our own array */ - for (i = 0; i < MSP3400_MAX; i++) { - if (NULL == msps[i]) { - msps[i] = client; - break; - } - } - - return 0; -} - -static int msp_detach(struct i2c_client *client) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - int i; - - /* shutdown control thread */ - if (msp->kthread) { - msp->restart = 1; - kthread_stop(msp->kthread); - } - msp3400c_reset(client); - - /* update our own array */ - for (i = 0; i < MSP3400_MAX; i++) { - if (client == msps[i]) { - msps[i] = NULL; - break; - } - } - - i2c_detach_client(client); - - kfree(msp); - kfree(client); - return 0; -} - -static int msp_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, msp_attach); - return 0; -} - -static void msp_wake_thread(struct i2c_client *client) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - - if (NULL == msp->kthread) - return; - msp3400c_setvolume(client,msp->muted,0,0); - msp->watch_stereo = 0; - msp->restart = 1; - wake_up_interruptible(&msp->wq); -} - -/* ----------------------------------------------------------------------- */ - -static int mode_v4l2_to_v4l1(int rxsubchans) -{ - int mode = 0; - - if (rxsubchans & V4L2_TUNER_SUB_STEREO) - mode |= VIDEO_SOUND_STEREO; - if (rxsubchans & V4L2_TUNER_SUB_LANG2) - mode |= VIDEO_SOUND_LANG2; - if (rxsubchans & V4L2_TUNER_SUB_LANG1) - mode |= VIDEO_SOUND_LANG1; - if (0 == mode) - mode |= VIDEO_SOUND_MONO; - return mode; -} - -static int mode_v4l1_to_v4l2(int mode) -{ - if (mode & VIDEO_SOUND_STEREO) - return V4L2_TUNER_MODE_STEREO; - if (mode & VIDEO_SOUND_LANG2) - return V4L2_TUNER_MODE_LANG2; - if (mode & VIDEO_SOUND_LANG1) - return V4L2_TUNER_MODE_LANG1; - return V4L2_TUNER_MODE_MONO; -} - -static void msp_any_detect_stereo(struct i2c_client *client) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - - switch (msp->opmode) { - case OPMODE_MANUAL: - case OPMODE_SIMPLE: - autodetect_stereo(client); - break; - case OPMODE_SIMPLER: - msp34xxg_detect_stereo(client); - break; - } -} - -static void msp_any_set_audmode(struct i2c_client *client, int audmode) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - - switch (msp->opmode) { - case OPMODE_MANUAL: - case OPMODE_SIMPLE: - msp->watch_stereo = 0; - msp3400c_setstereo(client, audmode); - break; - case OPMODE_SIMPLER: - msp34xxg_set_audmode(client, audmode); - break; - } -} - - -static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct msp3400c *msp = i2c_get_clientdata(client); - __u16 *sarg = arg; - int scart = 0; - - switch (cmd) { - - case AUDC_SET_INPUT: - msp3400_dbg("AUDC_SET_INPUT(%d)\n",*sarg); - - if (*sarg == msp->input) - break; - msp->input = *sarg; - switch (*sarg) { - case AUDIO_RADIO: - /* Hauppauge uses IN2 for the radio */ - msp->mode = MSP_MODE_FM_RADIO; - scart = SCART_IN2; - break; - case AUDIO_EXTERN_1: - /* IN1 is often used for external input ... */ - msp->mode = MSP_MODE_EXTERN; - scart = SCART_IN1; - break; - case AUDIO_EXTERN_2: - /* ... sometimes it is IN2 through ;) */ - msp->mode = MSP_MODE_EXTERN; - scart = SCART_IN2; - break; - case AUDIO_TUNER: - msp->mode = -1; - break; - default: - if (*sarg & AUDIO_MUTE) - msp3400c_set_scart(client,SCART_MUTE,0); - break; - } - if (scart) { - msp->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp->audmode = V4L2_TUNER_MODE_STEREO; - msp3400c_set_scart(client,scart,0); - msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900); - if (msp->opmode != OPMODE_SIMPLER) - msp3400c_setstereo(client, msp->audmode); - } - msp_wake_thread(client); - break; - - case AUDC_SET_RADIO: - msp3400_dbg("AUDC_SET_RADIO\n"); - msp->norm = VIDEO_MODE_RADIO; - msp3400_dbg("switching to radio mode\n"); - msp->watch_stereo = 0; - switch (msp->opmode) { - case OPMODE_MANUAL: - /* set msp3400 to FM radio mode */ - msp3400c_setmode(client,MSP_MODE_FM_RADIO); - msp3400c_setcarrier(client, MSP_CARRIER(10.7), - MSP_CARRIER(10.7)); - msp3400c_setvolume(client, msp->muted, msp->left, msp->right); - break; - case OPMODE_SIMPLE: - case OPMODE_SIMPLER: - /* the thread will do for us */ - msp_wake_thread(client); - break; - } - break; - /* work-in-progress: hook to control the DFP registers */ - case MSP_SET_DFPREG: - { - struct msp_dfpreg *r = arg; - int i; - - if (r->reg < 0 || r->reg >= DFP_COUNT) - return -EINVAL; - for (i = 0; i < sizeof(bl_dfp) / sizeof(int); i++) - if (r->reg == bl_dfp[i]) - return -EINVAL; - msp->dfp_regs[r->reg] = r->value; - msp3400c_write(client, I2C_MSP3400C_DFP, r->reg, r->value); - return 0; - } - case MSP_GET_DFPREG: - { - struct msp_dfpreg *r = arg; - - if (r->reg < 0 || r->reg >= DFP_COUNT) - return -EINVAL; - r->value = msp3400c_read(client, I2C_MSP3400C_DFP, r->reg); - return 0; - } - - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOCGAUDIO: - { - struct video_audio *va = arg; - - msp3400_dbg("VIDIOCGAUDIO\n"); - va->flags |= VIDEO_AUDIO_VOLUME | - VIDEO_AUDIO_BASS | - VIDEO_AUDIO_TREBLE | - VIDEO_AUDIO_MUTABLE; - if (msp->muted) - va->flags |= VIDEO_AUDIO_MUTE; - - if (msp->muted) - va->flags |= VIDEO_AUDIO_MUTE; - va->volume = MAX(msp->left, msp->right); - va->balance = (32768 * MIN(msp->left, msp->right)) / - (va->volume ? va->volume : 1); - va->balance = (msp->left < msp->right) ? - (65535 - va->balance) : va->balance; - if (0 == va->volume) - va->balance = 32768; - va->bass = msp->bass; - va->treble = msp->treble; - - msp_any_detect_stereo(client); - va->mode = mode_v4l2_to_v4l1(msp->rxsubchans); - break; - } - case VIDIOCSAUDIO: - { - struct video_audio *va = arg; - - msp3400_dbg("VIDIOCSAUDIO\n"); - msp->muted = (va->flags & VIDEO_AUDIO_MUTE); - msp->left = (MIN(65536 - va->balance, 32768) * - va->volume) / 32768; - msp->right = (MIN(va->balance, 32768) * va->volume) / 32768; - msp->bass = va->bass; - msp->treble = va->treble; - msp3400_dbg("VIDIOCSAUDIO setting va->volume to %d\n", - va->volume); - msp3400_dbg("VIDIOCSAUDIO setting va->balance to %d\n", - va->balance); - msp3400_dbg("VIDIOCSAUDIO setting va->flags to %d\n", - va->flags); - msp3400_dbg("VIDIOCSAUDIO setting msp->left to %d\n", - msp->left); - msp3400_dbg("VIDIOCSAUDIO setting msp->right to %d\n", - msp->right); - msp3400_dbg("VIDIOCSAUDIO setting msp->bass to %d\n", - msp->bass); - msp3400_dbg("VIDIOCSAUDIO setting msp->treble to %d\n", - msp->treble); - msp3400_dbg("VIDIOCSAUDIO setting msp->mode to %d\n", - msp->mode); - msp3400c_setvolume(client, msp->muted, msp->left, msp->right); - msp3400c_setbass(client, msp->bass); - msp3400c_settreble(client, msp->treble); - - if (va->mode != 0 && msp->norm != VIDEO_MODE_RADIO) - msp_any_set_audmode(client,mode_v4l1_to_v4l2(va->mode)); - break; - } - - case VIDIOCSCHAN: - { - struct video_channel *vc = arg; - - msp3400_dbg("VIDIOCSCHAN (norm=%d)\n",vc->norm); - msp->norm = vc->norm; - msp_wake_thread(client); - break; - } - - case VIDIOCSFREQ: - case VIDIOC_S_FREQUENCY: - { - /* new channel -- kick audio carrier scan */ - msp3400_dbg("VIDIOCSFREQ\n"); - msp_wake_thread(client); - break; - } - - /* msp34xx specific */ - case MSP_SET_MATRIX: - { - struct msp_matrix *mspm = arg; - - msp3400_dbg("MSP_SET_MATRIX\n"); - msp3400c_set_scart(client, mspm->input, mspm->output); - break; - } - - /* --- v4l2 ioctls --- */ - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - - /*FIXME: use V4L2 mode flags on msp3400 instead of V4L1*/ - if (*id & V4L2_STD_PAL) { - msp->norm=VIDEO_MODE_PAL; - } else if (*id & V4L2_STD_SECAM) { - msp->norm=VIDEO_MODE_SECAM; - } else { - msp->norm=VIDEO_MODE_NTSC; - } - - msp_wake_thread(client); - return 0; - } - - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; - - if (i->index != 0) - return -EINVAL; - - i->type = V4L2_INPUT_TYPE_TUNER; - switch (i->index) { - case AUDIO_RADIO: - strcpy(i->name,"Radio"); - break; - case AUDIO_EXTERN_1: - strcpy(i->name,"Extern 1"); - break; - case AUDIO_EXTERN_2: - strcpy(i->name,"Extern 2"); - break; - case AUDIO_TUNER: - strcpy(i->name,"Television"); - break; - default: - return -EINVAL; - } - return 0; - } - - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *a = arg; - - memset(a,0,sizeof(*a)); - - switch (a->index) { - case AUDIO_RADIO: - strcpy(a->name,"Radio"); - break; - case AUDIO_EXTERN_1: - strcpy(a->name,"Extern 1"); - break; - case AUDIO_EXTERN_2: - strcpy(a->name,"Extern 2"); - break; - case AUDIO_TUNER: - strcpy(a->name,"Television"); - break; - default: - return -EINVAL; - } - - msp_any_detect_stereo(client); - if (msp->audmode == V4L2_TUNER_MODE_STEREO) { - a->capability=V4L2_AUDCAP_STEREO; - } - - break; - } - case VIDIOC_S_AUDIO: - { - struct v4l2_audio *sarg = arg; - - switch (sarg->index) { - case AUDIO_RADIO: - /* Hauppauge uses IN2 for the radio */ - msp->mode = MSP_MODE_FM_RADIO; - scart = SCART_IN2; - break; - case AUDIO_EXTERN_1: - /* IN1 is often used for external input ... */ - msp->mode = MSP_MODE_EXTERN; - scart = SCART_IN1; - break; - case AUDIO_EXTERN_2: - /* ... sometimes it is IN2 through ;) */ - msp->mode = MSP_MODE_EXTERN; - scart = SCART_IN2; - break; - case AUDIO_TUNER: - msp->mode = -1; - break; - } - if (scart) { - msp->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp->audmode = V4L2_TUNER_MODE_STEREO; - msp3400c_set_scart(client,scart,0); - msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900); - } - if (sarg->capability==V4L2_AUDCAP_STEREO) { - msp->audmode = V4L2_TUNER_MODE_STEREO; - } else { - msp->audmode &= ~V4L2_TUNER_MODE_STEREO; - } - msp_any_set_audmode(client, msp->audmode); - msp_wake_thread(client); - break; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; - - msp_any_detect_stereo(client); - vt->audmode = msp->audmode; - vt->rxsubchans = msp->rxsubchans; - vt->capability = V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1| - V4L2_TUNER_CAP_LANG2; - break; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; - - /* only set audmode */ - if (vt->audmode != -1 && vt->audmode != 0) - msp_any_set_audmode(client, vt->audmode); - break; - } - - case VIDIOC_G_AUDOUT: - { - struct v4l2_audioout *a=(struct v4l2_audioout *)arg; - int idx=a->index; - - memset(a,0,sizeof(*a)); - - switch (idx) { - case 0: - strcpy(a->name,"Scart1 Out"); - break; - case 1: - strcpy(a->name,"Scart2 Out"); - break; - case 2: - strcpy(a->name,"I2S Out"); - break; - default: - return -EINVAL; - } - break; - - } - case VIDIOC_S_AUDOUT: - { - struct v4l2_audioout *a=(struct v4l2_audioout *)arg; - - if (a->index<0||a->index>2) - return -EINVAL; - - if (a->index==2) { - if (a->mode == V4L2_AUDMODE_32BITS) - msp->i2s_mode=1; - else - msp->i2s_mode=0; - } - msp3400_dbg("Setting audio out on msp34xx to input %i, mode %i\n", - a->index,msp->i2s_mode); - msp3400c_set_scart(client,msp->in_scart,a->index+1); - - break; - } - - default: - /* nothing */ - break; - } - return 0; -} - -static int msp_suspend(struct device * dev, pm_message_t state) -{ - struct i2c_client *client = container_of(dev, struct i2c_client, dev); - - msp3400_dbg("msp34xx: suspend\n"); - msp3400c_reset(client); - return 0; -} - -static int msp_resume(struct device * dev) -{ - struct i2c_client *client = container_of(dev, struct i2c_client, dev); - - msp3400_dbg("msp34xx: resume\n"); - msp_wake_thread(client); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static int __init msp3400_init_module(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit msp3400_cleanup_module(void) -{ - i2c_del_driver(&driver); -} - -module_init(msp3400_init_module); -module_exit(msp3400_cleanup_module); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/msp3400.h b/drivers/media/video/msp3400.h index 2d9ff40f0b0..a9ac57d0700 100644 --- a/drivers/media/video/msp3400.h +++ b/drivers/media/video/msp3400.h @@ -6,21 +6,19 @@ /* ---------------------------------------------------------------------- */ -struct msp_dfpreg { - int reg; - int value; -}; - -struct msp_matrix { - int input; - int output; -}; - -#define MSP_SET_DFPREG _IOW('m',15,struct msp_dfpreg) -#define MSP_GET_DFPREG _IOW('m',16,struct msp_dfpreg) +/* This macro is allowed for *constants* only, gcc must calculate it + at compile time. Remember -- no floats in kernel mode */ +#define MSP_CARRIER(freq) ((int)((float)(freq / 18.432) * (1 << 24))) -/* ioctl for MSP_SET_MATRIX will have to be registered */ -#define MSP_SET_MATRIX _IOW('m',17,struct msp_matrix) +#define MSP_MODE_AM_DETECT 0 +#define MSP_MODE_FM_RADIO 2 +#define MSP_MODE_FM_TERRA 3 +#define MSP_MODE_FM_SAT 4 +#define MSP_MODE_FM_NICAM1 5 +#define MSP_MODE_FM_NICAM2 6 +#define MSP_MODE_AM_NICAM 7 +#define MSP_MODE_BTSC 8 +#define MSP_MODE_EXTERN 9 #define SCART_MASK 0 #define SCART_IN1 1 @@ -36,4 +34,84 @@ struct msp_matrix { #define SCART1_OUT 1 #define SCART2_OUT 2 +#define OPMODE_AUTO -1 +#define OPMODE_MANUAL 0 +#define OPMODE_AUTODETECT 1 /* use autodetect (>= msp3410 only) */ +#define OPMODE_AUTOSELECT 2 /* use autodetect & autoselect (>= msp34xxG) */ + +/* module parameters */ +extern int msp_debug; +extern int msp_once; +extern int msp_amsound; +extern int msp_standard; +extern int msp_dolby; +extern int msp_stereo_thresh; + +struct msp_state { + int rev1, rev2; + u8 has_nicam; + u8 has_radio; + u8 has_headphones; + u8 has_ntsc_jp_d_k3; + u8 has_scart4; + u8 has_scart23_in_scart2_out; + u8 has_scart2_out_volume; + u8 has_i2s_conf; + u8 has_subwoofer; + u8 has_sound_processing; + u8 has_virtual_dolby_surround; + u8 has_dolby_pro_logic; + + int radio; + int opmode; + int std; + int mode; + v4l2_std_id v4l2_std; + int nicam_on; + int acb; + int in_scart; + int i2s_mode; + int main, second; /* sound carrier */ + int input; + int source; /* see msp34xxg_set_source */ + + /* v4l2 */ + int audmode; + int rxsubchans; + + int volume, muted; + int balance, loudness; + int bass, treble; + + /* thread */ + struct task_struct *kthread; + wait_queue_head_t wq; + int restart:1; + int watch_stereo:1; +}; + +/* msp3400-driver.c */ +int msp_write_dem(struct i2c_client *client, int addr, int val); +int msp_write_dsp(struct i2c_client *client, int addr, int val); +int msp_read_dem(struct i2c_client *client, int addr); +int msp_read_dsp(struct i2c_client *client, int addr); +int msp_reset(struct i2c_client *client); +void msp_set_scart(struct i2c_client *client, int in, int out); +void msp_set_mute(struct i2c_client *client); +void msp_set_audio(struct i2c_client *client); +int msp_modus(struct i2c_client *client); +int msp_sleep(struct msp_state *state, int timeout); + +/* msp3400-kthreads.c */ +const char *msp_standard_std_name(int std); +void msp3400c_setcarrier(struct i2c_client *client, int cdo1, int cdo2); +void msp3400c_setmode(struct i2c_client *client, int type); +void msp3400c_setstereo(struct i2c_client *client, int mode); +int autodetect_stereo(struct i2c_client *client); +int msp3400c_thread(void *data); +int msp3410d_thread(void *data); +int msp34xxg_thread(void *data); +void msp34xxg_detect_stereo(struct i2c_client *client); +void msp34xxg_set_audmode(struct i2c_client *client, int audmode); + #endif /* MSP3400_H */ diff --git a/drivers/media/video/mt20xx.c b/drivers/media/video/mt20xx.c index 2180018f06d..c7c9f3f8715 100644 --- a/drivers/media/video/mt20xx.c +++ b/drivers/media/video/mt20xx.c @@ -20,6 +20,9 @@ module_param(tv_antenna, int, 0644); static unsigned int radio_antenna = 0; module_param(radio_antenna, int, 0644); +/* from tuner-core.c */ +extern int tuner_debug; + /* ---------------------------------------------------------------------- */ #define MT2032 0x04 @@ -350,8 +353,8 @@ static int mt2032_init(struct i2c_client *c) } while (xok != 1 ); t->xogc=xogc; - t->tv_freq = mt2032_set_tv_freq; - t->radio_freq = mt2032_set_radio_freq; + t->set_tv_freq = mt2032_set_tv_freq; + t->set_radio_freq = mt2032_set_radio_freq; return(1); } @@ -478,8 +481,8 @@ static int mt2050_init(struct i2c_client *c) i2c_master_recv(c,buf,1); tuner_dbg("mt2050: sro is %x\n",buf[0]); - t->tv_freq = mt2050_set_tv_freq; - t->radio_freq = mt2050_set_radio_freq; + t->set_tv_freq = mt2050_set_tv_freq; + t->set_radio_freq = mt2050_set_radio_freq; return 0; } @@ -491,9 +494,16 @@ int microtune_init(struct i2c_client *c) int company_code; memset(buf,0,sizeof(buf)); - t->tv_freq = NULL; - t->radio_freq = NULL; + t->set_tv_freq = NULL; + t->set_radio_freq = NULL; t->standby = NULL; + if (t->std & V4L2_STD_525_60) { + tuner_dbg("pinnacle ntsc\n"); + t->radio_if2 = 41300 * 1000; + } else { + tuner_dbg("pinnacle pal\n"); + t->radio_if2 = 33300 * 1000; + } name = "unknown"; i2c_master_send(c,buf,1); diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index d04793fb80f..8416ceff524 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -26,6 +26,7 @@ #include <media/saa7146_vv.h> #include <media/tuner.h> #include <linux/video_decoder.h> +#include <media/v4l2-common.h> #include "mxb.h" #include "tea6415c.h" @@ -176,12 +177,11 @@ static int mxb_probe(struct saa7146_dev* dev) return -ENODEV; } - mxb = (struct mxb*)kmalloc(sizeof(struct mxb), GFP_KERNEL); + mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); if( NULL == mxb ) { DEB_D(("not enough kernel memory.\n")); return -ENOMEM; } - memset(mxb, 0x0, sizeof(struct mxb)); mxb->i2c_adapter = (struct i2c_adapter) { .class = I2C_CLASS_TV_ANALOG, diff --git a/drivers/media/video/ovcamchip/ov6x20.c b/drivers/media/video/ovcamchip/ov6x20.c index b3f4d266ced..c04130dab12 100644 --- a/drivers/media/video/ovcamchip/ov6x20.c +++ b/drivers/media/video/ovcamchip/ov6x20.c @@ -178,10 +178,9 @@ static int ov6x20_init(struct i2c_client *c) if (rc < 0) return rc; - ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); if (!s) return -ENOMEM; - memset(s, 0, sizeof *s); s->auto_brt = 1; s->auto_exp = 1; diff --git a/drivers/media/video/ovcamchip/ov6x30.c b/drivers/media/video/ovcamchip/ov6x30.c index 6eab458ab79..73b94f51a85 100644 --- a/drivers/media/video/ovcamchip/ov6x30.c +++ b/drivers/media/video/ovcamchip/ov6x30.c @@ -141,10 +141,9 @@ static int ov6x30_init(struct i2c_client *c) if (rc < 0) return rc; - ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); if (!s) return -ENOMEM; - memset(s, 0, sizeof *s); s->auto_brt = 1; s->auto_exp = 1; diff --git a/drivers/media/video/ovcamchip/ov76be.c b/drivers/media/video/ovcamchip/ov76be.c index 29bbdc05e3b..11f6be924d8 100644 --- a/drivers/media/video/ovcamchip/ov76be.c +++ b/drivers/media/video/ovcamchip/ov76be.c @@ -105,10 +105,9 @@ static int ov76be_init(struct i2c_client *c) if (rc < 0) return rc; - ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); if (!s) return -ENOMEM; - memset(s, 0, sizeof *s); s->auto_brt = 1; s->auto_exp = 1; diff --git a/drivers/media/video/ovcamchip/ov7x10.c b/drivers/media/video/ovcamchip/ov7x10.c index 6c383d4b14f..5206e791392 100644 --- a/drivers/media/video/ovcamchip/ov7x10.c +++ b/drivers/media/video/ovcamchip/ov7x10.c @@ -115,10 +115,9 @@ static int ov7x10_init(struct i2c_client *c) if (rc < 0) return rc; - ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); if (!s) return -ENOMEM; - memset(s, 0, sizeof *s); s->auto_brt = 1; s->auto_exp = 1; diff --git a/drivers/media/video/ovcamchip/ov7x20.c b/drivers/media/video/ovcamchip/ov7x20.c index 3c8c48f338b..8e26ae338f3 100644 --- a/drivers/media/video/ovcamchip/ov7x20.c +++ b/drivers/media/video/ovcamchip/ov7x20.c @@ -232,10 +232,9 @@ static int ov7x20_init(struct i2c_client *c) if (rc < 0) return rc; - ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); + ov->spriv = s = kzalloc(sizeof *s, GFP_KERNEL); if (!s) return -ENOMEM; - memset(s, 0, sizeof *s); s->auto_brt = 1; s->auto_exp = DFL_AUTO_EXP; diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c index 2de34ebf067..e76b53d5909 100644 --- a/drivers/media/video/ovcamchip/ovcamchip_core.c +++ b/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -316,12 +316,11 @@ static int ovcamchip_attach(struct i2c_adapter *adap) c->adapter = adap; strcpy(c->name, "OV????"); - ov = kmalloc(sizeof *ov, GFP_KERNEL); + ov = kzalloc(sizeof *ov, GFP_KERNEL); if (!ov) { rc = -ENOMEM; goto no_ov; } - memset(ov, 0, sizeof *ov); i2c_set_clientdata(c, ov); rc = ovcamchip_detect(c); @@ -410,11 +409,11 @@ static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg) /* ----------------------------------------------------------------------- */ static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "ovcamchip", + .driver = { + .name = "ovcamchip", + }, .id = I2C_DRIVERID_OVCAMCHIP, .class = I2C_CLASS_CAM_DIGITAL, - .flags = I2C_DF_NOTIFY, .attach_adapter = ovcamchip_attach, .detach_client = ovcamchip_detach, .command = ovcamchip_command, diff --git a/drivers/media/video/planb.c b/drivers/media/video/planb.c index b19c33434ea..f3fc361bec9 100644 --- a/drivers/media/video/planb.c +++ b/drivers/media/video/planb.c @@ -76,9 +76,9 @@ static volatile struct planb_registers *planb_regs; static int def_norm = PLANB_DEF_NORM; /* default norm */ static int video_nr = -1; -MODULE_PARM(def_norm, "i"); +module_param(def_norm, int, 0); MODULE_PARM_DESC(def_norm, "Default startup norm (0=PAL, 1=NTSC, 2=SECAM)"); -MODULE_PARM(video_nr,"i"); +module_param(video_nr, int, 0); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index 2504207b2e3..9e644863948 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -883,6 +883,7 @@ static struct file_operations pms_fops = { .open = video_exclusive_open, .release = video_exclusive_release, .ioctl = pms_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .read = pms_read, .llseek = no_llseek, }; diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c index b8054da31ff..2ce01020130 100644 --- a/drivers/media/video/saa5246a.c +++ b/drivers/media/video/saa5246a.c @@ -83,13 +83,12 @@ static int saa5246a_attach(struct i2c_adapter *adap, int addr, int kind) client_template.adapter = adap; client_template.addr = addr; memcpy(client, &client_template, sizeof(*client)); - t = kmalloc(sizeof(*t), GFP_KERNEL); + t = kzalloc(sizeof(*t), GFP_KERNEL); if(t==NULL) { kfree(client); return -ENOMEM; } - memset(t, 0, sizeof(*t)); strlcpy(client->name, IF_NAME, I2C_NAME_SIZE); init_MUTEX(&t->lock); @@ -151,25 +150,18 @@ static int saa5246a_detach(struct i2c_client *client) return 0; } -static int saa5246a_command(struct i2c_client *device, unsigned int cmd, - void *arg) -{ - return -EINVAL; -} - /* * I2C interfaces */ static struct i2c_driver i2c_driver_videotext = { - .owner = THIS_MODULE, - .name = IF_NAME, /* name */ + .driver = { + .name = IF_NAME, /* name */ + }, .id = I2C_DRIVERID_SAA5249, /* in i2c.h */ - .flags = I2C_DF_NOTIFY, .attach_adapter = saa5246a_probe, .detach_client = saa5246a_detach, - .command = saa5246a_command }; static struct i2c_client client_template = { diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index 7ffa2e9a9bf..5694eb58c3a 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -151,13 +151,12 @@ static int saa5249_attach(struct i2c_adapter *adap, int addr, int kind) client_template.adapter = adap; client_template.addr = addr; memcpy(client, &client_template, sizeof(*client)); - t = kmalloc(sizeof(*t), GFP_KERNEL); + t = kzalloc(sizeof(*t), GFP_KERNEL); if(t==NULL) { kfree(client); return -ENOMEM; } - memset(t, 0, sizeof(*t)); strlcpy(client->name, IF_NAME, I2C_NAME_SIZE); init_MUTEX(&t->lock); @@ -165,7 +164,7 @@ static int saa5249_attach(struct i2c_adapter *adap, int addr, int kind) * Now create a video4linux device */ - vd = (struct video_device *)kmalloc(sizeof(struct video_device), GFP_KERNEL); + vd = kmalloc(sizeof(struct video_device), GFP_KERNEL); if(vd==NULL) { kfree(t); @@ -226,23 +225,16 @@ static int saa5249_detach(struct i2c_client *client) return 0; } -static int saa5249_command(struct i2c_client *device, - unsigned int cmd, void *arg) -{ - return -EINVAL; -} - /* new I2C driver support */ static struct i2c_driver i2c_driver_videotext = { - .owner = THIS_MODULE, - .name = IF_NAME, /* name */ + .driver = { + .name = IF_NAME, /* name */ + }, .id = I2C_DRIVERID_SAA5249, /* in i2c.h */ - .flags = I2C_DF_NOTIFY, .attach_adapter = saa5249_probe, .detach_client = saa5249_detach, - .command = saa5249_command }; static struct i2c_client client_template = { @@ -709,6 +701,7 @@ static struct file_operations saa_fops = { .open = saa5249_open, .release = saa5249_release, .ioctl = saa5249_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c index dca3ddfd510..d17395c4f55 100644 --- a/drivers/media/video/saa6588.c +++ b/drivers/media/video/saa6588.c @@ -50,15 +50,15 @@ static unsigned int rbds = 0; static unsigned int plvl = 0; static unsigned int bufblocks = 100; -MODULE_PARM(debug, "i"); +module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable debug messages"); -MODULE_PARM(xtal, "i"); +module_param(xtal, int, 0); MODULE_PARM_DESC(xtal, "select oscillator frequency (0..3), default 0"); -MODULE_PARM(rbds, "i"); +module_param(rbds, int, 0); MODULE_PARM_DESC(rbds, "select mode, 0=RDS, 1=RBDS, default 0"); -MODULE_PARM(plvl, "i"); +module_param(plvl, int, 0); MODULE_PARM_DESC(plvl, "select pause level (0..3), default 0"); -MODULE_PARM(bufblocks, "i"); +module_param(bufblocks, int, 0); MODULE_PARM_DESC(bufblocks, "number of buffered blocks, default 100"); MODULE_DESCRIPTION("v4l2 driver module for SAA6588 RDS decoder"); @@ -422,24 +422,13 @@ static int saa6588_attach(struct i2c_adapter *adap, int addr, int kind) s->timer.function = saa6588_timer; s->timer.data = (unsigned long)s; schedule_work(&s->work); - return 0; } static int saa6588_probe(struct i2c_adapter *adap) { -#ifdef I2C_CLASS_TV_ANALOG if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, saa6588_attach); -#else - switch (adap->id) { - case I2C_HW_B_BT848: - case I2C_HW_B_RIVA: - case I2C_HW_SAA7134: - return i2c_probe(adap, &addr_data, saa6588_attach); - break; - } -#endif return 0; } @@ -496,10 +485,10 @@ static int saa6588_command(struct i2c_client *client, unsigned int cmd, /* ----------------------------------------------------------------------- */ static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "i2c saa6588 driver", + .driver = { + .name = "saa6588", + }, .id = -1, /* FIXME */ - .flags = I2C_DF_NOTIFY, .attach_adapter = saa6588_probe, .detach_client = saa6588_detach, .command = saa6588_command, @@ -507,7 +496,6 @@ static struct i2c_driver driver = { static struct i2c_client client_template = { .name = "saa6588", - .flags = I2C_CLIENT_ALLOW_USE, .driver = &driver, }; diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c index e116bdbed31..7bb85a7b326 100644 --- a/drivers/media/video/saa7110.c +++ b/drivers/media/video/saa7110.c @@ -494,22 +494,19 @@ saa7110_detect_client (struct i2c_adapter *adapter, I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_saa7110; - client->flags = I2C_CLIENT_ALLOW_USE; strlcpy(I2C_NAME(client), "saa7110", sizeof(I2C_NAME(client))); - decoder = kmalloc(sizeof(struct saa7110), GFP_KERNEL); + decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL); if (decoder == 0) { kfree(client); return -ENOMEM; } - memset(decoder, 0, sizeof(struct saa7110)); decoder->norm = VIDEO_MODE_PAL; decoder->input = 0; decoder->enable = 1; @@ -587,11 +584,11 @@ saa7110_detach_client (struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_driver i2c_driver_saa7110 = { - .owner = THIS_MODULE, - .name = "saa7110", + .driver = { + .name = "saa7110", + }, .id = I2C_DRIVERID_SAA7110, - .flags = I2C_DF_NOTIFY, .attach_adapter = saa7110_attach_adapter, .detach_client = saa7110_detach_client, diff --git a/drivers/media/video/saa7111.c b/drivers/media/video/saa7111.c index fe8a5e45396..8c06592b37f 100644 --- a/drivers/media/video/saa7111.c +++ b/drivers/media/video/saa7111.c @@ -511,22 +511,19 @@ saa7111_detect_client (struct i2c_adapter *adapter, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_saa7111; - client->flags = I2C_CLIENT_ALLOW_USE; strlcpy(I2C_NAME(client), "saa7111", sizeof(I2C_NAME(client))); - decoder = kmalloc(sizeof(struct saa7111), GFP_KERNEL); + decoder = kzalloc(sizeof(struct saa7111), GFP_KERNEL); if (decoder == NULL) { kfree(client); return -ENOMEM; } - memset(decoder, 0, sizeof(struct saa7111)); decoder->norm = VIDEO_MODE_NTSC; decoder->input = 0; decoder->enable = 1; @@ -590,11 +587,11 @@ saa7111_detach_client (struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_driver i2c_driver_saa7111 = { - .owner = THIS_MODULE, - .name = "saa7111", + .driver = { + .name = "saa7111", + }, .id = I2C_DRIVERID_SAA7111A, - .flags = I2C_DF_NOTIFY, .attach_adapter = saa7111_attach_adapter, .detach_client = saa7111_detach_client, diff --git a/drivers/media/video/saa7114.c b/drivers/media/video/saa7114.c index d9f50e2f7b9..fd0a4b4ef01 100644 --- a/drivers/media/video/saa7114.c +++ b/drivers/media/video/saa7114.c @@ -852,22 +852,19 @@ saa7114_detect_client (struct i2c_adapter *adapter, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_saa7114; - client->flags = I2C_CLIENT_ALLOW_USE; strlcpy(I2C_NAME(client), "saa7114", sizeof(I2C_NAME(client))); - decoder = kmalloc(sizeof(struct saa7114), GFP_KERNEL); + decoder = kzalloc(sizeof(struct saa7114), GFP_KERNEL); if (decoder == NULL) { kfree(client); return -ENOMEM; } - memset(decoder, 0, sizeof(struct saa7114)); decoder->norm = VIDEO_MODE_NTSC; decoder->input = -1; decoder->enable = 1; @@ -1204,11 +1201,11 @@ saa7114_detach_client (struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_driver i2c_driver_saa7114 = { - .owner = THIS_MODULE, - .name = "saa7114", + .driver = { + .name = "saa7114", + }, .id = I2C_DRIVERID_SAA7114, - .flags = I2C_DF_NOTIFY, .attach_adapter = saa7114_attach_adapter, .detach_client = saa7114_detach_client, diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c new file mode 100644 index 00000000000..048d000941c --- /dev/null +++ b/drivers/media/video/saa7115.c @@ -0,0 +1,1357 @@ +/* saa7115 - Philips SAA7114/SAA7115 video decoder driver + * + * Based on saa7114 driver by Maxim Yevtyushkin, which is based on + * the saa7111 driver by Dave Perks. + * + * Copyright (C) 1998 Dave Perks <dperks@ibm.net> + * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com> + * + * Slight changes for video timing and attachment output by + * Wolfgang Scherr <scherr@net4you.net> + * + * Moved over to the linux >= 2.4.x i2c protocol (1/1/2003) + * by Ronald Bultje <rbultje@ronald.bitfreak.net> + * + * Added saa7115 support by Kevin Thayer <nufan_wfk at yahoo.com> + * (2/17/2003) + * + * VBI support (2004) and cleanups (2005) by Hans Verkuil <hverkuil@xs4all.nl> + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/audiochip.h> +#include <asm/div64.h> + +MODULE_DESCRIPTION("Philips SAA7114/SAA7115 video decoder driver"); +MODULE_AUTHOR("Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug = 0; +module_param(debug, bool, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +static unsigned short normal_i2c[] = { 0x42 >> 1, 0x40 >> 1, I2C_CLIENT_END }; + + +I2C_CLIENT_INSMOD; + +struct saa7115_state { + v4l2_std_id std; + int input; + int enable; + int radio; + int bright; + int contrast; + int hue; + int sat; + enum v4l2_chip_ident ident; + u32 audclk_freq; +}; + +/* ----------------------------------------------------------------------- */ + +static inline int saa7115_write(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int saa7115_writeregs(struct i2c_client *client, const unsigned char *regs) +{ + unsigned char reg, data; + + while (*regs != 0x00) { + reg = *(regs++); + data = *(regs++); + if (saa7115_write(client, reg, data) < 0) + return -1; + } + return 0; +} + +static inline int saa7115_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/* ----------------------------------------------------------------------- */ + +/* If a value differs from the Hauppauge driver values, then the comment starts with + 'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the + Hauppauge driver sets. */ + +static const unsigned char saa7115_init_auto_input[] = { + 0x01, 0x48, /* white peak control disabled */ + 0x03, 0x20, /* was 0x30. 0x20: long vertical blanking */ + 0x04, 0x90, /* analog gain set to 0 */ + 0x05, 0x90, /* analog gain set to 0 */ + 0x06, 0xeb, /* horiz sync begin = -21 */ + 0x07, 0xe0, /* horiz sync stop = -17 */ + 0x0a, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */ + 0x0b, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */ + 0x0c, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */ + 0x0d, 0x00, /* chrominance hue control */ + 0x0f, 0x00, /* chrominance gain control: use automicatic mode */ + 0x10, 0x06, /* chrominance/luminance control: active adaptive combfilter */ + 0x11, 0x00, /* delay control */ + 0x12, 0x9d, /* RTS0 output control: VGATE */ + 0x13, 0x80, /* X-port output control: ITU656 standard mode, RTCO output enable RTCE */ + 0x14, 0x00, /* analog/ADC/auto compatibility control */ + 0x18, 0x40, /* raw data gain 0x00 = nominal */ + 0x19, 0x80, /* raw data offset 0x80 = 0 LSB */ + 0x1a, 0x77, /* color killer level control 0x77 = recommended */ + 0x1b, 0x42, /* misc chroma control 0x42 = recommended */ + 0x1c, 0xa9, /* combfilter control 0xA9 = recommended */ + 0x1d, 0x01, /* combfilter control 0x01 = recommended */ + 0x88, 0xd0, /* reset device */ + 0x88, 0xf0, /* set device programmed, all in operational mode */ + 0x00, 0x00 +}; + +static const unsigned char saa7115_cfg_reset_scaler[] = { + 0x87, 0x00, /* disable I-port output */ + 0x88, 0xd0, /* reset scaler */ + 0x88, 0xf0, /* activate scaler */ + 0x87, 0x01, /* enable I-port output */ + 0x00, 0x00 +}; + +/* ============== SAA7715 VIDEO templates ============= */ + +static const unsigned char saa7115_cfg_60hz_fullres_x[] = { + 0xcc, 0xd0, /* hsize low (output), hor. output window size = 0x2d0 = 720 */ + 0xcd, 0x02, /* hsize hi (output) */ + + /* Why not in 60hz-Land, too? */ + 0xd0, 0x01, /* downscale = 1 */ + 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */ + 0xd9, 0x04, + 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */ + 0xdd, 0x02, /* H-scaling incr chroma */ + + 0x00, 0x00 +}; +static const unsigned char saa7115_cfg_60hz_fullres_y[] = { + 0xce, 0xf8, /* vsize low (output), ver. output window size = 248 (but 60hz is 240?) */ + 0xcf, 0x00, /* vsize hi (output) */ + + /* Why not in 60hz-Land, too? */ + 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */ + 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */ + + 0xe0, 0x00, /* V-scaling incr luma low */ + 0xe1, 0x04, /* " hi */ + 0xe2, 0x00, /* V-scaling incr chroma low */ + 0xe3, 0x04, /* " hi */ + + 0x00, 0x00 +}; + +static const unsigned char saa7115_cfg_60hz_video[] = { + 0x80, 0x00, /* reset tasks */ + 0x88, 0xd0, /* reset scaler */ + + 0x15, 0x03, /* VGATE pulse start */ + 0x16, 0x11, /* VGATE pulse stop */ + 0x17, 0x9c, /* VGATE MSB and other values */ + + 0x08, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */ + 0x0e, 0x07, /* lots of different stuff... video autodetection is on */ + + 0x5a, 0x06, /* Vertical offset, standard 60hz value for ITU656 line counting */ + + /* Task A */ + 0x90, 0x80, /* Task Handling Control */ + 0x91, 0x48, /* X-port formats/config */ + 0x92, 0x40, /* Input Ref. signal Def. */ + 0x93, 0x84, /* I-port config */ + 0x94, 0x01, /* hoffset low (input), 0x0002 is minimum */ + 0x95, 0x00, /* hoffset hi (input) */ + 0x96, 0xd0, /* hsize low (input), 0x02d0 = 720 */ + 0x97, 0x02, /* hsize hi (input) */ + 0x98, 0x05, /* voffset low (input) */ + 0x99, 0x00, /* voffset hi (input) */ + 0x9a, 0x0c, /* vsize low (input), 0x0c = 12 */ + 0x9b, 0x00, /* vsize hi (input) */ + 0x9c, 0xa0, /* hsize low (output), 0x05a0 = 1440 */ + 0x9d, 0x05, /* hsize hi (output) */ + 0x9e, 0x0c, /* vsize low (output), 0x0c = 12 */ + 0x9f, 0x00, /* vsize hi (output) */ + + /* Task B */ + 0xc0, 0x00, /* Task Handling Control */ + 0xc1, 0x08, /* X-port formats/config */ + 0xc2, 0x00, /* Input Ref. signal Def. */ + 0xc3, 0x80, /* I-port config */ + 0xc4, 0x02, /* hoffset low (input), 0x0002 is minimum */ + 0xc5, 0x00, /* hoffset hi (input) */ + 0xc6, 0xd0, /* hsize low (input), 0x02d0 = 720 */ + 0xc7, 0x02, /* hsize hi (input) */ + 0xc8, 0x12, /* voffset low (input), 0x12 = 18 */ + 0xc9, 0x00, /* voffset hi (input) */ + 0xca, 0xf8, /* vsize low (input), 0xf8 = 248 */ + 0xcb, 0x00, /* vsize hi (input) */ + 0xcc, 0xd0, /* hsize low (output), 0x02d0 = 720 */ + 0xcd, 0x02, /* hsize hi (output) */ + + 0xf0, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */ + 0xf1, 0x05, /* low bit with 0xF0 */ + 0xf5, 0xad, /* Set pulse generator register */ + 0xf6, 0x01, + + 0x87, 0x00, /* Disable I-port output */ + 0x88, 0xd0, /* reset scaler */ + 0x80, 0x20, /* Activate only task "B", continuous mode (was 0xA0) */ + 0x88, 0xf0, /* activate scaler */ + 0x87, 0x01, /* Enable I-port output */ + 0x00, 0x00 +}; + +static const unsigned char saa7115_cfg_50hz_fullres_x[] = { + 0xcc, 0xd0, /* hsize low (output), 720 same as 60hz */ + 0xcd, 0x02, /* hsize hi (output) */ + + 0xd0, 0x01, /* down scale = 1 */ + 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */ + 0xd9, 0x04, + 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */ + 0xdd, 0x02, /* H-scaling incr chroma */ + + 0x00, 0x00 +}; +static const unsigned char saa7115_cfg_50hz_fullres_y[] = { + 0xce, 0x20, /* vsize low (output), 0x0120 = 288 */ + 0xcf, 0x01, /* vsize hi (output) */ + + 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */ + 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */ + + 0xe0, 0x00, /* V-scaling incr luma low */ + 0xe1, 0x04, /* " hi */ + 0xe2, 0x00, /* V-scaling incr chroma low */ + 0xe3, 0x04, /* " hi */ + + 0x00, 0x00 +}; + +static const unsigned char saa7115_cfg_50hz_video[] = { + 0x80, 0x00, /* reset tasks */ + 0x88, 0xd0, /* reset scaler */ + + 0x15, 0x37, /* VGATE start */ + 0x16, 0x16, /* VGATE stop */ + 0x17, 0x99, /* VGATE MSB and other values */ + + 0x08, 0x28, /* 0x28 = PAL */ + 0x0e, 0x07, /* chrominance control 1 */ + + 0x5a, 0x03, /* Vertical offset, standard 50hz value */ + + /* Task A */ + 0x90, 0x81, /* Task Handling Control */ + 0x91, 0x48, /* X-port formats/config */ + 0x92, 0x40, /* Input Ref. signal Def. */ + 0x93, 0x84, /* I-port config */ + /* This is weird: the datasheet says that you should use 2 as the minimum value, */ + /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ + 0x94, 0x00, /* hoffset low (input), 0x0002 is minimum */ + 0x95, 0x00, /* hoffset hi (input) */ + 0x96, 0xd0, /* hsize low (input), 0x02d0 = 720 */ + 0x97, 0x02, /* hsize hi (input) */ + 0x98, 0x03, /* voffset low (input) */ + 0x99, 0x00, /* voffset hi (input) */ + 0x9a, 0x12, /* vsize low (input), 0x12 = 18 */ + 0x9b, 0x00, /* vsize hi (input) */ + 0x9c, 0xa0, /* hsize low (output), 0x05a0 = 1440 */ + 0x9d, 0x05, /* hsize hi (output) */ + 0x9e, 0x12, /* vsize low (output), 0x12 = 18 */ + 0x9f, 0x00, /* vsize hi (output) */ + + /* Task B */ + 0xc0, 0x00, /* Task Handling Control */ + 0xc1, 0x08, /* X-port formats/config */ + 0xc2, 0x00, /* Input Ref. signal Def. */ + 0xc3, 0x80, /* I-port config */ + 0xc4, 0x00, /* hoffset low (input), 0x0002 is minimum. See comment at 0x94 above. */ + 0xc5, 0x00, /* hoffset hi (input) */ + 0xc6, 0xd0, /* hsize low (input), 0x02d0 = 720 */ + 0xc7, 0x02, /* hsize hi (input) */ + 0xc8, 0x16, /* voffset low (input), 0x16 = 22 */ + 0xc9, 0x00, /* voffset hi (input) */ + 0xca, 0x20, /* vsize low (input), 0x0120 = 288 */ + 0xcb, 0x01, /* vsize hi (input) */ + 0xcc, 0xd0, /* hsize low (output), 0x02d0 = 720 */ + 0xcd, 0x02, /* hsize hi (output) */ + 0xce, 0x20, /* vsize low (output), 0x0120 = 288 */ + 0xcf, 0x01, /* vsize hi (output) */ + + 0xf0, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */ + 0xf1, 0x05, /* low bit with 0xF0, (was 0x05) */ + 0xf5, 0xb0, /* Set pulse generator register */ + 0xf6, 0x01, + + 0x87, 0x00, /* Disable I-port output */ + 0x88, 0xd0, /* reset scaler (was 0xD0) */ + 0x80, 0x20, /* Activate only task "B" */ + 0x88, 0xf0, /* activate scaler */ + 0x87, 0x01, /* Enable I-port output */ + 0x00, 0x00 +}; + +/* ============== SAA7715 VIDEO templates (end) ======= */ + +static const unsigned char saa7115_cfg_vbi_on[] = { + 0x80, 0x00, /* reset tasks */ + 0x88, 0xd0, /* reset scaler */ + 0x80, 0x30, /* Activate both tasks */ + 0x88, 0xf0, /* activate scaler */ + 0x87, 0x01, /* Enable I-port output */ + 0x00, 0x00 +}; + +static const unsigned char saa7115_cfg_vbi_off[] = { + 0x80, 0x00, /* reset tasks */ + 0x88, 0xd0, /* reset scaler */ + 0x80, 0x20, /* Activate only task "B" */ + 0x88, 0xf0, /* activate scaler */ + 0x87, 0x01, /* Enable I-port output */ + 0x00, 0x00 +}; + +static const unsigned char saa7115_init_misc[] = { + 0x38, 0x03, /* audio stuff */ + 0x39, 0x10, + 0x3a, 0x08, + + 0x81, 0x01, /* reg 0x15,0x16 define blanking window */ + 0x82, 0x00, + 0x83, 0x01, /* I port settings */ + 0x84, 0x20, + 0x85, 0x21, + 0x86, 0xc5, + 0x87, 0x01, + + /* Task A */ + 0xa0, 0x01, /* down scale = 1 */ + 0xa1, 0x00, /* prescale accumulation length = 1 */ + 0xa2, 0x00, /* dc gain and fir prefilter control */ + 0xa4, 0x80, /* Lum Brightness, nominal value = 0x80 */ + 0xa5, 0x40, /* Lum contrast, nominal value = 0x40 */ + 0xa6, 0x40, /* Chroma satur. nominal value = 0x80 */ + 0xa8, 0x00, /* hor lum scaling 0x0200 = 2 zoom */ + 0xa9, 0x02, /* note: 2 x zoom ensures that VBI lines have same length as video lines. */ + 0xaa, 0x00, /* H-phase offset Luma = 0 */ + 0xac, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */ + 0xad, 0x01, /* H-scaling incr chroma */ + 0xae, 0x00, /* H-phase offset chroma. must be offset luma / 2 */ + + 0xb0, 0x00, /* V-scaling incr luma low */ + 0xb1, 0x04, /* " hi */ + 0xb2, 0x00, /* V-scaling incr chroma low */ + 0xb3, 0x04, /* " hi */ + 0xb4, 0x01, /* V-scaling mode control */ + 0xb8, 0x00, /* V-phase offset chroma 00 */ + 0xb9, 0x00, /* V-phase offset chroma 01 */ + 0xba, 0x00, /* V-phase offset chroma 10 */ + 0xbb, 0x00, /* V-phase offset chroma 11 */ + 0xbc, 0x00, /* V-phase offset luma 00 */ + 0xbd, 0x00, /* V-phase offset luma 01 */ + 0xbe, 0x00, /* V-phase offset luma 10 */ + 0xbf, 0x00, /* V-phase offset luma 11 */ + + /* Task B */ + 0xd0, 0x01, /* down scale = 1 */ + 0xd1, 0x00, /* prescale accumulation length = 1 */ + 0xd2, 0x00, /* dc gain and fir prefilter control */ + 0xd4, 0x80, /* Lum Brightness, nominal value = 0x80 */ + 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */ + 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */ + 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */ + 0xd9, 0x04, + 0xda, 0x00, /* H-phase offset Luma = 0 */ + 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */ + 0xdd, 0x02, /* H-scaling incr chroma */ + 0xde, 0x00, /* H-phase offset chroma. must be offset luma / 2 */ + + 0xe0, 0x00, /* V-scaling incr luma low */ + 0xe1, 0x04, /* " hi */ + 0xe2, 0x00, /* V-scaling incr chroma low */ + 0xe3, 0x04, /* " hi */ + 0xe4, 0x01, /* V-scaling mode control */ + 0xe8, 0x00, /* V-phase offset chroma 00 */ + 0xe9, 0x00, /* V-phase offset chroma 01 */ + 0xea, 0x00, /* V-phase offset chroma 10 */ + 0xeb, 0x00, /* V-phase offset chroma 11 */ + 0xec, 0x00, /* V-phase offset luma 00 */ + 0xed, 0x00, /* V-phase offset luma 01 */ + 0xee, 0x00, /* V-phase offset luma 10 */ + 0xef, 0x00, /* V-phase offset luma 11 */ + + 0xf2, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */ + 0xf3, 0x46, + 0xf4, 0x00, + 0xf7, 0x4b, /* not the recommended settings! */ + 0xf8, 0x00, + 0xf9, 0x4b, + 0xfa, 0x00, + 0xfb, 0x4b, + 0xff, 0x88, /* PLL2 lock detection settings: 71 lines 50% phase error */ + + /* Turn off VBI */ + 0x40, 0x20, /* No framing code errors allowed. */ + 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, 0x40, + 0x59, 0x47, + 0x5b, 0x83, + 0x5d, 0xbd, + 0x5e, 0x35, + + 0x02, 0x84, /* input tuner -> input 4, amplifier active */ + 0x09, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */ + + 0x80, 0x20, /* enable task B */ + 0x88, 0xd0, + 0x88, 0xf0, + 0x00, 0x00 +}; + +static int saa7115_odd_parity(u8 c) +{ + c ^= (c >> 4); + c ^= (c >> 2); + c ^= (c >> 1); + + return c & 1; +} + +static int saa7115_decode_vps(u8 * dst, u8 * p) +{ + static const u8 biphase_tbl[] = { + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, + 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, + 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, + 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, + 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, + 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, + 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, + 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, + 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, + 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, + 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, + 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, + 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, + 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, + 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, + 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, + }; + int i; + u8 c, err = 0; + + for (i = 0; i < 2 * 13; i += 2) { + err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; + c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4); + dst[i / 2] = c; + } + return err & 0xf0; +} + +static int saa7115_decode_wss(u8 * p) +{ + static const int wss_bits[8] = { + 0, 0, 0, 1, 0, 1, 1, 1 + }; + unsigned char parity; + int wss = 0; + int i; + + for (i = 0; i < 16; i++) { + int b1 = wss_bits[p[i] & 7]; + int b2 = wss_bits[(p[i] >> 3) & 7]; + + if (b1 == b2) + return -1; + wss |= b2 << i; + } + parity = wss & 15; + parity ^= parity >> 2; + parity ^= parity >> 1; + + if (!(parity & 1)) + return -1; + + return wss; +} + + +static int saa7115_set_audio_clock_freq(struct i2c_client *client, u32 freq) +{ + struct saa7115_state *state = i2c_get_clientdata(client); + u32 acpf; + u32 acni; + u32 hz; + u64 f; + + v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq); + + /* sanity check */ + if (freq < 32000 || freq > 48000) + return -EINVAL; + + /* hz is the refresh rate times 100 */ + hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000; + /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */ + acpf = (25600 * freq) / hz; + /* acni = (256 * freq * 2^23) / crystal_frequency = + (freq * 2^(8+23)) / crystal_frequency = + (freq << 31) / 32.11 MHz */ + f = freq; + f = f << 31; + do_div(f, 32110000); + acni = f; + + saa7115_write(client, 0x30, acpf & 0xff); + saa7115_write(client, 0x31, (acpf >> 8) & 0xff); + saa7115_write(client, 0x32, (acpf >> 16) & 0x03); + saa7115_write(client, 0x34, acni & 0xff); + saa7115_write(client, 0x35, (acni >> 8) & 0xff); + saa7115_write(client, 0x36, (acni >> 16) & 0x3f); + state->audclk_freq = freq; + return 0; +} + +static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct saa7115_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(client, "invalid brightness setting %d\n", ctrl->value); + return -ERANGE; + } + + state->bright = ctrl->value; + saa7115_write(client, 0x0a, state->bright); + break; + + case V4L2_CID_CONTRAST: + if (ctrl->value < 0 || ctrl->value > 127) { + v4l_err(client, "invalid contrast setting %d\n", ctrl->value); + return -ERANGE; + } + + state->contrast = ctrl->value; + saa7115_write(client, 0x0b, state->contrast); + break; + + case V4L2_CID_SATURATION: + if (ctrl->value < 0 || ctrl->value > 127) { + v4l_err(client, "invalid saturation setting %d\n", ctrl->value); + return -ERANGE; + } + + state->sat = ctrl->value; + saa7115_write(client, 0x0c, state->sat); + break; + + case V4L2_CID_HUE: + if (ctrl->value < -127 || ctrl->value > 127) { + v4l_err(client, "invalid hue setting %d\n", ctrl->value); + return -ERANGE; + } + + state->hue = ctrl->value; + saa7115_write(client, 0x0d, state->hue); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int saa7115_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct saa7115_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = state->bright; + break; + case V4L2_CID_CONTRAST: + ctrl->value = state->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = state->sat; + break; + case V4L2_CID_HUE: + ctrl->value = state->hue; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std) +{ + struct saa7115_state *state = i2c_get_clientdata(client); + int taskb = saa7115_read(client, 0x80) & 0x10; + + /* Prevent unnecessary standard changes. During a standard + change the I-Port is temporarily disabled. Any devices + reading from that port can get confused. + Note that VIDIOC_S_STD is also used to switch from + radio to TV mode, so if a VIDIOC_S_STD is broadcast to + all I2C devices then you do not want to have an unwanted + side-effect here. */ + if (std == state->std) + return; + + // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. + if (std & V4L2_STD_525_60) { + v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n"); + saa7115_writeregs(client, saa7115_cfg_60hz_video); + } else { + v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n"); + saa7115_writeregs(client, saa7115_cfg_50hz_video); + } + + state->std = std; + + /* restart task B if needed */ + if (taskb && state->ident == V4L2_IDENT_SAA7114) { + saa7115_writeregs(client, saa7115_cfg_vbi_on); + } + + /* switch audio mode too! */ + saa7115_set_audio_clock_freq(client, state->audclk_freq); +} + +static v4l2_std_id saa7115_get_v4lstd(struct i2c_client *client) +{ + struct saa7115_state *state = i2c_get_clientdata(client); + + return state->std; +} + +static void saa7115_log_status(struct i2c_client *client) +{ + struct saa7115_state *state = i2c_get_clientdata(client); + int reg1e, reg1f; + int signalOk; + int vcr; + + v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq); + if (client->name[6] == '4') { + /* status for the saa7114 */ + reg1f = saa7115_read(client, 0x1f); + signalOk = (reg1f & 0xc1) == 0x81; + v4l_info(client, "Video signal: %s\n", signalOk ? "ok" : "bad"); + v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + return; + } + + /* status for the saa7115 */ + reg1e = saa7115_read(client, 0x1e); + reg1f = saa7115_read(client, 0x1f); + + signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; + vcr = !(reg1f & 0x10); + + if (state->input >= 6) { + v4l_info(client, "Input: S-Video %d\n", state->input - 6); + } else { + v4l_info(client, "Input: Composite %d\n", state->input); + } + v4l_info(client, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); + v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + + switch (reg1e & 0x03) { + case 1: + v4l_info(client, "Detected format: NTSC\n"); + break; + case 2: + v4l_info(client, "Detected format: PAL\n"); + break; + case 3: + v4l_info(client, "Detected format: SECAM\n"); + break; + default: + v4l_info(client, "Detected format: BW/No color\n"); + break; + } +} + +/* setup the sliced VBI lcr registers according to the sliced VBI format */ +static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt) +{ + struct saa7115_state *state = i2c_get_clientdata(client); + int is_50hz = (state->std & V4L2_STD_625_50); + u8 lcr[24]; + int i, x; + + /* saa7114 doesn't yet support VBI */ + if (state->ident == V4L2_IDENT_SAA7114) + return; + + for (i = 0; i <= 23; i++) + lcr[i] = 0xff; + + if (fmt->service_set == 0) { + /* raw VBI */ + if (is_50hz) + for (i = 6; i <= 23; i++) + lcr[i] = 0xdd; + else + for (i = 10; i <= 21; i++) + lcr[i] = 0xdd; + } else { + /* sliced VBI */ + /* first clear lines that cannot be captured */ + if (is_50hz) { + for (i = 0; i <= 5; i++) + fmt->service_lines[0][i] = + fmt->service_lines[1][i] = 0; + } + else { + for (i = 0; i <= 9; i++) + fmt->service_lines[0][i] = + fmt->service_lines[1][i] = 0; + for (i = 22; i <= 23; i++) + fmt->service_lines[0][i] = + fmt->service_lines[1][i] = 0; + } + + /* Now set the lcr values according to the specified service */ + for (i = 6; i <= 23; i++) { + lcr[i] = 0; + for (x = 0; x <= 1; x++) { + switch (fmt->service_lines[1-x][i]) { + case 0: + lcr[i] |= 0xf << (4 * x); + break; + case V4L2_SLICED_TELETEXT_B: + lcr[i] |= 1 << (4 * x); + break; + case V4L2_SLICED_CAPTION_525: + lcr[i] |= 4 << (4 * x); + break; + case V4L2_SLICED_WSS_625: + lcr[i] |= 5 << (4 * x); + break; + case V4L2_SLICED_VPS: + lcr[i] |= 7 << (4 * x); + break; + } + } + } + } + + /* write the lcr registers */ + for (i = 2; i <= 23; i++) { + saa7115_write(client, i - 2 + 0x41, lcr[i]); + } + + /* enable/disable raw VBI capturing */ + saa7115_writeregs(client, fmt->service_set == 0 ? saa7115_cfg_vbi_on : saa7115_cfg_vbi_off); +} + +static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +{ + static u16 lcr2vbi[] = { + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ + 0, V4L2_SLICED_CAPTION_525, /* 4 */ + V4L2_SLICED_WSS_625, 0, /* 5 */ + V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */ + 0, 0, 0, 0 + }; + struct v4l2_sliced_vbi_format *sliced = &fmt->fmt.sliced; + int i; + + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + memset(sliced, 0, sizeof(*sliced)); + /* done if using raw VBI */ + if (saa7115_read(client, 0x80) & 0x10) + return 0; + for (i = 2; i <= 23; i++) { + u8 v = saa7115_read(client, i - 2 + 0x41); + + sliced->service_lines[0][i] = lcr2vbi[v >> 4]; + sliced->service_lines[1][i] = lcr2vbi[v & 0xf]; + sliced->service_set |= + sliced->service_lines[0][i] | sliced->service_lines[1][i]; + } + return 0; +} + +static int saa7115_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +{ + struct saa7115_state *state = i2c_get_clientdata(client); + struct v4l2_pix_format *pix; + int HPSC, HFSC; + int VSCY, Vsrc; + int is_50hz = state->std & V4L2_STD_625_50; + + if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { + saa7115_set_lcr(client, &fmt->fmt.sliced); + return 0; + } + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + pix = &(fmt->fmt.pix); + + v4l_dbg(1, debug, client, "decoder set size\n"); + + /* FIXME need better bounds checking here */ + if ((pix->width < 1) || (pix->width > 1440)) + return -EINVAL; + if ((pix->height < 1) || (pix->height > 960)) + return -EINVAL; + + /* probably have a valid size, let's set it */ + /* Set output width/height */ + /* width */ + saa7115_write(client, 0xcc, (u8) (pix->width & 0xff)); + saa7115_write(client, 0xcd, (u8) ((pix->width >> 8) & 0xff)); + /* height */ + saa7115_write(client, 0xce, (u8) (pix->height & 0xff)); + saa7115_write(client, 0xcf, (u8) ((pix->height >> 8) & 0xff)); + + /* Scaling settings */ + /* Hprescaler is floor(inres/outres) */ + /* FIXME hardcoding input res */ + if (pix->width != 720) { + HPSC = (int)(720 / pix->width); + /* 0 is not allowed (div. by zero) */ + HPSC = HPSC ? HPSC : 1; + HFSC = (int)((1024 * 720) / (HPSC * pix->width)); + + v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); + /* FIXME hardcodes to "Task B" + * write H prescaler integer */ + saa7115_write(client, 0xd0, (u8) (HPSC & 0x3f)); + + /* write H fine-scaling (luminance) */ + saa7115_write(client, 0xd8, (u8) (HFSC & 0xff)); + saa7115_write(client, 0xd9, (u8) ((HFSC >> 8) & 0xff)); + /* write H fine-scaling (chrominance) + * must be lum/2, so i'll just bitshift :) */ + saa7115_write(client, 0xDC, (u8) ((HFSC >> 1) & 0xff)); + saa7115_write(client, 0xDD, (u8) ((HFSC >> 9) & 0xff)); + } else { + if (is_50hz) { + v4l_dbg(1, debug, client, "Setting full 50hz width\n"); + saa7115_writeregs(client, saa7115_cfg_50hz_fullres_x); + } else { + v4l_dbg(1, debug, client, "Setting full 60hz width\n"); + saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x); + } + } + + Vsrc = is_50hz ? 576 : 480; + + if (pix->height != Vsrc) { + VSCY = (int)((1024 * Vsrc) / pix->height); + v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); + + /* Correct Contrast and Luminance */ + saa7115_write(client, 0xd5, (u8) (64 * 1024 / VSCY)); + saa7115_write(client, 0xd6, (u8) (64 * 1024 / VSCY)); + + /* write V fine-scaling (luminance) */ + saa7115_write(client, 0xe0, (u8) (VSCY & 0xff)); + saa7115_write(client, 0xe1, (u8) ((VSCY >> 8) & 0xff)); + /* write V fine-scaling (chrominance) */ + saa7115_write(client, 0xe2, (u8) (VSCY & 0xff)); + saa7115_write(client, 0xe3, (u8) ((VSCY >> 8) & 0xff)); + } else { + if (is_50hz) { + v4l_dbg(1, debug, client, "Setting full 50Hz height\n"); + saa7115_writeregs(client, saa7115_cfg_50hz_fullres_y); + } else { + v4l_dbg(1, debug, client, "Setting full 60hz height\n"); + saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y); + } + } + + saa7115_writeregs(client, saa7115_cfg_reset_scaler); + return 0; +} + +/* Decode the sliced VBI data stream as created by the saa7115. + The format is described in the saa7115 datasheet in Tables 25 and 26 + and in Figure 33. + The current implementation uses SAV/EAV codes and not the ancillary data + headers. The vbi->p pointer points to the SDID byte right after the SAV + code. */ +static void saa7115_decode_vbi_line(struct i2c_client *client, + struct v4l2_decode_vbi_line *vbi) +{ + static const char vbi_no_data_pattern[] = { + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0 + }; + struct saa7115_state *state = i2c_get_clientdata(client); + u8 *p = vbi->p; + u32 wss; + int id1, id2; /* the ID1 and ID2 bytes from the internal header */ + + vbi->type = 0; /* mark result as a failure */ + id1 = p[2]; + id2 = p[3]; + /* Note: the field bit is inverted for 60 Hz video */ + if (state->std & V4L2_STD_525_60) + id1 ^= 0x40; + + /* Skip internal header, p now points to the start of the payload */ + p += 4; + vbi->p = p; + + /* calculate field and line number of the VBI packet (1-23) */ + vbi->is_second_field = ((id1 & 0x40) != 0); + vbi->line = (id1 & 0x3f) << 3; + vbi->line |= (id2 & 0x70) >> 4; + + /* Obtain data type */ + id2 &= 0xf; + + /* If the VBI slicer does not detect any signal it will fill up + the payload buffer with 0xa0 bytes. */ + if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern))) + return; + + /* decode payloads */ + switch (id2) { + case 1: + vbi->type = V4L2_SLICED_TELETEXT_B; + break; + case 4: + if (!saa7115_odd_parity(p[0]) || !saa7115_odd_parity(p[1])) + return; + vbi->type = V4L2_SLICED_CAPTION_525; + break; + case 5: + wss = saa7115_decode_wss(p); + if (wss == -1) + return; + p[0] = wss & 0xff; + p[1] = wss >> 8; + vbi->type = V4L2_SLICED_WSS_625; + break; + case 7: + if (saa7115_decode_vps(p, p) != 0) + return; + vbi->type = V4L2_SLICED_VPS; + break; + default: + return; + } +} + +/* ============ SAA7115 AUDIO settings (end) ============= */ + +static struct v4l2_queryctrl saa7115_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 128, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64, + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64, + .flags = 0, + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 1, + .default_value = 0, + .flags = 0, + }, +}; + +/* ----------------------------------------------------------------------- */ + +static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct saa7115_state *state = i2c_get_clientdata(client); + int *iarg = arg; + + /* ioctls to allow direct access to the saa7115 registers for testing */ + switch (cmd) { + case VIDIOC_S_FMT: + return saa7115_set_v4lfmt(client, (struct v4l2_format *)arg); + + case VIDIOC_G_FMT: + return saa7115_get_v4lfmt(client, (struct v4l2_format *)arg); + + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + return saa7115_set_audio_clock_freq(client, *(u32 *)arg); + + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *vt = arg; + int status; + + if (state->radio) + break; + status = saa7115_read(client, 0x1f); + + v4l_dbg(1, debug, client, "status: 0x%02x\n", status); + vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; + break; + } + + case VIDIOC_LOG_STATUS: + saa7115_log_status(client); + break; + + case VIDIOC_G_CTRL: + return saa7115_get_v4lctrl(client, (struct v4l2_control *)arg); + + case VIDIOC_S_CTRL: + return saa7115_set_v4lctrl(client, (struct v4l2_control *)arg); + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(saa7115_qctrl); i++) + if (qc->id && qc->id == saa7115_qctrl[i].id) { + memcpy(qc, &saa7115_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; + } + + case VIDIOC_G_STD: + *(v4l2_std_id *)arg = saa7115_get_v4lstd(client); + break; + + case VIDIOC_S_STD: + state->radio = 0; + saa7115_set_v4lstd(client, *(v4l2_std_id *)arg); + break; + + case AUDC_SET_RADIO: + state->radio = 1; + break; + + case VIDIOC_G_INPUT: + *(int *)arg = state->input; + break; + + case VIDIOC_S_INPUT: + v4l_dbg(1, debug, client, "decoder set input %d\n", *iarg); + /* inputs from 0-9 are available */ + if (*iarg < 0 || *iarg > 9) { + return -EINVAL; + } + + if (state->input == *iarg) + break; + v4l_dbg(1, debug, client, "now setting %s input\n", + *iarg >= 6 ? "S-Video" : "Composite"); + state->input = *iarg; + + /* select mode */ + saa7115_write(client, 0x02, + (saa7115_read(client, 0x02) & 0xf0) | + state->input); + + /* bypass chrominance trap for modes 6..9 */ + saa7115_write(client, 0x09, + (saa7115_read(client, 0x09) & 0x7f) | + (state->input < 6 ? 0x0 : 0x80)); + break; + + case VIDIOC_STREAMON: + case VIDIOC_STREAMOFF: + v4l_dbg(1, debug, client, "%s output\n", + (cmd == VIDIOC_STREAMON) ? "enable" : "disable"); + + if (state->enable != (cmd == VIDIOC_STREAMON)) { + state->enable = (cmd == VIDIOC_STREAMON); + saa7115_write(client, 0x87, state->enable); + } + break; + + case VIDIOC_INT_DECODE_VBI_LINE: + saa7115_decode_vbi_line(client, arg); + break; + + case VIDIOC_INT_RESET: + v4l_dbg(1, debug, client, "decoder RESET\n"); + saa7115_writeregs(client, saa7115_cfg_reset_scaler); + break; + + case VIDIOC_INT_G_VBI_DATA: + { + struct v4l2_sliced_vbi_data *data = arg; + + switch (data->id) { + case V4L2_SLICED_WSS_625: + if (saa7115_read(client, 0x6b) & 0xc0) + return -EIO; + data->data[0] = saa7115_read(client, 0x6c); + data->data[1] = saa7115_read(client, 0x6d); + return 0; + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) { + /* CC */ + if (saa7115_read(client, 0x66) & 0xc0) + return -EIO; + data->data[0] = saa7115_read(client, 0x67); + data->data[1] = saa7115_read(client, 0x68); + return 0; + } + /* XDS */ + if (saa7115_read(client, 0x66) & 0x30) + return -EIO; + data->data[0] = saa7115_read(client, 0x69); + data->data[1] = saa7115_read(client, 0x6a); + return 0; + default: + return -EINVAL; + } + break; + } + +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_SAA711X) + return -EINVAL; + reg->val = saa7115_read(client, reg->reg & 0xff); + break; + } + + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_SAA711X) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa7115_write(client, reg->reg & 0xff, reg->val & 0xff); + break; + } +#endif + + case VIDIOC_INT_G_CHIP_IDENT: + *iarg = state->ident; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_saa7115; + +static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct saa7115_state *state; + u8 chip_id; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_saa7115; + snprintf(client->name, sizeof(client->name) - 1, "saa7115"); + + v4l_dbg(1, debug, client, "detecting saa7115 client on address 0x%x\n", address << 1); + + saa7115_write(client, 0, 5); + chip_id = saa7115_read(client, 0) & 0x0f; + if (chip_id != 4 && chip_id != 5) { + v4l_dbg(1, debug, client, "saa7115 not found\n"); + kfree(client); + return 0; + } + if (chip_id == 4) { + snprintf(client->name, sizeof(client->name) - 1, "saa7114"); + } + v4l_info(client, "saa711%d found @ 0x%x (%s)\n", chip_id, address << 1, adapter->name); + + state = kzalloc(sizeof(struct saa7115_state), GFP_KERNEL); + i2c_set_clientdata(client, state); + if (state == NULL) { + kfree(client); + return -ENOMEM; + } + state->std = V4L2_STD_NTSC; + state->input = -1; + state->enable = 1; + state->radio = 0; + state->bright = 128; + state->contrast = 64; + state->hue = 0; + state->sat = 64; + state->ident = (chip_id == 4) ? V4L2_IDENT_SAA7114 : V4L2_IDENT_SAA7115; + state->audclk_freq = 48000; + + v4l_dbg(1, debug, client, "writing init values\n"); + + /* init to 60hz/48khz */ + saa7115_writeregs(client, saa7115_init_auto_input); + saa7115_writeregs(client, saa7115_init_misc); + saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x); + saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y); + saa7115_writeregs(client, saa7115_cfg_60hz_video); + saa7115_set_audio_clock_freq(client, state->audclk_freq); + saa7115_writeregs(client, saa7115_cfg_reset_scaler); + + i2c_attach_client(client); + + v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n", + saa7115_read(client, 0x1e), saa7115_read(client, 0x1f)); + + return 0; +} + +static int saa7115_probe(struct i2c_adapter *adapter) +{ + if (adapter->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adapter, &addr_data, &saa7115_attach); + return 0; +} + +static int saa7115_detach(struct i2c_client *client) +{ + struct saa7115_state *state = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(state); + kfree(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +static struct i2c_driver i2c_driver_saa7115 = { + .driver = { + .name = "saa7115", + }, + .id = I2C_DRIVERID_SAA711X, + .attach_adapter = saa7115_probe, + .detach_client = saa7115_detach, + .command = saa7115_command, +}; + + +static int __init saa7115_init_module(void) +{ + return i2c_add_driver(&i2c_driver_saa7115); +} + +static void __exit saa7115_cleanup_module(void) +{ + i2c_del_driver(&i2c_driver_saa7115); +} + +module_init(saa7115_init_module); +module_exit(saa7115_cleanup_module); diff --git a/drivers/media/video/saa711x.c b/drivers/media/video/saa711x.c index 9aa8827de2c..6c161f2f5e2 100644 --- a/drivers/media/video/saa711x.c +++ b/drivers/media/video/saa711x.c @@ -36,7 +36,6 @@ #include <asm/pgtable.h> #include <asm/page.h> #include <linux/sched.h> -#include <asm/segment.h> #include <linux/types.h> #include <asm/uaccess.h> #include <linux/videodev.h> @@ -53,14 +52,14 @@ MODULE_LICENSE("GPL"); #include <linux/video_decoder.h> static int debug = 0; -MODULE_PARM(debug, "i"); +module_param(debug, int, 0644); MODULE_PARM_DESC(debug, " Set the default Debug level. Default: 0 (Off) - (0-1)"); #define dprintk(num, format, args...) \ do { \ if (debug >= num) \ - printk(format , ##args); \ + printk(format, ##args); \ } while (0) /* ----------------------------------------------------------------------- */ @@ -324,7 +323,7 @@ saa711x_command (struct i2c_client *client, case VIDEO_MODE_SECAM: saa711x_write(client, 0x08, - (decoder->reg[0x0e] & 0x3f) | 0x00); + (decoder->reg[0x08] & 0x3f) | 0x00); saa711x_write(client, 0x0e, (decoder->reg[0x0e] & 0x8f) | 0x50); break; @@ -488,21 +487,18 @@ saa711x_detect_client (struct i2c_adapter *adapter, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_saa711x; - client->flags = I2C_CLIENT_ALLOW_USE; strlcpy(I2C_NAME(client), "saa711x", sizeof(I2C_NAME(client))); - decoder = kmalloc(sizeof(struct saa711x), GFP_KERNEL); + decoder = kzalloc(sizeof(struct saa711x), GFP_KERNEL); if (decoder == NULL) { kfree(client); return -ENOMEM; } - memset(decoder, 0, sizeof(struct saa711x)); decoder->norm = VIDEO_MODE_NTSC; decoder->input = 0; decoder->enable = 1; @@ -566,12 +562,10 @@ saa711x_detach_client (struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_driver i2c_driver_saa711x = { - .owner = THIS_MODULE, - .name = "saa711x", - + .driver = { + .name = "saa711x", + }, .id = I2C_DRIVERID_SAA711X, - .flags = I2C_DF_NOTIFY, - .attach_adapter = saa711x_attach_adapter, .detach_client = saa711x_detach_client, .command = saa711x_command, diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c new file mode 100644 index 00000000000..992c71774f3 --- /dev/null +++ b/drivers/media/video/saa7127.c @@ -0,0 +1,820 @@ +/* + * saa7127 - Philips SAA7127/SAA7129 video encoder driver + * + * Copyright (C) 2003 Roy Bulter <rbulter@hetnet.nl> + * + * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter + * + * Copyright (C) 2000-2001 Gillem <htoa@gmx.net> + * Copyright (C) 2002 Andreas Oberritter <obi@saftware.de> + * + * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo + * + * Copyright (C) 1999 Nathan Laredo <laredo@gnu.org> + * + * This driver is designed for the Hauppauge 250/350 Linux driver + * from the ivtv Project + * + * Copyright (C) 2003 Kevin Thayer <nufan_wfk@yahoo.com> + * + * Dual output support: + * Copyright (C) 2004 Eric Varsanyi + * + * NTSC Tuning and 7.5 IRE Setup + * Copyright (C) 2004 Chris Kennedy <c@groovy.org> + * + * VBI additions & cleanup: + * Copyright (C) 2004, 2005 Hans Verkuil <hverkuil@xs4all.nl> + * + * Note: the saa7126 is identical to the saa7127, and the saa7128 is + * identical to the saa7129, except that the saa7126 and saa7128 have + * macrovision anti-taping support. This driver will almost certainly + * work find for those chips, except of course for the missing anti-taping + * support. + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> + +static int debug = 0; +static int test_image = 0; + +MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver"); +MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); +MODULE_LICENSE("GPL"); +module_param(debug, int, 0644); +module_param(test_image, int, 0644); +MODULE_PARM_DESC(debug, "debug level (0-2)"); +MODULE_PARM_DESC(test_image, "test_image (0-1)"); + +static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; + + +I2C_CLIENT_INSMOD; + +/* + * SAA7127 registers + */ + +#define SAA7127_REG_STATUS 0x00 +#define SAA7127_REG_WIDESCREEN_CONFIG 0x26 +#define SAA7127_REG_WIDESCREEN_ENABLE 0x27 +#define SAA7127_REG_BURST_START 0x28 +#define SAA7127_REG_BURST_END 0x29 +#define SAA7127_REG_COPYGEN_0 0x2a +#define SAA7127_REG_COPYGEN_1 0x2b +#define SAA7127_REG_COPYGEN_2 0x2c +#define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d +#define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38 +#define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39 +#define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3a +#define SAA7129_REG_FADE_KEY_COL2 0x4f +#define SAA7127_REG_CHROMA_PHASE 0x5a +#define SAA7127_REG_GAINU 0x5b +#define SAA7127_REG_GAINV 0x5c +#define SAA7127_REG_BLACK_LEVEL 0x5d +#define SAA7127_REG_BLANKING_LEVEL 0x5e +#define SAA7127_REG_VBI_BLANKING 0x5f +#define SAA7127_REG_DAC_CONTROL 0x61 +#define SAA7127_REG_BURST_AMP 0x62 +#define SAA7127_REG_SUBC3 0x63 +#define SAA7127_REG_SUBC2 0x64 +#define SAA7127_REG_SUBC1 0x65 +#define SAA7127_REG_SUBC0 0x66 +#define SAA7127_REG_LINE_21_ODD_0 0x67 +#define SAA7127_REG_LINE_21_ODD_1 0x68 +#define SAA7127_REG_LINE_21_EVEN_0 0x69 +#define SAA7127_REG_LINE_21_EVEN_1 0x6a +#define SAA7127_REG_RCV_PORT_CONTROL 0x6b +#define SAA7127_REG_VTRIG 0x6c +#define SAA7127_REG_HTRIG_HI 0x6d +#define SAA7127_REG_MULTI 0x6e +#define SAA7127_REG_CLOSED_CAPTION 0x6f +#define SAA7127_REG_RCV2_OUTPUT_START 0x70 +#define SAA7127_REG_RCV2_OUTPUT_END 0x71 +#define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72 +#define SAA7127_REG_TTX_REQUEST_H_START 0x73 +#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74 +#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75 +#define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76 +#define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77 +#define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78 +#define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79 +#define SAA7127_REG_FIRST_ACTIVE 0x7a +#define SAA7127_REG_LAST_ACTIVE 0x7b +#define SAA7127_REG_MSB_VERTICAL 0x7c +#define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7e +#define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7f + +/* + ********************************************************************** + * + * Arrays with configuration parameters for the SAA7127 + * + ********************************************************************** + */ + +struct i2c_reg_value { + unsigned char reg; + unsigned char value; +}; + +static const struct i2c_reg_value saa7129_init_config_extra[] = { + { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 }, + { SAA7127_REG_VTRIG, 0xfa }, +}; + +static const struct i2c_reg_value saa7127_init_config_common[] = { + { SAA7127_REG_WIDESCREEN_CONFIG, 0x0d }, + { SAA7127_REG_WIDESCREEN_ENABLE, 0x00 }, + { SAA7127_REG_COPYGEN_0, 0x77 }, + { SAA7127_REG_COPYGEN_1, 0x41 }, + { SAA7127_REG_COPYGEN_2, 0x00 }, /* Macrovision enable/disable */ + { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x9e }, + { SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00 }, + { SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00 }, + { SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80 }, /* for color bars */ + { SAA7127_REG_LINE_21_ODD_0, 0x77 }, + { SAA7127_REG_LINE_21_ODD_1, 0x41 }, + { SAA7127_REG_LINE_21_EVEN_0, 0x88 }, + { SAA7127_REG_LINE_21_EVEN_1, 0x41 }, + { SAA7127_REG_RCV_PORT_CONTROL, 0x12 }, + { SAA7127_REG_VTRIG, 0xf9 }, + { SAA7127_REG_HTRIG_HI, 0x00 }, + { SAA7127_REG_RCV2_OUTPUT_START, 0x41 }, + { SAA7127_REG_RCV2_OUTPUT_END, 0xc3 }, + { SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00 }, + { SAA7127_REG_TTX_REQUEST_H_START, 0x3e }, + { SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8 }, + { SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03 }, + { SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15 }, + { SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16 }, + { SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15 }, + { SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16 }, + { SAA7127_REG_FIRST_ACTIVE, 0x1a }, + { SAA7127_REG_LAST_ACTIVE, 0x01 }, + { SAA7127_REG_MSB_VERTICAL, 0xc0 }, + { SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00 }, + { SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00 }, + { 0, 0 } +}; + +#define SAA7127_60HZ_DAC_CONTROL 0x15 +static const struct i2c_reg_value saa7127_init_config_60hz[] = { + { SAA7127_REG_BURST_START, 0x19 }, + /* BURST_END is also used as a chip ID in saa7127_detect_client */ + { SAA7127_REG_BURST_END, 0x1d }, + { SAA7127_REG_CHROMA_PHASE, 0xa3 }, + { SAA7127_REG_GAINU, 0x98 }, + { SAA7127_REG_GAINV, 0xd3 }, + { SAA7127_REG_BLACK_LEVEL, 0x39 }, + { SAA7127_REG_BLANKING_LEVEL, 0x2e }, + { SAA7127_REG_VBI_BLANKING, 0x2e }, + { SAA7127_REG_DAC_CONTROL, 0x15 }, + { SAA7127_REG_BURST_AMP, 0x4d }, + { SAA7127_REG_SUBC3, 0x1f }, + { SAA7127_REG_SUBC2, 0x7c }, + { SAA7127_REG_SUBC1, 0xf0 }, + { SAA7127_REG_SUBC0, 0x21 }, + { SAA7127_REG_MULTI, 0x90 }, + { SAA7127_REG_CLOSED_CAPTION, 0x11 }, + { 0, 0 } +}; + +#define SAA7127_50HZ_DAC_CONTROL 0x02 +static struct i2c_reg_value saa7127_init_config_50hz[] = { + { SAA7127_REG_BURST_START, 0x21 }, + /* BURST_END is also used as a chip ID in saa7127_detect_client */ + { SAA7127_REG_BURST_END, 0x1d }, + { SAA7127_REG_CHROMA_PHASE, 0x3f }, + { SAA7127_REG_GAINU, 0x7d }, + { SAA7127_REG_GAINV, 0xaf }, + { SAA7127_REG_BLACK_LEVEL, 0x33 }, + { SAA7127_REG_BLANKING_LEVEL, 0x35 }, + { SAA7127_REG_VBI_BLANKING, 0x35 }, + { SAA7127_REG_DAC_CONTROL, 0x02 }, + { SAA7127_REG_BURST_AMP, 0x2f }, + { SAA7127_REG_SUBC3, 0xcb }, + { SAA7127_REG_SUBC2, 0x8a }, + { SAA7127_REG_SUBC1, 0x09 }, + { SAA7127_REG_SUBC0, 0x2a }, + { SAA7127_REG_MULTI, 0xa0 }, + { SAA7127_REG_CLOSED_CAPTION, 0x00 }, + { 0, 0 } +}; + +/* Enumeration for the Supported input types */ +enum saa7127_input_type { + SAA7127_INPUT_TYPE_NORMAL, + SAA7127_INPUT_TYPE_TEST_IMAGE +}; + +/* Enumeration for the Supported Output signal types */ +enum saa7127_output_type { + SAA7127_OUTPUT_TYPE_BOTH, + SAA7127_OUTPUT_TYPE_COMPOSITE, + SAA7127_OUTPUT_TYPE_SVIDEO, + SAA7127_OUTPUT_TYPE_RGB, + SAA7127_OUTPUT_TYPE_YUV_C, + SAA7127_OUTPUT_TYPE_YUV_V +}; + +/* + ********************************************************************** + * + * Encoder Struct, holds the configuration state of the encoder + * + ********************************************************************** + */ + +struct saa7127_state { + v4l2_std_id std; + enum v4l2_chip_ident ident; + enum saa7127_input_type input_type; + enum saa7127_output_type output_type; + int video_enable; + int wss_enable; + u16 wss_mode; + int cc_enable; + u16 cc_data; + int xds_enable; + u16 xds_data; + int vps_enable; + u8 vps_data[5]; + u8 reg_2d; + u8 reg_3a; + u8 reg_3a_cb; /* colorbar bit */ + u8 reg_61; +}; + +static const char * const output_strs[] = +{ + "S-Video + Composite", + "Composite", + "S-Video", + "RGB", + "YUV C", + "YUV V" +}; + +static const char * const wss_strs[] = { + "invalid", + "letterbox 14:9 center", + "letterbox 14:9 top", + "invalid", + "letterbox 16:9 top", + "invalid", + "invalid", + "16:9 full format anamorphic" + "4:3 full format", + "invalid", + "invalid", + "letterbox 16:9 center", + "invalid", + "letterbox >16:9 center", + "14:9 full format center", + "invalid", +}; + +/* ----------------------------------------------------------------------- */ + +static int saa7127_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_write(struct i2c_client *client, u8 reg, u8 val) +{ + int i; + + for (i = 0; i < 3; i++) { + if (i2c_smbus_write_byte_data(client, reg, val) == 0) + return 0; + } + v4l_err(client, "I2C Write Problem\n"); + return -1; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_write_inittab(struct i2c_client *client, + const struct i2c_reg_value *regs) +{ + while (regs->reg != 0) { + saa7127_write(client, regs->reg, regs->value); + regs++; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = i2c_get_clientdata(client); + int enable = (data->line != 0); + + if (enable && (data->field != 0 || data->line != 16)) + return -EINVAL; + if (state->vps_enable != enable) { + v4l_dbg(1, debug, client, "Turn VPS Signal %s\n", enable ? "on" : "off"); + saa7127_write(client, 0x54, enable << 7); + state->vps_enable = enable; + } + if (!enable) + return 0; + + state->vps_data[0] = data->data[4]; + state->vps_data[1] = data->data[10]; + state->vps_data[2] = data->data[11]; + state->vps_data[3] = data->data[12]; + state->vps_data[4] = data->data[13]; + v4l_dbg(1, debug, client, "Set VPS data %02x %02x %02x %02x %02x\n", + state->vps_data[0], state->vps_data[1], + state->vps_data[2], state->vps_data[3], + state->vps_data[4]); + saa7127_write(client, 0x55, state->vps_data[0]); + saa7127_write(client, 0x56, state->vps_data[1]); + saa7127_write(client, 0x57, state->vps_data[2]); + saa7127_write(client, 0x58, state->vps_data[3]); + saa7127_write(client, 0x59, state->vps_data[4]); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_cc(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = i2c_get_clientdata(client); + u16 cc = data->data[1] << 8 | data->data[0]; + int enable = (data->line != 0); + + if (enable && (data->field != 0 || data->line != 21)) + return -EINVAL; + if (state->cc_enable != enable) { + v4l_dbg(1, debug, client, "Turn CC %s\n", enable ? "on" : "off"); + saa7127_write(client, SAA7127_REG_CLOSED_CAPTION, + (state->xds_enable << 7) | (enable << 6) | 0x11); + state->cc_enable = enable; + } + if (!enable) + return 0; + + v4l_dbg(2, debug, client, "CC data: %04x\n", cc); + saa7127_write(client, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); + saa7127_write(client, SAA7127_REG_LINE_21_ODD_1, cc >> 8); + state->cc_data = cc; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_xds(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = i2c_get_clientdata(client); + u16 xds = data->data[1] << 8 | data->data[0]; + int enable = (data->line != 0); + + if (enable && (data->field != 1 || data->line != 21)) + return -EINVAL; + if (state->xds_enable != enable) { + v4l_dbg(1, debug, client, "Turn XDS %s\n", enable ? "on" : "off"); + saa7127_write(client, SAA7127_REG_CLOSED_CAPTION, + (enable << 7) | (state->cc_enable << 6) | 0x11); + state->xds_enable = enable; + } + if (!enable) + return 0; + + v4l_dbg(2, debug, client, "XDS data: %04x\n", xds); + saa7127_write(client, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); + saa7127_write(client, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); + state->xds_data = xds; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +{ + struct saa7127_state *state = i2c_get_clientdata(client); + int enable = (data->line != 0); + + if (enable && (data->field != 0 || data->line != 23)) + return -EINVAL; + if (state->wss_enable != enable) { + v4l_dbg(1, debug, client, "Turn WSS %s\n", enable ? "on" : "off"); + saa7127_write(client, 0x27, enable << 7); + state->wss_enable = enable; + } + if (!enable) + return 0; + + saa7127_write(client, 0x26, data->data[0]); + saa7127_write(client, 0x27, 0x80 | (data->data[1] & 0x3f)); + v4l_dbg(1, debug, client, "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]); + state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0]; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_video_enable(struct i2c_client *client, int enable) +{ + struct saa7127_state *state = i2c_get_clientdata(client); + + if (enable) { + v4l_dbg(1, debug, client, "Enable Video Output\n"); + saa7127_write(client, 0x2d, state->reg_2d); + saa7127_write(client, 0x61, state->reg_61); + } else { + v4l_dbg(1, debug, client, "Disable Video Output\n"); + saa7127_write(client, 0x2d, (state->reg_2d & 0xf0)); + saa7127_write(client, 0x61, (state->reg_61 | 0xc0)); + } + state->video_enable = enable; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_std(struct i2c_client *client, v4l2_std_id std) +{ + struct saa7127_state *state = i2c_get_clientdata(client); + const struct i2c_reg_value *inittab; + + if (std & V4L2_STD_525_60) { + v4l_dbg(1, debug, client, "Selecting 60 Hz video Standard\n"); + inittab = saa7127_init_config_60hz; + state->reg_61 = SAA7127_60HZ_DAC_CONTROL; + } else { + v4l_dbg(1, debug, client, "Selecting 50 Hz video Standard\n"); + inittab = saa7127_init_config_50hz; + state->reg_61 = SAA7127_50HZ_DAC_CONTROL; + } + + /* Write Table */ + saa7127_write_inittab(client, inittab); + state->std = std; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_output_type(struct i2c_client *client, int output) +{ + struct saa7127_state *state = i2c_get_clientdata(client); + + switch (output) { + case SAA7127_OUTPUT_TYPE_RGB: + state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */ + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + case SAA7127_OUTPUT_TYPE_COMPOSITE: + state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */ + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + case SAA7127_OUTPUT_TYPE_SVIDEO: + state->reg_2d = 0xff; /* 11111111 croma -> R, luma -> CVBS + G + B */ + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + case SAA7127_OUTPUT_TYPE_YUV_V: + state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */ + state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ + break; + + case SAA7127_OUTPUT_TYPE_YUV_C: + state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */ + state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ + break; + + case SAA7127_OUTPUT_TYPE_BOTH: + state->reg_2d = 0xbf; + state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ + break; + + default: + return -EINVAL; + } + v4l_dbg(1, debug, client, "Selecting %s output type\n", output_strs[output]); + + /* Configure Encoder */ + saa7127_write(client, 0x2d, state->reg_2d); + saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb); + state->output_type = output; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_input_type(struct i2c_client *client, int input) +{ + struct saa7127_state *state = i2c_get_clientdata(client); + + switch (input) { + case SAA7127_INPUT_TYPE_NORMAL: /* avia */ + v4l_dbg(1, debug, client, "Selecting Normal Encoder Input\n"); + state->reg_3a_cb = 0; + break; + + case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ + v4l_dbg(1, debug, client, "Selecting Color Bar generator\n"); + state->reg_3a_cb = 0x80; + break; + + default: + return -EINVAL; + } + saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb); + state->input_type = input; + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct saa7127_state *state = i2c_get_clientdata(client); + struct v4l2_format *fmt = arg; + int *iarg = arg; + + switch (cmd) { + case VIDIOC_S_STD: + if (state->std == *(v4l2_std_id *)arg) + break; + return saa7127_set_std(client, *(v4l2_std_id *)arg); + + case VIDIOC_G_STD: + *(v4l2_std_id *)arg = state->std; + break; + + case VIDIOC_S_INPUT: + if (state->input_type == *iarg) + break; + return saa7127_set_input_type(client, *iarg); + + case VIDIOC_S_OUTPUT: + if (state->output_type == *iarg) + break; + return saa7127_set_output_type(client, *iarg); + + case VIDIOC_STREAMON: + case VIDIOC_STREAMOFF: + if (state->video_enable == (cmd == VIDIOC_STREAMON)) + break; + return saa7127_set_video_enable(client, cmd == VIDIOC_STREAMON); + + case VIDIOC_G_FMT: + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + + memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced)); + if (state->vps_enable) + fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS; + if (state->wss_enable) + fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; + if (state->cc_enable) { + fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; + fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } + fmt->fmt.sliced.service_set = + (state->vps_enable ? V4L2_SLICED_VPS : 0) | + (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | + (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); + break; + + case VIDIOC_LOG_STATUS: + v4l_info(client, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); + v4l_info(client, "Input: %s\n", state->input_type ? "color bars" : "normal"); + v4l_info(client, "Output: %s\n", state->video_enable ? + output_strs[state->output_type] : "disabled"); + v4l_info(client, "WSS: %s\n", state->wss_enable ? + wss_strs[state->wss_mode] : "disabled"); + v4l_info(client, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); + v4l_info(client, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); + break; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_SAA7127) + return -EINVAL; + reg->val = saa7127_read(client, reg->reg & 0xff); + break; + } + + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_SAA7127) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa7127_write(client, reg->reg & 0xff, reg->val & 0xff); + break; + } +#endif + + case VIDIOC_INT_S_VBI_DATA: + { + struct v4l2_sliced_vbi_data *data = arg; + + switch (data->id) { + case V4L2_SLICED_WSS_625: + return saa7127_set_wss(client, data); + case V4L2_SLICED_VPS: + return saa7127_set_vps(client, data); + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) + return saa7127_set_cc(client, data); + return saa7127_set_xds(client, data); + default: + return -EINVAL; + } + break; + } + + case VIDIOC_INT_G_CHIP_IDENT: + *(enum v4l2_chip_ident *)arg = state->ident; + break; + + default: + return -EINVAL; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_saa7127; + +/* ----------------------------------------------------------------------- */ + +static int saa7127_attach(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct saa7127_state *state; + struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ + int read_result = 0; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_saa7127; + snprintf(client->name, sizeof(client->name) - 1, "saa7127"); + + v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n", address << 1); + + /* First test register 0: Bits 5-7 are a version ID (should be 0), + and bit 2 should also be 0. + This is rather general, so the second test is more specific and + looks at the 'ending point of burst in clock cycles' which is + 0x1d after a reset and not expected to ever change. */ + if ((saa7127_read(client, 0) & 0xe4) != 0 || + (saa7127_read(client, 0x29) & 0x3f) != 0x1d) { + v4l_dbg(1, debug, client, "saa7127 not found\n"); + kfree(client); + return 0; + } + state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); + + if (state == NULL) { + kfree(client); + return (-ENOMEM); + } + + i2c_set_clientdata(client, state); + + /* Configure Encoder */ + + v4l_dbg(1, debug, client, "Configuring encoder\n"); + saa7127_write_inittab(client, saa7127_init_config_common); + saa7127_set_std(client, V4L2_STD_NTSC); + saa7127_set_output_type(client, SAA7127_OUTPUT_TYPE_BOTH); + saa7127_set_vps(client, &vbi); + saa7127_set_wss(client, &vbi); + saa7127_set_cc(client, &vbi); + saa7127_set_xds(client, &vbi); + if (test_image == 1) { + /* The Encoder has an internal Colorbar generator */ + /* This can be used for debugging */ + saa7127_set_input_type(client, SAA7127_INPUT_TYPE_TEST_IMAGE); + } else { + saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL); + } + saa7127_set_video_enable(client, 1); + + /* Detect if it's an saa7129 */ + read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2); + saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa); + if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { + v4l_info(client, "saa7129 found @ 0x%x (%s)\n", address << 1, adapter->name); + saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, read_result); + saa7127_write_inittab(client, saa7129_init_config_extra); + state->ident = V4L2_IDENT_SAA7129; + } else { + v4l_info(client, "saa7127 found @ 0x%x (%s)\n", address << 1, adapter->name); + state->ident = V4L2_IDENT_SAA7127; + } + + i2c_attach_client(client); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_probe(struct i2c_adapter *adapter) +{ + if (adapter->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adapter, &addr_data, saa7127_attach); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_detach(struct i2c_client *client) +{ + struct saa7127_state *state = i2c_get_clientdata(client); + int err; + + /* Turn off TV output */ + saa7127_set_video_enable(client, 0); + + err = i2c_detach_client(client); + + if (err) { + return err; + } + + kfree(state); + kfree(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver i2c_driver_saa7127 = { + .driver = { + .name = "saa7127", + }, + .id = I2C_DRIVERID_SAA7127, + .attach_adapter = saa7127_probe, + .detach_client = saa7127_detach, + .command = saa7127_command, +}; + + +/* ----------------------------------------------------------------------- */ + +static int __init saa7127_init_module(void) +{ + return i2c_add_driver(&i2c_driver_saa7127); +} + +/* ----------------------------------------------------------------------- */ + +static void __exit saa7127_cleanup_module(void) +{ + i2c_del_driver(&i2c_driver_saa7127); +} + +/* ----------------------------------------------------------------------- */ + +module_init(saa7127_init_module); +module_exit(saa7127_cleanup_module); diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 624e8808a51..86671a43e76 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -1,6 +1,6 @@ config VIDEO_SAA7134 tristate "Philips SAA7134 support" - depends on VIDEO_DEV && PCI && I2C && SOUND + depends on VIDEO_DEV && PCI && I2C select VIDEO_BUF select VIDEO_IR select VIDEO_TUNER @@ -12,6 +12,29 @@ config VIDEO_SAA7134 To compile this driver as a module, choose M here: the module will be called saa7134. +config VIDEO_SAA7134_ALSA + tristate "Philips SAA7134 DMA audio support" + depends on VIDEO_SAA7134 && SND + select SND_PCM + ---help--- + This is a video4linux driver for direct (DMA) audio in + Philips SAA713x based TV cards using ALSA + + To compile this driver as a module, choose M here: the + module will be called saa7134-alsa. + +config VIDEO_SAA7134_OSS + tristate "Philips SAA7134 DMA audio support (OSS, DEPRECATED)" + depends on VIDEO_SAA7134 && SOUND_PRIME && !VIDEO_SAA7134_ALSA + ---help--- + This is a video4linux driver for direct (DMA) audio in + Philips SAA713x based TV cards using OSS + + This is deprecated in favor of the ALSA module + + To compile this driver as a module, choose M here: the + module will be called saa7134-oss. + config VIDEO_SAA7134_DVB tristate "DVB/ATSC Support for saa7134 based TV cards" depends on VIDEO_SAA7134 && DVB_CORE @@ -41,8 +64,8 @@ config VIDEO_SAA7134_DVB_ALL_FRONTENDS If you are unsure, choose Y. config VIDEO_SAA7134_DVB_MT352 - tristate "Zarlink MT352 DVB-T Support" - default m + bool "Zarlink MT352 DVB-T Support" + default y depends on VIDEO_SAA7134_DVB && !VIDEO_SAA7134_DVB_ALL_FRONTENDS select DVB_MT352 ---help--- @@ -50,8 +73,8 @@ config VIDEO_SAA7134_DVB_MT352 Philips saa7134 chip and the MT352 demodulator. config VIDEO_SAA7134_DVB_TDA1004X - tristate "Phillips TDA10045H/TDA10046H DVB-T Support" - default m + bool "Phillips TDA10045H/TDA10046H DVB-T Support" + default y depends on VIDEO_SAA7134_DVB && !VIDEO_SAA7134_DVB_ALL_FRONTENDS select DVB_TDA1004X ---help--- @@ -59,8 +82,8 @@ config VIDEO_SAA7134_DVB_TDA1004X Philips saa7134 chip and the TDA10045H/TDA10046H demodulator. config VIDEO_SAA7134_DVB_NXT200X - tristate "NXT2002/NXT2004 ATSC Support" - default m + bool "NXT2002/NXT2004 ATSC Support" + default y depends on VIDEO_SAA7134_DVB && !VIDEO_SAA7134_DVB_ALL_FRONTENDS select DVB_NXT200X ---help--- diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile index e0b28f0533a..1ba998424bb 100644 --- a/drivers/media/video/saa7134/Makefile +++ b/drivers/media/video/saa7134/Makefile @@ -1,24 +1,23 @@ saa7134-objs := saa7134-cards.o saa7134-core.o saa7134-i2c.o \ - saa7134-oss.o saa7134-ts.o saa7134-tvaudio.o \ - saa7134-vbi.o saa7134-video.o saa7134-input.o + saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o \ + saa7134-video.o saa7134-input.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o saa7134-empress.o \ - saa6752hs.o saa7134-alsa.o + saa6752hs.o + +obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o +obj-$(CONFIG_VIDEO_SAA7134_OSS) += saa7134-oss.o + obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o EXTRA_CFLAGS += -I$(src)/.. EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/frontends -ifneq ($(CONFIG_VIDEO_BUF_DVB),n) - EXTRA_CFLAGS += -DHAVE_VIDEO_BUF_DVB=1 -endif -ifneq ($(CONFIG_DVB_MT352),n) - EXTRA_CFLAGS += -DHAVE_MT352=1 -endif -ifneq ($(CONFIG_DVB_TDA1004X),n) - EXTRA_CFLAGS += -DHAVE_TDA1004X=1 -endif -ifneq ($(CONFIG_DVB_NXT200X),n) - EXTRA_CFLAGS += -DHAVE_NXT200X=1 -endif + +extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1 +extra-cflags-$(CONFIG_DVB_MT352) += -DHAVE_MT352=1 +extra-cflags-$(CONFIG_DVB_TDA1004X) += -DHAVE_TDA1004X=1 +extra-cflags-$(CONFIG_DVB_NXT200X) += -DHAVE_NXT200X=1 + +EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c index cdd1ed9c806..0e0ba50946e 100644 --- a/drivers/media/video/saa7134/saa6752hs.c +++ b/drivers/media/video/saa7134/saa6752hs.c @@ -9,7 +9,8 @@ #include <linux/poll.h> #include <linux/i2c.h> #include <linux/types.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> #include <linux/init.h> #include <linux/crc32.h> @@ -509,11 +510,9 @@ static int saa6752hs_attach(struct i2c_adapter *adap, int addr, int kind) { struct saa6752hs_state *h; - printk("saa6752hs: chip found @ 0x%x\n", addr<<1); - if (NULL == (h = kmalloc(sizeof(*h), GFP_KERNEL))) + if (NULL == (h = kzalloc(sizeof(*h), GFP_KERNEL))) return -ENOMEM; - memset(h,0,sizeof(*h)); h->client = client_template; h->params = param_defaults; h->client.adapter = adap; @@ -523,7 +522,10 @@ static int saa6752hs_attach(struct i2c_adapter *adap, int addr, int kind) h->standard = 0; i2c_set_clientdata(&h->client, h); - i2c_attach_client(&h->client); + i2c_attach_client(&h->client); + + v4l_info(&h->client,"saa6752hs: chip found @ 0x%x\n", addr<<1); + return 0; } @@ -596,20 +598,19 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) /* ----------------------------------------------------------------------- */ static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "i2c saa6752hs MPEG encoder", - .id = I2C_DRIVERID_SAA6752HS, - .flags = I2C_DF_NOTIFY, - .attach_adapter = saa6752hs_probe, - .detach_client = saa6752hs_detach, - .command = saa6752hs_command, + .driver = { + .name = "saa6752hs", + }, + .id = I2C_DRIVERID_SAA6752HS, + .attach_adapter = saa6752hs_probe, + .detach_client = saa6752hs_detach, + .command = saa6752hs_command, }; static struct i2c_client client_template = { .name = "saa6752hs", - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, + .driver = &driver, }; static int __init saa6752hs_init_module(void) diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index 4f3c4235432..a7a6ab9298a 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -20,17 +20,19 @@ * */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/time.h> #include <linux/wait.h> #include <linux/moduleparam.h> #include <linux/module.h> +#include <sound/driver.h> #include <sound/core.h> #include <sound/control.h> #include <sound/pcm.h> +#include <sound/pcm_params.h> #include <sound/initval.h> +#include <linux/interrupt.h> #include "saa7134.h" #include "saa7134-reg.h" @@ -49,6 +51,7 @@ MODULE_PARM_DESC(debug,"enable debug messages [alsa]"); #define MIXER_ADDR_LINE2 2 #define MIXER_ADDR_LAST 2 + static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; @@ -57,18 +60,21 @@ module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for SAA7134 capture interface(s)."); #define dprintk(fmt, arg...) if (debug) \ - printk(KERN_DEBUG "%s/alsa: " fmt, dev->name , ## arg) + printk(KERN_DEBUG "%s/alsa: " fmt, dev->name , ##arg) + + /* * Main chip structure */ + typedef struct snd_card_saa7134 { snd_card_t *card; spinlock_t mixer_lock; int mixer_volume[MIXER_ADDR_LAST+1][2]; int capture_source[MIXER_ADDR_LAST+1][2]; struct pci_dev *pci; - struct saa7134_dev *saadev; + struct saa7134_dev *dev; unsigned long iobase; int irq; @@ -83,12 +89,10 @@ typedef struct snd_card_saa7134 { */ typedef struct snd_card_saa7134_pcm { - struct saa7134_dev *saadev; + struct saa7134_dev *dev; spinlock_t lock; - unsigned int pcm_size; /* buffer size */ - unsigned int pcm_count; /* bytes per period */ - unsigned int pcm_bps; /* bytes per second */ + snd_pcm_substream_t *substream; } snd_card_saa7134_pcm_t; @@ -100,13 +104,11 @@ static snd_card_t *snd_saa7134_cards[SNDRV_CARDS]; * * Called when the capture device is released or the buffer overflows * - * - Copied verbatim from saa7134-oss's dsp_dma_stop. Can be dropped - * if we just share dsp_dma_stop and use it here + * - Copied verbatim from saa7134-oss's dsp_dma_stop. * */ static void saa7134_dma_stop(struct saa7134_dev *dev) - { dev->dmasound.dma_blk = -1; dev->dmasound.dma_running = 0; @@ -118,8 +120,7 @@ static void saa7134_dma_stop(struct saa7134_dev *dev) * * Called when preparing the capture device for use * - * - Copied verbatim from saa7134-oss's dsp_dma_start. Can be dropped - * if we just share dsp_dma_start and use it here + * - Copied verbatim from saa7134-oss's dsp_dma_start. * */ @@ -141,7 +142,8 @@ static void saa7134_dma_start(struct saa7134_dev *dev) * */ -void saa7134_irq_alsa_done(struct saa7134_dev *dev, unsigned long status) +static void saa7134_irq_alsa_done(struct saa7134_dev *dev, + unsigned long status) { int next_blk, reg = 0; @@ -170,9 +172,9 @@ void saa7134_irq_alsa_done(struct saa7134_dev *dev, unsigned long status) if (dev->dmasound.read_count >= dev->dmasound.blksize * (dev->dmasound.blocks-2)) { dprintk("irq: overrun [full=%d/%d] - Blocks in %d\n",dev->dmasound.read_count, dev->dmasound.bufsize, dev->dmasound.blocks); + spin_unlock(&dev->slock); snd_pcm_stop(dev->dmasound.substream,SNDRV_PCM_STATE_XRUN); - saa7134_dma_stop(dev); - goto done; + return; } /* next block addr */ @@ -194,6 +196,7 @@ void saa7134_irq_alsa_done(struct saa7134_dev *dev, unsigned long status) snd_pcm_period_elapsed(dev->dmasound.substream); spin_lock(&dev->slock); } + done: spin_unlock(&dev->slock); @@ -209,7 +212,9 @@ void saa7134_irq_alsa_done(struct saa7134_dev *dev, unsigned long status) static irqreturn_t saa7134_alsa_irq(int irq, void *dev_id, struct pt_regs *regs) { - struct saa7134_dev *dev = (struct saa7134_dev*) dev_id; + struct saa7134_dmasound *dmasound = dev_id; + struct saa7134_dev *dev = dmasound->priv_data; + unsigned long report, status; int loop, handled = 0; @@ -248,56 +253,23 @@ static int snd_card_saa7134_capture_trigger(snd_pcm_substream_t * substream, int cmd) { snd_pcm_runtime_t *runtime = substream->runtime; - snd_card_saa7134_pcm_t *saapcm = runtime->private_data; - struct saa7134_dev *dev=saapcm->saadev; + snd_card_saa7134_pcm_t *pcm = runtime->private_data; + struct saa7134_dev *dev=pcm->dev; int err = 0; - spin_lock_irq(&dev->slock); - if (cmd == SNDRV_PCM_TRIGGER_START) { + spin_lock(&dev->slock); + if (cmd == SNDRV_PCM_TRIGGER_START) { /* start dma */ saa7134_dma_start(dev); - } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { /* stop dma */ saa7134_dma_stop(dev); - } else { - err = -EINVAL; - } - spin_unlock_irq(&dev->slock); - - return err; -} - -/* - * DMA buffer config - * - * Sets the values that will later be used as the size of the buffer, - * size of the fragments, and total number of fragments. - * Must be called during the preparation stage, before memory is - * allocated - * - * - Copied verbatim from saa7134-oss. Can be dropped - * if we just share dsp_buffer_conf from OSS. - */ + } else { + err = -EINVAL; + } + spin_unlock(&dev->slock); -static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks) -{ - if (blksize < 0x100) - blksize = 0x100; - if (blksize > 0x10000) - blksize = 0x10000; - - if (blocks < 2) - blocks = 2; - if ((blksize * blocks) > 1024*1024) - blocks = 1024*1024 / blksize; - - dev->dmasound.blocks = blocks; - dev->dmasound.blksize = blksize; - dev->dmasound.bufsize = blksize * blocks; - - dprintk("buffer config: %d blocks / %d bytes, %d kB total\n", - blocks,blksize,blksize * blocks / 1024); - return 0; + return err; } /* @@ -307,16 +279,16 @@ static int dsp_buffer_conf(struct saa7134_dev *dev, int blksize, int blocks) * ALSA, but I was unable to use ALSA's own DMA, and had to force the * usage of V4L's * - * - Copied verbatim from saa7134-oss. Can be dropped - * if we just share dsp_buffer_init from OSS. + * - Copied verbatim from saa7134-oss. + * */ static int dsp_buffer_init(struct saa7134_dev *dev) { int err; - if (!dev->dmasound.bufsize) - BUG(); + BUG_ON(!dev->dmasound.bufsize); + videobuf_dma_init(&dev->dmasound.dma); err = videobuf_dma_init_kernel(&dev->dmasound.dma, PCI_DMA_FROMDEVICE, (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT); @@ -326,6 +298,28 @@ static int dsp_buffer_init(struct saa7134_dev *dev) } /* + * DMA buffer release + * + * Called after closing the device, during snd_card_saa7134_capture_close + * + */ + +static int dsp_buffer_free(struct saa7134_dev *dev) +{ + if (!dev->dmasound.blksize) + BUG(); + + videobuf_dma_free(&dev->dmasound.dma); + + dev->dmasound.blocks = 0; + dev->dmasound.blksize = 0; + dev->dmasound.bufsize = 0; + + return 0; +} + + +/* * ALSA PCM preparation * * - One of the ALSA capture callbacks. @@ -340,84 +334,30 @@ static int dsp_buffer_init(struct saa7134_dev *dev) static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; - int err, bswap, sign; + int bswap, sign; u32 fmt, control; snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); struct saa7134_dev *dev; - snd_card_saa7134_pcm_t *saapcm = runtime->private_data; - unsigned int bps; - unsigned long size; - unsigned count; - - size = snd_pcm_lib_buffer_bytes(substream); - count = snd_pcm_lib_period_bytes(substream); - - saapcm->saadev->dmasound.substream = substream; - bps = runtime->rate * runtime->channels; - bps *= snd_pcm_format_width(runtime->format); - bps /= 8; - if (bps <= 0) - return -EINVAL; - saapcm->pcm_bps = bps; - saapcm->pcm_size = snd_pcm_lib_buffer_bytes(substream); - saapcm->pcm_count = snd_pcm_lib_period_bytes(substream); - - - dev=saa7134->saadev; + snd_card_saa7134_pcm_t *pcm = runtime->private_data; - dsp_buffer_conf(dev,saapcm->pcm_count,(saapcm->pcm_size/saapcm->pcm_count)); + pcm->dev->dmasound.substream = substream; - err = dsp_buffer_init(dev); - if (0 != err) - goto fail2; - - /* prepare buffer */ - if (0 != (err = videobuf_dma_pci_map(dev->pci,&dev->dmasound.dma))) - return err; - if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) - goto fail1; - if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt, - dev->dmasound.dma.sglist, - dev->dmasound.dma.sglen, - 0))) - goto fail2; + dev = saa7134->dev; - - - switch (runtime->format) { - case SNDRV_PCM_FORMAT_U8: - case SNDRV_PCM_FORMAT_S8: + if (snd_pcm_format_width(runtime->format) == 8) fmt = 0x00; - break; - case SNDRV_PCM_FORMAT_U16_LE: - case SNDRV_PCM_FORMAT_U16_BE: - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_S16_BE: + else fmt = 0x01; - break; - default: - err = -EINVAL; - return 1; - } - switch (runtime->format) { - case SNDRV_PCM_FORMAT_S8: - case SNDRV_PCM_FORMAT_S16_LE: - case SNDRV_PCM_FORMAT_S16_BE: + if (snd_pcm_format_signed(runtime->format)) sign = 1; - break; - default: + else sign = 0; - break; - } - switch (runtime->format) { - case SNDRV_PCM_FORMAT_U16_BE: - case SNDRV_PCM_FORMAT_S16_BE: - bswap = 1; break; - default: - bswap = 0; break; - } + if (snd_pcm_format_big_endian(runtime->format)) + bswap = 1; + else + bswap = 0; switch (dev->pci->device) { case PCI_DEVICE_ID_PHILIPS_SAA7134: @@ -445,7 +385,6 @@ static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream) fmt |= 0x04; saa_writel(SAA7133_NUM_SAMPLES, dev->dmasound.blksize -1); saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210 | (fmt << 24)); - //saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210); break; } @@ -459,12 +398,6 @@ static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream) if (bswap) control |= SAA7134_RS_CONTROL_BSWAP; - /* I should be able to use runtime->dma_addr in the control - byte, but it doesn't work. So I allocate the DMA using the - V4L functions, and force ALSA to use that as the DMA area */ - - runtime->dma_area = dev->dmasound.dma.vmalloc; - saa_writel(SAA7134_RS_BA1(6),0); saa_writel(SAA7134_RS_BA2(6),dev->dmasound.blksize); saa_writel(SAA7134_RS_PITCH(6),0); @@ -473,12 +406,6 @@ static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream) dev->dmasound.rate = runtime->rate; return 0; - fail2: - saa7134_pgtable_free(dev->pci,&dev->dmasound.pt); - fail1: - videobuf_dma_pci_unmap(dev->pci,&dev->dmasound.dma); - return err; - } @@ -496,10 +423,8 @@ static int snd_card_saa7134_capture_prepare(snd_pcm_substream_t * substream) static snd_pcm_uframes_t snd_card_saa7134_capture_pointer(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; - snd_card_saa7134_pcm_t *saapcm = runtime->private_data; - struct saa7134_dev *dev=saapcm->saadev; - - + snd_card_saa7134_pcm_t *pcm = runtime->private_data; + struct saa7134_dev *dev=pcm->dev; if (dev->dmasound.read_count) { dev->dmasound.read_count -= snd_pcm_lib_period_bytes(substream); @@ -540,9 +465,9 @@ static snd_pcm_hardware_t snd_card_saa7134_capture = static void snd_card_saa7134_runtime_free(snd_pcm_runtime_t *runtime) { - snd_card_saa7134_pcm_t *saapcm = runtime->private_data; + snd_card_saa7134_pcm_t *pcm = runtime->private_data; - kfree(saapcm); + kfree(pcm); } @@ -552,17 +477,76 @@ static void snd_card_saa7134_runtime_free(snd_pcm_runtime_t *runtime) * - One of the ALSA capture callbacks. * * Called on initialization, right before the PCM preparation - * Usually used in ALSA to allocate the DMA, but since we don't use the - * ALSA DMA it does nothing * */ static int snd_card_saa7134_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params) { + snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); + struct saa7134_dev *dev; + unsigned int period_size, periods; + int err; - return 0; + period_size = params_period_bytes(hw_params); + periods = params_periods(hw_params); + snd_assert(period_size >= 0x100 && period_size <= 0x10000, + return -EINVAL); + snd_assert(periods >= 2, return -EINVAL); + snd_assert(period_size * periods <= 1024 * 1024, return -EINVAL); + + dev = saa7134->dev; + + if (dev->dmasound.blocks == periods && + dev->dmasound.blksize == period_size) + return 0; + + /* release the old buffer */ + if (substream->runtime->dma_area) { + saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); + videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma); + dsp_buffer_free(dev); + substream->runtime->dma_area = NULL; + } + dev->dmasound.blocks = periods; + dev->dmasound.blksize = period_size; + dev->dmasound.bufsize = period_size * periods; + + err = dsp_buffer_init(dev); + if (0 != err) { + dev->dmasound.blocks = 0; + dev->dmasound.blksize = 0; + dev->dmasound.bufsize = 0; + return err; + } + + if (0 != (err = videobuf_dma_pci_map(dev->pci, &dev->dmasound.dma))) { + dsp_buffer_free(dev); + return err; + } + if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) { + videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma); + dsp_buffer_free(dev); + return err; + } + if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt, + dev->dmasound.dma.sglist, + dev->dmasound.dma.sglen, + 0))) { + saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); + videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma); + dsp_buffer_free(dev); + return err; + } + + /* I should be able to use runtime->dma_addr in the control + byte, but it doesn't work. So I allocate the DMA using the + V4L functions, and force ALSA to use that as the DMA area */ + + substream->runtime->dma_area = dev->dmasound.dma.vmalloc; + + return 1; } @@ -572,33 +556,23 @@ static int snd_card_saa7134_hw_params(snd_pcm_substream_t * substream, * - One of the ALSA capture callbacks. * * Called after closing the device, but before snd_card_saa7134_capture_close - * Usually used in ALSA to free the DMA, but since we don't use the - * ALSA DMA I'm almost sure this isn't necessary. + * It stops the DMA audio and releases the buffers. * */ static int snd_card_saa7134_hw_free(snd_pcm_substream_t * substream) { - return 0; -} - -/* - * DMA buffer release - * - * Called after closing the device, during snd_card_saa7134_capture_close - * - */ - -static int dsp_buffer_free(struct saa7134_dev *dev) -{ - if (!dev->dmasound.blksize) - BUG(); + snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); + struct saa7134_dev *dev; - videobuf_dma_free(&dev->dmasound.dma); + dev = saa7134->dev; - dev->dmasound.blocks = 0; - dev->dmasound.blksize = 0; - dev->dmasound.bufsize = 0; + if (substream->runtime->dma_area) { + saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); + videobuf_dma_pci_unmap(dev->pci, &dev->dmasound.dma); + dsp_buffer_free(dev); + substream->runtime->dma_area = NULL; + } return 0; } @@ -608,21 +582,12 @@ static int dsp_buffer_free(struct saa7134_dev *dev) * * - One of the ALSA capture callbacks. * - * Called after closing the device. It stops the DMA audio and releases - * the buffers + * Called after closing the device. * */ static int snd_card_saa7134_capture_close(snd_pcm_substream_t * substream) { - snd_card_saa7134_t *chip = snd_pcm_substream_chip(substream); - struct saa7134_dev *dev = chip->saadev; - - /* unlock buffer */ - saa7134_pgtable_free(dev->pci,&dev->dmasound.pt); - videobuf_dma_pci_unmap(dev->pci,&dev->dmasound.dma); - - dsp_buffer_free(dev); return 0; } @@ -639,29 +604,28 @@ static int snd_card_saa7134_capture_close(snd_pcm_substream_t * substream) static int snd_card_saa7134_capture_open(snd_pcm_substream_t * substream) { snd_pcm_runtime_t *runtime = substream->runtime; - snd_card_saa7134_pcm_t *saapcm; + snd_card_saa7134_pcm_t *pcm; snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); - struct saa7134_dev *dev = saa7134->saadev; + struct saa7134_dev *dev = saa7134->dev; int err; down(&dev->dmasound.lock); - dev->dmasound.afmt = SNDRV_PCM_FORMAT_U8; - dev->dmasound.channels = 2; dev->dmasound.read_count = 0; dev->dmasound.read_offset = 0; up(&dev->dmasound.lock); - saapcm = kzalloc(sizeof(*saapcm), GFP_KERNEL); - if (saapcm == NULL) + pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + if (pcm == NULL) return -ENOMEM; - saapcm->saadev=saa7134->saadev; - spin_lock_init(&saapcm->lock); + pcm->dev=saa7134->dev; + + spin_lock_init(&pcm->lock); - saapcm->substream = substream; - runtime->private_data = saapcm; + pcm->substream = substream; + runtime->private_data = pcm; runtime->private_free = snd_card_saa7134_runtime_free; runtime->hw = snd_card_saa7134_capture; @@ -736,7 +700,6 @@ static int snd_saa7134_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_ static int snd_saa7134_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; int change, addr = kcontrol->private_value; int left, right; @@ -750,12 +713,12 @@ static int snd_saa7134_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_ right = 0; if (right > 20) right = 20; - spin_lock_irqsave(&chip->mixer_lock, flags); + spin_lock_irq(&chip->mixer_lock); change = chip->mixer_volume[addr][0] != left || chip->mixer_volume[addr][1] != right; chip->mixer_volume[addr][0] = left; chip->mixer_volume[addr][1] = right; - spin_unlock_irqrestore(&chip->mixer_lock, flags); + spin_unlock_irq(&chip->mixer_lock); return change; } @@ -777,38 +740,37 @@ static int snd_saa7134_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_ static int snd_saa7134_capsrc_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; int addr = kcontrol->private_value; - spin_lock_irqsave(&chip->mixer_lock, flags); + spin_lock_irq(&chip->mixer_lock); ucontrol->value.integer.value[0] = chip->capture_source[addr][0]; ucontrol->value.integer.value[1] = chip->capture_source[addr][1]; - spin_unlock_irqrestore(&chip->mixer_lock, flags); + spin_unlock_irq(&chip->mixer_lock); + return 0; } static int snd_saa7134_capsrc_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) { snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); - unsigned long flags; int change, addr = kcontrol->private_value; int left, right; u32 anabar, xbarin; int analog_io, rate; struct saa7134_dev *dev; - dev = chip->saadev; + dev = chip->dev; left = ucontrol->value.integer.value[0] & 1; right = ucontrol->value.integer.value[1] & 1; - spin_lock_irqsave(&chip->mixer_lock, flags); + spin_lock_irq(&chip->mixer_lock); change = chip->capture_source[addr][0] != left || chip->capture_source[addr][1] != right; chip->capture_source[addr][0] = left; chip->capture_source[addr][1] = right; dev->dmasound.input=addr; - spin_unlock_irqrestore(&chip->mixer_lock, flags); + spin_unlock_irq(&chip->mixer_lock); if (change) { @@ -898,43 +860,44 @@ static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) return 0; } -static int snd_saa7134_free(snd_card_saa7134_t *chip) +static void snd_saa7134_free(snd_card_t * card) { - return 0; -} + snd_card_saa7134_t *chip = card->private_data; + + if (chip->dev->dmasound.priv_data == NULL) + return; + + if (chip->irq >= 0) { + synchronize_irq(chip->irq); + free_irq(chip->irq, &chip->dev->dmasound); + } + + chip->dev->dmasound.priv_data = NULL; -static int snd_saa7134_dev_free(snd_device_t *device) -{ - snd_card_saa7134_t *chip = device->device_data; - return snd_saa7134_free(chip); } /* * ALSA initialization * - * Called by saa7134-core, it creates the basic structures and registers - * the ALSA devices + * Called by the init routine, once for each saa7134 device present, + * it creates the basic structures and registers the ALSA devices * */ -int alsa_card_saa7134_create (struct saa7134_dev *saadev) +static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) { - static int dev; snd_card_t *card; snd_card_saa7134_t *chip; int err; - static snd_device_ops_t ops = { - .dev_free = snd_saa7134_dev_free, - }; - if (dev >= SNDRV_CARDS) + if (devnum >= SNDRV_CARDS) return -ENODEV; - if (!enable[dev]) + if (!enable[devnum]) return -ENODEV; - card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + card = snd_card_new(index[devnum], id[devnum], THIS_MODULE, sizeof(snd_card_saa7134_t)); if (card == NULL) return -ENOMEM; @@ -943,34 +906,33 @@ int alsa_card_saa7134_create (struct saa7134_dev *saadev) /* Card "creation" */ - chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); - if (chip == NULL) { - return -ENOMEM; - } + card->private_free = snd_saa7134_free; + chip = (snd_card_saa7134_t *) card->private_data; spin_lock_init(&chip->lock); spin_lock_init(&chip->mixer_lock); - chip->saadev = saadev; + chip->dev = dev; chip->card = card; - chip->pci = saadev->pci; - chip->irq = saadev->pci->irq; - chip->iobase = pci_resource_start(saadev->pci, 0); + chip->pci = dev->pci; + chip->iobase = pci_resource_start(dev->pci, 0); - err = request_irq(saadev->pci->irq, saa7134_alsa_irq, - SA_SHIRQ | SA_INTERRUPT, saadev->name, saadev); + + err = request_irq(dev->pci->irq, saa7134_alsa_irq, + SA_SHIRQ | SA_INTERRUPT, dev->name, + (void*) &dev->dmasound); if (err < 0) { printk(KERN_ERR "%s: can't get IRQ %d for ALSA\n", - saadev->name, saadev->pci->irq); + dev->name, dev->pci->irq); goto __nodev; } - if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { - goto __nodev; - } + chip->irq = dev->pci->irq; + + init_MUTEX(&dev->dmasound.lock); if ((err = snd_card_saa7134_new_mixer(chip)) < 0) goto __nodev; @@ -984,19 +946,36 @@ int alsa_card_saa7134_create (struct saa7134_dev *saadev) strcpy(card->shortname, "SAA7134"); sprintf(card->longname, "%s at 0x%lx irq %d", - chip->saadev->name, chip->iobase, chip->irq); + chip->dev->name, chip->iobase, chip->irq); + + printk(KERN_INFO "%s/alsa: %s registered as card %d\n",dev->name,card->longname,index[devnum]); if ((err = snd_card_register(card)) == 0) { - snd_saa7134_cards[dev] = card; + snd_saa7134_cards[devnum] = card; return 0; } __nodev: snd_card_free(card); - kfree(chip); return err; } + +static int alsa_device_init(struct saa7134_dev *dev) +{ + dev->dmasound.priv_data = dev; + alsa_card_saa7134_create(dev,dev->nr); + return 1; +} + +static int alsa_device_exit(struct saa7134_dev *dev) +{ + + snd_card_free(snd_saa7134_cards[dev->nr]); + snd_saa7134_cards[dev->nr] = NULL; + return 1; +} + /* * Module initializer * @@ -1007,17 +986,30 @@ __nodev: static int saa7134_alsa_init(void) { - struct saa7134_dev *saadev = NULL; - struct list_head *list; + struct saa7134_dev *dev = NULL; + struct list_head *list; + + if (!dmasound_init && !dmasound_exit) { + dmasound_init = alsa_device_init; + dmasound_exit = alsa_device_exit; + } else { + printk(KERN_WARNING "saa7134 ALSA: can't load, DMA sound handler already assigned (probably to OSS)\n"); + return -EBUSY; + } printk(KERN_INFO "saa7134 ALSA driver for DMA sound loaded\n"); - list_for_each(list,&saa7134_devlist) { - saadev = list_entry(list, struct saa7134_dev, devlist); - alsa_card_saa7134_create(saadev); - } + list_for_each(list,&saa7134_devlist) { + dev = list_entry(list, struct saa7134_dev, devlist); + if (dev->dmasound.priv_data == NULL) { + alsa_device_init(dev); + } else { + printk(KERN_ERR "saa7134 ALSA: DMA sound is being handled by OSS. ignoring %s\n",dev->name); + return -EBUSY; + } + } - if (saadev == NULL) + if (dev == NULL) printk(KERN_INFO "saa7134 ALSA: no saa7134 cards found\n"); return 0; @@ -1028,7 +1020,7 @@ static int saa7134_alsa_init(void) * Module destructor */ -void saa7134_alsa_exit(void) +static void saa7134_alsa_exit(void) { int idx; @@ -1036,12 +1028,18 @@ void saa7134_alsa_exit(void) snd_card_free(snd_saa7134_cards[idx]); } + dmasound_init = NULL; + dmasound_exit = NULL; printk(KERN_INFO "saa7134 ALSA driver for DMA sound unloaded\n"); return; } -module_init(saa7134_alsa_init); +/* We initialize this late, to make sure the sound system is up and running */ +late_initcall(saa7134_alsa_init); module_exit(saa7134_alsa_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ricardo Cerqueira"); + + + diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 663d03e5bc6..c64718aec9c 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -25,6 +25,7 @@ #include "saa7134-reg.h" #include "saa7134.h" +#include <media/v4l2-common.h> /* commly used strings */ static char name_mute[] = "mute"; @@ -976,7 +977,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER, + .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_ACTIVE, .inputs = {{ .name = name_tv, .vmux = 3, @@ -1665,7 +1666,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER, + .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_ACTIVE, .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_tv, @@ -2514,6 +2515,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 1 << 21, .inputs = {{ .name = name_tv, .vmux = 1, @@ -2528,6 +2530,100 @@ struct saa7134_board saa7134_boards[] = { .vmux = 8, .amux = LINE1, }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_MSI_TVATANYWHERE_PLUS] = { + .name = "MSI TV@Anywhere plus", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 0, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_CINERGY250PCI] = { + /* remote-control does not work. The signal about a + key press comes in via gpio, but the key code + doesn't. Neither does it have an i2c remote control + interface. */ + .name = "Terratec Cinergy 250 PCI TV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x80200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_svideo, /* NOT tested */ + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_FLYDVB_TRIO] = { + /* LifeView LR319 FlyDVB Trio */ + /* Peter Missel <peter.missel@onlinehome.de> */ + .name = "LifeView FlyDVB Trio", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00200000, + .inputs = {{ + .name = name_tv, /* Analog broadcast/cable TV */ + .vmux = 1, + .amux = TV, + .gpio = 0x200000, /* GPIO21=High for TV input */ + .tv = 1, + },{ + .name = name_svideo, /* S-Video signal on S-Video input */ + .vmux = 8, + .amux = LINE2, + },{ + .name = name_comp1, /* Composite signal on S-Video input */ + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, /* Composite input */ + .vmux = 3, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ + }, }, }; @@ -2869,6 +2965,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x1421, + .subdevice = 0x0351, /* PCI version, new revision */ + .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1421, .subdevice = 0x0370, /* cardbus version */ .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, },{ @@ -2970,6 +3072,24 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x2018, .driver_data = SAA7134_BOARD_PHILIPS_TIGER, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1462, + .subdevice = 0x6231, + .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x153b, + .subdevice = 0x1160, + .driver_data = SAA7134_BOARD_CINERGY250PCI, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA 7131E */ + .subvendor = 0x5168, + .subdevice = 0x0319, + .driver_data = SAA7134_BOARD_FLYDVB_TRIO, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -3058,6 +3178,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_GO_007_FM: /* case SAA7134_BOARD_SABRENT_SBTTVFM: */ /* not finished yet */ case SAA7134_BOARD_VIDEOMATE_TV_PVR: + case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII: case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_VIDEOMATE_DVBT_200: diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 19b88744fb3..028904bd94a 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -31,6 +31,7 @@ #include <linux/sound.h> #include <linux/interrupt.h> #include <linux/delay.h> +#include <linux/mutex.h> #include "saa7134-reg.h" #include "saa7134.h" @@ -53,13 +54,13 @@ static unsigned int gpio_tracking = 0; module_param(gpio_tracking, int, 0644); MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]"); -static unsigned int oss = 0; -module_param(oss, int, 0444); -MODULE_PARM_DESC(oss,"register oss devices (default: no)"); - static unsigned int alsa = 0; -module_param(alsa, int, 0444); -MODULE_PARM_DESC(alsa,"register alsa devices (default: no)"); +module_param(alsa, int, 0644); +MODULE_PARM_DESC(alsa,"enable ALSA DMA sound [dmasound]"); + +static unsigned int oss = 0; +module_param(oss, int, 0644); +MODULE_PARM_DESC(oss,"enable OSS DMA sound [dmasound]"); static unsigned int latency = UNSET; module_param(latency, int, 0444); @@ -68,106 +69,33 @@ MODULE_PARM_DESC(latency,"pci latency timer"); static unsigned int video_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; static unsigned int vbi_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; static unsigned int radio_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; -static unsigned int dsp_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; -static unsigned int mixer_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; static unsigned int tuner[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; static unsigned int card[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; + module_param_array(video_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); module_param_array(radio_nr, int, NULL, 0444); -module_param_array(dsp_nr, int, NULL, 0444); -module_param_array(mixer_nr, int, NULL, 0444); module_param_array(tuner, int, NULL, 0444); module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(video_nr, "video device number"); MODULE_PARM_DESC(vbi_nr, "vbi device number"); MODULE_PARM_DESC(radio_nr, "radio device number"); -MODULE_PARM_DESC(dsp_nr, "oss dsp device number"); -MODULE_PARM_DESC(mixer_nr, "oss mixer device number"); MODULE_PARM_DESC(tuner, "tuner type"); MODULE_PARM_DESC(card, "card type"); -static DECLARE_MUTEX(devlist_lock); +static DEFINE_MUTEX(devlist_lock); LIST_HEAD(saa7134_devlist); static LIST_HEAD(mops_list); static unsigned int saa7134_devcount; +int (*dmasound_init)(struct saa7134_dev *dev); +int (*dmasound_exit)(struct saa7134_dev *dev); + #define dprintk(fmt, arg...) if (core_debug) \ printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg) -/* ------------------------------------------------------------------ */ -/* debug help functions */ - -static const char *v4l1_ioctls[] = { - "0", "GCAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", - "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", - "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", - "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", - "SMICROCODE", "GVBIFMT", "SVBIFMT" }; -#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) - -static const char *v4l2_ioctls[] = { - "QUERYCAP", "1", "ENUM_PIXFMT", "ENUM_FBUFFMT", "G_FMT", "S_FMT", - "G_COMP", "S_COMP", "REQBUFS", "QUERYBUF", "G_FBUF", "S_FBUF", - "G_WIN", "S_WIN", "PREVIEW", "QBUF", "16", "DQBUF", "STREAMON", - "STREAMOFF", "G_PERF", "G_PARM", "S_PARM", "G_STD", "S_STD", - "ENUMSTD", "ENUMINPUT", "G_CTRL", "S_CTRL", "G_TUNER", "S_TUNER", - "G_FREQ", "S_FREQ", "G_AUDIO", "S_AUDIO", "35", "QUERYCTRL", - "QUERYMENU", "G_INPUT", "S_INPUT", "ENUMCVT", "41", "42", "43", - "44", "45", "G_OUTPUT", "S_OUTPUT", "ENUMOUTPUT", "G_AUDOUT", - "S_AUDOUT", "ENUMFX", "G_EFFECT", "S_EFFECT", "G_MODULATOR", - "S_MODULATOR" -}; -#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) - -static const char *osspcm_ioctls[] = { - "RESET", "SYNC", "SPEED", "STEREO", "GETBLKSIZE", "SETFMT", - "CHANNELS", "?", "POST", "SUBDIVIDE", "SETFRAGMENT", "GETFMTS", - "GETOSPACE", "GETISPACE", "NONBLOCK", "GETCAPS", "GET/SETTRIGGER", - "GETIPTR", "GETOPTR", "MAPINBUF", "MAPOUTBUF", "SETSYNCRO", - "SETDUPLEX", "GETODELAY" -}; -#define OSSPCM_IOCTLS ARRAY_SIZE(v4l2_ioctls) - -void saa7134_print_ioctl(char *name, unsigned int cmd) -{ - char *dir; - - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: dir = "--"; break; - case _IOC_READ: dir = "r-"; break; - case _IOC_WRITE: dir = "-w"; break; - case _IOC_READ | _IOC_WRITE: dir = "rw"; break; - default: dir = "??"; break; - } - switch (_IOC_TYPE(cmd)) { - case 'v': - printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l1, %s, VIDIOC%s)\n", - name, cmd, dir, (_IOC_NR(cmd) < V4L1_IOCTLS) ? - v4l1_ioctls[_IOC_NR(cmd)] : "???"); - break; - case 'V': - printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l2, %s, VIDIOC_%s)\n", - name, cmd, dir, (_IOC_NR(cmd) < V4L2_IOCTLS) ? - v4l2_ioctls[_IOC_NR(cmd)] : "???"); - break; - case 'P': - printk(KERN_DEBUG "%s: ioctl 0x%08x (oss dsp, %s, SNDCTL_DSP_%s)\n", - name, cmd, dir, (_IOC_NR(cmd) < OSSPCM_IOCTLS) ? - osspcm_ioctls[_IOC_NR(cmd)] : "???"); - break; - case 'M': - printk(KERN_DEBUG "%s: ioctl 0x%08x (oss mixer, %s, #%d)\n", - name, cmd, dir, _IOC_NR(cmd)); - break; - default: - printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n", - name, cmd, dir, _IOC_NR(cmd)); - } -} - void saa7134_track_gpio(struct saa7134_dev *dev, char *msg) { unsigned long mode,status; @@ -190,11 +118,11 @@ void saa7134_track_gpio(struct saa7134_dev *dev, char *msg) /* ----------------------------------------------------------- */ /* delayed request_module */ -#ifdef CONFIG_MODULES - +#if defined(CONFIG_MODULES) && defined(MODULE) static int need_empress; static int need_dvb; static int need_alsa; +static int need_oss; static int pending_call(struct notifier_block *self, unsigned long state, void *module) @@ -208,6 +136,8 @@ static int pending_call(struct notifier_block *self, unsigned long state, request_module("saa7134-dvb"); if (need_alsa) request_module("saa7134-alsa"); + if (need_oss) + request_module("saa7134-oss"); return NOTIFY_DONE; } @@ -218,10 +148,11 @@ static struct notifier_block pending_notifier = { static void request_module_depend(char *name, int *flag) { + int err; switch (THIS_MODULE->state) { case MODULE_STATE_COMING: if (!pending_registered) { - register_module_notifier(&pending_notifier); + err = register_module_notifier(&pending_notifier); pending_registered = 1; } *flag = 1; @@ -236,9 +167,7 @@ static void request_module_depend(char *name, int *flag) } #else - #define request_module_depend(name,flag) - #endif /* CONFIG_MODULES */ /* ------------------------------------------------------------------ */ @@ -578,12 +507,14 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id, struct pt_regs *regs) goto out; } - /* If alsa support is active and we get a sound report, exit - and let the saa7134-alsa module deal with it */ + /* If dmasound support is active and we get a sound report, exit + and let the saa7134-alsa/oss module deal with it */ - if ((report & SAA7134_IRQ_REPORT_DONE_RA3) && alsa) { + if ((report & SAA7134_IRQ_REPORT_DONE_RA3) && + (dev->dmasound.priv_data != NULL) ) + { if (irq_debug > 1) - printk(KERN_DEBUG "%s/irq: ignoring interrupt for ALSA\n", + printk(KERN_DEBUG "%s/irq: ignoring interrupt for DMA sound\n", dev->name); goto out; } @@ -609,17 +540,38 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id, struct pt_regs *regs) card_has_mpeg(dev)) saa7134_irq_ts_done(dev,status); - if ((report & SAA7134_IRQ_REPORT_DONE_RA3)) { - if (oss) { - saa7134_irq_oss_done(dev,status); + if (report & SAA7134_IRQ_REPORT_GPIO16) { + switch (dev->has_remote) { + case SAA7134_REMOTE_GPIO: + if (dev->remote->mask_keydown & 0x10000) { + saa7134_input_irq(dev); + } + break; + + case SAA7134_REMOTE_I2C: + break; /* FIXME: invoke I2C get_key() */ + + default: /* GPIO16 not used by IR remote */ + break; } } - if ((report & (SAA7134_IRQ_REPORT_GPIO16 | - SAA7134_IRQ_REPORT_GPIO18)) && - dev->remote) - saa7134_input_irq(dev); + if (report & SAA7134_IRQ_REPORT_GPIO18) { + switch (dev->has_remote) { + case SAA7134_REMOTE_GPIO: + if ((dev->remote->mask_keydown & 0x40000) || + (dev->remote->mask_keyup & 0x40000)) { + saa7134_input_irq(dev); + } + break; + + case SAA7134_REMOTE_I2C: + break; /* FIXME: invoke I2C get_key() */ + default: /* GPIO18 not used by IR remote */ + break; + } + } } if (10 == loop) { @@ -629,13 +581,16 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id, struct pt_regs *regs) printk(KERN_WARNING "%s/irq: looping -- " "clearing PE (parity error!) enable bit\n",dev->name); saa_clearl(SAA7134_IRQ2,SAA7134_IRQ2_INTE_PE); - } else if (report & (SAA7134_IRQ_REPORT_GPIO16 | - SAA7134_IRQ_REPORT_GPIO18)) { - /* disable gpio IRQs */ + } else if (report & SAA7134_IRQ_REPORT_GPIO16) { + /* disable gpio16 IRQ */ + printk(KERN_WARNING "%s/irq: looping -- " + "clearing GPIO16 enable bit\n",dev->name); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16); + } else if (report & SAA7134_IRQ_REPORT_GPIO18) { + /* disable gpio18 IRQs */ printk(KERN_WARNING "%s/irq: looping -- " - "clearing GPIO enable bits\n",dev->name); - saa_clearl(SAA7134_IRQ2, (SAA7134_IRQ2_INTE_GPIO16 | - SAA7134_IRQ2_INTE_GPIO18)); + "clearing GPIO18 enable bit\n",dev->name); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18); } else { /* disable all irqs */ printk(KERN_WARNING "%s/irq: looping -- " @@ -689,14 +644,6 @@ static int saa7134_hwinit1(struct saa7134_dev *dev) * audio will not work. */ - switch (dev->pci->device) { - case PCI_DEVICE_ID_PHILIPS_SAA7134: - case PCI_DEVICE_ID_PHILIPS_SAA7133: - case PCI_DEVICE_ID_PHILIPS_SAA7135: - saa7134_oss_init1(dev); - break; - } - /* enable peripheral devices */ saa_writeb(SAA7134_SPECIAL_MODE, 0x01); @@ -724,12 +671,14 @@ static int saa7134_hwinit2(struct saa7134_dev *dev) SAA7134_IRQ2_INTE_PE | SAA7134_IRQ2_INTE_AR; - if (dev->has_remote == SAA7134_REMOTE_GPIO) - irq2_mask |= (SAA7134_IRQ2_INTE_GPIO18 | - SAA7134_IRQ2_INTE_GPIO18A | - SAA7134_IRQ2_INTE_GPIO16 ); - else if (dev->has_remote == SAA7134_REMOTE_I2C) - request_module("ir-kbd-i2c"); + if (dev->has_remote == SAA7134_REMOTE_GPIO) { + if (dev->remote->mask_keydown & 0x10000) + irq2_mask |= SAA7134_IRQ2_INTE_GPIO16; + else if (dev->remote->mask_keydown & 0x40000) + irq2_mask |= SAA7134_IRQ2_INTE_GPIO18; + else if (dev->remote->mask_keyup & 0x40000) + irq2_mask |= SAA7134_IRQ2_INTE_GPIO18A; + } saa_writel(SAA7134_IRQ1, 0); saa_writel(SAA7134_IRQ2, irq2_mask); @@ -742,13 +691,6 @@ static int saa7134_hwfini(struct saa7134_dev *dev) { dprintk("hwfini\n"); - switch (dev->pci->device) { - case PCI_DEVICE_ID_PHILIPS_SAA7134: - case PCI_DEVICE_ID_PHILIPS_SAA7133: - case PCI_DEVICE_ID_PHILIPS_SAA7135: - saa7134_oss_fini(dev); - break; - } if (card_has_mpeg(dev)) saa7134_ts_fini(dev); saa7134_input_fini(dev); @@ -862,10 +804,9 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, struct saa7134_mpeg_ops *mops; int err; - dev = kmalloc(sizeof(*dev),GFP_KERNEL); + dev = kzalloc(sizeof(*dev),GFP_KERNEL); if (NULL == dev) return -ENOMEM; - memset(dev,0,sizeof(*dev)); /* pci init */ dev->pci = pci_dev; @@ -986,11 +927,12 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, if (card_is_dvb(dev)) request_module_depend("saa7134-dvb",&need_dvb); - if (!oss && alsa) { - dprintk("Requesting ALSA module\n"); + + if (alsa) request_module_depend("saa7134-alsa",&need_alsa); - } + if (oss) + request_module_depend("saa7134-oss",&need_oss); v4l2_prio_init(&dev->prio); @@ -1024,57 +966,27 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, dev->name,dev->radio_dev->minor & 0x1f); } - /* register oss devices */ - switch (dev->pci->device) { - case PCI_DEVICE_ID_PHILIPS_SAA7134: - case PCI_DEVICE_ID_PHILIPS_SAA7133: - case PCI_DEVICE_ID_PHILIPS_SAA7135: - if (oss) { - err = dev->dmasound.minor_dsp = - register_sound_dsp(&saa7134_dsp_fops, - dsp_nr[dev->nr]); - if (err < 0) { - goto fail4; - } - printk(KERN_INFO "%s: registered device dsp%d\n", - dev->name,dev->dmasound.minor_dsp >> 4); - - err = dev->dmasound.minor_mixer = - register_sound_mixer(&saa7134_mixer_fops, - mixer_nr[dev->nr]); - if (err < 0) - goto fail5; - printk(KERN_INFO "%s: registered device mixer%d\n", - dev->name,dev->dmasound.minor_mixer >> 4); - } - break; - } - /* everything worked */ pci_set_drvdata(pci_dev,dev); saa7134_devcount++; - down(&devlist_lock); + mutex_lock(&devlist_lock); list_for_each(item,&mops_list) { mops = list_entry(item, struct saa7134_mpeg_ops, next); mpeg_ops_attach(mops, dev); } list_add_tail(&dev->devlist,&saa7134_devlist); - up(&devlist_lock); + mutex_unlock(&devlist_lock); /* check for signal */ saa7134_irq_video_intl(dev); - return 0; - fail5: - switch (dev->pci->device) { - case PCI_DEVICE_ID_PHILIPS_SAA7134: - case PCI_DEVICE_ID_PHILIPS_SAA7133: - case PCI_DEVICE_ID_PHILIPS_SAA7135: - if (oss) - unregister_sound_dsp(dev->dmasound.minor_dsp); - break; + if (dmasound_init && !dev->dmasound.priv_data) { + dmasound_init(dev); } + + return 0; + fail4: saa7134_unregister_video(dev); saa7134_i2c_unregister(dev); @@ -1096,6 +1008,11 @@ static void __devexit saa7134_finidev(struct pci_dev *pci_dev) struct list_head *item; struct saa7134_mpeg_ops *mops; + /* Release DMA sound modules if present */ + if (dmasound_exit && dev->dmasound.priv_data) { + dmasound_exit(dev); + } + /* debugging ... */ if (irq_debug) { u32 report = saa_readl(SAA7134_IRQ_REPORT); @@ -1115,29 +1032,28 @@ static void __devexit saa7134_finidev(struct pci_dev *pci_dev) saa7134_hwfini(dev); /* unregister */ - down(&devlist_lock); + mutex_lock(&devlist_lock); list_del(&dev->devlist); list_for_each(item,&mops_list) { mops = list_entry(item, struct saa7134_mpeg_ops, next); mpeg_ops_detach(mops, dev); } - up(&devlist_lock); + mutex_unlock(&devlist_lock); saa7134_devcount--; saa7134_i2c_unregister(dev); - switch (dev->pci->device) { - case PCI_DEVICE_ID_PHILIPS_SAA7134: - case PCI_DEVICE_ID_PHILIPS_SAA7133: - case PCI_DEVICE_ID_PHILIPS_SAA7135: - if (oss) { - unregister_sound_mixer(dev->dmasound.minor_mixer); - unregister_sound_dsp(dev->dmasound.minor_dsp); - } - break; - } saa7134_unregister_video(dev); - /* release ressources */ + + /* the DMA sound modules should be unloaded before reaching + this, but just in case they are still present... */ + if (dev->dmasound.priv_data != NULL) { + free_irq(pci_dev->irq, &dev->dmasound); + dev->dmasound.priv_data = NULL; + } + + + /* release resources */ free_irq(pci_dev->irq, dev); iounmap(dev->lmmio); release_mem_region(pci_resource_start(pci_dev,0), @@ -1156,13 +1072,13 @@ int saa7134_ts_register(struct saa7134_mpeg_ops *ops) struct list_head *item; struct saa7134_dev *dev; - down(&devlist_lock); + mutex_lock(&devlist_lock); list_for_each(item,&saa7134_devlist) { dev = list_entry(item, struct saa7134_dev, devlist); mpeg_ops_attach(ops, dev); } list_add_tail(&ops->next,&mops_list); - up(&devlist_lock); + mutex_unlock(&devlist_lock); return 0; } @@ -1171,13 +1087,13 @@ void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops) struct list_head *item; struct saa7134_dev *dev; - down(&devlist_lock); + mutex_lock(&devlist_lock); list_del(&ops->next); list_for_each(item,&saa7134_devlist) { dev = list_entry(item, struct saa7134_dev, devlist); mpeg_ops_detach(ops, dev); } - up(&devlist_lock); + mutex_unlock(&devlist_lock); } EXPORT_SYMBOL(saa7134_ts_register); @@ -1203,15 +1119,15 @@ static int saa7134_init(void) printk(KERN_INFO "saa7130/34: snapshot date %04d-%02d-%02d\n", SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); #endif - return pci_module_init(&saa7134_pci_driver); + return pci_register_driver(&saa7134_pci_driver); } static void saa7134_fini(void) { -#ifdef CONFIG_MODULES +#if defined(CONFIG_MODULES) && defined(MODULE) if (pending_registered) unregister_module_notifier(&pending_notifier); -#endif +#endif /* CONFIG_MODULES */ pci_unregister_driver(&saa7134_pci_driver); } @@ -1220,13 +1136,14 @@ module_exit(saa7134_fini); /* ----------------------------------------------------------- */ -EXPORT_SYMBOL(saa7134_print_ioctl); EXPORT_SYMBOL(saa7134_i2c_call_clients); EXPORT_SYMBOL(saa7134_devlist); EXPORT_SYMBOL(saa7134_boards); -/* ----------------- For ALSA -------------------------------- */ +/* ----------------- for the DMA sound modules --------------- */ +EXPORT_SYMBOL(dmasound_init); +EXPORT_SYMBOL(dmasound_exit); EXPORT_SYMBOL(saa7134_pgtable_free); EXPORT_SYMBOL(saa7134_pgtable_build); EXPORT_SYMBOL(saa7134_pgtable_alloc); diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index e016480c346..399f9952596 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -31,6 +31,7 @@ #include "saa7134-reg.h" #include "saa7134.h" +#include <media/v4l2-common.h> #ifdef HAVE_MT352 # include "mt352.h" diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index e9ec69efb4c..bd4c389d4c3 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -29,6 +29,7 @@ #include "saa7134.h" #include <media/saa6752hs.h> +#include <media/v4l2-common.h> /* ------------------------------------------------------------------ */ @@ -36,6 +37,7 @@ MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; + module_param_array(empress_nr, int, NULL, 0444); MODULE_PARM_DESC(empress_nr,"ts device number"); @@ -162,7 +164,7 @@ static int ts_do_ioctl(struct inode *inode, struct file *file, struct saa7134_dev *dev = file->private_data; if (debug > 1) - saa7134_print_ioctl(dev->name,cmd); + v4l_print_ioctl(dev->name,cmd); switch (cmd) { case VIDIOC_QUERYCAP: { diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 7575043f087..6162550c413 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -30,6 +30,7 @@ #include "saa7134-reg.h" #include "saa7134.h" +#include <media/v4l2-common.h> /* ----------------------------------------------------------- */ @@ -333,7 +334,7 @@ static int attach_inform(struct i2c_client *client) struct tuner_setup tun_setup; d1printk( "%s i2c attach [addr=0x%x,client=%s]\n", - client->driver->name, client->addr, client->name); + client->driver->driver.name, client->addr, client->name); /* Am I an i2c remote control? */ @@ -343,7 +344,7 @@ static int attach_inform(struct i2c_client *client) { struct IR_i2c *ir = i2c_get_clientdata(client); d1printk("%s i2c IR detected (%s).\n", - client->driver->name,ir->phys); + client->driver->driver.name, ir->phys); saa7134_set_i2c_ir(dev,ir); break; } @@ -390,9 +391,7 @@ static struct i2c_algorithm saa7134_algo = { static struct i2c_adapter saa7134_adap_template = { .owner = THIS_MODULE, -#ifdef I2C_CLASS_TV_ANALOG .class = I2C_CLASS_TV_ANALOG, -#endif .name = "saa7134", .id = I2C_HW_SAA7134, .algo = &saa7134_algo, diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 329accda6d4..82d28cbf289 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -56,23 +56,23 @@ static IR_KEYTAB_TYPE flyvideo_codes[IR_KEYTAB_SIZE] = { [ 12 ] = KEY_KP8, [ 13 ] = KEY_KP9, - [ 14 ] = KEY_TUNER, // Air/Cable + [ 14 ] = KEY_MODE, // Air/Cable [ 17 ] = KEY_VIDEO, // Video [ 21 ] = KEY_AUDIO, // Audio - [ 0 ] = KEY_POWER, // Pover + [ 0 ] = KEY_POWER, // Power + [ 24 ] = KEY_TUNER, // AV Source [ 2 ] = KEY_ZOOM, // Fullscreen + [ 26 ] = KEY_LANGUAGE, // Stereo [ 27 ] = KEY_MUTE, // Mute - [ 20 ] = KEY_VOLUMEUP, - [ 23 ] = KEY_VOLUMEDOWN, + [ 20 ] = KEY_VOLUMEUP, // Volume + + [ 23 ] = KEY_VOLUMEDOWN, // Volume - [ 18 ] = KEY_CHANNELUP, // Channel + [ 19 ] = KEY_CHANNELDOWN, // Channel - - [ 6 ] = KEY_AGAIN, // Recal - [ 16 ] = KEY_KPENTER, // Enter - - [ 26 ] = KEY_F22, // Stereo - [ 24 ] = KEY_EDIT, // AV Source + [ 6 ] = KEY_AGAIN, // Recall + [ 16 ] = KEY_ENTER, // Enter }; + static IR_KEYTAB_TYPE cinergy_codes[IR_KEYTAB_SIZE] = { [ 0 ] = KEY_KP0, [ 1 ] = KEY_KP1, @@ -485,64 +485,6 @@ static IR_KEYTAB_TYPE ir_codes_purpletv[IR_KEYTAB_SIZE] = { }; -static IR_KEYTAB_TYPE ir_codes_pinnacle[IR_KEYTAB_SIZE] = { - [ 0x59 ] = KEY_MUTE, - [ 0x4a ] = KEY_POWER, - - [ 0x18 ] = KEY_TEXT, - [ 0x26 ] = KEY_TV, - [ 0x3d ] = KEY_PRINT, - - [ 0x48 ] = KEY_RED, - [ 0x04 ] = KEY_GREEN, - [ 0x11 ] = KEY_YELLOW, - [ 0x00 ] = KEY_BLUE, - - [ 0x2d ] = KEY_VOLUMEUP, - [ 0x1e ] = KEY_VOLUMEDOWN, - - [ 0x49 ] = KEY_MENU, - - [ 0x16 ] = KEY_CHANNELUP, - [ 0x17 ] = KEY_CHANNELDOWN, - - [ 0x20 ] = KEY_UP, - [ 0x21 ] = KEY_DOWN, - [ 0x22 ] = KEY_LEFT, - [ 0x23 ] = KEY_RIGHT, - [ 0x0d ] = KEY_SELECT, - - - - [ 0x08 ] = KEY_BACK, - [ 0x07 ] = KEY_REFRESH, - - [ 0x2f ] = KEY_ZOOM, - [ 0x29 ] = KEY_RECORD, - - [ 0x4b ] = KEY_PAUSE, - [ 0x4d ] = KEY_REWIND, - [ 0x2e ] = KEY_PLAY, - [ 0x4e ] = KEY_FORWARD, - [ 0x53 ] = KEY_PREVIOUS, - [ 0x4c ] = KEY_STOP, - [ 0x54 ] = KEY_NEXT, - - [ 0x69 ] = KEY_KP0, - [ 0x6a ] = KEY_KP1, - [ 0x6b ] = KEY_KP2, - [ 0x6c ] = KEY_KP3, - [ 0x6d ] = KEY_KP4, - [ 0x6e ] = KEY_KP5, - [ 0x6f ] = KEY_KP6, - [ 0x70 ] = KEY_KP7, - [ 0x71 ] = KEY_KP8, - [ 0x72 ] = KEY_KP9, - - [ 0x74 ] = KEY_CHANNEL, - [ 0x0a ] = KEY_BACKSPACE, -}; - /* Mapping for the 28 key remote control as seen at http://www.sednacomputer.com/photo/cardbus-tv.jpg Pavel Mihaylov <bin@bash.info> */ @@ -601,12 +543,22 @@ static int build_key(struct saa7134_dev *dev) dprintk("build_key gpio=0x%x mask=0x%x data=%d\n", gpio, ir->mask_keycode, data); - if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || - (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - ir_input_keydown(ir->dev, &ir->ir, data, data); - } else { - ir_input_nokey(ir->dev, &ir->ir); + if (ir->polling) { + if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || + (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { + ir_input_keydown(ir->dev, &ir->ir, data, data); + } else { + ir_input_nokey(ir->dev, &ir->ir); + } } + else { /* IRQ driven mode - handle key press and release in one go */ + if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || + (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { + ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_nokey(ir->dev, &ir->ir); + } + } + return 0; } @@ -635,57 +587,6 @@ static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -/* The new pinnacle PCTV remote (with the colored buttons) - * - * Ricardo Cerqueira <v4l@cerqueira.org> - */ - -static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - unsigned char b[4]; - unsigned int start = 0,parity = 0,code = 0; - - /* poll IR chip */ - if (4 != i2c_master_recv(&ir->c,b,4)) { - i2cdprintk("read error\n"); - return -EIO; - } - - for (start = 0; start<4; start++) { - if (b[start] == 0x80) { - code=b[(start+3)%4]; - parity=b[(start+2)%4]; - } - } - - /* Empty Request */ - if (parity==0) - return 0; - - /* Repeating... */ - if (ir->old == parity) - return 0; - - - ir->old = parity; - - /* Reduce code value to fit inside IR_KEYTAB_SIZE - * - * this is the only value that results in 42 unique - * codes < 128 - */ - - code %= 0x88; - - *ir_raw = code; - *ir_key = code; - - i2cdprintk("Pinnacle PCTV key %02x\n", code); - - return 1; -} - - void saa7134_input_irq(struct saa7134_dev *dev) { struct saa7134_ir *ir = dev->remote; @@ -795,6 +696,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) polling = 50; // ms break; case SAA7134_BOARD_VIDEOMATE_TV_PVR: + case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII: ir_codes = videomate_tv_pvr_codes; mask_keycode = 0x00003F; @@ -822,6 +724,8 @@ int saa7134_input_init1(struct saa7134_dev *dev) return -ENOMEM; } + ir->dev = input_dev; + /* init hardware-specific stuff */ ir->mask_keycode = mask_keycode; ir->mask_keydown = mask_keydown; diff --git a/drivers/media/video/saa7134/saa7134-oss.c b/drivers/media/video/saa7134/saa7134-oss.c index fd53dfcc164..7448e386a80 100644 --- a/drivers/media/video/saa7134/saa7134-oss.c +++ b/drivers/media/video/saa7134/saa7134-oss.c @@ -4,6 +4,8 @@ * oss dsp interface * * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] + * 2005 conversion to standalone module: + * Ricardo Cerqueira <v4l@cerqueira.org> * * 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 @@ -25,7 +27,9 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> +#include <linux/interrupt.h> #include <linux/slab.h> +#include <linux/sound.h> #include <linux/soundcard.h> #include "saa7134-reg.h" @@ -33,15 +37,23 @@ /* ------------------------------------------------------------------ */ -static unsigned int oss_debug = 0; -module_param(oss_debug, int, 0644); -MODULE_PARM_DESC(oss_debug,"enable debug messages [oss]"); +static unsigned int debug = 0; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug,"enable debug messages [oss]"); -static unsigned int oss_rate = 0; -module_param(oss_rate, int, 0444); -MODULE_PARM_DESC(oss_rate,"sample rate (valid are: 32000,48000)"); +static unsigned int rate = 0; +module_param(rate, int, 0444); +MODULE_PARM_DESC(rate,"sample rate (valid are: 32000,48000)"); -#define dprintk(fmt, arg...) if (oss_debug) \ +static unsigned int dsp_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; +MODULE_PARM_DESC(dsp_nr, "device numbers for SAA7134 capture interface(s)."); +module_param_array(dsp_nr, int, NULL, 0444); + +static unsigned int mixer_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; +MODULE_PARM_DESC(mixer_nr, "mixer numbers for SAA7134 capture interface(s)."); +module_param_array(mixer_nr, int, NULL, 0444); + +#define dprintk(fmt, arg...) if (debug) \ printk(KERN_DEBUG "%s/oss: " fmt, dev->name , ## arg) @@ -361,6 +373,42 @@ static ssize_t dsp_write(struct file *file, const char __user *buffer, return -EINVAL; } +static const char *osspcm_ioctls[] = { + "RESET", "SYNC", "SPEED", "STEREO", "GETBLKSIZE", "SETFMT", + "CHANNELS", "?", "POST", "SUBDIVIDE", "SETFRAGMENT", "GETFMTS", + "GETOSPACE", "GETISPACE", "NONBLOCK", "GETCAPS", "GET/SETTRIGGER", + "GETIPTR", "GETOPTR", "MAPINBUF", "MAPOUTBUF", "SETSYNCRO", + "SETDUPLEX", "GETODELAY" +}; +#define OSSPCM_IOCTLS ARRAY_SIZE(osspcm_ioctls) + +static void saa7134_oss_print_ioctl(char *name, unsigned int cmd) +{ + char *dir; + + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: dir = "--"; break; + case _IOC_READ: dir = "r-"; break; + case _IOC_WRITE: dir = "-w"; break; + case _IOC_READ | _IOC_WRITE: dir = "rw"; break; + default: dir = "??"; break; + } + switch (_IOC_TYPE(cmd)) { + case 'P': + printk(KERN_DEBUG "%s: ioctl 0x%08x (oss dsp, %s, SNDCTL_DSP_%s)\n", + name, cmd, dir, (_IOC_NR(cmd) < OSSPCM_IOCTLS) ? + osspcm_ioctls[_IOC_NR(cmd)] : "???"); + break; + case 'M': + printk(KERN_DEBUG "%s: ioctl 0x%08x (oss mixer, %s, #%d)\n", + name, cmd, dir, _IOC_NR(cmd)); + break; + default: + printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n", + name, cmd, dir, _IOC_NR(cmd)); + } +} + static int dsp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -369,8 +417,8 @@ static int dsp_ioctl(struct inode *inode, struct file *file, int __user *p = argp; int val = 0; - if (oss_debug > 1) - saa7134_print_ioctl(dev->name,cmd); + if (debug > 1) + saa7134_oss_print_ioctl(dev->name,cmd); switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, p); @@ -665,8 +713,8 @@ static int mixer_ioctl(struct inode *inode, struct file *file, void __user *argp = (void __user *) arg; int __user *p = argp; - if (oss_debug > 1) - saa7134_print_ioctl(dev->name,cmd); + if (debug > 1) + saa7134_oss_print_ioctl(dev->name,cmd); switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, p); @@ -768,8 +816,41 @@ struct file_operations saa7134_mixer_fops = { /* ------------------------------------------------------------------ */ +static irqreturn_t saa7134_oss_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct saa7134_dmasound *dmasound = dev_id; + struct saa7134_dev *dev = dmasound->priv_data; + unsigned long report, status; + int loop, handled = 0; + + for (loop = 0; loop < 10; loop++) { + report = saa_readl(SAA7134_IRQ_REPORT); + status = saa_readl(SAA7134_IRQ_STATUS); + + if (report & SAA7134_IRQ_REPORT_DONE_RA3) { + handled = 1; + saa_writel(SAA7134_IRQ_REPORT,report); + saa7134_irq_oss_done(dev, status); + } else { + goto out; + } + } + + if (loop == 10) { + dprintk("error! looping IRQ!"); + } +out: + return IRQ_RETVAL(handled); +} + int saa7134_oss_init1(struct saa7134_dev *dev) { + + if ((request_irq(dev->pci->irq, saa7134_oss_irq, + SA_SHIRQ | SA_INTERRUPT, dev->name, + (void*) &dev->dmasound)) < 0) + return -1; + /* general */ init_MUTEX(&dev->dmasound.lock); init_waitqueue_head(&dev->dmasound.wq); @@ -785,8 +866,8 @@ int saa7134_oss_init1(struct saa7134_dev *dev) /* dsp */ dev->dmasound.rate = 32000; - if (oss_rate) - dev->dmasound.rate = oss_rate; + if (rate) + dev->dmasound.rate = rate; dev->dmasound.rate = (dev->dmasound.rate > 40000) ? 48000 : 32000; /* mixer */ @@ -840,7 +921,7 @@ void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status) /* next block addr */ next_blk = (dev->dmasound.dma_blk + 2) % dev->dmasound.blocks; saa_writel(reg,next_blk * dev->dmasound.blksize); - if (oss_debug > 2) + if (debug > 2) dprintk("irq: ok, %s, next_blk=%d, addr=%x\n", (status & 0x10000000) ? "even" : "odd ", next_blk, next_blk * dev->dmasound.blksize); @@ -854,6 +935,124 @@ void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status) spin_unlock(&dev->slock); } +static int saa7134_dsp_create(struct saa7134_dev *dev) +{ + int err; + + err = dev->dmasound.minor_dsp = + register_sound_dsp(&saa7134_dsp_fops, + dsp_nr[dev->nr]); + if (err < 0) { + goto fail; + } + printk(KERN_INFO "%s: registered device dsp%d\n", + dev->name,dev->dmasound.minor_dsp >> 4); + + err = dev->dmasound.minor_mixer = + register_sound_mixer(&saa7134_mixer_fops, + mixer_nr[dev->nr]); + if (err < 0) + goto fail; + printk(KERN_INFO "%s: registered device mixer%d\n", + dev->name,dev->dmasound.minor_mixer >> 4); + + return 0; + +fail: + unregister_sound_dsp(dev->dmasound.minor_dsp); + return 0; + + +} + +static int oss_device_init(struct saa7134_dev *dev) +{ + dev->dmasound.priv_data = dev; + saa7134_oss_init1(dev); + saa7134_dsp_create(dev); + return 1; +} + +static int oss_device_exit(struct saa7134_dev *dev) +{ + + unregister_sound_mixer(dev->dmasound.minor_mixer); + unregister_sound_dsp(dev->dmasound.minor_dsp); + + saa7134_oss_fini(dev); + + if (dev->pci->irq > 0) { + synchronize_irq(dev->pci->irq); + free_irq(dev->pci->irq,&dev->dmasound); + } + + dev->dmasound.priv_data = NULL; + return 1; +} + +static int saa7134_oss_init(void) +{ + struct saa7134_dev *dev = NULL; + struct list_head *list; + + if (!dmasound_init && !dmasound_exit) { + dmasound_init = oss_device_init; + dmasound_exit = oss_device_exit; + } else { + printk(KERN_WARNING "saa7134 OSS: can't load, DMA sound handler already assigned (probably to ALSA)\n"); + return -EBUSY; + } + + printk(KERN_INFO "saa7134 OSS driver for DMA sound loaded\n"); + + + list_for_each(list,&saa7134_devlist) { + dev = list_entry(list, struct saa7134_dev, devlist); + if (dev->dmasound.priv_data == NULL) { + oss_device_init(dev); + } else { + printk(KERN_ERR "saa7134 OSS: DMA sound is being handled by ALSA, ignoring %s\n",dev->name); + return -EBUSY; + } + } + + if (dev == NULL) + printk(KERN_INFO "saa7134 OSS: no saa7134 cards found\n"); + + return 0; + +} + +static void saa7134_oss_exit(void) +{ + struct saa7134_dev *dev = NULL; + struct list_head *list; + + list_for_each(list,&saa7134_devlist) { + dev = list_entry(list, struct saa7134_dev, devlist); + + /* Device isn't registered by OSS, probably ALSA's */ + if (!dev->dmasound.minor_dsp) + continue; + + oss_device_exit(dev); + + } + + dmasound_init = NULL; + dmasound_exit = NULL; + + printk(KERN_INFO "saa7134 OSS driver for DMA sound unloaded\n"); + + return; +} + +/* We initialize this late, to make sure the sound system is up and running */ +late_initcall(saa7134_oss_init); +module_exit(saa7134_oss_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); + /* ----------------------------------------------------------- */ /* * Local variables: diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c index 93268427750..afa4dcb3f96 100644 --- a/drivers/media/video/saa7134/saa7134-tvaudio.c +++ b/drivers/media/video/saa7134/saa7134-tvaudio.c @@ -180,8 +180,8 @@ static void tvaudio_init(struct saa7134_dev *dev) saa_writeb(SAA7134_AUDIO_CLOCK0, clock & 0xff); saa_writeb(SAA7134_AUDIO_CLOCK1, (clock >> 8) & 0xff); saa_writeb(SAA7134_AUDIO_CLOCK2, (clock >> 16) & 0xff); - // frame locked audio was reported not to be reliable - saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x02); + /* frame locked audio is mandatory for NICAM */ + saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x01); saa_writeb(SAA7134_NICAM_ERROR_LOW, 0x14); saa_writeb(SAA7134_NICAM_ERROR_HIGH, 0x50); @@ -809,7 +809,12 @@ static int tvaudio_thread_ddep(void *data) dprintk("ddep override: %s\n",stdres[audio_ddep]); } else if (&card(dev).radio == dev->input) { dprintk("FM Radio\n"); - norms = (0x0f << 2) | 0x01; + if (dev->tuner_type == TUNER_PHILIPS_TDA8290) { + norms = (0x11 << 2) | 0x01; + saa_dsp_writel(dev, 0x42c >> 2, 0x729555); + } else { + norms = (0x0f << 2) | 0x01; + } } else { /* (let chip) scan for sound carrier */ norms = 0; diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 45c852df13e..e97426bc85d 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -29,6 +29,7 @@ #include "saa7134-reg.h" #include "saa7134.h" +#include <media/v4l2-common.h> /* Include V4L1 specific functions. Should be removed soon */ #include <linux/videodev.h> @@ -1263,10 +1264,9 @@ static int video_open(struct inode *inode, struct file *file) v4l2_type_names[type]); /* allocate + initialize per filehandle data */ - fh = kmalloc(sizeof(*fh),GFP_KERNEL); + fh = kzalloc(sizeof(*fh),GFP_KERNEL); if (NULL == fh) return -ENOMEM; - memset(fh,0,sizeof(*fh)); file->private_data = fh; fh->dev = dev; fh->radio = radio; @@ -1689,7 +1689,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, int err; if (video_debug > 1) - saa7134_print_ioctl(dev->name,cmd); + v4l_print_ioctl(dev->name,cmd); switch (cmd) { case VIDIOC_S_CTRL: @@ -2142,7 +2142,7 @@ static int radio_do_ioctl(struct inode *inode, struct file *file, struct saa7134_dev *dev = fh->dev; if (video_debug > 1) - saa7134_print_ioctl(dev->name,cmd); + v4l_print_ioctl(dev->name,cmd); switch (cmd) { case VIDIOC_QUERYCAP: { @@ -2262,6 +2262,7 @@ static struct file_operations video_fops = .poll = video_poll, .mmap = video_mmap, .ioctl = video_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -2271,6 +2272,7 @@ static struct file_operations radio_fops = .open = video_open, .release = video_release, .ioctl = radio_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index fb972747166..e70eae8d29b 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -37,6 +37,9 @@ #include <media/ir-common.h> #include <media/ir-kbd-i2c.h> #include <media/video-buf.h> +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/pcm.h> #include <media/video-buf-dvb.h> #ifndef TRUE @@ -47,10 +50,6 @@ #endif #define UNSET (-1U) -#include <sound/driver.h> -#include <sound/core.h> -#include <sound/pcm.h> - /* ----------------------------------------------------------- */ /* enums */ @@ -208,6 +207,9 @@ struct saa7134_format { #define SAA7134_BOARD_SEDNA_PC_TV_CARDBUS 79 #define SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV 80 #define SAA7134_BOARD_PHILIPS_TIGER 81 +#define SAA7134_BOARD_MSI_TVATANYWHERE_PLUS 82 +#define SAA7134_BOARD_CINERGY250PCI 83 +#define SAA7134_BOARD_FLYDVB_TRIO 84 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 @@ -383,6 +385,7 @@ struct saa7134_dmasound { unsigned int dma_blk; unsigned int read_offset; unsigned int read_count; + void * priv_data; snd_pcm_substream_t *substream; }; @@ -544,7 +547,6 @@ struct saa7134_dev { extern struct list_head saa7134_devlist; -void saa7134_print_ioctl(char *name, unsigned int cmd); void saa7134_track_gpio(struct saa7134_dev *dev, char *msg); #define SAA7134_PGTABLE_SIZE 4096 @@ -569,6 +571,10 @@ void saa7134_dma_free(struct saa7134_dev *dev,struct saa7134_buf *buf); int saa7134_set_dmabits(struct saa7134_dev *dev); +extern int (*dmasound_init)(struct saa7134_dev *dev); +extern int (*dmasound_exit)(struct saa7134_dev *dev); + + /* ----------------------------------------------------------- */ /* saa7134-cards.c */ diff --git a/drivers/media/video/saa7146.h b/drivers/media/video/saa7146.h index f305ec802ea..756963f01bb 100644 --- a/drivers/media/video/saa7146.h +++ b/drivers/media/video/saa7146.h @@ -73,7 +73,6 @@ struct saa7146 unsigned int nr; unsigned long irq; /* IRQ used by SAA7146 card */ unsigned short id; - struct pci_dev *dev; unsigned char revision; unsigned char boardcfg[64]; /* 64 bytes of config from eeprom */ unsigned long saa7146_adr; /* bus address of IO mem from PCI BIOS */ diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c index 132aa7943c1..3ed0edb870a 100644 --- a/drivers/media/video/saa7185.c +++ b/drivers/media/video/saa7185.c @@ -408,22 +408,19 @@ saa7185_detect_client (struct i2c_adapter *adapter, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_saa7185; - client->flags = I2C_CLIENT_ALLOW_USE; strlcpy(I2C_NAME(client), "saa7185", sizeof(I2C_NAME(client))); - encoder = kmalloc(sizeof(struct saa7185), GFP_KERNEL); + encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL); if (encoder == NULL) { kfree(client); return -ENOMEM; } - memset(encoder, 0, sizeof(struct saa7185)); encoder->norm = VIDEO_MODE_NTSC; encoder->enable = 1; i2c_set_clientdata(client, encoder); @@ -487,11 +484,11 @@ saa7185_detach_client (struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_driver i2c_driver_saa7185 = { - .owner = THIS_MODULE, - .name = "saa7185", /* name */ + .driver = { + .name = "saa7185", /* name */ + }, .id = I2C_DRIVERID_SAA7185B, - .flags = I2C_DF_NOTIFY, .attach_adapter = saa7185_attach_adapter, .detach_client = saa7185_detach_client, diff --git a/drivers/media/video/saa7191.c b/drivers/media/video/saa7191.c index cbca896e8cf..746cadb8f1c 100644 --- a/drivers/media/video/saa7191.c +++ b/drivers/media/video/saa7191.c @@ -571,18 +571,15 @@ static int saa7191_attach(struct i2c_adapter *adap, int addr, int kind) printk(KERN_INFO "Philips SAA7191 driver version %s\n", SAA7191_MODULE_VERSION); - client = kmalloc(sizeof(*client), GFP_KERNEL); + client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) return -ENOMEM; - decoder = kmalloc(sizeof(*decoder), GFP_KERNEL); + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); if (!decoder) { err = -ENOMEM; goto out_free_client; } - memset(client, 0, sizeof(struct i2c_client)); - memset(decoder, 0, sizeof(struct saa7191)); - client->addr = addr; client->adapter = adap; client->driver = &i2c_driver_saa7191; @@ -788,10 +785,10 @@ static int saa7191_command(struct i2c_client *client, unsigned int cmd, } static struct i2c_driver i2c_driver_saa7191 = { - .owner = THIS_MODULE, - .name = "saa7191", + .driver = { + .name = "saa7191", + }, .id = I2C_DRIVERID_SAA7191, - .flags = I2C_DF_NOTIFY, .attach_adapter = saa7191_probe, .detach_client = saa7191_detach, .command = saa7191_command diff --git a/drivers/media/video/stradis.c b/drivers/media/video/stradis.c index d4497dbae05..54fc33011ff 100644 --- a/drivers/media/video/stradis.c +++ b/drivers/media/video/stradis.c @@ -49,9 +49,9 @@ #include "saa7121.h" #include "cs8420.h" -#define DEBUG(x) /* debug driver */ -#undef IDEBUG /* debug irq handler */ -#undef MDEBUG /* debug memory management */ +#define DEBUG(x) /* debug driver */ +#undef IDEBUG /* debug irq handler */ +#undef MDEBUG /* debug memory management */ #define SAA7146_MAX 6 @@ -63,7 +63,6 @@ static int video_nr = -1; module_param(video_nr, int, 0); MODULE_LICENSE("GPL"); - #define nDebNormal 0x00480000 #define nDebNoInc 0x00480000 #define nDebVideo 0xd0480000 @@ -99,7 +98,12 @@ MODULE_LICENSE("GPL"); #ifdef USE_RESCUE_EEPROM_SDM275 static unsigned char rescue_eeprom[64] = { -0x00,0x01,0x04,0x13,0x26,0x0f,0x10,0x00,0x00,0x00,0x43,0x63,0x22,0x01,0x29,0x15,0x73,0x00,0x1f, 'd', 'e', 'c', 'x', 'l', 'd', 'v', 'a',0x02,0x00,0x01,0x00,0xcc,0xa4,0x63,0x09,0xe2,0x10,0x00,0x0a,0x00,0x02,0x02, 'd', 'e', 'c', 'x', 'l', 'a',0x00,0x00,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00, 0x01, 0x04, 0x13, 0x26, 0x0f, 0x10, 0x00, 0x00, 0x00, 0x43, 0x63, + 0x22, 0x01, 0x29, 0x15, 0x73, 0x00, 0x1f, 'd', 'e', 'c', 'x', 'l', + 'd', 'v', 'a', 0x02, 0x00, 0x01, 0x00, 0xcc, 0xa4, 0x63, 0x09, 0xe2, + 0x10, 0x00, 0x0a, 0x00, 0x02, 0x02, 'd', 'e', 'c', 'x', 'l', 'a', + 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, }; #endif @@ -140,17 +144,18 @@ static int I2CRead(struct saa7146 *saa, unsigned char addr, if (saaread(SAA7146_I2C_STATUS) & 0x3c) I2CWipe(saa); - for (i = 0; i < 1000 && - (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); i++) + for (i = 0; + i < 1000 && (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_BUSY); + i++) schedule(); if (i == 1000) I2CWipe(saa); if (dosub) saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 8) | - ((subaddr & 0xff) << 16) | 0xed, SAA7146_I2C_TRANSFER); + ((subaddr & 0xff) << 16) | 0xed, SAA7146_I2C_TRANSFER); else saawrite(((addr & 0xfe) << 24) | (((addr | 1) & 0xff) << 16) | - 0xf1, SAA7146_I2C_TRANSFER); + 0xf1, SAA7146_I2C_TRANSFER); saawrite((SAA7146_MC2_UPLD_I2C << 16) | SAA7146_MC2_UPLD_I2C, SAA7146_MC2); /* wait for i2c registers to be programmed */ @@ -163,7 +168,7 @@ static int I2CRead(struct saa7146 *saa, unsigned char addr, schedule(); if (saaread(SAA7146_I2C_STATUS) & SAA7146_I2C_ERR) return -1; - if (i == 1000) + if (i == 1000) printk("i2c setup read timeout\n"); saawrite(0x41, SAA7146_I2C_TRANSFER); saawrite((SAA7146_MC2_UPLD_I2C << 16) | @@ -178,7 +183,7 @@ static int I2CRead(struct saa7146 *saa, unsigned char addr, schedule(); if (saaread(SAA7146_I2C_TRANSFER) & SAA7146_I2C_ERR) return -1; - if (i == 1000) + if (i == 1000) printk("i2c read timeout\n"); return ((saaread(SAA7146_I2C_TRANSFER) >> 24) & 0xff); } @@ -213,20 +218,22 @@ static void attach_inform(struct saa7146 *saa, int id) { int i; - DEBUG(printk(KERN_DEBUG "stradis%d: i2c: device found=%02x\n", saa->nr, id)); - if (id == 0xa0) { /* we have rev2 or later board, fill in info */ + DEBUG(printk(KERN_DEBUG "stradis%d: i2c: device found=%02x\n", saa->nr, + id)); + if (id == 0xa0) { /* we have rev2 or later board, fill in info */ for (i = 0; i < 64; i++) saa->boardcfg[i] = I2CRead(saa, 0xa0, i, 1); #ifdef USE_RESCUE_EEPROM_SDM275 if (saa->boardcfg[0] != 0) { - printk("stradis%d: WARNING: EEPROM STORED VALUES HAVE BEEN IGNORED\n", saa->nr); + printk("stradis%d: WARNING: EEPROM STORED VALUES HAVE " + "BEEN IGNORED\n", saa->nr); for (i = 0; i < 64; i++) saa->boardcfg[i] = rescue_eeprom[i]; } #endif printk("stradis%d: config =", saa->nr); for (i = 0; i < 51; i++) { - printk(" %02x",saa->boardcfg[i]); + printk(" %02x", saa->boardcfg[i]); } printk("\n"); } @@ -254,17 +261,19 @@ static int wait_for_debi_done(struct saa7146 *saa) for (i = 0; i < 500000 && (saaread(SAA7146_PSR) & SAA7146_PSR_DEBI_S); i++) saaread(SAA7146_MC2); + if (i > debiwait_maxwait) printk("wait-for-debi-done maxwait: %d\n", debiwait_maxwait = i); - + if (i == 500000) return -1; + return 0; } static int debiwrite(struct saa7146 *saa, u32 config, int addr, - u32 val, int count) + u32 val, int count) { u32 cmd; if (count <= 0 || count > 32764) @@ -309,41 +318,6 @@ static u32 debiread(struct saa7146 *saa, u32 config, int addr, int count) return result; } -#if 0 /* unused */ -/* MUST be a multiple of 8 bytes and 8-byte aligned and < 32768 bytes */ -/* data copied into saa->dmadebi buffer, caller must re-enable interrupts */ -static void ibm_block_dram_read(struct saa7146 *saa, int address, int bytes) -{ - int i, j; - u32 *buf; - buf = (u32 *) saa->dmadebi; - if (bytes > 0x7000) - bytes = 0x7000; - saawrite(0, SAA7146_IER); /* disable interrupts */ - for (i=0; i < 10000 && - (debiread(saa, debNormal, IBM_MP2_DRAM_CMD_STAT, 2) - & 0x8000); i++) - saaread(SAA7146_MC2); - if (i == 10000) - printk(KERN_ERR "stradis%d: dram_busy never cleared\n", - saa->nr); - debiwrite(saa, debNormal, IBM_MP2_SRC_ADDR, (address<<16) | - (address>>16), 4); - debiwrite(saa, debNormal, IBM_MP2_BLOCK_SIZE, bytes, 2); - debiwrite(saa, debNormal, IBM_MP2_CMD_STAT, 0x8a10, 2); - for (j = 0; j < bytes/4; j++) { - for (i = 0; i < 10000 && - (!(debiread(saa, debNormal, IBM_MP2_DRAM_CMD_STAT, 2) - & 0x4000)); i++) - saaread(SAA7146_MC2); - if (i == 10000) - printk(KERN_ERR "stradis%d: dram_ready never set\n", - saa->nr); - buf[j] = debiread(saa, debNormal, IBM_MP2_DRAM_DATA, 4); - } -} -#endif /* unused */ - static void do_irq_send_data(struct saa7146 *saa) { int split, audbytes, vidbytes; @@ -365,16 +339,15 @@ static void do_irq_send_data(struct saa7146 *saa) return; } /* if at least 1 block audio waiting and audio fifo isn't full */ - if (audbytes >= 2048 && (debiread(saa, debNormal, - IBM_MP2_AUD_FIFO, 2) & 0xff) < 60) { + if (audbytes >= 2048 && (debiread(saa, debNormal, IBM_MP2_AUD_FIFO, 2) + & 0xff) < 60) { if (saa->audhead > saa->audtail) split = 65536 - saa->audhead; else split = 0; audbytes = 2048; if (split > 0 && split < 2048) { - memcpy(saa->dmadebi, saa->audbuf + saa->audhead, - split); + memcpy(saa->dmadebi, saa->audbuf + saa->audhead, split); saa->audhead = 0; audbytes -= split; } else @@ -383,20 +356,19 @@ static void do_irq_send_data(struct saa7146 *saa) audbytes); saa->audhead += audbytes; saa->audhead &= 0xffff; - debiwrite(saa, debAudio, (NewCard? IBM_MP2_AUD_FIFO : - IBM_MP2_AUD_FIFOW), 0, 2048); + debiwrite(saa, debAudio, (NewCard ? IBM_MP2_AUD_FIFO : + IBM_MP2_AUD_FIFOW), 0, 2048); wake_up_interruptible(&saa->audq); - /* if at least 1 block video waiting and video fifo isn't full */ + /* if at least 1 block video waiting and video fifo isn't full */ } else if (vidbytes >= 30720 && (debiread(saa, debNormal, - IBM_MP2_FIFO, 2)) < 16384) { + IBM_MP2_FIFO, 2)) < 16384) { if (saa->vidhead > saa->vidtail) split = 524288 - saa->vidhead; else split = 0; vidbytes = 30720; if (split > 0 && split < 30720) { - memcpy(saa->dmadebi, saa->vidbuf + saa->vidhead, - split); + memcpy(saa->dmadebi, saa->vidbuf + saa->vidhead, split); saa->vidhead = 0; vidbytes -= split; } else @@ -406,7 +378,7 @@ static void do_irq_send_data(struct saa7146 *saa) saa->vidhead += vidbytes; saa->vidhead &= 0x7ffff; debiwrite(saa, debVideo, (NewCard ? IBM_MP2_FIFO : - IBM_MP2_FIFOW), 0, 30720); + IBM_MP2_FIFOW), 0, 30720); wake_up_interruptible(&saa->vidq); } saawrite(SAA7146_PSR_DEBI_S | SAA7146_PSR_PIN1, SAA7146_IER); @@ -418,10 +390,10 @@ static void send_osd_data(struct saa7146 *saa) if (size > 30720) size = 30720; /* ensure some multiple of 8 bytes is transferred */ - size = 8 * ((size + 8)>>3); + size = 8 * ((size + 8) >> 3); if (size) { debiwrite(saa, debNormal, IBM_MP2_OSD_ADDR, - (saa->osdhead>>3), 2); + (saa->osdhead >> 3), 2); memcpy(saa->dmadebi, &saa->osdbuf[saa->osdhead], size); saa->osdhead += size; /* block transfer of next 8 bytes to ~32k bytes */ @@ -435,7 +407,7 @@ static void send_osd_data(struct saa7146 *saa) static irqreturn_t saa7146_irq(int irq, void *dev_id, struct pt_regs *regs) { - struct saa7146 *saa = (struct saa7146 *) dev_id; + struct saa7146 *saa = dev_id; u32 stat, astat; int count; int handled = 0; @@ -484,7 +456,7 @@ static irqreturn_t saa7146_irq(int irq, void *dev_id, struct pt_regs *regs) saa->vidinfo.v_size = 480; #if 0 if (saa->endmarkhead != saa->endmarktail) { - saa->audhead = + saa->audhead = saa->endmark[saa->endmarkhead]; saa->endmarkhead++; if (saa->endmarkhead >= MAX_MARKS) @@ -494,7 +466,7 @@ static irqreturn_t saa7146_irq(int irq, void *dev_id, struct pt_regs *regs) } if (istat & 0x4000) { /* Sequence Error Code */ if (saa->endmarkhead != saa->endmarktail) { - saa->audhead = + saa->audhead = saa->endmark[saa->endmarkhead]; saa->endmarkhead++; if (saa->endmarkhead >= MAX_MARKS) @@ -613,7 +585,7 @@ static int ibm_send_command(struct saa7146 *saa, int i; if (chain) - debiwrite(saa, debNormal, IBM_MP2_COMMAND, (command << 1) | 1, 2); + debiwrite(saa, debNormal, IBM_MP2_COMMAND, (command << 1)| 1,2); else debiwrite(saa, debNormal, IBM_MP2_COMMAND, command << 1, 2); debiwrite(saa, debNormal, IBM_MP2_CMD_DATA, data, 2); @@ -663,11 +635,9 @@ static void initialize_cs8420(struct saa7146 *saa, int pro) else sequence = mode8420con; for (i = 0; i < INIT8420LEN; i++) - I2CWrite(saa, 0x20, init8420[i * 2], - init8420[i * 2 + 1], 2); + I2CWrite(saa, 0x20, init8420[i * 2], init8420[i * 2 + 1], 2); for (i = 0; i < MODE8420LEN; i++) - I2CWrite(saa, 0x20, sequence[i * 2], - sequence[i * 2 + 1], 2); + I2CWrite(saa, 0x20, sequence[i * 2], sequence[i * 2 + 1], 2); printk("stradis%d: CS8420 initialized\n", saa->nr); } @@ -683,35 +653,36 @@ static void initialize_saa7121(struct saa7146 *saa, int dopal) /* initialize PAL/NTSC video encoder */ for (i = 0; i < INIT7121LEN; i++) { if (NewCard) { /* handle new card encoder differences */ - if (sequence[i*2] == 0x3a) + if (sequence[i * 2] == 0x3a) I2CWrite(saa, 0x88, 0x3a, 0x13, 2); - else if (sequence[i*2] == 0x6b) + else if (sequence[i * 2] == 0x6b) I2CWrite(saa, 0x88, 0x6b, 0x20, 2); - else if (sequence[i*2] == 0x6c) + else if (sequence[i * 2] == 0x6c) I2CWrite(saa, 0x88, 0x6c, dopal ? 0x09 : 0xf5, 2); - else if (sequence[i*2] == 0x6d) + else if (sequence[i * 2] == 0x6d) I2CWrite(saa, 0x88, 0x6d, dopal ? 0x20 : 0x00, 2); - else if (sequence[i*2] == 0x7a) + else if (sequence[i * 2] == 0x7a) I2CWrite(saa, 0x88, 0x7a, dopal ? (PALFirstActive - 1) : (NTSCFirstActive - 4), 2); - else if (sequence[i*2] == 0x7b) + else if (sequence[i * 2] == 0x7b) I2CWrite(saa, 0x88, 0x7b, dopal ? PALLastActive : NTSCLastActive, 2); - else I2CWrite(saa, 0x88, sequence[i * 2], - sequence[i * 2 + 1], 2); + else + I2CWrite(saa, 0x88, sequence[i * 2], + sequence[i * 2 + 1], 2); } else { - if (sequence[i*2] == 0x6b && mod) - I2CWrite(saa, 0x88, 0x6b, - (sequence[i * 2 + 1] ^ 0x09), 2); - else if (sequence[i*2] == 0x7a) + if (sequence[i * 2] == 0x6b && mod) + I2CWrite(saa, 0x88, 0x6b, + (sequence[i * 2 + 1] ^ 0x09), 2); + else if (sequence[i * 2] == 0x7a) I2CWrite(saa, 0x88, 0x7a, dopal ? (PALFirstActive - 1) : (NTSCFirstActive - 4), 2); - else if (sequence[i*2] == 0x7b) + else if (sequence[i * 2] == 0x7b) I2CWrite(saa, 0x88, 0x7b, dopal ? PALLastActive : NTSCLastActive, 2); @@ -735,7 +706,8 @@ static void set_genlock_offset(struct saa7146 *saa, int noffset) nCode = noffset + 0x100; if (nCode == 1) nCode = 0x401; - else if (nCode < 1) nCode = 0x400 + PixelsPerLine + nCode; + else if (nCode < 1) + nCode = 0x400 + PixelsPerLine + nCode; debiwrite(saa, debNormal, XILINX_GLDELAY, nCode, 2); } @@ -745,33 +717,31 @@ static void set_out_format(struct saa7146 *saa, int mode) saa->boardcfg[2] = mode; /* do not adjust analog video parameters here, use saa7121 init */ /* you will affect the SDI output on the new card */ - if (mode == VIDEO_MODE_PAL) { /* PAL */ + if (mode == VIDEO_MODE_PAL) { /* PAL */ debiwrite(saa, debNormal, XILINX_CTL0, 0x0808, 2); mdelay(50); saawrite(0x012002c0, SAA7146_NUM_LINE_BYTE1); if (NewCard) { - debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - 0xe100, 2); + debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, 0xe100, 2); mdelay(50); } debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - NewCard ? 0xe500: 0x6500, 2); + NewCard ? 0xe500 : 0x6500, 2); debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, (1 << 8) | - (NewCard ? PALFirstActive : PALFirstActive-6), 2); + (NewCard ? PALFirstActive : PALFirstActive - 6), 2); } else { /* NTSC */ debiwrite(saa, debNormal, XILINX_CTL0, 0x0800, 2); mdelay(50); saawrite(0x00f002c0, SAA7146_NUM_LINE_BYTE1); debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - NewCard ? 0xe100: 0x6100, 2); + NewCard ? 0xe100 : 0x6100, 2); debiwrite(saa, debNormal, IBM_MP2_DISP_DLY, (1 << 8) | - (NewCard ? NTSCFirstActive : NTSCFirstActive-6), 2); + (NewCard ? NTSCFirstActive : NTSCFirstActive - 6), 2); } } - /* Intialize bitmangler to map from a byte value to the mangled word that * must be output to program the Xilinx part through the DEBI port. * Xilinx Data Bit->DEBI Bit: 0->15 1->7 2->6 3->12 4->11 5->2 6->1 7->0 @@ -799,43 +769,41 @@ static int initialize_fpga(struct video_code *bitdata) for (num = 0; num < saa_num; num++) { saa = &saa7146s[num]; if (saa->boardcfg[0] > 20) - continue; /* card was programmed */ + continue; /* card was programmed */ loadtwo = (saa->boardcfg[18] & 0x10); if (!NewCard) /* we have an old board */ for (i = 0; i < 256; i++) - bitmangler[i] = ((i & 0x01) << 15) | - ((i & 0x02) << 6) | ((i & 0x04) << 4) | - ((i & 0x08) << 9) | ((i & 0x10) << 7) | - ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | - ((i & 0x80) >> 7); - else /* else we have a new board */ + bitmangler[i] = ((i & 0x01) << 15) | + ((i & 0x02) << 6) | ((i & 0x04) << 4) | + ((i & 0x08) << 9) | ((i & 0x10) << 7) | + ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | + ((i & 0x80) >> 7); + else /* else we have a new board */ for (i = 0; i < 256; i++) - bitmangler[i] = ((i & 0x01) << 7) | - ((i & 0x02) << 5) | ((i & 0x04) << 3) | - ((i & 0x08) << 1) | ((i & 0x10) >> 1) | - ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | - ((i & 0x80) >> 7); + bitmangler[i] = ((i & 0x01) << 7) | + ((i & 0x02) << 5) | ((i & 0x04) << 3) | + ((i & 0x08) << 1) | ((i & 0x10) >> 1) | + ((i & 0x20) >> 3) | ((i & 0x40) >> 5) | + ((i & 0x80) >> 7); dmabuf = (u16 *) saa->dmadebi; newdma = (u8 *) saa->dmadebi; if (NewCard) { /* SDM2xxx */ if (!strncmp(bitdata->loadwhat, "decoder2", 8)) continue; /* fpga not for this card */ - if (!strncmp(&saa->boardcfg[42], - bitdata->loadwhat, 8)) { + if (!strncmp(&saa->boardcfg[42], bitdata->loadwhat, 8)) loadfile = 1; - } else if (loadtwo && !strncmp(&saa->boardcfg[19], - bitdata->loadwhat, 8)) { + else if (loadtwo && !strncmp(&saa->boardcfg[19], + bitdata->loadwhat, 8)) loadfile = 2; - } else if (!saa->boardcfg[42] && /* special */ - !strncmp("decxl", bitdata->loadwhat, 8)) { - loadfile = 1; - } else + else if (!saa->boardcfg[42] && !strncmp("decxl", + bitdata->loadwhat, 8)) + loadfile = 1; /* special */ + else continue; /* fpga not for this card */ - if (loadfile != 1 && loadfile != 2) { + if (loadfile != 1 && loadfile != 2) continue; /* skip to next card */ - } - if (saa->boardcfg[0] && loadfile == 1 ) + if (saa->boardcfg[0] && loadfile == 1) continue; /* skip to next card */ if (saa->boardcfg[0] != 1 && loadfile == 2) continue; /* skip to next card */ @@ -870,8 +838,9 @@ static int initialize_fpga(struct video_code *bitdata) /* Release Xilinx INIT signal (WS2) */ saawrite(0x00000000, SAA7146_GPIO_CTRL); /* Wait for the INIT to go High */ - for (i = 0; i < 10000 && - !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); i++) + for (i = 0; + i < 10000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); + i++) schedule(); if (i == 1000) { printk(KERN_INFO "stradis%d: no fpga INIT\n", saa->nr); @@ -881,17 +850,13 @@ send_fpga_stuff: if (NewCard) { for (i = startindex; i < bitdata->datasize; i++) newdma[i - startindex] = - bitmangler[bitdata->data[i]]; + bitmangler[bitdata->data[i]]; debiwrite(saa, 0x01420000, 0, 0, ((bitdata->datasize - startindex) + 5)); - if (loadtwo) { - if (loadfile == 1) { - printk("stradis%d: " - "awaiting 2nd FPGA bitfile\n", - saa->nr); - continue; /* skip to next card */ - } - + if (loadtwo && loadfile == 1) { + printk("stradis%d: awaiting 2nd FPGA bitfile\n", + saa->nr); + continue; /* skip to next card */ } } else { for (i = startindex; i < bitdata->datasize; i++) @@ -900,8 +865,9 @@ send_fpga_stuff: debiwrite(saa, 0x014a0000, 0, 0, ((bitdata->datasize - startindex) + 5) * 2); } - for (i = 0; i < 1000 && - !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); i++) + for (i = 0; + i < 1000 && !(saaread(SAA7146_PSR) & SAA7146_PSR_PIN2); + i++) schedule(); if (i == 1000) { printk(KERN_INFO "stradis%d: FPGA load failed\n", @@ -925,14 +891,14 @@ send_fpga_stuff: /* mute CS3310 */ if (HaveCS3310) debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, - 0, 2); + 0, 2); /* set VXCO to PWM mode, release reset, blank on */ debiwrite(saa, debNormal, XILINX_CTL0, 0xffc4, 2); mdelay(10); /* unmute CS3310 */ if (HaveCS3310) debiwrite(saa, debNormal, XILINX_CTL0, - 0x2020, 2); + 0x2020, 2); } /* set source Black */ debiwrite(saa, debNormal, XILINX_CTL0, 0x1707, 2); @@ -958,10 +924,10 @@ send_fpga_stuff: /* we must init CS8420 first since rev b pulls i2s */ /* master clock low and CS4341 needs i2s master to */ /* run the i2c port. */ - if (HaveCS8420) { + if (HaveCS8420) /* 0=consumer, 1=pro */ initialize_cs8420(saa, 0); - } + mdelay(5); if (HaveCS4341) initialize_cs4341(saa); @@ -981,6 +947,7 @@ send_fpga_stuff: debiwrite(saa, debNormal, XILINX_CTL0, 0x8080, 2); #endif } + return failure; } @@ -1021,10 +988,10 @@ static int do_ibm_reset(struct saa7146 *saa) /* we must init CS8420 first since rev b pulls i2s */ /* master clock low and CS4341 needs i2s master to */ /* run the i2c port. */ - if (HaveCS8420) { + if (HaveCS8420) /* 0=consumer, 1=pro */ initialize_cs8420(saa, 1); - } + mdelay(5); if (HaveCS4341) initialize_cs4341(saa); @@ -1039,12 +1006,12 @@ static int do_ibm_reset(struct saa7146 *saa) debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, - (ChipControl == 0x43 ? 0xe800 : 0xe000), 1)) { + (ChipControl == 0x43 ? 0xe800 : 0xe000), 1)) { printk(KERN_ERR "stradis%d: IBM config failed\n", saa->nr); } if (HaveCS3310) { int i = CS3310MaxLvl; - debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, ((i<<8)|i), 2); + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, ((i << 8)| i),2); } /* start video decoder */ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); @@ -1057,6 +1024,7 @@ static int do_ibm_reset(struct saa7146 *saa) /* clear pending interrupts */ debiread(saa, debNormal, IBM_MP2_HOST_INT, 2); debiwrite(saa, debNormal, XILINX_CTL0, 0x1711, 2); + return 0; } @@ -1070,8 +1038,8 @@ static int initialize_ibmmpeg2(struct video_code *microcode) saa = &saa7146s[num]; /* check that FPGA is loaded */ debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0xa55a, 2); - if ((i = debiread(saa, debNormal, IBM_MP2_OSD_SIZE, 2)) != - 0xa55a) { + i = debiread(saa, debNormal, IBM_MP2_OSD_SIZE, 2); + if (i != 0xa55a) { printk(KERN_INFO "stradis%d: %04x != 0xa55a\n", saa->nr, i); #if 0 @@ -1082,17 +1050,17 @@ static int initialize_ibmmpeg2(struct video_code *microcode) if (saa->boardcfg[0] > 27) continue; /* skip to next card */ /* load video control store */ - saa->boardcfg[1] = 0x13; /* no-sync default */ + saa->boardcfg[1] = 0x13; /* no-sync default */ debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 1, 2); debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); for (i = 0; i < microcode->datasize / 2; i++) debiwrite(saa, debNormal, IBM_MP2_PROC_IDATA, (microcode->data[i * 2] << 8) | - microcode->data[i * 2 + 1], 2); + microcode->data[i * 2 + 1], 2); debiwrite(saa, debNormal, IBM_MP2_PROC_IADDR, 0, 2); debiwrite(saa, debNormal, IBM_MP2_WR_PROT, 0, 2); debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, - ChipControl, 2); + ChipControl, 2); saa->boardcfg[0] = 28; } if (!strncmp(microcode->loadwhat, "decoder.aud", 11)) { @@ -1109,34 +1077,32 @@ static int initialize_ibmmpeg2(struct video_code *microcode) debiwrite(saa, debNormal, IBM_MP2_OSD_SIZE, 0x2000, 2); debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4552, 2); if (ibm_send_command(saa, IBM_MP2_CONFIG_DECODER, - 0xe000, 1)) { - printk(KERN_ERR - "stradis%d: IBM config failed\n", - saa->nr); + 0xe000, 1)) { + printk(KERN_ERR "stradis%d: IBM config " + "failed\n", saa->nr); return -1; } /* set PWM to center value */ if (NewCard) { debiwrite(saa, debNormal, XILINX_PWM, - saa->boardcfg[14] + - (saa->boardcfg[13]<<8), 2); + saa->boardcfg[14] + + (saa->boardcfg[13] << 8), 2); } else - debiwrite(saa, debNormal, XILINX_PWM, - 0x46, 2); + debiwrite(saa, debNormal, XILINX_PWM, 0x46, 2); + if (HaveCS3310) { i = CS3310MaxLvl; - debiwrite(saa, debNormal, - XILINX_CS3310_CMPLT, ((i<<8)|i), 2); + debiwrite(saa, debNormal, XILINX_CS3310_CMPLT, + (i << 8) | i, 2); } - printk(KERN_INFO - "stradis%d: IBM MPEGCD%d Initialized\n", - saa->nr, 18 + (debiread(saa, debNormal, - IBM_MP2_CHIP_CONTROL, 2) >> 12)); + printk(KERN_INFO "stradis%d: IBM MPEGCD%d Inited\n", + saa->nr, 18 + (debiread(saa, debNormal, + IBM_MP2_CHIP_CONTROL, 2) >> 12)); /* start video decoder */ debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); - debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, - 0x4037, 2); /* 256k vid, 3520 bytes aud */ + debiwrite(saa, debNormal, IBM_MP2_RB_THRESHOLD, 0x4037, + 2); /* 256k vid, 3520 bytes aud */ debiwrite(saa, debNormal, IBM_MP2_AUD_CTL, 0x4573, 2); ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); /* enable buffer threshold irq */ @@ -1149,52 +1115,48 @@ static int initialize_ibmmpeg2(struct video_code *microcode) saa->boardcfg[0] = 37; } } + return 0; } -static u32 palette2fmt[] = -{ /* some of these YUV translations are wrong */ - 0xffffffff, 0x86000000, 0x87000000, 0x80000000, 0x8100000, 0x82000000, - 0x83000000, 0x00000000, 0x03000000, 0x03000000, 0x0a00000, 0x03000000, - 0x06000000, 0x00000000, 0x03000000, 0x0a000000, 0x0300000 +static u32 palette2fmt[] = { /* some of these YUV translations are wrong */ + 0xffffffff, 0x86000000, 0x87000000, 0x80000000, 0x8100000, 0x82000000, + 0x83000000, 0x00000000, 0x03000000, 0x03000000, 0x0a00000, 0x03000000, + 0x06000000, 0x00000000, 0x03000000, 0x0a000000, 0x0300000 }; -static int bpp2fmt[4] = -{ +static int bpp2fmt[4] = { VIDEO_PALETTE_HI240, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB24, VIDEO_PALETTE_RGB32 }; /* I wish I could find a formula to calculate these... */ -static u32 h_prescale[64] = -{ - 0x10000000, 0x18040202, 0x18080000, 0x380c0606, 0x38100204, 0x38140808, - 0x38180000, 0x381c0000, 0x3820161c, 0x38242a3b, 0x38281230, 0x382c4460, - 0x38301040, 0x38340080, 0x38380000, 0x383c0000, 0x3840fefe, 0x3844ee9f, - 0x3848ee9f, 0x384cee9f, 0x3850ee9f, 0x38542a3b, 0x38581230, 0x385c0000, - 0x38600000, 0x38640000, 0x38680000, 0x386c0000, 0x38700000, 0x38740000, - 0x38780000, 0x387c0000, 0x30800000, 0x38840000, 0x38880000, 0x388c0000, - 0x38900000, 0x38940000, 0x38980000, 0x389c0000, 0x38a00000, 0x38a40000, - 0x38a80000, 0x38ac0000, 0x38b00000, 0x38b40000, 0x38b80000, 0x38bc0000, - 0x38c00000, 0x38c40000, 0x38c80000, 0x38cc0000, 0x38d00000, 0x38d40000, - 0x38d80000, 0x38dc0000, 0x38e00000, 0x38e40000, 0x38e80000, 0x38ec0000, - 0x38f00000, 0x38f40000, 0x38f80000, 0x38fc0000, +static u32 h_prescale[64] = { + 0x10000000, 0x18040202, 0x18080000, 0x380c0606, 0x38100204, 0x38140808, + 0x38180000, 0x381c0000, 0x3820161c, 0x38242a3b, 0x38281230, 0x382c4460, + 0x38301040, 0x38340080, 0x38380000, 0x383c0000, 0x3840fefe, 0x3844ee9f, + 0x3848ee9f, 0x384cee9f, 0x3850ee9f, 0x38542a3b, 0x38581230, 0x385c0000, + 0x38600000, 0x38640000, 0x38680000, 0x386c0000, 0x38700000, 0x38740000, + 0x38780000, 0x387c0000, 0x30800000, 0x38840000, 0x38880000, 0x388c0000, + 0x38900000, 0x38940000, 0x38980000, 0x389c0000, 0x38a00000, 0x38a40000, + 0x38a80000, 0x38ac0000, 0x38b00000, 0x38b40000, 0x38b80000, 0x38bc0000, + 0x38c00000, 0x38c40000, 0x38c80000, 0x38cc0000, 0x38d00000, 0x38d40000, + 0x38d80000, 0x38dc0000, 0x38e00000, 0x38e40000, 0x38e80000, 0x38ec0000, + 0x38f00000, 0x38f40000, 0x38f80000, 0x38fc0000, }; -static u32 v_gain[64] = -{ - 0x016000ff, 0x016100ff, 0x016100ff, 0x016200ff, 0x016200ff, 0x016200ff, - 0x016200ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, - 0x016300ff, 0x016300ff, 0x016300ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, - 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, +static u32 v_gain[64] = { + 0x016000ff, 0x016100ff, 0x016100ff, 0x016200ff, 0x016200ff, 0x016200ff, + 0x016200ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, 0x016300ff, + 0x016300ff, 0x016300ff, 0x016300ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, + 0x016400ff, 0x016400ff, 0x016400ff, 0x016400ff, }; - static void saa7146_set_winsize(struct saa7146 *saa) { u32 format; @@ -1209,24 +1171,23 @@ static void saa7146_set_winsize(struct saa7146 *saa) saawrite(saa->win.vidadr + saa->win.bpl * saa->win.sheight, SAA7146_PROT_ADDR1); saawrite(0, SAA7146_PAGE1); - saawrite(format|0x60, SAA7146_CLIP_FORMAT_CTRL); + saawrite(format | 0x60, SAA7146_CLIP_FORMAT_CTRL); offset = (704 / (saa->win.width - 1)) & 0x3f; saawrite(h_prescale[offset], SAA7146_HPS_H_PRESCALE); offset = (720896 / saa->win.width) / (offset + 1); - saawrite((offset<<12)|0x0c, SAA7146_HPS_H_SCALE); + saawrite((offset << 12) | 0x0c, SAA7146_HPS_H_SCALE); if (CurrentMode == VIDEO_MODE_NTSC) { - yacl = /*(480 / saa->win.height - 1) & 0x3f*/ 0; + yacl = /*(480 / saa->win.height - 1) & 0x3f */ 0; ysci = 1024 - (saa->win.height * 1024 / 480); } else { - yacl = /*(576 / saa->win.height - 1) & 0x3f*/ 0; + yacl = /*(576 / saa->win.height - 1) & 0x3f */ 0; ysci = 1024 - (saa->win.height * 1024 / 576); } - saawrite((1<<31)|(ysci<<21)|(yacl<<15), SAA7146_HPS_V_SCALE); + saawrite((1 << 31) | (ysci << 21) | (yacl << 15), SAA7146_HPS_V_SCALE); saawrite(v_gain[yacl], SAA7146_HPS_V_GAIN); saawrite(((SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_HPS_V | - SAA7146_MC2_UPLD_HPS_H) << 16) | (SAA7146_MC2_UPLD_DMA1 | - SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_HPS_H), - SAA7146_MC2); + SAA7146_MC2_UPLD_HPS_H) << 16) | (SAA7146_MC2_UPLD_DMA1 | + SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_HPS_H), SAA7146_MC2); } /* clip_draw_rectangle(cm,x,y,w,h) -- handle clipping an area @@ -1261,8 +1222,8 @@ static void clip_draw_rectangle(u32 *clipmap, int x, int y, int w, int h) startword = (x >> 5); endword = ((x + w) >> 5); bitsleft = (0xffffffff >> (x & 31)); - bitsright = (0xffffffff << (~((x + w) - (endword<<5)))); - temp = &clipmap[(y<<5) + startword]; + bitsright = (0xffffffff << (~((x + w) - (endword << 5)))); + temp = &clipmap[(y << 5) + startword]; w = endword - startword; if (!w) { bitsleft |= bitsright; @@ -1287,13 +1248,13 @@ static void make_clip_tab(struct saa7146 *saa, struct video_clip *cr, int ncr) u32 *clipmap; clipmap = saa->dmavid2; - if((width=saa->win.width)>1023) - width = 1023; /* sanity check */ - if((height=saa->win.height)>640) - height = 639; /* sanity check */ - if (ncr > 0) { /* rectangles pased */ + if ((width = saa->win.width) > 1023) + width = 1023; /* sanity check */ + if ((height = saa->win.height) > 640) + height = 639; /* sanity check */ + if (ncr > 0) { /* rectangles pased */ /* convert rectangular clips to a bitmap */ - memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ + memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ for (i = 0; i < ncr; i++) clip_draw_rectangle(clipmap, cr[i].x, cr[i].y, cr[i].width, cr[i].height); @@ -1301,14 +1262,15 @@ static void make_clip_tab(struct saa7146 *saa, struct video_clip *cr, int ncr) /* clip against viewing window AND screen so we do not have to rely on the user program */ - clip_draw_rectangle(clipmap,(saa->win.x+width>saa->win.swidth) ? - (saa->win.swidth-saa->win.x) : width, 0, 1024, 768); - clip_draw_rectangle(clipmap,0,(saa->win.y+height>saa->win.sheight) ? - (saa->win.sheight-saa->win.y) : height,1024,768); - if (saa->win.x<0) - clip_draw_rectangle(clipmap, 0, 0, -(saa->win.x), 768); - if (saa->win.y<0) - clip_draw_rectangle(clipmap, 0, 0, 1024, -(saa->win.y)); + clip_draw_rectangle(clipmap, (saa->win.x + width > saa->win.swidth) ? + (saa->win.swidth - saa->win.x) : width, 0, 1024, 768); + clip_draw_rectangle(clipmap, 0, + (saa->win.y + height > saa->win.sheight) ? + (saa->win.sheight - saa->win.y) : height, 1024, 768); + if (saa->win.x < 0) + clip_draw_rectangle(clipmap, 0, 0, -saa->win.x, 768); + if (saa->win.y < 0) + clip_draw_rectangle(clipmap, 0, 0, 1024, -saa->win.y); } static int saa_ioctl(struct inode *inode, struct file *file, @@ -1322,11 +1284,9 @@ static int saa_ioctl(struct inode *inode, struct file *file, { struct video_capability b; strcpy(b.name, saa->video_dev.name); - b.type = VID_TYPE_CAPTURE | - VID_TYPE_OVERLAY | - VID_TYPE_CLIPPING | - VID_TYPE_FRAMERAM | - VID_TYPE_SCALES; + b.type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | + VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM | + VID_TYPE_SCALES; b.channels = 1; b.audios = 1; b.maxwidth = 768; @@ -1363,17 +1323,18 @@ static int saa_ioctl(struct inode *inode, struct file *file, if (p.palette < sizeof(palette2fmt) / sizeof(u32)) { format = palette2fmt[p.palette]; saa->win.color_fmt = format; - saawrite(format|0x60, SAA7146_CLIP_FORMAT_CTRL); + saawrite(format | 0x60, + SAA7146_CLIP_FORMAT_CTRL); } saawrite(((p.brightness & 0xff00) << 16) | - ((p.contrast & 0xfe00) << 7) | - ((p.colour & 0xfe00) >> 9), SAA7146_BCS_CTRL); + ((p.contrast & 0xfe00) << 7) | + ((p.colour & 0xfe00) >> 9), SAA7146_BCS_CTRL); saa->picture = p; /* upload changed registers */ saawrite(((SAA7146_MC2_UPLD_HPS_H | - SAA7146_MC2_UPLD_HPS_V) << 16) | - SAA7146_MC2_UPLD_HPS_H | SAA7146_MC2_UPLD_HPS_V, - SAA7146_MC2); + SAA7146_MC2_UPLD_HPS_V) << 16) | + SAA7146_MC2_UPLD_HPS_H | + SAA7146_MC2_UPLD_HPS_V, SAA7146_MC2); return 0; } case VIDIOCSWIN: @@ -1384,11 +1345,14 @@ static int saa_ioctl(struct inode *inode, struct file *file, if (copy_from_user(&vw, arg, sizeof(vw))) return -EFAULT; - if (vw.flags || vw.width < 16 || vw.height < 16) { /* stop capture */ - saawrite((SAA7146_MC1_TR_E_1 << 16), SAA7146_MC1); + /* stop capture */ + if (vw.flags || vw.width < 16 || vw.height < 16) { + saawrite((SAA7146_MC1_TR_E_1 << 16), + SAA7146_MC1); return -EINVAL; } - if (saa->win.bpp < 4) { /* 32-bit align start and adjust width */ + /* 32-bit align start and adjust width */ + if (saa->win.bpp < 4) { int i = vw.x; vw.x = (vw.x + 3) & ~3; i = vw.x - i; @@ -1417,23 +1381,24 @@ static int saa_ioctl(struct inode *inode, struct file *file, */ if (vw.clipcount < 0) { if (copy_from_user(saa->dmavid2, vw.clips, - VIDEO_CLIPMAP_SIZE)) + VIDEO_CLIPMAP_SIZE)) return -EFAULT; - } - else if (vw.clipcount > 16384) { + } else if (vw.clipcount > 16384) { return -EINVAL; } else if (vw.clipcount > 0) { - if ((vcp = vmalloc(sizeof(struct video_clip) * - (vw.clipcount))) == NULL) - return -ENOMEM; + vcp = vmalloc(sizeof(struct video_clip) * + vw.clipcount); + if (vcp == NULL) + return -ENOMEM; if (copy_from_user(vcp, vw.clips, - sizeof(struct video_clip) * - vw.clipcount)) { + sizeof(struct video_clip) * + vw.clipcount)) { vfree(vcp); return -EFAULT; } } else /* nothing clipped */ memset(saa->dmavid2, 0, VIDEO_CLIPMAP_SIZE); + make_clip_tab(saa, vcp, vw.clipcount); if (vw.clipcount > 0) vfree(vcp); @@ -1466,21 +1431,21 @@ static int saa_ioctl(struct inode *inode, struct file *file, if (v == 0) { saa->cap &= ~1; saawrite((SAA7146_MC1_TR_E_1 << 16), - SAA7146_MC1); + SAA7146_MC1); } else { if (saa->win.vidadr == 0 || saa->win.width == 0 - || saa->win.height == 0) + || saa->win.height == 0) return -EINVAL; saa->cap |= 1; saawrite((SAA7146_MC1_TR_E_1 << 16) | 0xffff, - SAA7146_MC1); + SAA7146_MC1); } return 0; } case VIDIOCGFBUF: { struct video_buffer v; - v.base = (void *) saa->win.vidadr; + v.base = (void *)saa->win.vidadr; v.height = saa->win.sheight; v.width = saa->win.swidth; v.depth = saa->win.depth; @@ -1498,19 +1463,20 @@ static int saa_ioctl(struct inode *inode, struct file *file, if (copy_from_user(&v, arg, sizeof(v))) return -EFAULT; if (v.depth != 8 && v.depth != 15 && v.depth != 16 && - v.depth != 24 && v.depth != 32 && v.width > 16 && + v.depth != 24 && v.depth != 32 && v.width > 16 && v.height > 16 && v.bytesperline > 16) return -EINVAL; if (v.base) - saa->win.vidadr = (unsigned long) v.base; + saa->win.vidadr = (unsigned long)v.base; saa->win.sheight = v.height; saa->win.swidth = v.width; saa->win.bpp = ((v.depth + 7) & 0x38) / 8; saa->win.depth = v.depth; saa->win.bpl = v.bytesperline; - DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", - v.base, v.width, v.height, saa->win.bpp, saa->win.bpl)); + DEBUG(printk("Display at %p is %d by %d, bytedepth %d, " + "bpl %d\n", v.base, v.width, v.height, + saa->win.bpp, saa->win.bpl)); saa7146_set_winsize(saa); return 0; } @@ -1538,21 +1504,18 @@ static int saa_ioctl(struct inode *inode, struct file *file, int i; if (copy_from_user(&v, arg, sizeof(v))) return -EFAULT; - i = (~(v.volume>>8))&0xff; + i = (~(v.volume >> 8)) & 0xff; if (!HaveCS4341) { - if (v.flags & VIDEO_AUDIO_MUTE) { + if (v.flags & VIDEO_AUDIO_MUTE) debiwrite(saa, debNormal, - IBM_MP2_FRNT_ATTEN, - 0xffff, 2); - } + IBM_MP2_FRNT_ATTEN, 0xffff, 2); if (!(v.flags & VIDEO_AUDIO_MUTE)) debiwrite(saa, debNormal, - IBM_MP2_FRNT_ATTEN, - 0x0000, 2); + IBM_MP2_FRNT_ATTEN, 0x0000, 2); if (v.flags & VIDEO_AUDIO_VOLUME) debiwrite(saa, debNormal, IBM_MP2_FRNT_ATTEN, - (i<<8)|i, 2); + (i << 8) | i, 2); } else { if (v.flags & VIDEO_AUDIO_MUTE) cs4341_setlevel(saa, 0xff, 0xff); @@ -1580,163 +1543,138 @@ static int saa_ioctl(struct inode *inode, struct file *file, case VIDIOCSPLAYMODE: { struct video_play_mode pmode; - if (copy_from_user((void *) &pmode, arg, - sizeof(struct video_play_mode))) + if (copy_from_user((void *)&pmode, arg, + sizeof(struct video_play_mode))) return -EFAULT; switch (pmode.mode) { - case VID_PLAY_VID_OUT_MODE: - if (pmode.p1 != VIDEO_MODE_NTSC && + case VID_PLAY_VID_OUT_MODE: + if (pmode.p1 != VIDEO_MODE_NTSC && pmode.p1 != VIDEO_MODE_PAL) - return -EINVAL; - set_out_format(saa, pmode.p1); - return 0; - case VID_PLAY_GENLOCK: - debiwrite(saa, debNormal, - XILINX_CTL0, - (pmode.p1 ? 0x8000 : 0x8080), - 2); - if (NewCard) - set_genlock_offset(saa, - pmode.p2); - return 0; - case VID_PLAY_NORMAL: - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - ibm_send_command(saa, - IBM_MP2_PLAY, 0, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_PAUSE: - /* IBM removed the PAUSE command */ - /* they say use SINGLE_FRAME now */ - case VID_PLAY_SINGLE_FRAME: - ibm_send_command(saa, - IBM_MP2_SINGLE_FRAME, - 0, 0); - if (saa->playmode == pmode.mode) { - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - } - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_FAST_FORWARD: - ibm_send_command(saa, - IBM_MP2_FAST_FORWARD, 0, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_SLOW_MOTION: - ibm_send_command(saa, - IBM_MP2_SLOW_MOTION, - pmode.p1, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_IMMEDIATE_NORMAL: - /* ensure transfers resume */ - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - ibm_send_command(saa, - IBM_MP2_IMED_NORM_PLAY, 0, 0); - saa->playmode = VID_PLAY_NORMAL; - return 0; - case VID_PLAY_SWITCH_CHANNELS: - saa->audhead = saa->audtail = 0; - saa->vidhead = saa->vidtail = 0; - ibm_send_command(saa, - IBM_MP2_FREEZE_FRAME, 0, 1); - ibm_send_command(saa, - IBM_MP2_RESET_AUD_RATE, 0, 1); - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, 0, 2); - ibm_send_command(saa, - IBM_MP2_CHANNEL_SWITCH, 0, 1); + return -EINVAL; + set_out_format(saa, pmode.p1); + return 0; + case VID_PLAY_GENLOCK: + debiwrite(saa, debNormal, XILINX_CTL0, + pmode.p1 ? 0x8000 : 0x8080, 2); + if (NewCard) + set_genlock_offset(saa, pmode.p2); + return 0; + case VID_PLAY_NORMAL: + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_PAUSE: + /* IBM removed the PAUSE command */ + /* they say use SINGLE_FRAME now */ + case VID_PLAY_SINGLE_FRAME: + ibm_send_command(saa, IBM_MP2_SINGLE_FRAME,0,0); + if (saa->playmode == pmode.mode) { debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, ChipControl, 2); - ibm_send_command(saa, - IBM_MP2_PLAY, 0, 0); - saa->playmode = VID_PLAY_NORMAL; - return 0; - case VID_PLAY_FREEZE_FRAME: - ibm_send_command(saa, - IBM_MP2_FREEZE_FRAME, 0, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_STILL_MODE: - ibm_send_command(saa, - IBM_MP2_SET_STILL_MODE, 0, 0); - saa->playmode = pmode.mode; - return 0; - case VID_PLAY_MASTER_MODE: - if (pmode.p1 == VID_PLAY_MASTER_NONE) - saa->boardcfg[1] = 0x13; - else if (pmode.p1 == - VID_PLAY_MASTER_VIDEO) - saa->boardcfg[1] = 0x23; - else if (pmode.p1 == - VID_PLAY_MASTER_AUDIO) - saa->boardcfg[1] = 0x43; - else + } + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_FAST_FORWARD: + ibm_send_command(saa, IBM_MP2_FAST_FORWARD,0,0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_SLOW_MOTION: + ibm_send_command(saa, IBM_MP2_SLOW_MOTION, + pmode.p1, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_IMMEDIATE_NORMAL: + /* ensure transfers resume */ + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + ibm_send_command(saa, IBM_MP2_IMED_NORM_PLAY, + 0, 0); + saa->playmode = VID_PLAY_NORMAL; + return 0; + case VID_PLAY_SWITCH_CHANNELS: + saa->audhead = saa->audtail = 0; + saa->vidhead = saa->vidtail = 0; + ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,1); + ibm_send_command(saa, IBM_MP2_RESET_AUD_RATE, + 0, 1); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + 0, 2); + ibm_send_command(saa, IBM_MP2_CHANNEL_SWITCH, + 0, 1); + debiwrite(saa, debNormal, IBM_MP2_CHIP_CONTROL, + ChipControl, 2); + ibm_send_command(saa, IBM_MP2_PLAY, 0, 0); + saa->playmode = VID_PLAY_NORMAL; + return 0; + case VID_PLAY_FREEZE_FRAME: + ibm_send_command(saa, IBM_MP2_FREEZE_FRAME,0,0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_STILL_MODE: + ibm_send_command(saa, IBM_MP2_SET_STILL_MODE, + 0, 0); + saa->playmode = pmode.mode; + return 0; + case VID_PLAY_MASTER_MODE: + if (pmode.p1 == VID_PLAY_MASTER_NONE) + saa->boardcfg[1] = 0x13; + else if (pmode.p1 == VID_PLAY_MASTER_VIDEO) + saa->boardcfg[1] = 0x23; + else if (pmode.p1 == VID_PLAY_MASTER_AUDIO) + saa->boardcfg[1] = 0x43; + else + return -EINVAL; + debiwrite(saa, debNormal, + IBM_MP2_CHIP_CONTROL, ChipControl, 2); + return 0; + case VID_PLAY_ACTIVE_SCANLINES: + if (CurrentMode == VIDEO_MODE_PAL) { + if (pmode.p1 < 1 || pmode.p2 > 625) return -EINVAL; - debiwrite(saa, debNormal, - IBM_MP2_CHIP_CONTROL, - ChipControl, 2); - return 0; - case VID_PLAY_ACTIVE_SCANLINES: - if (CurrentMode == VIDEO_MODE_PAL) { - if (pmode.p1 < 1 || - pmode.p2 > 625) - return -EINVAL; - saa->boardcfg[5] = pmode.p1; - saa->boardcfg[55] = (pmode.p1 + - (pmode.p2/2) - 1) & - 0xff; - } else { - if (pmode.p1 < 4 || - pmode.p2 > 525) - return -EINVAL; - saa->boardcfg[4] = pmode.p1; - saa->boardcfg[54] = (pmode.p1 + - (pmode.p2/2) - 4) & - 0xff; - } - set_out_format(saa, CurrentMode); - case VID_PLAY_RESET: - return do_ibm_reset(saa); - case VID_PLAY_END_MARK: - if (saa->endmarktail < - saa->endmarkhead) { - if (saa->endmarkhead - + saa->boardcfg[5] = pmode.p1; + saa->boardcfg[55] = (pmode.p1 + + (pmode.p2 / 2) - 1) & 0xff; + } else { + if (pmode.p1 < 4 || pmode.p2 > 525) + return -EINVAL; + saa->boardcfg[4] = pmode.p1; + saa->boardcfg[54] = (pmode.p1 + + (pmode.p2 / 2) - 4) & 0xff; + } + set_out_format(saa, CurrentMode); + case VID_PLAY_RESET: + return do_ibm_reset(saa); + case VID_PLAY_END_MARK: + if (saa->endmarktail < saa->endmarkhead) { + if (saa->endmarkhead - saa->endmarktail < 2) - return -ENOSPC; - } else if (saa->endmarkhead <= - saa->endmarktail) { - if (saa->endmarktail - - saa->endmarkhead > - (MAX_MARKS - 2)) - return -ENOSPC; - } else return -ENOSPC; - saa->endmark[saa->endmarktail] = - saa->audtail; - saa->endmarktail++; - if (saa->endmarktail >= MAX_MARKS) - saa->endmarktail = 0; + } else if (saa->endmarkhead <=saa->endmarktail){ + if (saa->endmarktail - saa->endmarkhead + > (MAX_MARKS - 2)) + return -ENOSPC; + } else + return -ENOSPC; + saa->endmark[saa->endmarktail] = saa->audtail; + saa->endmarktail++; + if (saa->endmarktail >= MAX_MARKS) + saa->endmarktail = 0; } return -EINVAL; } case VIDIOCSWRITEMODE: { int mode; - if (copy_from_user((void *) &mode, arg, sizeof(int))) - return -EFAULT; + if (copy_from_user((void *)&mode, arg, sizeof(int))) + return -EFAULT; if (mode == VID_WRITE_MPEG_AUD || - mode == VID_WRITE_MPEG_VID || - mode == VID_WRITE_CC || - mode == VID_WRITE_TTX || - mode == VID_WRITE_OSD) { + mode == VID_WRITE_MPEG_VID || + mode == VID_WRITE_CC || + mode == VID_WRITE_TTX || + mode == VID_WRITE_OSD) { saa->writemode = mode; return 0; } @@ -1750,7 +1688,7 @@ static int saa_ioctl(struct inode *inode, struct file *file, if (copy_from_user(&ucode, arg, sizeof(ucode))) return -EFAULT; if (ucode.datasize > 65536 || ucode.datasize < 1024 || - strncmp(ucode.loadwhat, "dec", 3)) + strncmp(ucode.loadwhat, "dec", 3)) return -EINVAL; if ((udata = vmalloc(ucode.datasize)) == NULL) return -ENOMEM; @@ -1759,8 +1697,8 @@ static int saa_ioctl(struct inode *inode, struct file *file, return -EFAULT; } ucode.data = udata; - if (!strncmp(ucode.loadwhat, "decoder.aud", 11) - || !strncmp(ucode.loadwhat, "decoder.vid", 11)) + if (!strncmp(ucode.loadwhat, "decoder.aud", 11) || + !strncmp(ucode.loadwhat, "decoder.vid", 11)) i = initialize_ibmmpeg2(&ucode); else i = initialize_fpga(&ucode); @@ -1805,14 +1743,14 @@ static int saa_mmap(struct file *file, struct vm_area_struct *vma) return -EINVAL; } -static ssize_t saa_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) +static ssize_t saa_read(struct file *file, char __user * buf, + size_t count, loff_t * ppos) { return -EINVAL; } -static ssize_t saa_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t saa_write(struct file *file, const char __user * buf, + size_t count, loff_t * ppos) { struct saa7146 *saa = file->private_data; unsigned long todo = count; @@ -1823,20 +1761,22 @@ static ssize_t saa_write(struct file *file, const char __user *buf, if (saa->writemode == VID_WRITE_MPEG_AUD) { spin_lock_irqsave(&saa->lock, flags); if (saa->audhead <= saa->audtail) - blocksize = 65536-(saa->audtail - saa->audhead); + blocksize = 65536 - + (saa->audtail - saa->audhead); else blocksize = saa->audhead - saa->audtail; spin_unlock_irqrestore(&saa->lock, flags); if (blocksize < 16384) { saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); + SAA7146_PSR_PIN1, SAA7146_IER); saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); /* wait for buffer space to open */ interruptible_sleep_on(&saa->audq); } spin_lock_irqsave(&saa->lock, flags); if (saa->audhead <= saa->audtail) { - blocksize = 65536-(saa->audtail - saa->audhead); + blocksize = 65536 - + (saa->audtail - saa->audhead); split = 65536 - saa->audtail; } else { blocksize = saa->audhead - saa->audtail; @@ -1851,7 +1791,7 @@ static ssize_t saa_write(struct file *file, const char __user *buf, return -ENOSPC; if (split < blocksize) { if (copy_from_user(saa->audbuf + - saa->audtail, buf, split)) + saa->audtail, buf, split)) return -EFAULT; buf += split; todo -= split; @@ -1859,7 +1799,7 @@ static ssize_t saa_write(struct file *file, const char __user *buf, saa->audtail = 0; } if (copy_from_user(saa->audbuf + saa->audtail, buf, - blocksize)) + blocksize)) return -EFAULT; saa->audtail += blocksize; todo -= blocksize; @@ -1868,20 +1808,22 @@ static ssize_t saa_write(struct file *file, const char __user *buf, } else if (saa->writemode == VID_WRITE_MPEG_VID) { spin_lock_irqsave(&saa->lock, flags); if (saa->vidhead <= saa->vidtail) - blocksize=524288-(saa->vidtail - saa->vidhead); + blocksize = 524288 - + (saa->vidtail - saa->vidhead); else blocksize = saa->vidhead - saa->vidtail; spin_unlock_irqrestore(&saa->lock, flags); if (blocksize < 65536) { saawrite(SAA7146_PSR_DEBI_S | - SAA7146_PSR_PIN1, SAA7146_IER); + SAA7146_PSR_PIN1, SAA7146_IER); saawrite(SAA7146_PSR_PIN1, SAA7146_PSR); /* wait for buffer space to open */ interruptible_sleep_on(&saa->vidq); } spin_lock_irqsave(&saa->lock, flags); if (saa->vidhead <= saa->vidtail) { - blocksize=524288-(saa->vidtail - saa->vidhead); + blocksize = 524288 - + (saa->vidtail - saa->vidhead); split = 524288 - saa->vidtail; } else { blocksize = saa->vidhead - saa->vidtail; @@ -1896,7 +1838,7 @@ static ssize_t saa_write(struct file *file, const char __user *buf, return -ENOSPC; if (split < blocksize) { if (copy_from_user(saa->vidbuf + - saa->vidtail, buf, split)) + saa->vidtail, buf, split)) return -EFAULT; buf += split; todo -= split; @@ -1904,7 +1846,7 @@ static ssize_t saa_write(struct file *file, const char __user *buf, saa->vidtail = 0; } if (copy_from_user(saa->vidbuf + saa->vidtail, buf, - blocksize)) + blocksize)) return -EFAULT; saa->vidtail += blocksize; todo -= blocksize; @@ -1922,8 +1864,8 @@ static ssize_t saa_write(struct file *file, const char __user *buf, debiwrite(saa, debNormal, IBM_MP2_OSD_LINK_ADDR, 0, 2); debiwrite(saa, debNormal, IBM_MP2_MASK0, 0xc00d, 2); debiwrite(saa, debNormal, IBM_MP2_DISP_MODE, - debiread(saa, debNormal, - IBM_MP2_DISP_MODE, 2) | 1, 2); + debiread(saa, debNormal, + IBM_MP2_DISP_MODE, 2) | 1, 2); /* trigger osd data transfer */ saawrite(SAA7146_PSR_DEBI_S | SAA7146_PSR_PIN1, SAA7146_IER); @@ -1935,21 +1877,11 @@ static ssize_t saa_write(struct file *file, const char __user *buf, static int saa_open(struct inode *inode, struct file *file) { - struct saa7146 *saa = NULL; - unsigned int minor = iminor(inode); - int i; + struct video_device *vdev = video_devdata(file); + struct saa7146 *saa = container_of(vdev, struct saa7146, video_dev); - for (i = 0; i < SAA7146_MAX; i++) { - if (saa7146s[i].video_dev.minor == minor) { - saa = &saa7146s[i]; - } - } - if (saa == NULL) { - return -ENODEV; - } file->private_data = saa; - //saa->video_dev.busy = 0; /* old hack to support multiple open */ saa->user++; if (saa->user > 1) return 0; /* device open already, don't reset */ @@ -1961,42 +1893,39 @@ static int saa_release(struct inode *inode, struct file *file) { struct saa7146 *saa = file->private_data; saa->user--; - //saa->video_dev.busy = 0; /* old hack to support multiple open */ + if (saa->user > 0) /* still someone using device */ return 0; saawrite(0x007f0000, SAA7146_MC1); /* stop all overlay dma */ return 0; } -static struct file_operations saa_fops = -{ - .owner = THIS_MODULE, - .open = saa_open, - .release = saa_release, - .ioctl = saa_ioctl, - .read = saa_read, - .llseek = no_llseek, - .write = saa_write, - .mmap = saa_mmap, +static struct file_operations saa_fops = { + .owner = THIS_MODULE, + .open = saa_open, + .release = saa_release, + .ioctl = saa_ioctl, + .compat_ioctl = v4l_compat_ioctl32, + .read = saa_read, + .llseek = no_llseek, + .write = saa_write, + .mmap = saa_mmap, }; /* template for video_device-structure */ -static struct video_device saa_template = -{ - .name = "SAA7146A", - .type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY, - .hardware = VID_HARDWARE_SAA7146, - .fops = &saa_fops, - .minor = -1, +static struct video_device saa_template = { + .name = "SAA7146A", + .type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY, + .hardware = VID_HARDWARE_SAA7146, + .fops = &saa_fops, + .minor = -1, }; -static int configure_saa7146(struct pci_dev *dev, int num) +static int __devinit configure_saa7146(struct pci_dev *pdev, int num) { - int result; - struct saa7146 *saa; + int retval; + struct saa7146 *saa = pci_get_drvdata(pdev); - saa = &saa7146s[num]; - saa->endmarkhead = saa->endmarktail = 0; saa->win.x = saa->win.y = 0; saa->win.width = saa->win.cropwidth = 720; @@ -2012,7 +1941,6 @@ static int configure_saa7146(struct pci_dev *dev, int num) saa->picture.contrast = 38768; saa->picture.colour = 32768; saa->cap = 0; - saa->dev = dev; saa->nr = num; saa->playmode = VID_PLAY_NORMAL; memset(saa->boardcfg, 0, 64); /* clear board config area */ @@ -2032,45 +1960,59 @@ static int configure_saa7146(struct pci_dev *dev, int num) init_waitqueue_head(&saa->vidq); spin_lock_init(&saa->lock); - if (pci_enable_device(dev)) - return -EIO; - - saa->id = dev->device; - saa->irq = dev->irq; + retval = pci_enable_device(pdev); + if (retval) { + dev_err(&pdev->dev, "%d: pci_enable_device failed!\n", num); + goto err; + } + + saa->id = pdev->device; + saa->irq = pdev->irq; saa->video_dev.minor = -1; - saa->saa7146_adr = pci_resource_start(dev, 0); - pci_read_config_byte(dev, PCI_CLASS_REVISION, &saa->revision); + saa->saa7146_adr = pci_resource_start(pdev, 0); + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &saa->revision); saa->saa7146_mem = ioremap(saa->saa7146_adr, 0x200); - if (!saa->saa7146_mem) - return -EIO; + if (saa->saa7146_mem == NULL) { + dev_err(&pdev->dev, "%d: ioremap failed!\n", num); + retval = -EIO; + goto err; + } memcpy(&saa->video_dev, &saa_template, sizeof(saa_template)); saawrite(0, SAA7146_IER); /* turn off all interrupts */ - result = request_irq(saa->irq, saa7146_irq, - SA_SHIRQ | SA_INTERRUPT, "stradis", (void *) saa); - if (result == -EINVAL) - printk(KERN_ERR "stradis%d: Bad irq number or handler\n", - num); - if (result == -EBUSY) - printk(KERN_ERR "stradis%d: IRQ %ld busy, change your PnP" - " config in BIOS\n", num, saa->irq); - if (result < 0) { - iounmap(saa->saa7146_mem); - return result; - } - pci_set_master(dev); - if (video_register_device(&saa->video_dev, VFL_TYPE_GRABBER, video_nr) < 0) { - iounmap(saa->saa7146_mem); - return -1; + + retval = request_irq(saa->irq, saa7146_irq, SA_SHIRQ | SA_INTERRUPT, + "stradis", saa); + if (retval == -EINVAL) + dev_err(&pdev->dev, "%d: Bad irq number or handler\n", num); + else if (retval == -EBUSY) + dev_err(&pdev->dev, "%d: IRQ %ld busy, change your PnP config " + "in BIOS\n", num, saa->irq); + if (retval < 0) + goto errio; + + pci_set_master(pdev); + retval = video_register_device(&saa->video_dev, VFL_TYPE_GRABBER, + video_nr); + if (retval < 0) { + dev_err(&pdev->dev, "%d: error in registering video device!\n", + num); + goto errio; } + return 0; +errio: + iounmap(saa->saa7146_mem); +err: + return retval; } -static int init_saa7146(int i) +static int __devinit init_saa7146(struct pci_dev *pdev) { - struct saa7146 *saa = &saa7146s[i]; + struct saa7146 *saa = pci_get_drvdata(pdev); + memset(saa, 0, sizeof(*saa)); saa->user = 0; /* reset the saa7146 */ saawrite(0xffff0000, SAA7146_MC1); @@ -2088,23 +2030,23 @@ static int init_saa7146(int i) saawrite(0x00000000, SAA7146_DD1_STREAM_A); saawrite(0x00000000, SAA7146_BRS_CTRL); saawrite(0x80400040, SAA7146_BCS_CTRL); - saawrite(0x0000e000 /*| (1<<29)*/, SAA7146_HPS_CTRL); + saawrite(0x0000e000 /*| (1<<29) */ , SAA7146_HPS_CTRL); saawrite(0x00000060, SAA7146_CLIP_FORMAT_CTRL); saawrite(0x00000000, SAA7146_ACON1); saawrite(0x00000000, SAA7146_ACON2); saawrite(0x00000600, SAA7146_I2C_STATUS); saawrite(((SAA7146_MC2_UPLD_D1_B | SAA7146_MC2_UPLD_D1_A | - SAA7146_MC2_UPLD_BRS | SAA7146_MC2_UPLD_HPS_H | - SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_DMA2 | - SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_I2C) << 16) | 0xffff, - SAA7146_MC2); + SAA7146_MC2_UPLD_BRS | SAA7146_MC2_UPLD_HPS_H | + SAA7146_MC2_UPLD_HPS_V | SAA7146_MC2_UPLD_DMA2 | + SAA7146_MC2_UPLD_DMA1 | SAA7146_MC2_UPLD_I2C) << 16) | 0xffff, + SAA7146_MC2); /* setup arbitration control registers */ saawrite(0x1412121a, SAA7146_PCI_BT_V1); /* allocate 32k dma buffer + 4k for page table */ if ((saa->dmadebi = kmalloc(32768 + 4096, GFP_KERNEL)) == NULL) { - printk(KERN_ERR "stradis%d: debi kmalloc failed\n", i); - return -1; + dev_err(&pdev->dev, "%d: debi kmalloc failed\n", saa->nr); + goto err; } #if 0 saa->pagedebi = saa->dmadebi + 32768; /* top 4k is for mmu */ @@ -2114,133 +2056,162 @@ static int init_saa7146(int i) #endif saa->audhead = saa->vidhead = saa->osdhead = 0; saa->audtail = saa->vidtail = saa->osdtail = 0; - if (saa->vidbuf == NULL) - if ((saa->vidbuf = vmalloc(524288)) == NULL) { - printk(KERN_ERR "stradis%d: malloc failed\n", saa->nr); - return -ENOMEM; - } - if (saa->audbuf == NULL) - if ((saa->audbuf = vmalloc(65536)) == NULL) { - printk(KERN_ERR "stradis%d: malloc failed\n", saa->nr); - vfree(saa->vidbuf); - saa->vidbuf = NULL; - return -ENOMEM; - } - if (saa->osdbuf == NULL) - if ((saa->osdbuf = vmalloc(131072)) == NULL) { - printk(KERN_ERR "stradis%d: malloc failed\n", saa->nr); - vfree(saa->vidbuf); - vfree(saa->audbuf); - saa->vidbuf = saa->audbuf = NULL; - return -ENOMEM; - } + if (saa->vidbuf == NULL && (saa->vidbuf = vmalloc(524288)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto err; + } + if (saa->audbuf == NULL && (saa->audbuf = vmalloc(65536)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto errvid; + } + if (saa->osdbuf == NULL && (saa->osdbuf = vmalloc(131072)) == NULL) { + dev_err(&pdev->dev, "%d: malloc failed\n", saa->nr); + goto erraud; + } /* allocate 81920 byte buffer for clipping */ - if ((saa->dmavid2 = kmalloc(VIDEO_CLIPMAP_SIZE, GFP_KERNEL)) == NULL) { - printk(KERN_ERR "stradis%d: clip kmalloc failed\n", saa->nr); - vfree(saa->vidbuf); - vfree(saa->audbuf); - vfree(saa->osdbuf); - saa->vidbuf = saa->audbuf = saa->osdbuf = NULL; - saa->dmavid2 = NULL; - return -1; + if ((saa->dmavid2 = kzalloc(VIDEO_CLIPMAP_SIZE, GFP_KERNEL)) == NULL) { + dev_err(&pdev->dev, "%d: clip kmalloc failed\n", saa->nr); + goto errosd; } - memset(saa->dmavid2, 0x00, VIDEO_CLIPMAP_SIZE); /* clip everything */ /* setup clipping registers */ saawrite(virt_to_bus(saa->dmavid2), SAA7146_BASE_EVEN2); saawrite(virt_to_bus(saa->dmavid2) + 128, SAA7146_BASE_ODD2); saawrite(virt_to_bus(saa->dmavid2) + VIDEO_CLIPMAP_SIZE, - SAA7146_PROT_ADDR2); + SAA7146_PROT_ADDR2); saawrite(256, SAA7146_PITCH2); - saawrite(4, SAA7146_PAGE2); /* dma direction: read, no byteswap */ + saawrite(4, SAA7146_PAGE2); /* dma direction: read, no byteswap */ saawrite(((SAA7146_MC2_UPLD_DMA2) << 16) | SAA7146_MC2_UPLD_DMA2, SAA7146_MC2); I2CBusScan(saa); + return 0; +errosd: + vfree(saa->osdbuf); + saa->osdbuf = NULL; +erraud: + vfree(saa->audbuf); + saa->audbuf = NULL; +errvid: + vfree(saa->vidbuf); + saa->vidbuf = NULL; +err: + return -ENOMEM; } -static void release_saa(void) +static void stradis_release_saa(struct pci_dev *pdev) { u8 command; - int i; - struct saa7146 *saa; + struct saa7146 *saa = pci_get_drvdata(pdev); - for (i = 0; i < saa_num; i++) { - saa = &saa7146s[i]; + /* turn off all capturing, DMA and IRQs */ + saawrite(0xffff0000, SAA7146_MC1); /* reset chip */ + saawrite(0, SAA7146_MC2); + saawrite(0, SAA7146_IER); + saawrite(0xffffffffUL, SAA7146_ISR); + + /* disable PCI bus-mastering */ + pci_read_config_byte(pdev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MASTER; + pci_write_config_byte(pdev, PCI_COMMAND, command); + + /* unmap and free memory */ + saa->audhead = saa->audtail = saa->osdhead = 0; + saa->vidhead = saa->vidtail = saa->osdtail = 0; + vfree(saa->vidbuf); + vfree(saa->audbuf); + vfree(saa->osdbuf); + kfree(saa->dmavid2); + saa->audbuf = saa->vidbuf = saa->osdbuf = NULL; + saa->dmavid2 = NULL; + kfree(saa->dmadebi); + kfree(saa->dmavid1); + kfree(saa->dmavid3); + kfree(saa->dmaa1in); + kfree(saa->dmaa1out); + kfree(saa->dmaa2in); + kfree(saa->dmaa2out); + kfree(saa->dmaRPS1); + kfree(saa->dmaRPS2); + free_irq(saa->irq, saa); + if (saa->saa7146_mem) + iounmap(saa->saa7146_mem); + if (saa->video_dev.minor != -1) + video_unregister_device(&saa->video_dev); +} - /* turn off all capturing, DMA and IRQs */ - saawrite(0xffff0000, SAA7146_MC1); /* reset chip */ - saawrite(0, SAA7146_MC2); - saawrite(0, SAA7146_IER); - saawrite(0xffffffffUL, SAA7146_ISR); - - /* disable PCI bus-mastering */ - pci_read_config_byte(saa->dev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MASTER; - pci_write_config_byte(saa->dev, PCI_COMMAND, command); - - /* unmap and free memory */ - saa->audhead = saa->audtail = saa->osdhead = 0; - saa->vidhead = saa->vidtail = saa->osdtail = 0; - vfree(saa->vidbuf); - vfree(saa->audbuf); - vfree(saa->osdbuf); - kfree(saa->dmavid2); - saa->audbuf = saa->vidbuf = saa->osdbuf = NULL; - saa->dmavid2 = NULL; - kfree(saa->dmadebi); - kfree(saa->dmavid1); - kfree(saa->dmavid3); - kfree(saa->dmaa1in); - kfree(saa->dmaa1out); - kfree(saa->dmaa2in); - kfree(saa->dmaa2out); - kfree(saa->dmaRPS1); - kfree(saa->dmaRPS2); - free_irq(saa->irq, saa); - if (saa->saa7146_mem) - iounmap(saa->saa7146_mem); - if (saa->video_dev.minor != -1) - video_unregister_device(&saa->video_dev); +static int __devinit stradis_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int retval = -EINVAL; + + if (saa_num >= SAA7146_MAX) + goto err; + + if (!pdev->subsystem_vendor) + dev_info(&pdev->dev, "%d: rev1 decoder\n", saa_num); + else + dev_info(&pdev->dev, "%d: SDM2xx found\n", saa_num); + + pci_set_drvdata(pdev, &saa7146s[saa_num]); + + retval = configure_saa7146(pdev, saa_num); + if (retval) { + dev_err(&pdev->dev, "%d: error in configuring\n", saa_num); + goto err; + } + + if (init_saa7146(pdev) < 0) { + dev_err(&pdev->dev, "%d: error in initialization\n", saa_num); + retval = -EIO; + goto errrel; } + + saa_num++; + + return 0; +errrel: + stradis_release_saa(pdev); +err: + return retval; +} + +static void __devexit stradis_remove(struct pci_dev *pdev) +{ + stradis_release_saa(pdev); } +static struct pci_device_id stradis_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146) }, + { 0 } +}; + +MODULE_DEVICE_TABLE(pci, stradis_pci_tbl); + +static struct pci_driver stradis_driver = { + .name = "stradis", + .id_table = stradis_pci_tbl, + .probe = stradis_probe, + .remove = __devexit_p(stradis_remove) +}; -static int __init stradis_init (void) +int __init stradis_init(void) { - struct pci_dev *dev = NULL; - int result = 0, i; + int retval; saa_num = 0; - while ((dev = pci_find_device(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, dev))) { - if (!dev->subsystem_vendor) - printk(KERN_INFO "stradis%d: rev1 decoder\n", saa_num); - else - printk(KERN_INFO "stradis%d: SDM2xx found\n", saa_num); - result = configure_saa7146(dev, saa_num++); - if (result) - return result; - } - if (saa_num) - printk(KERN_INFO "stradis: %d card(s) found.\n", saa_num); - else - return -EINVAL; - for (i = 0; i < saa_num; i++) - if (init_saa7146(i) < 0) { - release_saa(); - return -EIO; - } - return 0; -} + retval = pci_register_driver(&stradis_driver); + if (retval) + printk(KERN_ERR "stradis: Unable to register pci driver.\n"); + return retval; +} -static void __exit stradis_exit (void) +void __exit stradis_exit(void) { - release_saa(); + pci_unregister_driver(&stradis_driver); printk(KERN_INFO "stradis: module cleanup complete\n"); } - module_init(stradis_init); module_exit(stradis_exit); - diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index d32737dd214..fc3d5824eff 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -50,6 +50,7 @@ #include "bttv.h" #include <media/audiochip.h> +#include <media/v4l2-common.h> #ifndef VIDEO_AUDIO_BALANCE # define VIDEO_AUDIO_BALANCE 32 @@ -90,9 +91,6 @@ struct tda7432 { static struct i2c_driver driver; static struct i2c_client client_template; -#define dprintk if (debug) printk -#define d2printk if (debug > 1) printk - /* The TDA7432 is made by STS-Thompson * http://www.st.com * http://us.st.com/stonline/books/pdf/docs/4056.pdf @@ -229,12 +227,12 @@ static struct i2c_client client_template; static int tda7432_write(struct i2c_client *client, int subaddr, int val) { unsigned char buffer[2]; - d2printk("tda7432: In tda7432_write\n"); - dprintk("tda7432: Writing %d 0x%x\n", subaddr, val); + v4l_dbg(2, debug,client,"In tda7432_write\n"); + v4l_dbg(1, debug,client,"Writing %d 0x%x\n", subaddr, val); buffer[0] = subaddr; buffer[1] = val; if (2 != i2c_master_send(client,buffer,2)) { - printk(KERN_WARNING "tda7432: I/O error, trying (write %d 0x%x)\n", + v4l_err(client,"I/O error, trying (write %d 0x%x)\n", subaddr, val); return -1; } @@ -247,9 +245,9 @@ static int tda7432_set(struct i2c_client *client) { struct tda7432 *t = i2c_get_clientdata(client); unsigned char buf[16]; - d2printk("tda7432: In tda7432_set\n"); + v4l_dbg(2, debug,client,"In tda7432_set\n"); - dprintk(KERN_INFO + v4l_dbg(1, debug,client, "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", t->input,t->volume,t->bass,t->treble,t->lf,t->lr,t->rf,t->rr,t->loud); buf[0] = TDA7432_IN; @@ -263,7 +261,7 @@ static int tda7432_set(struct i2c_client *client) buf[8] = t->rr; buf[9] = t->loud; if (10 != i2c_master_send(client,buf,10)) { - printk(KERN_WARNING "tda7432: I/O error, trying tda7432_set\n"); + v4l_err(client,"I/O error, trying tda7432_set\n"); return -1; } @@ -273,7 +271,7 @@ static int tda7432_set(struct i2c_client *client) static void do_tda7432_init(struct i2c_client *client) { struct tda7432 *t = i2c_get_clientdata(client); - d2printk("tda7432: In tda7432_init\n"); + v4l_dbg(2, debug,client,"In tda7432_init\n"); t->input = TDA7432_STEREO_IN | /* Main (stereo) input */ TDA7432_BASS_SYM | /* Symmetric bass cut */ @@ -301,12 +299,10 @@ static int tda7432_attach(struct i2c_adapter *adap, int addr, int kind) { struct tda7432 *t; struct i2c_client *client; - d2printk("tda7432: In tda7432_attach\n"); - t = kmalloc(sizeof *t,GFP_KERNEL); + t = kzalloc(sizeof *t,GFP_KERNEL); if (!t) return -ENOMEM; - memset(t,0,sizeof *t); client = &t->c; memcpy(client,&client_template,sizeof(struct i2c_client)); @@ -315,21 +311,16 @@ static int tda7432_attach(struct i2c_adapter *adap, int addr, int kind) i2c_set_clientdata(client, t); do_tda7432_init(client); - printk(KERN_INFO "tda7432: init\n"); - i2c_attach_client(client); + + v4l_info(client, "chip found @ 0x%x (%s)\n", addr << 1, adap->name); return 0; } static int tda7432_probe(struct i2c_adapter *adap) { -#ifdef I2C_CLASS_TV_ANALOG if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, tda7432_attach); -#else - if (adap->id == I2C_HW_B_BT848) - return i2c_probe(adap, &addr_data, tda7432_attach); -#endif return 0; } @@ -348,7 +339,9 @@ static int tda7432_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct tda7432 *t = i2c_get_clientdata(client); - d2printk("tda7432: In tda7432_command\n"); + v4l_dbg(2, debug,client,"In tda7432_command\n"); + if (debug>1) + v4l_i2c_print_ioctl(client,cmd); switch (cmd) { /* --- v4l ioctls --- */ @@ -359,7 +352,6 @@ static int tda7432_command(struct i2c_client *client, case VIDIOCGAUDIO: { struct video_audio *va = arg; - dprintk("tda7432: VIDIOCGAUDIO\n"); va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_BASS | @@ -414,7 +406,6 @@ static int tda7432_command(struct i2c_client *client, case VIDIOCSAUDIO: { struct video_audio *va = arg; - dprintk("tda7432: VIDEOCSAUDIO\n"); if(va->flags & VIDEO_AUDIO_VOLUME){ if(!maxvol){ /* max +20db */ @@ -490,21 +481,16 @@ static int tda7432_command(struct i2c_client *client, } /* end of VIDEOCSAUDIO case */ - default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */ - - /* nothing */ - d2printk("tda7432: Default\n"); - } /* end of (cmd) switch */ return 0; } static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "i2c tda7432 driver", + .driver = { + .name = "tda7432", + }, .id = I2C_DRIVERID_TDA7432, - .flags = I2C_DF_NOTIFY, .attach_adapter = tda7432_probe, .detach_client = tda7432_detach, .command = tda7432_command, @@ -519,7 +505,7 @@ static struct i2c_client client_template = static int __init tda7432_init(void) { if ( (loudness < 0) || (loudness > 15) ) { - printk(KERN_ERR "tda7432: loudness parameter must be between 0 and 15\n"); + printk(KERN_ERR "loudness parameter must be between 0 and 15\n"); return -EINVAL; } diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c index b2dfe07e9f9..7b4fb282ac8 100644 --- a/drivers/media/video/tda8290.c +++ b/drivers/media/video/tda8290.c @@ -398,14 +398,8 @@ static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq) return 0; } - /*---------------------------------------------------------------------*/ -#define V4L2_STD_MN (V4L2_STD_PAL_M|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc|V4L2_STD_NTSC) -#define V4L2_STD_B (V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_SECAM_B) -#define V4L2_STD_GH (V4L2_STD_PAL_G|V4L2_STD_PAL_H|V4L2_STD_SECAM_G|V4L2_STD_SECAM_H) -#define V4L2_STD_DK (V4L2_STD_PAL_DK|V4L2_STD_SECAM_DK) - static void set_audio(struct tuner *t) { char* mode; @@ -437,6 +431,10 @@ static void set_audio(struct tuner *t) t->sgIF = 124; t->tda8290_easy_mode = 0x20; mode = "L"; + } else if (t->std & V4L2_STD_SECAM_LC) { + t->sgIF = 20; + t->tda8290_easy_mode = 0x40; + mode = "LC"; } tuner_dbg("setting tda8290 to system %s\n", mode); } @@ -569,8 +567,8 @@ int tda8290_init(struct i2c_client *c) } tuner_info("tuner: type set to %s\n", c->name); - t->tv_freq = set_tv_freq; - t->radio_freq = set_radio_freq; + t->set_tv_freq = set_tv_freq; + t->set_radio_freq = set_radio_freq; t->has_signal = has_signal; t->standby = standby; t->tda827x_lpsel = 0; diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 1794686612c..ed4c04119cc 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -34,7 +34,7 @@ static int debug = 0; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); #define dprintk(args...) \ - do { if (debug) { printk("%s: %s()[%d]: ",__stringify(KBUILD_MODNAME), __FUNCTION__, __LINE__); printk(args); } } while (0) + do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __FUNCTION__, __LINE__); printk(args); } } while (0) #define SWITCH 0x00 #define LEVEL_ADJUST 0x02 @@ -221,10 +221,10 @@ static int detach(struct i2c_client *client) } static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "tda9840", + .driver = { + .name = "tda9840", + }, .id = I2C_DRIVERID_TDA9840, - .flags = I2C_DF_NOTIFY, .attach_adapter = attach, .detach_client = detach, .command = command, diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index a5e37dc91f3..ef98c498225 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -232,10 +232,9 @@ static int tda9875_attach(struct i2c_adapter *adap, int addr, int kind) struct i2c_client *client; dprintk("In tda9875_attach\n"); - t = kmalloc(sizeof *t,GFP_KERNEL); + t = kzalloc(sizeof *t,GFP_KERNEL); if (!t) return -ENOMEM; - memset(t,0,sizeof *t); client = &t->c; memcpy(client,&client_template,sizeof(struct i2c_client)); @@ -257,13 +256,8 @@ static int tda9875_attach(struct i2c_adapter *adap, int addr, int kind) static int tda9875_probe(struct i2c_adapter *adap) { -#ifdef I2C_CLASS_TV_ANALOG if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, tda9875_attach); -#else - if (adap->id == I2C_HW_B_BT848) - return i2c_probe(adap, &addr_data, tda9875_attach); -#endif return 0; } @@ -372,10 +366,10 @@ static int tda9875_command(struct i2c_client *client, static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "i2c tda9875 driver", + .driver = { + .name = "tda9875", + }, .id = I2C_DRIVERID_TDA9875, - .flags = I2C_DF_NOTIFY, .attach_adapter = tda9875_probe, .detach_client = tda9875_detach, .command = tda9875_command, diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index 4249127c0a1..5815649bdc7 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -9,9 +9,10 @@ #include <linux/slab.h> #include <linux/delay.h> -#include <media/audiochip.h> +#include <media/v4l2-common.h> #include <media/tuner.h> + /* Chips: TDA9885 (PAL, NTSC) TDA9886 (PAL, SECAM, NTSC) @@ -56,7 +57,6 @@ struct tda9887 { v4l2_std_id std; enum tuner_mode mode; unsigned int config; - unsigned int pinnacle_id; unsigned int using_v4l2; unsigned int radio_mode; unsigned char data[4]; @@ -114,6 +114,9 @@ static struct i2c_client client_template; #define cAudioGain0 0x00 // bit c7 #define cAudioGain6 0x80 // bit c7 +#define cTopMask 0x1f // bit c0:4 +#define cTopPalSecamDefault 0x14 // bit c0:4 +#define cTopNtscRadioDefault 0x10 // bit c0:4 //// third reg (e) #define cAudioIF_4_5 0x00 // bit e0:1 @@ -145,13 +148,15 @@ static struct i2c_client client_template; static struct tvnorm tvnorms[] = { { - .std = V4L2_STD_PAL_BG, - .name = "PAL-BG", + .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N, + .name = "PAL-BGHN", .b = ( cNegativeFmTV | cQSS ), .c = ( cDeemphasisON | - cDeemphasis50 ), - .e = ( cAudioIF_5_5 | + cDeemphasis50 | + cTopPalSecamDefault), + .e = ( cGating_36 | + cAudioIF_5_5 | cVideoIF_38_90 ), },{ .std = V4L2_STD_PAL_I, @@ -159,8 +164,10 @@ static struct tvnorm tvnorms[] = { .b = ( cNegativeFmTV | cQSS ), .c = ( cDeemphasisON | - cDeemphasis50 ), - .e = ( cAudioIF_6_0 | + cDeemphasis50 | + cTopPalSecamDefault), + .e = ( cGating_36 | + cAudioIF_6_0 | cVideoIF_38_90 ), },{ .std = V4L2_STD_PAL_DK, @@ -168,52 +175,80 @@ static struct tvnorm tvnorms[] = { .b = ( cNegativeFmTV | cQSS ), .c = ( cDeemphasisON | - cDeemphasis50 ), - .e = ( cAudioIF_6_5 | - cVideoIF_38_00 ), + cDeemphasis50 | + cTopPalSecamDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_38_90 ), },{ - .std = V4L2_STD_PAL_M | V4L2_STD_PAL_N, - .name = "PAL-M/N", + .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc, + .name = "PAL-M/Nc", .b = ( cNegativeFmTV | cQSS ), .c = ( cDeemphasisON | - cDeemphasis75 ), - .e = ( cAudioIF_4_5 | + cDeemphasis75 | + cTopNtscRadioDefault), + .e = ( cGating_36 | + cAudioIF_4_5 | cVideoIF_45_75 ), },{ + .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, + .name = "SECAM-BGH", + .b = ( cPositiveAmTV | + cQSS ), + .c = ( cTopPalSecamDefault), + .e = ( cGating_36 | + cAudioIF_5_5 | + cVideoIF_38_90 ), + },{ .std = V4L2_STD_SECAM_L, .name = "SECAM-L", .b = ( cPositiveAmTV | cQSS ), + .c = ( cTopPalSecamDefault), .e = ( cGating_36 | cAudioIF_6_5 | cVideoIF_38_90 ), },{ + .std = V4L2_STD_SECAM_LC, + .name = "SECAM-L'", + .b = ( cOutputPort2Inactive | + cPositiveAmTV | + cQSS ), + .c = ( cTopPalSecamDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_33_90 ), + },{ .std = V4L2_STD_SECAM_DK, .name = "SECAM-DK", .b = ( cNegativeFmTV | cQSS ), .c = ( cDeemphasisON | - cDeemphasis50 ), - .e = ( cAudioIF_6_5 | - cVideoIF_38_00 ), + cDeemphasis50 | + cTopPalSecamDefault), + .e = ( cGating_36 | + cAudioIF_6_5 | + cVideoIF_38_90 ), },{ .std = V4L2_STD_NTSC_M, .name = "NTSC-M", .b = ( cNegativeFmTV | cQSS ), .c = ( cDeemphasisON | - cDeemphasis75 ), + cDeemphasis75 | + cTopNtscRadioDefault), .e = ( cGating_36 | cAudioIF_4_5 | cVideoIF_45_75 ), },{ .std = V4L2_STD_NTSC_M_JP, - .name = "NTSC-JP", + .name = "NTSC-M-JP", .b = ( cNegativeFmTV | cQSS ), .c = ( cDeemphasisON | - cDeemphasis50 ), + cDeemphasis50 | + cTopNtscRadioDefault), .e = ( cGating_36 | cAudioIF_4_5 | cVideoIF_58_75 ), @@ -225,8 +260,10 @@ static struct tvnorm radio_stereo = { .b = ( cFmRadio | cQSS ), .c = ( cDeemphasisOFF | - cAudioGain6 ), - .e = ( cAudioIF_5_5 | + cAudioGain6 | + cTopNtscRadioDefault), + .e = ( cTunerGainLow | + cAudioIF_5_5 | cRadioIF_38_90 ), }; @@ -235,8 +272,10 @@ static struct tvnorm radio_mono = { .b = ( cFmRadio | cQSS ), .c = ( cDeemphasisON | - cDeemphasis50), - .e = ( cAudioIF_5_5 | + cDeemphasis75 | + cTopNtscRadioDefault), + .e = ( cTunerGainLow | + cAudioIF_5_5 | cRadioIF_38_90 ), }; @@ -399,7 +438,8 @@ static int tda9887_set_tvnorm(struct tda9887 *t, char *buf) static unsigned int port1 = UNSET; static unsigned int port2 = UNSET; static unsigned int qss = UNSET; -static unsigned int adjust = 0x10; +static unsigned int adjust = UNSET; + module_param(port1, int, 0644); module_param(port2, int, 0644); module_param(qss, int, 0644); @@ -427,8 +467,10 @@ static int tda9887_set_insmod(struct tda9887 *t, char *buf) buf[1] &= ~cQSS; } - if (adjust >= 0x00 && adjust < 0x20) + if (adjust >= 0x00 && adjust < 0x20) { + buf[2] &= ~cTopMask; buf[2] |= adjust; + } return 0; } @@ -464,6 +506,10 @@ static int tda9887_set_config(struct tda9887 *t, char *buf) break; } } + if (t->config & TDA9887_TOP_SET) { + buf[2] &= ~cTopMask; + buf[2] |= (t->config >> 8) & cTopMask; + } if ((t->config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC)) buf[1] &= ~cQSS; return 0; @@ -471,38 +517,13 @@ static int tda9887_set_config(struct tda9887 *t, char *buf) /* ---------------------------------------------------------------------- */ -static int tda9887_set_pinnacle(struct tda9887 *t, char *buf) -{ - unsigned int bCarrierMode = UNSET; - - if (t->std & V4L2_STD_625_50) { - if ((1 == t->pinnacle_id) || (7 == t->pinnacle_id)) { - bCarrierMode = cIntercarrier; - } else { - bCarrierMode = cQSS; - } - } - if (t->std & V4L2_STD_525_60) { - if ((5 == t->pinnacle_id) || (6 == t->pinnacle_id)) { - bCarrierMode = cIntercarrier; - } else { - bCarrierMode = cQSS; - } - } +static char pal[] = "--"; +static char secam[] = "--"; +static char ntsc[] = "-"; - if (bCarrierMode != UNSET) { - buf[1] &= ~0x04; - buf[1] |= bCarrierMode; - } - return 0; -} - -/* ---------------------------------------------------------------------- */ - -static char pal[] = "-"; module_param_string(pal, pal, sizeof(pal), 0644); -static char secam[] = "-"; module_param_string(secam, secam, sizeof(secam), 0644); +module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); static int tda9887_fixup_std(struct tda9887 *t) { @@ -513,8 +534,17 @@ static int tda9887_fixup_std(struct tda9887 *t) case 'B': case 'g': case 'G': - tda9887_dbg("insmod fixup: PAL => PAL-BG\n"); - t->std = V4L2_STD_PAL_BG; + case 'h': + case 'H': + case 'n': + case 'N': + if (pal[1] == 'c' || pal[1] == 'C') { + tda9887_dbg("insmod fixup: PAL => PAL-Nc\n"); + t->std = V4L2_STD_PAL_Nc; + } else { + tda9887_dbg("insmod fixup: PAL => PAL-BGHN\n"); + t->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N; + } break; case 'i': case 'I': @@ -528,6 +558,11 @@ static int tda9887_fixup_std(struct tda9887 *t) tda9887_dbg("insmod fixup: PAL => PAL-DK\n"); t->std = V4L2_STD_PAL_DK; break; + case 'm': + case 'M': + tda9887_dbg("insmod fixup: PAL => PAL-M\n"); + t->std = V4L2_STD_PAL_M; + break; case '-': /* default parameter, do nothing */ break; @@ -538,6 +573,15 @@ static int tda9887_fixup_std(struct tda9887 *t) } if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) { switch (secam[0]) { + case 'b': + case 'B': + case 'g': + case 'G': + case 'h': + case 'H': + tda9887_dbg("insmod fixup: SECAM => SECAM-BGH\n"); + t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; + break; case 'd': case 'D': case 'k': @@ -547,8 +591,13 @@ static int tda9887_fixup_std(struct tda9887 *t) break; case 'l': case 'L': - tda9887_dbg("insmod fixup: SECAM => SECAM-L\n"); - t->std = V4L2_STD_SECAM_L; + if (secam[1] == 'c' || secam[1] == 'C') { + tda9887_dbg("insmod fixup: SECAM => SECAM-L'\n"); + t->std = V4L2_STD_SECAM_LC; + } else { + tda9887_dbg("insmod fixup: SECAM => SECAM-L\n"); + t->std = V4L2_STD_SECAM_L; + } break; case '-': /* default parameter, do nothing */ @@ -558,6 +607,26 @@ static int tda9887_fixup_std(struct tda9887 *t) break; } } + if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) { + switch (ntsc[0]) { + case 'm': + case 'M': + tda9887_dbg("insmod fixup: NTSC => NTSC-M\n"); + t->std = V4L2_STD_NTSC_M; + break; + case 'j': + case 'J': + tda9887_dbg("insmod fixup: NTSC => NTSC_M_JP\n"); + t->std = V4L2_STD_NTSC_M_JP; + break; + case '-': + /* default parameter, do nothing */ + break; + default: + tda9887_info("ntsc= argument not recognised\n"); + break; + } + } return 0; } @@ -580,12 +649,22 @@ static int tda9887_configure(struct tda9887 *t) memset(t->data,0,sizeof(t->data)); tda9887_set_tvnorm(t,t->data); + /* A note on the port settings: + These settings tend to depend on the specifics of the board. + By default they are set to inactive (bit value 1) by this driver, + overwriting any changes made by the tvnorm. This means that it + is the responsibility of the module using the tda9887 to set + these values in case of changes in the tvnorm. + In many cases port 2 should be made active (0) when selecting + SECAM-L, and port 2 should remain inactive (1) for SECAM-L'. + + For the other standards the tda9887 application note says that + the ports should be set to active (0), but, again, that may + differ depending on the precise hardware configuration. + */ t->data[1] |= cOutputPort1Inactive; t->data[1] |= cOutputPort2Inactive; - if (UNSET != t->pinnacle_id) { - tda9887_set_pinnacle(t,t->data); - } tda9887_set_config(t,t->data); tda9887_set_insmod(t,t->data); @@ -593,7 +672,6 @@ static int tda9887_configure(struct tda9887 *t) t->data[1] |= cForcedMuteAudioON; } - tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n", t->data[1],t->data[2],t->data[3]); if (debug > 1) @@ -618,13 +696,11 @@ static int tda9887_attach(struct i2c_adapter *adap, int addr, int kind) client_template.adapter = adap; client_template.addr = addr; - if (NULL == (t = kmalloc(sizeof(*t), GFP_KERNEL))) + if (NULL == (t = kzalloc(sizeof(*t), GFP_KERNEL))) return -ENOMEM; - memset(t,0,sizeof(*t)); t->client = client_template; t->std = 0; - t->pinnacle_id = UNSET; t->radio_mode = V4L2_TUNER_MODE_STEREO; tda9887_info("chip found @ 0x%x (%s)\n", addr<<1, adap->name); @@ -637,18 +713,8 @@ static int tda9887_attach(struct i2c_adapter *adap, int addr, int kind) static int tda9887_probe(struct i2c_adapter *adap) { -#ifdef I2C_CLASS_TV_ANALOG if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, tda9887_attach); -#else - switch (adap->id) { - case I2C_HW_B_BT848: - case I2C_HW_B_RIVA: - case I2C_HW_SAA7134: - return i2c_probe(adap, &addr_data, tda9887_attach); - break; - } -#endif return 0; } @@ -688,14 +754,6 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) tda9887_configure(t); break; } - case AUDC_CONFIG_PINNACLE: - { - int *i = arg; - - t->pinnacle_id = *i; - tda9887_configure(t); - break; - } case TDA9887_SET_CONFIG: { int *i = arg; @@ -786,7 +844,7 @@ tda9887_command(struct i2c_client *client, unsigned int cmd, void *arg) } case VIDIOC_LOG_STATUS: { - tda9887_info("Data bytes: b=%02x c=%02x e=%02x\n", t->data[1], t->data[2], t->data[3]); + tda9887_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", t->data[1], t->data[2], t->data[3]); break; } default: @@ -818,14 +876,12 @@ static int tda9887_resume(struct device * dev) /* ----------------------------------------------------------------------- */ static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "i2c tda9887 driver", - .id = -1, /* FIXME */ - .flags = I2C_DF_NOTIFY, - .attach_adapter = tda9887_probe, - .detach_client = tda9887_detach, - .command = tda9887_command, + .id = -1, /* FIXME */ + .attach_adapter = tda9887_probe, + .detach_client = tda9887_detach, + .command = tda9887_command, .driver = { + .name = "tda9887", .suspend = tda9887_suspend, .resume = tda9887_resume, }, @@ -833,8 +889,7 @@ static struct i2c_driver driver = { static struct i2c_client client_template = { .name = "tda9887", - .flags = I2C_CLIENT_ALLOW_USE, - .driver = &driver, + .driver = &driver, }; static int __init tda9887_init_module(void) diff --git a/drivers/media/video/tea5767.c b/drivers/media/video/tea5767.c index a9375ef05de..c2b98f81c19 100644 --- a/drivers/media/video/tea5767.c +++ b/drivers/media/video/tea5767.c @@ -17,6 +17,9 @@ #define PREFIX "TEA5767 " +/* from tuner-core.c */ +extern int tuner_debug; + /*****************************************************************************/ /****************************** @@ -59,7 +62,7 @@ #define TEA5767_PORT1_HIGH 0x01 -/* Forth register */ +/* Fourth register */ #define TEA5767_PORT2_HIGH 0x80 /* Chips stops working. Only I2C bus remains on */ #define TEA5767_STDBY 0x40 @@ -82,7 +85,7 @@ /* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */ #define TEA5767_SRCH_IND 0x01 -/* Fiveth register */ +/* Fifth register */ /* By activating, it will use Xtal at 13 MHz as reference for divider */ #define TEA5767_PLLREF_ENABLE 0x80 @@ -106,13 +109,13 @@ #define TEA5767_STEREO_MASK 0x80 #define TEA5767_IF_CNTR_MASK 0x7f -/* Four register */ +/* Fourth register */ #define TEA5767_ADC_LEVEL_MASK 0xf0 /* should be 0 */ #define TEA5767_CHIP_ID_MASK 0x0f -/* Fiveth register */ +/* Fifth register */ /* Reserved for future extensions */ #define TEA5767_RESERVED_MASK 0xff @@ -217,19 +220,19 @@ static void set_radio_freq(struct i2c_client *c, unsigned int frq) tuner_dbg ("TEA5767 radio HIGH LO inject xtal @ 13 MHz\n"); buffer[2] |= TEA5767_HIGH_LO_INJECT; buffer[4] |= TEA5767_PLLREF_ENABLE; - div = (frq * 4000 / 16 + 700000 + 225000 + 25000) / 50000; + div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000; break; case TEA5767_LOW_LO_13MHz: tuner_dbg ("TEA5767 radio LOW LO inject xtal @ 13 MHz\n"); buffer[4] |= TEA5767_PLLREF_ENABLE; - div = (frq * 4000 / 16 - 700000 - 225000 + 25000) / 50000; + div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000; break; case TEA5767_LOW_LO_32768: tuner_dbg ("TEA5767 radio LOW LO inject xtal @ 32,768 MHz\n"); buffer[3] |= TEA5767_XTAL_32768; /* const 700=4000*175 Khz - to adjust freq to right value */ - div = ((frq * 4000 / 16 - 700000 - 225000) + 16384) >> 15; + div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15; break; case TEA5767_HIGH_LO_32768: default: @@ -264,7 +267,7 @@ static int tea5767_signal(struct i2c_client *c) if (5 != (rc = i2c_master_recv(c, buffer, 5))) tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); - return ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << (13 - 4)); + return ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8); } static int tea5767_stereo(struct i2c_client *c) @@ -347,8 +350,8 @@ int tea5767_tuner_init(struct i2c_client *c) tuner_info("type set to %d (%s)\n", t->type, "Philips TEA5767HN FM Radio"); strlcpy(c->name, "tea5767", sizeof(c->name)); - t->tv_freq = set_tv_freq; - t->radio_freq = set_radio_freq; + t->set_tv_freq = set_tv_freq; + t->set_radio_freq = set_radio_freq; t->has_signal = tea5767_signal; t->is_stereo = tea5767_stereo; t->standby = tea5767_standby; diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c index ee3688348b6..bb35844e384 100644 --- a/drivers/media/video/tea6415c.c +++ b/drivers/media/video/tea6415c.c @@ -36,7 +36,7 @@ static int debug = 0; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); #define dprintk(args...) \ - do { if (debug) { printk("%s: %s()[%d]: ",__stringify(KBUILD_MODNAME), __FUNCTION__, __LINE__); printk(args); } } while (0) + do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __FUNCTION__, __LINE__); printk(args); } } while (0) #define TEA6415C_NUM_INPUTS 8 #define TEA6415C_NUM_OUTPUTS 6 @@ -190,10 +190,10 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) } static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "tea6415c", + .driver = { + .name = "tea6415c", + }, .id = I2C_DRIVERID_TEA6415C, - .flags = I2C_DF_NOTIFY, .attach_adapter = attach, .detach_client = detach, .command = command, diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c index 17975c19da5..4dcba5a4fff 100644 --- a/drivers/media/video/tea6420.c +++ b/drivers/media/video/tea6420.c @@ -36,7 +36,7 @@ static int debug = 0; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); #define dprintk(args...) \ - do { if (debug) { printk("%s: %s()[%d]: ",__stringify(KBUILD_MODNAME), __FUNCTION__, __LINE__); printk(args); } } while (0) + do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __FUNCTION__, __LINE__); printk(args); } } while (0) /* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */ static unsigned short normal_i2c[] = { I2C_TEA6420_1, I2C_TEA6420_2, I2C_CLIENT_END }; @@ -99,11 +99,10 @@ static int tea6420_detect(struct i2c_adapter *adapter, int address, int kind) } /* allocate memory for client structure */ - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (0 == client) { return -ENOMEM; } - memset(client, 0x0, sizeof(struct i2c_client)); /* fill client structure */ memcpy(client, &client_template, sizeof(struct i2c_client)); @@ -167,10 +166,10 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) } static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "tea6420", + .driver = { + .name = "tea6420", + }, .id = I2C_DRIVERID_TEA6420, - .flags = I2C_DF_NOTIFY, .attach_adapter = attach, .detach_client = detach, .command = command, diff --git a/drivers/media/video/tuner-3036.c b/drivers/media/video/tuner-3036.c index 79203595b9c..c4a78e7a5a5 100644 --- a/drivers/media/video/tuner-3036.c +++ b/drivers/media/video/tuner-3036.c @@ -175,10 +175,10 @@ tuner_probe(struct i2c_adapter *adap) static struct i2c_driver i2c_driver_tuner = { - .owner = THIS_MODULE, - .name = "sab3036", + .driver = { + .name = "sab3036", + }, .id = I2C_DRIVERID_SAB3036, - .flags = I2C_DF_NOTIFY, .attach_adapter = tuner_probe, .detach_client = tuner_detach, .command = tuner_command @@ -193,8 +193,7 @@ static struct i2c_client client_template = static int __init tuner3036_init(void) { - i2c_add_driver(&i2c_driver_tuner); - return 0; + return i2c_add_driver(&i2c_driver_tuner); } static void __exit diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 73c4041c35d..2995b22acb4 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -20,10 +20,9 @@ #include <linux/init.h> #include <media/tuner.h> +#include <media/v4l2-common.h> #include <media/audiochip.h> -#include "msp3400.h" - #define UNSET (-1U) /* standard i2c insmod options */ @@ -38,21 +37,30 @@ I2C_CLIENT_INSMOD; /* insmod options used at init time => read/only */ static unsigned int addr = 0; -module_param(addr, int, 0444); - static unsigned int no_autodetect = 0; -module_param(no_autodetect, int, 0444); - static unsigned int show_i2c = 0; -module_param(show_i2c, int, 0444); /* insmod options used at runtime => read/write */ -unsigned int tuner_debug = 0; -module_param(tuner_debug, int, 0644); +static unsigned int tuner_debug_old = 0; +int tuner_debug = 0; static unsigned int tv_range[2] = { 44, 958 }; static unsigned int radio_range[2] = { 65, 108 }; +static char pal[] = "--"; +static char secam[] = "--"; +static char ntsc[] = "-"; + + +module_param(addr, int, 0444); +module_param(no_autodetect, int, 0444); +module_param(show_i2c, int, 0444); +/* Note: tuner_debug is deprecated and will be removed in 2.6.17 */ +module_param_named(tuner_debug,tuner_debug_old, int, 0444); +module_param_named(debug,tuner_debug, int, 0644); +module_param_string(pal, pal, sizeof(pal), 0644); +module_param_string(secam, secam, sizeof(secam), 0644); +module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); module_param_array(tv_range, int, NULL, 0644); module_param_array(radio_range, int, NULL, 0644); @@ -74,7 +82,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) tuner_warn ("tuner type not set\n"); return; } - if (NULL == t->tv_freq) { + if (NULL == t->set_tv_freq) { tuner_warn ("Tuner has no way to set tv freq\n"); return; } @@ -82,8 +90,14 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n", freq / 16, freq % 16 * 100 / 16, tv_range[0], tv_range[1]); + /* V4L2 spec: if the freq is not possible then the closest + possible value should be selected */ + if (freq < tv_range[0] * 16) + freq = tv_range[0] * 16; + else + freq = tv_range[1] * 16; } - t->tv_freq(c, freq); + t->set_tv_freq(c, freq); } static void set_radio_freq(struct i2c_client *c, unsigned int freq) @@ -94,18 +108,23 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) tuner_warn ("tuner type not set\n"); return; } - if (NULL == t->radio_freq) { + if (NULL == t->set_radio_freq) { tuner_warn ("tuner has no way to set radio frequency\n"); return; } - if (freq <= radio_range[0] * 16000 || freq >= radio_range[1] * 16000) { + if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) { tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n", freq / 16000, freq % 16000 * 100 / 16000, radio_range[0], radio_range[1]); + /* V4L2 spec: if the freq is not possible then the closest + possible value should be selected */ + if (freq < radio_range[0] * 16000) + freq = radio_range[0] * 16000; + else + freq = radio_range[1] * 16000; } - t->radio_freq(c, freq); - return; + t->set_radio_freq(c, freq); } static void set_freq(struct i2c_client *c, unsigned long freq) @@ -117,15 +136,16 @@ static void set_freq(struct i2c_client *c, unsigned long freq) tuner_dbg("radio freq set to %lu.%02lu\n", freq / 16000, freq % 16000 * 100 / 16000); set_radio_freq(c, freq); + t->radio_freq = freq; break; case V4L2_TUNER_ANALOG_TV: case V4L2_TUNER_DIGITAL_TV: tuner_dbg("tv freq set to %lu.%02lu\n", freq / 16, freq % 16 * 100 / 16); set_tv_freq(c, freq); + t->tv_freq = freq; break; } - t->freq = freq; } static void set_type(struct i2c_client *c, unsigned int type, @@ -204,9 +224,9 @@ static void set_type(struct i2c_client *c, unsigned int type, if (t->mode_mask == T_UNINITIALIZED) t->mode_mask = new_mode_mask; - set_freq(c, t->freq); + set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq); tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n", - c->adapter->name, c->driver->name, c->addr << 1, type, + c->adapter->name, c->driver->driver.name, c->addr << 1, type, t->mode_mask); } @@ -249,11 +269,6 @@ static inline int check_mode(struct tuner *t, char *cmd) return 0; } -static char pal[] = "-"; -module_param_string(pal, pal, sizeof(pal), 0644); -static char secam[] = "-"; -module_param_string(secam, secam, sizeof(secam), 0644); - /* get more precise norm info from insmod option */ static int tuner_fixup_std(struct tuner *t) { @@ -285,8 +300,13 @@ static int tuner_fixup_std(struct tuner *t) break; case 'N': case 'n': - tuner_dbg ("insmod fixup: PAL => PAL-N\n"); - t->std = V4L2_STD_PAL_N; + if (pal[1] == 'c' || pal[1] == 'C') { + tuner_dbg("insmod fixup: PAL => PAL-Nc\n"); + t->std = V4L2_STD_PAL_Nc; + } else { + tuner_dbg ("insmod fixup: PAL => PAL-N\n"); + t->std = V4L2_STD_PAL_N; + } break; case '-': /* default parameter, do nothing */ @@ -298,6 +318,15 @@ static int tuner_fixup_std(struct tuner *t) } if ((t->std & V4L2_STD_SECAM) == V4L2_STD_SECAM) { switch (secam[0]) { + case 'b': + case 'B': + case 'g': + case 'G': + case 'h': + case 'H': + tuner_dbg("insmod fixup: SECAM => SECAM-BGH\n"); + t->std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; + break; case 'd': case 'D': case 'k': @@ -307,8 +336,13 @@ static int tuner_fixup_std(struct tuner *t) break; case 'l': case 'L': - tuner_dbg ("insmod fixup: SECAM => SECAM-L\n"); - t->std = V4L2_STD_SECAM_L; + if ((secam[1]=='C')||(secam[1]=='c')) { + tuner_dbg ("insmod fixup: SECAM => SECAM-L'\n"); + t->std = V4L2_STD_SECAM_LC; + } else { + tuner_dbg ("insmod fixup: SECAM => SECAM-L\n"); + t->std = V4L2_STD_SECAM_L; + } break; case '-': /* default parameter, do nothing */ @@ -319,9 +353,60 @@ static int tuner_fixup_std(struct tuner *t) } } + if ((t->std & V4L2_STD_NTSC) == V4L2_STD_NTSC) { + switch (ntsc[0]) { + case 'm': + case 'M': + tuner_dbg("insmod fixup: NTSC => NTSC-M\n"); + t->std = V4L2_STD_NTSC_M; + break; + case 'j': + case 'J': + tuner_dbg("insmod fixup: NTSC => NTSC_M_JP\n"); + t->std = V4L2_STD_NTSC_M_JP; + break; + case '-': + /* default parameter, do nothing */ + break; + default: + tuner_info("ntsc= argument not recognised\n"); + break; + } + } return 0; } +static void tuner_status(struct i2c_client *client) +{ + struct tuner *t = i2c_get_clientdata(client); + unsigned long freq, freq_fraction; + const char *p; + + switch (t->mode) { + case V4L2_TUNER_RADIO: p = "radio"; break; + case V4L2_TUNER_ANALOG_TV: p = "analog TV"; break; + case V4L2_TUNER_DIGITAL_TV: p = "digital TV"; break; + default: p = "undefined"; break; + } + if (t->mode == V4L2_TUNER_RADIO) { + freq = t->radio_freq / 16000; + freq_fraction = (t->radio_freq % 16000) * 100 / 16000; + } else { + freq = t->tv_freq / 16; + freq_fraction = (t->tv_freq % 16) * 100 / 16; + } + tuner_info("Tuner mode: %s\n", p); + tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction); + tuner_info("Standard: 0x%08llx\n", t->std); + if (t->mode == V4L2_TUNER_RADIO) { + if (t->has_signal) { + tuner_info("Signal strength: %d\n", t->has_signal(client)); + } + if (t->is_stereo) { + tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no"); + } + } +} /* ---------------------------------------------------------------------- */ /* static var Used only in tuner_attach and tuner_probe */ @@ -337,16 +422,20 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) client_template.adapter = adap; client_template.addr = addr; - t = kmalloc(sizeof(struct tuner), GFP_KERNEL); + t = kzalloc(sizeof(struct tuner), GFP_KERNEL); if (NULL == t) return -ENOMEM; - memset(t, 0, sizeof(struct tuner)); memcpy(&t->i2c, &client_template, sizeof(struct i2c_client)); i2c_set_clientdata(&t->i2c, t); t->type = UNSET; t->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */ t->audmode = V4L2_TUNER_MODE_STEREO; t->mode_mask = T_UNINITIALIZED; + if (tuner_debug_old) { + tuner_debug = tuner_debug_old; + printk(KERN_ERR "tuner: tuner_debug is deprecated and will be removed in 2.6.17.\n"); + printk(KERN_ERR "tuner: use the debug option instead.\n"); + } if (show_i2c) { unsigned char buffer[16]; @@ -379,7 +468,7 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) t->type = TUNER_TEA5767; t->mode_mask = T_RADIO; t->mode = T_STANDBY; - t->freq = 87.5 * 16; /* Sets freq to FM range */ + t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */ default_mode_mask &= ~T_RADIO; goto register_client; @@ -392,7 +481,8 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) if (default_mode_mask != T_UNINITIALIZED) { tuner_dbg ("Setting mode_mask to 0x%02x\n", default_mode_mask); t->mode_mask = default_mode_mask; - t->freq = 400 * 16; /* Sets freq to VHF High */ + t->tv_freq = 400 * 16; /* Sets freq to VHF High */ + t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */ default_mode_mask = T_UNINITIALIZED; } @@ -473,7 +563,9 @@ static inline int check_v4l2(struct tuner *t) static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct tuner *t = i2c_get_clientdata(client); - unsigned int *iarg = (int *)arg; + + if (tuner_debug>1) + v4l_i2c_print_ioctl(&(t->i2c),cmd); switch (cmd) { /* --- configuration --- */ @@ -486,27 +578,17 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) set_addr(client, (struct tuner_setup *)arg); break; case AUDC_SET_RADIO: - set_mode(client,t,V4L2_TUNER_RADIO, "AUDC_SET_RADIO"); + if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO") + == EINVAL) + return 0; + if (t->radio_freq) + set_freq(client, t->radio_freq); break; case TUNER_SET_STANDBY: - { - if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL) - return 0; - if (t->standby) - t->standby (client); - break; - } - case AUDC_CONFIG_PINNACLE: - switch (*iarg) { - case 2: - tuner_dbg("pinnacle pal\n"); - t->radio_if2 = 33300 * 1000; - break; - case 3: - tuner_dbg("pinnacle ntsc\n"); - t->radio_if2 = 41300 * 1000; - break; - } + if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL) + return 0; + if (t->standby) + t->standby (client); break; case VIDIOCSAUDIO: if (check_mode(t, "VIDIOCSAUDIO") == EINVAL) @@ -516,10 +598,6 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) /* Should be implemented, since bttv calls it */ tuner_dbg("VIDIOCSAUDIO not implemented.\n"); - - break; - case MSP_SET_MATRIX: - case TDA9887_SET_CONFIG: break; /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a @@ -545,8 +623,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (vc->norm < ARRAY_SIZE(map)) t->std = map[vc->norm]; tuner_fixup_std(t); - if (t->freq) - set_tv_freq(client, t->freq); + if (t->tv_freq) + set_tv_freq(client, t->tv_freq); return 0; } case VIDIOCSFREQ: @@ -620,15 +698,14 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) t->std = *id; tuner_fixup_std(t); - if (t->freq) - set_freq(client, t->freq); + if (t->tv_freq) + set_freq(client, t->tv_freq); break; } case VIDIOC_S_FREQUENCY: { struct v4l2_frequency *f = arg; - t->freq = f->frequency; switch_v4l2(); if (V4L2_TUNER_RADIO == f->type && V4L2_TUNER_RADIO != t->mode) { @@ -636,7 +713,7 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) == EINVAL) return 0; } - set_freq(client,t->freq); + set_freq(client,f->frequency); break; } @@ -648,7 +725,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; switch_v4l2(); f->type = t->mode; - f->frequency = t->freq; + f->frequency = (V4L2_TUNER_RADIO == t->mode) ? + t->radio_freq : t->tv_freq; break; } case VIDIOC_G_TUNER: @@ -699,14 +777,12 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (V4L2_TUNER_RADIO == t->mode) { t->audmode = tuner->audmode; - set_radio_freq(client, t->freq); + set_radio_freq(client, t->radio_freq); } break; } - default: - tuner_dbg("Unimplemented IOCTL 0x%08x(dir=%d,tp='%c',nr=%d,sz=%d)\n", - cmd, _IOC_DIR(cmd), _IOC_TYPE(cmd), - _IOC_NR(cmd), _IOC_SIZE(cmd)); + case VIDIOC_LOG_STATUS: + tuner_status(client); break; } @@ -729,29 +805,31 @@ static int tuner_resume(struct device *dev) struct tuner *t = i2c_get_clientdata (c); tuner_dbg ("resume\n"); - if (t->freq) - set_freq(c, t->freq); + if (V4L2_TUNER_RADIO == t->mode) { + if (t->radio_freq) + set_freq(c, t->radio_freq); + } else { + if (t->tv_freq) + set_freq(c, t->tv_freq); + } return 0; } /* ----------------------------------------------------------------------- */ static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "tuner", .id = I2C_DRIVERID_TUNER, - .flags = I2C_DF_NOTIFY, .attach_adapter = tuner_probe, .detach_client = tuner_detach, .command = tuner_command, .driver = { - .suspend = tuner_suspend, - .resume = tuner_resume, - }, + .name = "tuner", + .suspend = tuner_suspend, + .resume = tuner_resume, + }, }; static struct i2c_client client_template = { .name = "(tuner unset)", - .flags = I2C_CLIENT_ALLOW_USE, .driver = &driver, }; diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c index d832205818f..37977ff4978 100644 --- a/drivers/media/video/tuner-simple.c +++ b/drivers/media/video/tuner-simple.c @@ -8,6 +8,10 @@ #include <linux/videodev.h> #include <media/tuner.h> +static int offset = 0; +module_param(offset, int, 0666); +MODULE_PARM_DESC(offset,"Allows to specify an offset for tuner"); + /* ---------------------------------------------------------------------- */ /* tv standard selection for Temic 4046 FM5 @@ -75,186 +79,16 @@ #define TUNER_PLL_LOCKED 0x40 #define TUNER_STEREO_MK3 0x04 -/* ---------------------------------------------------------------------- */ - -struct tunertype -{ - char *name; - unsigned char Vendor; - unsigned char Type; - - unsigned short thresh1; /* band switch VHF_LO <=> VHF_HI */ - unsigned short thresh2; /* band switch VHF_HI <=> UHF */ - unsigned char VHF_L; - unsigned char VHF_H; - unsigned char UHF; - unsigned char config; - unsigned short IFPCoff; /* 622.4=16*38.90 MHz PAL, - 732 =16*45.75 NTSCi, - 940 =16*58.75 NTSC-Japan - 704 =16*44 ATSC */ -}; - -/* - * The floats in the tuner struct are computed at compile time - * by gcc and cast back to integers. Thus we don't violate the - * "no float in kernel" rule. +#define TUNER_PARAM_ANALOG 0 /* to be removed */ +/* FIXME: + * Right now, all tuners are using the first tuner_params[] array element + * for analog mode. In the future, we will be merging similar tuner + * definitions together, such that each tuner definition will have a + * tuner_params struct for each available video standard. At that point, + * TUNER_PARAM_ANALOG will be removed, and the tuner_params[] array + * element will be chosen based on the video standard in use. + * */ -static struct tunertype tuners[] = { - /* 0-9 */ - { "Temic PAL (4002 FH5)", TEMIC, PAL, - 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,623}, - { "Philips PAL_I (FI1246 and compatibles)", Philips, PAL_I, - 16*140.25,16*463.25,0xa0,0x90,0x30,0x8e,623}, - { "Philips NTSC (FI1236,FM1236 and compatibles)", Philips, NTSC, - 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,732}, - { "Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF)", Philips, SECAM, - 16*168.25,16*447.25,0xA7,0x97,0x37,0x8e,623}, - { "NoTuner", NoTuner, NOTUNER, - 0,0,0x00,0x00,0x00,0x00,0x00}, - { "Philips PAL_BG (FI1216 and compatibles)", Philips, PAL, - 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,623}, - { "Temic NTSC (4032 FY5)", TEMIC, NTSC, - 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,732}, - { "Temic PAL_I (4062 FY5)", TEMIC, PAL_I, - 16*170.00,16*450.00,0x02,0x04,0x01,0x8e,623}, - { "Temic NTSC (4036 FY5)", TEMIC, NTSC, - 16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,732}, - { "Alps HSBH1", TEMIC, NTSC, - 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732}, - - /* 10-19 */ - { "Alps TSBE1", TEMIC, PAL, - 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732}, - { "Alps TSBB5", Alps, PAL_I, /* tested (UK UHF) with Modulartech MM205 */ - 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,632}, - { "Alps TSBE5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */ - 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,622}, - { "Alps TSBC5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */ - 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,608}, - { "Temic PAL_BG (4006FH5)", TEMIC, PAL, - 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, - { "Alps TSCH6", Alps, NTSC, - 16*137.25,16*385.25,0x14,0x12,0x11,0x8e,732}, - { "Temic PAL_DK (4016 FY5)", TEMIC, PAL, - 16*168.25,16*456.25,0xa0,0x90,0x30,0x8e,623}, - { "Philips NTSC_M (MK2)", Philips, NTSC, - 16*160.00,16*454.00,0xa0,0x90,0x30,0x8e,732}, - { "Temic PAL_I (4066 FY5)", TEMIC, PAL_I, - 16*169.00, 16*454.00, 0xa0,0x90,0x30,0x8e,623}, - { "Temic PAL* auto (4006 FN5)", TEMIC, PAL, - 16*169.00, 16*454.00, 0xa0,0x90,0x30,0x8e,623}, - - /* 20-29 */ - { "Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5)", TEMIC, PAL, - 16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623}, - { "Temic NTSC (4039 FR5)", TEMIC, NTSC, - 16*158.00, 16*453.00, 0xa0,0x90,0x30,0x8e,732}, - { "Temic PAL/SECAM multi (4046 FM5)", TEMIC, PAL, - 16*169.00, 16*454.00, 0xa0,0x90,0x30,0x8e,623}, - { "Philips PAL_DK (FI1256 and compatibles)", Philips, PAL, - 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, - { "Philips PAL/SECAM multi (FQ1216ME)", Philips, PAL, - 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, - { "LG PAL_I+FM (TAPC-I001D)", LGINNOTEK, PAL_I, - 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, - { "LG PAL_I (TAPC-I701D)", LGINNOTEK, PAL_I, - 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, - { "LG NTSC+FM (TPI8NSR01F)", LGINNOTEK, NTSC, - 16*210.00,16*497.00,0xa0,0x90,0x30,0x8e,732}, - { "LG PAL_BG+FM (TPI8PSB01D)", LGINNOTEK, PAL, - 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, - { "LG PAL_BG (TPI8PSB11D)", LGINNOTEK, PAL, - 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, - - /* 30-39 */ - { "Temic PAL* auto + FM (4009 FN5)", TEMIC, PAL, - 16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623}, - { "SHARP NTSC_JP (2U5JF5540)", SHARP, NTSC, /* 940=16*58.75 NTSC@Japan */ - 16*137.25,16*317.25,0x01,0x02,0x08,0x8e,940 }, - { "Samsung PAL TCPM9091PD27", Samsung, PAL, /* from sourceforge v3tv */ - 16*169,16*464,0xA0,0x90,0x30,0x8e,623}, - { "MT20xx universal", Microtune, PAL|NTSC, - /* see mt20xx.c for details */ }, - { "Temic PAL_BG (4106 FH5)", TEMIC, PAL, - 16*141.00, 16*464.00, 0xa0,0x90,0x30,0x8e,623}, - { "Temic PAL_DK/SECAM_L (4012 FY5)", TEMIC, PAL, - 16*140.25, 16*463.25, 0x02,0x04,0x01,0x8e,623}, - { "Temic NTSC (4136 FY5)", TEMIC, NTSC, - 16*158.00, 16*453.00, 0xa0,0x90,0x30,0x8e,732}, - { "LG PAL (newer TAPC series)", LGINNOTEK, PAL, - 16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,623}, - { "Philips PAL/SECAM multi (FM1216ME MK3)", Philips, PAL, - 16*158.00,16*442.00,0x01,0x02,0x04,0x8e,623 }, - { "LG NTSC (newer TAPC series)", LGINNOTEK, NTSC, - 16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,732}, - - /* 40-49 */ - { "HITACHI V7-J180AT", HITACHI, NTSC, - 16*170.00, 16*450.00, 0x01,0x02,0x08,0x8e,940 }, - { "Philips PAL_MK (FI1216 MK)", Philips, PAL, - 16*140.25,16*463.25,0x01,0xc2,0xcf,0x8e,623}, - { "Philips 1236D ATSC/NTSC daul in", Philips, ATSC, - 16*157.25,16*454.00,0xa0,0x90,0x30,0x8e,732}, - { "Philips NTSC MK3 (FM1236MK3 or FM1236/F)", Philips, NTSC, - 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732}, - { "Philips 4 in 1 (ATI TV Wonder Pro/Conexant)", Philips, NTSC, - 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732}, - { "Microtune 4049 FM5", Microtune, PAL, - 16*141.00,16*464.00,0xa0,0x90,0x30,0x8e,623}, - { "Panasonic VP27s/ENGE4324D", Panasonic, NTSC, - 16*160.00,16*454.00,0x01,0x02,0x08,0xce,940}, - { "LG NTSC (TAPE series)", LGINNOTEK, NTSC, - 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732 }, - { "Tenna TNF 8831 BGFF)", Philips, PAL, - 16*161.25,16*463.25,0xa0,0x90,0x30,0x8e,623}, - { "Microtune 4042 FI5 ATSC/NTSC dual in", Microtune, NTSC, - 16*162.00,16*457.00,0xa2,0x94,0x31,0x8e,732}, - - /* 50-59 */ - { "TCL 2002N", TCL, NTSC, - 16*172.00,16*448.00,0x01,0x02,0x08,0x8e,732}, - { "Philips PAL/SECAM_D (FM 1256 I-H3)", Philips, PAL, - 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,623 }, - { "Thomson DDT 7610 (ATSC/NTSC)", THOMSON, ATSC, - 16*157.25,16*454.00,0x39,0x3a,0x3c,0x8e,732}, - { "Philips FQ1286", Philips, NTSC, - 16*160.00,16*454.00,0x41,0x42,0x04,0x8e,940}, /* UHF band untested */ - { "tda8290+75", Philips, PAL|NTSC, - /* see tda8290.c for details */ }, - { "TCL 2002MB", TCL, PAL, - 16*170.00, 16*450.00, 0x01,0x02,0x08,0xce,623}, - { "Philips PAL/SECAM multi (FQ1216AME MK4)", Philips, PAL, - 16*160.00,16*442.00,0x01,0x02,0x04,0xce,623 }, - { "Philips FQ1236A MK4", Philips, NTSC, - 16*160.00,16*442.00,0x01,0x02,0x04,0x8e,732 }, - { "Ymec TVision TVF-8531MF/8831MF/8731MF", Philips, NTSC, - 16*160.00,16*454.00,0xa0,0x90,0x30,0x8e,732}, - { "Ymec TVision TVF-5533MF", Philips, NTSC, - 16*160.00,16*454.00,0x01,0x02,0x04,0x8e,732}, - - /* 60-68 */ - { "Thomson DDT 7611 (ATSC/NTSC)", THOMSON, ATSC, - 16*157.25,16*454.00,0x39,0x3a,0x3c,0x8e,732}, - { "Tena TNF9533-D/IF/TNF9533-B/DF", Philips, PAL, - 16*160.25,16*464.25,0x01,0x02,0x04,0x8e,623}, - { "Philips TEA5767HN FM Radio", Philips, RADIO, - /* see tea5767.c for details */}, - { "Philips FMD1216ME MK3 Hybrid Tuner", Philips, PAL, - 16*160.00,16*442.00,0x51,0x52,0x54,0x86,623 }, - { "LG TDVS-H062F/TUA6034", LGINNOTEK, ATSC, - 16*160.00,16*455.00,0x01,0x02,0x04,0x8e,732}, - { "Ymec TVF66T5-B/DFF", Philips, PAL, - 16*160.25,16*464.25,0x01,0x02,0x08,0x8e,623}, - { "LG NTSC (TALN mini series)", LGINNOTEK, NTSC, - 16*137.25,16*373.25,0x01,0x02,0x08,0x8e,732 }, - { "Philips TD1316 Hybrid Tuner", Philips, PAL, - 16*160.00,16*442.00,0xa1,0xa2,0xa4,0xc8,623 }, - { "Philips TUV1236D ATSC/NTSC dual in", Philips, ATSC, - 16*157.25,16*454.00,0x01,0x02,0x04,0xce,732 }, -}; - -unsigned const int tuner_count = ARRAY_SIZE(tuners); /* ---------------------------------------------------------------------- */ @@ -302,21 +136,27 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq) u8 config, tuneraddr; u16 div; struct tunertype *tun; - unsigned char buffer[4]; - int rc; + u8 buffer[4]; + int rc, IFPCoff, i, j; tun = &tuners[t->type]; - if (freq < tun->thresh1) { - config = tun->VHF_L; - tuner_dbg("tv: VHF lowrange\n"); - } else if (freq < tun->thresh2) { - config = tun->VHF_H; - tuner_dbg("tv: VHF high range\n"); - } else { - config = tun->UHF; - tuner_dbg("tv: UHF range\n"); - } + j = TUNER_PARAM_ANALOG; + for (i = 0; i < tun->params[j].count; i++) { + if (freq > tun->params[j].ranges[i].limit) + continue; + break; + } + if (i == tun->params[j].count) { + tuner_dbg("TV frequency out of range (%d > %d)", + freq, tun->params[j].ranges[i - 1].limit); + freq = tun->params[j].ranges[--i].limit; + } + config = tun->params[j].ranges[i].cb; + /* i == 0 -> VHF_LO */ + /* i == 1 -> VHF_HI */ + /* i == 2 -> UHF */ + tuner_dbg("tv: range %d\n",i); /* tv norm specific stuff for multi-norm tuners */ switch (t->type) { @@ -375,7 +215,7 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq) case TUNER_MICROTUNE_4042FI5: /* Set the charge pump for fast tuning */ - tun->config |= TUNER_CHARGE_PUMP; + tun->params[j].config |= TUNER_CHARGE_PUMP; break; case TUNER_PHILIPS_TUV1236D: @@ -404,32 +244,49 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq) break; } - /* - * Philips FI1216MK2 remark from specification : - * for channel selection involving band switching, and to ensure - * smooth tuning to the desired channel without causing - * unnecessary charge pump action, it is recommended to consider - * the difference between wanted channel frequency and the - * current channel frequency. Unnecessary charge pump action - * will result in very low tuning voltage which may drive the - * oscillator to extreme conditions. - * - * Progfou: specification says to send config data before - * frequency in case (wanted frequency < current frequency). - */ - - div=freq + tun->IFPCoff; - if (t->type == TUNER_PHILIPS_SECAM && freq < t->freq) { - buffer[0] = tun->config; + /* IFPCoff = Video Intermediate Frequency - Vif: + 940 =16*58.75 NTSC/J (Japan) + 732 =16*45.75 M/N STD + 704 =16*44 ATSC (at DVB code) + 632 =16*39.50 I U.K. + 622.4=16*38.90 B/G D/K I, L STD + 592 =16*37.00 D China + 590 =16.36.875 B Australia + 543.2=16*33.95 L' STD + 171.2=16*10.70 FM Radio (at set_radio_freq) + */ + + if (t->std == V4L2_STD_NTSC_M_JP) { + IFPCoff = 940; + } else if ((t->std & V4L2_STD_MN) && + !(t->std & ~V4L2_STD_MN)) { + IFPCoff = 732; + } else if (t->std == V4L2_STD_SECAM_LC) { + IFPCoff = 543; + } else { + IFPCoff = 623; + } + + div=freq + IFPCoff + offset; + + tuner_dbg("Freq= %d.%02d MHz, V_IF=%d.%02d MHz, Offset=%d.%02d MHz, div=%0d\n", + freq / 16, freq % 16 * 100 / 16, + IFPCoff / 16, IFPCoff % 16 * 100 / 16, + offset / 16, offset % 16 * 100 / 16, + div); + + if (tuners[t->type].params->cb_first_if_lower_freq && div < t->last_div) { + buffer[0] = tun->params[j].config; buffer[1] = config; buffer[2] = (div>>8) & 0x7f; buffer[3] = div & 0xff; } else { buffer[0] = (div>>8) & 0x7f; buffer[1] = div & 0xff; - buffer[2] = tun->config; + buffer[2] = tun->params[j].config; buffer[3] = config; } + t->last_div = div; tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", buffer[0],buffer[1],buffer[2],buffer[3]); @@ -455,10 +312,10 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq) } /* Set the charge pump for optimized phase noise figure */ - tun->config &= ~TUNER_CHARGE_PUMP; + tun->params[j].config &= ~TUNER_CHARGE_PUMP; buffer[0] = (div>>8) & 0x7f; buffer[1] = div & 0xff; - buffer[2] = tun->config; + buffer[2] = tun->params[j].config; buffer[3] = config; tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", buffer[0],buffer[1],buffer[2],buffer[3]); @@ -472,13 +329,15 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq) { struct tunertype *tun; struct tuner *t = i2c_get_clientdata(c); - unsigned char buffer[4]; - unsigned div; - int rc; + u8 buffer[4]; + u16 div; + int rc, j; tun = &tuners[t->type]; + j = TUNER_PARAM_ANALOG; + div = (20 * freq / 16000) + (int)(20*10.7); /* IF 10.7 MHz */ - buffer[2] = (tun->config & ~TUNER_RATIO_MASK) | TUNER_RATIO_SELECT_50; /* 50 kHz step */ + buffer[2] = (tun->params[j].config & ~TUNER_RATIO_MASK) | TUNER_RATIO_SELECT_50; /* 50 kHz step */ switch (t->type) { case TUNER_TENA_9533_DI: @@ -507,9 +366,19 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq) } buffer[0] = (div>>8) & 0x7f; buffer[1] = div & 0xff; + if (tuners[t->type].params->cb_first_if_lower_freq && div < t->last_div) { + buffer[0] = buffer[2]; + buffer[1] = buffer[3]; + buffer[2] = (div>>8) & 0x7f; + buffer[3] = div & 0xff; + } else { + buffer[0] = (div>>8) & 0x7f; + buffer[1] = div & 0xff; + } tuner_dbg("radio 0x%02x 0x%02x 0x%02x 0x%02x\n", buffer[0],buffer[1],buffer[2],buffer[3]); + t->last_div = div; if (4 != (rc = i2c_master_send(c,buffer,4))) tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc); @@ -523,10 +392,10 @@ int default_tuner_init(struct i2c_client *c) t->type, tuners[t->type].name); strlcpy(c->name, tuners[t->type].name, sizeof(c->name)); - t->tv_freq = default_set_tv_freq; - t->radio_freq = default_set_radio_freq; + t->set_tv_freq = default_set_tv_freq; + t->set_radio_freq = default_set_radio_freq; t->has_signal = tuner_signal; - t->is_stereo = tuner_stereo; + t->is_stereo = tuner_stereo; t->standby = NULL; return 0; diff --git a/drivers/media/video/tuner-types.c b/drivers/media/video/tuner-types.c new file mode 100644 index 00000000000..6fe781798d8 --- /dev/null +++ b/drivers/media/video/tuner-types.c @@ -0,0 +1,1406 @@ +/* + * + * i2c tv tuner chip device type database. + * + */ + +#include <linux/i2c.h> +#include <media/tuner.h> +#include <media/tuner-types.h> + +/* ---------------------------------------------------------------------- */ + +/* + * The floats in the tuner struct are computed at compile time + * by gcc and cast back to integers. Thus we don't violate the + * "no float in kernel" rule. + * + * A tuner_range may be referenced by multiple tuner_params structs. + * There are many duplicates in here. Reusing tuner_range structs, + * rather than defining new ones for each tuner, will cut down on + * memory usage, and is preferred when possible. + * + * Each tuner_params array may contain one or more elements, one + * for each video standard. + * + * FIXME: Some tuner_range definitions are duplicated, and + * should be eliminated. + * + * FIXME: tunertype struct contains an element, has_tda988x. + * We must set this for all tunertypes that contain a tda988x + * chip, and then we can remove this setting from the various + * card structs. + */ + +/* 0-9 */ +/* ------------ TUNER_TEMIC_PAL - TEMIC PAL ------------ */ + +static struct tuner_range tuner_temic_pal_ranges[] = { + { 16 * 140.25 /*MHz*/, 0x02, }, + { 16 * 463.25 /*MHz*/, 0x04, }, + { 16 * 999.99 , 0x01, }, +}; + +static struct tuner_params tuner_temic_pal_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_PAL_I - Philips PAL_I ------------ */ + +static struct tuner_range tuner_philips_pal_i_ranges[] = { + { 16 * 140.25 /*MHz*/, 0xa0, }, + { 16 * 463.25 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_philips_pal_i_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_pal_i_ranges, + .count = ARRAY_SIZE(tuner_philips_pal_i_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_NTSC - Philips NTSC ------------ */ + +static struct tuner_range tuner_philips_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0xa0, }, + { 16 * 451.25 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_philips_ntsc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_philips_ntsc_ranges, + .count = ARRAY_SIZE(tuner_philips_ntsc_ranges), + .config = 0x8e, + .cb_first_if_lower_freq = 1, + }, +}; + +/* ------------ TUNER_PHILIPS_SECAM - Philips SECAM ------------ */ + +static struct tuner_range tuner_philips_secam_ranges[] = { + { 16 * 168.25 /*MHz*/, 0xa7, }, + { 16 * 447.25 /*MHz*/, 0x97, }, + { 16 * 999.99 , 0x37, }, +}; + +static struct tuner_params tuner_philips_secam_params[] = { + { + .type = TUNER_PARAM_TYPE_SECAM, + .ranges = tuner_philips_secam_ranges, + .count = ARRAY_SIZE(tuner_philips_secam_ranges), + .config = 0x8e, + .cb_first_if_lower_freq = 1, + }, +}; + +/* ------------ TUNER_PHILIPS_PAL - Philips PAL ------------ */ + +static struct tuner_range tuner_philips_pal_ranges[] = { + { 16 * 168.25 /*MHz*/, 0xa0, }, + { 16 * 447.25 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_philips_pal_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_pal_ranges), + .config = 0x8e, + .cb_first_if_lower_freq = 1, + }, +}; + +/* ------------ TUNER_TEMIC_NTSC - TEMIC NTSC ------------ */ + +static struct tuner_range tuner_temic_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0x02, }, + { 16 * 463.25 /*MHz*/, 0x04, }, + { 16 * 999.99 , 0x01, }, +}; + +static struct tuner_params tuner_temic_ntsc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_temic_ntsc_ranges, + .count = ARRAY_SIZE(tuner_temic_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TEMIC_PAL_I - TEMIC PAL_I ------------ */ + +static struct tuner_range tuner_temic_pal_i_ranges[] = { + { 16 * 170.00 /*MHz*/, 0x02, }, + { 16 * 450.00 /*MHz*/, 0x04, }, + { 16 * 999.99 , 0x01, }, +}; + +static struct tuner_params tuner_temic_pal_i_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_pal_i_ranges, + .count = ARRAY_SIZE(tuner_temic_pal_i_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TEMIC_4036FY5_NTSC - TEMIC NTSC ------------ */ + +static struct tuner_range tuner_temic_4036fy5_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0xa0, }, + { 16 * 463.25 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_temic_4036fy5_ntsc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_temic_4036fy5_ntsc_ranges, + .count = ARRAY_SIZE(tuner_temic_4036fy5_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_ALPS_TSBH1_NTSC - TEMIC NTSC ------------ */ + +static struct tuner_range tuner_alps_tsb_1_ranges[] = { + { 16 * 137.25 /*MHz*/, 0x01, }, + { 16 * 385.25 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x08, }, +}; + +static struct tuner_params tuner_alps_tsbh1_ntsc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_alps_tsb_1_ranges, + .count = ARRAY_SIZE(tuner_alps_tsb_1_ranges), + .config = 0x8e, + }, +}; + +/* 10-19 */ +/* ------------ TUNER_ALPS_TSBE1_PAL - TEMIC PAL ------------ */ + +static struct tuner_params tuner_alps_tsb_1_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_alps_tsb_1_ranges, + .count = ARRAY_SIZE(tuner_alps_tsb_1_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_ALPS_TSBB5_PAL_I - Alps PAL_I ------------ */ + +static struct tuner_range tuner_alps_tsb_5_pal_ranges[] = { + { 16 * 133.25 /*MHz*/, 0x01, }, + { 16 * 351.25 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x08, }, +}; + +static struct tuner_params tuner_alps_tsbb5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_alps_tsb_5_pal_ranges, + .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_ALPS_TSBE5_PAL - Alps PAL ------------ */ + +static struct tuner_params tuner_alps_tsbe5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_alps_tsb_5_pal_ranges, + .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_ALPS_TSBC5_PAL - Alps PAL ------------ */ + +static struct tuner_params tuner_alps_tsbc5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_alps_tsb_5_pal_ranges, + .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TEMIC_4006FH5_PAL - TEMIC PAL ------------ */ + +static struct tuner_range tuner_temic_4006fh5_pal_ranges[] = { + { 16 * 170.00 /*MHz*/, 0xa0, }, + { 16 * 450.00 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_temic_4006fh5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_4006fh5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_4006fh5_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_ALPS_TSHC6_NTSC - Alps NTSC ------------ */ + +static struct tuner_range tuner_alps_tshc6_ntsc_ranges[] = { + { 16 * 137.25 /*MHz*/, 0x14, }, + { 16 * 385.25 /*MHz*/, 0x12, }, + { 16 * 999.99 , 0x11, }, +}; + +static struct tuner_params tuner_alps_tshc6_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_alps_tshc6_ntsc_ranges, + .count = ARRAY_SIZE(tuner_alps_tshc6_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TEMIC_PAL_DK - TEMIC PAL ------------ */ + +static struct tuner_range tuner_temic_pal_dk_ranges[] = { + { 16 * 168.25 /*MHz*/, 0xa0, }, + { 16 * 456.25 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_temic_pal_dk_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_pal_dk_ranges, + .count = ARRAY_SIZE(tuner_temic_pal_dk_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_NTSC_M - Philips NTSC ------------ */ + +static struct tuner_range tuner_philips_ntsc_m_ranges[] = { + { 16 * 160.00 /*MHz*/, 0xa0, }, + { 16 * 454.00 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_philips_ntsc_m_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_philips_ntsc_m_ranges, + .count = ARRAY_SIZE(tuner_philips_ntsc_m_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TEMIC_4066FY5_PAL_I - TEMIC PAL_I ------------ */ + +static struct tuner_range tuner_temic_40x6f_5_pal_ranges[] = { + { 16 * 169.00 /*MHz*/, 0xa0, }, + { 16 * 454.00 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_temic_4066fy5_pal_i_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_40x6f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TEMIC_4006FN5_MULTI_PAL - TEMIC PAL ------------ */ + +static struct tuner_params tuner_temic_4006fn5_multi_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_40x6f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges), + .config = 0x8e, + }, +}; + +/* 20-29 */ +/* ------------ TUNER_TEMIC_4009FR5_PAL - TEMIC PAL ------------ */ + +static struct tuner_range tuner_temic_4009f_5_pal_ranges[] = { + { 16 * 141.00 /*MHz*/, 0xa0, }, + { 16 * 464.00 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_temic_4009f_5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_4009f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TEMIC_4039FR5_NTSC - TEMIC NTSC ------------ */ + +static struct tuner_range tuner_temic_4039fr5_ntsc_ranges[] = { + { 16 * 158.00 /*MHz*/, 0xa0, }, + { 16 * 453.00 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_temic_4039fr5_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_temic_4039fr5_ntsc_ranges, + .count = ARRAY_SIZE(tuner_temic_4039fr5_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TEMIC_4046FM5 - TEMIC PAL ------------ */ + +static struct tuner_range tuner_temic_4046fm5_pal_ranges[] = { + { 16 * 169.00 /*MHz*/, 0xa0, }, + { 16 * 454.00 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_temic_4046fm5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_4046fm5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_4046fm5_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_PAL_DK - Philips PAL ------------ */ + +static struct tuner_range tuner_lg_pal_ranges[] = { + { 16 * 170.00 /*MHz*/, 0xa0, }, + { 16 * 450.00 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_philips_pal_dk_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_FQ1216ME - Philips PAL ------------ */ + +static struct tuner_params tuner_philips_fq1216me_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_LG_PAL_I_FM - LGINNOTEK PAL_I ------------ */ + +static struct tuner_params tuner_lg_pal_i_fm_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_LG_PAL_I - LGINNOTEK PAL_I ------------ */ + +static struct tuner_params tuner_lg_pal_i_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_LG_NTSC_FM - LGINNOTEK NTSC ------------ */ + +static struct tuner_range tuner_lg_ntsc_fm_ranges[] = { + { 16 * 210.00 /*MHz*/, 0xa0, }, + { 16 * 497.00 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_lg_ntsc_fm_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_lg_ntsc_fm_ranges, + .count = ARRAY_SIZE(tuner_lg_ntsc_fm_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_LG_PAL_FM - LGINNOTEK PAL ------------ */ + +static struct tuner_params tuner_lg_pal_fm_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_LG_PAL - LGINNOTEK PAL ------------ */ + +static struct tuner_params tuner_lg_pal_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_pal_ranges, + .count = ARRAY_SIZE(tuner_lg_pal_ranges), + .config = 0x8e, + }, +}; + +/* 30-39 */ +/* ------------ TUNER_TEMIC_4009FN5_MULTI_PAL_FM - TEMIC PAL ------------ */ + +static struct tuner_params tuner_temic_4009_fn5_multi_pal_fm_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_4009f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_SHARP_2U5JF5540_NTSC - SHARP NTSC ------------ */ + +static struct tuner_range tuner_sharp_2u5jf5540_ntsc_ranges[] = { + { 16 * 137.25 /*MHz*/, 0x01, }, + { 16 * 317.25 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x08, }, +}; + +static struct tuner_params tuner_sharp_2u5jf5540_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_sharp_2u5jf5540_ntsc_ranges, + .count = ARRAY_SIZE(tuner_sharp_2u5jf5540_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_Samsung_PAL_TCPM9091PD27 - Samsung PAL ------------ */ + +static struct tuner_range tuner_samsung_pal_tcpm9091pd27_ranges[] = { + { 16 * 169 /*MHz*/, 0xa0, }, + { 16 * 464 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_samsung_pal_tcpm9091pd27_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_samsung_pal_tcpm9091pd27_ranges, + .count = ARRAY_SIZE(tuner_samsung_pal_tcpm9091pd27_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TEMIC_4106FH5 - TEMIC PAL ------------ */ + +static struct tuner_params tuner_temic_4106fh5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_4009f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TEMIC_4012FY5 - TEMIC PAL ------------ */ + +static struct tuner_range tuner_temic_4012fy5_pal_ranges[] = { + { 16 * 140.25 /*MHz*/, 0x02, }, + { 16 * 463.25 /*MHz*/, 0x04, }, + { 16 * 999.99 , 0x01, }, +}; + +static struct tuner_params tuner_temic_4012fy5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_4012fy5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_4012fy5_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TEMIC_4136FY5 - TEMIC NTSC ------------ */ + +static struct tuner_range tuner_temic_4136_fy5_ntsc_ranges[] = { + { 16 * 158.00 /*MHz*/, 0xa0, }, + { 16 * 453.00 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_temic_4136_fy5_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_temic_4136_fy5_ntsc_ranges, + .count = ARRAY_SIZE(tuner_temic_4136_fy5_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_LG_PAL_NEW_TAPC - LGINNOTEK PAL ------------ */ + +static struct tuner_range tuner_lg_new_tapc_ranges[] = { + { 16 * 170.00 /*MHz*/, 0x01, }, + { 16 * 450.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x08, }, +}; + +static struct tuner_params tuner_lg_pal_new_tapc_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_lg_new_tapc_ranges, + .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_FM1216ME_MK3 - Philips PAL ------------ */ + +static struct tuner_range tuner_fm1216me_mk3_pal_ranges[] = { + { 16 * 158.00 /*MHz*/, 0x01, }, + { 16 * 442.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x04, }, +}; + +static struct tuner_params tuner_fm1216me_mk3_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_fm1216me_mk3_pal_ranges, + .count = ARRAY_SIZE(tuner_fm1216me_mk3_pal_ranges), + .config = 0x8e, + .cb_first_if_lower_freq = 1, + }, +}; + +/* ------------ TUNER_LG_NTSC_NEW_TAPC - LGINNOTEK NTSC ------------ */ + +static struct tuner_params tuner_lg_ntsc_new_tapc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_lg_new_tapc_ranges, + .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), + .config = 0x8e, + }, +}; + +/* 40-49 */ +/* ------------ TUNER_HITACHI_NTSC - HITACHI NTSC ------------ */ + +static struct tuner_params tuner_hitachi_ntsc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_lg_new_tapc_ranges, + .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_PAL_MK - Philips PAL ------------ */ + +static struct tuner_range tuner_philips_pal_mk_pal_ranges[] = { + { 16 * 140.25 /*MHz*/, 0x01, }, + { 16 * 463.25 /*MHz*/, 0xc2, }, + { 16 * 999.99 , 0xcf, }, +}; + +static struct tuner_params tuner_philips_pal_mk_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_pal_mk_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_pal_mk_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_ATSC - Philips ATSC ------------ */ + +static struct tuner_range tuner_philips_atsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0xa0, }, + { 16 * 454.00 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_philips_atsc_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_philips_atsc_ranges, + .count = ARRAY_SIZE(tuner_philips_atsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_FM1236_MK3 - Philips NTSC ------------ */ + +static struct tuner_range tuner_fm1236_mk3_ntsc_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x01, }, + { 16 * 442.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x04, }, +}; + +static struct tuner_params tuner_fm1236_mk3_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_fm1236_mk3_ntsc_ranges, + .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), + .config = 0x8e, + .cb_first_if_lower_freq = 1, + }, +}; + +/* ------------ TUNER_PHILIPS_4IN1 - Philips NTSC ------------ */ + +static struct tuner_range tuner_philips_4in1_ntsc_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x01, }, + { 16 * 442.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x04, }, +}; + +static struct tuner_params tuner_philips_4in1_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_philips_4in1_ntsc_ranges, + .count = ARRAY_SIZE(tuner_philips_4in1_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_MICROTUNE_4049FM5 - Microtune PAL ------------ */ + +static struct tuner_params tuner_microtune_4049_fm5_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_temic_4009f_5_pal_ranges, + .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PANASONIC_VP27 - Panasonic NTSC ------------ */ + +static struct tuner_range tuner_panasonic_vp27_ntsc_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x01, }, + { 16 * 454.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x08, }, +}; + +static struct tuner_params tuner_panasonic_vp27_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_panasonic_vp27_ntsc_ranges, + .count = ARRAY_SIZE(tuner_panasonic_vp27_ntsc_ranges), + .config = 0xce, + }, +}; + +/* ------------ TUNER_LG_NTSC_TAPE - LGINNOTEK NTSC ------------ */ + +static struct tuner_range tuner_lg_ntsc_tape_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x01, }, + { 16 * 442.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x04, }, +}; + +static struct tuner_params tuner_lg_ntsc_tape_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_lg_ntsc_tape_ranges, + .count = ARRAY_SIZE(tuner_lg_ntsc_tape_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TNF_8831BGFF - Philips PAL ------------ */ + +static struct tuner_range tuner_tnf_8831bgff_pal_ranges[] = { + { 16 * 161.25 /*MHz*/, 0xa0, }, + { 16 * 463.25 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_tnf_8831bgff_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_tnf_8831bgff_pal_ranges, + .count = ARRAY_SIZE(tuner_tnf_8831bgff_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_MICROTUNE_4042FI5 - Microtune NTSC ------------ */ + +static struct tuner_range tuner_microtune_4042fi5_ntsc_ranges[] = { + { 16 * 162.00 /*MHz*/, 0xa2, }, + { 16 * 457.00 /*MHz*/, 0x94, }, + { 16 * 999.99 , 0x31, }, +}; + +static struct tuner_params tuner_microtune_4042fi5_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_microtune_4042fi5_ntsc_ranges, + .count = ARRAY_SIZE(tuner_microtune_4042fi5_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* 50-59 */ +/* ------------ TUNER_TCL_2002N - TCL NTSC ------------ */ + +static struct tuner_range tuner_tcl_2002n_ntsc_ranges[] = { + { 16 * 172.00 /*MHz*/, 0x01, }, + { 16 * 448.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x08, }, +}; + +static struct tuner_params tuner_tcl_2002n_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_tcl_2002n_ntsc_ranges, + .count = ARRAY_SIZE(tuner_tcl_2002n_ntsc_ranges), + .config = 0x8e, + .cb_first_if_lower_freq = 1, + }, +}; + +/* ------------ TUNER_PHILIPS_FM1256_IH3 - Philips PAL ------------ */ + +static struct tuner_range tuner_philips_fm1256_ih3_pal_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x01, }, + { 16 * 442.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x04, }, +}; + +static struct tuner_params tuner_philips_fm1256_ih3_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_fm1256_ih3_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_fm1256_ih3_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_THOMSON_DTT7610 - THOMSON ATSC ------------ */ + +static struct tuner_range tuner_thomson_dtt7610_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0x39, }, + { 16 * 454.00 /*MHz*/, 0x3a, }, + { 16 * 999.99 , 0x3c, }, +}; + +static struct tuner_params tuner_thomson_dtt7610_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_thomson_dtt7610_ntsc_ranges, + .count = ARRAY_SIZE(tuner_thomson_dtt7610_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_FQ1286 - Philips NTSC ------------ */ + +static struct tuner_range tuner_philips_fq1286_ntsc_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x41, }, + { 16 * 454.00 /*MHz*/, 0x42, }, + { 16 * 999.99 , 0x04, }, +}; + +static struct tuner_params tuner_philips_fq1286_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_philips_fq1286_ntsc_ranges, + .count = ARRAY_SIZE(tuner_philips_fq1286_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TCL_2002MB - TCL PAL ------------ */ + +static struct tuner_range tuner_tcl_2002mb_pal_ranges[] = { + { 16 * 170.00 /*MHz*/, 0x01, }, + { 16 * 450.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x08, }, +}; + +static struct tuner_params tuner_tcl_2002mb_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_tcl_2002mb_pal_ranges, + .count = ARRAY_SIZE(tuner_tcl_2002mb_pal_ranges), + .config = 0xce, + }, +}; + +/* ------------ TUNER_PHILIPS_FQ1216AME_MK4 - Philips PAL ------------ */ + +static struct tuner_range tuner_philips_fq12_6a___mk4_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x01, }, + { 16 * 442.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x04, }, +}; + +static struct tuner_params tuner_philips_fq1216ame_mk4_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_fq12_6a___mk4_ranges, + .count = ARRAY_SIZE(tuner_philips_fq12_6a___mk4_ranges), + .config = 0xce, + }, +}; + +/* ------------ TUNER_PHILIPS_FQ1236A_MK4 - Philips NTSC ------------ */ + +static struct tuner_params tuner_philips_fq1236a_mk4_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_philips_fq12_6a___mk4_ranges, + .count = ARRAY_SIZE(tuner_philips_fq12_6a___mk4_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_YMEC_TVF_8531MF - Philips NTSC ------------ */ + +static struct tuner_range tuner_ymec_tvf_8531mf_ntsc_ranges[] = { + { 16 * 160.00 /*MHz*/, 0xa0, }, + { 16 * 454.00 /*MHz*/, 0x90, }, + { 16 * 999.99 , 0x30, }, +}; + +static struct tuner_params tuner_ymec_tvf_8531mf_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_ymec_tvf_8531mf_ntsc_ranges, + .count = ARRAY_SIZE(tuner_ymec_tvf_8531mf_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_YMEC_TVF_5533MF - Philips NTSC ------------ */ + +static struct tuner_range tuner_ymec_tvf_5533mf_ntsc_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x01, }, + { 16 * 454.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x04, }, +}; + +static struct tuner_params tuner_ymec_tvf_5533mf_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_ymec_tvf_5533mf_ntsc_ranges, + .count = ARRAY_SIZE(tuner_ymec_tvf_5533mf_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* 60-69 */ +/* ------------ TUNER_THOMSON_DTT761X - THOMSON ATSC ------------ */ +/* DTT 7611 7611A 7612 7613 7613A 7614 7615 7615A */ + +static struct tuner_range tuner_thomson_dtt761x_ntsc_ranges[] = { + { 16 * 145.25 /*MHz*/, 0x39, }, + { 16 * 415.25 /*MHz*/, 0x3a, }, + { 16 * 999.99 , 0x3c, }, +}; + + +static struct tuner_params tuner_thomson_dtt761x_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_thomson_dtt761x_ntsc_ranges, + .count = ARRAY_SIZE(tuner_thomson_dtt761x_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_TENA_9533_DI - Philips PAL ------------ */ + +static struct tuner_range tuner_tuner_tena_9533_di_pal_ranges[] = { + { 16 * 160.25 /*MHz*/, 0x01, }, + { 16 * 464.25 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x04, }, +}; + +static struct tuner_params tuner_tena_9533_di_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_tuner_tena_9533_di_pal_ranges, + .count = ARRAY_SIZE(tuner_tuner_tena_9533_di_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_FMD1216ME_MK3 - Philips PAL ------------ */ + +static struct tuner_range tuner_philips_fmd1216me_mk3_pal_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x51, }, + { 16 * 442.00 /*MHz*/, 0x52, }, + { 16 * 999.99 , 0x54, }, +}; + + +static struct tuner_params tuner_tuner_philips_fmd1216me_mk3_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_fmd1216me_mk3_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_pal_ranges), + .config = 0x86, + }, +}; + + +/* ------------ TUNER_LG_TDVS_H062F - INFINEON ATSC ------------ */ + +static struct tuner_range tuner_tua6034_ntsc_ranges[] = { + { 16 * 160.00 /*MHz*/, 0x01 }, + { 16 * 455.00 /*MHz*/, 0x02 }, + { 16 * 999.99 , 0x04 }, +}; + + +static struct tuner_params tuner_tua6034_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_tua6034_ntsc_ranges, + .count = ARRAY_SIZE(tuner_tua6034_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_YMEC_TVF66T5_B_DFF - Philips PAL ------------ */ + +static struct tuner_range tuner_ymec_tvf66t5_b_dff_pal_ranges[] = { + { 16 * 160.25 /*MHz*/, 0x01, }, + { 16 * 464.25 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x08, }, +}; + +static struct tuner_params tuner_ymec_tvf66t5_b_dff_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_ymec_tvf66t5_b_dff_pal_ranges, + .count = ARRAY_SIZE(tuner_ymec_tvf66t5_b_dff_pal_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_LG_NTSC_TALN_MINI - LGINNOTEK NTSC ------------ */ + +static struct tuner_range tuner_lg_taln_mini_ntsc_ranges[] = { + { 16 * 137.25 /*MHz*/, 0x01, }, + { 16 * 373.25 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x08, }, +}; + +static struct tuner_params tuner_lg_taln_mini_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_lg_taln_mini_ntsc_ranges, + .count = ARRAY_SIZE(tuner_lg_taln_mini_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* ------------ TUNER_PHILIPS_TD1316 - Philips PAL ------------ */ + +static struct tuner_range tuner_philips_td1316_pal_ranges[] = { + { 16 * 160.00 /*MHz*/, 0xa1, }, + { 16 * 442.00 /*MHz*/, 0xa2, }, + { 16 * 999.99 , 0xa4, }, +}; + +static struct tuner_params tuner_philips_td1316_params[] = { + { + .type = TUNER_PARAM_TYPE_PAL, + .ranges = tuner_philips_td1316_pal_ranges, + .count = ARRAY_SIZE(tuner_philips_td1316_pal_ranges), + .config = 0xc8, + }, +}; + +/* ------------ TUNER_PHILIPS_TUV1236D - Philips ATSC ------------ */ + +static struct tuner_range tuner_tuv1236d_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0x01, }, + { 16 * 454.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x04, }, +}; + + +static struct tuner_params tuner_tuner_tuv1236d_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_tuv1236d_ntsc_ranges, + .count = ARRAY_SIZE(tuner_tuv1236d_ntsc_ranges), + .config = 0xce, + }, +}; + +/* ------------ TUNER_TNF_5335MF - Philips NTSC ------------ */ + +static struct tuner_range tuner_tnf_5335mf_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0x01, }, + { 16 * 454.00 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x04, }, +}; + +static struct tuner_params tuner_tnf_5335mf_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_tnf_5335mf_ntsc_ranges, + .count = ARRAY_SIZE(tuner_tnf_5335mf_ntsc_ranges), + .config = 0x8e, + }, +}; + +/* 70-79 */ +/* ------------ TUNER_SAMSUNG_TCPN_2121P30A - Samsung NTSC ------------ */ + +static struct tuner_range tuner_samsung_tcpn_2121p30a_ntsc_ranges[] = { + { 16 * 175.75 /*MHz*/, 0x01, }, + { 16 * 410.25 /*MHz*/, 0x02, }, + { 16 * 999.99 , 0x08, }, +}; + +static struct tuner_params tuner_samsung_tcpn_2121p30a_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_samsung_tcpn_2121p30a_ntsc_ranges, + .count = ARRAY_SIZE(tuner_samsung_tcpn_2121p30a_ntsc_ranges), + .config = 0xce, + }, +}; + +/* --------------------------------------------------------------------- */ + +struct tunertype tuners[] = { + /* 0-9 */ + [TUNER_TEMIC_PAL] = { /* TEMIC PAL */ + .name = "Temic PAL (4002 FH5)", + .params = tuner_temic_pal_params, + }, + [TUNER_PHILIPS_PAL_I] = { /* Philips PAL_I */ + .name = "Philips PAL_I (FI1246 and compatibles)", + .params = tuner_philips_pal_i_params, + }, + [TUNER_PHILIPS_NTSC] = { /* Philips NTSC */ + .name = "Philips NTSC (FI1236,FM1236 and compatibles)", + .params = tuner_philips_ntsc_params, + }, + [TUNER_PHILIPS_SECAM] = { /* Philips SECAM */ + .name = "Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF)", + .params = tuner_philips_secam_params, + }, + [TUNER_ABSENT] = { /* Tuner Absent */ + .name = "NoTuner", + }, + [TUNER_PHILIPS_PAL] = { /* Philips PAL */ + .name = "Philips PAL_BG (FI1216 and compatibles)", + .params = tuner_philips_pal_params, + }, + [TUNER_TEMIC_NTSC] = { /* TEMIC NTSC */ + .name = "Temic NTSC (4032 FY5)", + .params = tuner_temic_ntsc_params, + }, + [TUNER_TEMIC_PAL_I] = { /* TEMIC PAL_I */ + .name = "Temic PAL_I (4062 FY5)", + .params = tuner_temic_pal_i_params, + }, + [TUNER_TEMIC_4036FY5_NTSC] = { /* TEMIC NTSC */ + .name = "Temic NTSC (4036 FY5)", + .params = tuner_temic_4036fy5_ntsc_params, + }, + [TUNER_ALPS_TSBH1_NTSC] = { /* TEMIC NTSC */ + .name = "Alps HSBH1", + .params = tuner_alps_tsbh1_ntsc_params, + }, + + /* 10-19 */ + [TUNER_ALPS_TSBE1_PAL] = { /* TEMIC PAL */ + .name = "Alps TSBE1", + .params = tuner_alps_tsb_1_params, + }, + [TUNER_ALPS_TSBB5_PAL_I] = { /* Alps PAL_I */ + .name = "Alps TSBB5", + .params = tuner_alps_tsbb5_params, + }, + [TUNER_ALPS_TSBE5_PAL] = { /* Alps PAL */ + .name = "Alps TSBE5", + .params = tuner_alps_tsbe5_params, + }, + [TUNER_ALPS_TSBC5_PAL] = { /* Alps PAL */ + .name = "Alps TSBC5", + .params = tuner_alps_tsbc5_params, + }, + [TUNER_TEMIC_4006FH5_PAL] = { /* TEMIC PAL */ + .name = "Temic PAL_BG (4006FH5)", + .params = tuner_temic_4006fh5_params, + }, + [TUNER_ALPS_TSHC6_NTSC] = { /* Alps NTSC */ + .name = "Alps TSCH6", + .params = tuner_alps_tshc6_params, + }, + [TUNER_TEMIC_PAL_DK] = { /* TEMIC PAL */ + .name = "Temic PAL_DK (4016 FY5)", + .params = tuner_temic_pal_dk_params, + }, + [TUNER_PHILIPS_NTSC_M] = { /* Philips NTSC */ + .name = "Philips NTSC_M (MK2)", + .params = tuner_philips_ntsc_m_params, + }, + [TUNER_TEMIC_4066FY5_PAL_I] = { /* TEMIC PAL_I */ + .name = "Temic PAL_I (4066 FY5)", + .params = tuner_temic_4066fy5_pal_i_params, + }, + [TUNER_TEMIC_4006FN5_MULTI_PAL] = { /* TEMIC PAL */ + .name = "Temic PAL* auto (4006 FN5)", + .params = tuner_temic_4006fn5_multi_params, + }, + + /* 20-29 */ + [TUNER_TEMIC_4009FR5_PAL] = { /* TEMIC PAL */ + .name = "Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5)", + .params = tuner_temic_4009f_5_params, + }, + [TUNER_TEMIC_4039FR5_NTSC] = { /* TEMIC NTSC */ + .name = "Temic NTSC (4039 FR5)", + .params = tuner_temic_4039fr5_params, + }, + [TUNER_TEMIC_4046FM5] = { /* TEMIC PAL */ + .name = "Temic PAL/SECAM multi (4046 FM5)", + .params = tuner_temic_4046fm5_params, + }, + [TUNER_PHILIPS_PAL_DK] = { /* Philips PAL */ + .name = "Philips PAL_DK (FI1256 and compatibles)", + .params = tuner_philips_pal_dk_params, + }, + [TUNER_PHILIPS_FQ1216ME] = { /* Philips PAL */ + .name = "Philips PAL/SECAM multi (FQ1216ME)", + .params = tuner_philips_fq1216me_params, + }, + [TUNER_LG_PAL_I_FM] = { /* LGINNOTEK PAL_I */ + .name = "LG PAL_I+FM (TAPC-I001D)", + .params = tuner_lg_pal_i_fm_params, + }, + [TUNER_LG_PAL_I] = { /* LGINNOTEK PAL_I */ + .name = "LG PAL_I (TAPC-I701D)", + .params = tuner_lg_pal_i_params, + }, + [TUNER_LG_NTSC_FM] = { /* LGINNOTEK NTSC */ + .name = "LG NTSC+FM (TPI8NSR01F)", + .params = tuner_lg_ntsc_fm_params, + }, + [TUNER_LG_PAL_FM] = { /* LGINNOTEK PAL */ + .name = "LG PAL_BG+FM (TPI8PSB01D)", + .params = tuner_lg_pal_fm_params, + }, + [TUNER_LG_PAL] = { /* LGINNOTEK PAL */ + .name = "LG PAL_BG (TPI8PSB11D)", + .params = tuner_lg_pal_params, + }, + + /* 30-39 */ + [TUNER_TEMIC_4009FN5_MULTI_PAL_FM] = { /* TEMIC PAL */ + .name = "Temic PAL* auto + FM (4009 FN5)", + .params = tuner_temic_4009_fn5_multi_pal_fm_params, + }, + [TUNER_SHARP_2U5JF5540_NTSC] = { /* SHARP NTSC */ + .name = "SHARP NTSC_JP (2U5JF5540)", + .params = tuner_sharp_2u5jf5540_params, + }, + [TUNER_Samsung_PAL_TCPM9091PD27] = { /* Samsung PAL */ + .name = "Samsung PAL TCPM9091PD27", + .params = tuner_samsung_pal_tcpm9091pd27_params, + }, + [TUNER_MT2032] = { /* Microtune PAL|NTSC */ + .name = "MT20xx universal", + /* see mt20xx.c for details */ }, + [TUNER_TEMIC_4106FH5] = { /* TEMIC PAL */ + .name = "Temic PAL_BG (4106 FH5)", + .params = tuner_temic_4106fh5_params, + }, + [TUNER_TEMIC_4012FY5] = { /* TEMIC PAL */ + .name = "Temic PAL_DK/SECAM_L (4012 FY5)", + .params = tuner_temic_4012fy5_params, + }, + [TUNER_TEMIC_4136FY5] = { /* TEMIC NTSC */ + .name = "Temic NTSC (4136 FY5)", + .params = tuner_temic_4136_fy5_params, + }, + [TUNER_LG_PAL_NEW_TAPC] = { /* LGINNOTEK PAL */ + .name = "LG PAL (newer TAPC series)", + .params = tuner_lg_pal_new_tapc_params, + }, + [TUNER_PHILIPS_FM1216ME_MK3] = { /* Philips PAL */ + .name = "Philips PAL/SECAM multi (FM1216ME MK3)", + .params = tuner_fm1216me_mk3_params, + }, + [TUNER_LG_NTSC_NEW_TAPC] = { /* LGINNOTEK NTSC */ + .name = "LG NTSC (newer TAPC series)", + .params = tuner_lg_ntsc_new_tapc_params, + }, + + /* 40-49 */ + [TUNER_HITACHI_NTSC] = { /* HITACHI NTSC */ + .name = "HITACHI V7-J180AT", + .params = tuner_hitachi_ntsc_params, + }, + [TUNER_PHILIPS_PAL_MK] = { /* Philips PAL */ + .name = "Philips PAL_MK (FI1216 MK)", + .params = tuner_philips_pal_mk_params, + }, + [TUNER_PHILIPS_ATSC] = { /* Philips ATSC */ + .name = "Philips 1236D ATSC/NTSC dual in", + .params = tuner_philips_atsc_params, + }, + [TUNER_PHILIPS_FM1236_MK3] = { /* Philips NTSC */ + .name = "Philips NTSC MK3 (FM1236MK3 or FM1236/F)", + .params = tuner_fm1236_mk3_params, + }, + [TUNER_PHILIPS_4IN1] = { /* Philips NTSC */ + .name = "Philips 4 in 1 (ATI TV Wonder Pro/Conexant)", + .params = tuner_philips_4in1_params, + }, + [TUNER_MICROTUNE_4049FM5] = { /* Microtune PAL */ + .name = "Microtune 4049 FM5", + .params = tuner_microtune_4049_fm5_params, + }, + [TUNER_PANASONIC_VP27] = { /* Panasonic NTSC */ + .name = "Panasonic VP27s/ENGE4324D", + .params = tuner_panasonic_vp27_params, + }, + [TUNER_LG_NTSC_TAPE] = { /* LGINNOTEK NTSC */ + .name = "LG NTSC (TAPE series)", + .params = tuner_lg_ntsc_tape_params, + }, + [TUNER_TNF_8831BGFF] = { /* Philips PAL */ + .name = "Tenna TNF 8831 BGFF)", + .params = tuner_tnf_8831bgff_params, + }, + [TUNER_MICROTUNE_4042FI5] = { /* Microtune NTSC */ + .name = "Microtune 4042 FI5 ATSC/NTSC dual in", + .params = tuner_microtune_4042fi5_params, + }, + + /* 50-59 */ + [TUNER_TCL_2002N] = { /* TCL NTSC */ + .name = "TCL 2002N", + .params = tuner_tcl_2002n_params, + }, + [TUNER_PHILIPS_FM1256_IH3] = { /* Philips PAL */ + .name = "Philips PAL/SECAM_D (FM 1256 I-H3)", + .params = tuner_philips_fm1256_ih3_params, + }, + [TUNER_THOMSON_DTT7610] = { /* THOMSON ATSC */ + .name = "Thomson DTT 7610 (ATSC/NTSC)", + .params = tuner_thomson_dtt7610_params, + }, + [TUNER_PHILIPS_FQ1286] = { /* Philips NTSC */ + .name = "Philips FQ1286", + .params = tuner_philips_fq1286_params, + }, + [TUNER_PHILIPS_TDA8290] = { /* Philips PAL|NTSC */ + .name = "tda8290+75", + /* see tda8290.c for details */ }, + [TUNER_TCL_2002MB] = { /* TCL PAL */ + .name = "TCL 2002MB", + .params = tuner_tcl_2002mb_params, + }, + [TUNER_PHILIPS_FQ1216AME_MK4] = { /* Philips PAL */ + .name = "Philips PAL/SECAM multi (FQ1216AME MK4)", + .params = tuner_philips_fq1216ame_mk4_params, + }, + [TUNER_PHILIPS_FQ1236A_MK4] = { /* Philips NTSC */ + .name = "Philips FQ1236A MK4", + .params = tuner_philips_fq1236a_mk4_params, + }, + [TUNER_YMEC_TVF_8531MF] = { /* Philips NTSC */ + .name = "Ymec TVision TVF-8531MF/8831MF/8731MF", + .params = tuner_ymec_tvf_8531mf_params, + }, + [TUNER_YMEC_TVF_5533MF] = { /* Philips NTSC */ + .name = "Ymec TVision TVF-5533MF", + .params = tuner_ymec_tvf_5533mf_params, + }, + + /* 60-69 */ + [TUNER_THOMSON_DTT761X] = { /* THOMSON ATSC */ + /* DTT 7611 7611A 7612 7613 7613A 7614 7615 7615A */ + .name = "Thomson DTT 761X (ATSC/NTSC)", + .params = tuner_thomson_dtt761x_params, + }, + [TUNER_TENA_9533_DI] = { /* Philips PAL */ + .name = "Tena TNF9533-D/IF/TNF9533-B/DF", + .params = tuner_tena_9533_di_params, + }, + [TUNER_TEA5767] = { /* Philips RADIO */ + .name = "Philips TEA5767HN FM Radio", + /* see tea5767.c for details */ + }, + [TUNER_PHILIPS_FMD1216ME_MK3] = { /* Philips PAL */ + .name = "Philips FMD1216ME MK3 Hybrid Tuner", + .params = tuner_tuner_philips_fmd1216me_mk3_params, + }, + [TUNER_LG_TDVS_H062F] = { /* LGINNOTEK ATSC */ + .name = "LG TDVS-H062F/TUA6034", + .params = tuner_tua6034_params, + }, + [TUNER_YMEC_TVF66T5_B_DFF] = { /* Philips PAL */ + .name = "Ymec TVF66T5-B/DFF", + .params = tuner_ymec_tvf66t5_b_dff_params, + }, + [TUNER_LG_NTSC_TALN_MINI] = { /* LGINNOTEK NTSC */ + .name = "LG NTSC (TALN mini series)", + .params = tuner_lg_taln_mini_params, + }, + [TUNER_PHILIPS_TD1316] = { /* Philips PAL */ + .name = "Philips TD1316 Hybrid Tuner", + .params = tuner_philips_td1316_params, + }, + [TUNER_PHILIPS_TUV1236D] = { /* Philips ATSC */ + .name = "Philips TUV1236D ATSC/NTSC dual in", + .params = tuner_tuner_tuv1236d_params, + }, + [TUNER_TNF_5335MF] = { /* Philips NTSC */ + .name = "Tena TNF 5335 MF", + .params = tuner_tnf_5335mf_params, + }, + + /* 70-79 */ + [TUNER_SAMSUNG_TCPN_2121P30A] = { /* Samsung NTSC */ + .name = "Samsung TCPN 2121P30A", + .params = tuner_samsung_tcpn_2121p30a_params, + }, +}; + +unsigned const int tuner_count = ARRAY_SIZE(tuners); diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index c31bf28b73f..6d03b9b05c6 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -31,6 +31,7 @@ #include <linux/smp_lock.h> #include <media/audiochip.h> +#include <media/v4l2-common.h> #include "tvaudio.h" @@ -46,17 +47,6 @@ MODULE_LICENSE("GPL"); #define UNSET (-1U) -#define tvaudio_info(fmt, arg...) do {\ - printk(KERN_INFO "tvaudio %d-%04x: " fmt, \ - chip->c.adapter->nr, chip->c.addr , ##arg); } while (0) -#define tvaudio_warn(fmt, arg...) do {\ - printk(KERN_WARNING "tvaudio %d-%04x: " fmt, \ - chip->c.adapter->nr, chip->c.addr , ##arg); } while (0) -#define tvaudio_dbg(fmt, arg...) do {\ - if (debug) \ - printk(KERN_INFO "tvaudio %d-%04x: " fmt, \ - chip->c.adapter->nr, chip->c.addr , ##arg); } while (0) - /* ---------------------------------------------------------------------- */ /* our structs */ @@ -131,7 +121,7 @@ struct CHIPSTATE { /* current settings */ __u16 left,right,treble,bass,mode; int prevmode; - int norm; + int radio; /* thread */ pid_t tpid; @@ -142,8 +132,6 @@ struct CHIPSTATE { int watch_stereo; }; -#define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ - /* ---------------------------------------------------------------------- */ /* i2c addresses */ @@ -171,24 +159,24 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) unsigned char buffer[2]; if (-1 == subaddr) { - tvaudio_dbg("%s: chip_write: 0x%x\n", + v4l_dbg(1, debug, &chip->c, "%s: chip_write: 0x%x\n", chip->c.name, val); chip->shadow.bytes[1] = val; buffer[0] = val; if (1 != i2c_master_send(&chip->c,buffer,1)) { - tvaudio_warn("%s: I/O error (write 0x%x)\n", + v4l_warn(&chip->c, "%s: I/O error (write 0x%x)\n", chip->c.name, val); return -1; } } else { - tvaudio_dbg("%s: chip_write: reg%d=0x%x\n", + v4l_dbg(1, debug, &chip->c, "%s: chip_write: reg%d=0x%x\n", chip->c.name, subaddr, val); chip->shadow.bytes[subaddr+1] = val; buffer[0] = subaddr; buffer[1] = val; if (2 != i2c_master_send(&chip->c,buffer,2)) { - tvaudio_warn("%s: I/O error (write reg%d=0x%x)\n", - chip->c.name, subaddr, val); + v4l_warn(&chip->c, "%s: I/O error (write reg%d=0x%x)\n", + chip->c.name, subaddr, val); return -1; } } @@ -212,11 +200,11 @@ static int chip_read(struct CHIPSTATE *chip) unsigned char buffer; if (1 != i2c_master_recv(&chip->c,&buffer,1)) { - tvaudio_warn("%s: I/O error (read)\n", + v4l_warn(&chip->c, "%s: I/O error (read)\n", chip->c.name); return -1; } - tvaudio_dbg("%s: chip_read: 0x%x\n",chip->c.name,buffer); + v4l_dbg(1, debug, &chip->c, "%s: chip_read: 0x%x\n",chip->c.name, buffer); return buffer; } @@ -231,11 +219,11 @@ static int chip_read2(struct CHIPSTATE *chip, int subaddr) write[0] = subaddr; if (2 != i2c_transfer(chip->c.adapter,msgs,2)) { - tvaudio_warn("%s: I/O error (read2)\n", chip->c.name); + v4l_warn(&chip->c, "%s: I/O error (read2)\n", chip->c.name); return -1; } - tvaudio_dbg("%s: chip_read2: reg%d=0x%x\n", - chip->c.name,subaddr,read[0]); + v4l_dbg(1, debug, &chip->c, "%s: chip_read2: reg%d=0x%x\n", + chip->c.name, subaddr,read[0]); return read[0]; } @@ -247,8 +235,8 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) return 0; /* update our shadow register set; print bytes if (debug > 0) */ - tvaudio_dbg("%s: chip_cmd(%s): reg=%d, data:", - chip->c.name,name,cmd->bytes[0]); + v4l_dbg(1, debug, &chip->c, "%s: chip_cmd(%s): reg=%d, data:", + chip->c.name, name,cmd->bytes[0]); for (i = 1; i < cmd->count; i++) { if (debug) printk(" 0x%x",cmd->bytes[i]); @@ -259,7 +247,7 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) /* send data to the chip */ if (cmd->count != i2c_master_send(&chip->c,cmd->bytes,cmd->count)) { - tvaudio_warn("%s: I/O error (%s)\n", chip->c.name, name); + v4l_warn(&chip->c, "%s: I/O error (%s)\n", chip->c.name, name); return -1; } return 0; @@ -286,7 +274,7 @@ static int chip_thread(void *data) daemonize("%s", chip->c.name); allow_signal(SIGTERM); - tvaudio_dbg("%s: thread started\n", chip->c.name); + v4l_dbg(1, debug, &chip->c, "%s: thread started\n", chip->c.name); for (;;) { add_wait_queue(&chip->wq, &wait); @@ -298,10 +286,10 @@ static int chip_thread(void *data) try_to_freeze(); if (chip->done || signal_pending(current)) break; - tvaudio_dbg("%s: thread wakeup\n", chip->c.name); + v4l_dbg(1, debug, &chip->c, "%s: thread wakeup\n", chip->c.name); /* don't do anything for radio or if mode != auto */ - if (chip->norm == VIDEO_MODE_RADIO || chip->mode != 0) + if (chip->radio || chip->mode != 0) continue; /* have a look what's going on */ @@ -311,7 +299,7 @@ static int chip_thread(void *data) mod_timer(&chip->wt, jiffies+2*HZ); } - tvaudio_dbg("%s: thread exiting\n", chip->c.name); + v4l_dbg(1, debug, &chip->c, "%s: thread exiting\n", chip->c.name); complete_and_exit(&chip->texit, 0); return 0; } @@ -322,9 +310,9 @@ static void generic_checkmode(struct CHIPSTATE *chip) int mode = desc->getmode(chip); if (mode == chip->prevmode) - return; + return; - tvaudio_dbg("%s: thread checkmode\n", chip->c.name); + v4l_dbg(1, debug, &chip->c, "%s: thread checkmode\n", chip->c.name); chip->prevmode = mode; if (mode & VIDEO_SOUND_STEREO) @@ -371,7 +359,7 @@ static int tda9840_getmode(struct CHIPSTATE *chip) if (val & TDA9840_ST_STEREO) mode |= VIDEO_SOUND_STEREO; - tvaudio_dbg ("tda9840_getmode(): raw chip read: %d, return: %d\n", + v4l_dbg(1, debug, &chip->c, "tda9840_getmode(): raw chip read: %d, return: %d\n", val, mode); return mode; } @@ -667,7 +655,7 @@ static int tda9873_getmode(struct CHIPSTATE *chip) mode |= VIDEO_SOUND_STEREO; if (val & TDA9873_DUAL) mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; - tvaudio_dbg ("tda9873_getmode(): raw chip read: %d, return: %d\n", + v4l_dbg(1, debug, &chip->c, "tda9873_getmode(): raw chip read: %d, return: %d\n", val, mode); return mode; } @@ -678,12 +666,12 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode) /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) { - tvaudio_dbg("tda9873_setmode(): external input\n"); + v4l_dbg(1, debug, &chip->c, "tda9873_setmode(): external input\n"); return; } - tvaudio_dbg("tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); - tvaudio_dbg("tda9873_setmode(): sw_data = %d\n", sw_data); + v4l_dbg(1, debug, &chip->c, "tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); + v4l_dbg(1, debug, &chip->c, "tda9873_setmode(): sw_data = %d\n", sw_data); switch (mode) { case VIDEO_SOUND_MONO: @@ -704,7 +692,7 @@ static void tda9873_setmode(struct CHIPSTATE *chip, int mode) } chip_write(chip, TDA9873_SW, sw_data); - tvaudio_dbg("tda9873_setmode(): req. mode %d; chip_write: %d\n", + v4l_dbg(1, debug, &chip->c, "tda9873_setmode(): req. mode %d; chip_write: %d\n", mode, sw_data); } @@ -843,7 +831,7 @@ static int tda9874a_setup(struct CHIPSTATE *chip) chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */ } - tvaudio_dbg("tda9874a_setup(): %s [0x%02X].\n", + v4l_dbg(1, debug, &chip->c, "tda9874a_setup(): %s [0x%02X].\n", tda9874a_modelist[tda9874a_STD].name,tda9874a_STD); return 1; } @@ -886,7 +874,7 @@ static int tda9874a_getmode(struct CHIPSTATE *chip) mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; } - tvaudio_dbg("tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", + v4l_dbg(1, debug, &chip->c, "tda9874a_getmode(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", dsr, nsr, necr, mode); return mode; } @@ -932,7 +920,7 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) chip_write(chip, TDA9874A_AOSR, aosr); chip_write(chip, TDA9874A_MDACOSR, mdacosr); - tvaudio_dbg("tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", + v4l_dbg(1, debug, &chip->c, "tda9874a_setmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", mode, aosr, mdacosr); } else { /* dic == 0x07 */ @@ -967,7 +955,7 @@ static void tda9874a_setmode(struct CHIPSTATE *chip, int mode) chip_write(chip, TDA9874A_FMMR, fmmr); chip_write(chip, TDA9874A_AOSR, aosr); - tvaudio_dbg("tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", + v4l_dbg(1, debug, &chip->c, "tda9874a_setmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", mode, fmmr, aosr); } } @@ -981,10 +969,10 @@ static int tda9874a_checkit(struct CHIPSTATE *chip) if(-1 == (sic = chip_read2(chip,TDA9874A_SIC))) return 0; - tvaudio_dbg("tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); + v4l_dbg(1, debug, &chip->c, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); if((dic == 0x11)||(dic == 0x07)) { - tvaudio_info("found tda9874%s.\n", (dic == 0x11) ? "a":"h"); + v4l_info(&chip->c, "found tda9874%s.\n", (dic == 0x11) ? "a":"h"); tda9874a_dic = dic; /* remember device id. */ return 1; } @@ -1196,7 +1184,7 @@ static int ta8874z_getmode(struct CHIPSTATE *chip) }else if (!(val & TA8874Z_B0)){ mode |= VIDEO_SOUND_STEREO; } - /* tvaudio_dbg ("ta8874z_getmode(): raw chip read: 0x%02x, return: 0x%02x\n", val, mode); */ + /* v4l_dbg(1, debug, &chip->c, "ta8874z_getmode(): raw chip read: 0x%02x, return: 0x%02x\n", val, mode); */ return mode; } @@ -1209,7 +1197,7 @@ static void ta8874z_setmode(struct CHIPSTATE *chip, int mode) { int update = 1; audiocmd *t = NULL; - tvaudio_dbg("ta8874z_setmode(): mode: 0x%02x\n", mode); + v4l_dbg(1, debug, &chip->c, "ta8874z_setmode(): mode: 0x%02x\n", mode); switch(mode){ case VIDEO_SOUND_MONO: @@ -1480,17 +1468,16 @@ static int chip_attach(struct i2c_adapter *adap, int addr, int kind) struct CHIPSTATE *chip; struct CHIPDESC *desc; - chip = kmalloc(sizeof(*chip),GFP_KERNEL); + chip = kzalloc(sizeof(*chip),GFP_KERNEL); if (!chip) return -ENOMEM; - memset(chip,0,sizeof(*chip)); memcpy(&chip->c,&client_template,sizeof(struct i2c_client)); chip->c.adapter = adap; chip->c.addr = addr; i2c_set_clientdata(&chip->c, chip); /* find description for the chip */ - tvaudio_dbg("chip found @ 0x%x\n", addr<<1); + v4l_dbg(1, debug, &chip->c, "chip found @ 0x%x\n", addr<<1); for (desc = chiplist; desc->name != NULL; desc++) { if (0 == *(desc->insmodopt)) continue; @@ -1502,22 +1489,22 @@ static int chip_attach(struct i2c_adapter *adap, int addr, int kind) break; } if (desc->name == NULL) { - tvaudio_dbg("no matching chip description found\n"); + v4l_dbg(1, debug, &chip->c, "no matching chip description found\n"); return -EIO; } - tvaudio_info("%s found @ 0x%x (%s)\n", desc->name, addr<<1, adap->name); - if (desc->flags) { - tvaudio_dbg("matches:%s%s%s.\n", - (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", - (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", - (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); - } + v4l_info(&chip->c, "%s found @ 0x%x (%s)\n", desc->name, addr<<1, adap->name); + if (desc->flags) { + v4l_dbg(1, debug, &chip->c, "matches:%s%s%s.\n", + (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", + (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", + (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); + } /* fill required data structures */ - strcpy(chip->c.name,desc->name); + strcpy(chip->c.name, desc->name); chip->type = desc-chiplist; chip->shadow.count = desc->registers+1; - chip->prevmode = -1; + chip->prevmode = -1; /* register */ i2c_attach_client(&chip->c); @@ -1550,7 +1537,7 @@ static int chip_attach(struct i2c_adapter *adap, int addr, int kind) init_completion(&chip->texit); chip->tpid = kernel_thread(chip_thread,(void *)chip,0); if (chip->tpid < 0) - tvaudio_warn("%s: kernel_thread() failed\n", + v4l_warn(&chip->c, "%s: kernel_thread() failed\n", chip->c.name); wake_up_interruptible(&chip->wq); } @@ -1563,17 +1550,8 @@ static int chip_probe(struct i2c_adapter *adap) because dedicated drivers are used */ if ((adap->id == I2C_HW_SAA7146)) return 0; -#ifdef I2C_CLASS_TV_ANALOG if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, chip_attach); -#else - switch (adap->id) { - case I2C_HW_B_BT848: - case I2C_HW_B_RIVA: - case I2C_HW_SAA7134: - return i2c_probe(adap, &addr_data, chip_attach); - } -#endif return 0; } @@ -1604,7 +1582,7 @@ static int chip_command(struct i2c_client *client, struct CHIPSTATE *chip = i2c_get_clientdata(client); struct CHIPDESC *desc = chiplist + chip->type; - tvaudio_dbg("%s: chip_command 0x%x\n",chip->c.name,cmd); + v4l_dbg(1, debug, &chip->c, "%s: chip_command 0x%x\n", chip->c.name, cmd); switch (cmd) { case AUDC_SET_INPUT: @@ -1617,14 +1595,14 @@ static int chip_command(struct i2c_client *client, break; case AUDC_SET_RADIO: - chip->norm = VIDEO_MODE_RADIO; + chip->radio = 1; chip->watch_stereo = 0; /* del_timer(&chip->wt); */ break; /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ + kernel pointer here... */ case VIDIOCGAUDIO: { struct video_audio *va = arg; @@ -1643,7 +1621,7 @@ static int chip_command(struct i2c_client *client, va->bass = chip->bass; va->treble = chip->treble; } - if (chip->norm != VIDEO_MODE_RADIO) { + if (!chip->radio) { if (desc->getmode) va->mode = desc->getmode(chip); else @@ -1678,15 +1656,80 @@ static int chip_command(struct i2c_client *client, } break; } - case VIDIOCSCHAN: + + case VIDIOC_S_TUNER: { - struct video_channel *vc = arg; + struct v4l2_tuner *vt = arg; + int mode = 0; - chip->norm = vc->norm; + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + mode = VIDEO_SOUND_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + mode = VIDEO_SOUND_STEREO; + break; + case V4L2_TUNER_MODE_LANG1: + mode = VIDEO_SOUND_LANG1; + break; + case V4L2_TUNER_MODE_LANG2: + mode = VIDEO_SOUND_LANG2; + break; + default: + break; + } + + if (desc->setmode && mode) { + chip->watch_stereo = 0; + /* del_timer(&chip->wt); */ + chip->mode = mode; + desc->setmode(chip, mode); + } break; } - case VIDIOCSFREQ: + + case VIDIOC_G_TUNER: { + struct v4l2_tuner *vt = arg; + int mode = VIDEO_SOUND_MONO; + + if (chip->radio) + break; + vt->audmode = 0; + vt->rxsubchans = 0; + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + + if (desc->getmode) + mode = desc->getmode(chip); + + if (mode & VIDEO_SOUND_MONO) + vt->rxsubchans |= V4L2_TUNER_SUB_MONO; + if (mode & VIDEO_SOUND_STEREO) + vt->rxsubchans |= V4L2_TUNER_SUB_STEREO; + if (mode & VIDEO_SOUND_LANG1) + vt->rxsubchans |= V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + + mode = chip->mode; + if (mode & VIDEO_SOUND_MONO) + vt->audmode = V4L2_TUNER_MODE_MONO; + if (mode & VIDEO_SOUND_STEREO) + vt->audmode = V4L2_TUNER_MODE_STEREO; + if (mode & VIDEO_SOUND_LANG1) + vt->audmode = V4L2_TUNER_MODE_LANG1; + if (mode & VIDEO_SOUND_LANG2) + vt->audmode = V4L2_TUNER_MODE_LANG2; + break; + } + + case VIDIOCSCHAN: + case VIDIOC_S_STD: + chip->radio = 0; + break; + + case VIDIOCSFREQ: + case VIDIOC_S_FREQUENCY: chip->mode = 0; /* automatic */ if (desc->checkmode) { desc->setmode(chip,VIDEO_SOUND_MONO); @@ -1695,17 +1738,16 @@ static int chip_command(struct i2c_client *client, mod_timer(&chip->wt, jiffies+2*HZ); /* the thread will call checkmode() later */ } - } + break; } return 0; } - static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "generic i2c audio driver", + .driver = { + .name = "tvaudio", + }, .id = I2C_DRIVERID_TVAUDIO, - .flags = I2C_DF_NOTIFY, .attach_adapter = chip_probe, .detach_client = chip_detach, .command = chip_command, @@ -1714,7 +1756,6 @@ static struct i2c_driver driver = { static struct i2c_client client_template = { .name = "(unset)", - .flags = I2C_CLIENT_ALLOW_USE, .driver = &driver, }; diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 72e8741e8b5..582551b0969 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -40,6 +40,7 @@ #include <media/tuner.h> #include <media/tveeprom.h> +#include <media/v4l2-common.h> #include <media/audiochip.h> MODULE_DESCRIPTION("i2c Hauppauge eeprom decoder driver"); @@ -52,21 +53,19 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define STRM(array,i) (i < sizeof(array)/sizeof(char*) ? array[i] : "unknown") -#define tveeprom_info(fmt, arg...) do {\ - printk(KERN_INFO "tveeprom %d-%04x: " fmt, \ - c->adapter->nr, c->addr , ##arg); } while (0) -#define tveeprom_warn(fmt, arg...) do {\ - printk(KERN_WARNING "tveeprom %d-%04x: " fmt, \ - c->adapter->nr, c->addr , ##arg); } while (0) -#define tveeprom_dbg(fmt, arg...) do {\ +#define tveeprom_info(fmt, arg...) \ + v4l_printk(KERN_INFO, "tveeprom", c->adapter, c->addr, fmt , ## arg) +#define tveeprom_warn(fmt, arg...) \ + v4l_printk(KERN_WARNING, "tveeprom", c->adapter, c->addr, fmt , ## arg) +#define tveeprom_dbg(fmt, arg...) do { \ if (debug) \ - printk(KERN_INFO "tveeprom %d-%04x: " fmt, \ - c->adapter->nr, c->addr , ##arg); } while (0) - - -/* ----------------------------------------------------------------------- */ -/* some hauppauge specific stuff */ + v4l_printk(KERN_DEBUG, "tveeprom", c->adapter, c->addr, fmt , ## arg); \ + } while (0) +/* + * The Hauppauge eeprom uses an 8bit field to determine which + * tuner formats the tuner supports. + */ static struct HAUPPAUGE_TUNER_FMT { int id; @@ -74,14 +73,14 @@ static struct HAUPPAUGE_TUNER_FMT } hauppauge_tuner_fmt[] = { - { 0x00000000, " unknown1" }, - { 0x00000000, " unknown2" }, - { 0x00000007, " PAL(B/G)" }, - { 0x00001000, " NTSC(M)" }, - { 0x00000010, " PAL(I)" }, - { 0x00400000, " SECAM(L/L')" }, - { 0x00000e00, " PAL(D/K)" }, - { 0x03000000, " ATSC Digital" }, + { V4L2_STD_UNKNOWN," UNKNOWN" }, + { V4L2_STD_UNKNOWN," FM" }, + { V4L2_STD_PAL_BG, " PAL(B/G)" }, + { V4L2_STD_NTSC_M, " NTSC(M)" }, + { V4L2_STD_PAL_I, " PAL(I)" }, + { V4L2_STD_SECAM_L," SECAM(L/L')" }, + { V4L2_STD_PAL_DK, " PAL(D/D1/K)" }, + { V4L2_STD_ATSC, " ATSC/DVB Digital" }, }; /* This is the full list of possible tuners. Many thanks to Hauppauge for @@ -191,7 +190,7 @@ hauppauge_tuner[] = { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MI 3"}, { TUNER_TCL_2002N, "TCL 2002N 6A"}, { TUNER_PHILIPS_FM1236_MK3, "Philips FQ1236 MK3"}, - { TUNER_ABSENT, "Samsung TCPN 2121P30A"}, + { TUNER_SAMSUNG_TCPN_2121P30A, "Samsung TCPN 2121P30A"}, { TUNER_ABSENT, "Samsung TCPE 4121P30A"}, { TUNER_PHILIPS_FM1216ME_MK3, "TCL MFPE05 2"}, /* 90-99 */ @@ -206,16 +205,30 @@ hauppauge_tuner[] = { TUNER_ABSENT, "TCL 2002MI_3H"}, { TUNER_TCL_2002N, "TCL 2002N 5H"}, /* 100-109 */ - { TUNER_ABSENT, "Philips FMD1216ME"}, + { TUNER_PHILIPS_FMD1216ME_MK3, "Philips FMD1216ME"}, { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, { TUNER_ABSENT, "Panasonic ENV57H12D5"}, - { TUNER_ABSENT, "TCL MFNM05-4"}, + { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, { TUNER_ABSENT, "TCL MNM05-4"}, { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, { TUNER_ABSENT, "TCL MQNM05-4"}, { TUNER_ABSENT, "LG TAPC-W701D"}, { TUNER_ABSENT, "TCL 9886P-WM"}, { TUNER_ABSENT, "TCL 1676NM-WM"}, + /* 110-119 */ + { TUNER_ABSENT, "Thompson DTT75105"}, + { TUNER_ABSENT, "Conexant_CX24109"}, + { TUNER_ABSENT, "TCL M2523_5N_E"}, + { TUNER_ABSENT, "TCL M2523_3DB_E"}, + { TUNER_ABSENT, "Philips 8275A"}, + { TUNER_ABSENT, "Microtune MT2060"}, + { TUNER_ABSENT, "Philips FM1236 MK5"}, + { TUNER_ABSENT, "Philips FM1216ME MK5"}, + { TUNER_ABSENT, "TCL M2523_3DI_E"}, + { TUNER_ABSENT, "Samsung THPD5222FG30A"}, + /* 120-129 */ + { TUNER_ABSENT, "Xceive XC3028"}, + { TUNER_ABSENT, "Philips FQ1216LME MK5"}, }; static struct HAUPPAUGE_AUDIOIC @@ -325,6 +338,7 @@ static int hasRadioTuner(int tunerType) case 78: //PNPEnv_TUNER_TDA8275C1_8290_FM: case 89: //PNPEnv_TUNER_TCL_MFPE05_2: case 92: //PNPEnv_TUNER_PHILIPS_FQ1236A_MK4: + case 105: return 1; } return 0; @@ -368,10 +382,15 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, memset(tvee, 0, sizeof(*tvee)); done = len = beenhere = 0; - /* Hack for processing eeprom for em28xx */ - if ((eeprom_data[0]==0x1a)&&(eeprom_data[1]==0xeb)&& - (eeprom_data[2]==0x67)&&(eeprom_data[3]==0x95)) - start=0xa0; + /* Hack for processing eeprom for em28xx and cx 2388x*/ + if ((eeprom_data[0] == 0x1a) && (eeprom_data[1] == 0xeb) && + (eeprom_data[2] == 0x67) && (eeprom_data[3] == 0x95)) + start=0xa0; /* Generic em28xx offset */ + else if (((eeprom_data[0] & 0xe1) == 0x01) && + (eeprom_data[1] == 0x00) && + (eeprom_data[2] == 0x00) && + (eeprom_data[8] == 0x84)) + start=8; /* Generic cx2388x offset */ else start=0; @@ -448,6 +467,17 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, eeprom_data[i+5] + (eeprom_data[i+6] << 8) + (eeprom_data[i+7] << 16); + + if ( (eeprom_data[i + 8] && 0xf0) && + (tvee->serial_number < 0xffffff) ) { + tvee->MAC_address[0] = 0x00; + tvee->MAC_address[1] = 0x0D; + tvee->MAC_address[2] = 0xFE; + tvee->MAC_address[3] = eeprom_data[i + 7]; + tvee->MAC_address[4] = eeprom_data[i + 6]; + tvee->MAC_address[5] = eeprom_data[i + 5]; + tvee->has_MAC_address = 1; + } break; case 0x05: @@ -466,11 +496,14 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, case 0x06: /* tag 'ModelRev' */ tvee->model = - eeprom_data[i+1] + - (eeprom_data[i+2] << 8); - tvee->revision = eeprom_data[i+5] + - (eeprom_data[i+6] << 8) + - (eeprom_data[i+7] << 16); + eeprom_data[i + 1] + + (eeprom_data[i + 2] << 8) + + (eeprom_data[i + 3] << 16) + + (eeprom_data[i + 4] << 24); + tvee->revision = + eeprom_data[i +5 ] + + (eeprom_data[i + 6] << 8) + + (eeprom_data[i + 7] << 16); break; case 0x07: @@ -563,6 +596,8 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, t_name2 = "unknown"; } + tvee->tuner_hauppauge_model = tuner1; + tvee->tuner2_hauppauge_model = tuner2; tvee->tuner_formats = 0; tvee->tuner2_formats = 0; for (i = j = 0; i < 8; i++) { @@ -578,6 +613,12 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n", tvee->model, tvee->rev_str, tvee->serial_number); + if (tvee->has_MAC_address == 1) { + tveeprom_info("MAC address is %02X-%02X-%02X-%02X-%02X-%02X\n", + tvee->MAC_address[0], tvee->MAC_address[1], + tvee->MAC_address[2], tvee->MAC_address[3], + tvee->MAC_address[4], tvee->MAC_address[5]); + } tveeprom_info("tuner model is %s (idx %d, type %d)\n", t_name1, tuner1, tvee->tuner_type); tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", @@ -678,8 +719,7 @@ tveeprom_command(struct i2c_client *client, switch (cmd) { case 0: - buf = kmalloc(256,GFP_KERNEL); - memset(buf,0,256); + buf = kzalloc(256,GFP_KERNEL); tveeprom_read(client,buf,256); tveeprom_hauppauge_analog(client, &eeprom,buf); kfree(buf); @@ -702,16 +742,15 @@ tveeprom_detect_client(struct i2c_adapter *adapter, { struct i2c_client *client; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (NULL == client) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_tveeprom; - client->flags = I2C_CLIENT_ALLOW_USE; snprintf(client->name, sizeof(client->name), "tveeprom"); - i2c_attach_client(client); + i2c_attach_client(client); + return 0; } @@ -736,10 +775,10 @@ tveeprom_detach_client (struct i2c_client *client) } static struct i2c_driver i2c_driver_tveeprom = { - .owner = THIS_MODULE, - .name = "tveeprom", + .driver = { + .name = "tveeprom", + }, .id = I2C_DRIVERID_TVEEPROM, - .flags = I2C_DF_NOTIFY, .attach_adapter = tveeprom_attach_adapter, .detach_client = tveeprom_detach_client, .command = tveeprom_command, diff --git a/drivers/media/video/tvmixer.c b/drivers/media/video/tvmixer.c index 8318bd1aad0..9e86caeb96a 100644 --- a/drivers/media/video/tvmixer.c +++ b/drivers/media/video/tvmixer.c @@ -227,17 +227,11 @@ static int tvmixer_release(struct inode *inode, struct file *file) } static struct i2c_driver driver = { -#ifdef I2C_PEC - .owner = THIS_MODULE, -#endif - .name = "tv card mixer driver", + .driver = { + .name = "tvmixer", + }, .id = I2C_DRIVERID_TVMIXER, -#ifdef I2C_DF_DUMMY - .flags = I2C_DF_DUMMY, -#else - .flags = I2C_DF_NOTIFY, .detach_adapter = tvmixer_adapters, -#endif .attach_adapter = tvmixer_adapters, .detach_client = tvmixer_clients, }; @@ -269,22 +263,8 @@ static int tvmixer_clients(struct i2c_client *client) struct video_audio va; int i,minor; -#ifdef I2C_CLASS_TV_ANALOG if (!(client->adapter->class & I2C_CLASS_TV_ANALOG)) return -1; -#else - /* TV card ??? */ - switch (client->adapter->id) { - case I2C_HW_SMBUS_VOODOO3: - case I2C_HW_B_BT848: - case I2C_HW_B_RIVA: - /* ok, have a look ... */ - break; - default: - /* ignore that one */ - return -1; - } -#endif /* unregister ?? */ for (i = 0; i < DEV_MAX; i++) { diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 81e6d4494e7..fad9ea0ae4f 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -9,6 +9,7 @@ #include <linux/videodev.h> #include <linux/delay.h> #include <linux/video_decoder.h> +#include <media/v4l2-common.h> #include "tvp5150_reg.h" @@ -28,33 +29,38 @@ static int debug = 0; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#define dprintk(num, format, args...) \ +#define tvp5150_info(fmt, arg...) do { \ + printk(KERN_INFO "%s %d-%04x: " fmt, c->driver->driver.name, \ + i2c_adapter_id(c->adapter), c->addr , ## arg); } while (0) +#define tvp5150_dbg(num, fmt, arg...) \ do { \ if (debug >= num) \ - printk(format , ##args); \ - } while (0) + printk(KERN_DEBUG "%s debug %d-%04x: " fmt,\ + c->driver->driver.name, \ + i2c_adapter_id(c->adapter), \ + c->addr , ## arg); } while (0) /* supported controls */ static struct v4l2_queryctrl tvp5150_qctrl[] = { { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 0, - .flags = 0, - }, { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 0x1, - .default_value = 0x10, - .flags = 0, - }, { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 0x10, + .flags = 0, + }, { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Saturation", @@ -63,16 +69,16 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { .step = 0x1, .default_value = 0x10, .flags = 0, - }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -128, - .maximum = 127, - .step = 0x1, - .default_value = 0x10, - .flags = 0, - } + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 0x1, + .default_value = 0x10, + .flags = 0, + } }; struct tvp5150 { @@ -87,19 +93,21 @@ struct tvp5150 { int sat; }; -static inline int tvp5150_read(struct i2c_client *c, unsigned char addr) +static int tvp5150_read(struct i2c_client *c, unsigned char addr) { unsigned char buffer[1]; int rc; buffer[0] = addr; if (1 != (rc = i2c_master_send(c, buffer, 1))) - dprintk(0, "i2c i/o error: rc == %d (should be 1)\n", rc); + tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc); msleep(10); if (1 != (rc = i2c_master_recv(c, buffer, 1))) - dprintk(0, "i2c i/o error: rc == %d (should be 1)\n", rc); + tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 1)\n", rc); + + tvp5150_dbg(2, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); return (buffer[0]); } @@ -109,13 +117,12 @@ static inline void tvp5150_write(struct i2c_client *c, unsigned char addr, { unsigned char buffer[2]; int rc; -/* struct tvp5150 *core = i2c_get_clientdata(c); */ buffer[0] = addr; buffer[1] = value; - dprintk(1, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); + tvp5150_dbg(2, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); if (2 != (rc = i2c_master_send(c, buffer, 2))) - dprintk(0, "i2c i/o error: rc == %d (should be 2)\n", rc); + tvp5150_dbg(0, "i2c i/o error: rc == %d (should be 2)\n", rc); } static void dump_reg(struct i2c_client *c) @@ -437,48 +444,346 @@ enum tvp5150_input { static inline void tvp5150_selmux(struct i2c_client *c, enum tvp5150_input input) { + int opmode=0; + struct tvp5150 *decoder = i2c_get_clientdata(c); if (!decoder->enable) input |= TVP5150_BLACK_SCREEN; + switch (input) { + case TVP5150_ANALOG_CH0: + case TVP5150_ANALOG_CH1: + opmode=0x30; /* TV Mode */ + break; + default: + opmode=0; /* Auto Mode */ + break; + } + + tvp5150_write(c, TVP5150_OP_MODE_CTL, opmode); tvp5150_write(c, TVP5150_VD_IN_SRC_SEL_1, input); }; -static inline void tvp5150_reset(struct i2c_client *c) +struct i2c_reg_value { + unsigned char reg; + unsigned char value; +}; + +/* Default values as sugested at TVP5150AM1 datasheet */ +static const struct i2c_reg_value tvp5150_init_default[] = { + { /* 0x00 */ + TVP5150_VD_IN_SRC_SEL_1,0x00 + }, + { /* 0x01 */ + TVP5150_ANAL_CHL_CTL,0x15 + }, + { /* 0x02 */ + TVP5150_OP_MODE_CTL,0x00 + }, + { /* 0x03 */ + TVP5150_MISC_CTL,0x01 + }, + { /* 0x06 */ + TVP5150_COLOR_KIL_THSH_CTL,0x10 + }, + { /* 0x07 */ + TVP5150_LUMA_PROC_CTL_1,0x60 + }, + { /* 0x08 */ + TVP5150_LUMA_PROC_CTL_2,0x00 + }, + { /* 0x09 */ + TVP5150_BRIGHT_CTL,0x80 + }, + { /* 0x0a */ + TVP5150_SATURATION_CTL,0x80 + }, + { /* 0x0b */ + TVP5150_HUE_CTL,0x00 + }, + { /* 0x0c */ + TVP5150_CONTRAST_CTL,0x80 + }, + { /* 0x0d */ + TVP5150_DATA_RATE_SEL,0x47 + }, + { /* 0x0e */ + TVP5150_LUMA_PROC_CTL_3,0x00 + }, + { /* 0x0f */ + TVP5150_CONF_SHARED_PIN,0x08 + }, + { /* 0x11 */ + TVP5150_ACT_VD_CROP_ST_MSB,0x00 + }, + { /* 0x12 */ + TVP5150_ACT_VD_CROP_ST_LSB,0x00 + }, + { /* 0x13 */ + TVP5150_ACT_VD_CROP_STP_MSB,0x00 + }, + { /* 0x14 */ + TVP5150_ACT_VD_CROP_STP_LSB,0x00 + }, + { /* 0x15 */ + TVP5150_GENLOCK,0x01 + }, + { /* 0x16 */ + TVP5150_HORIZ_SYNC_START,0x80 + }, + { /* 0x18 */ + TVP5150_VERT_BLANKING_START,0x00 + }, + { /* 0x19 */ + TVP5150_VERT_BLANKING_STOP,0x00 + }, + { /* 0x1a */ + TVP5150_CHROMA_PROC_CTL_1,0x0c + }, + { /* 0x1b */ + TVP5150_CHROMA_PROC_CTL_2,0x14 + }, + { /* 0x1c */ + TVP5150_INT_RESET_REG_B,0x00 + }, + { /* 0x1d */ + TVP5150_INT_ENABLE_REG_B,0x00 + }, + { /* 0x1e */ + TVP5150_INTT_CONFIG_REG_B,0x00 + }, + { /* 0x28 */ + TVP5150_VIDEO_STD,0x00 + }, + { /* 0x2e */ + TVP5150_MACROVISION_ON_CTR,0x0f + }, + { /* 0x2f */ + TVP5150_MACROVISION_OFF_CTR,0x01 + }, + { /* 0xbb */ + TVP5150_TELETEXT_FIL_ENA,0x00 + }, + { /* 0xc0 */ + TVP5150_INT_STATUS_REG_A,0x00 + }, + { /* 0xc1 */ + TVP5150_INT_ENABLE_REG_A,0x00 + }, + { /* 0xc2 */ + TVP5150_INT_CONF,0x04 + }, + { /* 0xc8 */ + TVP5150_FIFO_INT_THRESHOLD,0x80 + }, + { /* 0xc9 */ + TVP5150_FIFO_RESET,0x00 + }, + { /* 0xca */ + TVP5150_LINE_NUMBER_INT,0x00 + }, + { /* 0xcb */ + TVP5150_PIX_ALIGN_REG_LOW,0x4e + }, + { /* 0xcc */ + TVP5150_PIX_ALIGN_REG_HIGH,0x00 + }, + { /* 0xcd */ + TVP5150_FIFO_OUT_CTRL,0x01 + }, + { /* 0xcf */ + TVP5150_FULL_FIELD_ENA_1,0x00 + }, + { /* 0xd0 */ + TVP5150_FULL_FIELD_ENA_2,0x00 + }, + { /* 0xfc */ + TVP5150_FULL_FIELD_MODE_REG,0x7f + }, + { /* end of data */ + 0xff,0xff + } +}; + +/* Default values as sugested at TVP5150AM1 datasheet */ +static const struct i2c_reg_value tvp5150_init_enable[] = { + { + TVP5150_CONF_SHARED_PIN, 2 + },{ /* Automatic offset and AGC enabled */ + TVP5150_ANAL_CHL_CTL, 0x15 + },{ /* Activate YCrCb output 0x9 or 0xd ? */ + TVP5150_MISC_CTL, 0x6f + },{ /* Activates video std autodetection for all standards */ + TVP5150_AUTOSW_MSK, 0x0 + },{ /* Default format: 0x47. For 4:2:2: 0x40 */ + TVP5150_DATA_RATE_SEL, 0x47 + },{ + TVP5150_CHROMA_PROC_CTL_1, 0x0c + },{ + TVP5150_CHROMA_PROC_CTL_2, 0x54 + },{ /* Non documented, but initialized on WinTV USB2 */ + 0x27, 0x20 + },{ + 0xff,0xff + } +}; + +struct i2c_vbi_ram_value { + u16 reg; + unsigned char values[26]; +}; + +static struct i2c_vbi_ram_value vbi_ram_default[] = { - struct tvp5150 *decoder = i2c_get_clientdata(c); + {0x010, /* WST SECAM 6 */ + { 0xaa, 0xaa, 0xff, 0xff , 0xe7, 0x2e, 0x20, 0x26, 0xe6, 0xb4, 0x0e, 0x0, 0x0, 0x0, 0x10, 0x0 } + }, + {0x030, /* WST PAL B 6 */ + { 0xaa, 0xaa, 0xff, 0xff , 0x27, 0x2e, 0x20, 0x2b, 0xa6, 0x72, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0 } + }, + {0x050, /* WST PAL C 6 */ + { 0xaa, 0xaa, 0xff, 0xff , 0xe7, 0x2e, 0x20, 0x22, 0xa6, 0x98, 0x0d, 0x0, 0x0, 0x0, 0x10, 0x0 } + }, + {0x070, /* WST NTSC 6 */ + { 0xaa, 0xaa, 0xff, 0xff , 0x27, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x0, 0x0, 0x0, 0x10, 0x0 } + }, + {0x090, /* NABTS, NTSC 6 */ + { 0xaa, 0xaa, 0xff, 0xff , 0xe7, 0x2e, 0x20, 0x22, 0x69, 0x93, 0x0d, 0x0, 0x0, 0x0, 0x15, 0x0 } + }, + {0x0b0, /* NABTS, NTSC-J 6 */ + { 0xaa, 0xaa, 0xff, 0xff , 0xa7, 0x2e, 0x20, 0x23, 0x69, 0x93, 0x0d, 0x0, 0x0, 0x0, 0x10, 0x0 } + }, + {0x0d0, /* CC, PAL/SECAM 6 */ + { 0xaa, 0x2a, 0xff, 0x3f , 0x04, 0x51, 0x6e, 0x02, 0xa6, 0x7b, 0x09, 0x0, 0x0, 0x0, 0x27, 0x0 } + }, + {0x0f0, /* CC, NTSC 6 */ + { 0xaa, 0x2a, 0xff, 0x3f , 0x04, 0x51, 0x6e, 0x02, 0x69, 0x8c, 0x09, 0x0, 0x0, 0x0, 0x27, 0x0 } + }, + {0x110, /* WSS, PAL/SECAM 6 */ + { 0x5b, 0x55, 0xc5, 0xff , 0x0, 0x71, 0x6e, 0x42, 0xa6, 0xcd, 0x0f, 0x0, 0x0, 0x0, 0x3a, 0x0 } + }, + {0x130, /* WSS, NTSC C */ + { 0x38, 0x00, 0x3f, 0x00 , 0x0, 0x71, 0x6e, 0x43, 0x69, 0x7c, 0x08, 0x0, 0x0, 0x0, 0x39, 0x0 } + }, + {0x150, /* VITC, PAL/SECAM 6 */ + { 0x0, 0x0, 0x0, 0x0 , 0x0, 0x8f, 0x6d, 0x49, 0xa6, 0x85, 0x08, 0x0, 0x0, 0x0, 0x4c, 0x0 } + }, + {0x170, /* VITC, NTSC 6 */ + { 0x0, 0x0, 0x0, 0x0 , 0x0, 0x8f, 0x6d, 0x49, 0x69, 0x94, 0x08, 0x0, 0x0, 0x0, 0x4c, 0x0 } + }, + { (u16)-1 } +}; - tvp5150_write(c, TVP5150_CONF_SHARED_PIN, 2); +static int tvp5150_write_inittab(struct i2c_client *c, + const struct i2c_reg_value *regs) +{ + while (regs->reg != 0xff) { + tvp5150_write(c, regs->reg, regs->value); + regs++; + } + return 0; +} - /* Automatic offset and AGC enabled */ - tvp5150_write(c, TVP5150_ANAL_CHL_CTL, 0x15); +static int tvp5150_vdp_init(struct i2c_client *c, + const struct i2c_vbi_ram_value *regs) +{ + unsigned int i; - /* Normal Operation */ -// tvp5150_write(c, TVP5150_OP_MODE_CTL, 0x00); + /* Disable Full Field */ + tvp5150_write(c, TVP5150_FULL_FIELD_ENA_1, 0); - /* Activate YCrCb output 0x9 or 0xd ? */ - tvp5150_write(c, TVP5150_MISC_CTL, 0x6f); + /* Before programming, Line mode should be at 0xff */ + for (i=TVP5150_FULL_FIELD_ENA_2; i<=TVP5150_LINE_MODE_REG_44; i++) + tvp5150_write(c, i, 0xff); - /* Activates video std autodetection for all standards */ - tvp5150_write(c, TVP5150_AUTOSW_MSK, 0x0); + /* Load Ram Table */ + while (regs->reg != (u16)-1 ) { + tvp5150_write(c, TVP5150_CONF_RAM_ADDR_HIGH,regs->reg>>8); + tvp5150_write(c, TVP5150_CONF_RAM_ADDR_LOW,regs->reg); - /* Default format: 0x47, 4:2:2: 0x40 */ - tvp5150_write(c, TVP5150_DATA_RATE_SEL, 0x47); + for (i=0;i<16;i++) + tvp5150_write(c, TVP5150_VDP_CONF_RAM_DATA,regs->values[i]); - tvp5150_selmux(c, decoder->input); + regs++; + } + return 0; +} + +static int tvp5150_set_std(struct i2c_client *c, v4l2_std_id std) +{ + struct tvp5150 *decoder = i2c_get_clientdata(c); + int fmt=0; + + decoder->norm=std; + + /* First tests should be against specific std */ + + if (std == V4L2_STD_ALL) { + fmt=0; /* Autodetect mode */ + } else if (std & V4L2_STD_NTSC_443) { + fmt=0xa; + } else if (std & V4L2_STD_PAL_M) { + fmt=0x6; + } else if (std & (V4L2_STD_PAL_N| V4L2_STD_PAL_Nc)) { + fmt=0x8; + } else { + /* Then, test against generic ones */ + if (std & V4L2_STD_NTSC) { + fmt=0x2; + } else if (std & V4L2_STD_PAL) { + fmt=0x4; + } else if (std & V4L2_STD_SECAM) { + fmt=0xc; + } + } + + tvp5150_dbg(1,"Set video std register to %d.\n",fmt); + tvp5150_write(c, TVP5150_VIDEO_STD, fmt); + + return 0; +} + +static inline void tvp5150_reset(struct i2c_client *c) +{ + u8 type, ver_656, msb_id, lsb_id, msb_rom, lsb_rom; + struct tvp5150 *decoder = i2c_get_clientdata(c); + + type=tvp5150_read(c,TVP5150_AUTOSW_MSK); + msb_id=tvp5150_read(c,TVP5150_MSB_DEV_ID); + lsb_id=tvp5150_read(c,TVP5150_LSB_DEV_ID); + msb_rom=tvp5150_read(c,TVP5150_ROM_MAJOR_VER); + lsb_rom=tvp5150_read(c,TVP5150_ROM_MINOR_VER); + + if (type==0xdc) { + ver_656=tvp5150_read(c,TVP5150_REV_SELECT); + tvp5150_info("tvp%02x%02xam1 detected 656 version is %d.\n",msb_id, lsb_id,ver_656); + } else if (type==0xfc) { + tvp5150_info("tvp%02x%02xa detected.\n",msb_id, lsb_id); + } else { + tvp5150_info("unknown tvp%02x%02x chip detected(%d).\n",msb_id,lsb_id,type); + } + tvp5150_info("Rom ver is %d.%d\n",msb_rom,lsb_rom); - tvp5150_write(c, TVP5150_CHROMA_PROC_CTL_1, 0x0c); - tvp5150_write(c, TVP5150_CHROMA_PROC_CTL_2, 0x54); + /* Initializes TVP5150 to its default values */ + tvp5150_write_inittab(c, tvp5150_init_default); - tvp5150_write(c, 0x27, 0x20); /* ?????????? */ + /* Initializes VDP registers */ + tvp5150_vdp_init(c, vbi_ram_default); + + /* Selects decoder input */ + tvp5150_selmux(c, decoder->input); - tvp5150_write(c, TVP5150_VIDEO_STD, 0x0); /* Auto switch */ + /* Initializes TVP5150 to stream enabled values */ + tvp5150_write_inittab(c, tvp5150_init_enable); + /* Initialize image preferences */ tvp5150_write(c, TVP5150_BRIGHT_CTL, decoder->bright >> 8); tvp5150_write(c, TVP5150_CONTRAST_CTL, decoder->contrast >> 8); tvp5150_write(c, TVP5150_SATURATION_CTL, decoder->contrast >> 8); tvp5150_write(c, TVP5150_HUE_CTL, (decoder->hue - 32768) >> 8); + + tvp5150_set_std(c, decoder->norm); }; static int tvp5150_get_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) @@ -498,9 +803,8 @@ static int tvp5150_get_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) case V4L2_CID_HUE: ctrl->value = tvp5150_read(c, TVP5150_HUE_CTL); return 0; - default: - return -EINVAL; } + return -EINVAL; } static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) @@ -520,28 +824,59 @@ static int tvp5150_set_ctrl(struct i2c_client *c, struct v4l2_control *ctrl) case V4L2_CID_HUE: tvp5150_write(c, TVP5150_HUE_CTL, ctrl->value); return 0; - default: - return -EINVAL; } + return -EINVAL; } /**************************************************************************** I2C Command ****************************************************************************/ -static int tvp5150_command(struct i2c_client *client, +static int tvp5150_command(struct i2c_client *c, unsigned int cmd, void *arg) { - struct tvp5150 *decoder = i2c_get_clientdata(client); + struct tvp5150 *decoder = i2c_get_clientdata(c); switch (cmd) { case 0: + case VIDIOC_INT_RESET: case DECODER_INIT: - tvp5150_reset(client); + tvp5150_reset(c); + break; + case VIDIOC_S_STD: + if (decoder->norm == *(v4l2_std_id *)arg) + break; + return tvp5150_set_std(c, *(v4l2_std_id *)arg); + case VIDIOC_G_STD: + *(v4l2_std_id *)arg = decoder->norm; + break; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_TVP5150) + return -EINVAL; + reg->val = tvp5150_read(c, reg->reg & 0xff); break; + } + + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_TVP5150) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + tvp5150_write(c, reg->reg & 0xff, reg->val & 0xff); + break; + } +#endif case DECODER_DUMP: - dump_reg(client); + dump_reg(c); break; case DECODER_GET_CAPABILITIES: @@ -600,7 +935,7 @@ static int tvp5150_command(struct i2c_client *client, } decoder->input = *iarg; - tvp5150_selmux(client, decoder->input); + tvp5150_selmux(c, decoder->input); break; } @@ -620,19 +955,18 @@ static int tvp5150_command(struct i2c_client *client, decoder->enable = (*iarg != 0); - tvp5150_selmux(client, decoder->input); + tvp5150_selmux(c, decoder->input); break; } case VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *qc = arg; - u8 i, n; + int i; - dprintk(1, KERN_DEBUG "VIDIOC_QUERYCTRL"); + tvp5150_dbg(1, "VIDIOC_QUERYCTRL called\n"); - n = sizeof(tvp5150_qctrl) / sizeof(tvp5150_qctrl[0]); - for (i = 0; i < n; i++) + for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++) if (qc->id && qc->id == tvp5150_qctrl[i].id) { memcpy(qc, &(tvp5150_qctrl[i]), sizeof(*qc)); @@ -644,16 +978,14 @@ static int tvp5150_command(struct i2c_client *client, case VIDIOC_G_CTRL: { struct v4l2_control *ctrl = arg; - dprintk(1, KERN_DEBUG "VIDIOC_G_CTRL"); + tvp5150_dbg(1, "VIDIOC_G_CTRL called\n"); - return tvp5150_get_ctrl(client, ctrl); + return tvp5150_get_ctrl(c, ctrl); } - case VIDIOC_S_CTRL_OLD: /* ??? */ case VIDIOC_S_CTRL: { struct v4l2_control *ctrl = arg; u8 i, n; - dprintk(1, KERN_DEBUG "VIDIOC_S_CTRL"); n = sizeof(tvp5150_qctrl) / sizeof(tvp5150_qctrl[0]); for (i = 0; i < n; i++) if (ctrl->id == tvp5150_qctrl[i].id) { @@ -662,11 +994,10 @@ static int tvp5150_command(struct i2c_client *client, || ctrl->value > tvp5150_qctrl[i].maximum) return -ERANGE; - dprintk(1, - KERN_DEBUG - "VIDIOC_S_CTRL: id=%d, value=%d", + tvp5150_dbg(1, + "VIDIOC_S_CTRL: id=%d, value=%d\n", ctrl->id, ctrl->value); - return tvp5150_set_ctrl(client, ctrl); + return tvp5150_set_ctrl(c, ctrl); } return -EINVAL; } @@ -677,25 +1008,25 @@ static int tvp5150_command(struct i2c_client *client, if (decoder->bright != pic->brightness) { /* We want 0 to 255 we get 0-65535 */ decoder->bright = pic->brightness; - tvp5150_write(client, TVP5150_BRIGHT_CTL, + tvp5150_write(c, TVP5150_BRIGHT_CTL, decoder->bright >> 8); } if (decoder->contrast != pic->contrast) { /* We want 0 to 255 we get 0-65535 */ decoder->contrast = pic->contrast; - tvp5150_write(client, TVP5150_CONTRAST_CTL, + tvp5150_write(c, TVP5150_CONTRAST_CTL, decoder->contrast >> 8); } if (decoder->sat != pic->colour) { /* We want 0 to 255 we get 0-65535 */ decoder->sat = pic->colour; - tvp5150_write(client, TVP5150_SATURATION_CTL, + tvp5150_write(c, TVP5150_SATURATION_CTL, decoder->contrast >> 8); } if (decoder->hue != pic->hue) { /* We want -128 to 127 we get 0-65535 */ decoder->hue = pic->hue; - tvp5150_write(client, TVP5150_HUE_CTL, + tvp5150_write(c, TVP5150_HUE_CTL, (decoder->hue - 32768) >> 8); } break; @@ -714,19 +1045,18 @@ static struct i2c_driver driver; static struct i2c_client client_template = { .name = "(unset)", - .flags = I2C_CLIENT_ALLOW_USE, .driver = &driver, }; static int tvp5150_detect_client(struct i2c_adapter *adapter, int address, int kind) { - struct i2c_client *client; + struct i2c_client *c; struct tvp5150 *core; int rv; - dprintk(1, - KERN_INFO + if (debug) + printk( KERN_INFO "tvp5150.c: detecting tvp5150 client on address 0x%x\n", address << 1); @@ -739,22 +1069,21 @@ static int tvp5150_detect_client(struct i2c_adapter *adapter, I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) + c = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (c == 0) return -ENOMEM; - memcpy(client, &client_template, sizeof(struct i2c_client)); + memcpy(c, &client_template, sizeof(struct i2c_client)); - core = kmalloc(sizeof(struct tvp5150), GFP_KERNEL); + core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL); if (core == 0) { - kfree(client); + kfree(c); return -ENOMEM; } - memset(core, 0, sizeof(struct tvp5150)); - i2c_set_clientdata(client, core); + i2c_set_clientdata(c, core); - rv = i2c_attach_client(client); + rv = i2c_attach_client(c); - core->norm = VIDEO_MODE_AUTO; + core->norm = V4L2_STD_ALL; core->input = 2; core->enable = 1; core->bright = 32768; @@ -763,38 +1092,41 @@ static int tvp5150_detect_client(struct i2c_adapter *adapter, core->sat = 32768; if (rv) { - kfree(client); + kfree(c); kfree(core); return rv; } if (debug > 1) - dump_reg(client); - + dump_reg(c); return 0; } static int tvp5150_attach_adapter(struct i2c_adapter *adapter) { - dprintk(1, - KERN_INFO + if (debug) + printk( KERN_INFO "tvp5150.c: starting probe for adapter %s (0x%x)\n", adapter->name, adapter->id); return i2c_probe(adapter, &addr_data, &tvp5150_detect_client); } -static int tvp5150_detach_client(struct i2c_client *client) +static int tvp5150_detach_client(struct i2c_client *c) { - struct tvp5150 *decoder = i2c_get_clientdata(client); + struct tvp5150 *decoder = i2c_get_clientdata(c); int err; - err = i2c_detach_client(client); + tvp5150_dbg(1, + "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", + c->addr << 1); + + err = i2c_detach_client(c); if (err) { return err; } kfree(decoder); - kfree(client); + kfree(c); return 0; } @@ -802,12 +1134,10 @@ static int tvp5150_detach_client(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_driver driver = { - .owner = THIS_MODULE, - .name = "tvp5150", - - /* FIXME */ - .id = I2C_DRIVERID_SAA7110, - .flags = I2C_DF_NOTIFY, + .driver = { + .name = "tvp5150", + }, + .id = I2C_DRIVERID_TVP5150, .attach_adapter = tvp5150_attach_adapter, .detach_client = tvp5150_detach_client, diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index 4134549d11a..474a29bc176 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -305,9 +305,8 @@ v4l_compat_translate_ioctl(struct inode *inode, { struct video_capability *cap = arg; - cap2 = kmalloc(sizeof(*cap2),GFP_KERNEL); + cap2 = kzalloc(sizeof(*cap2),GFP_KERNEL); memset(cap, 0, sizeof(*cap)); - memset(cap2, 0, sizeof(*cap2)); memset(&fbuf2, 0, sizeof(fbuf2)); err = drv(inode, file, VIDIOC_QUERYCAP, cap2); @@ -422,9 +421,8 @@ v4l_compat_translate_ioctl(struct inode *inode, { struct video_window *win = arg; - fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); + fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); memset(win,0,sizeof(*win)); - memset(fmt2,0,sizeof(*fmt2)); fmt2->type = V4L2_BUF_TYPE_VIDEO_OVERLAY; err = drv(inode, file, VIDIOC_G_FMT, fmt2); @@ -461,8 +459,7 @@ v4l_compat_translate_ioctl(struct inode *inode, struct video_window *win = arg; int err1,err2; - fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); - memset(fmt2,0,sizeof(*fmt2)); + fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; drv(inode, file, VIDIOC_STREAMOFF, &fmt2->type); err1 = drv(inode, file, VIDIOC_G_FMT, fmt2); @@ -595,8 +592,7 @@ v4l_compat_translate_ioctl(struct inode *inode, pict->whiteness = get_v4l_control(inode, file, V4L2_CID_WHITENESS, drv); - fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); - memset(fmt2,0,sizeof(*fmt2)); + fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; err = drv(inode, file, VIDIOC_G_FMT, fmt2); if (err < 0) { @@ -622,8 +618,7 @@ v4l_compat_translate_ioctl(struct inode *inode, set_v4l_control(inode, file, V4L2_CID_WHITENESS, pict->whiteness, drv); - fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); - memset(fmt2,0,sizeof(*fmt2)); + fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; err = drv(inode, file, VIDIOC_G_FMT, fmt2); if (err < 0) @@ -846,9 +841,8 @@ v4l_compat_translate_ioctl(struct inode *inode, { struct video_mmap *mm = arg; - fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); + fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); memset(&buf2,0,sizeof(buf2)); - memset(fmt2,0,sizeof(*fmt2)); fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; err = drv(inode, file, VIDIOC_G_FMT, fmt2); @@ -942,8 +936,7 @@ v4l_compat_translate_ioctl(struct inode *inode, { struct vbi_format *fmt = arg; - fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); - memset(fmt2, 0, sizeof(*fmt2)); + fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE; err = drv(inode, file, VIDIOC_G_FMT, fmt2); @@ -951,6 +944,10 @@ v4l_compat_translate_ioctl(struct inode *inode, dprintk("VIDIOCGVBIFMT / VIDIOC_G_FMT: %d\n", err); break; } + if (fmt2->fmt.vbi.sample_format != V4L2_PIX_FMT_GREY) { + err = -EINVAL; + break; + } memset(fmt, 0, sizeof(*fmt)); fmt->samples_per_line = fmt2->fmt.vbi.samples_per_line; fmt->sampling_rate = fmt2->fmt.vbi.sampling_rate; @@ -966,8 +963,12 @@ v4l_compat_translate_ioctl(struct inode *inode, { struct vbi_format *fmt = arg; - fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL); - memset(fmt2, 0, sizeof(*fmt2)); + if (VIDEO_PALETTE_RAW != fmt->sample_format) { + err = -EINVAL; + break; + } + + fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE; fmt2->fmt.vbi.samples_per_line = fmt->samples_per_line; @@ -986,7 +987,7 @@ v4l_compat_translate_ioctl(struct inode *inode, if (fmt2->fmt.vbi.samples_per_line != fmt->samples_per_line || fmt2->fmt.vbi.sampling_rate != fmt->sampling_rate || - VIDEO_PALETTE_RAW != fmt->sample_format || + fmt2->fmt.vbi.sample_format != V4L2_PIX_FMT_GREY || fmt2->fmt.vbi.start[0] != fmt->start[0] || fmt2->fmt.vbi.count[0] != fmt->count[0] || fmt2->fmt.vbi.start[1] != fmt->start[1] || diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 597b8db35a1..cd2c4475525 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -58,6 +58,8 @@ #include <asm/pgtable.h> #include <asm/io.h> #include <asm/div64.h> +#include <linux/video_decoder.h> +#include <media/v4l2-common.h> #ifdef CONFIG_KMOD #include <linux/kmod.h> @@ -190,57 +192,175 @@ char *v4l2_type_names[] = { [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", }; -char *v4l2_ioctl_names[256] = { -#if __GNUC__ >= 3 - [0 ... 255] = "UNKNOWN", +/* ------------------------------------------------------------------ */ +/* debug help functions */ + +#ifdef HAVE_V4L1 +static const char *v4l1_ioctls[] = { + [_IOC_NR(VIDIOCGCAP)] = "VIDIOCGCAP", + [_IOC_NR(VIDIOCGCHAN)] = "VIDIOCGCHAN", + [_IOC_NR(VIDIOCSCHAN)] = "VIDIOCSCHAN", + [_IOC_NR(VIDIOCGTUNER)] = "VIDIOCGTUNER", + [_IOC_NR(VIDIOCSTUNER)] = "VIDIOCSTUNER", + [_IOC_NR(VIDIOCGPICT)] = "VIDIOCGPICT", + [_IOC_NR(VIDIOCSPICT)] = "VIDIOCSPICT", + [_IOC_NR(VIDIOCCAPTURE)] = "VIDIOCCAPTURE", + [_IOC_NR(VIDIOCGWIN)] = "VIDIOCGWIN", + [_IOC_NR(VIDIOCSWIN)] = "VIDIOCSWIN", + [_IOC_NR(VIDIOCGFBUF)] = "VIDIOCGFBUF", + [_IOC_NR(VIDIOCSFBUF)] = "VIDIOCSFBUF", + [_IOC_NR(VIDIOCKEY)] = "VIDIOCKEY", + [_IOC_NR(VIDIOCGFREQ)] = "VIDIOCGFREQ", + [_IOC_NR(VIDIOCSFREQ)] = "VIDIOCSFREQ", + [_IOC_NR(VIDIOCGAUDIO)] = "VIDIOCGAUDIO", + [_IOC_NR(VIDIOCSAUDIO)] = "VIDIOCSAUDIO", + [_IOC_NR(VIDIOCSYNC)] = "VIDIOCSYNC", + [_IOC_NR(VIDIOCMCAPTURE)] = "VIDIOCMCAPTURE", + [_IOC_NR(VIDIOCGMBUF)] = "VIDIOCGMBUF", + [_IOC_NR(VIDIOCGUNIT)] = "VIDIOCGUNIT", + [_IOC_NR(VIDIOCGCAPTURE)] = "VIDIOCGCAPTURE", + [_IOC_NR(VIDIOCSCAPTURE)] = "VIDIOCSCAPTURE", + [_IOC_NR(VIDIOCSPLAYMODE)] = "VIDIOCSPLAYMODE", + [_IOC_NR(VIDIOCSWRITEMODE)] = "VIDIOCSWRITEMODE", + [_IOC_NR(VIDIOCGPLAYINFO)] = "VIDIOCGPLAYINFO", + [_IOC_NR(VIDIOCSMICROCODE)] = "VIDIOCSMICROCODE", + [_IOC_NR(VIDIOCGVBIFMT)] = "VIDIOCGVBIFMT", + [_IOC_NR(VIDIOCSVBIFMT)] = "VIDIOCSVBIFMT" +}; +#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) +#endif + +static const char *v4l2_ioctls[] = { + [_IOC_NR(VIDIOC_QUERYCAP)] = "VIDIOC_QUERYCAP", + [_IOC_NR(VIDIOC_RESERVED)] = "VIDIOC_RESERVED", + [_IOC_NR(VIDIOC_ENUM_FMT)] = "VIDIOC_ENUM_FMT", + [_IOC_NR(VIDIOC_G_FMT)] = "VIDIOC_G_FMT", + [_IOC_NR(VIDIOC_S_FMT)] = "VIDIOC_S_FMT", + [_IOC_NR(VIDIOC_G_MPEGCOMP)] = "VIDIOC_G_MPEGCOMP", + [_IOC_NR(VIDIOC_S_MPEGCOMP)] = "VIDIOC_S_MPEGCOMP", + [_IOC_NR(VIDIOC_REQBUFS)] = "VIDIOC_REQBUFS", + [_IOC_NR(VIDIOC_QUERYBUF)] = "VIDIOC_QUERYBUF", + [_IOC_NR(VIDIOC_G_FBUF)] = "VIDIOC_G_FBUF", + [_IOC_NR(VIDIOC_S_FBUF)] = "VIDIOC_S_FBUF", + [_IOC_NR(VIDIOC_OVERLAY)] = "VIDIOC_OVERLAY", + [_IOC_NR(VIDIOC_QBUF)] = "VIDIOC_QBUF", + [_IOC_NR(VIDIOC_DQBUF)] = "VIDIOC_DQBUF", + [_IOC_NR(VIDIOC_STREAMON)] = "VIDIOC_STREAMON", + [_IOC_NR(VIDIOC_STREAMOFF)] = "VIDIOC_STREAMOFF", + [_IOC_NR(VIDIOC_G_PARM)] = "VIDIOC_G_PARM", + [_IOC_NR(VIDIOC_S_PARM)] = "VIDIOC_S_PARM", + [_IOC_NR(VIDIOC_G_STD)] = "VIDIOC_G_STD", + [_IOC_NR(VIDIOC_S_STD)] = "VIDIOC_S_STD", + [_IOC_NR(VIDIOC_ENUMSTD)] = "VIDIOC_ENUMSTD", + [_IOC_NR(VIDIOC_ENUMINPUT)] = "VIDIOC_ENUMINPUT", + [_IOC_NR(VIDIOC_G_CTRL)] = "VIDIOC_G_CTRL", + [_IOC_NR(VIDIOC_S_CTRL)] = "VIDIOC_S_CTRL", + [_IOC_NR(VIDIOC_G_TUNER)] = "VIDIOC_G_TUNER", + [_IOC_NR(VIDIOC_S_TUNER)] = "VIDIOC_S_TUNER", + [_IOC_NR(VIDIOC_G_AUDIO)] = "VIDIOC_G_AUDIO", + [_IOC_NR(VIDIOC_S_AUDIO)] = "VIDIOC_S_AUDIO", + [_IOC_NR(VIDIOC_QUERYCTRL)] = "VIDIOC_QUERYCTRL", + [_IOC_NR(VIDIOC_QUERYMENU)] = "VIDIOC_QUERYMENU", + [_IOC_NR(VIDIOC_G_INPUT)] = "VIDIOC_G_INPUT", + [_IOC_NR(VIDIOC_S_INPUT)] = "VIDIOC_S_INPUT", + [_IOC_NR(VIDIOC_G_OUTPUT)] = "VIDIOC_G_OUTPUT", + [_IOC_NR(VIDIOC_S_OUTPUT)] = "VIDIOC_S_OUTPUT", + [_IOC_NR(VIDIOC_ENUMOUTPUT)] = "VIDIOC_ENUMOUTPUT", + [_IOC_NR(VIDIOC_G_AUDOUT)] = "VIDIOC_G_AUDOUT", + [_IOC_NR(VIDIOC_S_AUDOUT)] = "VIDIOC_S_AUDOUT", + [_IOC_NR(VIDIOC_G_MODULATOR)] = "VIDIOC_G_MODULATOR", + [_IOC_NR(VIDIOC_S_MODULATOR)] = "VIDIOC_S_MODULATOR", + [_IOC_NR(VIDIOC_G_FREQUENCY)] = "VIDIOC_G_FREQUENCY", + [_IOC_NR(VIDIOC_S_FREQUENCY)] = "VIDIOC_S_FREQUENCY", + [_IOC_NR(VIDIOC_CROPCAP)] = "VIDIOC_CROPCAP", + [_IOC_NR(VIDIOC_G_CROP)] = "VIDIOC_G_CROP", + [_IOC_NR(VIDIOC_S_CROP)] = "VIDIOC_S_CROP", + [_IOC_NR(VIDIOC_G_JPEGCOMP)] = "VIDIOC_G_JPEGCOMP", + [_IOC_NR(VIDIOC_S_JPEGCOMP)] = "VIDIOC_S_JPEGCOMP", + [_IOC_NR(VIDIOC_QUERYSTD)] = "VIDIOC_QUERYSTD", + [_IOC_NR(VIDIOC_TRY_FMT)] = "VIDIOC_TRY_FMT", + [_IOC_NR(VIDIOC_ENUMAUDIO)] = "VIDIOC_ENUMAUDIO", + [_IOC_NR(VIDIOC_ENUMAUDOUT)] = "VIDIOC_ENUMAUDOUT", + [_IOC_NR(VIDIOC_G_PRIORITY)] = "VIDIOC_G_PRIORITY", + [_IOC_NR(VIDIOC_S_PRIORITY)] = "VIDIOC_S_PRIORITY", +#if 1 + [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP", #endif - [_IOC_NR(VIDIOC_QUERYCAP)] = "VIDIOC_QUERYCAP", - [_IOC_NR(VIDIOC_RESERVED)] = "VIDIOC_RESERVED", - [_IOC_NR(VIDIOC_ENUM_FMT)] = "VIDIOC_ENUM_FMT", - [_IOC_NR(VIDIOC_G_FMT)] = "VIDIOC_G_FMT", - [_IOC_NR(VIDIOC_S_FMT)] = "VIDIOC_S_FMT", - [_IOC_NR(VIDIOC_REQBUFS)] = "VIDIOC_REQBUFS", - [_IOC_NR(VIDIOC_QUERYBUF)] = "VIDIOC_QUERYBUF", - [_IOC_NR(VIDIOC_G_FBUF)] = "VIDIOC_G_FBUF", - [_IOC_NR(VIDIOC_S_FBUF)] = "VIDIOC_S_FBUF", - [_IOC_NR(VIDIOC_OVERLAY)] = "VIDIOC_OVERLAY", - [_IOC_NR(VIDIOC_QBUF)] = "VIDIOC_QBUF", - [_IOC_NR(VIDIOC_DQBUF)] = "VIDIOC_DQBUF", - [_IOC_NR(VIDIOC_STREAMON)] = "VIDIOC_STREAMON", - [_IOC_NR(VIDIOC_STREAMOFF)] = "VIDIOC_STREAMOFF", - [_IOC_NR(VIDIOC_G_PARM)] = "VIDIOC_G_PARM", - [_IOC_NR(VIDIOC_S_PARM)] = "VIDIOC_S_PARM", - [_IOC_NR(VIDIOC_G_STD)] = "VIDIOC_G_STD", - [_IOC_NR(VIDIOC_S_STD)] = "VIDIOC_S_STD", - [_IOC_NR(VIDIOC_ENUMSTD)] = "VIDIOC_ENUMSTD", - [_IOC_NR(VIDIOC_ENUMINPUT)] = "VIDIOC_ENUMINPUT", - [_IOC_NR(VIDIOC_G_CTRL)] = "VIDIOC_G_CTRL", - [_IOC_NR(VIDIOC_S_CTRL)] = "VIDIOC_S_CTRL", - [_IOC_NR(VIDIOC_G_TUNER)] = "VIDIOC_G_TUNER", - [_IOC_NR(VIDIOC_S_TUNER)] = "VIDIOC_S_TUNER", - [_IOC_NR(VIDIOC_G_AUDIO)] = "VIDIOC_G_AUDIO", - [_IOC_NR(VIDIOC_S_AUDIO)] = "VIDIOC_S_AUDIO", - [_IOC_NR(VIDIOC_QUERYCTRL)] = "VIDIOC_QUERYCTRL", - [_IOC_NR(VIDIOC_QUERYMENU)] = "VIDIOC_QUERYMENU", - [_IOC_NR(VIDIOC_G_INPUT)] = "VIDIOC_G_INPUT", - [_IOC_NR(VIDIOC_S_INPUT)] = "VIDIOC_S_INPUT", - [_IOC_NR(VIDIOC_G_OUTPUT)] = "VIDIOC_G_OUTPUT", - [_IOC_NR(VIDIOC_S_OUTPUT)] = "VIDIOC_S_OUTPUT", - [_IOC_NR(VIDIOC_ENUMOUTPUT)] = "VIDIOC_ENUMOUTPUT", - [_IOC_NR(VIDIOC_G_AUDOUT)] = "VIDIOC_G_AUDOUT", - [_IOC_NR(VIDIOC_S_AUDOUT)] = "VIDIOC_S_AUDOUT", - [_IOC_NR(VIDIOC_G_MODULATOR)] = "VIDIOC_G_MODULATOR", - [_IOC_NR(VIDIOC_S_MODULATOR)] = "VIDIOC_S_MODULATOR", - [_IOC_NR(VIDIOC_G_FREQUENCY)] = "VIDIOC_G_FREQUENCY", - [_IOC_NR(VIDIOC_S_FREQUENCY)] = "VIDIOC_S_FREQUENCY", - [_IOC_NR(VIDIOC_CROPCAP)] = "VIDIOC_CROPCAP", - [_IOC_NR(VIDIOC_G_CROP)] = "VIDIOC_G_CROP", - [_IOC_NR(VIDIOC_S_CROP)] = "VIDIOC_S_CROP", - [_IOC_NR(VIDIOC_G_JPEGCOMP)] = "VIDIOC_G_JPEGCOMP", - [_IOC_NR(VIDIOC_S_JPEGCOMP)] = "VIDIOC_S_JPEGCOMP", - [_IOC_NR(VIDIOC_QUERYSTD)] = "VIDIOC_QUERYSTD", - [_IOC_NR(VIDIOC_TRY_FMT)] = "VIDIOC_TRY_FMT", + [_IOC_NR(VIDIOC_LOG_STATUS)] = "VIDIOC_LOG_STATUS" }; +#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) + +static const char *v4l2_int_ioctls[] = { +#ifdef HAVE_VIDEO_DECODER + [_IOC_NR(DECODER_GET_CAPABILITIES)] = "DECODER_GET_CAPABILITIES", + [_IOC_NR(DECODER_GET_STATUS)] = "DECODER_GET_STATUS", + [_IOC_NR(DECODER_SET_NORM)] = "DECODER_SET_NORM", + [_IOC_NR(DECODER_SET_INPUT)] = "DECODER_SET_INPUT", + [_IOC_NR(DECODER_SET_OUTPUT)] = "DECODER_SET_OUTPUT", + [_IOC_NR(DECODER_ENABLE_OUTPUT)] = "DECODER_ENABLE_OUTPUT", + [_IOC_NR(DECODER_SET_PICTURE)] = "DECODER_SET_PICTURE", + [_IOC_NR(DECODER_SET_GPIO)] = "DECODER_SET_GPIO", + [_IOC_NR(DECODER_INIT)] = "DECODER_INIT", + [_IOC_NR(DECODER_SET_VBI_BYPASS)] = "DECODER_SET_VBI_BYPASS", + [_IOC_NR(DECODER_DUMP)] = "DECODER_DUMP", +#endif + [_IOC_NR(AUDC_SET_RADIO)] = "AUDC_SET_RADIO", + [_IOC_NR(AUDC_SET_INPUT)] = "AUDC_SET_INPUT", + [_IOC_NR(MSP_SET_MATRIX)] = "MSP_SET_MATRIX", + + [_IOC_NR(TUNER_SET_TYPE_ADDR)] = "TUNER_SET_TYPE_ADDR", + [_IOC_NR(TUNER_SET_STANDBY)] = "TUNER_SET_STANDBY", + [_IOC_NR(TDA9887_SET_CONFIG)] = "TDA9887_SET_CONFIG", + + [_IOC_NR(VIDIOC_INT_S_REGISTER)] = "VIDIOC_INT_S_REGISTER", + [_IOC_NR(VIDIOC_INT_G_REGISTER)] = "VIDIOC_INT_G_REGISTER", + [_IOC_NR(VIDIOC_INT_RESET)] = "VIDIOC_INT_RESET", + [_IOC_NR(VIDIOC_INT_AUDIO_CLOCK_FREQ)] = "VIDIOC_INT_AUDIO_CLOCK_FREQ", + [_IOC_NR(VIDIOC_INT_DECODE_VBI_LINE)] = "VIDIOC_INT_DECODE_VBI_LINE", + [_IOC_NR(VIDIOC_INT_S_VBI_DATA)] = "VIDIOC_INT_S_VBI_DATA", + [_IOC_NR(VIDIOC_INT_G_VBI_DATA)] = "VIDIOC_INT_G_VBI_DATA", + [_IOC_NR(VIDIOC_INT_G_CHIP_IDENT)] = "VIDIOC_INT_G_CHIP_IDENT", + [_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)] = "VIDIOC_INT_I2S_CLOCK_FREQ" +}; +#define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls) + +/* Common ioctl debug function. This function can be used by + external ioctl messages as well as internal V4L ioctl */ +void v4l_printk_ioctl(unsigned int cmd) +{ + char *dir; + + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: dir = "--"; break; + case _IOC_READ: dir = "r-"; break; + case _IOC_WRITE: dir = "-w"; break; + case _IOC_READ | _IOC_WRITE: dir = "rw"; break; + default: dir = "*ERR*"; break; + } + switch (_IOC_TYPE(cmd)) { + case 'd': + printk("v4l2_int ioctl %s, dir=%s (0x%08x)\n", + (_IOC_NR(cmd) < V4L2_INT_IOCTLS) ? + v4l2_int_ioctls[_IOC_NR(cmd)] : "UNKNOWN", dir, cmd); + break; +#ifdef HAVE_V4L1 + case 'v': + printk("v4l1 ioctl %s, dir=%s (0x%08x)\n", + (_IOC_NR(cmd) < V4L1_IOCTLS) ? + v4l1_ioctls[_IOC_NR(cmd)] : "UNKNOWN", dir, cmd); + break; +#endif + case 'V': + printk("v4l2 ioctl %s, dir=%s (0x%08x)\n", + (_IOC_NR(cmd) < V4L2_IOCTLS) ? + v4l2_ioctls[_IOC_NR(cmd)] : "UNKNOWN", dir, cmd); + break; + + default: + printk("unknown ioctl '%c', dir=%s, #%d (0x%08x)\n", + _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd); + } +} /* ----------------------------------------------------------------- */ @@ -255,7 +375,7 @@ EXPORT_SYMBOL(v4l2_prio_check); EXPORT_SYMBOL(v4l2_field_names); EXPORT_SYMBOL(v4l2_type_names); -EXPORT_SYMBOL(v4l2_ioctl_names); +EXPORT_SYMBOL(v4l_printk_ioctl); /* * Local variables: diff --git a/drivers/media/video/video-buf-dvb.c b/drivers/media/video/video-buf-dvb.c index 55f129e964e..0a4004a4393 100644 --- a/drivers/media/video/video-buf-dvb.c +++ b/drivers/media/video/video-buf-dvb.c @@ -13,6 +13,7 @@ * (at your option) any later version. */ + #include <linux/module.h> #include <linux/init.h> #include <linux/device.h> @@ -247,3 +248,4 @@ EXPORT_SYMBOL(videobuf_dvb_unregister); * compile-command: "make DVB=1" * End: */ + diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c index acfd3a103f3..9ef477523d2 100644 --- a/drivers/media/video/video-buf.c +++ b/drivers/media/video/video-buf.c @@ -52,10 +52,9 @@ videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) struct page *pg; int i; - sglist = kmalloc(sizeof(struct scatterlist)*nr_pages, GFP_KERNEL); + sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL); if (NULL == sglist) return NULL; - memset(sglist,0,sizeof(struct scatterlist)*nr_pages); for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { pg = vmalloc_to_page(virt); if (NULL == pg) @@ -80,10 +79,9 @@ videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) if (NULL == pages[0]) return NULL; - sglist = kmalloc(sizeof(*sglist) * nr_pages, GFP_KERNEL); + sglist = kcalloc(nr_pages, sizeof(*sglist), GFP_KERNEL); if (NULL == sglist) return NULL; - memset(sglist, 0, sizeof(*sglist) * nr_pages); if (NULL == pages[0]) goto nopage; @@ -284,9 +282,8 @@ void* videobuf_alloc(unsigned int size) { struct videobuf_buffer *vb; - vb = kmalloc(size,GFP_KERNEL); + vb = kzalloc(size,GFP_KERNEL); if (NULL != vb) { - memset(vb,0,size); videobuf_dma_init(&vb->dma); init_waitqueue_head(&vb->done); vb->magic = MAGIC_BUFFER; @@ -753,10 +750,9 @@ videobuf_read_zerocopy(struct videobuf_queue *q, char __user *data, int retval; /* setup stuff */ - retval = -ENOMEM; q->read_buf = videobuf_alloc(q->msize); if (NULL == q->read_buf) - goto done; + return -ENOMEM; q->read_buf->memory = V4L2_MEMORY_USERPTR; q->read_buf->baddr = (unsigned long)data; @@ -817,10 +813,14 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, if (NULL == q->read_buf) goto done; q->read_buf->memory = V4L2_MEMORY_USERPTR; + q->read_buf->bsize = count; /* preferred size */ field = videobuf_next_field(q); retval = q->ops->buf_prepare(q,q->read_buf,field); - if (0 != retval) + if (0 != retval) { + kfree (q->read_buf); + q->read_buf = NULL; goto done; + } spin_lock_irqsave(q->irqlock,flags); q->ops->buf_queue(q,q->read_buf); spin_unlock_irqrestore(q->irqlock,flags); diff --git a/drivers/media/video/videocodec.c b/drivers/media/video/videocodec.c index 839db622040..8f271de57fd 100644 --- a/drivers/media/video/videocodec.c +++ b/drivers/media/video/videocodec.c @@ -124,17 +124,13 @@ videocodec_attach (struct videocodec_master *master) if (res == 0) { dprintk(3, "videocodec_attach '%s'\n", codec->name); - ptr = (struct attached_list *) - kmalloc(sizeof(struct attached_list), - GFP_KERNEL); + ptr = kzalloc(sizeof(struct attached_list), GFP_KERNEL); if (!ptr) { dprintk(1, KERN_ERR "videocodec_attach: no memory\n"); goto out_kfree; } - memset(ptr, 0, - sizeof(struct attached_list)); ptr->codec = codec; a = h->list; @@ -249,14 +245,11 @@ videocodec_register (const struct videocodec *codec) "videocodec: register '%s', type: %x, flags %lx, magic %lx\n", codec->name, codec->type, codec->flags, codec->magic); - ptr = - (struct codec_list *) kmalloc(sizeof(struct codec_list), - GFP_KERNEL); + ptr = kzalloc(sizeof(struct codec_list), GFP_KERNEL); if (!ptr) { dprintk(1, KERN_ERR "videocodec_register: no memory\n"); return -ENOMEM; } - memset(ptr, 0, sizeof(struct codec_list)); ptr->codec = codec; if (!h) { diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 83c49f9610d..078880e4c8c 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -29,7 +29,6 @@ #include <linux/devfs_fs_kernel.h> #include <asm/uaccess.h> #include <asm/system.h> -#include <asm/semaphore.h> #include <linux/videodev.h> @@ -52,10 +51,7 @@ struct video_device *video_device_alloc(void) { struct video_device *vfd; - vfd = kmalloc(sizeof(*vfd),GFP_KERNEL); - if (NULL == vfd) - return NULL; - memset(vfd,0,sizeof(*vfd)); + vfd = kzalloc(sizeof(*vfd),GFP_KERNEL); return vfd; } @@ -68,7 +64,8 @@ static void video_release(struct class_device *cd) { struct video_device *vfd = container_of(cd, struct video_device, class_dev); -#if 1 /* needed until all drivers are fixed */ +#if 1 + /* needed until all drivers are fixed */ if (!vfd->release) return; #endif @@ -76,16 +73,16 @@ static void video_release(struct class_device *cd) } static struct class video_class = { - .name = VIDEO_NAME, + .name = VIDEO_NAME, .release = video_release, }; /* - * Active devices + * Active devices */ - + static struct video_device *video_device[VIDEO_NUM_DEVICES]; -static DECLARE_MUTEX(videodev_lock); +static DEFINE_MUTEX(videodev_lock); struct video_device* video_devdata(struct file *file) { @@ -101,18 +98,18 @@ static int video_open(struct inode *inode, struct file *file) int err = 0; struct video_device *vfl; struct file_operations *old_fops; - + if(minor>=VIDEO_NUM_DEVICES) return -ENODEV; - down(&videodev_lock); + mutex_lock(&videodev_lock); vfl=video_device[minor]; if(vfl==NULL) { - up(&videodev_lock); + mutex_unlock(&videodev_lock); request_module("char-major-%d-%d", VIDEO_MAJOR, minor); - down(&videodev_lock); + mutex_lock(&videodev_lock); vfl=video_device[minor]; if (vfl==NULL) { - up(&videodev_lock); + mutex_unlock(&videodev_lock); return -ENODEV; } } @@ -125,7 +122,7 @@ static int video_open(struct inode *inode, struct file *file) file->f_op = fops_get(old_fops); } fops_put(old_fops); - up(&videodev_lock); + mutex_unlock(&videodev_lock); return err; } @@ -189,7 +186,7 @@ video_usercopy(struct inode *inode, struct file *file, return -ENOMEM; parg = mbuf; } - + err = -EFAULT; if (_IOC_DIR(cmd) & _IOC_WRITE) if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) @@ -240,7 +237,7 @@ int video_exclusive_open(struct inode *inode, struct file *file) int video_exclusive_release(struct inode *inode, struct file *file) { struct video_device *vfl = video_devdata(file); - + vfl->users--; return 0; } @@ -253,7 +250,7 @@ static struct file_operations video_fops; * @type: type of device to register * @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ... * -1 == first free) - * + * * The registration code assigns minor numbers based on the type * requested. -ENFILE is returned in all the device slots for this * category are full. If not then the minor field is set and the @@ -269,7 +266,7 @@ static struct file_operations video_fops; * * %VFL_TYPE_VBI - Vertical blank data (undecoded) * - * %VFL_TYPE_RADIO - A radio card + * %VFL_TYPE_RADIO - A radio card */ int video_register_device(struct video_device *vfd, int type, int nr) @@ -278,7 +275,7 @@ int video_register_device(struct video_device *vfd, int type, int nr) int base; int end; char *name_base; - + switch(type) { case VFL_TYPE_GRABBER: @@ -293,7 +290,7 @@ int video_register_device(struct video_device *vfd, int type, int nr) break; case VFL_TYPE_VBI: base=224; - end=240; + end=256; name_base = "vbi"; break; case VFL_TYPE_RADIO: @@ -306,12 +303,12 @@ int video_register_device(struct video_device *vfd, int type, int nr) } /* pick a minor number */ - down(&videodev_lock); + mutex_lock(&videodev_lock); if (nr >= 0 && nr < end-base) { /* use the one the driver asked for */ i = base+nr; if (NULL != video_device[i]) { - up(&videodev_lock); + mutex_unlock(&videodev_lock); return -ENFILE; } } else { @@ -320,13 +317,13 @@ int video_register_device(struct video_device *vfd, int type, int nr) if (NULL == video_device[i]) break; if (i == end) { - up(&videodev_lock); + mutex_unlock(&videodev_lock); return -ENFILE; } } video_device[i]=vfd; vfd->minor=i; - up(&videodev_lock); + mutex_unlock(&videodev_lock); sprintf(vfd->devfs_name, "v4l/%s%d", name_base, i - base); devfs_mk_cdev(MKDEV(VIDEO_MAJOR, vfd->minor), @@ -334,17 +331,18 @@ int video_register_device(struct video_device *vfd, int type, int nr) init_MUTEX(&vfd->lock); /* sysfs class */ - memset(&vfd->class_dev, 0x00, sizeof(vfd->class_dev)); + memset(&vfd->class_dev, 0x00, sizeof(vfd->class_dev)); if (vfd->dev) vfd->class_dev.dev = vfd->dev; vfd->class_dev.class = &video_class; - vfd->class_dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); + vfd->class_dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); strlcpy(vfd->class_dev.class_id, vfd->devfs_name + 4, BUS_ID_SIZE); class_device_register(&vfd->class_dev); class_device_create_file(&vfd->class_dev, - &class_device_attr_name); + &class_device_attr_name); -#if 1 /* needed until all drivers are fixed */ +#if 1 + /* needed until all drivers are fixed */ if (!vfd->release) printk(KERN_WARNING "videodev: \"%s\" has no release callback. " "Please fix your driver for proper sysfs support, see " @@ -360,17 +358,17 @@ int video_register_device(struct video_device *vfd, int type, int nr) * This unregisters the passed device and deassigns the minor * number. Future open calls will be met with errors. */ - + void video_unregister_device(struct video_device *vfd) { - down(&videodev_lock); + mutex_lock(&videodev_lock); if(video_device[vfd->minor]!=vfd) panic("videodev: bad unregister"); devfs_remove(vfd->devfs_name); video_device[vfd->minor]=NULL; class_device_unregister(&vfd->class_dev); - up(&videodev_lock); + mutex_unlock(&videodev_lock); } @@ -384,7 +382,7 @@ static struct file_operations video_fops= /* * Initialise video for linux */ - + static int __init videodev_init(void) { int ret; diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index 71b28e9e085..c8fd8238904 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -4499,13 +4499,11 @@ static int vino_init(void) dma_addr_t dma_dummy_address; int i; - vino_drvdata = (struct vino_settings *) - kmalloc(sizeof(struct vino_settings), GFP_KERNEL); + vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL); if (!vino_drvdata) { vino_module_cleanup(vino_init_stage); return -ENOMEM; } - memset(vino_drvdata, 0, sizeof(struct vino_settings)); vino_init_stage++; /* create a dummy dma descriptor */ diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c index 137b58f2c66..d0a1e72ea8c 100644 --- a/drivers/media/video/vpx3220.c +++ b/drivers/media/video/vpx3220.c @@ -621,17 +621,14 @@ vpx3220_detect_client (struct i2c_adapter *adapter, (adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == NULL) { return -ENOMEM; } - memset(client, 0, sizeof(struct i2c_client)); - client->addr = address; client->adapter = adapter; client->driver = &vpx3220_i2c_driver; - client->flags = I2C_CLIENT_ALLOW_USE; /* Check for manufacture ID and part number */ if (kind < 0) { @@ -676,12 +673,11 @@ vpx3220_detect_client (struct i2c_adapter *adapter, sizeof(I2C_NAME(client))); } - decoder = kmalloc(sizeof(struct vpx3220), GFP_KERNEL); + decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL); if (decoder == NULL) { kfree(client); return -ENOMEM; } - memset(decoder, 0, sizeof(struct vpx3220)); decoder->norm = VIDEO_MODE_PAL; decoder->input = 0; decoder->enable = 1; @@ -722,11 +718,11 @@ vpx3220_attach_adapter (struct i2c_adapter *adapter) */ static struct i2c_driver vpx3220_i2c_driver = { - .owner = THIS_MODULE, - .name = "vpx3220", + .driver = { + .name = "vpx3220", + }, .id = I2C_DRIVERID_VPX3220, - .flags = I2C_DF_NOTIFY, .attach_adapter = vpx3220_attach_adapter, .detach_client = vpx3220_detach_client, diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index c318ba32fba..b7b0afffd21 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -187,6 +187,7 @@ static struct file_operations w9966_fops = { .open = video_exclusive_open, .release = video_exclusive_release, .ioctl = w9966_v4l_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .read = w9966_v4l_read, .llseek = no_llseek, }; diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index 22f28622200..8cb64f8a8a9 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -5,6 +5,11 @@ * * Based on saa7115 driver * + * Copyright (C) 2005 Hans Verkuil <hverkuil@xs4all.nl> + * - Cleanup + * - V4L2 API update + * - sound fixes + * * 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 @@ -20,7 +25,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - #include <linux/module.h> #include <linux/types.h> #include <linux/ioctl.h> @@ -28,20 +32,12 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev.h> -#include <media/audiochip.h> +#include <media/v4l2-common.h> MODULE_DESCRIPTION("wm8775 driver"); -MODULE_AUTHOR("Ulf Eklund"); +MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); MODULE_LICENSE("GPL"); -#define wm8775_err(fmt, arg...) do { \ - printk(KERN_ERR "%s %d-%04x: " fmt, client->driver->name, \ - i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0) -#define wm8775_info(fmt, arg...) do { \ - printk(KERN_INFO "%s %d-%04x: " fmt, client->driver->name, \ - i2c_adapter_id(client->adapter), client->addr , ## arg); } while (0) - - static unsigned short normal_i2c[] = { 0x36 >> 1, I2C_CLIENT_END }; @@ -65,7 +61,7 @@ static int wm8775_write(struct i2c_client *client, int reg, u16 val) int i; if (reg < 0 || reg >= TOT_REGS) { - wm8775_err("Invalid register R%d\n", reg); + v4l_err(client, "Invalid register R%d\n", reg); return -1; } @@ -75,7 +71,7 @@ static int wm8775_write(struct i2c_client *client, int reg, u16 val) return 0; } } - wm8775_err("I2C: cannot write %03x to register R%d\n", val, reg); + v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); return -1; } @@ -83,38 +79,53 @@ static int wm8775_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct wm8775_state *state = i2c_get_clientdata(client); - int *input = arg; + struct v4l2_audio *input = arg; + struct v4l2_control *ctrl = arg; switch (cmd) { - case AUDC_SET_INPUT: + case VIDIOC_S_AUDIO: + /* There are 4 inputs and one output. Zero or more inputs + are multiplexed together to the output. Hence there are + 16 combinations. + If only one input is active (the normal case) then the + input values 1, 2, 4 or 8 should be used. */ + if (input->index > 15) { + v4l_err(client, "Invalid input %d.\n", input->index); + return -EINVAL; + } + state->input = input->index; + if (state->muted) + break; wm8775_write(client, R21, 0x0c0); wm8775_write(client, R14, 0x1d4); wm8775_write(client, R15, 0x1d4); + wm8775_write(client, R21, 0x100 + state->input); + break; - if (*input == AUDIO_RADIO) { - wm8775_write(client, R21, 0x108); - state->input = 8; - state->muted = 0; - break; - } - if (*input == AUDIO_MUTE) { - state->muted = 1; - break; - } - if (*input == AUDIO_UNMUTE) { + case VIDIOC_G_AUDIO: + memset(input, 0, sizeof(*input)); + input->index = state->input; + break; + + case VIDIOC_G_CTRL: + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + ctrl->value = state->muted; + break; + + case VIDIOC_S_CTRL: + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + state->muted = ctrl->value; + wm8775_write(client, R21, 0x0c0); + wm8775_write(client, R14, 0x1d4); + wm8775_write(client, R15, 0x1d4); + if (!state->muted) wm8775_write(client, R21, 0x100 + state->input); - state->muted = 0; - break; - } - /* All other inputs... */ - wm8775_write(client, R21, 0x102); - state->input = 2; - state->muted = 0; break; case VIDIOC_LOG_STATUS: - wm8775_info("Input: %s%s\n", - state->input == 8 ? "radio" : "default", + v4l_info(client, "Input: %d%s\n", state->input, state->muted ? " (muted)" : ""); break; @@ -155,18 +166,16 @@ static int wm8775_attach(struct i2c_adapter *adapter, int address, int kind) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; - memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver; - client->flags = I2C_CLIENT_ALLOW_USE; snprintf(client->name, sizeof(client->name) - 1, "wm8775"); - wm8775_info("chip found @ 0x%x (%s)\n", address << 1, adapter->name); + v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name); state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL); if (state == NULL) { @@ -202,11 +211,7 @@ static int wm8775_attach(struct i2c_adapter *adapter, int address, int kind) static int wm8775_probe(struct i2c_adapter *adapter) { -#ifdef I2C_CLASS_TV_ANALOG if (adapter->class & I2C_CLASS_TV_ANALOG) -#else - if (adapter->id == I2C_HW_B_BT848) -#endif return i2c_probe(adapter, &addr_data, wm8775_attach); return 0; } @@ -228,15 +233,13 @@ static int wm8775_detach(struct i2c_client *client) /* i2c implementation */ static struct i2c_driver i2c_driver = { - .name = "wm8775", - - .id = I2C_DRIVERID_WM8775, - .flags = I2C_DF_NOTIFY, - + .driver = { + .name = "wm8775", + }, + .id = I2C_DRIVERID_WM8775, .attach_adapter = wm8775_probe, - .detach_client = wm8775_detach, - .command = wm8775_command, - .owner = THIS_MODULE, + .detach_client = wm8775_detach, + .command = wm8775_command, }; diff --git a/drivers/media/video/zoran_card.c b/drivers/media/video/zoran_card.c index 39a0d238900..246e67cd8b5 100644 --- a/drivers/media/video/zoran_card.c +++ b/drivers/media/video/zoran_card.c @@ -995,7 +995,7 @@ test_interrupts (struct zoran *zr) static int __devinit zr36057_init (struct zoran *zr) { - unsigned long mem; + u32 *mem; void *vdev; unsigned mem_needed; int j; @@ -1050,7 +1050,7 @@ zr36057_init (struct zoran *zr) /* allocate memory *before* doing anything to the hardware * in case allocation fails */ mem_needed = BUZ_NUM_STAT_COM * 4; - mem = (unsigned long) kmalloc(mem_needed, GFP_KERNEL); + mem = kzalloc(mem_needed, GFP_KERNEL); vdev = (void *) kmalloc(sizeof(struct video_device), GFP_KERNEL); if (!mem || !vdev) { dprintk(1, @@ -1058,11 +1058,10 @@ zr36057_init (struct zoran *zr) "%s: zr36057_init() - kmalloc (STAT_COM) failed\n", ZR_DEVNAME(zr)); kfree(vdev); - kfree((void *)mem); + kfree(mem); return -ENOMEM; } - memset((void *) mem, 0, mem_needed); - zr->stat_com = (u32 *) mem; + zr->stat_com = mem; for (j = 0; j < BUZ_NUM_STAT_COM; j++) { zr->stat_com[j] = 1; /* mark as unavailable to zr36057 */ } diff --git a/drivers/media/video/zoran_driver.c b/drivers/media/video/zoran_driver.c index 07bde9acd67..485553be190 100644 --- a/drivers/media/video/zoran_driver.c +++ b/drivers/media/video/zoran_driver.c @@ -1311,7 +1311,7 @@ zoran_open (struct inode *inode, res = -ENODEV; goto open_unlock_and_return; } - if (!try_module_get(zr->decoder->driver->owner)) { + if (!try_module_get(zr->decoder->driver->driver.owner)) { dprintk(1, KERN_ERR "%s: failed to grab ownership of i2c decoder\n", @@ -1321,13 +1321,13 @@ zoran_open (struct inode *inode, goto open_unlock_and_return; } if (zr->encoder && - !try_module_get(zr->encoder->driver->owner)) { + !try_module_get(zr->encoder->driver->driver.owner)) { dprintk(1, KERN_ERR "%s: failed to grab ownership of i2c encoder\n", ZR_DEVNAME(zr)); res = -EIO; - module_put(zr->decoder->driver->owner); + module_put(zr->decoder->driver->driver.owner); module_put(THIS_MODULE); goto open_unlock_and_return; } @@ -1345,7 +1345,7 @@ zoran_open (struct inode *inode, ZR_DEVNAME(zr), current->comm, current->pid, zr->user); /* now, create the open()-specific file_ops struct */ - fh = kmalloc(sizeof(struct zoran_fh), GFP_KERNEL); + fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1, KERN_ERR @@ -1354,7 +1354,6 @@ zoran_open (struct inode *inode, res = -ENOMEM; goto open_unlock_and_return; } - memset(fh, 0, sizeof(struct zoran_fh)); /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows * on norm-change! */ fh->overlay_mask = @@ -1393,9 +1392,9 @@ zoran_open (struct inode *inode, open_unlock_and_return: /* if we grabbed locks, release them accordingly */ if (have_module_locks) { - module_put(zr->decoder->driver->owner); + module_put(zr->decoder->driver->driver.owner); if (zr->encoder) { - module_put(zr->encoder->driver->owner); + module_put(zr->encoder->driver->driver.owner); } module_put(THIS_MODULE); } @@ -1461,9 +1460,9 @@ zoran_close (struct inode *inode, kfree(fh); /* release locks on the i2c modules */ - module_put(zr->decoder->driver->owner); + module_put(zr->decoder->driver->driver.owner); if (zr->encoder) { - module_put(zr->encoder->driver->owner); + module_put(zr->encoder->driver->driver.owner); } module_put(THIS_MODULE); @@ -4678,6 +4677,7 @@ static struct file_operations zoran_fops = { .open = zoran_open, .release = zoran_close, .ioctl = zoran_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, .read = zoran_read, .write = zoran_write, diff --git a/drivers/media/video/zr36016.c b/drivers/media/video/zr36016.c index 4ed898585c7..10130ef67ea 100644 --- a/drivers/media/video/zr36016.c +++ b/drivers/media/video/zr36016.c @@ -451,12 +451,11 @@ zr36016_setup (struct videocodec *codec) return -ENOSPC; } //mem structure init - codec->data = ptr = kmalloc(sizeof(struct zr36016), GFP_KERNEL); + codec->data = ptr = kzalloc(sizeof(struct zr36016), GFP_KERNEL); if (NULL == ptr) { dprintk(1, KERN_ERR "zr36016: Can't get enough memory!\n"); return -ENOMEM; } - memset(ptr, 0, sizeof(struct zr36016)); snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]", zr36016_codecs); diff --git a/drivers/media/video/zr36050.c b/drivers/media/video/zr36050.c index 0144576a612..bd0cd28543c 100644 --- a/drivers/media/video/zr36050.c +++ b/drivers/media/video/zr36050.c @@ -813,12 +813,11 @@ zr36050_setup (struct videocodec *codec) return -ENOSPC; } //mem structure init - codec->data = ptr = kmalloc(sizeof(struct zr36050), GFP_KERNEL); + codec->data = ptr = kzalloc(sizeof(struct zr36050), GFP_KERNEL); if (NULL == ptr) { dprintk(1, KERN_ERR "zr36050: Can't get enough memory!\n"); return -ENOMEM; } - memset(ptr, 0, sizeof(struct zr36050)); snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]", zr36050_codecs); diff --git a/drivers/media/video/zr36060.c b/drivers/media/video/zr36060.c index 129744a07ab..28fa31a5f15 100644 --- a/drivers/media/video/zr36060.c +++ b/drivers/media/video/zr36060.c @@ -919,12 +919,11 @@ zr36060_setup (struct videocodec *codec) return -ENOSPC; } //mem structure init - codec->data = ptr = kmalloc(sizeof(struct zr36060), GFP_KERNEL); + codec->data = ptr = kzalloc(sizeof(struct zr36060), GFP_KERNEL); if (NULL == ptr) { dprintk(1, KERN_ERR "zr36060: Can't get enough memory!\n"); return -ENOMEM; } - memset(ptr, 0, sizeof(struct zr36060)); snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]", zr36060_codecs); diff --git a/drivers/media/video/zr36120.c b/drivers/media/video/zr36120.c index 07286816d7d..d4c633b8a7f 100644 --- a/drivers/media/video/zr36120.c +++ b/drivers/media/video/zr36120.c @@ -1490,6 +1490,7 @@ static struct video_device zr36120_template= .write = zoran_write, .poll = zoran_poll, .ioctl = zoran_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .mmap = zoran_mmap, .minor = -1, }; |