diff options
Diffstat (limited to 'linux/drivers/media/video')
194 files changed, 8396 insertions, 6211 deletions
diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index d6b1a106a..e28e292fe 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -34,6 +34,7 @@ config VIDEOBUF_DVB select VIDEOBUF_GEN config VIDEO_BTCX + depends on PCI tristate config VIDEO_IR @@ -578,13 +579,6 @@ config VIDEO_SAA5249 To compile this driver as a module, choose M here: the module will be called saa5249. -config TUNER_3036 - tristate "SAB3036 tuner" - depends on I2C && VIDEO_V4L1 - help - Say Y here to include support for Philips SAB3036 compatible tuners. - If in doubt, say N. - config VIDEO_VINO tristate "SGI Vino Video For Linux (EXPERIMENTAL)" depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2 @@ -697,7 +691,7 @@ config VIDEO_MXB depends on PCI && VIDEO_V4L1 && I2C select VIDEO_SAA7146_VV select VIDEO_TUNER - select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA7115 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TEA6420 if VIDEO_HELPER_CHIPS_AUTO @@ -708,21 +702,6 @@ config VIDEO_MXB To compile this driver as a module, choose M here: the module will be called mxb. -config VIDEO_DPC - tristate "Philips-Semiconductors 'dpc7146 demonstration board'" - depends on PCI && VIDEO_V4L1 && I2C - select VIDEO_SAA7146_VV - select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO - ---help--- - This is a video4linux driver for the 'dpc7146 demonstration - board' by Philips-Semiconductors. It's the reference design - 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. - config VIDEO_HEXIUM_ORION tristate "Hexium HV-PCI6 and Orion frame grabber" depends on PCI && VIDEO_V4L2 && I2C @@ -808,6 +787,12 @@ config MT9M001_PCA9536_SWITCH Select this if your MT9M001 camera uses a PCA9536 I2C GPIO extender to switch between 8 and 10 bit datawidth modes +config SOC_CAMERA_MT9M111 + tristate "mt9m111 support" + depends on SOC_CAMERA && I2C + help + This driver supports MT9M111 cameras from Micron + config SOC_CAMERA_MT9V022 tristate "mt9v022 support" depends on SOC_CAMERA && I2C @@ -837,7 +822,7 @@ config VIDEO_PXA27x config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" - depends on VIDEO_DEV && SOC_CAMERA + depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA select VIDEOBUF_DMA_CONTIG ---help--- This is a v4l2 driver for the SuperH Mobile CEU Interface diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index bf429b023..bcd23e3cb 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -20,6 +20,8 @@ ifeq ($(CONFIG_VIDEO_V4L1_COMPAT),y) obj-$(CONFIG_VIDEO_DEV) += v4l1-compat.o endif +obj-$(CONFIG_VIDEO_TUNER) += tuner.o + obj-$(CONFIG_VIDEO_BT848) += bt8xx/ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o @@ -83,10 +85,6 @@ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o -obj-$(CONFIG_VIDEO_DPC) += dpc7146.o -obj-$(CONFIG_TUNER_3036) += tuner-3036.o - -obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o @@ -138,6 +136,7 @@ obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o +obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o diff --git a/linux/drivers/media/video/arv.c b/linux/drivers/media/video/arv.c index e100d4cfb..a6c2ff1b4 100644 --- a/linux/drivers/media/video/arv.c +++ b/linux/drivers/media/video/arv.c @@ -39,7 +39,7 @@ #include <asm/dma.h> #include <asm/byteorder.h> -#if 0 /* keep */; +#if 0 /* keep */ #define DEBUG(n, args...) printk(args) #define CHECK_LOST 1 #else @@ -117,6 +117,7 @@ struct ar_device { int width, height; int frame_bytes, line_bytes; wait_queue_head_t wait; + unsigned long in_use; struct mutex lock; }; @@ -270,7 +271,7 @@ static inline void wait_for_vertical_sync(int exp_line) static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct video_device *v = video_devdata(file); - struct ar_device *ar = v->priv; + struct ar_device *ar = video_get_drvdata(v); long ret = ar->frame_bytes; /* return read bytes */ unsigned long arvcr1 = 0; unsigned long flags; @@ -400,7 +401,7 @@ static int ar_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { struct video_device *dev = video_devdata(file); - struct ar_device *ar = dev->priv; + struct ar_device *ar = video_get_drvdata(dev); DEBUG(1, "ar_ioctl()\n"); switch(cmd) { @@ -579,11 +580,11 @@ static void ar_interrupt(int irq, void *dev) * we have to start capture. */ disable_dma(); -#if 0 /* keep */; +#if 0 /* keep */ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* needless? */ #endif memcpy(ar->frame[0], ar->line_buff, ar->line_bytes); -#if 0 /* keep */; +#if 0 /* keep */ ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); #endif enable_dma(); @@ -609,7 +610,7 @@ static void ar_interrupt(int irq, void *dev) ar_outl(arvcr1, ARVCR1); /* disable */ wake_up_interruptible(&ar->wait); } else { -#if 0 /* keep */; +#if 0 /* keep */ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); #endif @@ -630,7 +631,7 @@ static void ar_interrupt(int irq, void *dev) */ static int ar_initialize(struct video_device *dev) { - struct ar_device *ar = dev->priv; + struct ar_device *ar = video_get_drvdata(dev); unsigned long cr = 0; int i,found=0; @@ -693,7 +694,7 @@ static int ar_initialize(struct video_device *dev) printk("."); iic(2,0x78,0x8e,0x0c,0x00); iic(2,0x78,0x8f,0x00,0x00); -#if 0 /* keep */; +#if 0 /* keep */ iic(2,0x78,0x90,0x00,0x00); /* AWB on=1 off=0 */ #endif iic(2,0x78,0x93,0x01,0x00); @@ -712,7 +713,7 @@ static int ar_initialize(struct video_device *dev) iic(2,0x78,0x9e,0x2e,0x00); iic(2,0x78,0xb8,0x78,0x00); iic(2,0x78,0xba,0x05,0x00); -#if 0 /* keep */; +#if 0 /* keep */ iic(2,0x78,0x83,0x8c,0x00); /* brightness */ #endif printk("."); @@ -737,7 +738,7 @@ static int ar_initialize(struct video_device *dev) void ar_release(struct video_device *vfd) { - struct ar_device *ar = vfd->priv; + struct ar_device *ar = video_get_drvdata(vfd); mutex_lock(&ar->lock); video_device_release(vfd); } @@ -747,10 +748,23 @@ void ar_release(struct video_device *vfd) * Video4Linux Module functions * ****************************************************************************/ +static struct ar_device ardev; + +static int ar_exclusive_open(struct inode *inode, struct file *file) +{ + return test_and_set_bit(0, &ardev.in_use) ? -EBUSY : 0; +} + +static int ar_exclusive_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &ardev.in_use); + return 0; +} + static const struct file_operations ar_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = ar_exclusive_open, + .release = ar_exclusive_release, .read = ar_read, .ioctl = ar_ioctl, #ifdef CONFIG_COMPAT @@ -767,7 +781,6 @@ static struct video_device ar_template = { }; #define ALIGN4(x) ((((int)(x)) & 0x3) == 0) -static struct ar_device ardev; static int __init ar_init(void) { @@ -807,7 +820,7 @@ static int __init ar_init(void) return -ENOMEM; } memcpy(ar->vdev, &ar_template, sizeof(ar_template)); - ar->vdev->priv = ar; + video_set_drvdata(ar->vdev, ar); if (vga) { ar->width = AR_WIDTH_VGA; diff --git a/linux/drivers/media/video/au0828/au0828-cards.c b/linux/drivers/media/video/au0828/au0828-cards.c index 14b9fc9bb..ed48908a9 100644 --- a/linux/drivers/media/video/au0828/au0828-cards.c +++ b/linux/drivers/media/video/au0828/au0828-cards.c @@ -1,7 +1,7 @@ /* * Driver for the Auvitek USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.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 diff --git a/linux/drivers/media/video/au0828/au0828-cards.h b/linux/drivers/media/video/au0828/au0828-cards.h index 102ed03dc..48a1882c2 100644 --- a/linux/drivers/media/video/au0828/au0828-cards.h +++ b/linux/drivers/media/video/au0828/au0828-cards.h @@ -1,7 +1,7 @@ /* * Driver for the Auvitek USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.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 diff --git a/linux/drivers/media/video/au0828/au0828-core.c b/linux/drivers/media/video/au0828/au0828-core.c index 123422f82..59dcb3255 100644 --- a/linux/drivers/media/video/au0828/au0828-core.c +++ b/linux/drivers/media/video/au0828/au0828-core.c @@ -1,7 +1,7 @@ /* * Driver for the Auvitek USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.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 @@ -253,5 +253,5 @@ module_init(au0828_init); module_exit(au0828_exit); MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products"); -MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>"); +MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/au0828/au0828-dvb.c b/linux/drivers/media/video/au0828/au0828-dvb.c index 1fa77ba11..24160ca59 100644 --- a/linux/drivers/media/video/au0828/au0828-dvb.c +++ b/linux/drivers/media/video/au0828/au0828-dvb.c @@ -1,7 +1,7 @@ /* * Driver for the Auvitek USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.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 @@ -363,9 +363,8 @@ int au0828_dvb_register(struct au0828_dev *dev) &hauppauge_hvr950q_config, &dev->i2c_adap); if (dvb->frontend != NULL) - dvb_attach(xc5000_attach, dvb->frontend, - &dev->i2c_adap, - &hauppauge_hvr950q_tunerconfig, dev); + dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, + &hauppauge_hvr950q_tunerconfig); break; case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: dvb->frontend = dvb_attach(au8522_attach, diff --git a/linux/drivers/media/video/au0828/au0828-i2c.c b/linux/drivers/media/video/au0828/au0828-i2c.c index 32858dceb..03b797b9b 100644 --- a/linux/drivers/media/video/au0828/au0828-i2c.c +++ b/linux/drivers/media/video/au0828/au0828-i2c.c @@ -1,7 +1,7 @@ /* * Driver for the Auvitek AU0828 USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.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 diff --git a/linux/drivers/media/video/au0828/au0828-reg.h b/linux/drivers/media/video/au0828/au0828-reg.h index 398275508..1e87fa0c6 100644 --- a/linux/drivers/media/video/au0828/au0828-reg.h +++ b/linux/drivers/media/video/au0828/au0828-reg.h @@ -1,7 +1,7 @@ /* * Driver for the Auvitek USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.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 diff --git a/linux/drivers/media/video/au0828/au0828.h b/linux/drivers/media/video/au0828/au0828.h index 7beb57179..4f10ff300 100644 --- a/linux/drivers/media/video/au0828/au0828.h +++ b/linux/drivers/media/video/au0828/au0828.h @@ -1,7 +1,7 @@ /* * Driver for the Auvitek AU0828 USB bridge * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.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 diff --git a/linux/drivers/media/video/bt856.c b/linux/drivers/media/video/bt856.c index 50ca987ad..e31c9d4b9 100644 --- a/linux/drivers/media/video/bt856.c +++ b/linux/drivers/media/video/bt856.c @@ -69,8 +69,8 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); /* ----------------------------------------------------------------------- */ -#define REG_OFFSET 0xDA -#define BT856_NR_REG 6 +#define BT856_REG_OFFSET 0xDA +#define BT856_NR_REG 6 struct bt856 { unsigned char reg[BT856_NR_REG]; @@ -90,7 +90,7 @@ bt856_write (struct i2c_client *client, { struct bt856 *encoder = i2c_get_clientdata(client); - encoder->reg[reg - REG_OFFSET] = value; + encoder->reg[reg - BT856_REG_OFFSET] = value; return i2c_smbus_write_byte_data(client, reg, value); } @@ -104,7 +104,7 @@ bt856_setbit (struct i2c_client *client, return bt856_write(client, reg, (encoder-> - reg[reg - REG_OFFSET] & ~(1 << bit)) | + reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) | (value ? (1 << bit) : 0)); } diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index bf3339436..d67e127f1 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -3201,8 +3201,9 @@ static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256]) static void flyvideo_gpio(struct bttv *btv) { - int gpio,has_remote,has_radio,is_capture_only,is_lr90,has_tda9820_tda9821; - int tuner=UNSET,ttype; + int gpio, has_remote, has_radio, is_capture_only; + int is_lr90, has_tda9820_tda9821; + int tuner_type = UNSET, ttype; gpio_inout(0xffffff, 0); udelay(8); /* without this we would see the 0x1800 mask */ @@ -3220,20 +3221,26 @@ static void flyvideo_gpio(struct bttv *btv) * xxxF00(LR26/LR50), xxxFE0(LR90): Remote control chip (LVA001 or CF45) soldered * Note: Some bits are Audio_Mask ! */ - ttype=(gpio&0x0f0000)>>16; - switch(ttype) { - case 0x0: tuner=2; /* NTSC, e.g. TPI8NSR11P */ + ttype = (gpio & 0x0f0000) >> 16; + switch (ttype) { + case 0x0: + tuner_type = 2; /* NTSC, e.g. TPI8NSR11P */ break; - case 0x2: tuner=39;/* LG NTSC (newer TAPC series) TAPC-H701P */ + case 0x2: + tuner_type = 39; /* LG NTSC (newer TAPC series) TAPC-H701P */ break; - case 0x4: tuner=5; /* Philips PAL TPI8PSB02P, TPI8PSB12P, TPI8PSB12D or FI1216, FM1216 */ + case 0x4: + tuner_type = 5; /* Philips PAL TPI8PSB02P, TPI8PSB12P, TPI8PSB12D or FI1216, FM1216 */ break; - case 0x6: tuner=37;/* LG PAL (newer TAPC series) TAPC-G702P */ + case 0x6: + tuner_type = 37; /* LG PAL (newer TAPC series) TAPC-G702P */ break; - case 0xC: tuner=3; /* Philips SECAM(+PAL) FQ1216ME or FI1216MF */ + case 0xC: + tuner_type = 3; /* Philips SECAM(+PAL) FQ1216ME or FI1216MF */ break; default: printk(KERN_INFO "bttv%d: FlyVideo_gpio: unknown tuner type.\n", btv->c.nr); + break; } has_remote = gpio & 0x800000; @@ -3246,23 +3253,26 @@ static void flyvideo_gpio(struct bttv *btv) /* * gpio & 0x001000 output bit for audio routing */ - if(is_capture_only) - tuner = TUNER_ABSENT; /* No tuner present */ + if (is_capture_only) + tuner_type = TUNER_ABSENT; /* No tuner present */ printk(KERN_INFO "bttv%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n", - btv->c.nr, has_radio? "yes":"no ", has_remote? "yes":"no ", tuner, gpio); + btv->c.nr, has_radio ? "yes" : "no ", + has_remote ? "yes" : "no ", tuner_type, gpio); printk(KERN_INFO "bttv%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n", - btv->c.nr, is_lr90?"yes":"no ", has_tda9820_tda9821?"yes":"no ", - is_capture_only?"yes":"no "); + btv->c.nr, is_lr90 ? "yes" : "no ", + has_tda9820_tda9821 ? "yes" : "no ", + is_capture_only ? "yes" : "no "); - if (tuner != UNSET) /* only set if known tuner autodetected, else let insmod option through */ - btv->tuner_type = tuner; + if (tuner_type != UNSET) /* only set if known tuner autodetected, else let insmod option through */ + btv->tuner_type = tuner_type; btv->has_radio = has_radio; /* LR90 Audio Routing is done by 2 hef4052, so Audio_Mask has 4 bits: 0x001c80 * LR26/LR50 only has 1 hef4052, Audio_Mask 0x000c00 * Audio options: from tuner, from tda9821/tda9821(mono,stereo,sap), from tda9874, ext., mute */ - if(has_tda9820_tda9821) btv->audio_mode_gpio = lt9415_audio; + if (has_tda9820_tda9821) + btv->audio_mode_gpio = lt9415_audio; /* todo: if(has_tda9874) btv->audio_mode_gpio = fv2000s_audio; */ } @@ -4036,7 +4046,7 @@ static int tuner_1_table[] = { static void __devinit avermedia_eeprom(struct bttv *btv) { - int tuner_make,tuner_tv_fm,tuner_format,tuner=0; + int tuner_make, tuner_tv_fm, tuner_format, tuner_type = 0; tuner_make = (eeprom_data[0x41] & 0x7); tuner_tv_fm = (eeprom_data[0x41] & 0x18) >> 3; @@ -4044,24 +4054,24 @@ static void __devinit avermedia_eeprom(struct bttv *btv) btv->has_remote = (eeprom_data[0x42] & 0x01); if (tuner_make == 0 || tuner_make == 2) - if(tuner_format <=0x0a) - tuner = tuner_0_table[tuner_format]; + if (tuner_format <= 0x0a) + tuner_type = tuner_0_table[tuner_format]; if (tuner_make == 1) - if(tuner_format <=9) - tuner = tuner_1_table[tuner_format]; + if (tuner_format <= 9) + tuner_type = tuner_1_table[tuner_format]; if (tuner_make == 4) - if(tuner_format == 0x09) - tuner = TUNER_LG_NTSC_NEW_TAPC; /* TAPC-G702P */ + if (tuner_format == 0x09) + tuner_type = TUNER_LG_NTSC_NEW_TAPC; /* TAPC-G702P */ printk(KERN_INFO "bttv%d: Avermedia eeprom[0x%02x%02x]: tuner=", - btv->c.nr,eeprom_data[0x41],eeprom_data[0x42]); - if(tuner) { - btv->tuner_type=tuner; - printk("%d",tuner); + btv->c.nr, eeprom_data[0x41], eeprom_data[0x42]); + if (tuner_type) { + btv->tuner_type = tuner_type; + printk(KERN_CONT "%d", tuner_type); } else - printk("Unknown type"); - printk(" radio:%s remote control:%s\n", + printk(KERN_CONT "Unknown type"); + printk(KERN_CONT " radio:%s remote control:%s\n", tuner_tv_fm ? "yes" : "no", btv->has_remote ? "yes" : "no"); } @@ -4103,7 +4113,8 @@ static void __devinit boot_msp34xx(struct bttv *btv, int pin) gpio_inout(mask,mask); gpio_bits(mask,0); - udelay(2500); + mdelay(2); + udelay(500); gpio_bits(mask,mask); if (bttv_gpio) diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 012eeba5e..af7272665 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -76,9 +76,9 @@ static unsigned int gbuffers = 8; static unsigned int gbufsize = 0x208000; static unsigned int reset_crop = 1; -static int video_nr = -1; -static int radio_nr = -1; -static int vbi_nr = -1; +static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; +static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; +static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; static int debug_latency; static unsigned int fdsr; @@ -96,7 +96,6 @@ static unsigned int irq_iswitch; static unsigned int uv_ratio = 50; 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; @@ -109,9 +108,6 @@ module_param(irq_debug, int, 0644); module_param(debug_latency, int, 0644); module_param(fdsr, int, 0444); -module_param(video_nr, int, 0444); -module_param(radio_nr, int, 0444); -module_param(vbi_nr, int, 0444); module_param(gbuffers, int, 0444); module_param(gbufsize, int, 0444); module_param(reset_crop, int, 0444); @@ -131,7 +127,10 @@ module_param(uv_ratio, int, 0444); module_param(full_luma_range, int, 0444); module_param(coring, int, 0444); -module_param_array(radio, int, NULL, 0444); +module_param_array(radio, int, NULL, 0444); +module_param_array(video_nr, int, NULL, 0444); +module_param_array(radio_nr, int, NULL, 0444); +module_param_array(vbi_nr, int, NULL, 0444); MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)"); MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian"); @@ -153,6 +152,9 @@ MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler"); MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50"); MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)"); MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)"); +MODULE_PARM_DESC(video_nr, "video device numbers"); +MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); +MODULE_PARM_DESC(radio_nr, "radio device numbers"); MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards"); MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr"); @@ -1388,7 +1390,7 @@ static void init_irqreg(struct bttv *btv) (btv->gpioirq ? BT848_INT_GPINT : 0) | BT848_INT_SCERR | (fdsr ? BT848_INT_FDSR : 0) | - BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES| + BT848_INT_RISCI | BT848_INT_OCERR | BT848_INT_FMTCHG|BT848_INT_HLOCK| BT848_INT_I2CDONE, BT848_INT_MASK); @@ -3464,7 +3466,7 @@ static int radio_open(struct inode *inode, struct file *file) lock_kernel(); for (i = 0; i < bttv_num; i++) { - if (bttvs[i].radio_dev->minor == minor) { + if (bttvs[i].radio_dev && bttvs[i].radio_dev->minor == minor) { btv = &bttvs[i]; break; } @@ -4277,7 +4279,8 @@ static int __devinit bttv_register_video(struct bttv *btv) if (NULL == btv->video_dev) goto err; - if (video_register_device(btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0) + if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER, + video_nr[btv->c.nr]) < 0) goto err; printk(KERN_INFO "bttv%d: registered device video%d\n", btv->c.nr,btv->video_dev->minor & 0x1f); @@ -4293,7 +4296,8 @@ static int __devinit bttv_register_video(struct bttv *btv) if (NULL == btv->vbi_dev) goto err; - if (video_register_device(btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) + if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI, + vbi_nr[btv->c.nr]) < 0) goto err; printk(KERN_INFO "bttv%d: registered device vbi%d\n", btv->c.nr,btv->vbi_dev->minor & 0x1f); @@ -4304,7 +4308,8 @@ static int __devinit bttv_register_video(struct bttv *btv) btv->radio_dev = vdev_init(btv, &radio_template, "radio"); if (NULL == btv->radio_dev) goto err; - if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0) + if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO, + radio_nr[btv->c.nr]) < 0) goto err; printk(KERN_INFO "bttv%d: registered device radio%d\n", btv->c.nr,btv->radio_dev->minor & 0x1f); diff --git a/linux/drivers/media/video/bt8xx/bttv-risc.c b/linux/drivers/media/video/bt8xx/bttv-risc.c index 0cffd3a72..310924f77 100644 --- a/linux/drivers/media/video/bt8xx/bttv-risc.c +++ b/linux/drivers/media/video/bt8xx/bttv-risc.c @@ -244,7 +244,8 @@ bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, const struct bttv_format *fmt, struct bttv_overlay *ov, int skip_even, int skip_odd) { - int dwords,rc,line,maxy,start,end,skip,nskips; + int dwords, rc, line, maxy, start, end; + unsigned skip, nskips; struct btcx_skiplist *skips; __le32 *rp; u32 ri,ra; diff --git a/linux/drivers/media/video/bt8xx/bttvp.h b/linux/drivers/media/video/bt8xx/bttvp.h index 63d1d4451..403695f9a 100644 --- a/linux/drivers/media/video/bt8xx/bttvp.h +++ b/linux/drivers/media/video/bt8xx/bttvp.h @@ -269,6 +269,11 @@ int bttv_sub_add_device(struct bttv_core *core, char *name); int bttv_sub_del_devices(struct bttv_core *core); /* ---------------------------------------------------------- */ +/* bttv-cards.c */ + +extern int no_overlay; + +/* ---------------------------------------------------------- */ /* bttv-driver.c */ /* insmod options */ diff --git a/linux/drivers/media/video/btcx-risc.c b/linux/drivers/media/video/btcx-risc.c index e0e1158ce..1c2413571 100644 --- a/linux/drivers/media/video/btcx-risc.c +++ b/linux/drivers/media/video/btcx-risc.c @@ -65,7 +65,7 @@ int btcx_riscmem_alloc(struct pci_dev *pci, unsigned int size) { __le32 *cpu; - dma_addr_t dma; + dma_addr_t dma = 0; if (NULL != risc->cpu && risc->size < size) btcx_riscmem_free(pci,risc); @@ -185,12 +185,12 @@ btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) } void -btcx_calc_skips(int line, int width, unsigned int *maxy, +btcx_calc_skips(int line, int width, int *maxy, struct btcx_skiplist *skips, unsigned int *nskips, const struct v4l2_clip *clips, unsigned int nclips) { unsigned int clip,skip; - int end,maxline; + int end, maxline; skip=0; maxline = 9999; diff --git a/linux/drivers/media/video/btcx-risc.h b/linux/drivers/media/video/btcx-risc.h index 861bc8112..f8bc6e8e7 100644 --- a/linux/drivers/media/video/btcx-risc.h +++ b/linux/drivers/media/video/btcx-risc.h @@ -23,7 +23,7 @@ int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask); void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); -void btcx_calc_skips(int line, int width, unsigned int *maxy, +void btcx_calc_skips(int line, int width, int *maxy, struct btcx_skiplist *skips, unsigned int *nskips, const struct v4l2_clip *clips, unsigned int nclips); diff --git a/linux/drivers/media/video/bw-qcam.c b/linux/drivers/media/video/bw-qcam.c index df2b25b69..ca8fabdf3 100644 --- a/linux/drivers/media/video/bw-qcam.c +++ b/linux/drivers/media/video/bw-qcam.c @@ -496,7 +496,7 @@ static void qc_set(struct qcam_device *q) val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * q->transfer_scale; } - val = (val + val2 - 1) / val2; + val = DIV_ROUND_UP(val, val2); qc_command(q, 0x13); qc_command(q, val); @@ -652,7 +652,7 @@ static long qc_capture(struct qcam_device * q, char __user *buf, unsigned long l transperline = q->width * q->bpp; divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * q->transfer_scale; - transperline = (transperline + divisor - 1) / divisor; + transperline = DIV_ROUND_UP(transperline, divisor); for (i = 0, yield = yieldlines; i < linestotrans; i++) { @@ -895,10 +895,27 @@ static ssize_t qcam_read(struct file *file, char __user *buf, return len; } +static int qcam_exclusive_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct qcam_device *qcam = (struct qcam_device *)dev; + + return test_and_set_bit(0, &qcam->in_use) ? -EBUSY : 0; +} + +static int qcam_exclusive_release(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct qcam_device *qcam = (struct qcam_device *)dev; + + clear_bit(0, &qcam->in_use); + return 0; +} + static const struct file_operations qcam_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = qcam_exclusive_open, + .release = qcam_exclusive_release, .ioctl = qcam_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -910,6 +927,7 @@ static struct video_device qcam_template= { .name = "Connectix Quickcam", .fops = &qcam_fops, + .release = video_device_release_empty, }; #define MAX_CAMS 4 @@ -947,8 +965,7 @@ static int init_bwqcam(struct parport *port) printk(KERN_INFO "Connectix Quickcam on %s\n", qcam->pport->name); - if(video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr)==-1) - { + if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { parport_unregister_device(qcam->pdev); kfree(qcam); return -ENODEV; diff --git a/linux/drivers/media/video/bw-qcam.h b/linux/drivers/media/video/bw-qcam.h index 6701dafbc..8a60c5de0 100644 --- a/linux/drivers/media/video/bw-qcam.h +++ b/linux/drivers/media/video/bw-qcam.h @@ -65,4 +65,5 @@ struct qcam_device { int top, left; int status; unsigned int saved_bits; + unsigned long in_use; }; diff --git a/linux/drivers/media/video/c-qcam.c b/linux/drivers/media/video/c-qcam.c index fe8937ed1..ba4d43b90 100644 --- a/linux/drivers/media/video/c-qcam.c +++ b/linux/drivers/media/video/c-qcam.c @@ -52,6 +52,7 @@ struct qcam_device { int contrast, brightness, whitebal; int top, left; unsigned int bidirectional; + unsigned long in_use; struct mutex lock; }; @@ -688,11 +689,28 @@ static ssize_t qcam_read(struct file *file, char __user *buf, return len; } +static int qcam_exclusive_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct qcam_device *qcam = (struct qcam_device *)dev; + + return test_and_set_bit(0, &qcam->in_use) ? -EBUSY : 0; +} + +static int qcam_exclusive_release(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct qcam_device *qcam = (struct qcam_device *)dev; + + clear_bit(0, &qcam->in_use); + return 0; +} + /* video device template */ static const struct file_operations qcam_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = qcam_exclusive_open, + .release = qcam_exclusive_release, .ioctl = qcam_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -705,6 +723,7 @@ static struct video_device qcam_template= { .name = "Colour QuickCam", .fops = &qcam_fops, + .release = video_device_release_empty, }; /* Initialize the QuickCam driver control structure. */ @@ -788,8 +807,7 @@ static int init_cqcam(struct parport *port) parport_release(qcam->pdev); - if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr)==-1) - { + if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { printk(KERN_ERR "Unable to register Colour QuickCam on %s\n", qcam->pport->name); parport_unregister_device(qcam->pdev); diff --git a/linux/drivers/media/video/cafe_ccic.c b/linux/drivers/media/video/cafe_ccic.c index 4c0d44c5d..26c08f00b 100644 --- a/linux/drivers/media/video/cafe_ccic.c +++ b/linux/drivers/media/video/cafe_ccic.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> +#include <linux/mm.h> #include <linux/pci.h> #include <linux/i2c.h> #include <linux/interrupt.h> diff --git a/linux/drivers/media/video/cpia.c b/linux/drivers/media/video/cpia.c index 21c1f4dbb..0bee440ab 100644 --- a/linux/drivers/media/video/cpia.c +++ b/linux/drivers/media/video/cpia.c @@ -44,6 +44,7 @@ #endif #include "cpia.h" +#include "compat.h" static int video_nr = -1; @@ -3155,7 +3156,7 @@ static void put_cam(struct cpia_camera_ops* ops) static int cpia_open(struct inode *inode, struct file *file) { struct video_device *dev = video_devdata(file); - struct cam_data *cam = dev->priv; + struct cam_data *cam = video_get_drvdata(dev); int err; if (!cam) { @@ -3202,7 +3203,7 @@ static int cpia_open(struct inode *inode, struct file *file) /* Set ownership of /proc/cpia/videoX to current user */ if(cam->proc_entry) - cam->proc_entry->uid = current->uid; + cam->proc_entry->uid = current_uid(); /* set mark for loading first frame uncompressed */ cam->first_frame = 1; @@ -3232,7 +3233,7 @@ static int cpia_open(struct inode *inode, struct file *file) static int cpia_close(struct inode *inode, struct file *file) { struct video_device *dev = file->private_data; - struct cam_data *cam = dev->priv; + struct cam_data *cam = video_get_drvdata(dev); if (cam->ops) { /* Return ownership of /proc/cpia/videoX to root */ @@ -3284,7 +3285,7 @@ static ssize_t cpia_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct video_device *dev = file->private_data; - struct cam_data *cam = dev->priv; + struct cam_data *cam = video_get_drvdata(dev); int err; /* make this _really_ smp and multithread-safe */ @@ -3341,7 +3342,7 @@ static int cpia_do_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, void *arg) { struct video_device *dev = file->private_data; - struct cam_data *cam = dev->priv; + struct cam_data *cam = video_get_drvdata(dev); int retval = 0; if (!cam || !cam->ops) @@ -3739,7 +3740,7 @@ static int cpia_mmap(struct file *file, struct vm_area_struct *vma) unsigned long start = vma->vm_start; unsigned long size = vma->vm_end - vma->vm_start; unsigned long page, pos; - struct cam_data *cam = dev->priv; + struct cam_data *cam = video_get_drvdata(dev); int retval; if (!cam || !cam->ops) @@ -3801,6 +3802,7 @@ static const struct file_operations cpia_fops = { static struct video_device cpia_template = { .name = "CPiA Camera", .fops = &cpia_fops, + .release = video_device_release_empty, }; /* initialise cam_data structure */ @@ -3928,7 +3930,7 @@ static void init_camera_struct(struct cam_data *cam, cam->proc_entry = NULL; memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template)); - cam->vdev.priv = cam; + video_set_drvdata(&cam->vdev, cam); cam->curframe = 0; for (i = 0; i < FRAME_NUM; i++) { @@ -3955,7 +3957,7 @@ struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowleve camera->lowlevel_data = lowlevel; /* register v4l device */ - if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { kfree(camera); printk(KERN_DEBUG "video_register_device failed\n"); return NULL; diff --git a/linux/drivers/media/video/cpia2/cpia2_core.c b/linux/drivers/media/video/cpia2/cpia2_core.c index ce43a054a..8e55d3a21 100644 --- a/linux/drivers/media/video/cpia2/cpia2_core.c +++ b/linux/drivers/media/video/cpia2/cpia2_core.c @@ -1563,7 +1563,7 @@ static int config_sensor_500(struct camera_data *cam, * * This sets all user changeable properties to the values in cam->params. *****************************************************************************/ -int set_all_properties(struct camera_data *cam) +static int set_all_properties(struct camera_data *cam) { /** * Don't set target_kb here, it will be set later. @@ -1614,7 +1614,7 @@ void cpia2_save_camera_state(struct camera_data *cam) * get_color_params * *****************************************************************************/ -void get_color_params(struct camera_data *cam) +static void get_color_params(struct camera_data *cam) { cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS, TRANSFER_READ, 0); cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION, TRANSFER_READ, 0); @@ -1907,7 +1907,7 @@ void cpia2_set_saturation(struct camera_data *cam, unsigned char value) * wake_system * *****************************************************************************/ -void wake_system(struct camera_data *cam) +static void wake_system(struct camera_data *cam) { cpia2_do_command(cam, CPIA2_CMD_SET_WAKEUP, TRANSFER_WRITE, 0); } @@ -1918,7 +1918,7 @@ void wake_system(struct camera_data *cam) * * Valid for STV500 sensor only *****************************************************************************/ -void set_lowlight_boost(struct camera_data *cam) +static void set_lowlight_boost(struct camera_data *cam) { struct cpia2_command cmd; @@ -2195,7 +2195,7 @@ void cpia2_dbg_dump_registers(struct camera_data *cam) * * Sets all values to the defaults *****************************************************************************/ -void reset_camera_struct(struct camera_data *cam) +static void reset_camera_struct(struct camera_data *cam) { /*** * The following parameter values are the defaults from the register map. diff --git a/linux/drivers/media/video/cpia2/cpia2_usb.c b/linux/drivers/media/video/cpia2/cpia2_usb.c index 01131e984..bd9277873 100644 --- a/linux/drivers/media/video/cpia2/cpia2_usb.c +++ b/linux/drivers/media/video/cpia2/cpia2_usb.c @@ -486,7 +486,7 @@ int cpia2_usb_change_streaming_alternate(struct camera_data *cam, * set_alternate * *****************************************************************************/ -int set_alternate(struct camera_data *cam, unsigned int alt) +static int set_alternate(struct camera_data *cam, unsigned int alt) { int ret = 0; @@ -640,7 +640,7 @@ int cpia2_usb_transfer_cmd(struct camera_data *cam, static int submit_urbs(struct camera_data *cam) { struct urb *urb; - int fx, err, i; + int fx, err, i, j; for(i=0; i<NUM_SBUF; ++i) { if (cam->sbuf[i].data) @@ -665,6 +665,9 @@ static int submit_urbs(struct camera_data *cam) } urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); if (!urb) { + ERR("%s: usb_alloc_urb error!\n", __func__); + for (j = 0; j < i; j++) + usb_free_urb(cam->sbuf[j].urb); return -ENOMEM; } diff --git a/linux/drivers/media/video/cpia2/cpia2_v4l.c b/linux/drivers/media/video/cpia2/cpia2_v4l.c index 515c8b57a..897e8d1a5 100644 --- a/linux/drivers/media/video/cpia2/cpia2_v4l.c +++ b/linux/drivers/media/video/cpia2/cpia2_v4l.c @@ -241,8 +241,7 @@ static struct v4l2_queryctrl controls[] = { *****************************************************************************/ static int cpia2_open(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct camera_data *cam = video_get_drvdata(dev); + struct camera_data *cam = video_drvdata(file); int retval = 0; if (!cam) { @@ -357,8 +356,7 @@ static int cpia2_close(struct inode *inode, struct file *file) static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, loff_t *off) { - struct video_device *dev = video_devdata(file); - struct camera_data *cam = video_get_drvdata(dev); + struct camera_data *cam = video_drvdata(file); int noblock = file->f_flags&O_NONBLOCK; struct cpia2_fh *fh = file->private_data; @@ -382,9 +380,7 @@ static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, *****************************************************************************/ static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait) { - struct video_device *dev = video_devdata(filp); - struct camera_data *cam = video_get_drvdata(dev); - + struct camera_data *cam = video_drvdata(filp); struct cpia2_fh *fh = filp->private_data; if(!cam) @@ -1024,7 +1020,6 @@ static int ioctl_queryctrl(void *arg,struct camera_data *cam) if(cam->params.pnp_id.device_type == DEVICE_STV_672 && cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){ // Maximum 15fps - int i; for(i=0; i<c->maximum; ++i) { if(framerate_controls[i].value == CPIA2_VP_FRAMERATE_15) { @@ -1580,8 +1575,7 @@ static int ioctl_dqbuf(void *arg,struct camera_data *cam, struct file *file) static int cpia2_do_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_nr, void *arg) { - struct video_device *dev = video_devdata(file); - struct camera_data *cam = video_get_drvdata(dev); + struct camera_data *cam = video_drvdata(file); int retval = 0; if (!cam) @@ -1861,9 +1855,8 @@ static int cpia2_ioctl(struct inode *inode, struct file *file, *****************************************************************************/ static int cpia2_mmap(struct file *file, struct vm_area_struct *area) { + struct camera_data *cam = video_drvdata(file); int retval; - struct video_device *dev = video_devdata(file); - struct camera_data *cam = video_get_drvdata(dev); /* Priority check */ struct cpia2_fh *fh = file->private_data; @@ -1959,8 +1952,7 @@ int cpia2_register_camera(struct camera_data *cam) reset_camera_struct_v4l(cam); /* register v4l device */ - if (video_register_device - (cam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { ERR("video_register_device failed\n"); video_device_release(cam->vdev); return -ENODEV; diff --git a/linux/drivers/media/video/cs5345.c b/linux/drivers/media/video/cs5345.c index e4c505d2f..decbc5f1f 100644 --- a/linux/drivers/media/video/cs5345.c +++ b/linux/drivers/media/video/cs5345.c @@ -36,7 +36,7 @@ static int debug; module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debugging messages\n\t\t\t0=Off (default), 1=On"); +MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) static unsigned short normal_i2c[] = { 0x98 >> 1, I2C_CLIENT_END }; diff --git a/linux/drivers/media/video/cs53l32a.c b/linux/drivers/media/video/cs53l32a.c index f5ea3f4a9..68f31270c 100644 --- a/linux/drivers/media/video/cs53l32a.c +++ b/linux/drivers/media/video/cs53l32a.c @@ -40,7 +40,7 @@ static int debug; module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Debugging messages\n\t\t\t0=Off (default), 1=On"); +MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; diff --git a/linux/drivers/media/video/cx18/Makefile b/linux/drivers/media/video/cx18/Makefile index b23d2e261..f7bf0edf9 100644 --- a/linux/drivers/media/video/cx18/Makefile +++ b/linux/drivers/media/video/cx18/Makefile @@ -2,7 +2,7 @@ cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio. cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \ cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \ cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \ - cx18-dvb.o + cx18-dvb.o cx18-io.o obj-$(CONFIG_VIDEO_CX18) += cx18.o diff --git a/linux/drivers/media/video/cx18/cx18-audio.c b/linux/drivers/media/video/cx18/cx18-audio.c index 6d5b94fc7..57beddf0a 100644 --- a/linux/drivers/media/video/cx18/cx18-audio.c +++ b/linux/drivers/media/video/cx18/cx18-audio.c @@ -22,6 +22,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-i2c.h" #include "cx18-cards.h" #include "cx18-audio.h" @@ -60,10 +61,10 @@ int cx18_audio_set_io(struct cx18 *cx) if (err) return err; - val = read_reg(CX18_AUDIO_ENABLE) & ~0x30; + val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30; val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 : (audio_input << 4); - write_reg(val | 0xb00, CX18_AUDIO_ENABLE); + cx18_write_reg(cx, val | 0xb00, CX18_AUDIO_ENABLE); cx18_vapi(cx, CX18_APU_RESETAI, 1, 0); return 0; } diff --git a/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index 3b0a2c450..d8626e354 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/drivers/media/video/cx18/cx18-av-core.c @@ -22,27 +22,29 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) { - u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3)); + u32 reg = 0xc40000 + (addr & ~3); u32 mask = 0xff; int shift = (addr & 3) * 8; + u32 x = cx18_read_reg(cx, reg); x = (x & ~(mask << shift)) | ((u32)value << shift); - writel(x, cx->reg_mem + 0xc40000 + (addr & ~3)); + cx18_write_reg(cx, x, reg); return 0; } int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value) { - writel(value, cx->reg_mem + 0xc40000 + addr); + cx18_write_reg(cx, value, 0xc40000 + addr); return 0; } u8 cx18_av_read(struct cx18 *cx, u16 addr) { - u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3)); + u32 x = cx18_read_reg(cx, 0xc40000 + (addr & ~3)); int shift = (addr & 3) * 8; return (x >> shift) & 0xff; @@ -50,7 +52,7 @@ u8 cx18_av_read(struct cx18 *cx, u16 addr) u32 cx18_av_read4(struct cx18 *cx, u16 addr) { - return readl(cx->reg_mem + 0xc40000 + addr); + return cx18_read_reg(cx, 0xc40000 + addr); } int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask, diff --git a/linux/drivers/media/video/cx18/cx18-av-firmware.c b/linux/drivers/media/video/cx18/cx18-av-firmware.c index 834b92482..0488b6297 100644 --- a/linux/drivers/media/video/cx18/cx18-av-firmware.c +++ b/linux/drivers/media/video/cx18/cx18-av-firmware.c @@ -20,6 +20,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include <linux/firmware.h> #define CX18_AUDIO_ENABLE 0xc72014 @@ -32,7 +33,7 @@ int cx18_av_loadfw(struct cx18 *cx) u32 v; const u8 *ptr; int i; - int retries = 0; + int retries1 = 0; if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) { CX18_ERR("unable to open firmware %s\n", FWFILE); @@ -41,7 +42,7 @@ int cx18_av_loadfw(struct cx18 *cx) /* The firmware load often has byte errors, so allow for several retries, both at byte level and at the firmware load level. */ - while (retries < 5) { + while (retries1 < 5) { cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000); cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); @@ -57,9 +58,9 @@ int cx18_av_loadfw(struct cx18 *cx) for (i = 0; i < size; i++) { u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16); u32 value = 0; - int retries; + int retries2; - for (retries = 0; retries < 5; retries++) { + for (retries2 = 0; retries2 < 5; retries2++) { cx18_av_write4(cx, CXADEC_DL_CTL, dl_control); udelay(10); value = cx18_av_read4(cx, CXADEC_DL_CTL); @@ -69,18 +70,18 @@ int cx18_av_loadfw(struct cx18 *cx) the address. We can only write the lower address byte of the address. */ if ((value & 0x3F00) != (dl_control & 0x3F00)) { - retries = 5; + retries2 = 5; break; } } - if (retries >= 5) + if (retries2 >= 5) break; } if (i == size) break; - retries++; + retries1++; } - if (retries >= 5) { + if (retries1 >= 5) { CX18_ERR("unable to load firmware %s\n", FWFILE); release_firmware(fw); return -EIO; @@ -119,10 +120,10 @@ int cx18_av_loadfw(struct cx18 *cx) have a name in the spec. */ cx18_av_write4(cx, 0x09CC, 1); - v = read_reg(CX18_AUDIO_ENABLE); - /* If bit 11 is 1 */ + v = cx18_read_reg(cx, CX18_AUDIO_ENABLE); + /* If bit 11 is 1, clear bit 10 */ if (v & 0x800) - write_reg(v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); /* Clear bit 10 */ + cx18_write_reg(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); /* Enable WW auto audio standard detection */ v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); diff --git a/linux/drivers/media/video/cx18/cx18-cards.c b/linux/drivers/media/video/cx18/cx18-cards.c index 8fe5f38c4..3cb9734ec 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.c +++ b/linux/drivers/media/video/cx18/cx18-cards.c @@ -163,7 +163,7 @@ static const struct cx18_card cx18_card_h900 = { }, .audio_inputs = { { CX18_CARD_INPUT_AUD_TUNER, - CX18_AV_AUDIO8, 0 }, + CX18_AV_AUDIO5, 0 }, { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 0 }, }, diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index 22434aadd..4de7b501f 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -4,6 +4,7 @@ * Derived from ivtv-driver.c * * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +23,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-version.h" #include "cx18-cards.h" #include "cx18-i2c.h" @@ -73,10 +75,14 @@ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - -static int cardtype_c = 1; -static int tuner_c = 1; -static int radio_c = 1; +static int mmio_ndelay[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; +static unsigned cardtype_c = 1; +static unsigned tuner_c = 1; +static unsigned radio_c = 1; +static unsigned mmio_ndelay_c = 1; static char pal[] = "--"; static char secam[] = "--"; static char ntsc[] = "-"; @@ -95,6 +101,7 @@ int cx18_debug; module_param_array(tuner, int, &tuner_c, 0644); module_param_array(radio, bool, &radio_c, 0644); module_param_array(cardtype, int, &cardtype_c, 0644); +module_param_array(mmio_ndelay, int, &mmio_ndelay_c, 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); @@ -140,6 +147,11 @@ MODULE_PARM_DESC(debug, MODULE_PARM_DESC(cx18_pci_latency, "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" "\t\t\tDefault: Yes"); +MODULE_PARM_DESC(mmio_ndelay, + "Delay (ns) for each CX23418 memory mapped IO access.\n" + "\t\t\tTry larger values that are close to a multiple of the\n" + "\t\t\tPCI clock period, 30.3 ns, if your card doesn't work.\n" + "\t\t\tDefault: " __stringify(CX18_DEFAULT_MMIO_NDELAY)); MODULE_PARM_DESC(enc_mpg_buffers, "Encoder MPG Buffers (in MB)\n" "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS)); @@ -356,6 +368,11 @@ static void cx18_process_options(struct cx18 *cx) cx->options.tuner = tuner[cx->num]; cx->options.radio = radio[cx->num]; + if (mmio_ndelay[cx->num] < 0) + cx->options.mmio_ndelay = CX18_DEFAULT_MMIO_NDELAY; + else + cx->options.mmio_ndelay = mmio_ndelay[cx->num]; + cx->std = cx18_parse_std(cx); if (cx->options.cardtype == -1) { CX18_INFO("Ignore card\n"); @@ -395,9 +412,9 @@ done: if (cx->card == NULL) { cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); - CX18_ERR("Unknown card: vendor/device: %04x/%04x\n", + CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n", cx->dev->vendor, cx->dev->device); - CX18_ERR(" subsystem vendor/device: %04x/%04x\n", + CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n", cx->dev->subsystem_vendor, cx->dev->subsystem_device); CX18_ERR("Defaulting to %s card\n", cx->card->name); CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); @@ -511,9 +528,9 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev, return -EIO; } - /* Check for bus mastering */ + /* Enable bus mastering and memory mapped IO for the CX23418 */ pci_read_config_word(dev, PCI_COMMAND, &cmd); - cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; pci_write_config_word(dev, PCI_COMMAND, cmd); pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev); @@ -525,11 +542,6 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev, pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); } - /* This config space value relates to DMA latencies. The - default value 0x8080 is too low however and will lead - to DMA errors. 0xffff is the max value which solves - these problems. */ - pci_write_config_dword(dev, 0x40, 0xffff); CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, " "irq: %d, latency: %d, memory: 0x%lx\n", @@ -656,7 +668,7 @@ static int __devinit cx18_probe(struct pci_dev *dev, goto free_mem; } cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET; - devtype = read_reg(0xC72028); + devtype = cx18_read_reg(cx, 0xC72028); switch (devtype & 0xff000000) { case 0xff000000: CX18_INFO("cx23418 revision %08x (A)\n", devtype); @@ -902,8 +914,8 @@ static void cx18_remove(struct pci_dev *pci_dev) cx18_stop_all_captures(cx); /* Interrupts */ - sw1_irq_disable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); - sw2_irq_disable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); + cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); + cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); cx18_halt_firmware(cx); diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index 4801bc7fb..66cb74887 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/drivers/media/video/cx18/cx18-driver.h @@ -38,7 +38,6 @@ #include <linux/i2c-algo-bit.h> #include <linux/list.h> #include <linux/unistd.h> -#include <linux/byteorder/swab.h> #include <linux/pagemap.h> #include <linux/workqueue.h> #include <linux/mutex.h> @@ -64,6 +63,9 @@ # error "This driver requires kernel PCI support." #endif +/* Default delay to throttle mmio access to the CX23418 */ +#define CX18_DEFAULT_MMIO_NDELAY 0 /* 0 ns = 0 PCI clock(s) / 33 MHz */ + #define CX18_MEM_OFFSET 0x00000000 #define CX18_MEM_SIZE 0x04000000 #define CX18_REG_OFFSET 0x02000000 @@ -177,6 +179,7 @@ struct cx18_options { int cardtype; /* force card type on load */ int tuner; /* set tuner on load */ int radio; /* enable/disable radio */ + unsigned long mmio_ndelay; /* delay in ns after every PCI mmio access */ }; /* per-buffer bit flags */ @@ -216,8 +219,7 @@ struct cx18_buffer { struct cx18_queue { struct list_head list; - u32 buffers; - u32 length; + atomic_t buffers; u32 bytesused; }; @@ -237,6 +239,8 @@ struct cx18_dvb { struct cx18; /* forward reference */ struct cx18_scb; /* forward reference */ +#define CX18_INVALID_TASK_HANDLE 0xffffffff + struct cx18_stream { /* These first four fields are always set, even if the stream is not actually created. */ @@ -259,7 +263,6 @@ struct cx18_stream { /* Buffer Stats */ u32 buffers; u32 buf_size; - u32 buffers_stolen; /* Buffer Queues */ struct cx18_queue q_free; /* free buffers */ @@ -458,47 +461,4 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv); /* First-open initialization: load firmware, etc. */ int cx18_init_on_first_open(struct cx18 *cx); -/* This is a PCI post thing, where if the pci register is not read, then - the write doesn't always take effect right away. By reading back the - register any pending PCI writes will be performed (in order), and so - you can be sure that the writes are guaranteed to be done. - - Rarely needed, only in some timing sensitive cases. - Apparently if this is not done some motherboards seem - to kill the firmware and get into the broken state until computer is - rebooted. */ -#define write_sync(val, reg) \ - do { writel(val, reg); readl(reg); } while (0) - -#define read_reg(reg) readl(cx->reg_mem + (reg)) -#define write_reg(val, reg) writel(val, cx->reg_mem + (reg)) -#define write_reg_sync(val, reg) \ - do { write_reg(val, reg); read_reg(reg); } while (0) - -#define read_enc(addr) readl(cx->enc_mem + (u32)(addr)) -#define write_enc(val, addr) writel(val, cx->enc_mem + (u32)(addr)) -#define write_enc_sync(val, addr) \ - do { write_enc(val, addr); read_enc(addr); } while (0) - -#define sw1_irq_enable(val) do { \ - write_reg(val, SW1_INT_STATUS); \ - write_reg(read_reg(SW1_INT_ENABLE_PCI) | (val), SW1_INT_ENABLE_PCI); \ -} while (0) - -#define sw1_irq_disable(val) \ - write_reg(read_reg(SW1_INT_ENABLE_PCI) & ~(val), SW1_INT_ENABLE_PCI); - -#define sw2_irq_enable(val) do { \ - write_reg(val, SW2_INT_STATUS); \ - write_reg(read_reg(SW2_INT_ENABLE_PCI) | (val), SW2_INT_ENABLE_PCI); \ -} while (0) - -#define sw2_irq_disable(val) \ - write_reg(read_reg(SW2_INT_ENABLE_PCI) & ~(val), SW2_INT_ENABLE_PCI); - -#define setup_page(addr) do { \ - u32 val = read_reg(0xD000F8) & ~0x1f00; \ - write_reg(val | (((addr) >> 17) & 0x1f00), 0xD000F8); \ -} while (0) - #endif /* CX18_DRIVER_H */ diff --git a/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c index cae38985b..afc694e7b 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.c +++ b/linux/drivers/media/video/cx18/cx18-dvb.c @@ -1,7 +1,7 @@ /* * cx18 functions for DVB support * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.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 @@ -21,6 +21,7 @@ #include "cx18-version.h" #include "cx18-dvb.h" +#include "cx18-io.h" #include "cx18-streams.h" #include "cx18-cards.h" #include "s5h1409.h" @@ -87,13 +88,13 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) switch (cx->card->type) { case CX18_CARD_HVR_1600_ESMT: case CX18_CARD_HVR_1600_SAMSUNG: - v = read_reg(CX18_REG_DMUX_NUM_PORT_0_CONTROL); + v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL); v |= 0x00400000; /* Serial Mode */ v |= 0x00002000; /* Data Length - Byte */ v |= 0x00010000; /* Error - Polarity */ v |= 0x00020000; /* Error - Passthru */ v |= 0x000c0000; /* Error - Ignore */ - write_reg(v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); + cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); break; default: diff --git a/linux/drivers/media/video/cx18/cx18-dvb.h b/linux/drivers/media/video/cx18/cx18-dvb.h index d6a6ccda7..bf8d8f6f5 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.h +++ b/linux/drivers/media/video/cx18/cx18-dvb.h @@ -1,7 +1,7 @@ /* * cx18 functions for DVB support * - * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2008 Steven Toth <stoth@linuxtv.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 diff --git a/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c index 12f9de74d..e884271b8 100644 --- a/linux/drivers/media/video/cx18/cx18-fileops.c +++ b/linux/drivers/media/video/cx18/cx18-fileops.c @@ -132,6 +132,7 @@ static void cx18_dualwatch(struct cx18 *cx) u16 new_stereo_mode; const u16 stereo_mask = 0x0300; const u16 dual = 0x0200; + u32 h; new_stereo_mode = cx->params.audio_properties & stereo_mask; memset(&vt, 0, sizeof(vt)); @@ -143,13 +144,21 @@ static void cx18_dualwatch(struct cx18 *cx) if (new_stereo_mode == cx->dualwatch_stereo_mode) return; - new_bitmap = new_stereo_mode | (cx->params.audio_properties & ~stereo_mask); + new_bitmap = new_stereo_mode + | (cx->params.audio_properties & ~stereo_mask); - CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n", - cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); + CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. " + "new audio_bitmask=0x%ux\n", + cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); - if (cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2, - cx18_find_handle(cx), new_bitmap) == 0) { + h = cx18_find_handle(cx); + if (h == CX18_INVALID_TASK_HANDLE) { + CX18_DEBUG_INFO("dualwatch: can't find valid task handle\n"); + return; + } + + if (cx18_vapi(cx, + CX18_CPU_SET_AUDIO_PARAMETERS, 2, h, new_bitmap) == 0) { cx->dualwatch_stereo_mode = new_stereo_mode; return; } @@ -159,7 +168,8 @@ static void cx18_dualwatch(struct cx18 *cx) #if 0 static void cx18_update_pgm_info(struct cx18 *cx) { - u32 wr_idx = (read_enc(cx->pgm_info_offset) - cx->pgm_info_offset - 4) / 24; + u32 wr_idx = (cx18_read_enc(cx, cx->pgm_info_offset) + - cx->pgm_info_offset - 4) / 24; int cnt; int i = 0; @@ -174,13 +184,15 @@ static void cx18_update_pgm_info(struct cx18 *cx) u32 addr = cx->pgm_info_offset + 4 + idx * 24; const int mapping[] = { V4L2_ENC_IDX_FRAME_P, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_B, 0 }; - e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32); + e->offset = cx18_read_enc(cx, addr + 4) + + ((u64)cx18_read_enc(cx, addr + 8) << 32); if (e->offset > cx->mpg_data_received) break; e->offset += cx->vbi_data_inserted; - e->length = read_enc(addr); - e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32); - e->flags = mapping[read_enc(addr + 12) & 3]; + e->length = cx18_read_enc(cx, addr); + e->pts = cx18_read_enc(cx, addr + 16) + + ((u64)(cx18_read_enc(cx, addr + 20) & 1) << 32); + e->flags = mapping[cx18_read_enc(cx, addr + 12) & 3]; i++; } cx->pgm_info_write_idx = (cx->pgm_info_write_idx + i) % cx->pgm_info_num; @@ -258,7 +270,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); /* New buffers might have become available before we were added to the waitqueue */ - if (!s->q_full.buffers) + if (!atomic_read(&s->q_full.buffers)) schedule(); finish_wait(&s->waitq, &wait); if (signal_pending(current)) { @@ -544,7 +556,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) CX18_DEBUG_HI_FILE("Encoder poll\n"); poll_wait(filp, &s->waitq, wait); - if (s->q_full.length || s->q_io.length) + if (atomic_read(&s->q_full.buffers) || atomic_read(&s->q_io.buffers)) return POLLIN | POLLRDNORM; if (eof) return POLLHUP; @@ -730,20 +742,28 @@ int cx18_v4l2_open(struct inode *inode, struct file *filp) void cx18_mute(struct cx18 *cx) { - if (atomic_read(&cx->ana_capturing)) - cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, - cx18_find_handle(cx), 1); + u32 h; + if (atomic_read(&cx->ana_capturing)) { + h = cx18_find_handle(cx); + if (h != CX18_INVALID_TASK_HANDLE) + cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 1); + else + CX18_ERR("Can't find valid task handle for mute\n"); + } CX18_DEBUG_INFO("Mute\n"); } void cx18_unmute(struct cx18 *cx) { + u32 h; if (atomic_read(&cx->ana_capturing)) { - cx18_msleep_timeout(100, 0); - cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, - cx18_find_handle(cx), 12); - cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, - cx18_find_handle(cx), 0); + h = cx18_find_handle(cx); + if (h != CX18_INVALID_TASK_HANDLE) { + cx18_msleep_timeout(100, 0); + cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, h, 12); + cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 0); + } else + CX18_ERR("Can't find valid task handle for unmute\n"); } CX18_DEBUG_INFO("Unmute\n"); } diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c index 78fadd2ad..51534428c 100644 --- a/linux/drivers/media/video/cx18/cx18-firmware.c +++ b/linux/drivers/media/video/cx18/cx18-firmware.c @@ -20,6 +20,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-scb.h" #include "cx18-irq.h" #include "cx18-firmware.h" @@ -113,11 +114,11 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) src = (const u32 *)fw->data; for (i = 0; i < fw->size; i += 4096) { - setup_page(i); + cx18_setup_page(cx, i); for (j = i; j < fw->size && j < i + 4096; j += 4) { /* no need for endianness conversion on the ppc */ - __raw_writel(*src, dst); - if (__raw_readl(dst) != *src) { + cx18_raw_writel(cx, *src, dst); + if (cx18_raw_readl(cx, dst) != *src) { CX18_ERR("Mismatch at offset %x\n", i); release_firmware(fw); return -EIO; @@ -170,12 +171,15 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) if (offset + seghdr.size > sz) break; for (i = 0; i < seghdr.size; i += 4096) { - setup_page(offset + i); + cx18_setup_page(cx, offset + i); for (j = i; j < seghdr.size && j < i + 4096; j += 4) { /* no need for endianness conversion on the ppc */ - __raw_writel(src[(offset + j) / 4], dst + seghdr.addr + j); - if (__raw_readl(dst + seghdr.addr + j) != src[(offset + j) / 4]) { - CX18_ERR("Mismatch at offset %x\n", offset + j); + cx18_raw_writel(cx, src[(offset + j) / 4], + dst + seghdr.addr + j); + if (cx18_raw_readl(cx, dst + seghdr.addr + j) + != src[(offset + j) / 4]) { + CX18_ERR("Mismatch at offset %x\n", + offset + j); release_firmware(fw); return -EIO; } @@ -189,43 +193,45 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx) size = fw->size; release_firmware(fw); /* Clear bit0 for APU to start from 0 */ - write_reg(read_reg(0xc72030) & ~1, 0xc72030); + cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030); return size; } void cx18_halt_firmware(struct cx18 *cx) { CX18_DEBUG_INFO("Preparing for firmware halt.\n"); - write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ - write_reg(0x00020002, CX18_ADEC_CONTROL); + cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ + cx18_write_reg(cx, 0x00020002, CX18_ADEC_CONTROL); } void cx18_init_power(struct cx18 *cx, int lowpwr) { /* power-down Spare and AOM PLLs */ /* power-up fast, slow and mpeg PLLs */ - write_reg(0x00000008, CX18_PLL_POWER_DOWN); + cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN); /* ADEC out of sleep */ - write_reg(0x00020000, CX18_ADEC_CONTROL); + cx18_write_reg(cx, 0x00020000, CX18_ADEC_CONTROL); /* The fast clock is at 200/245 MHz */ - write_reg(lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); - write_reg(lowpwr ? 0x1EFBF37 : 0x038E3D7, CX18_FAST_CLOCK_PLL_FRAC); + cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); + cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7, + CX18_FAST_CLOCK_PLL_FRAC); - write_reg(2, CX18_FAST_CLOCK_PLL_POST); - write_reg(1, CX18_FAST_CLOCK_PLL_PRESCALE); - write_reg(4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); + cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST); + cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE); + cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); /* set slow clock to 125/120 MHz */ - write_reg(lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT); - write_reg(lowpwr ? 0xEBAF05 : 0x18618A8, CX18_SLOW_CLOCK_PLL_FRAC); - write_reg(4, CX18_SLOW_CLOCK_PLL_POST); + cx18_write_reg(cx, lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT); + cx18_write_reg(cx, lowpwr ? 0xEBAF05 : 0x18618A8, + CX18_SLOW_CLOCK_PLL_FRAC); + cx18_write_reg(cx, 4, CX18_SLOW_CLOCK_PLL_POST); /* mpeg clock pll 54MHz */ - write_reg(0xF, CX18_MPEG_CLOCK_PLL_INT); - write_reg(0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC); - write_reg(8, CX18_MPEG_CLOCK_PLL_POST); + cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT); + cx18_write_reg(cx, 0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC); + cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST); /* Defaults */ /* APU = SC or SC/2 = 125/62.5 */ @@ -242,81 +248,84 @@ void cx18_init_power(struct cx18 *cx, int lowpwr) /* VFC = disabled */ /* USB = disabled */ - write_reg(lowpwr ? 0xFFFF0020 : 0x00060004, CX18_CLOCK_SELECT1); - write_reg(lowpwr ? 0xFFFF0004 : 0x00060006, CX18_CLOCK_SELECT2); + cx18_write_reg(cx, lowpwr ? 0xFFFF0020 : 0x00060004, + CX18_CLOCK_SELECT1); + cx18_write_reg(cx, lowpwr ? 0xFFFF0004 : 0x00060006, + CX18_CLOCK_SELECT2); - write_reg(0xFFFF0002, CX18_HALF_CLOCK_SELECT1); - write_reg(0xFFFF0104, CX18_HALF_CLOCK_SELECT2); + cx18_write_reg(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1); + cx18_write_reg(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2); - write_reg(0xFFFF9026, CX18_CLOCK_ENABLE1); - write_reg(0xFFFF3105, CX18_CLOCK_ENABLE2); + cx18_write_reg(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1); + cx18_write_reg(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2); } void cx18_init_memory(struct cx18 *cx) { cx18_msleep_timeout(10, 0); - write_reg(0x10000, CX18_DDR_SOFT_RESET); + cx18_write_reg(cx, 0x10000, CX18_DDR_SOFT_RESET); cx18_msleep_timeout(10, 0); - write_reg(cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); + cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); cx18_msleep_timeout(10, 0); - write_reg(cx->card->ddr.refresh, CX18_DDR_REFRESH); - write_reg(cx->card->ddr.timing1, CX18_DDR_TIMING1); - write_reg(cx->card->ddr.timing2, CX18_DDR_TIMING2); + cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH); + cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1); + cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2); cx18_msleep_timeout(10, 0); /* Initialize DQS pad time */ - write_reg(cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); - write_reg(cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); + cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); + cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); cx18_msleep_timeout(10, 0); - write_reg(0x20000, CX18_DDR_SOFT_RESET); + cx18_write_reg(cx, 0x20000, CX18_DDR_SOFT_RESET); cx18_msleep_timeout(10, 0); /* use power-down mode when idle */ - write_reg(0x00000010, CX18_DDR_POWER_REG); - - write_reg(0x10001, CX18_REG_BUS_TIMEOUT_EN); - - write_reg(0x48, CX18_DDR_MB_PER_ROW_7); - write_reg(0xE0000, CX18_DDR_BASE_63_ADDR); - - write_reg(0x00000101, CX18_WMB_CLIENT02); /* AO */ - write_reg(0x00000101, CX18_WMB_CLIENT09); /* AI2 */ - write_reg(0x00000101, CX18_WMB_CLIENT05); /* VIM1 */ - write_reg(0x00000101, CX18_WMB_CLIENT06); /* AI1 */ - write_reg(0x00000101, CX18_WMB_CLIENT07); /* 3D comb */ - write_reg(0x00000101, CX18_WMB_CLIENT10); /* ME */ - write_reg(0x00000101, CX18_WMB_CLIENT12); /* ENC */ - write_reg(0x00000101, CX18_WMB_CLIENT13); /* PK */ - write_reg(0x00000101, CX18_WMB_CLIENT11); /* RC */ - write_reg(0x00000101, CX18_WMB_CLIENT14); /* AVO */ + cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG); + + cx18_write_reg(cx, 0x10001, CX18_REG_BUS_TIMEOUT_EN); + + cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7); + cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR); + + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02); /* AO */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09); /* AI2 */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05); /* VIM1 */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06); /* AI1 */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07); /* 3D comb */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10); /* ME */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12); /* ENC */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13); /* PK */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11); /* RC */ + cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14); /* AVO */ } int cx18_firmware_init(struct cx18 *cx) { /* Allow chip to control CLKRUN */ - write_reg(0x5, CX18_DSP0_INTERRUPT_MASK); + cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); - write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ + cx18_write_reg(cx, 0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */ cx18_msleep_timeout(1, 0); - sw1_irq_enable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); - sw2_irq_enable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); + cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); + cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); /* Only if the processor is not running */ - if (read_reg(CX18_PROC_SOFT_RESET) & 8) { + if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) { int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx); - write_enc(0xE51FF004, 0); - write_enc(0xa00000, 4); /* todo: not hardcoded */ - write_reg(0x00010000, CX18_PROC_SOFT_RESET); /* Start APU */ + cx18_write_enc(cx, 0xE51FF004, 0); + cx18_write_enc(cx, 0xa00000, 4); /* todo: not hardcoded */ + /* Start APU */ + cx18_write_reg(cx, 0x00010000, CX18_PROC_SOFT_RESET); cx18_msleep_timeout(500, 0); sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", @@ -326,9 +335,10 @@ int cx18_firmware_init(struct cx18 *cx) int retries = 0; /* start the CPU */ - write_reg(0x00080000, CX18_PROC_SOFT_RESET); + cx18_write_reg(cx, 0x00080000, CX18_PROC_SOFT_RESET); while (retries++ < 50) { /* Loop for max 500mS */ - if ((read_reg(CX18_PROC_SOFT_RESET) & 1) == 0) + if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) + & 1) == 0) break; cx18_msleep_timeout(10, 0); } @@ -342,6 +352,6 @@ int cx18_firmware_init(struct cx18 *cx) return -EIO; } /* initialize GPIO */ - write_reg(0x14001400, 0xC78110); + cx18_write_reg(cx, 0x14001400, 0xC78110); return 0; } diff --git a/linux/drivers/media/video/cx18/cx18-gpio.c b/linux/drivers/media/video/cx18/cx18-gpio.c index 3d495dba4..3bdffbf7a 100644 --- a/linux/drivers/media/video/cx18/cx18-gpio.c +++ b/linux/drivers/media/video/cx18/cx18-gpio.c @@ -22,6 +22,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-cards.h" #include "cx18-gpio.h" #include "tuner-xc2028.h" @@ -49,11 +50,11 @@ static void gpio_write(struct cx18 *cx) u32 dir = cx->gpio_dir; u32 val = cx->gpio_val; - write_reg((dir & 0xffff) << 16, CX18_REG_GPIO_DIR1); - write_reg(((dir & 0xffff) << 16) | (val & 0xffff), + cx18_write_reg(cx, (dir & 0xffff) << 16, CX18_REG_GPIO_DIR1); + cx18_write_reg(cx, ((dir & 0xffff) << 16) | (val & 0xffff), CX18_REG_GPIO_OUT1); - write_reg(dir & 0xffff0000, CX18_REG_GPIO_DIR2); - write_reg_sync((dir & 0xffff0000) | ((val & 0xffff0000) >> 16), + cx18_write_reg(cx, dir & 0xffff0000, CX18_REG_GPIO_DIR2); + cx18_write_reg_sync(cx, (dir & 0xffff0000) | ((val & 0xffff0000) >> 16), CX18_REG_GPIO_OUT2); } @@ -141,8 +142,10 @@ void cx18_gpio_init(struct cx18 *cx) } CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n", - read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_DIR2), - read_reg(CX18_REG_GPIO_OUT1), read_reg(CX18_REG_GPIO_OUT2)); + cx18_read_reg(cx, CX18_REG_GPIO_DIR1), + cx18_read_reg(cx, CX18_REG_GPIO_DIR2), + cx18_read_reg(cx, CX18_REG_GPIO_OUT1), + cx18_read_reg(cx, CX18_REG_GPIO_OUT2)); gpio_write(cx); mutex_unlock(&cx->gpio_lock); diff --git a/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c index 64a259405..f28a1ff2e 100644 --- a/linux/drivers/media/video/cx18/cx18-i2c.c +++ b/linux/drivers/media/video/cx18/cx18-i2c.c @@ -22,13 +22,12 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-cards.h" #include "cx18-gpio.h" #include "cx18-av-core.h" #include "cx18-i2c.h" -#include <media/ir-kbd-i2c.h> - #define CX18_REG_I2C_1_WR 0xf15000 #define CX18_REG_I2C_1_RD 0xf15008 #define CX18_REG_I2C_2_WR 0xf25100 @@ -181,12 +180,12 @@ static void cx18_setscl(void *data, int state) struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; - u32 r = read_reg(addr); + u32 r = cx18_read_reg(cx, addr); if (state) - write_reg_sync(r | SETSCL_BIT, addr); + cx18_write_reg_sync(cx, r | SETSCL_BIT, addr); else - write_reg_sync(r & ~SETSCL_BIT, addr); + cx18_write_reg_sync(cx, r & ~SETSCL_BIT, addr); } static void cx18_setsda(void *data, int state) @@ -194,12 +193,12 @@ static void cx18_setsda(void *data, int state) struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; - u32 r = read_reg(addr); + u32 r = cx18_read_reg(cx, addr); if (state) - write_reg_sync(r | SETSDL_BIT, addr); + cx18_write_reg_sync(cx, r | SETSDL_BIT, addr); else - write_reg_sync(r & ~SETSDL_BIT, addr); + cx18_write_reg_sync(cx, r & ~SETSDL_BIT, addr); } static int cx18_getscl(void *data) @@ -208,7 +207,7 @@ static int cx18_getscl(void *data) int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; - return read_reg(addr) & GETSCL_BIT; + return cx18_read_reg(cx, addr) & GETSCL_BIT; } static int cx18_getsda(void *data) @@ -217,7 +216,7 @@ static int cx18_getsda(void *data) int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; - return read_reg(addr) & GETSDL_BIT; + return cx18_read_reg(cx, addr) & GETSDL_BIT; } /* template for i2c-bit-algo */ @@ -422,29 +421,33 @@ int init_cx18_i2c(struct cx18 *cx) #endif } - if (read_reg(CX18_REG_I2C_2_WR) != 0x0003c02f) { + if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) { /* Reset/Unreset I2C hardware block */ - write_reg(0x10000000, 0xc71004); /* Clock select 220MHz */ - write_reg_sync(0x10001000, 0xc71024); /* Clock Enable */ + /* Clock select 220MHz */ + cx18_write_reg(cx, 0x10000000, 0xc71004); + /* Clock Enable */ + cx18_write_reg_sync(cx, 0x10001000, 0xc71024); } /* courtesy of Steven Toth <stoth@hauppauge.com> */ - write_reg_sync(0x00c00000, 0xc7001c); + cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); mdelay(10); - write_reg_sync(0x00c000c0, 0xc7001c); + cx18_write_reg_sync(cx, 0x00c000c0, 0xc7001c); mdelay(10); - write_reg_sync(0x00c00000, 0xc7001c); + cx18_write_reg_sync(cx, 0x00c00000, 0xc7001c); mdelay(10); - write_reg_sync(0x00c00000, 0xc730c8); /* Set to edge-triggered intrs. */ - write_reg_sync(0x00c00000, 0xc730c4); /* Clear any stale intrs */ + /* Set to edge-triggered intrs. */ + cx18_write_reg_sync(cx, 0x00c00000, 0xc730c8); + /* Clear any stale intrs */ + cx18_write_reg_sync(cx, 0x00c00000, 0xc730c4); /* Hw I2C1 Clock Freq ~100kHz */ - write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_1_WR); + cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR); cx18_setscl(&cx->i2c_algo_cb_data[0], 1); cx18_setsda(&cx->i2c_algo_cb_data[0], 1); /* Hw I2C2 Clock Freq ~100kHz */ - write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_2_WR); + cx18_write_reg_sync(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR); cx18_setscl(&cx->i2c_algo_cb_data[1], 1); cx18_setsda(&cx->i2c_algo_cb_data[1], 1); @@ -458,8 +461,10 @@ void exit_cx18_i2c(struct cx18 *cx) { int i; CX18_DEBUG_I2C("i2c exit\n"); - write_reg(read_reg(CX18_REG_I2C_1_WR) | 4, CX18_REG_I2C_1_WR); - write_reg(read_reg(CX18_REG_I2C_2_WR) | 4, CX18_REG_I2C_2_WR); + cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_1_WR) | 4, + CX18_REG_I2C_1_WR); + cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_2_WR) | 4, + CX18_REG_I2C_2_WR); for (i = 0; i < 2; i++) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) diff --git a/linux/drivers/media/video/cx18/cx18-io.c b/linux/drivers/media/video/cx18/cx18-io.c new file mode 100644 index 000000000..55d1df932 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-io.c @@ -0,0 +1,139 @@ +/* + * cx18 driver PCI memory mapped IO access routines + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include "cx18-driver.h" +#include "cx18-io.h" +#include "cx18-irq.h" + +void cx18_memcpy_fromio(struct cx18 *cx, void *to, + const void __iomem *from, unsigned int len) +{ + const u8 __iomem *src = from; + u8 *dst = to; + + /* Align reads on the CX23418's addresses */ + if ((len > 0) && ((unsigned long) src & 1)) { + *dst = cx18_readb(cx, src); + len--; + dst++; + src++; + } + if ((len > 1) && ((unsigned long) src & 2)) { + *((u16 *)dst) = cx18_raw_readw(cx, src); + len -= 2; + dst += 2; + src += 2; + } + while (len > 3) { + *((u32 *)dst) = cx18_raw_readl(cx, src); + len -= 4; + dst += 4; + src += 4; + } + if (len > 1) { + *((u16 *)dst) = cx18_raw_readw(cx, src); + len -= 2; + dst += 2; + src += 2; + } + if (len > 0) + *dst = cx18_readb(cx, src); +} + +void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) +{ + u8 __iomem *dst = addr; + u16 val2 = val | (val << 8); + u32 val4 = val2 | (val2 << 16); + + /* Align writes on the CX23418's addresses */ + if ((count > 0) && ((unsigned long)dst & 1)) { + cx18_writeb(cx, (u8) val, dst); + count--; + dst++; + } + if ((count > 1) && ((unsigned long)dst & 2)) { + cx18_writew(cx, val2, dst); + count -= 2; + dst += 2; + } + while (count > 3) { + cx18_writel(cx, val4, dst); + count -= 4; + dst += 4; + } + if (count > 1) { + cx18_writew(cx, val2, dst); + count -= 2; + dst += 2; + } + if (count > 0) + cx18_writeb(cx, (u8) val, dst); +} + +void cx18_sw1_irq_enable(struct cx18 *cx, u32 val) +{ + u32 r; + cx18_write_reg(cx, val, SW1_INT_STATUS); + r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI); + cx18_write_reg(cx, r | val, SW1_INT_ENABLE_PCI); +} + +void cx18_sw1_irq_disable(struct cx18 *cx, u32 val) +{ + u32 r; + r = cx18_read_reg(cx, SW1_INT_ENABLE_PCI); + cx18_write_reg(cx, r & ~val, SW1_INT_ENABLE_PCI); +} + +void cx18_sw2_irq_enable(struct cx18 *cx, u32 val) +{ + u32 r; + cx18_write_reg(cx, val, SW2_INT_STATUS); + r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI); + cx18_write_reg(cx, r | val, SW2_INT_ENABLE_PCI); +} + +void cx18_sw2_irq_disable(struct cx18 *cx, u32 val) +{ + u32 r; + r = cx18_read_reg(cx, SW2_INT_ENABLE_PCI); + cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_PCI); +} + +void cx18_setup_page(struct cx18 *cx, u32 addr) +{ + u32 val; + val = cx18_read_reg(cx, 0xD000F8); + val = (val & ~0x1f00) | ((addr >> 17) & 0x1f00); + cx18_write_reg(cx, val, 0xD000F8); +} + +/* Tries to recover from the CX23418 responding improperly on the PCI bus */ +int cx18_pci_try_recover(struct cx18 *cx) +{ + u16 status; + + pci_read_config_word(cx->dev, PCI_STATUS, &status); + pci_write_config_word(cx->dev, PCI_STATUS, status); + return 0; +} diff --git a/linux/drivers/media/video/cx18/cx18-io.h b/linux/drivers/media/video/cx18/cx18-io.h new file mode 100644 index 000000000..7ab7be253 --- /dev/null +++ b/linux/drivers/media/video/cx18/cx18-io.h @@ -0,0 +1,140 @@ +/* + * cx18 driver PCI memory mapped IO access routines + * + * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> + * Copyright (C) 2008 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#ifndef CX18_IO_H +#define CX18_IO_H + +#include "cx18-driver.h" + +static inline void cx18_io_delay(struct cx18 *cx) +{ + if (cx->options.mmio_ndelay) + ndelay(cx->options.mmio_ndelay); +} + +/* Non byteswapping memory mapped IO */ +static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr) +{ + __raw_writel(val, addr); + cx18_io_delay(cx); +} + +static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr) +{ + u32 ret = __raw_readl(addr); + cx18_io_delay(cx); + return ret; +} + +static inline u16 cx18_raw_readw(struct cx18 *cx, const void __iomem *addr) +{ + u16 ret = __raw_readw(addr); + cx18_io_delay(cx); + return ret; +} + +/* Normal memory mapped IO */ +static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr) +{ + writel(val, addr); + cx18_io_delay(cx); +} + +static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr) +{ + writew(val, addr); + cx18_io_delay(cx); +} + +static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr) +{ + writeb(val, addr); + cx18_io_delay(cx); +} + +static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr) +{ + u32 ret = readl(addr); + cx18_io_delay(cx); + return ret; +} + +static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr) +{ + u8 ret = readb(addr); + cx18_io_delay(cx); + return ret; +} + +static inline u32 cx18_write_sync(struct cx18 *cx, u32 val, void __iomem *addr) +{ + cx18_writel(cx, val, addr); + return cx18_readl(cx, addr); +} + +void cx18_memcpy_fromio(struct cx18 *cx, void *to, + const void __iomem *from, unsigned int len); +void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count); + +/* Access "register" region of CX23418 memory mapped I/O */ +static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg) +{ + cx18_writel(cx, val, cx->reg_mem + reg); +} + +static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg) +{ + return cx18_readl(cx, cx->reg_mem + reg); +} + +static inline u32 cx18_write_reg_sync(struct cx18 *cx, u32 val, u32 reg) +{ + return cx18_write_sync(cx, val, cx->reg_mem + reg); +} + +/* Access "encoder memory" region of CX23418 memory mapped I/O */ +static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr) +{ + cx18_writel(cx, val, cx->enc_mem + addr); +} + +static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr) +{ + return cx18_readl(cx, cx->enc_mem + addr); +} + +static inline u32 cx18_write_enc_sync(struct cx18 *cx, u32 val, u32 addr) +{ + return cx18_write_sync(cx, val, cx->enc_mem + addr); +} + + +void cx18_sw1_irq_enable(struct cx18 *cx, u32 val); +void cx18_sw1_irq_disable(struct cx18 *cx, u32 val); +void cx18_sw2_irq_enable(struct cx18 *cx, u32 val); +void cx18_sw2_irq_disable(struct cx18 *cx, u32 val); +void cx18_setup_page(struct cx18 *cx, u32 addr); + +/* Tries to recover from the CX23418 responding improperly on the PCI bus */ +int cx18_pci_try_recover(struct cx18 *cx); + +#endif /* CX18_IO_H */ diff --git a/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c index e6c2bfa5d..b909c78d3 100644 --- a/linux/drivers/media/video/cx18/cx18-ioctl.c +++ b/linux/drivers/media/video/cx18/cx18-ioctl.c @@ -22,6 +22,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-version.h" #include "cx18-mailbox.h" #include "cx18-i2c.h" @@ -200,7 +201,6 @@ static int cx18_try_fmt_vid_cap(struct file *file, void *fh, { struct cx18_open_id *id = fh; struct cx18 *cx = id->cx; - int w = fmt->fmt.pix.width; int h = fmt->fmt.pix.height; @@ -248,8 +248,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, struct cx18_open_id *id = fh; struct cx18 *cx = id->cx; int ret; - int w = fmt->fmt.pix.width; - int h = fmt->fmt.pix.height; + int w, h; ret = v4l2_prio_check(&cx->prio, &id->prio); if (ret) @@ -258,6 +257,8 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, ret = cx18_try_fmt_vid_cap(file, fh, fmt); if (ret) return ret; + w = fmt->fmt.pix.width; + h = fmt->fmt.pix.height; if (cx->params.width == w && cx->params.height == h) return 0; @@ -358,9 +359,9 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) spin_lock_irqsave(&cx18_cards_lock, flags); if (cmd == VIDIOC_DBG_G_REGISTER) - regs->val = read_enc(regs->reg); + regs->val = cx18_read_enc(cx, regs->reg); else - write_enc(regs->val, regs->reg); + cx18_write_enc(cx, regs->val, regs->reg); spin_unlock_irqrestore(&cx18_cards_lock, flags); return 0; } @@ -727,6 +728,7 @@ static int cx18_encoder_cmd(struct file *file, void *fh, { struct cx18_open_id *id = fh; struct cx18 *cx = id->cx; + u32 h; switch (enc->cmd) { case V4L2_ENC_CMD_START: @@ -748,8 +750,14 @@ static int cx18_encoder_cmd(struct file *file, void *fh, return -EPERM; if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) return 0; + h = cx18_find_handle(cx); + if (h == CX18_INVALID_TASK_HANDLE) { + CX18_ERR("Can't find valid task handle for " + "V4L2_ENC_CMD_PAUSE\n"); + return -EBADFD; + } cx18_mute(cx); - cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx)); + cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h); break; case V4L2_ENC_CMD_RESUME: @@ -759,7 +767,13 @@ static int cx18_encoder_cmd(struct file *file, void *fh, return -EPERM; if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) return 0; - cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx)); + h = cx18_find_handle(cx); + if (h == CX18_INVALID_TASK_HANDLE) { + CX18_ERR("Can't find valid task handle for " + "V4L2_ENC_CMD_RESUME\n"); + return -EBADFD; + } + cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h); cx18_unmute(cx); break; @@ -836,7 +850,8 @@ static int cx18_log_status(struct file *file, void *fh) continue; CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, - (s->buffers - s->q_free.buffers) * 100 / s->buffers, + (s->buffers - atomic_read(&s->q_free.buffers)) + * 100 / s->buffers, (s->buffers * s->buf_size) / 1024, s->buffers); } CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", diff --git a/linux/drivers/media/video/cx18/cx18-irq.c b/linux/drivers/media/video/cx18/cx18-irq.c index 654664783..f117af782 100644 --- a/linux/drivers/media/video/cx18/cx18-irq.c +++ b/linux/drivers/media/video/cx18/cx18-irq.c @@ -20,6 +20,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-firmware.h" #include "cx18-fileops.h" #include "cx18-queue.h" @@ -60,8 +61,8 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) if (mb->args[2] != 1) CX18_WARN("Ack struct = %d for %s\n", mb->args[2], s->name); - id = read_enc(off); - buf = cx18_queue_find_buf(s, id, read_enc(off + 4)); + id = cx18_read_enc(cx, off); + buf = cx18_queue_get_buf_irq(s, id, cx18_read_enc(cx, off + 4)); CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); if (buf) { cx18_buf_sync_for_cpu(s, buf); @@ -81,7 +82,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb) set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); } else { CX18_WARN("Could not find buf %d for stream %s\n", - read_enc(off), s->name); + cx18_read_enc(cx, off), s->name); } mb->error = 0; mb->cmd = 0; @@ -97,8 +98,8 @@ static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb) char *p; if (mb->args[1]) { - setup_page(mb->args[1]); - memcpy_fromio(str, cx->enc_mem + mb->args[1], 252); + cx18_setup_page(cx, mb->args[1]); + cx18_memcpy_fromio(cx, str, cx->enc_mem + mb->args[1], 252); str[252] = 0; } cx18_mb_ack(cx, mb); @@ -113,7 +114,7 @@ static void hpu_cmd(struct cx18 *cx, u32 sw1) struct cx18_mailbox mb; if (sw1 & IRQ_CPU_TO_EPU) { - memcpy_fromio(&mb, &cx->scb->cpu2epu_mb, sizeof(mb)); + cx18_memcpy_fromio(cx, &mb, &cx->scb->cpu2epu_mb, sizeof(mb)); mb.error = 0; switch (mb.cmd) { @@ -145,16 +146,16 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) spin_lock(&cx->dma_reg_lock); - hw2_mask = read_reg(HW2_INT_MASK5_PCI); - hw2 = read_reg(HW2_INT_CLR_STATUS) & hw2_mask; - sw2_mask = read_reg(SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK; - sw2 = read_reg(SW2_INT_STATUS) & sw2_mask; - sw1_mask = read_reg(SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU; - sw1 = read_reg(SW1_INT_STATUS) & sw1_mask; + hw2_mask = cx18_read_reg(cx, HW2_INT_MASK5_PCI); + hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & hw2_mask; + sw2_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK; + sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & sw2_mask; + sw1_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU; + sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & sw1_mask; - write_reg(sw2&sw2_mask, SW2_INT_STATUS); - write_reg(sw1&sw1_mask, SW1_INT_STATUS); - write_reg(hw2&hw2_mask, HW2_INT_CLR_STATUS); + cx18_write_reg(cx, sw2&sw2_mask, SW2_INT_STATUS); + cx18_write_reg(cx, sw1&sw1_mask, SW1_INT_STATUS); + cx18_write_reg(cx, hw2&hw2_mask, HW2_INT_CLR_STATUS); if (sw1 || sw2 || hw2) CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); @@ -165,15 +166,15 @@ irqreturn_t cx18_irq_handler(int irq, void *dev_id) */ if (sw2) { - if (sw2 & (readl(&cx->scb->cpu2hpu_irq_ack) | - readl(&cx->scb->cpu2epu_irq_ack))) + if (sw2 & (cx18_readl(cx, &cx->scb->cpu2hpu_irq_ack) | + cx18_readl(cx, &cx->scb->cpu2epu_irq_ack))) wake_up(&cx->mb_cpu_waitq); - if (sw2 & (readl(&cx->scb->apu2hpu_irq_ack) | - readl(&cx->scb->apu2epu_irq_ack))) + if (sw2 & (cx18_readl(cx, &cx->scb->apu2hpu_irq_ack) | + cx18_readl(cx, &cx->scb->apu2epu_irq_ack))) wake_up(&cx->mb_apu_waitq); - if (sw2 & readl(&cx->scb->epu2hpu_irq_ack)) + if (sw2 & cx18_readl(cx, &cx->scb->epu2hpu_irq_ack)) wake_up(&cx->mb_epu_waitq); - if (sw2 & readl(&cx->scb->hpu2epu_irq_ack)) + if (sw2 & cx18_readl(cx, &cx->scb->hpu2epu_irq_ack)) wake_up(&cx->mb_hpu_waitq); } diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 93177514e..9d18dd22d 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -22,6 +22,7 @@ #include <stdarg.h> #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-scb.h" #include "cx18-irq.h" #include "cx18-mailbox.h" @@ -82,6 +83,7 @@ static const struct cx18_api_info api_info[] = { API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST), + API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, 0), API_ENTRY(0, 0, 0), }; @@ -105,20 +107,20 @@ static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu switch (rpu) { case APU: mb = &cx->scb->epu2apu_mb; - *state = readl(&cx->scb->apu_state); - *irq = readl(&cx->scb->epu2apu_irq); + *state = cx18_readl(cx, &cx->scb->apu_state); + *irq = cx18_readl(cx, &cx->scb->epu2apu_irq); break; case CPU: mb = &cx->scb->epu2cpu_mb; - *state = readl(&cx->scb->cpu_state); - *irq = readl(&cx->scb->epu2cpu_irq); + *state = cx18_readl(cx, &cx->scb->cpu_state); + *irq = cx18_readl(cx, &cx->scb->epu2cpu_irq); break; case HPU: mb = &cx->scb->epu2hpu_mb; - *state = readl(&cx->scb->hpu_state); - *irq = readl(&cx->scb->epu2hpu_irq); + *state = cx18_readl(cx, &cx->scb->hpu_state); + *irq = cx18_readl(cx, &cx->scb->epu2hpu_irq); break; } @@ -126,8 +128,8 @@ static struct cx18_mailbox __iomem *cx18_mb_is_complete(struct cx18 *cx, int rpu return mb; do { - *req = readl(&mb->request); - ack = readl(&mb->ack); + *req = cx18_readl(cx, &mb->request); + ack = cx18_readl(cx, &mb->ack); wait_count++; } while (*req != ack && wait_count < 600); @@ -172,9 +174,9 @@ long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb) return -EINVAL; } - setup_page(SCB_OFFSET); - write_sync(mb->request, &ack_mb->ack); - write_reg(ack_irq, SW2_INT_SET); + cx18_setup_page(cx, SCB_OFFSET); + cx18_write_sync(cx, mb->request, &ack_mb->ack); + cx18_write_reg(cx, ack_irq, SW2_INT_SET); return 0; } @@ -199,7 +201,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) CX18_DEBUG_HI_API("%s\n", info->name); else CX18_DEBUG_API("%s\n", info->name); - setup_page(SCB_OFFSET); + cx18_setup_page(cx, SCB_OFFSET); mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req); if (mb == NULL) { @@ -208,11 +210,11 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) } oldreq = req - 1; - writel(cmd, &mb->cmd); + cx18_writel(cx, cmd, &mb->cmd); for (i = 0; i < args; i++) - writel(data[i], &mb->args[i]); - writel(0, &mb->error); - writel(req, &mb->request); + cx18_writel(cx, data[i], &mb->args[i]); + cx18_writel(cx, 0, &mb->error); + cx18_writel(cx, req, &mb->request); switch (info->rpu) { case APU: waitq = &cx->mb_apu_waitq; break; @@ -223,9 +225,10 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) } if (info->flags & API_FAST) timeout /= 2; - write_reg(irq, SW1_INT_SET); + cx18_write_reg(cx, irq, SW1_INT_SET); - while (!sig && readl(&mb->ack) != readl(&mb->request) && cnt < 660) { + while (!sig && cx18_readl(cx, &mb->ack) != cx18_readl(cx, &mb->request) + && cnt < 660) { if (cnt > 200 && !in_atomic()) sig = cx18_msleep_timeout(10, 1); cnt++; @@ -233,13 +236,13 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) if (sig) return -EINTR; if (cnt == 660) { - writel(oldreq, &mb->request); + cx18_writel(cx, oldreq, &mb->request); CX18_ERR("mb %s failed\n", info->name); return -EINVAL; } for (i = 0; i < MAX_MB_ARGUMENTS; i++) - data[i] = readl(&mb->args[i]); - err = readl(&mb->error); + data[i] = cx18_readl(cx, &mb->args[i]); + err = cx18_readl(cx, &mb->error); if (!in_atomic() && (info->flags & API_SLOW)) cx18_msleep_timeout(300, 0); if (err) diff --git a/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index 6990b77c6..a33ba04a2 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/drivers/media/video/cx18/cx18-queue.c @@ -37,8 +37,7 @@ void cx18_buf_swap(struct cx18_buffer *buf) void cx18_queue_init(struct cx18_queue *q) { INIT_LIST_HEAD(&q->list); - q->buffers = 0; - q->length = 0; + atomic_set(&q->buffers, 0); q->bytesused = 0; } @@ -55,8 +54,7 @@ void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, } spin_lock_irqsave(&s->qlock, flags); list_add_tail(&buf->list, &q->list); - q->buffers++; - q->length += s->buf_size; + atomic_inc(&q->buffers); q->bytesused += buf->bytesused - buf->readpos; spin_unlock_irqrestore(&s->qlock, flags); } @@ -70,20 +68,20 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) if (!list_empty(&q->list)) { buf = list_entry(q->list.next, struct cx18_buffer, list); list_del_init(q->list.next); - q->buffers--; - q->length -= s->buf_size; + atomic_dec(&q->buffers); q->bytesused -= buf->bytesused - buf->readpos; } spin_unlock_irqrestore(&s->qlock, flags); return buf; } -struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id, +struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, u32 bytesused) { struct cx18 *cx = s->cx; struct list_head *p; + spin_lock(&s->qlock); list_for_each(p, &s->q_free.list) { struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); @@ -92,114 +90,45 @@ struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id, continue; buf->bytesused = bytesused; /* the transport buffers are handled differently, - so there is no need to move them to the full queue */ - if (s->type == CX18_ENC_STREAM_TYPE_TS) - return buf; - s->q_free.buffers--; - s->q_free.length -= s->buf_size; - s->q_full.buffers++; - s->q_full.length += s->buf_size; - s->q_full.bytesused += buf->bytesused; - list_move_tail(&buf->list, &s->q_full.list); + they are not moved to the full queue */ + if (s->type != CX18_ENC_STREAM_TYPE_TS) { + atomic_dec(&s->q_free.buffers); + atomic_inc(&s->q_full.buffers); + s->q_full.bytesused += buf->bytesused; + list_move_tail(&buf->list, &s->q_full.list); + } + spin_unlock(&s->qlock); return buf; } + spin_unlock(&s->qlock); CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); return NULL; } -static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from, - struct cx18_queue *to, int clear, int full) -{ - struct cx18_buffer *buf = - list_entry(from->list.next, struct cx18_buffer, list); - - list_move_tail(from->list.next, &to->list); - from->buffers--; - from->length -= s->buf_size; - from->bytesused -= buf->bytesused - buf->readpos; - /* special handling for q_free */ - if (clear) - buf->bytesused = buf->readpos = buf->b_flags = 0; - else if (full) { - /* special handling for stolen buffers, assume - all bytes are used. */ - buf->bytesused = s->buf_size; - buf->readpos = buf->b_flags = 0; - } - to->buffers++; - to->length += s->buf_size; - to->bytesused += buf->bytesused - buf->readpos; -} - -/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'. - If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'. - If 'steal' != NULL, then buffers may also taken from that queue if - needed. - - The buffer is automatically cleared if it goes to the free queue. It is - also cleared if buffers need to be taken from the 'steal' queue and - the 'from' queue is the free queue. - - When 'from' is q_free, then needed_bytes is compared to the total - available buffer length, otherwise needed_bytes is compared to the - bytesused value. For the 'steal' queue the total available buffer - length is always used. - - -ENOMEM is returned if the buffers could not be obtained, 0 if all - buffers where obtained from the 'from' list and if non-zero then - the number of stolen buffers is returned. */ -static int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from, - struct cx18_queue *steal, struct cx18_queue *to, - int needed_bytes) +/* Move all buffers of a queue to q_free, while flushing the buffers */ +static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) { unsigned long flags; - int rc = 0; - int from_free = from == &s->q_free; - int to_free = to == &s->q_free; - int bytes_available; - - spin_lock_irqsave(&s->qlock, flags); - if (needed_bytes == 0) { - from_free = 1; - needed_bytes = from->length; - } - - bytes_available = from_free ? from->length : from->bytesused; - bytes_available += steal ? steal->length : 0; + struct cx18_buffer *buf; - if (bytes_available < needed_bytes) { - spin_unlock_irqrestore(&s->qlock, flags); - return -ENOMEM; - } - if (from_free) { - u32 old_length = to->length; + if (q == &s->q_free) + return; - while (to->length - old_length < needed_bytes) { - if (list_empty(&from->list)) - from = steal; - if (from == steal) - rc++; /* keep track of 'stolen' buffers */ - cx18_queue_move_buf(s, from, to, 1, 0); - } - } else { - u32 old_bytesused = to->bytesused; - - while (to->bytesused - old_bytesused < needed_bytes) { - if (list_empty(&from->list)) - from = steal; - if (from == steal) - rc++; /* keep track of 'stolen' buffers */ - cx18_queue_move_buf(s, from, to, to_free, rc); - } + spin_lock_irqsave(&s->qlock, flags); + while (!list_empty(&q->list)) { + buf = list_entry(q->list.next, struct cx18_buffer, list); + list_move_tail(q->list.next, &s->q_free.list); + buf->bytesused = buf->readpos = buf->b_flags = 0; + atomic_inc(&s->q_free.buffers); } + cx18_queue_init(q); spin_unlock_irqrestore(&s->qlock, flags); - return rc; } void cx18_flush_queues(struct cx18_stream *s) { - cx18_queue_move(s, &s->q_io, NULL, &s->q_free, 0); - cx18_queue_move(s, &s->q_full, NULL, &s->q_free, 0); + cx18_queue_flush(s, &s->q_io); + cx18_queue_flush(s, &s->q_full); } int cx18_stream_alloc(struct cx18_stream *s) @@ -214,10 +143,10 @@ int cx18_stream_alloc(struct cx18_stream *s) s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024); - if (((char *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] - - (char *)cx->scb) > SCB_RESERVED_SIZE) { - unsigned bufsz = (((char *)cx->scb) + SCB_RESERVED_SIZE - - ((char *)cx->scb->cpu_mdl)); + if (((char __iomem *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] - + (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) { + unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE - + ((char __iomem *)cx->scb->cpu_mdl)); CX18_ERR("Too many buffers, cannot fit in SCB area\n"); CX18_ERR("Max buffers = %zd\n", diff --git a/linux/drivers/media/video/cx18/cx18-queue.h b/linux/drivers/media/video/cx18/cx18-queue.h index 91423b986..7f93bb13c 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.h +++ b/linux/drivers/media/video/cx18/cx18-queue.h @@ -46,7 +46,7 @@ void cx18_queue_init(struct cx18_queue *q); void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, struct cx18_queue *q); struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); -struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id, +struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, u32 bytesused); void cx18_flush_queues(struct cx18_stream *s); diff --git a/linux/drivers/media/video/cx18/cx18-scb.c b/linux/drivers/media/video/cx18/cx18-scb.c index 30bc803e3..f56d3772a 100644 --- a/linux/drivers/media/video/cx18/cx18-scb.c +++ b/linux/drivers/media/video/cx18/cx18-scb.c @@ -20,102 +20,103 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-scb.h" void cx18_init_scb(struct cx18 *cx) { - setup_page(SCB_OFFSET); - memset_io(cx->scb, 0, 0x10000); + cx18_setup_page(cx, SCB_OFFSET); + cx18_memset_io(cx, cx->scb, 0, 0x10000); - writel(IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq); - writel(IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack); - writel(IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq); - writel(IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack); - writel(IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq); - writel(IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack); - writel(IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq); - writel(IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack); + cx18_writel(cx, IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq); + cx18_writel(cx, IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack); + cx18_writel(cx, IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq); + cx18_writel(cx, IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack); + cx18_writel(cx, IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq); + cx18_writel(cx, IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack); + cx18_writel(cx, IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq); + cx18_writel(cx, IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack); - writel(IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq); - writel(IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack); - writel(IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq); - writel(IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack); - writel(IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq); - writel(IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack); - writel(IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq); - writel(IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack); + cx18_writel(cx, IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq); + cx18_writel(cx, IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack); + cx18_writel(cx, IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq); + cx18_writel(cx, IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack); + cx18_writel(cx, IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq); + cx18_writel(cx, IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack); + cx18_writel(cx, IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq); + cx18_writel(cx, IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack); - writel(IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq); - writel(IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack); - writel(IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq); - writel(IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack); - writel(IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq); - writel(IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack); - writel(IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq); - writel(IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack); + cx18_writel(cx, IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq); + cx18_writel(cx, IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack); + cx18_writel(cx, IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq); + cx18_writel(cx, IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack); + cx18_writel(cx, IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq); + cx18_writel(cx, IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack); + cx18_writel(cx, IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq); + cx18_writel(cx, IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack); - writel(IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq); - writel(IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack); - writel(IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq); - writel(IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack); - writel(IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq); - writel(IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack); - writel(IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq); - writel(IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack); + cx18_writel(cx, IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq); + cx18_writel(cx, IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack); + cx18_writel(cx, IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq); + cx18_writel(cx, IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack); + cx18_writel(cx, IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq); + cx18_writel(cx, IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack); + cx18_writel(cx, IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq); + cx18_writel(cx, IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack); - writel(IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq); - writel(IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack); - writel(IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq); - writel(IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack); - writel(IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq); - writel(IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack); - writel(IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq); - writel(IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack); + cx18_writel(cx, IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq); + cx18_writel(cx, IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack); + cx18_writel(cx, IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq); + cx18_writel(cx, IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack); + cx18_writel(cx, IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq); + cx18_writel(cx, IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack); + cx18_writel(cx, IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq); + cx18_writel(cx, IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack); - writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb), &cx->scb->apu2cpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb), &cx->scb->hpu2cpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb), &cx->scb->ppu2cpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb), &cx->scb->epu2cpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb), &cx->scb->cpu2apu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb), &cx->scb->hpu2apu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb), &cx->scb->ppu2apu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb), &cx->scb->epu2apu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb), &cx->scb->cpu2hpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb), &cx->scb->apu2hpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb), &cx->scb->ppu2hpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb), &cx->scb->epu2hpu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb), &cx->scb->cpu2ppu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb), &cx->scb->apu2ppu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb), &cx->scb->hpu2ppu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb), &cx->scb->epu2ppu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb), &cx->scb->cpu2epu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb), &cx->scb->apu2epu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb), &cx->scb->hpu2epu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb), &cx->scb->ppu2epu_mb_offset); - writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu_state), + cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state), &cx->scb->ipc_offset); - writel(1, &cx->scb->hpu_state); - writel(1, &cx->scb->epu_state); + cx18_writel(cx, 1, &cx->scb->hpu_state); + cx18_writel(cx, 1, &cx->scb->epu_state); } diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 911a97ce3..fc793e5f2 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/drivers/media/video/cx18/cx18-streams.c @@ -22,6 +22,7 @@ */ #include "cx18-driver.h" +#include "cx18-io.h" #include "cx18-fileops.h" #include "cx18-mailbox.h" #include "cx18-i2c.h" @@ -119,7 +120,7 @@ static void cx18_stream_init(struct cx18 *cx, int type) s->cx = cx; s->type = type; s->name = cx18_stream_info[type].name; - s->handle = 0xffffffff; + s->handle = CX18_INVALID_TASK_HANDLE; s->dma = cx18_stream_info[type].dma; s->buf_size = cx->stream_buf_size[type]; @@ -432,7 +433,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) default: return -EINVAL; } - s->buffers_stolen = 0; /* mute/unmute video */ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, @@ -470,7 +470,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) if (atomic_read(&cx->tot_capturing) == 0) { clear_bit(CX18_F_I_EOS, &cx->i_flags); - write_reg(7, CX18_DSP0_INTERRUPT_MASK); + cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK); } cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle, @@ -480,8 +480,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) list_for_each(p, &s->q_free.list) { struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list); - writel(buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr); - writel(s->buf_size, &cx->scb->cpu_mdl[buf->id].length); + cx18_writel(cx, buf->dma_handle, + &cx->scb->cpu_mdl[buf->id].paddr); + cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 1, buf->id, s->buf_size); @@ -489,7 +490,14 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) /* begin_capture */ if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { CX18_DEBUG_WARN("Error starting capture!\n"); + /* Ensure we're really not capturing before releasing MDLs */ + if (s->type == CX18_ENC_STREAM_TYPE_MPG) + cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); + else + cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); + cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); + /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */ return -EINVAL; } @@ -568,6 +576,9 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) #endif } + /* Tell the CX23418 it can't use our buffers anymore */ + cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); + if (s->type != CX18_ENC_STREAM_TYPE_TS) atomic_dec(&cx->ana_capturing); atomic_dec(&cx->tot_capturing); @@ -576,12 +587,12 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) clear_bit(CX18_F_S_STREAMING, &s->s_flags); cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); - s->handle = 0xffffffff; + s->handle = CX18_INVALID_TASK_HANDLE; if (atomic_read(&cx->tot_capturing) > 0) return 0; - write_reg(5, CX18_DSP0_INTERRUPT_MASK); + cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); wake_up(&s->waitq); return 0; @@ -595,8 +606,8 @@ u32 cx18_find_handle(struct cx18 *cx) for (i = 0; i < CX18_MAX_STREAMS; i++) { struct cx18_stream *s = &cx->streams[i]; - if (s->v4l2dev && s->handle) + if (s->v4l2dev && (s->handle != CX18_INVALID_TASK_HANDLE)) return s->handle; } - return 0; + return CX18_INVALID_TASK_HANDLE; } diff --git a/linux/drivers/media/video/cx18/cx23418.h b/linux/drivers/media/video/cx18/cx23418.h index e7ed05305..668f968d7 100644 --- a/linux/drivers/media/video/cx18/cx23418.h +++ b/linux/drivers/media/video/cx18/cx23418.h @@ -351,7 +351,7 @@ Descriptor Lists to the driver IN[0] - Task handle. Handle of the task to start ReturnCode - One of the ERR_DE_... */ -/* #define CX18_CPU_DE_ReleaseMDL (CPU_CMD_MASK_DE | 0x0006) */ +#define CX18_CPU_DE_RELEASE_MDL (CPU_CMD_MASK_DE | 0x0006) /* Description: This command signals the cpu that the dat buffer has been consumed and ready for re-use. diff --git a/linux/drivers/media/video/cx2341x.c b/linux/drivers/media/video/cx2341x.c index 835062f5b..f31f11e3f 100644 --- a/linux/drivers/media/video/cx2341x.c +++ b/linux/drivers/media/video/cx2341x.c @@ -509,7 +509,10 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, /* this setting is read-only for the cx2341x since the V4L2_CID_MPEG_STREAM_TYPE really determines the MPEG-1/2 setting */ - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); if (err == 0) qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; return err; diff --git a/linux/drivers/media/video/cx23885/Kconfig b/linux/drivers/media/video/cx23885/Kconfig index e60bd31b5..8c1b7fa47 100644 --- a/linux/drivers/media/video/cx23885/Kconfig +++ b/linux/drivers/media/video/cx23885/Kconfig @@ -15,6 +15,7 @@ config VIDEO_CX23885 select DVB_S5H1409 if !DVB_FE_CUSTOMISE select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE diff --git a/linux/drivers/media/video/cx23885/cx23885-417.c b/linux/drivers/media/video/cx23885/cx23885-417.c index 106b594ed..25349f6b9 100644 --- a/linux/drivers/media/video/cx23885/cx23885-417.c +++ b/linux/drivers/media/video/cx23885/cx23885-417.c @@ -4,7 +4,7 @@ * * (c) 2004 Jelle Foks <jelle@foks.8m.com> * (c) 2004 Gerd Knorr <kraxel@bytesex.org> - * (c) 2008 Steven Toth <stoth@hauppauge.com> + * (c) 2008 Steven Toth <stoth@linuxtv.org> * - CX23885/7/8 support * * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/), @@ -632,7 +632,7 @@ int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value) /* ------------------------------------------------------------------ */ /* MPEG encoder API */ -char *cmd_to_str(int cmd) +static char *cmd_to_str(int cmd) { switch (cmd) { case CX2341X_ENC_PING_FW: diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index ee01ff8bf..be9a6d8ec 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.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 @@ -27,6 +27,7 @@ #include "compat.h" #include "cx23885.h" +#include "tuner-xc2028.h" /* ------------------------------------------------------------------ */ /* board config info */ @@ -149,6 +150,15 @@ struct cx23885_board cx23885_boards[] = { .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP] = { + .name = "DViCO FusionHDTV DVB-T Dual Express", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H] = { + .name = "Leadtek Winfast PxDVR3200 H", + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -220,6 +230,14 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x18ac, .subdevice = 0xd618, .card = CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP, + },{ + .subvendor = 0x18ac, + .subdevice = 0xdb78, + .card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP, + }, { + .subvendor = 0x107d, + .subdevice = 0x6681, + .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -320,15 +338,15 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) dev->name, tv.model); } -/* Tuner callback function for cx23885 boards. Currently only needed - * for HVR1500Q, which has an xc5000 tuner. - */ int cx23885_tuner_callback(void *priv, int command, int arg) { - struct cx23885_i2c *bus = priv; - struct cx23885_dev *dev = bus->dev; + struct cx23885_tsport *port = priv; + struct cx23885_dev *dev = port->dev; u32 bitmask = 0; + if (command == XC2028_RESET_CLK) + return 0; + if (command != 0) { printk(KERN_ERR "%s(): Unknown command 0x%x.\n", __func__, command); @@ -336,21 +354,21 @@ int cx23885_tuner_callback(void *priv, int command, int arg) } switch(dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1400: + case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: - /* Tuner Reset Command from xc5000 */ - if (command == 0) - bitmask = 0x04; + case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + /* Tuner Reset Command */ + bitmask = 0x04; break; case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: - if (command == 0) { - - /* Two identical tuners on two different i2c buses, - * we need to reset the correct gpio. */ - if (bus->nr == 0) - bitmask = 0x01; - else if (bus->nr == 1) - bitmask = 0x04; - } + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: + /* Two identical tuners on two different i2c buses, + * we need to reset the correct gpio. */ + if (port->nr == 0) + bitmask = 0x01; + else if (port->nr == 1) + bitmask = 0x04; break; } @@ -466,6 +484,32 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) mdelay(20); cx_set(GP0_IO, 0x000f000f); break; + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: + /* GPIO-0 portb xc3028 reset */ + /* GPIO-1 portb zl10353 reset */ + /* GPIO-2 portc xc3028 reset */ + /* GPIO-3 portc zl10353 reset */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x000f0000); + mdelay(20); + cx_clear(GP0_IO, 0x0000000f); + mdelay(20); + cx_set(GP0_IO, 0x000f000f); + break; + case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + /* GPIO-2 xc3028 tuner reset */ + + /* The following GPIO's are on the internal AVCore (cx25840) */ + /* GPIO-? zl10353 demod reset */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x00040000); + mdelay(20); + cx_clear(GP0_IO, 0x00000004); + mdelay(20); + cx_set(GP0_IO, 0x00040004); + break; } } @@ -480,6 +524,9 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1400: /* FIXME: Implement me */ break; + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: + request_module("ir-kbd-i2c"); + break; } return 0; @@ -517,6 +564,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) switch (dev->board) { case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; @@ -549,6 +597,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1200: case CX23885_BOARD_HAUPPAUGE_HVR1700: case CX23885_BOARD_HAUPPAUGE_HVR1400: + case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -562,6 +611,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1800lp: case CX23885_BOARD_HAUPPAUGE_HVR1700: + case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: request_module("cx25840"); break; } diff --git a/linux/drivers/media/video/cx23885/cx23885-core.c b/linux/drivers/media/video/cx23885/cx23885-core.c index 0c34db9f7..c050cd2ce 100644 --- a/linux/drivers/media/video/cx23885/cx23885-core.c +++ b/linux/drivers/media/video/cx23885/cx23885-core.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.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 @@ -34,7 +34,7 @@ #include "cx23885.h" MODULE_DESCRIPTION("Driver for cx23885 based TV cards"); -MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>"); +MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); MODULE_LICENSE("GPL"); static unsigned int debug; diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 3c01ef2bf..c84688aba 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.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 @@ -43,6 +43,7 @@ #include "tuner-simple.h" #include "dib7000p.h" #include "dibx000_common.h" +#include "zl10353.h" static unsigned int debug; @@ -189,13 +190,13 @@ static struct s5h1411_config dvico_s5h1411_config = { static struct xc5000_config hauppauge_hvr1500q_tunerconfig = { .i2c_address = 0x61, .if_khz = 5380, - .tuner_callback = cx23885_tuner_callback + .tuner_callback = cx23885_tuner_callback, }; static struct xc5000_config dvico_xc5000_tunerconfig = { .i2c_address = 0x64, .if_khz = 5380, - .tuner_callback = cx23885_tuner_callback + .tuner_callback = cx23885_tuner_callback, }; static struct tda829x_config tda829x_no_probe = { @@ -304,35 +305,11 @@ static struct dib7000p_config hauppauge_hvr1400_dib7000_config = { .output_mode = OUTMODE_MPEG2_SERIAL, }; -static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg) -{ - struct cx23885_tsport *port = ptr; - struct cx23885_dev *dev = port->dev; - - switch (command) { - case XC2028_TUNER_RESET: - /* Send the tuner in then out of reset */ - /* GPIO-2 xc3028 tuner */ - dprintk(1, "%s: XC2028_TUNER_RESET %d\n", __func__, arg); - - cx_set(GP0_IO, 0x00040000); - cx_clear(GP0_IO, 0x00000004); - msleep(5); - - cx_set(GP0_IO, 0x00040004); - msleep(5); - break; - case XC2028_RESET_CLK: - dprintk(1, "%s: XC2028_RESET_CLK %d\n", __func__, arg); - break; - default: - dprintk(1, "%s: unknown command %d, arg %d\n", __func__, - command, arg); - return -EINVAL; - } - - return 0; -} +static struct zl10353_config dvico_fusionhdtv_xc3028 = { + .demod_address = 0x0f, + .if2 = 45600, + .no_tuner = 1, +}; static int dvb_register(struct cx23885_tsport *port) { @@ -414,8 +391,8 @@ static int dvb_register(struct cx23885_tsport *port) &dev->i2c_bus[0].i2c_adap); if (port->dvb.frontend != NULL) dvb_attach(xc5000_attach, port->dvb.frontend, - &i2c_bus->i2c_adap, - &hauppauge_hvr1500q_tunerconfig, i2c_bus); + &i2c_bus->i2c_adap, + &hauppauge_hvr1500q_tunerconfig); break; case CX23885_BOARD_HAUPPAUGE_HVR1500: i2c_bus = &dev->i2c_bus[1]; @@ -427,10 +404,11 @@ static int dvb_register(struct cx23885_tsport *port) struct xc2028_config cfg = { .i2c_adap = &i2c_bus->i2c_adap, .i2c_addr = 0x61, - .callback = cx23885_hvr1500_xc3028_callback, + .video_dev = port, + .callback = cx23885_tuner_callback, }; static struct xc2028_ctrl ctl = { - .fname = "xc3028-v27.fw", + .fname = XC2028_DEFAULT_FIRMWARE, .max_len = 64, .scode_table = XC3028_FE_OREN538, }; @@ -466,10 +444,11 @@ static int dvb_register(struct cx23885_tsport *port) struct xc2028_config cfg = { .i2c_adap = &dev->i2c_bus[1].i2c_adap, .i2c_addr = 0x64, - .callback = cx23885_hvr1500_xc3028_callback, + .video_dev = port, + .callback = cx23885_tuner_callback, }; static struct xc2028_ctrl ctl = { - .fname = "xc3028L-v36.fw", + .fname = XC3028L_DEFAULT_FIRMWARE, .max_len = 64, .demod = 5000, .d2633 = 1 @@ -493,8 +472,61 @@ static int dvb_register(struct cx23885_tsport *port) &i2c_bus->i2c_adap); if (port->dvb.frontend != NULL) dvb_attach(xc5000_attach, port->dvb.frontend, - &i2c_bus->i2c_adap, - &dvico_xc5000_tunerconfig, i2c_bus); + &i2c_bus->i2c_adap, + &dvico_xc5000_tunerconfig); + break; + case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: { + i2c_bus = &dev->i2c_bus[port->nr - 1]; + + port->dvb.frontend = dvb_attach(zl10353_attach, + &dvico_fusionhdtv_xc3028, + &i2c_bus->i2c_adap); + if (port->dvb.frontend != NULL) { + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &i2c_bus->i2c_adap, + .i2c_addr = 0x61, + .video_dev = port, + .callback = cx23885_tuner_callback, + }; + static struct xc2028_ctrl ctl = { + .fname = XC2028_DEFAULT_FIRMWARE, + .max_len = 64, + .demod = XC3028_FE_ZARLINK456, + }; + + fe = dvb_attach(xc2028_attach, port->dvb.frontend, + &cfg); + if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) + fe->ops.tuner_ops.set_config(fe, &ctl); + } + break; + } + case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: + i2c_bus = &dev->i2c_bus[0]; + + port->dvb.frontend = dvb_attach(zl10353_attach, + &dvico_fusionhdtv_xc3028, + &i2c_bus->i2c_adap); + if (port->dvb.frontend != NULL) { + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_bus[1].i2c_adap, + .i2c_addr = 0x61, + .video_dev = port, + .callback = cx23885_tuner_callback, + }; + static struct xc2028_ctrl ctl = { + .fname = XC2028_DEFAULT_FIRMWARE, + .max_len = 64, + .demod = XC3028_FE_ZARLINK456, + }; + + fe = dvb_attach(xc2028_attach, port->dvb.frontend, + &cfg); + if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) + fe->ops.tuner_ops.set_config(fe, &ctl); + } break; default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", diff --git a/linux/drivers/media/video/cx23885/cx23885-i2c.c b/linux/drivers/media/video/cx23885/cx23885-i2c.c index f0ee47256..6f3a43607 100644 --- a/linux/drivers/media/video/cx23885/cx23885-i2c.c +++ b/linux/drivers/media/video/cx23885/cx23885-i2c.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.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 diff --git a/linux/drivers/media/video/cx23885/cx23885-reg.h b/linux/drivers/media/video/cx23885/cx23885-reg.h index bdd11bc51..20b68a236 100644 --- a/linux/drivers/media/video/cx23885/cx23885-reg.h +++ b/linux/drivers/media/video/cx23885/cx23885-reg.h @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.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 diff --git a/linux/drivers/media/video/cx23885/cx23885-vbi.c b/linux/drivers/media/video/cx23885/cx23885-vbi.c index a6cf4146d..4b65552b3 100644 --- a/linux/drivers/media/video/cx23885/cx23885-vbi.c +++ b/linux/drivers/media/video/cx23885/cx23885-vbi.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2007 Steven Toth <stoth@linuxtv.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 @@ -86,7 +86,9 @@ static int cx23885_start_vbi_dma(struct cx23885_dev *dev, return 0; } -int cx23885_stop_vbi_dma(struct cx23885_dev *dev) +#if 0 +/* not (yet) used */ +static int cx23885_stop_vbi_dma(struct cx23885_dev *dev) { /* stop dma */ cx_clear(VID_A_DMA_CTL, 0x00000022); @@ -96,8 +98,9 @@ int cx23885_stop_vbi_dma(struct cx23885_dev *dev) cx_clear(VID_A_INT_MSK, 0x00000022); return 0; } +#endif -int cx23885_restart_vbi_queue(struct cx23885_dev *dev, +static int cx23885_restart_vbi_queue(struct cx23885_dev *dev, struct cx23885_dmaqueue *q) { struct cx23885_buffer *buf; diff --git a/linux/drivers/media/video/cx23885/cx23885-video.c b/linux/drivers/media/video/cx23885/cx23885-video.c index 98a7dc1c0..70333ee2b 100644 --- a/linux/drivers/media/video/cx23885/cx23885-video.c +++ b/linux/drivers/media/video/cx23885/cx23885-video.c @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2007 Steven Toth <stoth@linuxtv.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 @@ -42,7 +42,7 @@ #endif MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); -MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>"); +MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); MODULE_LICENSE("GPL"); /* ------------------------------------------------------------------ */ @@ -276,7 +276,7 @@ static struct cx23885_ctrl cx23885_ctls[] = { }; static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls); -const u32 cx23885_user_ctrls[] = { +static const u32 cx23885_user_ctrls[] = { V4L2_CID_USER_CLASS, V4L2_CID_BRIGHTNESS, V4L2_CID_CONTRAST, @@ -289,14 +289,13 @@ const u32 cx23885_user_ctrls[] = { V4L2_CID_AUDIO_MUTE, 0 }; -EXPORT_SYMBOL(cx23885_user_ctrls); static const u32 *ctrl_classes[] = { cx23885_user_ctrls, NULL }; -void cx23885_video_wakeup(struct cx23885_dev *dev, +static void cx23885_video_wakeup(struct cx23885_dev *dev, struct cx23885_dmaqueue *q, u32 count) { struct cx23885_buffer *buf; @@ -331,7 +330,7 @@ void cx23885_video_wakeup(struct cx23885_dev *dev, __func__, bc); } -int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) +static int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) { dprintk(1, "%s(norm = 0x%08x) name: [%s]\n", __func__, @@ -349,7 +348,7 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) return 0; } -struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, +static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, struct pci_dev *pci, struct video_device *template, char *type) @@ -369,7 +368,7 @@ struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, return vfd; } -int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl) +static int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl) { int i; @@ -386,7 +385,6 @@ int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl) *qctrl = cx23885_ctls[i].v; return 0; } -EXPORT_SYMBOL(cx23885_ctrl_query); /* ------------------------------------------------------------------- */ /* resource management */ @@ -437,7 +435,7 @@ static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh, mutex_unlock(&dev->lock); } -int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) +static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) { struct v4l2_routing route; memset(&route, 0, sizeof(route)); @@ -462,10 +460,9 @@ int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) return 0; } -EXPORT_SYMBOL(cx23885_video_mux); /* ------------------------------------------------------------------ */ -int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width, +static int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width, unsigned int height, enum v4l2_field field) { dprintk(1, "%s()\n", __func__); @@ -967,15 +964,14 @@ static int video_mmap(struct file *file, struct vm_area_struct *vma) /* ------------------------------------------------------------------ */ /* VIDEO CTRL IOCTLS */ -int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl) +static int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl) { dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__); cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_CTRL, ctl); return 0; } -EXPORT_SYMBOL(cx23885_get_control); -int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl) +static int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl) { dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)" " (disabled - no action)\n", __func__); @@ -984,7 +980,6 @@ int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl) #endif return 0; } -EXPORT_SYMBOL(cx23885_set_control); static void init_controls(struct cx23885_dev *dev) { @@ -1235,7 +1230,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms) return 0; } -int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) +static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) { static const char *iname[] = { [CX23885_VMUX_COMPOSITE1] = "Composite1", @@ -1268,7 +1263,6 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) i->std = CX23885_NORMS; return 0; } -EXPORT_SYMBOL(cx23885_enum_input); static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) @@ -1383,7 +1377,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, return 0; } -int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f) +static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f) { if (unlikely(UNSET == dev->tuner_type)) return -EINVAL; @@ -1402,7 +1396,6 @@ int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f) return 0; } -EXPORT_SYMBOL(cx23885_set_freq); static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index 9a0b4bf3a..49e3eaec7 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -1,7 +1,7 @@ /* * Driver for the Conexant CX23885 PCIe bridge * - * Copyright (c) 2006 Steven Toth <stoth@hauppauge.com> + * Copyright (c) 2006 Steven Toth <stoth@linuxtv.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 @@ -65,6 +65,8 @@ #define CX23885_BOARD_HAUPPAUGE_HVR1700 8 #define CX23885_BOARD_HAUPPAUGE_HVR1400 9 #define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10 +#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11 +#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c index 7b48e0285..cb4a9c64e 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.c +++ b/linux/drivers/media/video/cx25840/cx25840-core.c @@ -13,7 +13,7 @@ * NTSC sliced VBI support by Christopher Neufeld <television@cneufeld.ca> * with additional fixes by Hans Verkuil <hverkuil@xs4all.nl>. * - * CX23885 support by Steven Toth <stoth@hauppauge.com>. + * CX23885 support by Steven Toth <stoth@linuxtv.org>. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 0cd510814..5f44368f6 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -1092,6 +1092,7 @@ static int mpeg_open(struct inode *inode, struct file *file) err = drv->request_acquire(drv); if(err != 0) { dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err); + unlock_kernel(); return err; } } diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index fc5807aeb..f5fef4983 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -820,13 +820,9 @@ static int dvb_register(struct cx8802_dev *dev) &pinnacle_pctv_hd_800i_config, &core->i2c_adap); if (dev->dvb.frontend != NULL) { - /* tuner_config.video_dev must point to - * i2c_adap.algo_data - */ if (!dvb_attach(xc5000_attach, dev->dvb.frontend, &core->i2c_adap, - &pinnacle_pctv_hd_800i_tuner_config, - core->i2c_adap.algo_data)) + &pinnacle_pctv_hd_800i_tuner_config)) goto frontend_detach; } break; @@ -842,7 +838,7 @@ static int dvb_register(struct cx8802_dev *dev) .callback = cx88_pci_nano_callback, }; static struct xc2028_ctrl ctl = { - .fname = "xc3028-v27.fw", + .fname = XC2028_DEFAULT_FIRMWARE, .max_len = 64, .scode_table = XC3028_FE_OREN538, }; @@ -881,13 +877,9 @@ static int dvb_register(struct cx8802_dev *dev) &dvico_fusionhdtv7_config, &core->i2c_adap); if (dev->dvb.frontend != NULL) { - /* tuner_config.video_dev must point to - * i2c_adap.algo_data - */ if (!dvb_attach(xc5000_attach, dev->dvb.frontend, &core->i2c_adap, - &dvico_fusionhdtv7_tuner_config, - core->i2c_adap.algo_data)) + &dvico_fusionhdtv7_tuner_config)) goto frontend_detach; } break; diff --git a/linux/drivers/media/video/dabusb.c b/linux/drivers/media/video/dabusb.c index f23e6b84d..4acba6e92 100644 --- a/linux/drivers/media/video/dabusb.c +++ b/linux/drivers/media/video/dabusb.c @@ -451,6 +451,7 @@ static int dabusb_fpga_download (pdabusb_t s, const char *fname) ret = request_firmware(&fw, "dabusb/bitstream.bin", &s->usbdev->dev); if (ret) { err("Failed to load \"dabusb/bitstream.bin\": %d\n", ret); + kfree(b); return ret; } diff --git a/linux/drivers/media/video/dpc7146.c b/linux/drivers/media/video/dpc7146.c deleted file mode 100644 index 4bfb3076b..000000000 --- a/linux/drivers/media/video/dpc7146.c +++ /dev/null @@ -1,409 +0,0 @@ -/* - dpc7146.c - v4l2 driver for the dpc7146 demonstration board - - Copyright (C) 2000-2003 Michael Hunold <michael@mihu.de> - - 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. -*/ - -#define DEBUG_VARIABLE debug - -#include <media/saa7146_vv.h> -#include <linux/video_decoder.h> /* for saa7111a */ -#include "compat.h" - -#define I2C_SAA7111A 0x24 - -/* All unused bytes are reserverd. */ -#define SAA711X_CHIP_VERSION 0x00 -#define SAA711X_ANALOG_INPUT_CONTROL_1 0x02 -#define SAA711X_ANALOG_INPUT_CONTROL_2 0x03 -#define SAA711X_ANALOG_INPUT_CONTROL_3 0x04 -#define SAA711X_ANALOG_INPUT_CONTROL_4 0x05 -#define SAA711X_HORIZONTAL_SYNC_START 0x06 -#define SAA711X_HORIZONTAL_SYNC_STOP 0x07 -#define SAA711X_SYNC_CONTROL 0x08 -#define SAA711X_LUMINANCE_CONTROL 0x09 -#define SAA711X_LUMINANCE_BRIGHTNESS 0x0A -#define SAA711X_LUMINANCE_CONTRAST 0x0B -#define SAA711X_CHROMA_SATURATION 0x0C -#define SAA711X_CHROMA_HUE_CONTROL 0x0D -#define SAA711X_CHROMA_CONTROL 0x0E -#define SAA711X_FORMAT_DELAY_CONTROL 0x10 -#define SAA711X_OUTPUT_CONTROL_1 0x11 -#define SAA711X_OUTPUT_CONTROL_2 0x12 -#define SAA711X_OUTPUT_CONTROL_3 0x13 -#define SAA711X_V_GATE_1_START 0x15 -#define SAA711X_V_GATE_1_STOP 0x16 -#define SAA711X_V_GATE_1_MSB 0x17 -#define SAA711X_TEXT_SLICER_STATUS 0x1A -#define SAA711X_DECODED_BYTES_OF_TS_1 0x1B -#define SAA711X_DECODED_BYTES_OF_TS_2 0x1C -#define SAA711X_STATUS_BYTE 0x1F - -#define DPC_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "debug verbosity"); - -static int dpc_num; - -#define DPC_INPUTS 2 -static struct v4l2_input dpc_inputs[DPC_INPUTS] = { - { 0, "Port A", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 }, - { 1, "Port B", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0 }, -}; - -#define DPC_AUDIOS 0 - -static struct saa7146_extension_ioctls ioctls[] = { - { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_S_STD, SAA7146_AFTER }, - { 0, 0 } -}; - -struct dpc -{ - struct video_device *video_dev; - struct video_device *vbi_dev; - - struct i2c_adapter i2c_adapter; - struct i2c_client *saa7111a; - - int cur_input; /* current input */ -}; - -static int dpc_check_clients(struct device *dev, void *data) -{ - struct dpc* dpc = data; - struct i2c_client *client = i2c_verify_client(dev); - - if( !client ) - return 0; - - if( I2C_SAA7111A == client->addr ) - dpc->saa7111a = client; - - return 0; -} - -/* fixme: add vbi stuff here */ -static int dpc_probe(struct saa7146_dev* dev) -{ - struct dpc* dpc = NULL; - - dpc = kzalloc(sizeof(struct dpc), GFP_KERNEL); - if( NULL == dpc ) { - printk("dpc_v4l2.o: dpc_probe: not enough kernel memory.\n"); - return -ENOMEM; - } - - /* FIXME: enable i2c-port pins, video-port-pins - video port pins should be enabled here ?! */ - saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); - - dpc->i2c_adapter = (struct i2c_adapter) { - .class = I2C_CLASS_TV_ANALOG, - .name = "dpc7146", - }; - saa7146_i2c_adapter_prepare(dev, &dpc->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); - if(i2c_add_adapter(&dpc->i2c_adapter) < 0) { - DEB_S(("cannot register i2c-device. skipping.\n")); - kfree(dpc); - return -EFAULT; - } - - /* loop through all i2c-devices on the bus and look who is there */ - device_for_each_child(&dpc->i2c_adapter.dev, dpc, dpc_check_clients); - - /* check if all devices are present */ - if (!dpc->saa7111a) { - DEB_D(("dpc_v4l2.o: dpc_attach failed for this device.\n")); - i2c_del_adapter(&dpc->i2c_adapter); - kfree(dpc); - return -ENODEV; - } - - /* all devices are present, probe was successful */ - DEB_D(("dpc_v4l2.o: dpc_probe succeeded for this device.\n")); - - /* we store the pointer in our private data field */ - dev->ext_priv = dpc; - - return 0; -} - -/* bring hardware to a sane state. this has to be done, just in case someone - wants to capture from this device before it has been properly initialized. - the capture engine would badly fail, because no valid signal arrives on the - saa7146, thus leading to timeouts and stuff. */ -static int dpc_init_done(struct saa7146_dev* dev) -{ - struct dpc* dpc = (struct dpc*)dev->ext_priv; - - DEB_D(("dpc_v4l2.o: dpc_init_done called.\n")); - - /* initialize the helper ics to useful values */ - i2c_smbus_write_byte_data(dpc->saa7111a, 0x00, 0x11); - - i2c_smbus_write_byte_data(dpc->saa7111a, 0x02, 0xc0); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x03, 0x30); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x04, 0x00); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x05, 0x00); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x06, 0xde); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x07, 0xad); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x08, 0xa8); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x09, 0x00); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x0a, 0x80); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x0b, 0x47); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x0c, 0x40); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x0d, 0x00); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x0e, 0x03); - - i2c_smbus_write_byte_data(dpc->saa7111a, 0x10, 0xd0); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x11, 0x1c); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x12, 0xc1); - i2c_smbus_write_byte_data(dpc->saa7111a, 0x13, 0x30); - - i2c_smbus_write_byte_data(dpc->saa7111a, 0x1f, 0x81); - - return 0; -} - -static struct saa7146_ext_vv vv_data; - -/* this function only gets called when the probing was successful */ -static int dpc_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) -{ - struct dpc* dpc = (struct dpc*)dev->ext_priv; - - DEB_D(("dpc_v4l2.o: dpc_attach called.\n")); - - /* checking for i2c-devices can be omitted here, because we - already did this in "dpc_vl42_probe" */ - - saa7146_vv_init(dev,&vv_data); - if( 0 != saa7146_register_device(&dpc->video_dev, dev, "dpc", VFL_TYPE_GRABBER)) { - ERR(("cannot register capture v4l2 device. skipping.\n")); - return -1; - } - - /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ - if( 0 != DPC_BOARD_CAN_DO_VBI(dev)) { - if( 0 != saa7146_register_device(&dpc->vbi_dev, dev, "dpc", VFL_TYPE_VBI)) { - ERR(("cannot register vbi v4l2 device. skipping.\n")); - } - } - - i2c_use_client(dpc->saa7111a); - - printk("dpc: found 'dpc7146 demonstration board'-%d.\n",dpc_num); - dpc_num++; - - /* the rest */ - dpc->cur_input = 0; - dpc_init_done(dev); - - return 0; -} - -static int dpc_detach(struct saa7146_dev* dev) -{ - struct dpc* dpc = (struct dpc*)dev->ext_priv; - - DEB_EE(("dev:%p\n",dev)); - - i2c_release_client(dpc->saa7111a); - - saa7146_unregister_device(&dpc->video_dev,dev); - if( 0 != DPC_BOARD_CAN_DO_VBI(dev)) { - saa7146_unregister_device(&dpc->vbi_dev,dev); - } - saa7146_vv_release(dev); - - dpc_num--; - - i2c_del_adapter(&dpc->i2c_adapter); - kfree(dpc); - return 0; -} - -#ifdef axa -int dpc_vbi_bypass(struct saa7146_dev* dev) -{ - struct dpc* dpc = (struct dpc*)dev->ext_priv; - - int i = 1; - - /* switch bypass in saa7111a */ - if ( 0 != dpc->saa7111a->driver->command(dpc->saa7111a,SAA711X_VBI_BYPASS, &i)) { - printk("dpc_v4l2.o: VBI_BYPASS: could not address saa7111a.\n"); - return -1; - } - - return 0; -} -#endif - -static int dpc_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) -{ - struct saa7146_dev *dev = fh->dev; - struct dpc* dpc = (struct dpc*)dev->ext_priv; -/* - struct saa7146_vv *vv = dev->vv_data; -*/ - switch(cmd) - { - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; - DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index)); - - if( i->index < 0 || i->index >= DPC_INPUTS) { - return -EINVAL; - } - - memcpy(i, &dpc_inputs[i->index], sizeof(struct v4l2_input)); - - DEB_D(("dpc_v4l2.o: v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n",i->index)); - return 0; - } - case VIDIOC_G_INPUT: - { - int *input = (int *)arg; - *input = dpc->cur_input; - - DEB_D(("dpc_v4l2.o: VIDIOC_G_INPUT: %d\n",*input)); - return 0; - } - case VIDIOC_S_INPUT: - { - int input = *(int *)arg; - - if (input < 0 || input >= DPC_INPUTS) { - return -EINVAL; - } - - dpc->cur_input = input; - - /* fixme: switch input here, switch audio, too! */ -// saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync); - printk("dpc_v4l2.o: VIDIOC_S_INPUT: fixme switch input.\n"); - - return 0; - } - default: -/* - DEB_D(("dpc_v4l2.o: v4l2_ioctl does not handle this ioctl.\n")); -*/ - return -ENOIOCTLCMD; - } - return 0; -} - -static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) -{ - return 0; -} - -static struct saa7146_standard standard[] = { - { - .name = "PAL", .id = V4L2_STD_PAL, - .v_offset = 0x17, .v_field = 288, - .h_offset = 0x14, .h_pixels = 680, - .v_max_out = 576, .h_max_out = 768, - }, { - .name = "NTSC", .id = V4L2_STD_NTSC, - .v_offset = 0x16, .v_field = 240, - .h_offset = 0x06, .h_pixels = 708, - .v_max_out = 480, .h_max_out = 640, - }, { - .name = "SECAM", .id = V4L2_STD_SECAM, - .v_offset = 0x14, .v_field = 288, - .h_offset = 0x14, .h_pixels = 720, - .v_max_out = 576, .h_max_out = 768, - } -}; - -static struct saa7146_extension extension; - -static struct saa7146_pci_extension_data dpc = { - .ext_priv = "Multimedia eXtension Board", - .ext = &extension, -}; - -static struct pci_device_id pci_tbl[] = { - { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x0000, - .subdevice = 0x0000, - .driver_data = (unsigned long)&dpc, - }, { - .vendor = 0, - } -}; - -MODULE_DEVICE_TABLE(pci, pci_tbl); - -static struct saa7146_ext_vv vv_data = { - .inputs = DPC_INPUTS, - .capabilities = V4L2_CAP_VBI_CAPTURE, - .stds = &standard[0], - .num_stds = sizeof(standard)/sizeof(struct saa7146_standard), - .std_callback = &std_callback, - .ioctls = &ioctls[0], - .ioctl = dpc_ioctl, -}; - -static struct saa7146_extension extension = { - .name = "dpc7146 demonstration board", - .flags = SAA7146_USE_I2C_IRQ, - - .pci_tbl = &pci_tbl[0], - .module = THIS_MODULE, - - .probe = dpc_probe, - .attach = dpc_attach, - .detach = dpc_detach, - - .irq_mask = 0, - .irq_func = NULL, -}; - -static int __init dpc_init_module(void) -{ - if( 0 != saa7146_register_extension(&extension)) { - DEB_S(("failed to register extension.\n")); - return -ENODEV; - } - - return 0; -} - -static void __exit dpc_cleanup_module(void) -{ - saa7146_unregister_extension(&extension); -} - -module_init(dpc_init_module); -module_exit(dpc_cleanup_module); - -MODULE_DESCRIPTION("video4linux-2 driver for the 'dpc7146 demonstration board'"); -MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); -MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index 904973305..14ef4da24 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -171,8 +171,14 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) memset(dev->adev->transfer_buffer[i], 0x80, sb_size); urb = usb_alloc_urb(EM28XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); - if (!urb) + if (!urb) { + em28xx_errdev("usb_alloc_urb failed!\n"); + for (j = 0; j < i; j++) { + usb_free_urb(dev->adev->urb[j]); + kfree(dev->adev->transfer_buffer[j]); + } return -ENOMEM; + } urb->dev = dev->udev; urb->context = dev; diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index eae5ebd0e..5abdefdf0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -94,28 +94,6 @@ struct em28xx_board em28xx_boards[] = { .amux = 0, } }, }, - [EM2800_BOARD_KWORLD_USB2800] = { - .name = "Kworld USB2800", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .is_em2800 = 1, - .vchannels = 3, - .tuner_type = TUNER_PHILIPS_FCV1236D, - .tda9887_conf = TDA9887_PRESENT, - .decoder = EM28XX_SAA7113, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = 0, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = 1, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = 1, - } }, - }, [EM2820_BOARD_KWORLD_PVRTV2800RF] = { .name = "Kworld PVR TV 2800 RF", .is_em2800 = 0, @@ -1123,7 +1101,7 @@ struct usb_device_id em28xx_id_table [] = { { USB_DEVICE(0xeb1a, 0x2820), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2821), - .driver_info = EM2820_BOARD_UNKNOWN }, + .driver_info = EM2820_BOARD_PROLINK_PLAYTV_USB2 }, { USB_DEVICE(0xeb1a, 0x2860), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2861), @@ -1556,9 +1534,12 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) /* djh - Not sure which demod we need here */ ctl->demod = XC3028_FE_DEFAULT; break; + case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: + ctl->demod = XC3028_FE_DEFAULT; + ctl->fname = XC3028L_DEFAULT_FIRMWARE; + break; case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: - case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600: /* FIXME: Better to specify the needed IF */ ctl->demod = XC3028_FE_DEFAULT; break; diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 5b532ccec..79c11b1d0 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -275,7 +275,7 @@ static int attach_xc3028(u8 addr, struct em28xx *dev) /* ------------------------------------------------------------------ */ -int register_dvb(struct em28xx_dvb *dvb, +static int register_dvb(struct em28xx_dvb *dvb, struct module *module, struct em28xx *dev, struct device *device) diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index 97853384c..3bab56b99 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -143,10 +143,11 @@ static int em2800_i2c_check_for_device(struct em28xx *dev, unsigned char addr) } for (write_timeout = EM2800_I2C_WRITE_TIMEOUT; write_timeout > 0; write_timeout -= 5) { - unsigned msg = dev->em28xx_read_reg(dev, 0x5); - if (msg == 0x94) + unsigned reg = dev->em28xx_read_reg(dev, 0x5); + + if (reg == 0x94) return -ENODEV; - else if (msg == 0x84) + else if (reg == 0x84) return 0; msleep(5); } @@ -335,8 +336,11 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) /* Check if board has eeprom */ err = i2c_master_recv(&dev->i2c_client, &buf, 0); - if (err < 0) - return -1; + if (err < 0) { + em28xx_errdev("%s: i2c_master_recv failed! err [%d]\n", + __func__, err); + return err; + } buf = 0; @@ -344,7 +348,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) if (err != 1) { printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", dev->name, err); - return -1; + return err; } while (size > 0) { if (size > 16) @@ -357,7 +361,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n", dev->name, err); - return -1; + return err; } size -= block; p += block; @@ -585,18 +589,31 @@ void em28xx_i2c_call_clients(struct em28xx *dev, unsigned int cmd, void *arg) */ int em28xx_i2c_register(struct em28xx *dev) { + int retval; + BUG_ON(!dev->em28xx_write_regs || !dev->em28xx_read_reg); BUG_ON(!dev->em28xx_write_regs_req || !dev->em28xx_read_reg_req); dev->i2c_adap = em28xx_adap_template; dev->i2c_adap.dev.parent = &dev->udev->dev; strcpy(dev->i2c_adap.name, dev->name); dev->i2c_adap.algo_data = dev; - i2c_add_adapter(&dev->i2c_adap); + + retval = i2c_add_adapter(&dev->i2c_adap); + if (retval < 0) { + em28xx_errdev("%s: i2c_add_adapter failed! retval [%d]\n", + __func__, retval); + return retval; + } dev->i2c_client = em28xx_client_template; dev->i2c_client.adapter = &dev->i2c_adap; - em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata)); + retval = em28xx_i2c_eeprom(dev, dev->eedata, sizeof(dev->eedata)); + if (retval < 0) { + em28xx_errdev("%s: em28xx_i2_eeprom failed! retval [%d]\n", + __func__, retval); + return retval; + } if (i2c_scan) em28xx_do_i2c_scan(dev); diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index c9a2d4808..e94b50f00 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -517,10 +517,17 @@ static struct videobuf_queue_ops em28xx_video_qops = { */ static int em28xx_config(struct em28xx *dev) { + int retval; /* Sets I2C speed to 100 KHz */ - if (!dev->is_em2800) - em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1); + if (!dev->is_em2800) { + retval = em28xx_write_regs_req(dev, 0x00, 0x06, "\x40", 1); + if (retval < 0) { + em28xx_errdev("%s: em28xx_write_regs_req failed! retval [%d]\n", + __func__, retval); + return retval; + } + } #if 1 /* enable vbi capturing */ @@ -2015,13 +2022,23 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, } /* register i2c bus */ - em28xx_i2c_register(dev); + errCode = em28xx_i2c_register(dev); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n", + __func__, errCode); + return errCode; + } /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); /* Configure audio */ - em28xx_audio_analog_set(dev); + errCode = em28xx_audio_analog_set(dev); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_audio_analog_set - errCode [%d]!\n", + __func__, errCode); + return errCode; + } /* configure the device */ em28xx_config_i2c(dev); @@ -2041,6 +2058,11 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->ctl_input = 2; errCode = em28xx_config(dev); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_config - errCode [%d]!\n", + __func__, errCode); + return errCode; + } list_add_tail(&dev->devlist, &em28xx_devlist); @@ -2096,9 +2118,20 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, if (dev->has_msp34xx) { /* Send a reset to other chips via gpio */ - em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1); + errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xf7", 1); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(1) failed! errCode [%d]\n", + __func__, errCode); + return errCode; + } msleep(3); - em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1); + + errCode = em28xx_write_regs_req(dev, 0x00, 0x08, "\xff", 1); + if (errCode < 0) { + em28xx_errdev("%s: em28xx_write_regs_req - msp34xx(2) failed! errCode [%d]\n", + __func__, errCode); + return errCode; + } msleep(3); } diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index e5e47fe66..4bdff9619 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -420,8 +420,8 @@ struct em28xx { /* frame properties */ int width; /* current frame width */ int height; /* current frame height */ - int hscale; /* horizontal scale factor (see datasheet) */ - int vscale; /* vertical scale factor (see datasheet) */ + unsigned hscale; /* horizontal scale factor (see datasheet) */ + unsigned vscale; /* vertical scale factor (see datasheet) */ int interlaced; /* 1=interlace fileds, 0=just top fileds */ unsigned int video_bytesread; /* Number of bytes read */ diff --git a/linux/drivers/media/video/et61x251/et61x251_core.c b/linux/drivers/media/video/et61x251/et61x251_core.c index 7d0f31db5..f58aee5aa 100644 --- a/linux/drivers/media/video/et61x251/et61x251_core.c +++ b/linux/drivers/media/video/et61x251/et61x251_core.c @@ -1218,7 +1218,7 @@ static int et61x251_open(struct inode* inode, struct file* filp) if (!down_read_trylock(&et61x251_dev_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); if (wait_for_completion_interruptible(&cam->probe)) { up_read(&et61x251_dev_lock); @@ -1301,7 +1301,7 @@ static int et61x251_release(struct inode* inode, struct file* filp) down_write(&et61x251_dev_lock); - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); et61x251_stop_transfer(cam); et61x251_release_buffers(cam); @@ -1322,7 +1322,7 @@ static ssize_t et61x251_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) { - struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_device *cam = video_drvdata(filp); struct et61x251_frame_t* f, * i; unsigned long lock_flags; long timeout; @@ -1430,7 +1430,7 @@ exit: static unsigned int et61x251_poll(struct file *filp, poll_table *wait) { - struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_device *cam = video_drvdata(filp); struct et61x251_frame_t* f; unsigned long lock_flags; unsigned int mask = 0; @@ -1506,7 +1506,7 @@ static struct vm_operations_struct et61x251_vm_ops = { static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) { - struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_device *cam = video_drvdata(filp); unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start; void *pos; @@ -2399,7 +2399,7 @@ et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg) static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg) { - struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_device *cam = video_drvdata(filp); switch (cmd) { @@ -2494,7 +2494,7 @@ static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, static int et61x251_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg) { - struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_device *cam = video_drvdata(filp); int err = 0; if (mutex_lock_interruptible(&cam->fileop_mutex)) @@ -2592,6 +2592,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->fops = &et61x251_fops; cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; + cam->v4ldev->parent = &udev->dev; video_set_drvdata(cam->v4ldev, cam); init_completion(&cam->probe); diff --git a/linux/drivers/media/video/gspca/conex.c b/linux/drivers/media/video/gspca/conex.c index cd3a3f582..4d9f4cc25 100644 --- a/linux/drivers/media/video/gspca/conex.c +++ b/linux/drivers/media/video/gspca/conex.c @@ -124,7 +124,7 @@ static void reg_r(struct gspca_dev *gspca_dev, struct usb_device *dev = gspca_dev->dev; #ifdef GSPCA_DEBUG - if (len > sizeof gspca_dev->usb_buf) { + if (len > USB_BUF_SZ) { err("reg_r: buffer overflow"); return; } @@ -164,7 +164,7 @@ static void reg_w(struct gspca_dev *gspca_dev, struct usb_device *dev = gspca_dev->dev; #ifdef GSPCA_DEBUG - if (len > sizeof gspca_dev->usb_buf) { + if (len > USB_BUF_SZ) { err("reg_w: buffer overflow"); return; } @@ -731,13 +731,13 @@ static void cx11646_jpeg(struct gspca_dev*gspca_dev) reg_w_val(gspca_dev, 0x0000, 0x00); /* wait for completion */ retry = 50; - while (retry--) { + do { reg_r(gspca_dev, 0x0002, 1); /* 0x07 until 0x00 */ if (gspca_dev->usb_buf[0] == 0x00) break; reg_w_val(gspca_dev, 0x0053, 0x00); - } + } while (--retry); if (retry == 0) PDEBUG(D_ERR, "Damned Errors sending jpeg Table"); /* send the qtable now */ @@ -826,8 +826,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { cx11646_init1(gspca_dev); cx11646_initsize(gspca_dev); @@ -845,10 +845,6 @@ static void sd_start(struct gspca_dev *gspca_dev) cx11646_jpeg(gspca_dev); } -static void sd_stopN(struct gspca_dev *gspca_dev) -{ -} - static void sd_stop0(struct gspca_dev *gspca_dev) { int retry = 50; @@ -871,10 +867,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) reg_w_val(gspca_dev, 0x00fc, 0xe0); } -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -998,11 +990,9 @@ static struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, - .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -1026,6 +1016,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/etoms.c b/linux/drivers/media/video/gspca/etoms.c index 60b316a37..86f3932c2 100644 --- a/linux/drivers/media/video/gspca/etoms.c +++ b/linux/drivers/media/video/gspca/etoms.c @@ -81,6 +81,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, +#define COLOR_IDX 2 { { .id = V4L2_CID_SATURATION, @@ -234,7 +235,7 @@ static void reg_r(struct gspca_dev *gspca_dev, struct usb_device *dev = gspca_dev->dev; #ifdef GSPCA_DEBUG - if (len > sizeof gspca_dev->usb_buf) { + if (len > USB_BUF_SZ) { err("reg_r: buffer overflow"); return; } @@ -272,7 +273,7 @@ static void reg_w(struct gspca_dev *gspca_dev, struct usb_device *dev = gspca_dev->dev; #ifdef GSPCA_DEBUG - if (len > sizeof gspca_dev->usb_buf) { + if (len > USB_BUF_SZ) { err("reg_w: buffer overflow"); return; } @@ -665,6 +666,7 @@ static int sd_config(struct gspca_dev *gspca_dev, } else { cam->cam_mode = vga_mode; cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + gspca_dev->ctrl_dis = (1 << COLOR_IDX); } sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; @@ -674,8 +676,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -709,14 +711,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) et_video(gspca_dev, 0); /* video off */ } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static __u8 Et_getgainG(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -899,21 +893,19 @@ static struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, }; /* -- module initialisation -- */ static __devinitdata struct usb_device_id device_table[] = { -#ifndef CONFIG_USB_ET61X251 {USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106}, -#endif +#if !defined CONFIG_USB_ET61X251 && !defined CONFIG_USB_ET61X251_MODULE {USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX}, +#endif {} }; @@ -932,6 +924,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 61071a4f4..8c98c7a05 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -47,7 +47,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 2, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 3, 0) static int video_nr = -1; @@ -118,7 +118,10 @@ static void fill_frame(struct gspca_dev *gspca_dev, cam_pkt_op pkt_scan; if (urb->status != 0) { - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); +#ifdef CONFIG_PM + if (!gspca_dev->frozen) +#endif + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); return; /* disconnection ? */ } pkt_scan = gspca_dev->sd_desc->pkt_scan; @@ -182,6 +185,46 @@ static void isoc_irq(struct urb *urb } /* + * bulk message interrupt from the USB device + */ +static void bulk_irq(struct urb *urb +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) + , struct pt_regs *regs +#endif +) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; + struct gspca_frame *frame; + int j; + + PDEBUG(D_PACK, "bulk irq"); + if (!gspca_dev->streaming) + return; + if (urb->status != 0 && urb->status != -ECONNRESET) { +#ifdef CONFIG_PM + if (!gspca_dev->frozen) +#endif + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + return; /* disconnection ? */ + } + + /* check the availability of the frame buffer */ + j = gspca_dev->fr_i; + j = gspca_dev->fr_queue[j]; + frame = &gspca_dev->frame[j]; + if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) + != V4L2_BUF_FLAG_QUEUED) { + gspca_dev->last_packet_type = DISCARD_PACKET; + } else { + PDEBUG(D_PACK, "packet l:%d", urb->actual_length); + gspca_dev->sd_desc->pkt_scan(gspca_dev, + frame, + urb->transfer_buffer, + urb->actual_length); + } +} + +/* * add data to the current frame * * This function is called by the subdrivers at interrupt level. @@ -378,10 +421,11 @@ static void destroy_urbs(struct gspca_dev *gspca_dev) } /* - * search an input isochronous endpoint in an alternate setting + * search an input transfer endpoint in an alternate setting */ -static struct usb_host_endpoint *alt_isoc(struct usb_host_interface *alt, - __u8 epaddr) +static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, + __u8 epaddr, + __u8 xfer) { struct usb_host_endpoint *ep; int i, attr; @@ -392,7 +436,7 @@ static struct usb_host_endpoint *alt_isoc(struct usb_host_interface *alt, if (ep->desc.bEndpointAddress == epaddr) { attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - if (attr == USB_ENDPOINT_XFER_ISOC) + if (attr == xfer) return ep; break; } @@ -401,14 +445,14 @@ static struct usb_host_endpoint *alt_isoc(struct usb_host_interface *alt, } /* - * search an input isochronous endpoint + * search an input (isoc or bulk) endpoint * * The endpoint is defined by the subdriver. * Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep). * This routine may be called many times when the bandwidth is too small * (the bandwidth is checked on urb submit). */ -static struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev) +static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) { struct usb_interface *intf; struct usb_host_endpoint *ep; @@ -418,15 +462,19 @@ static struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev) ep = NULL; i = gspca_dev->alt; /* previous alt setting */ while (--i > 0) { /* alt 0 is unusable */ - ep = alt_isoc(&intf->altsetting[i], gspca_dev->cam.epaddr); + ep = alt_xfer(&intf->altsetting[i], + gspca_dev->cam.epaddr, + gspca_dev->bulk + ? USB_ENDPOINT_XFER_BULK + : USB_ENDPOINT_XFER_ISOC); if (ep) break; } if (ep == NULL) { - err("no ISOC endpoint found"); + err("no transfer endpoint found"); return NULL; } - PDEBUG(D_STREAM, "use ISOC alt %d ep 0x%02x", + PDEBUG(D_STREAM, "use alt %d ep 0x%02x", i, ep->desc.bEndpointAddress); ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); if (ret < 0) { @@ -438,7 +486,7 @@ static struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev) } /* - * create the isochronous URBs + * create the URBs for image transfer */ static int create_urbs(struct gspca_dev *gspca_dev, struct usb_host_endpoint *ep) @@ -451,18 +499,28 @@ static int create_urbs(struct gspca_dev *gspca_dev, /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); - npkt = ISO_MAX_SIZE / psize; - if (npkt > ISO_MAX_PKT) - npkt = ISO_MAX_PKT; - bsize = psize * npkt; - PDEBUG(D_STREAM, - "isoc %d pkts size %d (bsize:%d)", npkt, psize, bsize); - nurbs = DEF_NURBS; + if (!gspca_dev->bulk) { + npkt = ISO_MAX_SIZE / psize; + if (npkt > ISO_MAX_PKT) + npkt = ISO_MAX_PKT; + bsize = psize * npkt; + PDEBUG(D_STREAM, + "isoc %d pkts size %d = bsize:%d", + npkt, psize, bsize); + nurbs = DEF_NURBS; + } else { + npkt = 0; + bsize = psize; + PDEBUG(D_STREAM, "bulk bsize:%d", bsize); + nurbs = 1; + } + gspca_dev->nurbs = nurbs; for (n = 0; n < nurbs; n++) { urb = usb_alloc_urb(npkt, GFP_KERNEL); if (!urb) { err("usb_alloc_urb failed"); + destroy_urbs(gspca_dev); return -ENOMEM; } urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev, @@ -472,24 +530,30 @@ static int create_urbs(struct gspca_dev *gspca_dev, if (urb->transfer_buffer == NULL) { usb_free_urb(urb); - destroy_urbs(gspca_dev); err("usb_buffer_urb failed"); + destroy_urbs(gspca_dev); return -ENOMEM; } gspca_dev->urb[n] = urb; urb->dev = gspca_dev->dev; urb->context = gspca_dev; - urb->pipe = usb_rcvisocpipe(gspca_dev->dev, - ep->desc.bEndpointAddress); - urb->transfer_flags = URB_ISO_ASAP - | URB_NO_TRANSFER_DMA_MAP; - urb->interval = ep->desc.bInterval; - urb->complete = isoc_irq; - urb->number_of_packets = npkt; urb->transfer_buffer_length = bsize; - for (i = 0; i < npkt; i++) { - urb->iso_frame_desc[i].length = psize; - urb->iso_frame_desc[i].offset = psize * i; + if (npkt != 0) { /* ISOC */ + urb->pipe = usb_rcvisocpipe(gspca_dev->dev, + ep->desc.bEndpointAddress); + urb->transfer_flags = URB_ISO_ASAP + | URB_NO_TRANSFER_DMA_MAP; + urb->interval = ep->desc.bInterval; + urb->complete = isoc_irq; + urb->number_of_packets = npkt; + for (i = 0; i < npkt; i++) { + urb->iso_frame_desc[i].length = psize; + urb->iso_frame_desc[i].offset = psize * i; + } + } else { /* bulk */ + urb->pipe = usb_rcvbulkpipe(gspca_dev->dev, + ep->desc.bEndpointAddress), + urb->complete = bulk_irq; } } return 0; @@ -511,7 +575,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->alt = gspca_dev->nbalt; for (;;) { PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt); - ep = get_isoc_ep(gspca_dev); + ep = get_ep(gspca_dev); if (ep == NULL) { ret = -EIO; goto out; @@ -525,6 +589,10 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->streaming = 1; atomic_set(&gspca_dev->nevent, 0); + /* start the bulk transfer is done by the subdriver */ + if (gspca_dev->bulk) + break; + /* submit the URBs */ for (n = 0; n < gspca_dev->nurbs; n++) { ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL); @@ -562,10 +630,12 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev) gspca_dev->streaming = 0; atomic_set(&gspca_dev->nevent, 0); if (gspca_dev->present) { - gspca_dev->sd_desc->stopN(gspca_dev); + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); gspca_set_alt0(gspca_dev); - gspca_dev->sd_desc->stop0(gspca_dev); + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); PDEBUG(D_STREAM, "stream off OK"); } } @@ -774,19 +844,7 @@ static int dev_open(struct inode *inode, struct file *file) goto out; } - /* if not done yet, initialize the sensor */ - if (gspca_dev->users == 0) { - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { - ret = -ERESTARTSYS; - goto out; - } - ret = gspca_dev->sd_desc->open(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - if (ret != 0) { - PDEBUG(D_ERR|D_CONF, "init device failed %d", ret); - goto out; - } - } else if (gspca_dev->users > 4) { /* (arbitrary value) */ + if (gspca_dev->users > 4) { /* (arbitrary value) */ ret = -EBUSY; goto out; } @@ -799,6 +857,7 @@ static int dev_open(struct inode *inode, struct file *file) else gspca_dev->vdev.debug &= ~3; #endif + ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); if (ret != 0) @@ -819,11 +878,11 @@ static int dev_close(struct inode *inode, struct file *file) /* if the file did the capture, free the streaming resources */ if (gspca_dev->capt_file == file) { - mutex_lock(&gspca_dev->usb_lock); - if (gspca_dev->streaming) + if (gspca_dev->streaming) { + mutex_lock(&gspca_dev->usb_lock); gspca_stream_off(gspca_dev); - gspca_dev->sd_desc->close(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->usb_lock); + } frame_free(gspca_dev); gspca_dev->capt_file = NULL; gspca_dev->memory = GSPCA_MEMORY_NO; @@ -860,42 +919,44 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -/* the use of V4L2_CTRL_FLAG_NEXT_CTRL asks for the controls to be sorted */ static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *q_ctrl) { struct gspca_dev *gspca_dev = priv; - int i; + int i, ix; u32 id; + ix = -1; id = q_ctrl->id; if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { id &= V4L2_CTRL_ID_MASK; id++; for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { - if (id >= gspca_dev->sd_desc->ctrls[i].qctrl.id) { - memcpy(q_ctrl, - &gspca_dev->sd_desc->ctrls[i].qctrl, - sizeof *q_ctrl); - return 0; + if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id) + continue; + if (ix < 0) { + ix = i; + continue; } + if (gspca_dev->sd_desc->ctrls[i].qctrl.id + > gspca_dev->sd_desc->ctrls[ix].qctrl.id) + continue; + ix = i; } - return -EINVAL; } for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { if (id == gspca_dev->sd_desc->ctrls[i].qctrl.id) { - memcpy(q_ctrl, - &gspca_dev->sd_desc->ctrls[i].qctrl, - sizeof *q_ctrl); - return 0; + ix = i; + break; } } - if (id >= V4L2_CID_BASE - && id <= V4L2_CID_LASTP1) { + if (ix < 0) + return -EINVAL; + memcpy(q_ctrl, &gspca_dev->sd_desc->ctrls[ix].qctrl, + sizeof *q_ctrl); + if (gspca_dev->ctrl_dis & (1 << ix)) q_ctrl->flags |= V4L2_CTRL_FLAG_DISABLED; - return 0; - } - return -EINVAL; + return 0; } static int vidioc_s_ctrl(struct file *file, void *priv, @@ -910,6 +971,8 @@ static int vidioc_s_ctrl(struct file *file, void *priv, i++, ctrls++) { if (ctrl->id != ctrls->qctrl.id) continue; + if (gspca_dev->ctrl_dis & (1 << i)) + return -EINVAL; if (ctrl->value < ctrls->qctrl.minimum || ctrl->value > ctrls->qctrl.maximum) return -ERANGE; @@ -936,6 +999,8 @@ static int vidioc_g_ctrl(struct file *file, void *priv, i++, ctrls++) { if (ctrl->id != ctrls->qctrl.id) continue; + if (gspca_dev->ctrl_dis & (1 << i)) + return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; ret = ctrls->get(gspca_dev, &ctrl->value); @@ -1410,7 +1475,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, i = ret; /* frame index */ frame = &gspca_dev->frame[i]; if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { - if (copy_to_user((__u8 *) frame->v4l2_buf.m.userptr, + if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, frame->data, frame->v4l2_buf.bytesused)) { PDEBUG(D_ERR|D_STREAM, @@ -1738,6 +1803,12 @@ int gspca_dev_probe(struct usb_interface *intf, err("couldn't kzalloc gspca struct"); return -EIO; } + gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); + if (!gspca_dev->usb_buf) { + err("out of memory"); + ret = -EIO; + goto out; + } gspca_dev->dev = dev; gspca_dev->iface = interface->bInterfaceNumber; gspca_dev->nbalt = intf->num_altsetting; @@ -1745,10 +1816,13 @@ int gspca_dev_probe(struct usb_interface *intf, /* gspca_dev->users = 0; (done by kzalloc) */ gspca_dev->nbufread = 2; - /* configure the subdriver */ + /* configure the subdriver and initialize the USB device */ ret = gspca_dev->sd_desc->config(gspca_dev, id); if (ret < 0) goto out; + ret = gspca_dev->sd_desc->init(gspca_dev); + if (ret < 0) + goto out; ret = gspca_set_alt0(gspca_dev); if (ret < 0) goto out; @@ -1778,6 +1852,7 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_PROBE, "probe ok"); return 0; out: + kfree(gspca_dev->usb_buf); kfree(gspca_dev); return ret; } @@ -1810,11 +1885,42 @@ void gspca_disconnect(struct usb_interface *intf) /* We don't want people trying to open up the device */ video_unregister_device(&gspca_dev->vdev); /* Free the memory */ + kfree(gspca_dev->usb_buf); kfree(gspca_dev); PDEBUG(D_PROBE, "disconnect complete"); } EXPORT_SYMBOL(gspca_disconnect); +#ifdef CONFIG_PM +int gspca_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + + if (!gspca_dev->streaming) + return 0; + gspca_dev->frozen = 1; /* avoid urb error messages */ + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_set_alt0(gspca_dev); + if (gspca_dev->sd_desc->stop0) + gspca_dev->sd_desc->stop0(gspca_dev); + return 0; +} +EXPORT_SYMBOL(gspca_suspend); + +int gspca_resume(struct usb_interface *intf) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + + gspca_dev->frozen = 0; + gspca_dev->sd_desc->init(gspca_dev); + if (gspca_dev->streaming) + return gspca_init_transfer(gspca_dev); + return 0; +} +EXPORT_SYMBOL(gspca_resume); +#endif /* -- cam driver utility functions -- */ /* auto gain and exposure algorithm based on the knee algorithm described here: diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 67e448940..78a35a88c 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -49,14 +49,13 @@ extern int gspca_debug; } while (0) #define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */ -/* ISOC transfers */ -#define MAX_NURBS 16 /* max number of URBs */ +/* image transfers */ +#define MAX_NURBS 4 /* max number of URBs */ #define ISO_MAX_PKT 32 /* max number of packets in an ISOC transfer */ #define ISO_MAX_SIZE 0x8000 /* max size of one URB buffer (32 Kb) */ /* device information - set at probe time */ struct cam { - char *dev_name; struct v4l2_pix_format *cam_mode; /* size nmodes */ char nmodes; __u8 epaddr; @@ -91,15 +90,14 @@ struct sd_desc { /* controls */ const struct ctrl *ctrls; int nctrls; -/* operations */ +/* mandatory operations */ cam_cf_op config; /* called on probe */ - cam_op open; /* called on open */ + cam_op init; /* called on probe and resume */ cam_v_op start; /* called on stream on */ - cam_v_op stopN; /* called on stream off - main alt */ - cam_v_op stop0; /* called on stream off - alt 0 */ - cam_v_op close; /* called on close */ cam_pkt_op pkt_scan; /* optional operations */ + cam_v_op stopN; /* called on stream off - main alt */ + cam_v_op stop0; /* called on stream off - alt 0 */ cam_v_op dq_callback; /* called when a frame has been dequeued */ cam_jpg_op get_jcomp; cam_jpg_op set_jcomp; @@ -127,8 +125,10 @@ struct gspca_dev { struct cam cam; /* device information */ const struct sd_desc *sd_desc; /* subdriver description */ + unsigned ctrl_dis; /* disabled controls (bit map) */ - __u8 usb_buf[8]; /* buffer for USB exchanges */ +#define USB_BUF_SZ 64 + __u8 *usb_buf; /* buffer for USB exchanges */ struct urb *urb[MAX_NURBS]; __u8 *frbuf; /* buffer for nframes */ @@ -143,6 +143,7 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ + __u8 bulk; /* image transfer by isoc (0) or bulk (1) */ __u8 curr_mode; /* current camera mode */ __u32 pixfmt; /* current mode parameters */ __u16 width; @@ -155,6 +156,9 @@ struct gspca_dev { struct mutex queue_lock; /* ISOC queue protection */ __u32 sequence; /* frame sequence number */ char streaming; +#ifdef CONFIG_PM + char frozen; /* suspend - resume */ +#endif char users; /* number of opens */ char present; /* device connected */ char nbufread; /* number of buffers for read() */ @@ -174,6 +178,10 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, struct gspca_frame *frame, const __u8 *data, int len); +#ifdef CONFIG_PM +int gspca_suspend(struct usb_interface *intf, pm_message_t message); +int gspca_resume(struct usb_interface *intf); +#endif int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); #endif /* GSPCAV2_H */ diff --git a/linux/drivers/media/video/gspca/mars.c b/linux/drivers/media/video/gspca/mars.c index 21c4ee56a..4d5db47ba 100644 --- a/linux/drivers/media/video/gspca/mars.c +++ b/linux/drivers/media/video/gspca/mars.c @@ -100,22 +100,6 @@ static int reg_w(struct gspca_dev *gspca_dev, return rc; } -static int reg_w_buf(struct gspca_dev *gspca_dev, - __u16 index, __u8 *buf, int len) -{ - int rc; - - rc = usb_control_msg(gspca_dev->dev, - usb_sndbulkpipe(gspca_dev->dev, 4), - 0x12, - 0xc8, /* ?? */ - 0, /* value */ - index, buf, len, 500); - if (rc < 0) - PDEBUG(D_ERR, "reg write [%02x] error %d", index, rc); - return rc; -} - static void bulk_w(struct gspca_dev *gspca_dev, __u16 *pch, __u16 Address) @@ -144,8 +128,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { return 0; } @@ -175,7 +159,6 @@ static void sd_start(struct gspca_dev *gspca_dev) /* Initialize the MR97113 chip register */ - data = kmalloc(16, GFP_KERNEL); data[0] = 0x00; /* address */ data[1] = 0x0c | 0x01; /* reg 0 */ data[2] = 0x01; /* reg 1 */ @@ -195,12 +178,10 @@ static void sd_start(struct gspca_dev *gspca_dev) data[10] = 0x5d; /* reg 9, I2C device address * [for PAS5101 (0x40)] [for MI (0x5d)] */ - err_code = reg_w_buf(gspca_dev, data[0], data, 11); - kfree(data); + err_code = reg_w(gspca_dev, data[0], 11); if (err_code < 0) return; - data = gspca_dev->usb_buf; data[0] = 0x23; /* address */ data[1] = 0x09; /* reg 35, append frame header */ @@ -358,14 +339,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_ERR, "Camera Stop failed"); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -411,11 +384,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -439,6 +410,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/ov519.c b/linux/drivers/media/video/gspca/ov519.c index 2379433f0..bb2842114 100644 --- a/linux/drivers/media/video/gspca/ov519.c +++ b/linux/drivers/media/video/gspca/ov519.c @@ -63,11 +63,10 @@ struct sd { #define SEN_OV6630 2 #define SEN_OV7610 3 #define SEN_OV7620 4 -#define SEN_OV7630 5 -#define SEN_OV7640 6 -#define SEN_OV7670 7 -#define SEN_OV76BE 8 -#define SEN_OV8610 9 +#define SEN_OV7640 5 +#define SEN_OV7670 6 +#define SEN_OV76BE 7 +#define SEN_OV8610 8 }; @@ -127,6 +126,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getcolors, }, /* next controls work with ov7670 only */ +#define HFLIP_IDX 3 { { .id = V4L2_CID_HFLIP, @@ -141,6 +141,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_sethflip, .get = sd_gethflip, }, +#define VFLIP_IDX 4 { { .id = V4L2_CID_VFLIP, @@ -293,6 +294,541 @@ static struct v4l2_pix_format sif_mode[] = { #define OV7670_REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ #define OV7670_REG_BD60MAX 0xab /* 60hz banding step limit */ +struct ov_regvals { + __u8 reg; + __u8 val; +}; +struct ov_i2c_regvals { + __u8 reg; + __u8 val; +}; + +static const struct ov_i2c_regvals norm_6x20[] = { + { 0x12, 0x80 }, /* reset */ + { 0x11, 0x01 }, + { 0x03, 0x60 }, + { 0x05, 0x7f }, /* For when autoadjust is off */ + { 0x07, 0xa8 }, + /* The ratio of 0x0c and 0x0d controls the white point */ + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x0f, 0x15 }, /* COMS */ + { 0x10, 0x75 }, /* AEC Exposure time */ + { 0x12, 0x24 }, /* Enable AGC */ + { 0x14, 0x04 }, + /* 0x16: 0x06 helps frame stability with moving objects */ + { 0x16, 0x06 }, +/* { 0x20, 0x30 }, * Aperture correction enable */ + { 0x26, 0xb2 }, /* BLC enable */ + /* 0x28: 0x05 Selects RGB format if RGB on */ + { 0x28, 0x05 }, + { 0x2a, 0x04 }, /* Disable framerate adjust */ +/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ + { 0x2d, 0x99 }, + { 0x33, 0xa0 }, /* Color Processing Parameter */ + { 0x34, 0xd2 }, /* Max A/D range */ + { 0x38, 0x8b }, + { 0x39, 0x40 }, + + { 0x3c, 0x39 }, /* Enable AEC mode changing */ + { 0x3c, 0x3c }, /* Change AEC mode */ + { 0x3c, 0x24 }, /* Disable AEC mode changing */ + + { 0x3d, 0x80 }, + /* These next two registers (0x4a, 0x4b) are undocumented. + * They control the color balance */ + { 0x4a, 0x80 }, + { 0x4b, 0x80 }, + { 0x4d, 0xd2 }, /* This reduces noise a bit */ + { 0x4e, 0xc1 }, + { 0x4f, 0x04 }, +/* Do 50-53 have any effect? */ +/* Toggle 0x12[2] off and on here? */ +}; + +static const struct ov_i2c_regvals norm_6x30[] = { + { 0x12, 0x80 }, /* Reset */ + { 0x00, 0x1f }, /* Gain */ + { 0x01, 0x99 }, /* Blue gain */ + { 0x02, 0x7c }, /* Red gain */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x0a }, /* Contrast */ + { 0x06, 0x95 }, /* Brightness */ + { 0x07, 0x2d }, /* Sharpness */ + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x0e, 0x20 }, + { 0x0f, 0x05 }, + { 0x10, 0x9a }, + { 0x11, 0x00 }, /* Pixel clock = fastest */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x21 }, + { 0x14, 0x80 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x38 }, + { 0x18, 0xea }, + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, + { 0x1e, 0xc4 }, + { 0x1f, 0x04 }, + { 0x20, 0x20 }, + { 0x21, 0x10 }, + { 0x22, 0x88 }, + { 0x23, 0xc0 }, /* Crystal circuit power level */ + { 0x25, 0x9a }, /* Increase AEC black ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x84 }, /* 60 Hz power */ + { 0x2b, 0xa8 }, /* 60 Hz power */ + { 0x2c, 0xa0 }, + { 0x2d, 0x95 }, /* Enable auto-brightness */ + { 0x2e, 0x88 }, + { 0x33, 0x26 }, + { 0x34, 0x03 }, + { 0x36, 0x8f }, + { 0x37, 0x80 }, + { 0x38, 0x83 }, + { 0x39, 0x80 }, + { 0x3a, 0x0f }, + { 0x3b, 0x3c }, + { 0x3c, 0x1a }, + { 0x3d, 0x80 }, + { 0x3e, 0x80 }, + { 0x3f, 0x0e }, + { 0x40, 0x00 }, /* White bal */ + { 0x41, 0x00 }, /* White bal */ + { 0x42, 0x80 }, + { 0x43, 0x3f }, /* White bal */ + { 0x44, 0x80 }, + { 0x45, 0x20 }, + { 0x46, 0x20 }, + { 0x47, 0x80 }, + { 0x48, 0x7f }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x80 }, + { 0x4c, 0xd0 }, + { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ + { 0x4e, 0x40 }, + { 0x4f, 0x07 }, /* UV avg., col. killer: max */ + { 0x50, 0xff }, + { 0x54, 0x23 }, /* Max AGC gain: 18dB */ + { 0x55, 0xff }, + { 0x56, 0x12 }, + { 0x57, 0x81 }, + { 0x58, 0x75 }, + { 0x59, 0x01 }, /* AGC dark current comp.: +1 */ + { 0x5a, 0x2c }, + { 0x5b, 0x0f }, /* AWB chrominance levels */ + { 0x5c, 0x10 }, + { 0x3d, 0x80 }, + { 0x27, 0xa6 }, + { 0x12, 0x20 }, /* Toggle AWB */ + { 0x12, 0x24 }, +}; + +/* Lawrence Glaister <lg@jfm.bc.ca> reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ +static const struct ov_i2c_regvals norm_7610[] = { + { 0x10, 0xff }, + { 0x16, 0x06 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, +}; + +static const struct ov_i2c_regvals norm_7620[] = { + { 0x00, 0x00 }, /* gain */ + { 0x01, 0x80 }, /* blue gain */ + { 0x02, 0x80 }, /* red gain */ + { 0x03, 0xc0 }, /* OV7670_REG_VREF */ + { 0x06, 0x60 }, + { 0x07, 0x00 }, + { 0x0c, 0x24 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x11, 0x01 }, + { 0x12, 0x24 }, + { 0x13, 0x01 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x18 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x20 }, + { 0x29, 0x00 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x91 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x27 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x02 }, + { 0x6d, 0x44 }, + { 0x6e, 0x80 }, + { 0x6f, 0x1d }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe2 }, + { 0x7c, 0x00 }, +}; + +/* 7640 and 7648. The defaults should be OK for most registers. */ +static const struct ov_i2c_regvals norm_7640[] = { + { 0x12, 0x80 }, + { 0x12, 0x14 }, +}; + +/* 7670. Defaults taken from OmniVision provided data, +* as provided by Jonathan Corbet of OLPC */ +static const struct ov_i2c_regvals norm_7670[] = { + { OV7670_REG_COM7, OV7670_COM7_RESET }, + { OV7670_REG_TSLB, 0x04 }, /* OV */ + { OV7670_REG_COM7, OV7670_COM7_FMT_VGA }, /* VGA */ + { OV7670_REG_CLKRC, 0x01 }, +/* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { OV7670_REG_HSTART, 0x13 }, + { OV7670_REG_HSTOP, 0x01 }, + { OV7670_REG_HREF, 0xb6 }, + { OV7670_REG_VSTART, 0x02 }, + { OV7670_REG_VSTOP, 0x7a }, + { OV7670_REG_VREF, 0x0a }, + + { OV7670_REG_COM3, 0 }, + { OV7670_REG_COM14, 0 }, +/* Mystery scaling numbers */ + { 0x70, 0x3a }, + { 0x71, 0x35 }, + { 0x72, 0x11 }, + { 0x73, 0xf0 }, + { 0xa2, 0x02 }, +/* { OV7670_REG_COM10, 0x0 }, */ + +/* Gamma curve values */ + { 0x7a, 0x20 }, + { 0x7b, 0x10 }, + { 0x7c, 0x1e }, + { 0x7d, 0x35 }, + { 0x7e, 0x5a }, + { 0x7f, 0x69 }, + { 0x80, 0x76 }, + { 0x81, 0x80 }, + { 0x82, 0x88 }, + { 0x83, 0x8f }, + { 0x84, 0x96 }, + { 0x85, 0xa3 }, + { 0x86, 0xaf }, + { 0x87, 0xc4 }, + { 0x88, 0xd7 }, + { 0x89, 0xe8 }, + +/* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT }, + { OV7670_REG_GAIN, 0 }, + { OV7670_REG_AECH, 0 }, + { OV7670_REG_COM4, 0x40 }, /* magic reserved bit */ + { OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { OV7670_REG_BD50MAX, 0x05 }, + { OV7670_REG_BD60MAX, 0x07 }, + { OV7670_REG_AEW, 0x95 }, + { OV7670_REG_AEB, 0x33 }, + { OV7670_REG_VPT, 0xe3 }, + { OV7670_REG_HAECC1, 0x78 }, + { OV7670_REG_HAECC2, 0x68 }, + { 0xa1, 0x03 }, /* magic */ + { OV7670_REG_HAECC3, 0xd8 }, + { OV7670_REG_HAECC4, 0xd8 }, + { OV7670_REG_HAECC5, 0xf0 }, + { OV7670_REG_HAECC6, 0x90 }, + { OV7670_REG_HAECC7, 0x94 }, + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC }, + +/* Almost all of these are magic "reserved" values. */ + { OV7670_REG_COM5, 0x61 }, + { OV7670_REG_COM6, 0x4b }, + { 0x16, 0x02 }, + { OV7670_REG_MVFP, 0x07 }, + { 0x21, 0x02 }, + { 0x22, 0x91 }, + { 0x29, 0x07 }, + { 0x33, 0x0b }, + { 0x35, 0x0b }, + { 0x37, 0x1d }, + { 0x38, 0x71 }, + { 0x39, 0x2a }, + { OV7670_REG_COM12, 0x78 }, + { 0x4d, 0x40 }, + { 0x4e, 0x20 }, + { OV7670_REG_GFIX, 0 }, + { 0x6b, 0x4a }, + { 0x74, 0x10 }, + { 0x8d, 0x4f }, + { 0x8e, 0 }, + { 0x8f, 0 }, + { 0x90, 0 }, + { 0x91, 0 }, + { 0x96, 0 }, + { 0x9a, 0 }, + { 0xb0, 0x84 }, + { 0xb1, 0x0c }, + { 0xb2, 0x0e }, + { 0xb3, 0x82 }, + { 0xb8, 0x0a }, + +/* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, + { 0x44, 0xf0 }, + { 0x45, 0x34 }, + { 0x46, 0x58 }, + { 0x47, 0x28 }, + { 0x48, 0x3a }, + { 0x59, 0x88 }, + { 0x5a, 0x88 }, + { 0x5b, 0x44 }, + { 0x5c, 0x67 }, + { 0x5d, 0x49 }, + { 0x5e, 0x0e }, + { 0x6c, 0x0a }, + { 0x6d, 0x55 }, + { 0x6e, 0x11 }, + { 0x6f, 0x9f }, + /* "9e for advance AWB" */ + { 0x6a, 0x40 }, + { OV7670_REG_BLUE, 0x40 }, + { OV7670_REG_RED, 0x60 }, + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC + | OV7670_COM8_AWB }, + +/* Matrix coefficients */ + { 0x4f, 0x80 }, + { 0x50, 0x80 }, + { 0x51, 0 }, + { 0x52, 0x22 }, + { 0x53, 0x5e }, + { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { OV7670_REG_COM16, OV7670_COM16_AWBGAIN }, + { OV7670_REG_EDGE, 0 }, + { 0x75, 0x05 }, + { 0x76, 0xe1 }, + { 0x4c, 0 }, + { 0x77, 0x01 }, + { OV7670_REG_COM13, OV7670_COM13_GAMMA + | OV7670_COM13_UVSAT + | 2}, /* was 3 */ + { 0x4b, 0x09 }, + { 0xc9, 0x60 }, + { OV7670_REG_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, + { OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO }, + { 0xa4, 0x88 }, + { 0x96, 0 }, + { 0x97, 0x30 }, + { 0x98, 0x20 }, + { 0x99, 0x30 }, + { 0x9a, 0x84 }, + { 0x9b, 0x29 }, + { 0x9c, 0x03 }, + { 0x9d, 0x4c }, + { 0x9e, 0x3f }, + { 0x78, 0x04 }, + +/* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, + { 0xc8, 0xf0 }, + { 0x79, 0x0f }, + { 0xc8, 0x00 }, + { 0x79, 0x10 }, + { 0xc8, 0x7e }, + { 0x79, 0x0a }, + { 0xc8, 0x80 }, + { 0x79, 0x0b }, + { 0xc8, 0x01 }, + { 0x79, 0x0c }, + { 0xc8, 0x0f }, + { 0x79, 0x0d }, + { 0xc8, 0x20 }, + { 0x79, 0x09 }, + { 0xc8, 0x80 }, + { 0x79, 0x02 }, + { 0xc8, 0xc0 }, + { 0x79, 0x03 }, + { 0xc8, 0x40 }, + { 0x79, 0x05 }, + { 0xc8, 0x30 }, + { 0x79, 0x26 }, +}; + +static const struct ov_i2c_regvals norm_8610[] = { + { 0x12, 0x80 }, + { 0x00, 0x00 }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, 0xc0 }, + { 0x04, 0x30 }, + { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */ + { 0x0a, 0x86 }, + { 0x0b, 0xb0 }, + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x11, 0x01 }, + { 0x12, 0x25 }, + { 0x13, 0x01 }, + { 0x14, 0x04 }, + { 0x15, 0x01 }, /* Lin and Win think different about UV order */ + { 0x16, 0x03 }, + { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */ + { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */ + { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */ + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */ + { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */ + { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */ + { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */ + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x80 }, + { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */ + { 0x2c, 0xac }, + { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */ + { 0x2e, 0x80 }, + { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */ + { 0x4c, 0x00 }, + { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */ + { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */ + { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */ + { 0x63, 0xff }, + { 0x64, 0x53 }, /* new windrv 090403 says 0x57, + * maybe thats wrong */ + { 0x65, 0x00 }, + { 0x66, 0x55 }, + { 0x67, 0xb0 }, + { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */ + { 0x69, 0x02 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but + * deleting bit7 colors the first images red */ + { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6f, 0x01 }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */ + { 0x75, 0x0e }, + { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */ + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */ + { 0x7c, 0x00 }, + { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */ + { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */ + { 0x7f, 0xfb }, + { 0x80, 0x28 }, + { 0x81, 0x00 }, + { 0x82, 0x23 }, + { 0x83, 0x0b }, + { 0x84, 0x00 }, + { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */ + { 0x86, 0xc9 }, + { 0x87, 0x00 }, + { 0x88, 0x00 }, + { 0x89, 0x01 }, + { 0x12, 0x20 }, + { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */ +}; + static unsigned char ov7670_abs_to_sm(unsigned char v) { if (v > 127) @@ -537,18 +1073,10 @@ static int ov51x_set_slave_ids(struct sd *sd, rc = reg_w(sd, R51x_I2C_W_SID, slave); if (rc < 0) return rc; + sd->primary_i2c_slave = slave; return reg_w(sd, R51x_I2C_R_SID, slave + 1); } -struct ov_regvals { - __u8 reg; - __u8 val; -}; -struct ov_i2c_regvals { - __u8 reg; - __u8 val; -}; - static int write_regvals(struct sd *sd, const struct ov_regvals *regvals, int n) @@ -591,101 +1119,9 @@ static int write_i2c_regvals(struct sd *sd, static int ov8xx0_configure(struct sd *sd) { int rc; - static const struct ov_i2c_regvals norm_8610[] = { - { 0x12, 0x80 }, - { 0x00, 0x00 }, - { 0x01, 0x80 }, - { 0x02, 0x80 }, - { 0x03, 0xc0 }, - { 0x04, 0x30 }, - { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */ - { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */ - { 0x0a, 0x86 }, - { 0x0b, 0xb0 }, - { 0x0c, 0x20 }, - { 0x0d, 0x20 }, - { 0x11, 0x01 }, - { 0x12, 0x25 }, - { 0x13, 0x01 }, - { 0x14, 0x04 }, - { 0x15, 0x01 }, /* Lin and Win think different about UV order */ - { 0x16, 0x03 }, - { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */ - { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */ - { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */ - { 0x1a, 0xf5 }, - { 0x1b, 0x00 }, - { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */ - { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */ - { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */ - { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */ - { 0x26, 0xa2 }, - { 0x27, 0xea }, - { 0x28, 0x00 }, - { 0x29, 0x00 }, - { 0x2a, 0x80 }, - { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */ - { 0x2c, 0xac }, - { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */ - { 0x2e, 0x80 }, - { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */ - { 0x4c, 0x00 }, - { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */ - { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */ - { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */ - { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */ - { 0x63, 0xff }, - { 0x64, 0x53 }, /* new windrv 090403 says 0x57, - * maybe thats wrong */ - { 0x65, 0x00 }, - { 0x66, 0x55 }, - { 0x67, 0xb0 }, - { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */ - { 0x69, 0x02 }, - { 0x6a, 0x22 }, - { 0x6b, 0x00 }, - { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but - deleting bit7 colors the first images red */ - { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */ - { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */ - { 0x6f, 0x01 }, - { 0x70, 0x8b }, - { 0x71, 0x00 }, - { 0x72, 0x14 }, - { 0x73, 0x54 }, - { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */ - { 0x75, 0x0e }, - { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */ - { 0x77, 0xff }, - { 0x78, 0x80 }, - { 0x79, 0x80 }, - { 0x7a, 0x80 }, - { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */ - { 0x7c, 0x00 }, - { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */ - { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */ - { 0x7f, 0xfb }, - { 0x80, 0x28 }, - { 0x81, 0x00 }, - { 0x82, 0x23 }, - { 0x83, 0x0b }, - { 0x84, 0x00 }, - { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */ - { 0x86, 0xc9 }, - { 0x87, 0x00 }, - { 0x88, 0x00 }, - { 0x89, 0x01 }, - { 0x12, 0x20 }, - { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */ - }; PDEBUG(D_PROBE, "starting ov8xx0 configuration"); - if (init_ov_sensor(sd) < 0) - PDEBUG(D_ERR|D_PROBE, "Failed to read sensor ID"); - else - PDEBUG(D_PROBE, "OV86x0 initialized"); - /* Detect sensor (sub)type */ rc = i2c_r(sd, OV7610_REG_COM_I); if (rc < 0) { @@ -698,9 +1134,6 @@ static int ov8xx0_configure(struct sd *sd) PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3); return -1; } - PDEBUG(D_PROBE, "Writing 8610 registers"); - if (write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610))) - return -1; /* Set sensor-specific vars */ /* sd->sif = 0; already done */ @@ -714,252 +1147,6 @@ static int ov7xx0_configure(struct sd *sd) { int rc, high, low; - /* Lawrence Glaister <lg@jfm.bc.ca> reports: - * - * Register 0x0f in the 7610 has the following effects: - * - * 0x85 (AEC method 1): Best overall, good contrast range - * 0x45 (AEC method 2): Very overexposed - * 0xa5 (spec sheet default): Ok, but the black level is - * shifted resulting in loss of contrast - * 0x05 (old driver setting): very overexposed, too much - * contrast - */ - static const struct ov_i2c_regvals norm_7610[] = { - { 0x10, 0xff }, - { 0x16, 0x06 }, - { 0x28, 0x24 }, - { 0x2b, 0xac }, - { 0x12, 0x00 }, - { 0x38, 0x81 }, - { 0x28, 0x24 }, /* 0c */ - { 0x0f, 0x85 }, /* lg's setting */ - { 0x15, 0x01 }, - { 0x20, 0x1c }, - { 0x23, 0x2a }, - { 0x24, 0x10 }, - { 0x25, 0x8a }, - { 0x26, 0xa2 }, - { 0x27, 0xc2 }, - { 0x2a, 0x04 }, - { 0x2c, 0xfe }, - { 0x2d, 0x93 }, - { 0x30, 0x71 }, - { 0x31, 0x60 }, - { 0x32, 0x26 }, - { 0x33, 0x20 }, - { 0x34, 0x48 }, - { 0x12, 0x24 }, - { 0x11, 0x01 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - }; - - static const struct ov_i2c_regvals norm_7620[] = { - { 0x00, 0x00 }, /* gain */ - { 0x01, 0x80 }, /* blue gain */ - { 0x02, 0x80 }, /* red gain */ - { 0x03, 0xc0 }, /* OV7670_REG_VREF */ - { 0x06, 0x60 }, - { 0x07, 0x00 }, - { 0x0c, 0x24 }, - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0x11, 0x01 }, - { 0x12, 0x24 }, - { 0x13, 0x01 }, - { 0x14, 0x84 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x2f }, - { 0x18, 0xcf }, - { 0x19, 0x06 }, - { 0x1a, 0xf5 }, - { 0x1b, 0x00 }, - { 0x20, 0x18 }, - { 0x21, 0x80 }, - { 0x22, 0x80 }, - { 0x23, 0x00 }, - { 0x26, 0xa2 }, - { 0x27, 0xea }, - { 0x28, 0x20 }, - { 0x29, 0x00 }, - { 0x2a, 0x10 }, - { 0x2b, 0x00 }, - { 0x2c, 0x88 }, - { 0x2d, 0x91 }, - { 0x2e, 0x80 }, - { 0x2f, 0x44 }, - { 0x60, 0x27 }, - { 0x61, 0x02 }, - { 0x62, 0x5f }, - { 0x63, 0xd5 }, - { 0x64, 0x57 }, - { 0x65, 0x83 }, - { 0x66, 0x55 }, - { 0x67, 0x92 }, - { 0x68, 0xcf }, - { 0x69, 0x76 }, - { 0x6a, 0x22 }, - { 0x6b, 0x00 }, - { 0x6c, 0x02 }, - { 0x6d, 0x44 }, - { 0x6e, 0x80 }, - { 0x6f, 0x1d }, - { 0x70, 0x8b }, - { 0x71, 0x00 }, - { 0x72, 0x14 }, - { 0x73, 0x54 }, - { 0x74, 0x00 }, - { 0x75, 0x8e }, - { 0x76, 0x00 }, - { 0x77, 0xff }, - { 0x78, 0x80 }, - { 0x79, 0x80 }, - { 0x7a, 0x80 }, - { 0x7b, 0xe2 }, - { 0x7c, 0x00 }, - }; - - /* 7640 and 7648. The defaults should be OK for most registers. */ - static const struct ov_i2c_regvals norm_7640[] = { - { 0x12, 0x80 }, - { 0x12, 0x14 }, - }; - - /* 7670. Defaults taken from OmniVision provided data, - * as provided by Jonathan Corbet of OLPC */ - static const struct ov_i2c_regvals norm_7670[] = { - { OV7670_REG_COM7, OV7670_COM7_RESET }, - { OV7670_REG_TSLB, 0x04 }, /* OV */ - { OV7670_REG_COM7, OV7670_COM7_FMT_VGA }, /* VGA */ - { OV7670_REG_CLKRC, 0x01 }, - /* - * Set the hardware window. These values from OV don't entirely - * make sense - hstop is less than hstart. But they work... - */ - { OV7670_REG_HSTART, 0x13 }, { OV7670_REG_HSTOP, 0x01 }, - { OV7670_REG_HREF, 0xb6 }, { OV7670_REG_VSTART, 0x02 }, - { OV7670_REG_VSTOP, 0x7a }, { OV7670_REG_VREF, 0x0a }, - - { OV7670_REG_COM3, 0 }, { OV7670_REG_COM14, 0 }, - /* Mystery scaling numbers */ - { 0x70, 0x3a }, { 0x71, 0x35 }, - { 0x72, 0x11 }, { 0x73, 0xf0 }, - { 0xa2, 0x02 }, -/* { OV7670_REG_COM10, 0x0 }, */ - - /* Gamma curve values */ - { 0x7a, 0x20 }, - { 0x7b, 0x10 }, - { 0x7c, 0x1e }, - { 0x7d, 0x35 }, - { 0x7e, 0x5a }, { 0x7f, 0x69 }, - { 0x80, 0x76 }, { 0x81, 0x80 }, - { 0x82, 0x88 }, { 0x83, 0x8f }, - { 0x84, 0x96 }, { 0x85, 0xa3 }, - { 0x86, 0xaf }, { 0x87, 0xc4 }, - { 0x88, 0xd7 }, { 0x89, 0xe8 }, - - /* AGC and AEC parameters. Note we start by disabling those features, - then turn them only after tweaking the values. */ - { OV7670_REG_COM8, OV7670_COM8_FASTAEC - | OV7670_COM8_AECSTEP - | OV7670_COM8_BFILT }, - { OV7670_REG_GAIN, 0 }, { OV7670_REG_AECH, 0 }, - { OV7670_REG_COM4, 0x40 }, /* magic reserved bit */ - { OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ - { OV7670_REG_BD50MAX, 0x05 }, { OV7670_REG_BD60MAX, 0x07 }, - { OV7670_REG_AEW, 0x95 }, { OV7670_REG_AEB, 0x33 }, - { OV7670_REG_VPT, 0xe3 }, { OV7670_REG_HAECC1, 0x78 }, - { OV7670_REG_HAECC2, 0x68 }, - { 0xa1, 0x03 }, /* magic */ - { OV7670_REG_HAECC3, 0xd8 }, { OV7670_REG_HAECC4, 0xd8 }, - { OV7670_REG_HAECC5, 0xf0 }, { OV7670_REG_HAECC6, 0x90 }, - { OV7670_REG_HAECC7, 0x94 }, - { OV7670_REG_COM8, OV7670_COM8_FASTAEC - | OV7670_COM8_AECSTEP - | OV7670_COM8_BFILT - | OV7670_COM8_AGC - | OV7670_COM8_AEC }, - - /* Almost all of these are magic "reserved" values. */ - { OV7670_REG_COM5, 0x61 }, { OV7670_REG_COM6, 0x4b }, - { 0x16, 0x02 }, - { OV7670_REG_MVFP, 0x07 }, - { 0x21, 0x02 }, { 0x22, 0x91 }, - { 0x29, 0x07 }, { 0x33, 0x0b }, - { 0x35, 0x0b }, { 0x37, 0x1d }, - { 0x38, 0x71 }, { 0x39, 0x2a }, - { OV7670_REG_COM12, 0x78 }, { 0x4d, 0x40 }, - { 0x4e, 0x20 }, { OV7670_REG_GFIX, 0 }, - { 0x6b, 0x4a }, { 0x74, 0x10 }, - { 0x8d, 0x4f }, { 0x8e, 0 }, - { 0x8f, 0 }, { 0x90, 0 }, - { 0x91, 0 }, { 0x96, 0 }, - { 0x9a, 0 }, { 0xb0, 0x84 }, - { 0xb1, 0x0c }, { 0xb2, 0x0e }, - { 0xb3, 0x82 }, { 0xb8, 0x0a }, - - /* More reserved magic, some of which tweaks white balance */ - { 0x43, 0x0a }, { 0x44, 0xf0 }, - { 0x45, 0x34 }, { 0x46, 0x58 }, - { 0x47, 0x28 }, { 0x48, 0x3a }, - { 0x59, 0x88 }, { 0x5a, 0x88 }, - { 0x5b, 0x44 }, { 0x5c, 0x67 }, - { 0x5d, 0x49 }, { 0x5e, 0x0e }, - { 0x6c, 0x0a }, { 0x6d, 0x55 }, - { 0x6e, 0x11 }, { 0x6f, 0x9f }, - /* "9e for advance AWB" */ - { 0x6a, 0x40 }, { OV7670_REG_BLUE, 0x40 }, - { OV7670_REG_RED, 0x60 }, - { OV7670_REG_COM8, OV7670_COM8_FASTAEC - | OV7670_COM8_AECSTEP - | OV7670_COM8_BFILT - | OV7670_COM8_AGC - | OV7670_COM8_AEC - | OV7670_COM8_AWB }, - - /* Matrix coefficients */ - { 0x4f, 0x80 }, { 0x50, 0x80 }, - { 0x51, 0 }, { 0x52, 0x22 }, - { 0x53, 0x5e }, { 0x54, 0x80 }, - { 0x58, 0x9e }, - - { OV7670_REG_COM16, OV7670_COM16_AWBGAIN }, - { OV7670_REG_EDGE, 0 }, - { 0x75, 0x05 }, { 0x76, 0xe1 }, - { 0x4c, 0 }, { 0x77, 0x01 }, - { OV7670_REG_COM13, OV7670_COM13_GAMMA - | OV7670_COM13_UVSAT - | 2}, /* was 3 */ - { 0x4b, 0x09 }, - { 0xc9, 0x60 }, { OV7670_REG_COM16, 0x38 }, - { 0x56, 0x40 }, - - { 0x34, 0x11 }, - { OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO }, - { 0xa4, 0x88 }, { 0x96, 0 }, - { 0x97, 0x30 }, { 0x98, 0x20 }, - { 0x99, 0x30 }, { 0x9a, 0x84 }, - { 0x9b, 0x29 }, { 0x9c, 0x03 }, - { 0x9d, 0x4c }, { 0x9e, 0x3f }, - { 0x78, 0x04 }, - - /* Extra-weird stuff. Some sort of multiplexor register */ - { 0x79, 0x01 }, { 0xc8, 0xf0 }, - { 0x79, 0x0f }, { 0xc8, 0x00 }, - { 0x79, 0x10 }, { 0xc8, 0x7e }, - { 0x79, 0x0a }, { 0xc8, 0x80 }, - { 0x79, 0x0b }, { 0xc8, 0x01 }, - { 0x79, 0x0c }, { 0xc8, 0x0f }, - { 0x79, 0x0d }, { 0xc8, 0x20 }, - { 0x79, 0x09 }, { 0xc8, 0x80 }, - { 0x79, 0x02 }, { 0xc8, 0xc0 }, - { 0x79, 0x03 }, { 0xc8, 0x40 }, - { 0x79, 0x05 }, { 0xc8, 0x30 }, - { 0x79, 0x26 }, - }; PDEBUG(D_PROBE, "starting OV7xx0 configuration"); @@ -1011,8 +1198,9 @@ static int ov7xx0_configure(struct sd *sd) switch (low) { case 0x30: PDEBUG(D_PROBE, "Sensor is an OV7630/OV7635"); - sd->sensor = SEN_OV7630; - break; + PDEBUG(D_ERR, + "7630 is not supported by this driver"); + return -1; case 0x40: PDEBUG(D_PROBE, "Sensor is an OV7645"); sd->sensor = SEN_OV7640; /* FIXME */ @@ -1038,32 +1226,6 @@ static int ov7xx0_configure(struct sd *sd) return -1; } - switch (sd->sensor) { - case SEN_OV7620: - PDEBUG(D_PROBE, "Writing 7620 registers"); - if (write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620))) - return -1; - break; - case SEN_OV7630: - PDEBUG(D_ERR, "7630 is not supported by this driver version"); - return -1; - case SEN_OV7640: - PDEBUG(D_PROBE, "Writing 7640 registers"); - if (write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640))) - return -1; - break; - case SEN_OV7670: - PDEBUG(D_PROBE, "Writing 7670 registers"); - if (write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670))) - return -1; - break; - default: - PDEBUG(D_PROBE, "Writing 7610 registers"); - if (write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610))) - return -1; - break; - } - /* Set sensor-specific vars */ /* sd->sif = 0; already done */ return 0; @@ -1073,141 +1235,7 @@ static int ov7xx0_configure(struct sd *sd) static int ov6xx0_configure(struct sd *sd) { int rc; - static const struct ov_i2c_regvals norm_6x20[] = { - { 0x12, 0x80 }, /* reset */ - { 0x11, 0x01 }, - { 0x03, 0x60 }, - { 0x05, 0x7f }, /* For when autoadjust is off */ - { 0x07, 0xa8 }, - /* The ratio of 0x0c and 0x0d controls the white point */ - { 0x0c, 0x24 }, - { 0x0d, 0x24 }, - { 0x0f, 0x15 }, /* COMS */ - { 0x10, 0x75 }, /* AEC Exposure time */ - { 0x12, 0x24 }, /* Enable AGC */ - { 0x14, 0x04 }, - /* 0x16: 0x06 helps frame stability with moving objects */ - { 0x16, 0x06 }, -/* { 0x20, 0x30 }, * Aperture correction enable */ - { 0x26, 0xb2 }, /* BLC enable */ - /* 0x28: 0x05 Selects RGB format if RGB on */ - { 0x28, 0x05 }, - { 0x2a, 0x04 }, /* Disable framerate adjust */ -/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ - { 0x2d, 0x99 }, - { 0x33, 0xa0 }, /* Color Processing Parameter */ - { 0x34, 0xd2 }, /* Max A/D range */ - { 0x38, 0x8b }, - { 0x39, 0x40 }, - - { 0x3c, 0x39 }, /* Enable AEC mode changing */ - { 0x3c, 0x3c }, /* Change AEC mode */ - { 0x3c, 0x24 }, /* Disable AEC mode changing */ - - { 0x3d, 0x80 }, - /* These next two registers (0x4a, 0x4b) are undocumented. - * They control the color balance */ - { 0x4a, 0x80 }, - { 0x4b, 0x80 }, - { 0x4d, 0xd2 }, /* This reduces noise a bit */ - { 0x4e, 0xc1 }, - { 0x4f, 0x04 }, -/* Do 50-53 have any effect? */ -/* Toggle 0x12[2] off and on here? */ - }; - - static const struct ov_i2c_regvals norm_6x30[] = { - { 0x12, 0x80 }, /* Reset */ - { 0x00, 0x1f }, /* Gain */ - { 0x01, 0x99 }, /* Blue gain */ - { 0x02, 0x7c }, /* Red gain */ - { 0x03, 0xc0 }, /* Saturation */ - { 0x05, 0x0a }, /* Contrast */ - { 0x06, 0x95 }, /* Brightness */ - { 0x07, 0x2d }, /* Sharpness */ - { 0x0c, 0x20 }, - { 0x0d, 0x20 }, - { 0x0e, 0x20 }, - { 0x0f, 0x05 }, - { 0x10, 0x9a }, - { 0x11, 0x00 }, /* Pixel clock = fastest */ - { 0x12, 0x24 }, /* Enable AGC and AWB */ - { 0x13, 0x21 }, - { 0x14, 0x80 }, - { 0x15, 0x01 }, - { 0x16, 0x03 }, - { 0x17, 0x38 }, - { 0x18, 0xea }, - { 0x19, 0x04 }, - { 0x1a, 0x93 }, - { 0x1b, 0x00 }, - { 0x1e, 0xc4 }, - { 0x1f, 0x04 }, - { 0x20, 0x20 }, - { 0x21, 0x10 }, - { 0x22, 0x88 }, - { 0x23, 0xc0 }, /* Crystal circuit power level */ - { 0x25, 0x9a }, /* Increase AEC black ratio */ - { 0x26, 0xb2 }, /* BLC enable */ - { 0x27, 0xa2 }, - { 0x28, 0x00 }, - { 0x29, 0x00 }, - { 0x2a, 0x84 }, /* 60 Hz power */ - { 0x2b, 0xa8 }, /* 60 Hz power */ - { 0x2c, 0xa0 }, - { 0x2d, 0x95 }, /* Enable auto-brightness */ - { 0x2e, 0x88 }, - { 0x33, 0x26 }, - { 0x34, 0x03 }, - { 0x36, 0x8f }, - { 0x37, 0x80 }, - { 0x38, 0x83 }, - { 0x39, 0x80 }, - { 0x3a, 0x0f }, - { 0x3b, 0x3c }, - { 0x3c, 0x1a }, - { 0x3d, 0x80 }, - { 0x3e, 0x80 }, - { 0x3f, 0x0e }, - { 0x40, 0x00 }, /* White bal */ - { 0x41, 0x00 }, /* White bal */ - { 0x42, 0x80 }, - { 0x43, 0x3f }, /* White bal */ - { 0x44, 0x80 }, - { 0x45, 0x20 }, - { 0x46, 0x20 }, - { 0x47, 0x80 }, - { 0x48, 0x7f }, - { 0x49, 0x00 }, - { 0x4a, 0x00 }, - { 0x4b, 0x80 }, - { 0x4c, 0xd0 }, - { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ - { 0x4e, 0x40 }, - { 0x4f, 0x07 }, /* UV avg., col. killer: max */ - { 0x50, 0xff }, - { 0x54, 0x23 }, /* Max AGC gain: 18dB */ - { 0x55, 0xff }, - { 0x56, 0x12 }, - { 0x57, 0x81 }, - { 0x58, 0x75 }, - { 0x59, 0x01 }, /* AGC dark current comp.: +1 */ - { 0x5a, 0x2c }, - { 0x5b, 0x0f }, /* AWB chrominance levels */ - { 0x5c, 0x10 }, - { 0x3d, 0x80 }, - { 0x27, 0xa6 }, - { 0x12, 0x20 }, /* Toggle AWB */ - { 0x12, 0x24 }, - }; - - PDEBUG(D_PROBE, "starting sensor configuration"); - - if (init_ov_sensor(sd) < 0) { - PDEBUG(D_ERR, "Failed to read sensor ID."); - return -1; - } - PDEBUG(D_PROBE, "OV6xx0 sensor detected"); + PDEBUG(D_PROBE, "starting OV6xx0 configuration"); /* Detect sensor (sub)type */ rc = i2c_r(sd, OV7610_REG_COM_I); @@ -1251,15 +1279,6 @@ static int ov6xx0_configure(struct sd *sd) /* Set sensor-specific vars */ sd->sif = 1; - if (sd->sensor == SEN_OV6620) { - PDEBUG(D_PROBE, "Writing 6x20 registers"); - if (write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20))) - return -1; - } else { - PDEBUG(D_PROBE, "Writing 6x30 registers"); - if (write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30))) - return -1; - } return 0; } @@ -1298,22 +1317,31 @@ static int sd_config(struct gspca_dev *gspca_dev, ov51x_led_control(sd, 0); /* turn LED off */ /* Test for 76xx */ - sd->primary_i2c_slave = OV7xx0_SID; if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0) goto error; /* The OV519 must be more aggressive about sensor detection since * I2C write will never fail if the sensor is not present. We have * to try to initialize the sensor to detect its presence */ - if (init_ov_sensor(sd) < 0) { + if (init_ov_sensor(sd) >= 0) { + if (ov7xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV7xx0"); + goto error; + } + } else { + /* Test for 6xx0 */ - sd->primary_i2c_slave = OV6xx0_SID; if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0) goto error; - if (init_ov_sensor(sd) < 0) { + if (init_ov_sensor(sd) >= 0) { + if (ov6xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV6xx0"); + goto error; + } + } else { + /* Test for 8xx0 */ - sd->primary_i2c_slave = OV8xx0_SID; if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0) goto error; @@ -1321,24 +1349,13 @@ static int sd_config(struct gspca_dev *gspca_dev, PDEBUG(D_ERR, "Can't determine sensor slave IDs"); goto error; - } else { - if (ov8xx0_configure(sd) < 0) { - PDEBUG(D_ERR, - "Failed to configure OV8xx0 sensor"); - goto error; - } } - } else { - if (ov6xx0_configure(sd) < 0) { - PDEBUG(D_ERR, "Failed to configure OV6xx0"); + if (ov8xx0_configure(sd) < 0) { + PDEBUG(D_ERR, + "Failed to configure OV8xx0 sensor"); goto error; } } - } else { - if (ov7xx0_configure(sd) < 0) { - PDEBUG(D_ERR, "Failed to configure OV7xx0"); - goto error; - } } cam = &gspca_dev->cam; @@ -1355,15 +1372,53 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->colors = COLOR_DEF; sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; + if (sd->sensor != SEN_OV7670) + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) + | (1 << VFLIP_IDX); return 0; error: PDEBUG(D_ERR, "OV519 Config failed"); return -EBUSY; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + + /* initialize the sensor */ + switch (sd->sensor) { + case SEN_OV6620: + if (write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20))) + return -EIO; + break; + case SEN_OV6630: + if (write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30))) + return -EIO; + break; + default: +/* case SEN_OV7610: */ +/* case SEN_OV76BE: */ + if (write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610))) + return -EIO; + break; + case SEN_OV7620: + if (write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620))) + return -EIO; + break; + case SEN_OV7640: + if (write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640))) + return -EIO; + break; + case SEN_OV7670: + if (write_i2c_regvals(sd, norm_7670, ARRAY_SIZE(norm_7670))) + return -EIO; + break; + case SEN_OV8610: + if (write_i2c_regvals(sd, norm_8610, ARRAY_SIZE(norm_8610))) + return -EIO; + break; + } return 0; } @@ -1852,14 +1907,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) ov51x_led_control((struct sd *) gspca_dev, 0); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -2116,11 +2163,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -2157,6 +2202,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/pac207.c b/linux/drivers/media/video/gspca/pac207.c index 7ef18d578..34e923d00 100644 --- a/linux/drivers/media/video/gspca/pac207.c +++ b/linux/drivers/media/video/gspca/pac207.c @@ -56,12 +56,6 @@ MODULE_LICENSE("GPL"); #define PAC207_GAIN_KNEE 20 #define PAC207_AUTOGAIN_DEADZONE 30 -/* We calculating the autogain at the end of the transfer of a frame, at this - moment a frame with the old settings is being transmitted, and a frame is - being captured with the old settings. So if we adjust the autogain we must - ignore atleast the 2 next frames for the new settings to come into effect - before doing any other adjustments */ -#define PAC207_AUTOGAIN_IGNORE_FRAMES 3 /* specific webcam descriptor */ struct sd { @@ -131,7 +125,8 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, - .default_value = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, .flags = 0, }, .set = sd_setautogain, @@ -181,9 +176,6 @@ static const __u8 pac207_sensor_init[][8] = { /* 48 reg_72 Rate Control end BalSize_4a =0x36 */ static const __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 }; -static const unsigned char pac207_sof_marker[5] = - { 0xff, 0xff, 0x00, 0xff, 0x96 }; - static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, const u8 *buffer, u16 length) { @@ -259,35 +251,32 @@ static int sd_config(struct gspca_dev *gspca_dev, return -ENODEV; } - pac207_write_reg(gspca_dev, 0x41, 0x00); - /* Bit_0=Image Format, - * Bit_1=LED, - * Bit_2=Compression test mode enable */ - pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ - pac207_write_reg(gspca_dev, 0x11, 0x30); /* Analog Bias */ - PDEBUG(D_PROBE, "Pixart PAC207BCA Image Processor and Control Chip detected" " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x05; cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); sd->brightness = PAC207_BRIGHTNESS_DEFAULT; sd->exposure = PAC207_EXPOSURE_DEFAULT; sd->gain = PAC207_GAIN_DEFAULT; + sd->autogain = AUTOGAIN_DEF; return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; + pac207_write_reg(gspca_dev, 0x41, 0x00); + /* Bit_0=Image Format, + * Bit_1=LED, + * Bit_2=Compression test mode enable */ + pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ + pac207_write_reg(gspca_dev, 0x11, 0x30); /* Analog Bias */ - sd->autogain = 1; return 0; } @@ -343,14 +332,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev) pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ -} +/* Include pac common sof detection functions */ +#include "pac_common.h" static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) { @@ -365,33 +348,7 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, 100 + sd->brightness / 2, PAC207_AUTOGAIN_DEADZONE, PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE)) - sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES; -} - -static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev, - unsigned char *m, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - - /* Search for the SOF marker (fixed part) in the header */ - for (i = 0; i < len; i++) { - if (m[i] == pac207_sof_marker[sd->sof_read]) { - sd->sof_read++; - if (sd->sof_read == sizeof(pac207_sof_marker)) { - PDEBUG(D_STREAM, - "SOF found, bytes to analyze: %u." - " Frame starts at byte #%u", - len, i + 1); - sd->sof_read = 0; - return m + i + 1; - } - } else { - sd->sof_read = 0; - } - } - - return NULL; + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } static void sd_pkt_scan(struct gspca_dev *gspca_dev, @@ -402,14 +359,14 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - sof = pac207_find_sof(gspca_dev, data, len); + sof = pac_find_sof(gspca_dev, data, len); if (sof) { int n; /* finish decoding current frame */ n = sof - data; - if (n > sizeof pac207_sof_marker) - n -= sizeof pac207_sof_marker; + if (n > sizeof pac_sof_marker) + n -= sizeof pac_sof_marker; else n = 0; frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, @@ -537,7 +494,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) sd->gain = PAC207_GAIN_DEFAULT; if (gspca_dev->streaming) { sd->autogain_ignore_frames = - PAC207_AUTOGAIN_IGNORE_FRAMES; + PAC_AUTOGAIN_IGNORE_FRAMES; setexposure(gspca_dev); setgain(gspca_dev); } @@ -560,11 +517,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .dq_callback = pac207_do_auto_gain, .pkt_scan = sd_pkt_scan, }; @@ -579,6 +534,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2470)}, {USB_DEVICE(0x093a, 0x2471)}, {USB_DEVICE(0x093a, 0x2472)}, + {USB_DEVICE(0x093a, 0x2476)}, {USB_DEVICE(0x2001, 0xf115)}, {} }; @@ -597,6 +553,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/pac7311.c b/linux/drivers/media/video/gspca/pac7311.c index 1b3ea1e93..296ba82e6 100644 --- a/linux/drivers/media/video/gspca/pac7311.c +++ b/linux/drivers/media/video/gspca/pac7311.c @@ -19,6 +19,36 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* Some documentation about various registers as determined by trial and error. + When the register addresses differ between the 7202 and the 7311 the 2 + different addresses are written as 7302addr/7311addr, when one of the 2 + addresses is a - sign that register description is not valid for the + matching IC. + + Register page 1: + + Address Description + -/0x08 Unknown compressor related, must always be 8 except when not + in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! + -/0x1b Auto white balance related, bit 0 is AWB enable (inverted) + bits 345 seem to toggle per color gains on/off (inverted) + 0x78 Global control, bit 6 controls the LED (inverted) + -/0x80 JPEG compression ratio ? Best not touched + + Register page 3/4: + + Address Description + 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on + the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + -/0x0f Master gain 1-245, low value = high gain + 0x10/- Master gain 0-31 + -/0x10 Another gain 0-15, limited influence (1-2x gain I guess) + 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to + completely disable the analog amplification block. Set to 0x68 + for max gain, 0x14 for minimal gain. +*/ + #define MODULE_NAME "pac7311" #include "gspca.h" @@ -31,18 +61,23 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - int lum_sum; - atomic_t avg_lum; - atomic_t do_gain; - unsigned char brightness; unsigned char contrast; unsigned char colors; + unsigned char gain; + unsigned char exposure; unsigned char autogain; + __u8 hflip; + __u8 vflip; + + __u8 sensor; +#define SENSOR_PAC7302 0 +#define SENSOR_PAC7311 1 - char ffseq; - signed char ag_cnt; -#define AG_CNT_START 13 + u8 sof_read; + u8 autogain_ignore_frames; + + atomic_t avg_lum; }; /* V4L2 controls supported by the driver */ @@ -54,8 +89,18 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { +/* This control is pac7302 only */ +#define BRIGHTNESS_IDX 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -71,13 +116,15 @@ static struct ctrl sd_ctrls[] = { .set = sd_setbrightness, .get = sd_getbrightness, }, +/* This control is for both the 7302 and the 7311 */ { { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", .minimum = 0, - .maximum = 255, +#define CONTRAST_MAX 255 + .maximum = CONTRAST_MAX, .step = 1, #define CONTRAST_DEF 127 .default_value = CONTRAST_DEF, @@ -85,13 +132,16 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, +/* This control is pac7302 only */ +#define SATURATION_IDX 2 { { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Color", + .name = "Saturation", .minimum = 0, - .maximum = 255, +#define COLOR_MAX 255 + .maximum = COLOR_MAX, .step = 1, #define COLOR_DEF 127 .default_value = COLOR_DEF, @@ -99,6 +149,39 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcolors, .get = sd_getcolors, }, +/* All controls below are for both the 7302 and the 7311 */ + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, +#define GAIN_MAX 255 + .maximum = GAIN_MAX, + .step = 1, +#define GAIN_DEF 127 +#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, +#define EXPOSURE_MAX 255 + .maximum = EXPOSURE_MAX, + .step = 1, +#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ +#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ + .default_value = EXPOSURE_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, { { .id = V4L2_CID_AUTOGAIN, @@ -113,101 +196,207 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEF 0 + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, }; static struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 2}, - {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + {320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 1}, - {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 640, .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; -#define PAC7311_JPEG_HEADER_SIZE (sizeof pac7311_jpeg_header) /* (594) */ - -static const __u8 pac7311_jpeg_header[] = { - 0xff, 0xd8, - 0xff, 0xe0, 0x00, 0x03, 0x20, - 0xff, 0xc0, 0x00, 0x11, 0x08, - 0x01, 0xe0, /* 12: height */ - 0x02, 0x80, /* 14: width */ - 0x03, /* 16 */ - 0x01, 0x21, 0x00, - 0x02, 0x11, 0x01, - 0x03, 0x11, 0x01, - 0xff, 0xdb, 0x00, 0x84, - 0x00, 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d, - 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18, 0x16, - 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d, 0x28, 0x3a, 0x33, 0x3d, - 0x3c, 0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, - 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, 0x5f, - 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, 0x79, 0x70, 0x64, - 0x78, 0x5c, 0x65, 0x67, 0x63, 0x01, 0x11, 0x12, 0x12, 0x18, - 0x15, 0x18, 0x2f, 0x1a, 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, - 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, - 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, - 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, - 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, - 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, - 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, - 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, - 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, - 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, - 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, - 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, - 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, - 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, - 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, - 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, - 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, - 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, - 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, - 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, - 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, - 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, - 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, - 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, - 0x11, 0x00, 0x3f, 0x00 +/* pac 7302 */ +static const __u8 init_7302[] = { +/* index,value */ + 0xff, 0x01, /* page 1 */ + 0x78, 0x00, /* deactivate */ + 0xff, 0x01, + 0x78, 0x40, /* led off */ +}; +static const __u8 start_7302[] = { +/* index, len, [value]* */ + 0xff, 1, 0x00, /* page 0 */ + 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, + 0x00, 0x00, 0x00, 0x00, + 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00, + 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7, + 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11, + 0x26, 2, 0xaa, 0xaa, + 0x2e, 1, 0x31, + 0x38, 1, 0x01, + 0x3a, 3, 0x14, 0xff, 0x5a, + 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, + 0x00, 0x54, 0x11, + 0x55, 1, 0x00, + 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, + 0x6b, 1, 0x00, + 0x6e, 3, 0x08, 0x06, 0x00, + 0x72, 3, 0x00, 0xff, 0x00, + 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c, + 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50, + 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00, + 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9, + 0xd2, 0xeb, + 0xaf, 1, 0x02, + 0xb5, 2, 0x08, 0x08, + 0xb8, 2, 0x08, 0x88, + 0xc4, 4, 0xae, 0x01, 0x04, 0x01, + 0xcc, 1, 0x00, + 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9, + 0xc1, 0xd7, 0xec, + 0xdc, 1, 0x01, + 0xff, 1, 0x01, /* page 1 */ + 0x12, 3, 0x02, 0x00, 0x01, + 0x3e, 2, 0x00, 0x00, + 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2, + 0x7c, 1, 0x00, + 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20, + 0x02, 0x00, + 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04, + 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x01, 0x07, 0x04, 0x01, + 0xd8, 1, 0x01, + 0xdb, 2, 0x00, 0x01, + 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00, + 0xe6, 4, 0x00, 0x00, 0x00, 0x01, + 0xeb, 1, 0x00, + 0xff, 1, 0x02, /* page 2 */ + 0x22, 1, 0x00, + 0xff, 1, 0x03, /* page 3 */ + 0x00, 255, /* load the page 3 */ + 0x11, 1, 0x01, + 0xff, 1, 0x02, /* page 2 */ + 0x13, 1, 0x00, + 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96, + 0x27, 2, 0x14, 0x0c, + 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22, + 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44, + 0x6e, 1, 0x08, + 0xff, 1, 0x01, /* page 1 */ + 0x78, 1, 0x00, + 0, 0 /* end of sequence */ +}; + +/* page 3 - the value 0xaa says skip the index - see reg_w_page() */ +static const __u8 page3_7302[] = { + 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16, + 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, + 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54, + 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, + 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, + 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00, + 0x00 +}; + +/* pac 7311 */ +static const __u8 init_7311[] = { + 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ + 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ + 0x78, 0x44, /* Bit_0=start stream, Bit_6=LED */ + 0xff, 0x04, + 0x27, 0x80, + 0x28, 0xca, + 0x29, 0x53, + 0x2a, 0x0e, + 0xff, 0x01, + 0x3e, 0x20, +}; + +static const __u8 start_7311[] = { +/* index, len, [value]* */ + 0xff, 1, 0x01, /* page 1 */ + 0x02, 43, 0x48, 0x0a, 0x40, 0x08, 0x00, 0x00, 0x08, 0x00, + 0x06, 0xff, 0x11, 0xff, 0x5a, 0x30, 0x90, 0x4c, + 0x00, 0x07, 0x00, 0x0a, 0x10, 0x00, 0xa0, 0x10, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x3e, 42, 0x00, 0x00, 0x78, 0x52, 0x4a, 0x52, 0x78, 0x6e, + 0x48, 0x46, 0x48, 0x6e, 0x5f, 0x49, 0x42, 0x49, + 0x5f, 0x5f, 0x49, 0x42, 0x49, 0x5f, 0x6e, 0x48, + 0x46, 0x48, 0x6e, 0x78, 0x52, 0x4a, 0x52, 0x78, + 0x00, 0x00, 0x09, 0x1b, 0x34, 0x49, 0x5c, 0x9b, + 0xd0, 0xff, + 0x78, 6, 0x44, 0x00, 0xf2, 0x01, 0x01, 0x80, + 0x7f, 18, 0x2a, 0x1c, 0x00, 0xc8, 0x02, 0x58, 0x03, 0x84, + 0x12, 0x00, 0x1a, 0x04, 0x08, 0x0c, 0x10, 0x14, + 0x18, 0x20, + 0x96, 3, 0x01, 0x08, 0x04, + 0xa0, 4, 0x44, 0x44, 0x44, 0x04, + 0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00, + 0x3f, 0x00, 0x0a, 0x01, 0x00, + 0xff, 1, 0x04, /* page 4 */ + 0x00, 254, /* load the page 4 */ + 0x11, 1, 0x01, + 0, 0 /* end of sequence */ +}; + +/* page 4 - the value 0xaa says skip the index - see reg_w_page() */ +static const __u8 page4_7311[] = { + 0xaa, 0xaa, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f, + 0x09, 0x00, 0xaa, 0xaa, 0x07, 0x00, 0x00, 0x62, + 0x08, 0xaa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, 0xaa, + 0xaa, 0x00, 0x08, 0xaa, 0x03, 0xaa, 0x00, 0x68, + 0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x23, 0x28, 0x04, 0x11, 0x00, 0x00 }; static void reg_w_buf(struct gspca_dev *gspca_dev, - __u16 index, - const char *buffer, __u16 len) + __u8 index, + const char *buffer, int len) { memcpy(gspca_dev->usb_buf, buffer, len); usb_control_msg(gspca_dev->dev, @@ -219,8 +408,9 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, 500); } +#if 0 /* not used */ static __u8 reg_r(struct gspca_dev *gspca_dev, - __u16 index) + __u8 index) { usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), @@ -231,9 +421,10 @@ static __u8 reg_r(struct gspca_dev *gspca_dev, 500); return gspca_dev->usb_buf[0]; } +#endif static void reg_w(struct gspca_dev *gspca_dev, - __u16 index, + __u8 index, __u8 value) { gspca_dev->usb_buf[0] = value; @@ -241,10 +432,78 @@ static void reg_w(struct gspca_dev *gspca_dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, gspca_dev->usb_buf, 1, + 0, index, gspca_dev->usb_buf, 1, 500); } +static void reg_w_seq(struct gspca_dev *gspca_dev, + const __u8 *seq, int len) +{ + while (--len >= 0) { + reg_w(gspca_dev, seq[0], seq[1]); + seq += 2; + } +} + +/* load the beginning of a page */ +static void reg_w_page(struct gspca_dev *gspca_dev, + const __u8 *page, int len) +{ + int index; + + for (index = 0; index < len; index++) { + if (page[index] == 0xaa) /* skip this index */ + continue; + gspca_dev->usb_buf[0] = page[index]; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + } +} + +/* output a variable sequence */ +static void reg_w_var(struct gspca_dev *gspca_dev, + const __u8 *seq) +{ + int index, len; + + for (;;) { + index = *seq++; + len = *seq++; + switch (len) { + case 0: + return; + case 254: + reg_w_page(gspca_dev, page4_7311, sizeof page4_7311); + break; + case 255: + reg_w_page(gspca_dev, page3_7302, sizeof page3_7302); + break; + default: + if (len > 64) { + PDEBUG(D_ERR|D_STREAM, + "Incorrect variable sequence"); + return; + } + while (len > 0) { + if (len < 8) { + reg_w_buf(gspca_dev, index, seq, len); + seq += len; + break; + } + reg_w_buf(gspca_dev, index, seq, 8); + seq += 8; + index += 8; + len -= 8; + } + } + } + /* not reached */ +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -252,203 +511,245 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - PDEBUG(D_CONF, "Find Sensor PAC7311"); - reg_w(gspca_dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x27, 0x80); - reg_w(gspca_dev, 0x28, 0xca); - reg_w(gspca_dev, 0x29, 0x53); - reg_w(gspca_dev, 0x2a, 0x0e); - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x3e, 0x20); - cam = &gspca_dev->cam; cam->epaddr = 0x05; - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); + + sd->sensor = id->driver_info; + if (sd->sensor == SENSOR_PAC7302) { + PDEBUG(D_CONF, "Find Sensor PAC7302"); + cam->cam_mode = &vga_mode[2]; /* only 640x480 */ + cam->nmodes = 1; + } else { + PDEBUG(D_CONF, "Find Sensor PAC7311"); + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) + | (1 << SATURATION_IDX); + } sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPOSURE_DEF; sd->autogain = AUTOGAIN_DEF; - sd->ag_cnt = -1; + sd->hflip = HFLIP_DEF; + sd->vflip = VFLIP_DEF; return 0; } -static void setbrightness(struct gspca_dev *gspca_dev) +/* This function is used by pac7302 only */ +static void setbrightcont(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, v; + static const __u8 max[10] = + {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, + 0xd4, 0xec}; + static const __u8 delta[10] = + {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, + 0x11, 0x0b}; + + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 10; i++) { + v = max[i]; + v += (sd->brightness - BRIGHTNESS_MAX) + * 150 / BRIGHTNESS_MAX; /* 200 ? */ + v -= delta[i] * sd->contrast / CONTRAST_MAX; + if (v < 0) + v = 0; + else if (v > 0xff) + v = 0xff; + reg_w(gspca_dev, 0xa2 + i, v); + } + reg_w(gspca_dev, 0xdc, 0x01); +} + +/* This function is used by pac7311 only */ +static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int brightness; -/*jfm: inverted?*/ - brightness = BRIGHTNESS_MAX - sd->brightness; reg_w(gspca_dev, 0xff, 0x04); -/* reg_w(gspca_dev, 0x0e, 0x00); */ - reg_w(gspca_dev, 0x0f, brightness); + reg_w(gspca_dev, 0x10, sd->contrast >> 4); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); - PDEBUG(D_CONF|D_STREAM, "brightness: %i", brightness); } -static void setcontrast(struct gspca_dev *gspca_dev) +/* This function is used by pac7302 only */ +static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int i, v; + static const int a[9] = + {217, -212, 0, -101, 170, -67, -38, -315, 355}; + static const int b[9] = + {19, 106, 0, 19, 106, 1, 19, 106, 1}; - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x80, sd->contrast); - /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ reg_w(gspca_dev, 0x11, 0x01); - PDEBUG(D_CONF|D_STREAM, "contrast: %i", sd->contrast); + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 9; i++) { + v = a[i] * sd->colors / COLOR_MAX + b[i]; + reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); + reg_w(gspca_dev, 0x0f + 2 * i + 1, v); + } + reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); } -static void setcolors(struct gspca_dev *gspca_dev) +static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x10, sd->colors); + if (sd->sensor == SENSOR_PAC7302) { + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + reg_w(gspca_dev, 0x10, sd->gain >> 3); + } else { + int gain = GAIN_MAX - sd->gain; + if (gain < 1) + gain = 1; + else if (gain > 245) + gain = 245; + reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + reg_w(gspca_dev, 0x0e, 0x00); + reg_w(gspca_dev, 0x0f, gain); + } /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); - PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); } -static void setautogain(struct gspca_dev *gspca_dev) +static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + __u8 reg; + + /* register 2 of frame 3/4 contains the clock divider configuring the + no fps according to the formula: 60 / reg. sd->exposure is the + desired exposure time in ms. */ + reg = 120 * sd->exposure / 1000; + if (reg < 2) + reg = 2; + else if (reg > 63) + reg = 63; + + if (sd->sensor == SENSOR_PAC7302) { + /* On the pac7302 reg2 MUST be a multiple of 3, so round it to + the nearest multiple of 3, except when between 6 and 12? */ + if (reg < 6 || reg > 12) + reg = ((reg + 1) / 3) * 3; + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + reg_w(gspca_dev, 0x02, reg); + } else { + reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + reg_w(gspca_dev, 0x02, reg); + /* Page 1 register 8 must always be 0x08 except when not in + 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */ + reg_w(gspca_dev, 0xff, 0x01); + if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && + reg <= 3) + reg_w(gspca_dev, 0x08, 0x09); + else + reg_w(gspca_dev, 0x08, 0x08); + } + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); +} - if (sd->autogain) { - sd->lum_sum = 0; - sd->ag_cnt = AG_CNT_START; +static void sethvflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 data; + + if (sd->sensor == SENSOR_PAC7302) { + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + data = (sd->hflip ? 0x08 : 0x00) + | (sd->vflip ? 0x04 : 0x00); } else { - sd->ag_cnt = -1; + reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + data = (sd->hflip ? 0x04 : 0x00) + | (sd->vflip ? 0x08 : 0x00); } + reg_w(gspca_dev, 0x21, data); + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { - reg_w(gspca_dev, 0x78, 0x00); /* Turn on LED */ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAC7302) + reg_w_seq(gspca_dev, init_7302, sizeof init_7302); + else + reg_w_seq(gspca_dev, init_7311, sizeof init_7311); + return 0; } static void sd_start(struct gspca_dev *gspca_dev) { - reg_w(gspca_dev, 0xff, 0x01); - reg_w_buf(gspca_dev, 0x0002, "\x48\x0a\x40\x08\x00\x00\x08\x00", 8); - reg_w_buf(gspca_dev, 0x000a, "\x06\xff\x11\xff\x5a\x30\x90\x4c", 8); - reg_w_buf(gspca_dev, 0x0012, "\x00\x07\x00\x0a\x10\x00\xa0\x10", 8); - reg_w_buf(gspca_dev, 0x001a, "\x02\x00\x00\x00\x00\x0b\x01\x00", 8); - reg_w_buf(gspca_dev, 0x0022, "\x00\x00\x00\x00\x00\x00\x00\x00", 8); - reg_w_buf(gspca_dev, 0x002a, "\x00\x00\x00", 3); - reg_w_buf(gspca_dev, 0x003e, "\x00\x00\x78\x52\x4a\x52\x78\x6e", 8); - reg_w_buf(gspca_dev, 0x0046, "\x48\x46\x48\x6e\x5f\x49\x42\x49", 8); - reg_w_buf(gspca_dev, 0x004e, "\x5f\x5f\x49\x42\x49\x5f\x6e\x48", 8); - reg_w_buf(gspca_dev, 0x0056, "\x46\x48\x6e\x78\x52\x4a\x52\x78", 8); - reg_w_buf(gspca_dev, 0x005e, "\x00\x00\x09\x1b\x34\x49\x5c\x9b", 8); - reg_w_buf(gspca_dev, 0x0066, "\xd0\xff", 2); - reg_w_buf(gspca_dev, 0x0078, "\x44\x00\xf2\x01\x01\x80", 6); - reg_w_buf(gspca_dev, 0x007f, "\x2a\x1c\x00\xc8\x02\x58\x03\x84", 8); - reg_w_buf(gspca_dev, 0x0087, "\x12\x00\x1a\x04\x08\x0c\x10\x14", 8); - reg_w_buf(gspca_dev, 0x008f, "\x18\x20", 2); - reg_w_buf(gspca_dev, 0x0096, "\x01\x08\x04", 3); - reg_w_buf(gspca_dev, 0x00a0, "\x44\x44\x44\x04", 4); - reg_w_buf(gspca_dev, 0x00f0, "\x01\x00\x00\x00\x22\x00\x20\x00", 8); - reg_w_buf(gspca_dev, 0x00f8, "\x3f\x00\x0a\x01\x00", 5); + struct sd *sd = (struct sd *) gspca_dev; - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x02, 0x04); - reg_w(gspca_dev, 0x03, 0x54); - reg_w(gspca_dev, 0x04, 0x07); - reg_w(gspca_dev, 0x05, 0x2b); - reg_w(gspca_dev, 0x06, 0x09); - reg_w(gspca_dev, 0x07, 0x0f); - reg_w(gspca_dev, 0x08, 0x09); - reg_w(gspca_dev, 0x09, 0x00); - reg_w(gspca_dev, 0x0c, 0x07); - reg_w(gspca_dev, 0x0d, 0x00); - reg_w(gspca_dev, 0x0e, 0x00); - reg_w(gspca_dev, 0x0f, 0x62); - reg_w(gspca_dev, 0x10, 0x08); - reg_w(gspca_dev, 0x12, 0x07); - reg_w(gspca_dev, 0x13, 0x00); - reg_w(gspca_dev, 0x14, 0x00); - reg_w(gspca_dev, 0x15, 0x00); - reg_w(gspca_dev, 0x16, 0x00); - reg_w(gspca_dev, 0x17, 0x00); - reg_w(gspca_dev, 0x18, 0x00); - reg_w(gspca_dev, 0x19, 0x00); - reg_w(gspca_dev, 0x1a, 0x00); - reg_w(gspca_dev, 0x1b, 0x03); - reg_w(gspca_dev, 0x1c, 0xa0); - reg_w(gspca_dev, 0x1d, 0x01); - reg_w(gspca_dev, 0x1e, 0xf4); - reg_w(gspca_dev, 0x21, 0x00); - reg_w(gspca_dev, 0x22, 0x08); - reg_w(gspca_dev, 0x24, 0x03); - reg_w(gspca_dev, 0x26, 0x00); - reg_w(gspca_dev, 0x27, 0x01); - reg_w(gspca_dev, 0x28, 0xca); - reg_w(gspca_dev, 0x29, 0x10); - reg_w(gspca_dev, 0x2a, 0x06); - reg_w(gspca_dev, 0x2b, 0x78); - reg_w(gspca_dev, 0x2c, 0x00); - reg_w(gspca_dev, 0x2d, 0x00); - reg_w(gspca_dev, 0x2e, 0x00); - reg_w(gspca_dev, 0x2f, 0x00); - reg_w(gspca_dev, 0x30, 0x23); - reg_w(gspca_dev, 0x31, 0x28); - reg_w(gspca_dev, 0x32, 0x04); - reg_w(gspca_dev, 0x33, 0x11); - reg_w(gspca_dev, 0x34, 0x00); - reg_w(gspca_dev, 0x35, 0x00); - reg_w(gspca_dev, 0x11, 0x01); - setcontrast(gspca_dev); - setbrightness(gspca_dev); - setcolors(gspca_dev); - setautogain(gspca_dev); + sd->sof_read = 0; + + if (sd->sensor == SENSOR_PAC7302) { + reg_w_var(gspca_dev, start_7302); + setbrightcont(gspca_dev); + setcolors(gspca_dev); + } else { + reg_w_var(gspca_dev, start_7311); + setcontrast(gspca_dev); + } + setgain(gspca_dev); + setexposure(gspca_dev); + sethvflip(gspca_dev); /* set correct resolution */ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - case 2: /* 160x120 */ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x02, 0x03); + case 2: /* 160x120 pac7311 */ reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x08, 0x09); reg_w(gspca_dev, 0x17, 0x20); - reg_w(gspca_dev, 0x1b, 0x00); -/* reg_w(gspca_dev, 0x80, 0x69); */ reg_w(gspca_dev, 0x87, 0x10); break; - case 1: /* 320x240 */ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x02, 0x03); + case 1: /* 320x240 pac7311 */ reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x08, 0x09); reg_w(gspca_dev, 0x17, 0x30); -/* reg_w(gspca_dev, 0x80, 0x3f); */ reg_w(gspca_dev, 0x87, 0x11); break; case 0: /* 640x480 */ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x02, 0x03); + if (sd->sensor == SENSOR_PAC7302) + break; reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x08, 0x08); reg_w(gspca_dev, 0x17, 0x00); -/* reg_w(gspca_dev, 0x80, 0x1c); */ reg_w(gspca_dev, 0x87, 0x12); break; } + sd->sof_read = 0; + sd->autogain_ignore_frames = 0; + atomic_set(&sd->avg_lum, -1); + /* start stream */ reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x04); - reg_w(gspca_dev, 0x78, 0x05); + if (sd->sensor == SENSOR_PAC7302) + reg_w(gspca_dev, 0x78, 0x01); + else + reg_w(gspca_dev, 0x78, 0x05); } static void sd_stopN(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAC7302) { + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x00); + reg_w(gspca_dev, 0x78, 0x00); + return; + } reg_w(gspca_dev, 0xff, 0x04); reg_w(gspca_dev, 0x27, 0x80); reg_w(gspca_dev, 0x28, 0xca); @@ -456,200 +757,147 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x2a, 0x0e); reg_w(gspca_dev, 0xff, 0x01); reg_w(gspca_dev, 0x3e, 0x20); - reg_w(gspca_dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ } static void sd_stop0(struct gspca_dev *gspca_dev) { -} + struct sd *sd = (struct sd *) gspca_dev; -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x27, 0x80); - reg_w(gspca_dev, 0x28, 0xca); - reg_w(gspca_dev, 0x29, 0x53); - reg_w(gspca_dev, 0x2a, 0x0e); - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x3e, 0x20); - reg_w(gspca_dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + if (sd->sensor == SENSOR_PAC7302) { + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x40); + } } +/* Include pac common sof detection functions */ +#include "pac_common.h" + static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int luma; - int luma_mean = 128; - int luma_delta = 20; - __u8 spring = 5; - int Gbright; + int avg_lum = atomic_read(&sd->avg_lum); + int desired_lum, deadzone; - if (!atomic_read(&sd->do_gain)) + if (avg_lum == -1) return; - atomic_set(&sd->do_gain, 0); - - luma = atomic_read(&sd->avg_lum); - Gbright = reg_r(gspca_dev, 0x02); - PDEBUG(D_FRAM, "luma mean %d", luma); - if (luma < luma_mean - luma_delta || - luma > luma_mean + luma_delta) { - Gbright += (luma_mean - luma) >> spring; - if (Gbright > 0x1a) - Gbright = 0x1a; - else if (Gbright < 4) - Gbright = 4; - PDEBUG(D_FRAM, "gbright %d", Gbright); - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x0f, Gbright); - /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + + if (sd->sensor == SENSOR_PAC7302) { + desired_lum = 270 + sd->brightness * 4; + /* Hack hack, with the 7202 the first exposure step is + pretty large, so if we're about to make the first + exposure increase make the deadzone large to avoid + oscilating */ + if (desired_lum > avg_lum && sd->gain == GAIN_DEF && + sd->exposure > EXPOSURE_DEF && + sd->exposure < 42) + deadzone = 90; + else + deadzone = 30; + } else { + desired_lum = 200; + deadzone = 20; } + + if (sd->autogain_ignore_frames > 0) + sd->autogain_ignore_frames--; + else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, + deadzone, GAIN_KNEE, EXPOSURE_KNEE)) + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } +static const unsigned char pac7311_jpeg_header1[] = { + 0xff, 0xd8, 0xff, 0xc0, 0x00, 0x11, 0x08 +}; + +static const unsigned char pac7311_jpeg_header2[] = { + 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, + 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +}; + +/* this function is run at interrupt level */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; - unsigned char tmpbuf[4]; - int i, p, ffseq; - -/* if (len < 5) { */ - if (len < 6) { -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ - return; - } - - ffseq = sd->ffseq; - - for (p = 0; p < len - 6; p++) { - if ((data[0 + p] == 0xff) - && (data[1 + p] == 0xff) - && (data[2 + p] == 0x00) - && (data[3 + p] == 0xff) - && (data[4 + p] == 0x96)) { - - /* start of frame */ - if (sd->ag_cnt >= 0 && p > 28) { - sd->lum_sum += data[p - 23]; - if (--sd->ag_cnt < 0) { - sd->ag_cnt = AG_CNT_START; - atomic_set(&sd->avg_lum, - sd->lum_sum / AG_CNT_START); - sd->lum_sum = 0; - atomic_set(&sd->do_gain, 1); - } - } + unsigned char *sof; + + sof = pac_find_sof(gspca_dev, data, len); + if (sof) { + unsigned char tmpbuf[4]; + int n, lum_offset, footer_length; + + if (sd->sensor == SENSOR_PAC7302) { + /* 6 bytes after the FF D9 EOF marker a number of lumination + bytes are send corresponding to different parts of the + image, the 14th and 15th byte after the EOF seem to + correspond to the center of the image */ + lum_offset = 61 + sizeof pac_sof_marker; + footer_length = 74; + } else { + lum_offset = 24 + sizeof pac_sof_marker; + footer_length = 26; + } - /* copy the end of data to the current frame */ + /* Finish decoding current frame */ + n = (sof - data) - (footer_length + sizeof pac_sof_marker); + if (n < 0) { + frame->data_end += n; + n = 0; + } + frame = gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, n); + if (gspca_dev->last_packet_type != DISCARD_PACKET && + frame->data_end[-2] == 0xff && + frame->data_end[-1] == 0xd9) frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, p); - - /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - (unsigned char *) pac7311_jpeg_header, - 12); + NULL, 0); + + n = sof - data; + len -= n; + data = sof; + + /* Get average lumination */ + if (gspca_dev->last_packet_type == LAST_PACKET && + n >= lum_offset) + atomic_set(&sd->avg_lum, data[-lum_offset] + + data[-lum_offset + 1]); + else + atomic_set(&sd->avg_lum, -1); + + /* Start the new frame with the jpeg header */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + pac7311_jpeg_header1, sizeof(pac7311_jpeg_header1)); + if (sd->sensor == SENSOR_PAC7302) { + /* The PAC7302 has the image rotated 90 degrees */ + tmpbuf[0] = gspca_dev->width >> 8; + tmpbuf[1] = gspca_dev->width & 0xff; + tmpbuf[2] = gspca_dev->height >> 8; + tmpbuf[3] = gspca_dev->height & 0xff; + } else { tmpbuf[0] = gspca_dev->height >> 8; tmpbuf[1] = gspca_dev->height & 0xff; tmpbuf[2] = gspca_dev->width >> 8; tmpbuf[3] = gspca_dev->width & 0xff; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - tmpbuf, 4); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - (unsigned char *) &pac7311_jpeg_header[16], - PAC7311_JPEG_HEADER_SIZE - 16); - - data += p + 7; - len -= p + 7; - ffseq = 0; - break; - } - } - - /* remove the 'ff ff ff xx' sequences */ - switch (ffseq) { - case 3: - data += 1; - len -= 1; - break; - case 2: - if (data[0] == 0xff) { - data += 2; - len -= 2; - frame->data_end -= 2; - } - break; - case 1: - if (data[0] == 0xff - && data[1] == 0xff) { - data += 3; - len -= 3; - frame->data_end -= 1; - } - break; - } - for (i = 0; i < len - 4; i++) { - if (data[i] == 0xff - && data[i + 1] == 0xff - && data[i + 2] == 0xff) { - memmove(&data[i], &data[i + 4], len - i - 4); - len -= 4; } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, tmpbuf, 4); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + pac7311_jpeg_header2, sizeof(pac7311_jpeg_header2)); } - ffseq = 0; - if (data[len - 4] == 0xff) { - if (data[len - 3] == 0xff - && data[len - 2] == 0xff) { - len -= 4; - } - } else if (data[len - 3] == 0xff) { - if (data[len - 2] == 0xff - && data[len - 1] == 0xff) - ffseq = 3; - } else if (data[len - 2] == 0xff) { - if (data[len - 1] == 0xff) - ffseq = 2; - } else if (data[len - 1] == 0xff) - ffseq = 1; - sd->ffseq = ffseq; gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } -static void getbrightness(struct gspca_dev *gspca_dev) -{ -/* sd->brightness = reg_r(gspca_dev, 0x08); - return sd->brightness; */ -/* PDEBUG(D_CONF, "Called pac7311_getbrightness: Not implemented yet"); */ -} - -#if 0 -static void getcontrast(struct gspca_dev *gspca_dev) -{ -/* sd->contrast = reg_r(gspca_dev, 0x0e); - return sd->contrast; */ - PDEBUG(D_CONF, "getcontrast: Not implemented yet"); -} -#endif - -#if 0 -static void getcolors(struct gspca_dev *gspca_dev) -{ -} -#endif - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; sd->brightness = val; if (gspca_dev->streaming) - setbrightness(gspca_dev); + setbrightcont(gspca_dev); return 0; } @@ -657,7 +905,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -667,8 +914,12 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->contrast = val; - if (gspca_dev->streaming) - setcontrast(gspca_dev); + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_PAC7302) + setbrightcont(gspca_dev); + else + setcontrast(gspca_dev); + } return 0; } @@ -676,7 +927,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; -/* getcontrast(gspca_dev); */ *val = sd->contrast; return 0; } @@ -695,18 +945,66 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; -/* getcolors(gspca_dev); */ *val = sd->colors; return 0; } +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; sd->autogain = val; - if (gspca_dev->streaming) - setautogain(gspca_dev); + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + if (sd->autogain) { + sd->exposure = EXPOSURE_DEF; + sd->gain = GAIN_DEF; + if (gspca_dev->streaming) { + sd->autogain_ignore_frames = + PAC_AUTOGAIN_IGNORE_FRAMES; + setexposure(gspca_dev); + setgain(gspca_dev); + } + } + return 0; } @@ -718,30 +1016,68 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + /* sub-driver description */ static struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, }; /* -- module initialisation -- */ static __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x093a, 0x2600)}, - {USB_DEVICE(0x093a, 0x2601)}, - {USB_DEVICE(0x093a, 0x2603)}, - {USB_DEVICE(0x093a, 0x2608)}, - {USB_DEVICE(0x093a, 0x260e)}, - {USB_DEVICE(0x093a, 0x260f)}, - {USB_DEVICE(0x093a, 0x2621)}, + {USB_DEVICE(0x093a, 0x2600), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x2601), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x2603), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311}, + {USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -759,6 +1095,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/pac_common.h b/linux/drivers/media/video/gspca/pac_common.h new file mode 100644 index 000000000..34d4b1494 --- /dev/null +++ b/linux/drivers/media/video/gspca/pac_common.h @@ -0,0 +1,60 @@ +/* + * Pixart PAC207BCA / PAC73xx common functions + * + * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* We calculate the autogain at the end of the transfer of a frame, at this + moment a frame with the old settings is being transmitted, and a frame is + being captured with the old settings. So if we adjust the autogain we must + ignore atleast the 2 next frames for the new settings to come into effect + before doing any other adjustments */ +#define PAC_AUTOGAIN_IGNORE_FRAMES 3 + +static const unsigned char pac_sof_marker[5] = + { 0xff, 0xff, 0x00, 0xff, 0x96 }; + +static unsigned char *pac_find_sof(struct gspca_dev *gspca_dev, + unsigned char *m, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + + /* Search for the SOF marker (fixed part) in the header */ + for (i = 0; i < len; i++) { + if (m[i] == pac_sof_marker[sd->sof_read]) { + sd->sof_read++; + if (sd->sof_read == sizeof(pac_sof_marker)) { + PDEBUG(D_FRAM, + "SOF found, bytes to analyze: %u." + " Frame starts at byte #%u", + len, i + 1); + sd->sof_read = 0; + return m + i + 1; + } + } else { + sd->sof_read = 0; + } + } + + return NULL; +} diff --git a/linux/drivers/media/video/gspca/sonixb.c b/linux/drivers/media/video/gspca/sonixb.c index 4d979fa39..53cb724bf 100644 --- a/linux/drivers/media/video/gspca/sonixb.c +++ b/linux/drivers/media/video/gspca/sonixb.c @@ -20,6 +20,26 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* Some documentation on known sonixb registers: + +Reg Use +0x10 high nibble red gain low nibble blue gain +0x11 low nibble green gain +0x12 hstart +0x13 vstart +0x15 hsize (hsize = register-value * 16) +0x16 vsize (vsize = register-value * 16) +0x17 bit 0 toggle compression quality (according to sn9c102 driver) +0x18 bit 7 enables compression, bit 4-5 set image down scaling: + 00 scale 1, 01 scale 1/2, 10, scale 1/4 +0x19 high-nibble is sensor clock divider, changes exposure on sensors which + use a clock generated by the bridge. Some sensors have their own clock. +0x1c auto_exposure area (for avg_lum) startx (startx = register-value * 32) +0x1d auto_exposure area (for avg_lum) starty (starty = register-value * 32) +0x1e auto_exposure area (for avg_lum) stopx (hsize = (0x1e - 0x1c) * 32) +0x1f auto_exposure area (for avg_lum) stopy (vsize = (0x1f - 0x1d) * 32) +*/ + #define MODULE_NAME "sonixb" #include "gspca.h" @@ -31,10 +51,8 @@ MODULE_LICENSE("GPL"); /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - - struct sd_desc sd_desc; /* our nctrls differ dependend upon the - sensor, so we use a per cam copy */ atomic_t avg_lum; + int prev_avg_lum; unsigned char gain; unsigned char exposure; @@ -44,8 +62,12 @@ struct sd { unsigned char frames_to_drop; unsigned char freq; /* light freq filter setting */ - unsigned char fr_h_sz; /* size of frame header */ - char sensor; /* Type of image sensor chip */ + __u8 bridge; /* Type of bridge */ +#define BRIDGE_101 0 +#define BRIDGE_102 0 /* We make no difference between 101 and 102 */ +#define BRIDGE_103 1 + + __u8 sensor; /* Type of image sensor chip */ #define SENSOR_HV7131R 0 #define SENSOR_OV6650 1 #define SENSOR_OV7630 2 @@ -53,16 +75,35 @@ struct sd { #define SENSOR_PAS202 4 #define SENSOR_TAS5110 5 #define SENSOR_TAS5130CXX 6 - char sensor_has_gain; - __u8 sensor_addr; __u8 reg11; }; -/* flags used in the device id table */ +typedef const __u8 sensor_init_t[8]; + +struct sensor_data { + const __u8 *bridge_init[2]; + int bridge_init_size[2]; + sensor_init_t *sensor_init; + int sensor_init_size; + sensor_init_t *sensor_bridge_init[2]; + int sensor_bridge_init_size[2]; + int flags; + unsigned ctrl_dis; + __u8 sensor_addr; +}; + +/* sensor_data flags */ #define F_GAIN 0x01 /* has gain */ -#define F_AUTO 0x02 /* has autogain */ -#define F_SIF 0x04 /* sif or vga */ -#define F_H18 0x08 /* long (18 b) or short (12 b) frame header */ +#define F_SIF 0x02 /* sif or vga */ + +/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */ +#define MODE_RAW 0x10 /* raw bayer mode */ +#define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */ + +/* ctrl_dis helper macros */ +#define NO_EXPO ((1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX)) +#define NO_FREQ (1 << FREQ_IDX) +#define NO_BRIGHTNESS (1 << BRIGHTNESS_IDX) #define COMP2 0x8f #define COMP 0xc7 /* 0x87 //0x07 */ @@ -73,6 +114,18 @@ struct sd { #define SYS_CLK 0x04 +#define SENS(bridge_1, bridge_3, sensor, sensor_1, \ + sensor_3, _flags, _ctrl_dis, _sensor_addr) \ +{ \ + .bridge_init = { bridge_1, bridge_3 }, \ + .bridge_init_size = { sizeof(bridge_1), sizeof(bridge_3) }, \ + .sensor_init = sensor, \ + .sensor_init_size = sizeof(sensor), \ + .sensor_bridge_init = { sensor_1, sensor_3,}, \ + .sensor_bridge_init_size = { sizeof(sensor_1), sizeof(sensor_3)}, \ + .flags = _flags, .ctrl_dis = _ctrl_dis, .sensor_addr = _sensor_addr \ +} + /* We calculate the autogain at the end of the transfer of a frame, at this moment a frame with the old settings is being transmitted, and a frame is being captured with the old settings. So if we adjust the autogain we must @@ -95,6 +148,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { +#define BRIGHTNESS_IDX 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -109,6 +163,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setbrightness, .get = sd_getbrightness, }, +#define GAIN_IDX 1 { { .id = V4L2_CID_GAIN, @@ -124,6 +179,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setgain, .get = sd_getgain, }, +#define EXPOSURE_IDX 2 { { .id = V4L2_CID_EXPOSURE, @@ -140,6 +196,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setexposure, .get = sd_getexposure, }, +#define AUTOGAIN_IDX 3 { { .id = V4L2_CID_AUTOGAIN, @@ -155,6 +212,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, +#define FREQ_IDX 4 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -172,31 +230,56 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vga_mode[] = { - {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120, .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 | MODE_RAW}, + {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, .bytesperline = 320, - .sizeimage = 320 * 240, + .sizeimage = 320 * 240 * 5 / 4, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, {640, 480, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, .bytesperline = 640, - .sizeimage = 640 * 480, + .sizeimage = 640 * 480 * 5 / 4, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; static struct v4l2_pix_format sif_mode[] = { - {176, 144, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW | MODE_REDUCED_SIF}, + {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_REDUCED_SIF}, + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144, .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 | MODE_RAW}, + {176, 144, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, + {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 5 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 | MODE_REDUCED_SIF}, {352, 288, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, .bytesperline = 352, - .sizeimage = 352 * 288, + .sizeimage = 352 * 288 * 5 / 4, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; @@ -204,7 +287,7 @@ static struct v4l2_pix_format sif_mode[] = { static const __u8 initHv7131[] = { 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, /* shift from 0x02 0x01 0x00 */ + 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x28, 0x1e, 0x60, 0x8a, 0x20, 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c }; @@ -219,13 +302,13 @@ static const __u8 initOv6650[] = { #if 1 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x0b, - 0x10, 0x1d, 0x10, 0x00, 0x06, 0x1f, 0x00 + 0x00, 0x01, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x8b, + 0x10, 0x1d, 0x10, 0x02, 0x02, 0x09, 0x07 #else /* old version? */ 0x64, 0x44, 0x28, 0x00, 0x00, 0x00, 0x00, 0x10, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x01, 0x0a, 0x14, 0x0f, 0x68, 0x8b, + 0x00, 0x01, 0x01, 0x0a, 0x14, 0x0f, 0x68, 0x8b, 0x10, 0x1d, 0x10, 0x01, 0x01, 0x07, 0x06 #endif }; @@ -283,22 +366,19 @@ static const __u8 ov6650_sensor_init[][8] = static const __u8 initOv7630[] = { 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ -#if 1 - 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ + 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */ 0x28, 0x1e, /* H & V sizes r15 .. r16 */ - 0x68, COMP1, MCK_INIT1, /* r17 .. r19 */ + 0x68, COMP2, MCK_INIT1, /* r17 .. r19 */ +#if 1 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c /* r1a .. r1f */ #else /* jfm from win */ - 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */ - 0x16, 0x12, /* H & V sizes r15 .. r16 */ - 0x68, COMP2, MCK_INIT1, /* r17 .. r19 */ 0x1d, 0x10, 0x06, 0x01, 0x00, 0x03 /* r1a .. r1f */ #endif }; static const __u8 initOv7630_3[] = { 0x44, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0x80, /* r01 .. r08 */ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, /* r09 .. r10 */ - 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */ + 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ 0x28, 0x1e, /* H & V sizes r15 .. r16 */ 0x68, 0x8f, MCK_INIT1, /* r17 .. r19 */ 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c, 0x00, /* r1a .. r20 */ @@ -327,47 +407,65 @@ static const __u8 ov7630_sensor_init[][8] = { {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, }; +static const __u8 ov7630_sensor_init_3[][8] = { + {0xa0, 0x21, 0x13, 0x80, 0x00, 0x00, 0x00, 0x10}, +}; + static const __u8 initPas106[] = { 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, - 0x16, 0x12, 0x28, COMP1, MCK_INIT1, - 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c + 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, + 0x16, 0x12, 0x24, COMP1, MCK_INIT1, + 0x18, 0x10, 0x02, 0x02, 0x09, 0x07 }; /* compression 0x86 mckinit1 0x2b */ -static const __u8 pas106_data[][2] = { - {0x02, 0x04}, /* Pixel Clock Divider 6 */ - {0x03, 0x13}, /* Frame Time MSB */ -/* {0x03, 0x12}, * Frame Time MSB */ - {0x04, 0x06}, /* Frame Time LSB */ -/* {0x04, 0x05}, * Frame Time LSB */ - {0x05, 0x65}, /* Shutter Time Line Offset */ -/* {0x05, 0x6d}, * Shutter Time Line Offset */ -/* {0x06, 0xb1}, * Shutter Time Pixel Offset */ - {0x06, 0xcd}, /* Shutter Time Pixel Offset */ - {0x07, 0xc1}, /* Black Level Subtract Sign */ -/* {0x07, 0x00}, * Black Level Subtract Sign */ - {0x08, 0x06}, /* Black Level Subtract Level */ - {0x08, 0x06}, /* Black Level Subtract Level */ -/* {0x08, 0x01}, * Black Level Subtract Level */ - {0x09, 0x05}, /* Color Gain B Pixel 5 a */ - {0x0a, 0x04}, /* Color Gain G1 Pixel 1 5 */ - {0x0b, 0x04}, /* Color Gain G2 Pixel 1 0 5 */ - {0x0c, 0x05}, /* Color Gain R Pixel 3 1 */ - {0x0d, 0x00}, /* Color GainH Pixel */ - {0x0e, 0x0e}, /* Global Gain */ - {0x0f, 0x00}, /* Contrast */ - {0x10, 0x06}, /* H&V synchro polarity */ - {0x11, 0x06}, /* ?default */ - {0x12, 0x06}, /* DAC scale */ - {0x14, 0x02}, /* ?default */ - {0x13, 0x01}, /* Validate Settings */ +static const __u8 pas106_sensor_init[][8] = { + /* Pixel Clock Divider 6 */ + { 0xa1, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14 }, + /* Frame Time MSB (also seen as 0x12) */ + { 0xa1, 0x40, 0x03, 0x13, 0x00, 0x00, 0x00, 0x14 }, + /* Frame Time LSB (also seen as 0x05) */ + { 0xa1, 0x40, 0x04, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* Shutter Time Line Offset (also seen as 0x6d) */ + { 0xa1, 0x40, 0x05, 0x65, 0x00, 0x00, 0x00, 0x14 }, + /* Shutter Time Pixel Offset (also seen as 0xb1) */ + { 0xa1, 0x40, 0x06, 0xcd, 0x00, 0x00, 0x00, 0x14 }, + /* Black Level Subtract Sign (also seen 0x00) */ + { 0xa1, 0x40, 0x07, 0xc1, 0x00, 0x00, 0x00, 0x14 }, + /* Black Level Subtract Level (also seen 0x01) */ + { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 }, + { 0xa1, 0x40, 0x08, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain B Pixel 5 a */ + { 0xa1, 0x40, 0x09, 0x05, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain G1 Pixel 1 5 */ + { 0xa1, 0x40, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain G2 Pixel 1 0 5 */ + { 0xa1, 0x40, 0x0b, 0x04, 0x00, 0x00, 0x00, 0x14 }, + /* Color Gain R Pixel 3 1 */ + { 0xa1, 0x40, 0x0c, 0x05, 0x00, 0x00, 0x00, 0x14 }, + /* Color GainH Pixel */ + { 0xa1, 0x40, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x14 }, + /* Global Gain */ + { 0xa1, 0x40, 0x0e, 0x0e, 0x00, 0x00, 0x00, 0x14 }, + /* Contrast */ + { 0xa1, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14 }, + /* H&V synchro polarity */ + { 0xa1, 0x40, 0x10, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* ?default */ + { 0xa1, 0x40, 0x11, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* DAC scale */ + { 0xa1, 0x40, 0x12, 0x06, 0x00, 0x00, 0x00, 0x14 }, + /* ?default */ + { 0xa1, 0x40, 0x14, 0x02, 0x00, 0x00, 0x00, 0x14 }, + /* Validate Settings */ + { 0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14 }, }; + static const __u8 initPas202[] = { 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x07, 0x03, 0x0a, /* 6 */ - 0x28, 0x1e, 0x28, 0x89, 0x30, + 0x00, 0x00, 0x00, 0x06, 0x03, 0x0a, + 0x28, 0x1e, 0x28, 0x89, 0x20, 0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c }; static const __u8 pas202_sensor_init[][8] = { @@ -397,7 +495,7 @@ static const __u8 pas202_sensor_init[][8] = { static const __u8 initTas5110[] = { 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x46, 0x09, 0x0a, /* shift from 0x45 0x09 0x0a */ + 0x00, 0x01, 0x00, 0x45, 0x09, 0x0a, 0x16, 0x12, 0x60, 0x86, 0x2b, 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 }; @@ -410,7 +508,7 @@ static const __u8 tas5110_sensor_init[][8] = { static const __u8 initTas5130[] = { 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x69, 0x0c, 0x0a, + 0x00, 0x01, 0x00, 0x68, 0x0c, 0x0a, 0x28, 0x1e, 0x60, COMP, MCK_INIT, 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c }; @@ -422,6 +520,21 @@ static const __u8 tas5130_sensor_init[][8] = { {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}, }; +static struct sensor_data sensor_data[] = { +SENS(initHv7131, NULL, hv7131_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, 0), +SENS(initOv6650, NULL, ov6650_sensor_init, NULL, NULL, F_GAIN|F_SIF, 0, 0x60), +SENS(initOv7630, initOv7630_3, ov7630_sensor_init, NULL, ov7630_sensor_init_3, + F_GAIN, 0, 0x21), +SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ, + 0), +SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0, + NO_EXPO|NO_FREQ, 0), +SENS(initTas5110, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF, + NO_BRIGHTNESS|NO_FREQ, 0), +SENS(initTas5130, NULL, tas5130_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, + 0), +}; + /* get one byte in gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, __u16 value) @@ -442,7 +555,7 @@ static void reg_w(struct gspca_dev *gspca_dev, int len) { #ifdef GSPCA_DEBUG - if (len > sizeof gspca_dev->usb_buf) { + if (len > USB_BUF_SZ) { PDEBUG(D_ERR|D_PACK, "reg_w: buffer overflow"); return; } @@ -458,26 +571,6 @@ static void reg_w(struct gspca_dev *gspca_dev, 500); } -static void reg_w_big(struct gspca_dev *gspca_dev, - __u16 value, - const __u8 *buffer, - int len) -{ - __u8 *tmpbuf; - - tmpbuf = kmalloc(len, GFP_KERNEL); - memcpy(tmpbuf, buffer, len); - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, /* request */ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, - 0, /* index */ - tmpbuf, len, - 500); - kfree(tmpbuf); -} - static int i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) { int retry = 60; @@ -520,7 +613,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) {0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}; /* change reg 0x06 */ - i2cOV[1] = sd->sensor_addr; + i2cOV[1] = sensor_data[sd->sensor].sensor_addr; i2cOV[3] = sd->brightness; if (i2c_w(gspca_dev, i2cOV) < 0) goto err; @@ -578,9 +671,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) goto err; break; } - case SENSOR_TAS5110: - /* FIXME figure out howto control brightness on TAS5110 */ - break; } return; err: @@ -610,7 +700,7 @@ static void setsensorgain(struct gspca_dev *gspca_dev) case SENSOR_OV7630: { __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; - i2c[1] = sd->sensor_addr; + i2c[1] = sensor_data[sd->sensor].sensor_addr; i2c[3] = gain >> 2; if (i2c_w(gspca_dev, i2c) < 0) goto err; @@ -637,7 +727,7 @@ static void setgain(struct gspca_dev *gspca_dev) rgb_value = gain; reg_w(gspca_dev, 0x11, &rgb_value, 1); - if (sd->sensor_has_gain) + if (sensor_data[sd->sensor].flags & F_GAIN) setsensorgain(gspca_dev); } @@ -698,6 +788,11 @@ static void setexposure(struct gspca_dev *gspca_dev) else if (reg11 > 16) reg11 = 16; + /* In 640x480, if the reg11 has less than 3, the image is + unstable (not enough bandwidth). */ + if (gspca_dev->width == 640 && reg11 < 3) + reg11 = 3; + /* frame exposure time in ms = 1000 * reg11 / 30 -> reg10 = sd->exposure * 2 * reg10_max / (1000 * reg11 / 30) */ reg10 = (sd->exposure * 60 * reg10_max) / (1000 * reg11); @@ -711,13 +806,8 @@ static void setexposure(struct gspca_dev *gspca_dev) else if (reg10 > reg10_max) reg10 = reg10_max; - /* In 640x480, if the reg11 has less than 3, the image is - unstable (not enough bandwidth). */ - if (gspca_dev->width == 640 && reg11 < 3) - reg11 = 3; - /* Write reg 10 and reg11 low nibble */ - i2c[1] = sd->sensor_addr; + i2c[1] = sensor_data[sd->sensor].sensor_addr; i2c[3] = reg10; i2c[4] |= reg11 - 1; @@ -757,7 +847,7 @@ static void setfreq(struct gspca_dev *gspca_dev) ? 0x4f : 0x8a; break; } - i2c[1] = sd->sensor_addr; + i2c[1] = sensor_data[sd->sensor].sensor_addr; if (i2c_w(gspca_dev, i2c) < 0) PDEBUG(D_ERR, "i2c error setfreq"); break; @@ -790,30 +880,19 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - int sif = 0; - /* nctrls depends upon the sensor, so we use a per cam copy */ - memcpy(&sd->sd_desc, gspca_dev->sd_desc, sizeof(struct sd_desc)); - gspca_dev->sd_desc = &sd->sd_desc; + reg_r(gspca_dev, 0x00); + if (gspca_dev->usb_buf[0] != 0x10) + return -ENODEV; /* copy the webcam info from the device id */ - sd->sensor = (id->driver_info >> 24) & 0xff; - if (id->driver_info & (F_GAIN << 16)) - sd->sensor_has_gain = 1; - if (id->driver_info & (F_AUTO << 16)) - sd->sd_desc.dq_callback = do_autogain; - if (id->driver_info & (F_SIF << 16)) - sif = 1; - if (id->driver_info & (F_H18 << 16)) - sd->fr_h_sz = 18; /* size of frame header */ - else - sd->fr_h_sz = 12; - sd->sd_desc.nctrls = (id->driver_info >> 8) & 0xff; - sd->sensor_addr = id->driver_info & 0xff; + sd->sensor = id->driver_info >> 8; + sd->bridge = id->driver_info & 0xff; + gspca_dev->ctrl_dis = sensor_data[sd->sensor].ctrl_dis; cam = &gspca_dev->cam; cam->epaddr = 0x01; - if (!sif) { + if (!(sensor_data[sd->sensor].flags & F_SIF)) { cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); } else { @@ -823,157 +902,98 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = BRIGHTNESS_DEF; sd->gain = GAIN_DEF; sd->exposure = EXPOSURE_DEF; - sd->autogain = AUTOGAIN_DEF; + if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX)) + sd->autogain = 0; /* Disable do_autogain callback */ + else + sd->autogain = AUTOGAIN_DEF; sd->freq = FREQ_DEF; return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { - reg_r(gspca_dev, 0x00); - if (gspca_dev->usb_buf[0] != 0x10) - return -ENODEV; - return 0; -} + const __u8 stop = 0x09; /* Disable stream turn of LED */ -static void pas106_i2cinit(struct gspca_dev *gspca_dev) -{ - int i; - const __u8 *data; - __u8 i2c1[] = { 0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 }; - - i = ARRAY_SIZE(pas106_data); - data = pas106_data[0]; - while (--i >= 0) { - memcpy(&i2c1[2], data, 2); - /* copy 2 bytes from the template */ - if (i2c_w(gspca_dev, i2c1) < 0) - PDEBUG(D_ERR, "i2c error pas106"); - data += 2; - } + reg_w(gspca_dev, 0x01, &stop, 1); + + return 0; } /* -- start the camera -- */ static void sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int mode, l = 0x1f; + struct cam *cam = &gspca_dev->cam; + int mode, l; const __u8 *sn9c10x; - __u8 reg17_19[3]; - - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + __u8 reg12_19[8]; + + mode = cam->cam_mode[gspca_dev->curr_mode].priv & 0x07; + sn9c10x = sensor_data[sd->sensor].bridge_init[sd->bridge]; + l = sensor_data[sd->sensor].bridge_init_size[sd->bridge]; + memcpy(reg12_19, &sn9c10x[0x12 - 1], 8); + reg12_19[6] = sn9c10x[0x18 - 1] | (mode << 4); + /* Special cases where reg 17 and or 19 value depends on mode */ switch (sd->sensor) { - case SENSOR_HV7131R: - sn9c10x = initHv7131; - reg17_19[0] = 0x60; - reg17_19[1] = (mode << 4) | 0x8a; - reg17_19[2] = 0x20; - break; - case SENSOR_OV6650: - sn9c10x = initOv6650; - reg17_19[0] = 0x68; - reg17_19[1] = (mode << 4) | 0x8b; - reg17_19[2] = 0x20; - break; - case SENSOR_OV7630: - if (sd->fr_h_sz == 18) { /* SN9C103 */ - sn9c10x = initOv7630_3; - l = sizeof initOv7630_3; - } else - sn9c10x = initOv7630; - reg17_19[0] = 0x68; - reg17_19[1] = (mode << 4) | COMP2; - reg17_19[2] = MCK_INIT1; - break; - case SENSOR_PAS106: - sn9c10x = initPas106; - reg17_19[0] = 0x24; /* 0x28 */ - reg17_19[1] = (mode << 4) | COMP1; - reg17_19[2] = MCK_INIT1; - break; case SENSOR_PAS202: - sn9c10x = initPas202; - reg17_19[0] = mode ? 0x24 : 0x20; - reg17_19[1] = (mode << 4) | 0x89; - reg17_19[2] = 0x20; - break; - case SENSOR_TAS5110: - sn9c10x = initTas5110; - reg17_19[0] = 0x60; - reg17_19[1] = (mode << 4) | 0x86; - reg17_19[2] = 0x2b; /* 0xf3; */ + reg12_19[5] = mode ? 0x24 : 0x20; break; - default: -/* case SENSOR_TAS5130CXX: */ - sn9c10x = initTas5130; - reg17_19[0] = 0x60; - reg17_19[1] = (mode << 4) | COMP; - reg17_19[2] = mode ? 0x23 : 0x43; + case SENSOR_TAS5130CXX: + /* probably not mode specific at all most likely the upper + nibble of 0x19 is exposure (clock divider) just as with + the tas5110, we need someone to test this. */ + reg12_19[7] = mode ? 0x23 : 0x43; break; } + /* Disable compression when the raw bayer format has been selected */ + if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) + reg12_19[6] &= ~0x80; + + /* Vga mode emulation on SIF sensor? */ + if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_REDUCED_SIF) { + reg12_19[0] += 16; /* 0x12: hstart adjust */ + reg12_19[1] += 24; /* 0x13: vstart adjust */ + reg12_19[3] = 320 / 16; /* 0x15: hsize */ + reg12_19[4] = 240 / 16; /* 0x16: vsize */ + } /* reg 0x01 bit 2 video transfert on */ reg_w(gspca_dev, 0x01, &sn9c10x[0x01 - 1], 1); /* reg 0x17 SensorClk enable inv Clk 0x60 */ reg_w(gspca_dev, 0x17, &sn9c10x[0x17 - 1], 1); /* Set the registers from the template */ - reg_w_big(gspca_dev, 0x01, sn9c10x, l); - switch (sd->sensor) { - case SENSOR_HV7131R: - i2c_w_vector(gspca_dev, hv7131_sensor_init, - sizeof hv7131_sensor_init); - break; - case SENSOR_OV6650: - i2c_w_vector(gspca_dev, ov6650_sensor_init, - sizeof ov6650_sensor_init); - break; - case SENSOR_OV7630: - i2c_w_vector(gspca_dev, ov7630_sensor_init, - sizeof ov7630_sensor_init); - if (sd->fr_h_sz == 18) { /* SN9C103 */ - const __u8 i2c[] = { 0xa0, 0x21, 0x13, 0x80, 0x00, - 0x00, 0x00, 0x10 }; - i2c_w(gspca_dev, i2c); - } - break; - case SENSOR_PAS106: - pas106_i2cinit(gspca_dev); - break; - case SENSOR_PAS202: - i2c_w_vector(gspca_dev, pas202_sensor_init, - sizeof pas202_sensor_init); - break; - case SENSOR_TAS5110: - i2c_w_vector(gspca_dev, tas5110_sensor_init, - sizeof tas5110_sensor_init); - break; - default: -/* case SENSOR_TAS5130CXX: */ - i2c_w_vector(gspca_dev, tas5130_sensor_init, - sizeof tas5130_sensor_init); - break; - } + reg_w(gspca_dev, 0x01, sn9c10x, l); + + /* Init the sensor */ + i2c_w_vector(gspca_dev, sensor_data[sd->sensor].sensor_init, + sensor_data[sd->sensor].sensor_init_size); + if (sensor_data[sd->sensor].sensor_bridge_init[sd->bridge]) + i2c_w_vector(gspca_dev, + sensor_data[sd->sensor].sensor_bridge_init[sd->bridge], + sensor_data[sd->sensor].sensor_bridge_init_size[ + sd->bridge]); + /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */ - reg_w(gspca_dev, 0x15, &sn9c10x[0x15 - 1], 2); + reg_w(gspca_dev, 0x15, ®12_19[3], 2); /* compression register */ - reg_w(gspca_dev, 0x18, ®17_19[1], 1); + reg_w(gspca_dev, 0x18, ®12_19[6], 1); /* H_start */ - reg_w(gspca_dev, 0x12, &sn9c10x[0x12 - 1], 1); + reg_w(gspca_dev, 0x12, ®12_19[0], 1); /* V_START */ - reg_w(gspca_dev, 0x13, &sn9c10x[0x13 - 1], 1); + reg_w(gspca_dev, 0x13, ®12_19[1], 1); /* reset 0x17 SensorClk enable inv Clk 0x60 */ /*fixme: ov7630 [17]=68 8f (+20 if 102)*/ - reg_w(gspca_dev, 0x17, ®17_19[0], 1); + reg_w(gspca_dev, 0x17, ®12_19[5], 1); /*MCKSIZE ->3 */ /*fixme: not ov7630*/ - reg_w(gspca_dev, 0x19, ®17_19[2], 1); + reg_w(gspca_dev, 0x19, ®12_19[7], 1); /* AE_STRX AE_STRY AE_ENDX AE_ENDY */ reg_w(gspca_dev, 0x1c, &sn9c10x[0x1c - 1], 4); /* Enable video transfert */ reg_w(gspca_dev, 0x01, &sn9c10x[0], 1); /* Compression */ - reg_w(gspca_dev, 0x18, ®17_19[1], 2); + reg_w(gspca_dev, 0x18, ®12_19[6], 2); msleep(20); sd->reg11 = -1; @@ -990,18 +1010,7 @@ static void sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { - __u8 ByteSend; - - ByteSend = 0x09; /* 0X00 */ - reg_w(gspca_dev, 0x01, &ByteSend, 1); -} - -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ + sd_init(gspca_dev); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, @@ -1011,6 +1020,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { int i; struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; /* frames start with: * ff ff 00 c4 c4 96 synchro @@ -1031,20 +1041,31 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, && data[5 + i] == 0x96) { /* start of frame */ int lum = -1; int pkt_type = LAST_PACKET; + int fr_h_sz = (sd->bridge == BRIDGE_103) ? + 18 : 12; - if (len - i < sd->fr_h_sz) { + if (len - i < fr_h_sz) { PDEBUG(D_STREAM, "packet too short to" " get avg brightness"); - } else if (sd->fr_h_sz == 12) { - lum = data[i + 8] + (data[i + 9] << 8); - } else { + } else if (sd->bridge == BRIDGE_103) { lum = data[i + 9] + (data[i + 10] << 8); + } else { + lum = data[i + 8] + (data[i + 9] << 8); } - if (lum == 0) { + /* When exposure changes midway a frame we + get a lum of 0 in this case drop 2 frames + as the frames directly after an exposure + change have an unstable image. Sometimes lum + *really* is 0 (cam used in low light with + low exposure setting), so do not drop frames + if the previous lum was 0 too. */ + if (lum == 0 && sd->prev_avg_lum != 0) { lum = -1; sd->frames_to_drop = 2; - } + sd->prev_avg_lum = 0; + } else + sd->prev_avg_lum = lum; atomic_set(&sd->avg_lum, lum); if (sd->frames_to_drop) { @@ -1054,14 +1075,25 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, frame = gspca_frame_add(gspca_dev, pkt_type, frame, data, 0); - data += i + sd->fr_h_sz; - len -= i + sd->fr_h_sz; + data += i + fr_h_sz; + len -= i + fr_h_sz; gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); return; } } } + + if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) { + /* In raw mode we sometimes get some garbage after the frame + ignore this */ + int used = frame->data_end - frame->data; + int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage; + + if (used + len > size) + len = size - used; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } @@ -1195,58 +1227,45 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, + .dq_callback = do_autogain, }; /* -- module initialisation -- */ -#define SFCI(sensor, flags, nctrls, i2c_addr) \ - .driver_info = (SENSOR_ ## sensor << 24) \ - | ((flags) << 16) \ - | ((nctrls) << 8) \ - | (i2c_addr) +#define SB(sensor, bridge) \ + .driver_info = (SENSOR_ ## sensor << 8) | BRIDGE_ ## bridge + + static __devinitdata struct usb_device_id device_table[] = { -#ifndef CONFIG_USB_SN9C102 - {USB_DEVICE(0x0c45, 0x6001), /* SN9C102 */ - SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, - {USB_DEVICE(0x0c45, 0x6005), /* SN9C101 */ - SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, - {USB_DEVICE(0x0c45, 0x6007), /* SN9C101 */ - SFCI(TAS5110, F_GAIN|F_AUTO|F_SIF, 4, 0)}, - {USB_DEVICE(0x0c45, 0x6009), /* SN9C101 */ - SFCI(PAS106, F_SIF, 2, 0)}, - {USB_DEVICE(0x0c45, 0x600d), /* SN9C101 */ - SFCI(PAS106, F_SIF, 2, 0)}, + {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110, 102)}, /* TAS5110C1B */ + {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110, 101)}, /* TAS5110C1B */ +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110, 101)}, /* TAS5110D */ + {USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)}, + {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)}, +#endif + {USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)}, + {USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)}, + {USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)}, + {USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)}, + {USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)}, + {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)}, #endif - {USB_DEVICE(0x0c45, 0x6011), /* SN9C101 - SN9C101G */ - SFCI(OV6650, F_GAIN|F_AUTO|F_SIF, 5, 0x60)}, -#ifndef CONFIG_USB_SN9C102 - {USB_DEVICE(0x0c45, 0x6019), /* SN9C101 */ - SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, - {USB_DEVICE(0x0c45, 0x6024), /* SN9C102 */ - SFCI(TAS5130CXX, 0, 2, 0)}, - {USB_DEVICE(0x0c45, 0x6025), /* SN9C102 */ - SFCI(TAS5130CXX, 0, 2, 0)}, - {USB_DEVICE(0x0c45, 0x6028), /* SN9C102 */ - SFCI(PAS202, 0, 2, 0)}, - {USB_DEVICE(0x0c45, 0x6029), /* SN9C101 */ - SFCI(PAS106, F_SIF, 2, 0)}, - {USB_DEVICE(0x0c45, 0x602c), /* SN9C102 */ - SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, - {USB_DEVICE(0x0c45, 0x602d), /* SN9C102 */ - SFCI(HV7131R, 0, 2, 0)}, - {USB_DEVICE(0x0c45, 0x602e), /* SN9C102 */ - SFCI(OV7630, F_GAIN|F_AUTO, 5, 0x21)}, - {USB_DEVICE(0x0c45, 0x60af), /* SN9C103 */ - SFCI(PAS202, F_H18, 2, 0)}, - {USB_DEVICE(0x0c45, 0x60b0), /* SN9C103 */ - SFCI(OV7630, F_GAIN|F_AUTO|F_H18, 5, 0x21)}, + {USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x602e), SB(OV7630, 102)}, #endif + {USB_DEVICE(0x0c45, 0x608f), SB(OV7630, 103)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x60af), SB(PAS202, 103)}, +#endif + {USB_DEVICE(0x0c45, 0x60b0), SB(OV7630, 103)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -1264,6 +1283,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index b30f7eeda..a77164581 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -39,6 +39,7 @@ struct sd { unsigned char contrast; unsigned char colors; unsigned char autogain; + __u8 vflip; /* ov7630 only */ signed char ag_cnt; #define AG_CNT_START 13 @@ -54,8 +55,10 @@ struct sd { #define SENSOR_HV7131R 0 #define SENSOR_MI0360 1 #define SENSOR_MO4000 2 -#define SENSOR_OV7648 3 -#define SENSOR_OV7660 4 +#define SENSOR_OM6802 3 +#define SENSOR_OV7630 4 +#define SENSOR_OV7648 5 +#define SENSOR_OV7660 6 unsigned char i2c_base; }; @@ -68,6 +71,8 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { { @@ -76,7 +81,8 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Brightness", .minimum = 0, - .maximum = 0xffff, +#define BRIGHTNESS_MAX 0xffff + .maximum = BRIGHTNESS_MAX, .step = 1, #define BRIGHTNESS_DEF 0x7fff .default_value = BRIGHTNESS_DEF, @@ -90,7 +96,8 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", .minimum = 0, - .maximum = 127, +#define CONTRAST_MAX 127 + .maximum = CONTRAST_MAX, .step = 1, #define CONTRAST_DEF 63 .default_value = CONTRAST_DEF, @@ -104,14 +111,15 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Color", .minimum = 0, - .maximum = 255, + .maximum = 64, .step = 1, -#define COLOR_DEF 127 +#define COLOR_DEF 32 .default_value = COLOR_DEF, }, .set = sd_setcolors, .get = sd_getcolors, }, +#define AUTOGAIN_IDX 3 { { .id = V4L2_CID_AUTOGAIN, @@ -126,12 +134,28 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, +/* ov7630 only */ +#define VFLIP_IDX 4 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, }; static struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 8 + 590, + .sizeimage = 160 * 120 * 4 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 2}, {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, @@ -180,6 +204,31 @@ static const __u8 sn_mo4000[] = { 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const __u8 sn_om6802[] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x23, 0x72, 0x00, 0x1a, 0x34, 0x27, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x51, 0x01, 0x00, 0x28, 0x1e, 0x40, +/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x22, 0x44, 0x63, 0x7d, 0x92, 0xa3, 0xaf, + 0xbc, 0xc4, 0xcd, 0xd5, 0xdc, 0xe1, 0xe8, 0xef, + 0xf7 +}; + +static const __u8 sn_ov7630[] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x21, 0x40, 0x00, 0x1a, 0x20, 0x1f, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0xa1, 0x21, 0x76, 0x21, 0x00, 0x00, 0x00, 0x10, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2, +/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + static const __u8 sn_ov7648[] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, @@ -219,31 +268,26 @@ static const __u8 *sn_tb[] = { sn_hv7131, sn_mi0360, sn_mo4000, + sn_om6802, + sn_ov7630, sn_ov7648, sn_ov7660 }; -static const __u8 regsn20[] = { +static const __u8 gamma_def[] = { 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99, 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff }; -static const __u8 regsn20_sn9c325[] = { - 0x0a, 0x3a, 0x56, 0x6c, 0x7e, 0x8d, 0x9a, 0xa4, - 0xaf, 0xbb, 0xc5, 0xcd, 0xd5, 0xde, 0xe8, 0xed, 0xf5 -}; static const __u8 reg84[] = { 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe5, 0x0f, 0xe4, 0x0f, 0x38, 0x00, 0x3e, 0x00, 0xc3, 0x0f, -/* 0x00, 0x00, 0x00, 0x00, 0x00 */ +#if 1 + 0xf7, 0x0f, 0x00, 0x00, 0x00 +#else 0xf7, 0x0f, 0x0a, 0x00, 0x00 +#endif }; -static const __u8 reg84_sn9c325[] = { - 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe4, 0x0f, - 0xd3, 0x0f, 0x4b, 0x00, 0x48, 0x00, 0xc0, 0x0f, - 0xf8, 0x0f, 0x00, 0x00, 0x00 -}; - static const __u8 hv7131r_sensor_init[][8] = { {0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, {0xB1, 0x11, 0x34, 0x17, 0x7F, 0x00, 0x00, 0x10}, @@ -352,6 +396,93 @@ static const __u8 mo4000_sensor_init[][8] = { {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, {} }; +static __u8 om6802_sensor_init[][8] = { + {0xa0, 0x34, 0x90, 0x05, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x49, 0x85, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10}, +/* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */ + {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10}, + /* white balance & auto-exposure */ +/* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10}, + * set color mode */ +/* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10}, + * max AGC value in AE */ +/* {0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10}, + * preset AGC */ +/* {0xa0, 0x34, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10}, + * preset brightness */ +/* {0xa0, 0x34, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x10}, + * preset contrast */ +/* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10}, + * preset gamma */ + {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10}, + /* luminance mode (0x4f = AE) */ + {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10}, + /* preset shutter */ +/* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10}, + * auto frame rate */ +/* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */ + +/* {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, */ +/* {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, */ +/* {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, */ +/* {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, */ + {} +}; +static const __u8 ov7630_sensor_init[][8] = { + {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, +/* win: delay 20ms */ + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, +/* win: delay 20ms */ + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, +/* win: i2c_r from 00 to 80 */ + {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10}, + {0xb1, 0x21, 0x0c, 0x20, 0x20, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x11, 0x00, 0x48, 0xc0, 0x00, 0x10}, + {0xb1, 0x21, 0x15, 0x80, 0x03, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, + {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x1f, 0x00, 0x80, 0x80, 0x80, 0x10}, + {0xd1, 0x21, 0x23, 0xde, 0x10, 0x8a, 0xa0, 0x10}, + {0xc1, 0x21, 0x27, 0xca, 0xa2, 0x74, 0x00, 0x10}, + {0xd1, 0x21, 0x2a, 0x88, 0x00, 0x88, 0x01, 0x10}, + {0xc1, 0x21, 0x2e, 0x80, 0x00, 0x18, 0x00, 0x10}, + {0xa1, 0x21, 0x21, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x32, 0xc2, 0x08, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x60, 0x05, 0x40, 0x12, 0x57, 0x10}, + {0xa1, 0x21, 0x64, 0x73, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x65, 0x00, 0x55, 0x01, 0xac, 0x10}, + {0xa1, 0x21, 0x69, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x21, 0x6f, 0x1f, 0x01, 0x00, 0x10, 0x10}, + {0xd1, 0x21, 0x73, 0x50, 0x20, 0x02, 0x01, 0x10}, + {0xd1, 0x21, 0x77, 0xf3, 0x90, 0x98, 0x98, 0x10}, + {0xc1, 0x21, 0x7b, 0x00, 0x4c, 0xf7, 0x00, 0x10}, + {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, + {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, +/* */ + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, +/*fixme: + 0x12, 0x04*/ +/* {0xa1, 0x21, 0x75, 0x82, 0x00, 0x00, 0x00, 0x10}, * COMN + * set by setvflip */ + {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10}, +/* */ + {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, +/* */ + {0xa1, 0x21, 0x10, 0x83, 0x00, 0x00, 0x00, 0x10}, +/* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */ + {} +}; static const __u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ /* (delay 20ms) */ @@ -518,10 +649,16 @@ static const __u8 qtable4[] = { 0x29, 0x29, 0x29, 0x29 }; -/* read <len> bytes (len < sizeof gspca_dev->usb_buf) to gspca_dev->usb_buf */ +/* read <len> bytes to gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, __u16 value, int len) { +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + err("reg_r: buffer overflow"); + return; + } +#endif usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), 0, @@ -554,29 +691,20 @@ static void reg_w(struct gspca_dev *gspca_dev, { PDEBUG(D_USBO, "reg_w [%02x] = %02x %02x ..", value, buffer[0], buffer[1]); - if (len <= sizeof gspca_dev->usb_buf) { - memcpy(gspca_dev->usb_buf, buffer, len); - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, 0, - gspca_dev->usb_buf, len, - 500); - } else { - __u8 *tmpbuf; - - tmpbuf = kmalloc(len, GFP_KERNEL); - memcpy(tmpbuf, buffer, len); - usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - 0x08, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, 0, - tmpbuf, len, - 500); - kfree(tmpbuf); +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + err("reg_w: buffer overflow"); + return; } +#endif + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, 0, + gspca_dev->usb_buf, len, + 500); } /* I2C write 1 byte */ @@ -677,7 +805,11 @@ static int configure_gpio(struct gspca_dev *gspca_dev, static const __u8 regd4[] = {0x60, 0x00, 0x00}; reg_w1(gspca_dev, 0xf1, 0x00); - reg_w1(gspca_dev, 0x01, 0x00); /*jfm was sn9c1xx[1] in v1*/ +#if 1 + reg_w1(gspca_dev, 0x01, sn9c1xx[1]); +#else + reg_w1(gspca_dev, 0x01, 0x00); /*jfm: in some win traces*/ +#endif /* configure gpio */ reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2); @@ -697,29 +829,45 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f); - switch (sd->bridge) { -#if 0 + switch (sd->sensor) { + case SENSOR_OM6802: + reg_w1(gspca_dev, 0x02, 0x71); + reg_w1(gspca_dev, 0x01, 0x42); + reg_w1(gspca_dev, 0x17, 0x64); + reg_w1(gspca_dev, 0x01, 0x42); + break; +#if 1 /*jfm: from win trace */ - case BRIDGE_SN9C120: + case SENSOR_OV7630: reg_w1(gspca_dev, 0x01, 0x61); - reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x17, 0xe2); reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); break; #endif - case BRIDGE_SN9C325: + case SENSOR_OV7648: reg_w1(gspca_dev, 0x01, 0x43); reg_w1(gspca_dev, 0x17, 0xae); reg_w1(gspca_dev, 0x01, 0x42); break; +#if 1 +/*jfm: from win trace */ + case SENSOR_OV7660: + reg_w1(gspca_dev, 0x01, 0x61); + reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); + break; +#endif default: reg_w1(gspca_dev, 0x01, 0x43); reg_w1(gspca_dev, 0x17, 0x61); reg_w1(gspca_dev, 0x01, 0x42); - } - - if (sd->sensor == SENSOR_HV7131R) { - if (probesensor(gspca_dev) < 0) - return -ENODEV; + if (sd->sensor == SENSOR_HV7131R) { + if (probesensor(gspca_dev) < 0) + return -ENODEV; + } + break; } return 0; } @@ -757,6 +905,40 @@ static void mo4000_InitSensor(struct gspca_dev *gspca_dev) } } +static void om6802_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + + while (om6802_sensor_init[i][0]) { + i2c_w8(gspca_dev, om6802_sensor_init[i]); + i++; + } +} + +static void ov7630_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + + i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 76 01 */ + i++; + i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 (RGB+SRST) */ + i++; + msleep(20); + i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */ + i++; + i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 */ + i++; + msleep(20); + i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */ + i++; +/*jfm:win i2c_r from 00 to 80*/ + + while (ov7630_sensor_init[i][0]) { + i2c_w8(gspca_dev, ov7630_sensor_init[i]); + i++; + } +} + static void ov7648_InitSensor(struct gspca_dev *gspca_dev) { int i = 0; @@ -803,11 +985,21 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->autogain = AUTOGAIN_DEF; sd->ag_cnt = -1; + switch (sd->sensor) { + case SENSOR_OV7630: + case SENSOR_OV7648: + case SENSOR_OV7660: + gspca_dev->ctrl_dis = (1 << AUTOGAIN_IDX); + break; + } + if (sd->sensor != SENSOR_OV7630) + gspca_dev->ctrl_dis |= (1 << VFLIP_IDX); + return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; /* const __u8 *sn9c1xx; */ @@ -916,16 +1108,53 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev, | ((expoMo10[3] & 0x30) >> 4)); break; } + case SENSOR_OM6802: { + __u8 gainOm[] = + { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 }; + + if (expo > 0x03ff) + expo = 0x03ff; + if (expo < 0x0001) + expo = 0x0001; + gainOm[3] = expo >> 2; + i2c_w8(gspca_dev, gainOm); + reg_w1(gspca_dev, 0x96, (expo >> 5) & 0x1f); + PDEBUG(D_CONF, "set exposure %d", gainOm[3]); + break; + } } return expo; } +/* this function is used for sensors o76xx only */ +static void setbrightcont(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned val; + __u8 reg84_full[0x15]; + + memset(reg84_full, 0, sizeof reg84_full); + val = sd->contrast * 0x20 / CONTRAST_MAX + 0x10; /* 10..30 */ + reg84_full[2] = val; + reg84_full[0] = (val + 1) / 2; + reg84_full[4] = (val + 1) / 5; + if (val > BRIGHTNESS_DEF) + val = (sd->brightness - BRIGHTNESS_DEF) * 0x20 + / BRIGHTNESS_MAX; + else + val = 0; + reg84_full[0x12] = val; /* 00..1f */ + reg_w(gspca_dev, 0x84, reg84_full, sizeof reg84_full); +} + +/* sensor != ov76xx */ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; unsigned int expo; __u8 k2; + k2 = sd->brightness >> 10; switch (sd->sensor) { case SENSOR_HV7131R: expo = sd->brightness << 4; @@ -940,12 +1169,17 @@ static void setbrightness(struct gspca_dev *gspca_dev) expo = sd->brightness >> 4; sd->exposure = setexposure(gspca_dev, expo); break; + case SENSOR_OM6802: + expo = sd->brightness >> 6; + sd->exposure = setexposure(gspca_dev, expo); + k2 = sd->brightness >> 11; + break; } - k2 = sd->brightness >> 10; reg_w1(gspca_dev, 0x96, k2); } +/* sensor != ov76xx */ static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -962,31 +1196,38 @@ static void setcontrast(struct gspca_dev *gspca_dev) static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 data; - int colour; + __u8 blue, red; - colour = sd->colors - 128; - if (colour > 0) - data = (colour + 32) & 0x7f; /* blue */ - else - data = (-colour + 32) & 0x7f; /* red */ - reg_w1(gspca_dev, 0x05, data); + if (sd->colors >= 32) { + red = 32 + (sd->colors - 32) / 2; + blue = 64 - sd->colors; + } else { + red = sd->colors; + blue = 32 + (32 - sd->colors) / 2; + } + reg_w1(gspca_dev, 0x05, red); +/* reg_w1(gspca_dev, 0x07, 32); */ + reg_w1(gspca_dev, 0x06, blue); } static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - switch (sd->sensor) { - case SENSOR_HV7131R: - case SENSOR_MO4000: - case SENSOR_MI0360: - if (sd->autogain) - sd->ag_cnt = AG_CNT_START; - else - sd->ag_cnt = -1; - break; - } + if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX)) + return; + if (sd->autogain) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; +} + +static void setvflip(struct sd *sd) +{ + if (sd->sensor != SENSOR_OV7630) + return; + i2c_w1(&sd->gspca_dev, 0x75, /* COMN */ + sd->vflip ? 0x82 : 0x02); } /* -- start the camera -- */ @@ -1000,13 +1241,12 @@ static void sd_start(struct gspca_dev *gspca_dev) static const __u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; static const __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; static const __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ - static const __u8 CE_sn9c325[] = - { 0x32, 0xdd, 0x32, 0xdd }; /* OV7648 - SN9C325 */ + static const __u8 CE_ov76xx[] = + { 0x32, 0xdd, 0x32, 0xdd }; /* OV7630/48 */ sn9c1xx = sn_tb[(int) sd->sensor]; configure_gpio(gspca_dev, sn9c1xx); -/* reg_w1(gspca_dev, 0x01, 0x44); jfm from win trace*/ reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]); reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]); reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]); @@ -1019,13 +1259,16 @@ static void sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0xc8, 0x50); reg_w1(gspca_dev, 0xc9, 0x3c); reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); - switch (sd->bridge) { - case BRIDGE_SN9C325: + switch (sd->sensor) { + case SENSOR_OV7630: + reg17 = 0xe2; + break; + case SENSOR_OV7648: reg17 = 0xae; break; -#if 0 +#if 1 /*jfm: from win trace */ - case BRIDGE_SN9C120: + case SENSOR_OV7660: reg17 = 0xa0; break; #endif @@ -1038,24 +1281,23 @@ static void sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x07, sn9c1xx[7]); reg_w1(gspca_dev, 0x06, sn9c1xx[6]); reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]); + reg_w(gspca_dev, 0x20, gamma_def, sizeof gamma_def); + for (i = 0; i < 8; i++) + reg_w(gspca_dev, 0x84, reg84, sizeof reg84); +#if 0 switch (sd->bridge) { case BRIDGE_SN9C325: - reg_w(gspca_dev, 0x20, regsn20_sn9c325, - sizeof regsn20_sn9c325); - for (i = 0; i < 8; i++) - reg_w(gspca_dev, 0x84, reg84_sn9c325, - sizeof reg84_sn9c325); reg_w1(gspca_dev, 0x9a, 0x0a); reg_w1(gspca_dev, 0x99, 0x60); break; default: - reg_w(gspca_dev, 0x20, regsn20, sizeof regsn20); - for (i = 0; i < 8; i++) - reg_w(gspca_dev, 0x84, reg84, sizeof reg84); +#endif reg_w1(gspca_dev, 0x9a, 0x08); reg_w1(gspca_dev, 0x99, 0x59); +#if 0 break; } +#endif mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; if (mode) @@ -1080,6 +1322,16 @@ static void sd_start(struct gspca_dev *gspca_dev) /* reg1 = 0x06; * 640 clk 24Mz (done) */ } break; + case SENSOR_OM6802: + om6802_InitSensor(gspca_dev); + reg17 = 0x64; /* 640 MCKSIZE */ + break; + case SENSOR_OV7630: + ov7630_InitSensor(gspca_dev); + setvflip(sd); + reg17 = 0xe2; + reg1 = 0x44; + break; case SENSOR_OV7648: ov7648_InitSensor(gspca_dev); reg17 = 0xa2; @@ -1110,9 +1362,10 @@ static void sd_start(struct gspca_dev *gspca_dev) } reg_w(gspca_dev, 0xc0, C0, 6); reg_w(gspca_dev, 0xca, CA, 4); - switch (sd->bridge) { - case BRIDGE_SN9C325: - reg_w(gspca_dev, 0xce, CE_sn9c325, 4); + switch (sd->sensor) { + case SENSOR_OV7630: + case SENSOR_OV7648: + reg_w(gspca_dev, 0xce, CE_ov76xx, 4); break; default: reg_w(gspca_dev, 0xce, CE, 4); @@ -1130,10 +1383,20 @@ static void sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x18, reg18); reg_w1(gspca_dev, 0x17, reg17); - reg_w1(gspca_dev, 0x01, reg1); - setbrightness(gspca_dev); - setcontrast(gspca_dev); + switch (sd->sensor) { + case SENSOR_HV7131R: + case SENSOR_MI0360: + case SENSOR_MO4000: + case SENSOR_OM6802: + setbrightness(gspca_dev); + setcontrast(gspca_dev); + break; + default: /* OV76xx */ + setbrightcont(gspca_dev); + break; + } setautogain(gspca_dev); + reg_w1(gspca_dev, 0x01, reg1); } static void sd_stopN(struct gspca_dev *gspca_dev) @@ -1156,6 +1419,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) i2c_w8(gspca_dev, stopmi0360); data = 0x29; break; + case SENSOR_OV7630: case SENSOR_OV7648: data = 0x29; break; @@ -1169,15 +1433,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]); reg_w1(gspca_dev, 0x01, sn9c1xx[1]); reg_w1(gspca_dev, 0x01, data); - reg_w1(gspca_dev, 0xf1, 0x01); -} - -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ + reg_w1(gspca_dev, 0xf1, 0x00); } static void do_autogain(struct gspca_dev *gspca_dev) @@ -1211,6 +1467,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) default: /* case SENSOR_MO4000: */ /* case SENSOR_MI0360: */ +/* case SENSOR_OM6802: */ expotimes = sd->exposure; expotimes += (luma_mean - delta) >> 6; if (expotimes < 0) @@ -1266,69 +1523,24 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } -static unsigned int getexposure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u8 hexpo, mexpo, lexpo; - - switch (sd->sensor) { - case SENSOR_HV7131R: - /* read sensor exposure */ - i2c_r5(gspca_dev, 0x25); - return (gspca_dev->usb_buf[0] << 16) - | (gspca_dev->usb_buf[1] << 8) - | gspca_dev->usb_buf[2]; - case SENSOR_MI0360: - /* read sensor exposure */ - i2c_r5(gspca_dev, 0x09); - return (gspca_dev->usb_buf[0] << 8) - | gspca_dev->usb_buf[1]; - case SENSOR_MO4000: - i2c_r5(gspca_dev, 0x0e); - hexpo = 0; /* gspca_dev->usb_buf[1] & 0x07; */ - mexpo = 0x40; /* gspca_dev->usb_buf[2] & 0xff; */ - lexpo = (gspca_dev->usb_buf[1] & 0x30) >> 4; - PDEBUG(D_CONF, "exposure %d", - (hexpo << 10) | (mexpo << 2) | lexpo); - return (hexpo << 10) | (mexpo << 2) | lexpo; - default: -/* case SENSOR_OV7648: * jfm: is it ok for 7648? */ -/* case SENSOR_OV7660: */ - /* read sensor exposure */ - i2c_r5(gspca_dev, 0x04); - hexpo = gspca_dev->usb_buf[3] & 0x2f; - lexpo = gspca_dev->usb_buf[0] & 0x02; - i2c_r5(gspca_dev, 0x08); - mexpo = gspca_dev->usb_buf[2]; - return (hexpo << 10) | (mexpo << 2) | lexpo; - } -} - -static void getbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* hardcoded registers seem not readable */ - switch (sd->sensor) { - case SENSOR_HV7131R: - sd->brightness = getexposure(gspca_dev) >> 4; - break; - case SENSOR_MI0360: - sd->brightness = getexposure(gspca_dev) << 4; - break; - case SENSOR_MO4000: - sd->brightness = getexposure(gspca_dev) << 4; - break; - } -} - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; sd->brightness = val; - if (gspca_dev->streaming) - setbrightness(gspca_dev); + if (gspca_dev->streaming) { + switch (sd->sensor) { + case SENSOR_HV7131R: + case SENSOR_MI0360: + case SENSOR_MO4000: + case SENSOR_OM6802: + setbrightness(gspca_dev); + break; + default: /* OV76xx */ + setbrightcont(gspca_dev); + break; + } + } return 0; } @@ -1336,7 +1548,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -1346,8 +1557,19 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->contrast = val; - if (gspca_dev->streaming) - setcontrast(gspca_dev); + if (gspca_dev->streaming) { + switch (sd->sensor) { + case SENSOR_HV7131R: + case SENSOR_MI0360: + case SENSOR_MO4000: + case SENSOR_OM6802: + setcontrast(gspca_dev); + break; + default: /* OV76xx */ + setbrightcont(gspca_dev); + break; + } + } return 0; } @@ -1395,17 +1617,32 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + setvflip(sd); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, }; @@ -1416,8 +1653,9 @@ static const struct sd_desc sd_desc = { | (SENSOR_ ## sensor << 8) \ | (i2c_addr) static const __devinitdata struct usb_device_id device_table[] = { -#ifndef CONFIG_USB_SN9C102 +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)}, + {USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)}, {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)}, @@ -1443,15 +1681,17 @@ static const __devinitdata struct usb_device_id device_table[] = { /* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */ /* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */ /* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C325, OV7648, 0x21)}, -/* bw600.inf: - {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C110, OV7648, 0x21)}, */ + {USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/ +/*bw600.inf:*/ + {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C110, OV7648, 0x21)}, /*sn9c325?*/ {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)}, -/* {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)}, /* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */ -#ifndef CONFIG_USB_SN9C102 +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)}, +#endif {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)}, +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE /* {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x??)}, */ {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, @@ -1475,6 +1715,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca500.c b/linux/drivers/media/video/gspca/spca500.c index be88d30a3..c33bfe48c 100644 --- a/linux/drivers/media/video/gspca/spca500.c +++ b/linux/drivers/media/video/gspca/spca500.c @@ -662,8 +662,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -897,14 +897,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) gspca_dev->usb_buf[0]); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -1068,11 +1060,9 @@ static struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -1110,6 +1100,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca501.c b/linux/drivers/media/video/gspca/spca501.c index f8df18e85..49e512cac 100644 --- a/linux/drivers/media/video/gspca/spca501.c +++ b/linux/drivers/media/video/gspca/spca501.c @@ -1964,8 +1964,8 @@ error: return -EINVAL; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2034,11 +2034,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) static void sd_stop0(struct gspca_dev *gspca_dev) { -} - -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x05, 0x00); } @@ -2131,11 +2126,10 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -2165,6 +2159,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca505.c b/linux/drivers/media/video/gspca/spca505.c index 1cde4b34f..e33666df4 100644 --- a/linux/drivers/media/video/gspca/spca505.c +++ b/linux/drivers/media/video/gspca/spca505.c @@ -724,8 +724,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int ret; @@ -812,11 +812,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) static void sd_stop0(struct gspca_dev *gspca_dev) { -} - -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ /* This maybe reset or power control */ reg_write(gspca_dev->dev, 0x03, 0x03, 0x20); reg_write(gspca_dev->dev, 0x03, 0x01, 0x0); @@ -894,11 +889,10 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -924,6 +918,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca506.c b/linux/drivers/media/video/gspca/spca506.c index f622fa757..195dce96e 100644 --- a/linux/drivers/media/video/gspca/spca506.c +++ b/linux/drivers/media/video/gspca/spca506.c @@ -313,8 +313,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; @@ -560,14 +560,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w(dev, 0x03, 0x00, 0x0003); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -740,11 +732,9 @@ static struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -772,6 +762,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca508.c b/linux/drivers/media/video/gspca/spca508.c index 52e2f7462..5e3f66ae2 100644 --- a/linux/drivers/media/video/gspca/spca508.c +++ b/linux/drivers/media/video/gspca/spca508.c @@ -1559,8 +1559,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; /* success */ } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { /* write_vector(gspca_dev, spca508_open_data); */ return 0; @@ -1592,15 +1592,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_write(gspca_dev->dev, 0x8112, 0x20); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -1671,11 +1662,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -1705,6 +1694,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index 1073ac3d2..b1329a149 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -32,69 +32,48 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned short contrast; - __u8 brightness; + __u16 contrast; /* rev72a only */ +#define CONTRAST_MIN 0x0000 +#define CONTRAST_DEF 0x2000 +#define CONTRAST_MAX 0x3fff + + __u16 exposure; /* rev12a only */ +#define EXPOSURE_MIN 1 +#define EXPOSURE_DEF 200 +#define EXPOSURE_MAX (4095 - 900) /* see set_exposure */ + + __u8 brightness; /* rev72a only */ +#define BRIGHTNESS_MIN 0 +#define BRIGHTNESS_DEF 32 +#define BRIGHTNESS_MAX 63 + + __u8 white; /* rev12a only */ +#define WHITE_MIN 1 +#define WHITE_DEF 0x40 +#define WHITE_MAX 0x7f + __u8 autogain; +#define AUTOGAIN_MIN 0 +#define AUTOGAIN_DEF 1 +#define AUTOGAIN_MAX 1 + + __u8 gain; /* rev12a only */ +#define GAIN_MIN 0x0 +#define GAIN_DEF 0x24 +#define GAIN_MAX 0x24 + +#define EXPO12A_DEF 3 + __u8 expo12a; /* expo/gain? for rev 12a */ __u8 chip_revision; +#define Rev012A 0 +#define Rev072A 1 + signed char ag_cnt; #define AG_CNT_START 13 }; -/* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); - -static struct ctrl sd_ctrls[] = { -#define SD_BRIGHTNESS 0 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 63, - .step = 1, - .default_value = 32, - }, - .set = sd_setbrightness, - .get = sd_getbrightness, - }, -#define SD_CONTRAST 1 - { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 0x3fff, - .step = 1, - .default_value = 0x2000, - }, - .set = sd_setcontrast, - .get = sd_getcontrast, - }, -#define SD_AUTOGAIN 2 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - .set = sd_setautogain, - .get = sd_getautogain, - }, -}; - -static struct v4l2_pix_format sif_mode[] = { +static struct v4l2_pix_format sif_012a_mode[] = { {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, .bytesperline = 160, .sizeimage = 160 * 120, @@ -117,6 +96,29 @@ static struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; +static struct v4l2_pix_format sif_072a_mode[] = { + {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + /* * Initialization data * I'm not very sure how to split initialization from open data @@ -143,12 +145,8 @@ static struct v4l2_pix_format sif_mode[] = { #define SPCA561_INDEX_I2C_BASE 0x8800 #define SPCA561_SNAPBIT 0x20 #define SPCA561_SNAPCTRL 0x40 -enum { - Rev072A = 0, - Rev012A, -}; -static void reg_w_val(struct usb_device *dev, __u16 index, __u16 value) +static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) { int ret; @@ -198,12 +196,6 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, index, gspca_dev->usb_buf, len, 500); } -static void i2c_init(struct gspca_dev *gspca_dev, __u8 mode) -{ - reg_w_val(gspca_dev->dev, 0x92, 0x8804); - reg_w_val(gspca_dev->dev, mode, 0x8802); -} - static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg) { int retry = 60; @@ -212,9 +204,9 @@ static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg) DataLow = valeur; DataHight = valeur >> 8; - reg_w_val(gspca_dev->dev, reg, 0x8801); - reg_w_val(gspca_dev->dev, DataLow, 0x8805); - reg_w_val(gspca_dev->dev, DataHight, 0x8800); + reg_w_val(gspca_dev->dev, 0x8801, reg); + reg_w_val(gspca_dev->dev, 0x8805, DataLow); + reg_w_val(gspca_dev->dev, 0x8800, DataHight); while (retry--) { reg_r(gspca_dev, 0x8803, 1); if (!gspca_dev->usb_buf[0]) @@ -228,14 +220,14 @@ static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) __u8 value; __u8 vallsb; - reg_w_val(gspca_dev->dev, 0x92, 0x8804); - reg_w_val(gspca_dev->dev, reg, 0x8801); - reg_w_val(gspca_dev->dev, (mode | 0x01), 0x8802); - while (retry--) { + reg_w_val(gspca_dev->dev, 0x8804, 0x92); + reg_w_val(gspca_dev->dev, 0x8801, reg); + reg_w_val(gspca_dev->dev, 0x8802, (mode | 0x01)); + do { reg_r(gspca_dev, 0x8803, 1); if (!gspca_dev->usb_buf) break; - } + } while (--retry); if (retry == 0) return -1; reg_r(gspca_dev, 0x8800, 1); @@ -438,10 +430,11 @@ static const __u16 spca561_init_data[][2] = { {0x0035, 0x8801}, /* 0x14 - set gain general */ {0x001f, 0x8805}, /* 0x14 */ {0x0000, 0x8800}, - {0x0030, 0x8112}, + {0x000e, 0x8112}, /* white balance - was 30 */ {} }; +#if 0 static void sensor_reset(struct gspca_dev *gspca_dev) { reg_w_val(gspca_dev->dev, 0x8631, 0xc8); @@ -449,10 +442,12 @@ static void sensor_reset(struct gspca_dev *gspca_dev) reg_w_val(gspca_dev->dev, 0x8112, 0x00); reg_w_val(gspca_dev->dev, 0x8114, 0x00); reg_w_val(gspca_dev->dev, 0x8118, 0x21); - i2c_init(gspca_dev, 0x14); - i2c_write(gspca_dev, 1, 0x0d); - i2c_write(gspca_dev, 0, 0x0d); + reg_w_val(gspca_dev->dev, 0x8804, 0x92); /* i2c init */ + reg_w_val(gspca_dev->dev, 0x8802, 0x14); + i2c_write(gspca_dev, 0x0001, 0x0d); + i2c_write(gspca_dev, 0x0000, 0x0d); } +#endif /******************** QC Express etch2 stuff ********************/ static const __u16 Pb100_1map8300[][2] = { @@ -462,9 +457,9 @@ static const __u16 Pb100_1map8300[][2] = { {0x8303, 0x0125}, /* image area */ {0x8304, 0x0169}, {0x8328, 0x000b}, - {0x833c, 0x0001}, + {0x833c, 0x0001}, /*fixme: win:07*/ - {0x832f, 0x0419}, + {0x832f, 0x1904}, /*fixme: was 0419*/ {0x8307, 0x00aa}, {0x8301, 0x0003}, {0x8302, 0x000e}, @@ -478,9 +473,10 @@ static const __u16 Pb100_2map8300[][2] = { }; static const __u16 spca561_161rev12A_data1[][2] = { - {0x21, 0x8118}, - {0x01, 0x8114}, - {0x00, 0x8112}, + {0x29, 0x8118}, /* white balance - was 21 */ + {0x08, 0x8114}, /* white balance - was 01 */ + {0x0e, 0x8112}, /* white balance - was 00 */ + {0x00, 0x8102}, /* white balance - new */ {0x92, 0x8804}, {0x04, 0x8802}, /* windows uses 08 */ {} @@ -505,14 +501,16 @@ static const __u16 spca561_161rev12A_data2[][2] = { {0xb0, 0x8603}, /* sensor gains */ + {0x07, 0x8601}, /* white balance - new */ + {0x07, 0x8602}, /* white balance - new */ {0x00, 0x8610}, /* *red */ {0x00, 0x8611}, /* 3f *green */ {0x00, 0x8612}, /* green *blue */ {0x00, 0x8613}, /* blue *green */ - {0x35, 0x8614}, /* green *red */ - {0x35, 0x8615}, /* 40 *green */ - {0x35, 0x8616}, /* 7a *blue */ - {0x35, 0x8617}, /* 40 *green */ + {0x43, 0x8614}, /* green *red - white balance - was 0x35 */ + {0x40, 0x8615}, /* 40 *green - white balance - was 0x35 */ + {0x71, 0x8616}, /* 7a *blue - white balance - was 0x35 */ + {0x40, 0x8617}, /* 40 *green - white balance - was 0x35 */ {0x0c, 0x8620}, /* 0c */ {0xc8, 0x8631}, /* c8 */ @@ -527,6 +525,7 @@ static const __u16 spca561_161rev12A_data2[][2] = { {0xdf, 0x863c}, /* df */ {0xf0, 0x8505}, {0x32, 0x850a}, +/* {0x99, 0x8700}, * - white balance - new (removed) */ {} }; @@ -545,9 +544,10 @@ static void sensor_mapwrite(struct gspca_dev *gspca_dev, } static void init_161rev12A(struct gspca_dev *gspca_dev) { - sensor_reset(gspca_dev); +/* sensor_reset(gspca_dev); (not in win) */ write_vector(gspca_dev, spca561_161rev12A_data1); sensor_mapwrite(gspca_dev, Pb100_1map8300); +/*fixme: should be in sd_start*/ write_vector(gspca_dev, spca561_161rev12A_data2); sensor_mapwrite(gspca_dev, Pb100_2map8300); } @@ -581,35 +581,38 @@ static int sd_config(struct gspca_dev *gspca_dev, } cam = &gspca_dev->cam; - cam->dev_name = (char *) id->driver_info; cam->epaddr = 0x01; gspca_dev->nbalt = 7 + 1; /* choose alternate 7 first */ - cam->cam_mode = sif_mode; - cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; sd->chip_revision = id->driver_info; - sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; - sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; - sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + if (sd->chip_revision == Rev012A) { + cam->cam_mode = sif_012a_mode; + cam->nmodes = ARRAY_SIZE(sif_012a_mode); + } else { + cam->cam_mode = sif_072a_mode; + cam->nmodes = ARRAY_SIZE(sif_072a_mode); + } + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->white = WHITE_DEF; + sd->exposure = EXPOSURE_DEF; + sd->autogain = AUTOGAIN_DEF; + sd->gain = GAIN_DEF; + sd->expo12a = EXPO12A_DEF; return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init_12a(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->chip_revision) { - case Rev072A: - PDEBUG(D_STREAM, "Chip revision id: 072a"); - write_vector(gspca_dev, spca561_init_data); - break; - default: -/* case Rev012A: */ - PDEBUG(D_STREAM, "Chip revision id: 012a"); - init_161rev12A(gspca_dev); - break; - } + PDEBUG(D_STREAM, "Chip revision: 012a"); + init_161rev12A(gspca_dev); + return 0; +} +static int sd_init_72a(struct gspca_dev *gspca_dev) +{ + PDEBUG(D_STREAM, "Chip revision: 072a"); + write_vector(gspca_dev, spca561_init_data); return 0; } @@ -618,25 +621,20 @@ static void setcontrast(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; __u8 lowb; - int expotimes; switch (sd->chip_revision) { case Rev072A: lowb = sd->contrast >> 8; - reg_w_val(dev, lowb, 0x8651); - reg_w_val(dev, lowb, 0x8652); - reg_w_val(dev, lowb, 0x8653); - reg_w_val(dev, lowb, 0x8654); + reg_w_val(dev, 0x8651, lowb); + reg_w_val(dev, 0x8652, lowb); + reg_w_val(dev, 0x8653, lowb); + reg_w_val(dev, 0x8654, lowb); break; - case Rev012A: { - __u8 Reg8391[] = - { 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00 }; - - /* Write camera sensor settings */ - expotimes = (sd->contrast >> 5) & 0x07ff; - Reg8391[0] = expotimes & 0xff; /* exposure */ - Reg8391[1] = 0x18 | (expotimes >> 8); - Reg8391[2] = sd->brightness; /* gain */ + default: { +/* case Rev012A: { */ + static const __u8 Reg8391[] = + { 0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00 }; + reg_w_buf(gspca_dev, 0x8391, Reg8391, 8); reg_w_buf(gspca_dev, 0x8390, Reg8391, 8); break; @@ -644,93 +642,151 @@ static void setcontrast(struct gspca_dev *gspca_dev) } } -static void setautogain(struct gspca_dev *gspca_dev) +/* rev12a only */ +static void setwhite(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + __u16 white; + __u8 reg8614, reg8616; + + white = sd->white; + /* try to emulate MS-win as possible */ + reg8616 = 0x90 - white * 5 / 8; + reg_w_val(gspca_dev->dev, 0x8616, reg8616); + reg8614 = 0x20 + white * 3 / 8; + reg_w_val(gspca_dev->dev, 0x8614, reg8614); +} - if (sd->chip_revision == Rev072A) { - if (sd->autogain) - sd->ag_cnt = AG_CNT_START; - else - sd->ag_cnt = -1; +/* rev 12a only */ +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int expo; + int clock_divider; + __u8 data[2]; + + /* Register 0x8309 controls exposure for the spca561, + the basic exposure setting goes from 1-2047, where 1 is completely + dark and 2047 is very bright. It not only influences exposure but + also the framerate (to allow for longer exposure) from 1 - 300 it + only raises the exposure time then from 300 - 600 it halves the + framerate to be able to further raise the exposure time and for every + 300 more it halves the framerate again. This allows for a maximum + exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps). + Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12 + configure a divider for the base framerate which us used at the + exposure setting of 1-300. These bits configure the base framerate + according to the following formula: fps = 60 / (value + 2) */ + if (sd->exposure < 2048) { + expo = sd->exposure; + clock_divider = 0; + } else { + /* Add 900 to make the 0 setting of the second part of the + exposure equal to the 2047 setting of the first part. */ + expo = (sd->exposure - 2048) + 900; + clock_divider = 3; } + expo |= clock_divider << 11; + data[0] = expo; + data[1] = expo >> 8; + reg_w_buf(gspca_dev, 0x8309, data, 2); +} + +/* rev 12a only */ +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 data[2]; + + data[0] = sd->gain; + data[1] = 0; + reg_w_buf(gspca_dev, 0x8335, data, 2); } -static void sd_start(struct gspca_dev *gspca_dev) +static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + + if (sd->autogain) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; +} + +static void sd_start_12a(struct gspca_dev *gspca_dev) +{ struct usb_device *dev = gspca_dev->dev; - int Clck; + int Clck = 0x8a; /* lower 0x8X values lead to fps > 30 */ __u8 Reg8307[] = { 0xaa, 0x00 }; int mode; mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - switch (sd->chip_revision) { - case Rev072A: - switch (mode) { - default: -/* case 0: - case 1: */ - Clck = 0x25; - break; - case 2: - Clck = 0x22; - break; - case 3: - Clck = 0x21; - break; - } - reg_w_val(dev, 0x8500, mode); /* mode */ - reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ - reg_w_val(dev, 0x8112, 0x10 | 0x20); - setautogain(gspca_dev); - break; + if (mode <= 1) { + /* Use compression on 320x240 and above */ + reg_w_val(dev, 0x8500, 0x10 | mode); + } else { + /* I couldn't get the compression to work below 320x240 + * Fortunately at these resolutions the bandwidth + * is sufficient to push raw frames at ~20fps */ + reg_w_val(dev, 0x8500, mode); + } /* -- qq@kuku.eu.org */ + reg_w_buf(gspca_dev, 0x8307, Reg8307, 2); + reg_w_val(gspca_dev->dev, 0x8700, Clck); + /* 0x8f 0x85 0x27 clock */ + reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); + reg_w_val(gspca_dev->dev, 0x850b, 0x03); + setcontrast(gspca_dev); + setwhite(gspca_dev); + setautogain(gspca_dev); + setexposure(gspca_dev); +} +static void sd_start_72a(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int Clck; + int mode; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + switch (mode) { default: -/* case Rev012A: */ - switch (mode) { - case 0: - case 1: - Clck = 0x8a; - break; - case 2: - Clck = 0x85; - break; - default: - Clck = 0x83; - break; - } - if (mode <= 1) { - /* Use compression on 320x240 and above */ - reg_w_val(dev, 0x8500, 0x10 | mode); - } else { - /* I couldn't get the compression to work below 320x240 - * Fortunately at these resolutions the bandwidth - * is sufficient to push raw frames at ~20fps */ - reg_w_val(dev, 0x8500, mode); - } /* -- qq@kuku.eu.org */ - reg_w_buf(gspca_dev, 0x8307, Reg8307, 2); - reg_w_val(gspca_dev->dev, 0x8700, Clck); - /* 0x8f 0x85 0x27 clock */ - reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); - reg_w_val(gspca_dev->dev, 0x850b, 0x03); - setcontrast(gspca_dev); +/* case 0: + case 1: */ + Clck = 0x25; + break; + case 2: + Clck = 0x22; + break; + case 3: + Clck = 0x21; break; } + reg_w_val(dev, 0x8500, mode); /* mode */ + reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ + reg_w_val(dev, 0x8112, 0x10 | 0x20); + setautogain(gspca_dev); } static void sd_stopN(struct gspca_dev *gspca_dev) { - reg_w_val(gspca_dev->dev, 0x8112, 0x20); + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->chip_revision == Rev012A) { + reg_w_val(gspca_dev->dev, 0x8112, 0x0e); + } else { + reg_w_val(gspca_dev->dev, 0x8112, 0x20); +/* reg_w_val(gspca_dev->dev, 0x8102, 0x00); ?? */ + } } static void sd_stop0(struct gspca_dev *gspca_dev) { -} + struct sd *sd = (struct sd *) gspca_dev; -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ - reg_w_val(gspca_dev->dev, 0x8114, 0); + if (sd->chip_revision == Rev012A) { + reg_w_val(gspca_dev->dev, 0x8118, 0x29); + reg_w_val(gspca_dev->dev, 0x8114, 0x08); + } +/* reg_w_val(gspca_dev->dev, 0x8114, 0); */ } static void do_autogain(struct gspca_dev *gspca_dev) @@ -744,6 +800,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) __u8 luma_mean = 110; __u8 luma_delta = 20; __u8 spring = 4; + __u8 reg8339[2]; if (sd->ag_cnt < 0) return; @@ -798,13 +855,16 @@ static void do_autogain(struct gspca_dev *gspca_dev) } break; case Rev012A: - /* sensor registers is access and memory mapped to 0x8300 */ - /* readind all 0x83xx block the sensor */ - /* - * The data from the header seem wrong where is the luma - * and chroma mean value - * at the moment set exposure in contrast set - */ + reg_r(gspca_dev, 0x8330, 2); + if (gspca_dev->usb_buf[1] > 0x08) { + reg8339[0] = ++sd->expo12a; + reg8339[1] = 0; + reg_w_buf(gspca_dev, 0x8339, reg8339, 2); + } else if (gspca_dev->usb_buf[1] < 0x02) { + reg8339[0] = --sd->expo12a; + reg8339[1] = 0; + reg_w_buf(gspca_dev, 0x8339, reg8339, 2); + } break; } } @@ -814,6 +874,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; + switch (data[0]) { case 0: /* start of frame */ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, @@ -826,8 +888,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, frame, data, len); } else { /* raw bayer (with a header, which we skip) */ - data += 20; - len -= 20; + if (sd->chip_revision == Rev012A) { + data += 20; + len -= 20; + } else { + data += 16; + len -= 16; + } gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); } @@ -841,24 +908,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } +/* rev 72a only */ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u8 value; - switch (sd->chip_revision) { - case Rev072A: - value = sd->brightness; - reg_w_val(gspca_dev->dev, value, 0x8611); - reg_w_val(gspca_dev->dev, value, 0x8612); - reg_w_val(gspca_dev->dev, value, 0x8613); - reg_w_val(gspca_dev->dev, value, 0x8614); - break; - default: -/* case Rev012A: */ - setcontrast(gspca_dev); - break; - } + value = sd->brightness; + reg_w_val(gspca_dev->dev, 0x8611, value); + reg_w_val(gspca_dev->dev, 0x8612, value); + reg_w_val(gspca_dev->dev, 0x8613, value); + reg_w_val(gspca_dev->dev, 0x8614, value); } static void getbrightness(struct gspca_dev *gspca_dev) @@ -866,52 +926,38 @@ static void getbrightness(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; __u16 tot; - switch (sd->chip_revision) { - case Rev072A: - tot = 0; - reg_r(gspca_dev, 0x8611, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8612, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8613, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8614, 1); - tot += gspca_dev->usb_buf[0]; - sd->brightness = tot >> 2; - break; - default: -/* case Rev012A: */ - /* no way to read sensor settings */ - break; - } + tot = 0; + reg_r(gspca_dev, 0x8611, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8612, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8613, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8614, 1); + tot += gspca_dev->usb_buf[0]; + sd->brightness = tot >> 2; } +/* rev72a only */ static void getcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u16 tot; - switch (sd->chip_revision) { - case Rev072A: - tot = 0; - reg_r(gspca_dev, 0x8651, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8652, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8653, 1); - tot += gspca_dev->usb_buf[0]; - reg_r(gspca_dev, 0x8654, 1); - tot += gspca_dev->usb_buf[0]; - sd->contrast = tot << 6; - break; - default: -/* case Rev012A: */ - /* no way to read sensor settings */ - break; - } + tot = 0; + reg_r(gspca_dev, 0x8651, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8652, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8653, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8654, 1); + tot += gspca_dev->usb_buf[0]; + sd->contrast = tot << 6; PDEBUG(D_CONF, "get contrast %d", sd->contrast); } +/* rev 72a only */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -931,6 +977,7 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +/* rev 72a only */ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -968,20 +1015,190 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +/* rev12a only */ +static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->white = val; + if (gspca_dev->streaming) + setwhite(gspca_dev); + return 0; +} + +static int sd_getwhite(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->white; + return 0; +} + +/* rev12a only */ +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +/* rev12a only */ +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +/* control tables */ +static struct ctrl sd_ctrls_12a[] = { + { + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "While Balance", + .minimum = WHITE_MIN, + .maximum = WHITE_MAX, + .step = 1, + .default_value = WHITE_DEF, + }, + .set = sd_setwhite, + .get = sd_getwhite, + }, + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = EXPOSURE_MIN, + .maximum = EXPOSURE_MAX, + .step = 1, + .default_value = EXPOSURE_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = AUTOGAIN_MIN, + .maximum = AUTOGAIN_MAX, + .step = 1, + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = GAIN_MIN, + .maximum = GAIN_MAX, + .step = 1, + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, +}; + +static struct ctrl sd_ctrls_72a[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = BRIGHTNESS_MIN, + .maximum = BRIGHTNESS_MAX, + .step = 1, + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = CONTRAST_MIN, + .maximum = CONTRAST_MAX, + .step = 1, + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = AUTOGAIN_MIN, + .maximum = AUTOGAIN_MAX, + .step = 1, + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + /* sub-driver description */ -static const struct sd_desc sd_desc = { +static const struct sd_desc sd_desc_12a = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_12a, + .nctrls = ARRAY_SIZE(sd_ctrls_12a), + .config = sd_config, + .init = sd_init_12a, + .start = sd_start_12a, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +/* .dq_callback = do_autogain, * fixme */ +}; +static const struct sd_desc sd_desc_72a = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), + .ctrls = sd_ctrls_72a, + .nctrls = ARRAY_SIZE(sd_ctrls_72a), .config = sd_config, - .open = sd_open, - .start = sd_start, + .init = sd_init_72a, + .start = sd_start_72a, .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, }; +static const struct sd_desc *sd_desc[2] = { + &sd_desc_12a, + &sd_desc_72a +}; /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { @@ -1009,7 +1226,9 @@ MODULE_DEVICE_TABLE(usb, device_table); static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + return gspca_dev_probe(intf, id, + sd_desc[id->driver_info], + sizeof(struct sd), THIS_MODULE); } @@ -1018,6 +1237,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/stk014.c b/linux/drivers/media/video/gspca/stk014.c index 16219cf6a..2f2de429e 100644 --- a/linux/drivers/media/video/gspca/stk014.c +++ b/linux/drivers/media/video/gspca/stk014.c @@ -306,8 +306,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { int ret; @@ -398,14 +398,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "camera stopped"); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -535,11 +527,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, }; @@ -564,6 +554,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/sunplus.c b/linux/drivers/media/video/gspca/sunplus.c index 8701b07c8..e8e5c47ba 100644 --- a/linux/drivers/media/video/gspca/sunplus.c +++ b/linux/drivers/media/video/gspca/sunplus.c @@ -480,31 +480,47 @@ static const __u8 qtable_spca504_default[2][64] = { 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} }; -static void reg_r(struct usb_device *dev, - __u16 req, - __u16 index, - __u8 *buffer, __u16 length) +/* read <len> bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 req, + __u16 index, + __u16 len) { - usb_control_msg(dev, - usb_rcvctrlpipe(dev, 0), +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + err("reg_r: buffer overflow"); + return; + } +#endif + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, /* value */ - index, buffer, length, + index, + len ? gspca_dev->usb_buf : NULL, len, 500); } -static void reg_w(struct usb_device *dev, - __u16 req, - __u16 value, - __u16 index, - __u8 *buffer, __u16 length) +/* write <len> bytes from gspca_dev->usb_buf */ +static void reg_w(struct gspca_dev *gspca_dev, + __u16 req, + __u16 value, + __u16 index, + __u16 len) { - usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), +#ifdef GSPCA_DEBUG + if (len > USB_BUF_SZ) { + err("reg_w: buffer overflow"); + return; + } +#endif + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, buffer, length, + value, index, + len ? gspca_dev->usb_buf : NULL, len, 500); } @@ -665,7 +681,7 @@ static int spca504B_PollingDataReady(struct gspca_dev *gspca_dev) int count = 10; while (--count > 0) { - reg_r(gspca_dev->dev, 0x21, 0, gspca_dev->usb_buf, 1); + reg_r(gspca_dev, 0x21, 0, 1); if ((gspca_dev->usb_buf[0] & 0x01) == 0) break; msleep(10); @@ -675,15 +691,14 @@ static int spca504B_PollingDataReady(struct gspca_dev *gspca_dev) static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; int count = 50; while (--count > 0) { - reg_r(dev, 0x21, 1, gspca_dev->usb_buf, 1); + reg_r(gspca_dev, 0x21, 1, 1); if (gspca_dev->usb_buf[0] != 0) { gspca_dev->usb_buf[0] = 0; - reg_w(dev, 0x21, 0, 1, gspca_dev->usb_buf, 1); - reg_r(dev, 0x21, 1, gspca_dev->usb_buf, 1); + reg_w(gspca_dev, 0x21, 0, 1, 1); + reg_r(gspca_dev, 0x21, 1, 1); spca504B_PollingDataReady(gspca_dev); break; } @@ -693,16 +708,14 @@ static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; __u8 *data; - data = kmalloc(64, GFP_KERNEL); - reg_r(dev, 0x20, 0, data, 5); + data = gspca_dev->usb_buf; + reg_r(gspca_dev, 0x20, 0, 5); PDEBUG(D_STREAM, "FirmWare : %d %d %d %d %d ", data[0], data[1], data[2], data[3], data[4]); - reg_r(dev, 0x23, 0, data, 64); - reg_r(dev, 0x23, 1, data, 64); - kfree(data); + reg_r(gspca_dev, 0x23, 0, 64); + reg_r(gspca_dev, 0x23, 1, 64); } static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) @@ -717,21 +730,21 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) Type = 0; switch (sd->bridge) { case BRIDGE_SPCA533: - reg_w(dev, 0x31, 0, 0, NULL, 0); + reg_w(gspca_dev, 0x31, 0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); rc = spca504B_PollingDataReady(gspca_dev); spca50x_GetFirmware(gspca_dev); gspca_dev->usb_buf[0] = 2; /* type */ - reg_w(dev, 0x24, 0, 8, gspca_dev->usb_buf, 1); - reg_r(dev, 0x24, 8, gspca_dev->usb_buf, 1); + reg_w(gspca_dev, 0x24, 0, 8, 1); + reg_r(gspca_dev, 0x24, 8, 1); gspca_dev->usb_buf[0] = Size; - reg_w(dev, 0x25, 0, 4, gspca_dev->usb_buf, 1); - reg_r(dev, 0x25, 4, gspca_dev->usb_buf, 1); /* size */ + reg_w(gspca_dev, 0x25, 0, 4, 1); + reg_r(gspca_dev, 0x25, 4, 1); /* size */ rc = spca504B_PollingDataReady(gspca_dev); /* Init the cam width height with some values get on init ? */ - reg_w(dev, 0x31, 0, 4, NULL, 0); + reg_w(gspca_dev, 0x31, 0, 4, 0); spca504B_WaitCmdStatus(gspca_dev); rc = spca504B_PollingDataReady(gspca_dev); break; @@ -739,12 +752,12 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA504B: */ /* case BRIDGE_SPCA536: */ gspca_dev->usb_buf[0] = Size; - reg_w(dev, 0x25, 0, 4, gspca_dev->usb_buf, 1); - reg_r(dev, 0x25, 4, gspca_dev->usb_buf, 1); /* size */ + reg_w(gspca_dev, 0x25, 0, 4, 1); + reg_r(gspca_dev, 0x25, 4, 1); /* size */ Type = 6; gspca_dev->usb_buf[0] = Type; - reg_w(dev, 0x27, 0, 0, gspca_dev->usb_buf, 1); - reg_r(dev, 0x27, 0, gspca_dev->usb_buf, 1); /* type */ + reg_w(gspca_dev, 0x27, 0, 0, 1); + reg_r(gspca_dev, 0x27, 0, 1); /* type */ rc = spca504B_PollingDataReady(gspca_dev); break; case BRIDGE_SPCA504: @@ -783,18 +796,15 @@ static void spca504_wait_status(struct gspca_dev *gspca_dev) static void spca504B_setQtable(struct gspca_dev *gspca_dev) { - struct usb_device *dev = gspca_dev->dev; - gspca_dev->usb_buf[0] = 3; - reg_w(dev, 0x26, 0, 0, gspca_dev->usb_buf, 1); - reg_r(dev, 0x26, 0, gspca_dev->usb_buf, 1); + reg_w(gspca_dev, 0x26, 0, 0, 1); + reg_r(gspca_dev, 0x26, 0, 1); spca504B_PollingDataReady(gspca_dev); } static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; int pollreg = 1; switch (sd->bridge) { @@ -805,20 +815,20 @@ static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev) default: /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA504B: */ - reg_w(dev, 0, 0, 0x21a7, NULL, 0); /* brightness */ - reg_w(dev, 0, 0x20, 0x21a8, NULL, 0); /* contrast */ - reg_w(dev, 0, 0, 0x21ad, NULL, 0); /* hue */ - reg_w(dev, 0, 1, 0x21ac, NULL, 0); /* sat/hue */ - reg_w(dev, 0, 0x20, 0x21ae, NULL, 0); /* saturation */ - reg_w(dev, 0, 0, 0x21a3, NULL, 0); /* gamma */ + reg_w(gspca_dev, 0, 0, 0x21a7, 0); /* brightness */ + reg_w(gspca_dev, 0, 0x20, 0x21a8, 0); /* contrast */ + reg_w(gspca_dev, 0, 0, 0x21ad, 0); /* hue */ + reg_w(gspca_dev, 0, 1, 0x21ac, 0); /* sat/hue */ + reg_w(gspca_dev, 0, 0x20, 0x21ae, 0); /* saturation */ + reg_w(gspca_dev, 0, 0, 0x21a3, 0); /* gamma */ break; case BRIDGE_SPCA536: - reg_w(dev, 0, 0, 0x20f0, NULL, 0); - reg_w(dev, 0, 0x21, 0x20f1, NULL, 0); - reg_w(dev, 0, 0x40, 0x20f5, NULL, 0); - reg_w(dev, 0, 1, 0x20f4, NULL, 0); - reg_w(dev, 0, 0x40, 0x20f6, NULL, 0); - reg_w(dev, 0, 0, 0x2089, NULL, 0); + reg_w(gspca_dev, 0, 0, 0x20f0, 0); + reg_w(gspca_dev, 0, 0x21, 0x20f1, 0); + reg_w(gspca_dev, 0, 0x40, 0x20f5, 0); + reg_w(gspca_dev, 0, 1, 0x20f4, 0); + reg_w(gspca_dev, 0, 0x40, 0x20f6, 0); + reg_w(gspca_dev, 0, 0, 0x2089, 0); break; } if (pollreg) @@ -830,7 +840,6 @@ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; struct cam *cam; cam = &gspca_dev->cam; @@ -842,7 +851,7 @@ static int sd_config(struct gspca_dev *gspca_dev, if (sd->subtype == AiptekMiniPenCam13) { /* try to get the firmware as some cam answer 2.0.1.2.2 * and should be a spca504b then overwrite that setting */ - reg_r(dev, 0x20, 0, gspca_dev->usb_buf, 1); + reg_r(gspca_dev, 0x20, 0, 1); switch (gspca_dev->usb_buf[0]) { case 1: break; /* (right bridge/subtype) */ @@ -879,8 +888,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; @@ -891,12 +900,12 @@ static int sd_open(struct gspca_dev *gspca_dev) switch (sd->bridge) { case BRIDGE_SPCA504B: - reg_w(dev, 0x1d, 0, 0, NULL, 0); - reg_w(dev, 0, 1, 0x2306, NULL, 0); - reg_w(dev, 0, 0, 0x0d04, NULL, 0); - reg_w(dev, 0, 0, 0x2000, NULL, 0); - reg_w(dev, 0, 0x13, 0x2301, NULL, 0); - reg_w(dev, 0, 0, 0x2306, NULL, 0); + reg_w(gspca_dev, 0x1d, 0, 0, 0); + reg_w(gspca_dev, 0, 1, 0x2306, 0); + reg_w(gspca_dev, 0, 0, 0x0d04, 0); + reg_w(gspca_dev, 0, 0, 0x2000, 0); + reg_w(gspca_dev, 0, 0x13, 0x2301, 0); + reg_w(gspca_dev, 0, 0, 0x2306, 0); /* fall thru */ case BRIDGE_SPCA533: rc = spca504B_PollingDataReady(gspca_dev); @@ -904,12 +913,12 @@ static int sd_open(struct gspca_dev *gspca_dev) break; case BRIDGE_SPCA536: spca50x_GetFirmware(gspca_dev); - reg_r(dev, 0x00, 0x5002, gspca_dev->usb_buf, 1); + reg_r(gspca_dev, 0x00, 0x5002, 1); gspca_dev->usb_buf[0] = 0; - reg_w(dev, 0x24, 0, 0, gspca_dev->usb_buf, 1); - reg_r(dev, 0x24, 0, gspca_dev->usb_buf, 1); + reg_w(gspca_dev, 0x24, 0, 0, 1); + reg_r(gspca_dev, 0x24, 0, 1); rc = spca504B_PollingDataReady(gspca_dev); - reg_w(dev, 0x34, 0, 0, NULL, 0); + reg_w(gspca_dev, 0x34, 0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); break; case BRIDGE_SPCA504C: /* pccam600 */ @@ -1002,12 +1011,12 @@ static void sd_start(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA536: */ if (sd->subtype == MegapixV4 || sd->subtype == LogitechClickSmart820) { - reg_w(dev, 0xf0, 0, 0, NULL, 0); + reg_w(gspca_dev, 0xf0, 0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); - reg_r(dev, 0xf0, 4, NULL, 0); + reg_r(gspca_dev, 0xf0, 4, 0); spca504B_WaitCmdStatus(gspca_dev); } else { - reg_w(dev, 0x31, 0, 4, NULL, 0); + reg_w(gspca_dev, 0x31, 0, 4, 0); spca504B_WaitCmdStatus(gspca_dev); rc = spca504B_PollingDataReady(gspca_dev); } @@ -1076,7 +1085,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA536: */ /* case BRIDGE_SPCA504B: */ - reg_w(dev, 0x31, 0, 0, NULL, 0); + reg_w(gspca_dev, 0x31, 0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); spca504B_PollingDataReady(gspca_dev); break; @@ -1100,14 +1109,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -1400,11 +1401,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -1487,6 +1486,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/t613.c b/linux/drivers/media/video/gspca/t613.c index 56b1dfa72..c7673661a 100644 --- a/linux/drivers/media/video/gspca/t613.c +++ b/linux/drivers/media/video/gspca/t613.c @@ -30,7 +30,7 @@ #define MAX_GAMMA 0x10 /* 0 to 15 */ -#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 0) MODULE_AUTHOR("Leandro Costantino <le_costantino@pixartargentina.com.ar>"); MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver"); @@ -233,7 +233,7 @@ static char *effects_control[] = { static struct v4l2_pix_format vga_mode_t16[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, - .sizeimage = 160 * 120 * 3 / 8 + 590, + .sizeimage = 160 * 120 * 4 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 4}, {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, @@ -391,7 +391,7 @@ static void reg_w(struct gspca_dev *gspca_dev, NULL, 0, 500); return; } - if (len <= sizeof gspca_dev->usb_buf) { + if (len <= USB_BUF_SZ) { memcpy(gspca_dev->usb_buf, buffer, len); usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -563,6 +563,13 @@ static int init_default_parameters(struct gspca_dev *gspca_dev) return 0; } +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + init_default_parameters(gspca_dev); + return 0; +} + static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -916,18 +923,6 @@ static void sd_start(struct gspca_dev *gspca_dev) #endif } -static void sd_stopN(struct gspca_dev *gspca_dev) -{ -} - -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -995,24 +990,14 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) -{ - init_default_parameters(gspca_dev); - return 0; -} - /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, }; @@ -1037,6 +1022,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/tv8532.c b/linux/drivers/media/video/gspca/tv8532.c index fcac077bb..01de48d4e 100644 --- a/linux/drivers/media/video/gspca/tv8532.c +++ b/linux/drivers/media/video/gspca/tv8532.c @@ -331,8 +331,8 @@ static void tv_8532_PollReg(struct gspca_dev *gspca_dev) } } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32); reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00); @@ -450,14 +450,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); } -static void sd_stop0(struct gspca_dev *gspca_dev) -{ -} - -static void sd_close(struct gspca_dev *gspca_dev) -{ -} - static void tv8532_preprocess(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -620,11 +612,9 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, }; @@ -653,6 +643,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 0e23edb16..cfb66c257 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -69,6 +69,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, +#define LIGHTFREQ_IDX 1 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -79,7 +80,6 @@ static struct ctrl sd_ctrls[] = { .step = 1, #define FREQ_DEF 1 .default_value = FREQ_DEF, - .default_value = 1, }, .set = sd_setfreq, .get = sd_getfreq, @@ -87,12 +87,12 @@ static struct ctrl sd_ctrls[] = { }; static struct v4l2_pix_format vc0321_mode[] = { - {320, 240, V4L2_PIX_FMT_YUV420, V4L2_FIELD_NONE, + {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, - {640, 480, V4L2_PIX_FMT_YUV420, V4L2_FIELD_NONE, + {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, .bytesperline = 640, .sizeimage = 640 * 480 * 2, .colorspace = V4L2_COLORSPACE_SRGB, @@ -1643,6 +1643,8 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->qindex = 7; sd->autogain = AUTOGAIN_DEF; sd->lightfreq = FREQ_DEF; + if (sd->sensor != SENSOR_OV7670) + gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); if (sd->bridge == BRIDGE_VC0321) { reg_r(gspca_dev, 0x8a, 0, 3); @@ -1654,8 +1656,8 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and time */ +static int sd_init(struct gspca_dev *gspca_dev) { return 0; } @@ -1834,19 +1836,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) reg_w(dev, 0x89, 0xffff, 0xffff); } -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ -/* struct usb_device *dev = gspca_dev->dev; - __u8 buffread; - - reg_w(dev, 0x89, 0xffff, 0xffff); - reg_w(dev, 0xa0, 0x01, 0xb301); - reg_w(dev, 0xa0, 0x09, 0xb303); - reg_w(dev, 0x89, 0xffff, 0xffff); -*/ -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -1935,11 +1924,10 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, }; @@ -1971,6 +1959,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; /* -- module insert / remove -- */ diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c index ff0c30e4a..3153bd4cd 100644 --- a/linux/drivers/media/video/gspca/zc3xx.c +++ b/linux/drivers/media/video/gspca/zc3xx.c @@ -85,6 +85,7 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { +#define BRIGHTNESS_IDX 0 #define SD_BRIGHTNESS 0 { { @@ -141,6 +142,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, +#define LIGHTFREQ_IDX 4 #define SD_FREQ 4 { { @@ -6579,8 +6581,8 @@ static int setlightfreq(struct gspca_dev *gspca_dev) cs2102_60HZ, cs2102_60HZScale}, /* SENSOR_CS2102K 1 */ {cs2102_NoFliker, cs2102_NoFlikerScale, - cs2102_50HZ, cs2102_50HZScale, - cs2102_60HZ, cs2102_60HZScale}, + NULL, NULL, /* currently disabled */ + NULL, NULL}, /* SENSOR_GC0305 2 */ {gc0305_NoFliker, gc0305_NoFliker, gc0305_50HZ, gc0305_50HZ, @@ -6969,8 +6971,13 @@ static int zcxx_probeSensor(struct gspca_dev *gspca_dev) case SENSOR_MC501CB: return -1; /* don't probe */ case SENSOR_TAS5130C_VF0250: - /* may probe but with write in reg 0x0010 */ + /* may probe but with no write in reg 0x0010 */ return -1; /* don't probe */ + case SENSOR_PAS106: + sensor = sif_probe(gspca_dev); + if (sensor >= 0) + return sensor; + break; } sensor = vga_2wr_probe(gspca_dev); if (sensor >= 0) { @@ -6979,12 +6986,10 @@ static int zcxx_probeSensor(struct gspca_dev *gspca_dev) /* next probe is needed for OmniVision ? */ } sensor2 = vga_3wr_probe(gspca_dev); - if (sensor2 >= 0) { - if (sensor >= 0) - return sensor; - return sensor2; - } - return sif_probe(gspca_dev); + if (sensor2 >= 0 + && sensor >= 0) + return sensor; + return sensor2; } /* this function is called at probe time */ @@ -7152,13 +7157,27 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value; + switch (sd->sensor) { + case SENSOR_GC0305: + case SENSOR_OV7620: + case SENSOR_PO2030: + gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); + break; + case SENSOR_HDCS2020: + case SENSOR_HV7131B: + case SENSOR_HV7131C: + case SENSOR_OV7630C: + gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); + break; + } + /* switch the led off */ reg_w(gspca_dev->dev, 0x01, 0x0000); return 0; } -/* this function is called at open time */ -static int sd_open(struct gspca_dev *gspca_dev) +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) { reg_w(gspca_dev->dev, 0x01, 0x0000); return 0; @@ -7319,10 +7338,6 @@ static void sd_start(struct gspca_dev *gspca_dev) } } -static void sd_stopN(struct gspca_dev *gspca_dev) -{ -} - static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -7330,18 +7345,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) send_unknown(gspca_dev->dev, sd->sensor); } -/* this function is called at close time */ -static void sd_close(struct gspca_dev *gspca_dev) -{ -#if 0 /* test */ - struct usb_device *dev = gspca_dev->dev; - - reg_r(gspca_dev, 0x0180); - reg_w(dev, 0x00, 0x0180); - reg_w(dev, 0x01, 0x0000); -#endif -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, __u8 *data, @@ -7501,37 +7504,30 @@ static const struct sd_desc sd_desc = { .ctrls = sd_ctrls, .nctrls = sizeof sd_ctrls / sizeof sd_ctrls[0], .config = sd_config, - .open = sd_open, + .init = sd_init, .start = sd_start, - .stopN = sd_stopN, .stop0 = sd_stop0, - .close = sd_close, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, }; static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x041e)}, -#ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x041e, 0x4017)}, - {USB_DEVICE(0x041e, 0x401c)}, + {USB_DEVICE(0x041e, 0x401c), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x041e, 0x401e)}, {USB_DEVICE(0x041e, 0x401f)}, -#endif + {USB_DEVICE(0x041e, 0x4022)}, {USB_DEVICE(0x041e, 0x4029)}, -#ifndef CONFIG_USB_ZC0301 - {USB_DEVICE(0x041e, 0x4034)}, - {USB_DEVICE(0x041e, 0x4035)}, + {USB_DEVICE(0x041e, 0x4034), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x041e, 0x4035), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x041e, 0x4036)}, {USB_DEVICE(0x041e, 0x403a)}, -#endif {USB_DEVICE(0x041e, 0x4051), .driver_info = SENSOR_TAS5130C_VF0250}, {USB_DEVICE(0x041e, 0x4053), .driver_info = SENSOR_TAS5130C_VF0250}, -#ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x0458, 0x7007)}, {USB_DEVICE(0x0458, 0x700c)}, {USB_DEVICE(0x0458, 0x700f)}, -#endif {USB_DEVICE(0x0461, 0x0a00)}, {USB_DEVICE(0x046d, 0x08a0)}, {USB_DEVICE(0x046d, 0x08a1)}, @@ -7543,7 +7539,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x046d, 0x08aa)}, {USB_DEVICE(0x046d, 0x08ac)}, {USB_DEVICE(0x046d, 0x08ad)}, -#ifndef CONFIG_USB_ZC0301 +#if !defined CONFIG_USB_ZC0301 && !defined CONFIG_USB_ZC0301_MODULE {USB_DEVICE(0x046d, 0x08ae)}, #endif {USB_DEVICE(0x046d, 0x08af)}, @@ -7553,27 +7549,25 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x046d, 0x08d8)}, {USB_DEVICE(0x046d, 0x08da)}, {USB_DEVICE(0x046d, 0x08dd), .driver_info = SENSOR_MC501CB}, - {USB_DEVICE(0x0471, 0x0325)}, - {USB_DEVICE(0x0471, 0x0326)}, - {USB_DEVICE(0x0471, 0x032d)}, - {USB_DEVICE(0x0471, 0x032e)}, + {USB_DEVICE(0x0471, 0x0325), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x0326), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x032d), .driver_info = SENSOR_PAS106}, + {USB_DEVICE(0x0471, 0x032e), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x055f, 0xc005)}, -#ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x055f, 0xd003)}, {USB_DEVICE(0x055f, 0xd004)}, -#endif {USB_DEVICE(0x0698, 0x2003)}, + {USB_DEVICE(0x0ac8, 0x0301), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x0ac8, 0x0302)}, -#ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x0ac8, 0x301b)}, +#if !defined CONFIG_USB_ZC0301 && !defined CONFIG_USB_ZC0301_MODULE {USB_DEVICE(0x0ac8, 0x303b)}, #endif {USB_DEVICE(0x0ac8, 0x305b), .driver_info = SENSOR_TAS5130C_VF0250}, -#ifndef CONFIG_USB_ZC0301 {USB_DEVICE(0x0ac8, 0x307b)}, {USB_DEVICE(0x10fd, 0x0128)}, + {USB_DEVICE(0x10fd, 0x804d)}, {USB_DEVICE(0x10fd, 0x8050)}, -#endif {} /* end of entry */ }; #undef DVNAME @@ -7593,6 +7587,10 @@ static struct usb_driver sd_driver = { .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif }; static int __init sd_mod_init(void) diff --git a/linux/drivers/media/video/ivtv/ivtv-cards.h b/linux/drivers/media/video/ivtv/ivtv-cards.h index 381af1bce..0b8fe85fb 100644 --- a/linux/drivers/media/video/ivtv/ivtv-cards.h +++ b/linux/drivers/media/video/ivtv/ivtv-cards.h @@ -154,7 +154,7 @@ #define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \ V4L2_CAP_SLICED_VBI_CAPTURE) -#define IVTV_CAP_DECODER (V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT | \ +#define IVTV_CAP_DECODER (V4L2_CAP_VIDEO_OUTPUT | \ V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY) struct ivtv_card_video_input { diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c index efdb59193..def2c5502 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.c +++ b/linux/drivers/media/video/ivtv/ivtv-driver.c @@ -655,9 +655,9 @@ done: if (itv->card == NULL) { itv->card = ivtv_get_card(IVTV_CARD_PVR_150); - IVTV_ERR("Unknown card: vendor/device: %04x/%04x\n", + IVTV_ERR("Unknown card: vendor/device: [%04x:%04x]\n", itv->dev->vendor, itv->dev->device); - IVTV_ERR(" subsystem vendor/device: %04x/%04x\n", + IVTV_ERR(" subsystem vendor/device: [%04x:%04x]\n", itv->dev->subsystem_vendor, itv->dev->subsystem_device); IVTV_ERR(" %s based\n", chipname); IVTV_ERR("Defaulting to %s card\n", itv->card->name); @@ -688,7 +688,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) spin_lock_init(&itv->lock); spin_lock_init(&itv->dma_reg_lock); - itv->irq_work_queues = create_workqueue(itv->name); + itv->irq_work_queues = create_singlethread_workqueue(itv->name); if (itv->irq_work_queues == NULL) { IVTV_ERR("Could not create ivtv workqueue\n"); return -1; diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.h b/linux/drivers/media/video/ivtv/ivtv-driver.h index d66ee14b6..80673a3da 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.h +++ b/linux/drivers/media/video/ivtv/ivtv-driver.h @@ -49,7 +49,6 @@ #include <linux/i2c-algo-bit.h> #include <linux/list.h> #include <linux/unistd.h> -#include <linux/byteorder/swab.h> #include <linux/pagemap.h> #include <linux/scatterlist.h> #include <linux/workqueue.h> @@ -252,6 +251,7 @@ struct ivtv_mailbox_data { #define IVTV_F_I_DEC_PAUSED 20 /* the decoder is paused */ #define IVTV_F_I_INITED 21 /* set after first open */ #define IVTV_F_I_FAILED 22 /* set if first open failed */ +#define IVTV_F_I_WORK_INITED 23 /* worker thread was initialized */ /* Event notifications */ #define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */ diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.c b/linux/drivers/media/video/ivtv/ivtv-i2c.c index 1488bc7ce..180f541f0 100644 --- a/linux/drivers/media/video/ivtv/ivtv-i2c.c +++ b/linux/drivers/media/video/ivtv/ivtv-i2c.c @@ -64,8 +64,6 @@ #include "ivtv-gpio.h" #include "ivtv-i2c.h" -#include <media/ir-kbd-i2c.h> - /* i2c implementation for cx23415/6 chip, ivtv project. * Author: Kevin Thayer (nufan_wfk at yahoo.com) */ diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.c b/linux/drivers/media/video/ivtv/ivtv-ioctl.c index 61030309d..f00854ad6 100644 --- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c @@ -101,18 +101,15 @@ void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) } } -static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +static void check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) { int f, l; - u16 set = 0; for (f = 0; f < 2; f++) { for (l = 0; l < 24; l++) { fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal); - set |= fmt->service_lines[f][l]; } } - return set != 0; } u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt) @@ -474,7 +471,7 @@ static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format int h = fmt->fmt.pix.height; w = min(w, 720); - w = max(w, 1); + w = max(w, 2); h = min(h, itv->is_50hz ? 576 : 480); h = max(h, 2); ivtv_g_fmt_vid_cap(file, fh, fmt); @@ -512,27 +509,20 @@ static int ivtv_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt) { struct ivtv_open_id *id = fh; - s32 w, h; - int field; - int ret; + struct ivtv *itv = id->itv; + s32 w = fmt->fmt.pix.width; + s32 h = fmt->fmt.pix.height; + int field = fmt->fmt.pix.field; + int ret = ivtv_g_fmt_vid_out(file, fh, fmt); - w = fmt->fmt.pix.width; - h = fmt->fmt.pix.height; - field = fmt->fmt.pix.field; - ret = ivtv_g_fmt_vid_out(file, fh, fmt); + w = min(w, 720); + w = max(w, 2); + h = min(h, itv->is_out_50hz ? 576 : 480); + h = max(h, 2); + if (id->type == IVTV_DEC_STREAM_TYPE_YUV) + fmt->fmt.pix.field = field; fmt->fmt.pix.width = w; fmt->fmt.pix.height = h; - if (!ret && id->type == IVTV_DEC_STREAM_TYPE_YUV) { - fmt->fmt.pix.field = field; - if (fmt->fmt.pix.width < 2) - fmt->fmt.pix.width = 2; - if (fmt->fmt.pix.width > 720) - fmt->fmt.pix.width = 720; - if (fmt->fmt.pix.height < 2) - fmt->fmt.pix.height = 2; - if (fmt->fmt.pix.height > 576) - fmt->fmt.pix.height = 576; - } return ret; } @@ -560,9 +550,9 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f struct ivtv_open_id *id = fh; struct ivtv *itv = id->itv; struct cx2341x_mpeg_params *p = &itv->params; + int ret = ivtv_try_fmt_vid_cap(file, fh, fmt); int w = fmt->fmt.pix.width; int h = fmt->fmt.pix.height; - int ret = ivtv_try_fmt_vid_cap(file, fh, fmt); if (ret) return ret; @@ -600,8 +590,7 @@ static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo if (ret || id->type == IVTV_DEC_STREAM_TYPE_VBI) return ret; - if (check_service_set(vbifmt, itv->is_50hz) == 0) - return -EINVAL; + check_service_set(vbifmt, itv->is_50hz); if (atomic_read(&itv->capturing) > 0) return -EBUSY; itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); diff --git a/linux/drivers/media/video/ivtv/ivtv-irq.c b/linux/drivers/media/video/ivtv/ivtv-irq.c index d5ac766c1..59fe56171 100644 --- a/linux/drivers/media/video/ivtv/ivtv-irq.c +++ b/linux/drivers/media/video/ivtv/ivtv-irq.c @@ -82,6 +82,13 @@ void ivtv_irq_work_handler(void *arg) DEFINE_WAIT(wait); + if (test_and_clear_bit(IVTV_F_I_WORK_INITED, &itv->i_flags)) { + struct sched_param param = { .sched_priority = 99 }; + + /* This thread must use the FIFO scheduler as it + is realtime sensitive. */ + sched_setscheduler(current, SCHED_FIFO, ¶m); + } if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags)) ivtv_pio_work_handler(itv); @@ -684,34 +691,14 @@ static void ivtv_irq_enc_start_cap(struct ivtv *itv) static void ivtv_irq_enc_vbi_cap(struct ivtv *itv) { - struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG]; u32 data[CX2341X_MBOX_MAX_DATA]; struct ivtv_stream *s; IVTV_DEBUG_HI_IRQ("ENC START VBI CAP\n"); s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; - /* If more than two VBI buffers are pending, then - clear the old ones and start with this new one. - This can happen during transition stages when MPEG capturing is - started, but the first interrupts haven't arrived yet. During - that period VBI requests can accumulate without being able to - DMA the data. Since at most four VBI DMA buffers are available, - we just drop the old requests when there are already three - requests queued. */ - if (s->sg_pending_size > 2) { - struct ivtv_buffer *buf; - list_for_each_entry(buf, &s->q_predma.list, list) - ivtv_buf_sync_for_cpu(s, buf); - ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0); - s->sg_pending_size = 0; - } - /* if we can append the data, and the MPEG stream isn't capturing, - then start a DMA request for just the VBI data. */ - if (!stream_enc_dma_append(s, data) && - !test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) { + if (!stream_enc_dma_append(s, data)) set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags); - } } static void ivtv_irq_dec_vbi_reinsert(struct ivtv *itv) diff --git a/linux/drivers/media/video/ivtv/ivtv-queue.h b/linux/drivers/media/video/ivtv/ivtv-queue.h index 7cfc0c9ab..476556afd 100644 --- a/linux/drivers/media/video/ivtv/ivtv-queue.h +++ b/linux/drivers/media/video/ivtv/ivtv-queue.h @@ -23,7 +23,7 @@ #define IVTV_QUEUE_H #define IVTV_DMA_UNMAPPED ((u32) -1) -#define SLICED_VBI_PIO 1 +#define SLICED_VBI_PIO 0 /* ivtv_buffer utility functions */ diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c index 54d2023b2..730e85d86 100644 --- a/linux/drivers/media/video/ivtv/ivtv-streams.c +++ b/linux/drivers/media/video/ivtv/ivtv-streams.c @@ -363,7 +363,7 @@ static void ivtv_vbi_setup(struct ivtv *itv) /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ data[1] = 1; /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */ - data[2] = raw ? 4 : 8; + data[2] = raw ? 4 : 4 * (itv->vbi.raw_size / itv->vbi.enc_size); /* The start/stop codes determine which VBI lines end up in the raw VBI data area. The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video) diff --git a/linux/drivers/media/video/ivtv/ivtv-vbi.c b/linux/drivers/media/video/ivtv/ivtv-vbi.c index 71798f0da..1ce9deb11 100644 --- a/linux/drivers/media/video/ivtv/ivtv-vbi.c +++ b/linux/drivers/media/video/ivtv/ivtv-vbi.c @@ -293,6 +293,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 u32 line_size = itv->vbi.sliced_decoder_line_size; struct v4l2_decode_vbi_line vbi; int i; + unsigned lines = 0; /* find the first valid line */ for (i = 0; i < size; i++, buf++) { @@ -313,7 +314,8 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 } vbi.p = p + 4; itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi); - if (vbi.type) { + if (vbi.type && !(lines & (1 << vbi.line))) { + lines |= 1 << vbi.line; itv->vbi.sliced_data[line].id = vbi.type; itv->vbi.sliced_data[line].field = vbi.is_second_field; itv->vbi.sliced_data[line].line = vbi.line; diff --git a/linux/drivers/media/video/ivtv/ivtv-version.h b/linux/drivers/media/video/ivtv/ivtv-version.h index 442f43f11..8cd753d30 100644 --- a/linux/drivers/media/video/ivtv/ivtv-version.h +++ b/linux/drivers/media/video/ivtv/ivtv-version.h @@ -22,7 +22,7 @@ #define IVTV_DRIVER_NAME "ivtv" #define IVTV_DRIVER_VERSION_MAJOR 1 -#define IVTV_DRIVER_VERSION_MINOR 3 +#define IVTV_DRIVER_VERSION_MINOR 4 #define IVTV_DRIVER_VERSION_PATCHLEVEL 0 #define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/linux/drivers/media/video/ks0127.c b/linux/drivers/media/video/ks0127.c index 90451230d..b21b6f097 100644 --- a/linux/drivers/media/video/ks0127.c +++ b/linux/drivers/media/video/ks0127.c @@ -680,26 +680,27 @@ static int ks0127_command(struct i2c_client *client, case DECODER_ENABLE_OUTPUT: { + int enable; - int *iarg = arg; - int enable = (*iarg != 0); - if (enable) { - dprintk("ks0127: command " + iarg = arg; + enable = (*iarg != 0); + if (enable) { + dprintk("ks0127: command " "DECODER_ENABLE_OUTPUT on " "(%d)\n", enable); - /* All output pins on */ - ks0127_and_or(ks, KS_OFMTA, 0xcf, 0x30); - /* Obey the OEN pin */ - ks0127_and_or(ks, KS_CDEM, 0x7f, 0x00); - } else { - dprintk("ks0127: command " + /* All output pins on */ + ks0127_and_or(ks, KS_OFMTA, 0xcf, 0x30); + /* Obey the OEN pin */ + ks0127_and_or(ks, KS_CDEM, 0x7f, 0x00); + } else { + dprintk("ks0127: command " "DECODER_ENABLE_OUTPUT off " "(%d)\n", enable); - /* Video output pins off */ - ks0127_and_or(ks, KS_OFMTA, 0xcf, 0x00); - /* Ignore the OEN pin */ - ks0127_and_or(ks, KS_CDEM, 0x7f, 0x80); - } + /* Video output pins off */ + ks0127_and_or(ks, KS_OFMTA, 0xcf, 0x00); + /* Ignore the OEN pin */ + ks0127_and_or(ks, KS_CDEM, 0x7f, 0x80); + } } break; diff --git a/linux/drivers/media/video/meye.c b/linux/drivers/media/video/meye.c index a0cdae0c1..535443887 100644 --- a/linux/drivers/media/video/meye.c +++ b/linux/drivers/media/video/meye.c @@ -848,21 +848,16 @@ again: static int meye_open(struct inode *inode, struct file *file) { - int i, err; + int i; - lock_kernel(); - err = video_exclusive_open(inode, file); - if (err < 0) { - unlock_kernel(); - return err; - } + if (test_and_set_bit(0, &meye.in_use)) + return -EBUSY; mchip_hic_stop(); if (mchip_dma_alloc()) { printk(KERN_ERR "meye: mchip framebuffer allocation failed\n"); - video_exclusive_release(inode, file); - unlock_kernel(); + clear_bit(0, &meye.in_use); return -ENOBUFS; } @@ -870,7 +865,6 @@ static int meye_open(struct inode *inode, struct file *file) meye.grab_buffer[i].state = MEYE_BUF_UNUSED; kfifo_reset(meye.grabq); kfifo_reset(meye.doneq); - unlock_kernel(); return 0; } @@ -878,7 +872,7 @@ static int meye_release(struct inode *inode, struct file *file) { mchip_hic_stop(); mchip_dma_free(); - video_exclusive_release(inode, file); + clear_bit(0, &meye.in_use); return 0; } @@ -1784,6 +1778,7 @@ static int __devinit meye_probe(struct pci_dev *pcidev, goto outnotdev; } + ret = -ENOMEM; meye.mchip_dev = pcidev; meye.video_dev = video_device_alloc(); if (!meye.video_dev) { @@ -1791,7 +1786,6 @@ static int __devinit meye_probe(struct pci_dev *pcidev, goto outnotdev; } - ret = -ENOMEM; meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE); if (!meye.grab_temp) { printk(KERN_ERR "meye: grab buffer allocation failed\n"); @@ -1816,6 +1810,7 @@ static int __devinit meye_probe(struct pci_dev *pcidev, memcpy(meye.video_dev, &meye_template, sizeof(meye_template)); meye.video_dev->parent = &meye.mchip_dev->dev; + ret = -EIO; if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) { printk(KERN_ERR "meye: unable to power on the camera\n"); printk(KERN_ERR "meye: did you enable the camera in " @@ -1823,7 +1818,6 @@ static int __devinit meye_probe(struct pci_dev *pcidev, goto outsonypienable; } - ret = -EIO; if ((ret = pci_enable_device(meye.mchip_dev))) { printk(KERN_ERR "meye: pci_enable_device failed\n"); goto outenabledev; diff --git a/linux/drivers/media/video/meye.h b/linux/drivers/media/video/meye.h index 4c0c031d3..b4a589f21 100644 --- a/linux/drivers/media/video/meye.h +++ b/linux/drivers/media/video/meye.h @@ -315,6 +315,7 @@ struct meye { struct video_device *video_dev; /* video device parameters */ struct video_picture picture; /* video picture parameters */ struct meye_params params; /* additional parameters */ + unsigned long in_use; /* set to 1 if the device is in use */ #ifdef CONFIG_PM u8 pm_mchip_mode; /* old mchip mode */ #endif diff --git a/linux/drivers/media/video/mt9m001.c b/linux/drivers/media/video/mt9m001.c index f4742936c..228183c54 100644 --- a/linux/drivers/media/video/mt9m001.c +++ b/linux/drivers/media/video/mt9m001.c @@ -117,24 +117,51 @@ static int reg_clear(struct soc_camera_device *icd, const u8 reg, static int mt9m001_init(struct soc_camera_device *icd) { + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct soc_camera_link *icl = mt9m001->client->dev.platform_data; int ret; - /* Disable chip, synchronous option update */ dev_dbg(icd->vdev->parent, "%s\n", __func__); - ret = reg_write(icd, MT9M001_RESET, 1); - if (ret >= 0) - ret = reg_write(icd, MT9M001_RESET, 0); - if (ret >= 0) + if (icl->power) { + ret = icl->power(&mt9m001->client->dev, 1); + if (ret < 0) { + dev_err(icd->vdev->parent, + "Platform failed to power-on the camera.\n"); + return ret; + } + } + + /* The camera could have been already on, we reset it additionally */ + if (icl->reset) + ret = icl->reset(&mt9m001->client->dev); + else + ret = -ENODEV; + + if (ret < 0) { + /* Either no platform reset, or platform reset failed */ + ret = reg_write(icd, MT9M001_RESET, 1); + if (!ret) + ret = reg_write(icd, MT9M001_RESET, 0); + } + /* Disable chip, synchronous option update */ + if (!ret) ret = reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); - return ret >= 0 ? 0 : -EIO; + return ret; } static int mt9m001_release(struct soc_camera_device *icd) { + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + /* Disable the chip */ reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); + + if (icl->power) + icl->power(&mt9m001->client->dev, 0); + return 0; } @@ -267,24 +294,24 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, /* Blanking and start values - default... */ ret = reg_write(icd, MT9M001_HORIZONTAL_BLANKING, hblank); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank); /* The caller provides a supported format, as verified per * call to icd->try_fmt_cap() */ - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9M001_COLUMN_START, rect->left); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9M001_ROW_START, rect->top); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9M001_WINDOW_WIDTH, rect->width - 1); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9M001_WINDOW_HEIGHT, rect->height + icd->y_skip_top - 1); - if (ret >= 0 && mt9m001->autoexposure) { + if (!ret && mt9m001->autoexposure) { ret = reg_write(icd, MT9M001_SHUTTER_WIDTH, rect->height + icd->y_skip_top + vblank); - if (ret >= 0) { + if (!ret) { const struct v4l2_queryctrl *qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); @@ -295,7 +322,7 @@ static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, } } - return ret < 0 ? ret : 0; + return ret; } static int mt9m001_try_fmt_cap(struct soc_camera_device *icd, diff --git a/linux/drivers/media/video/mt9m111.c b/linux/drivers/media/video/mt9m111.c new file mode 100644 index 000000000..ceff40edc --- /dev/null +++ b/linux/drivers/media/video/mt9m111.c @@ -0,0 +1,981 @@ +/* + * Driver for MT9M111 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/videodev2.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/log2.h> +#include <linux/gpio.h> +#include <linux/delay.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +/* + * mt9m111 i2c address is 0x5d or 0x48 (depending on SAddr pin) + * The platform has to define i2c_board_info and call i2c_register_board_info() + */ + +/* mt9m111: Sensor register addresses */ +#define MT9M111_CHIP_VERSION 0x000 +#define MT9M111_ROW_START 0x001 +#define MT9M111_COLUMN_START 0x002 +#define MT9M111_WINDOW_HEIGHT 0x003 +#define MT9M111_WINDOW_WIDTH 0x004 +#define MT9M111_HORIZONTAL_BLANKING_B 0x005 +#define MT9M111_VERTICAL_BLANKING_B 0x006 +#define MT9M111_HORIZONTAL_BLANKING_A 0x007 +#define MT9M111_VERTICAL_BLANKING_A 0x008 +#define MT9M111_SHUTTER_WIDTH 0x009 +#define MT9M111_ROW_SPEED 0x00a +#define MT9M111_EXTRA_DELAY 0x00b +#define MT9M111_SHUTTER_DELAY 0x00c +#define MT9M111_RESET 0x00d +#define MT9M111_READ_MODE_B 0x020 +#define MT9M111_READ_MODE_A 0x021 +#define MT9M111_FLASH_CONTROL 0x023 +#define MT9M111_GREEN1_GAIN 0x02b +#define MT9M111_BLUE_GAIN 0x02c +#define MT9M111_RED_GAIN 0x02d +#define MT9M111_GREEN2_GAIN 0x02e +#define MT9M111_GLOBAL_GAIN 0x02f +#define MT9M111_CONTEXT_CONTROL 0x0c8 +#define MT9M111_PAGE_MAP 0x0f0 +#define MT9M111_BYTE_WISE_ADDR 0x0f1 + +#define MT9M111_RESET_SYNC_CHANGES (1 << 15) +#define MT9M111_RESET_RESTART_BAD_FRAME (1 << 9) +#define MT9M111_RESET_SHOW_BAD_FRAMES (1 << 8) +#define MT9M111_RESET_RESET_SOC (1 << 5) +#define MT9M111_RESET_OUTPUT_DISABLE (1 << 4) +#define MT9M111_RESET_CHIP_ENABLE (1 << 3) +#define MT9M111_RESET_ANALOG_STANDBY (1 << 2) +#define MT9M111_RESET_RESTART_FRAME (1 << 1) +#define MT9M111_RESET_RESET_MODE (1 << 0) + +#define MT9M111_RMB_MIRROR_COLS (1 << 1) +#define MT9M111_RMB_MIRROR_ROWS (1 << 0) +#define MT9M111_CTXT_CTRL_RESTART (1 << 15) +#define MT9M111_CTXT_CTRL_DEFECTCOR_B (1 << 12) +#define MT9M111_CTXT_CTRL_RESIZE_B (1 << 10) +#define MT9M111_CTXT_CTRL_CTRL2_B (1 << 9) +#define MT9M111_CTXT_CTRL_GAMMA_B (1 << 8) +#define MT9M111_CTXT_CTRL_XENON_EN (1 << 7) +#define MT9M111_CTXT_CTRL_READ_MODE_B (1 << 3) +#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2) +#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1) +#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0) +/* + * mt9m111: Colorpipe register addresses (0x100..0x1ff) + */ +#define MT9M111_OPER_MODE_CTRL 0x106 +#define MT9M111_OUTPUT_FORMAT_CTRL 0x108 +#define MT9M111_REDUCER_XZOOM_B 0x1a0 +#define MT9M111_REDUCER_XSIZE_B 0x1a1 +#define MT9M111_REDUCER_YZOOM_B 0x1a3 +#define MT9M111_REDUCER_YSIZE_B 0x1a4 +#define MT9M111_REDUCER_XZOOM_A 0x1a6 +#define MT9M111_REDUCER_XSIZE_A 0x1a7 +#define MT9M111_REDUCER_YZOOM_A 0x1a9 +#define MT9M111_REDUCER_YSIZE_A 0x1aa + +#define MT9M111_OUTPUT_FORMAT_CTRL2_A 0x13a +#define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b + +#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14) + + +#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14) +#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10) +#define MT9M111_OUTFMT_INV_PIX_CLOCK (1 << 9) +#define MT9M111_OUTFMT_RGB (1 << 8) +#define MT9M111_OUTFMT_RGB565 (0x0 << 6) +#define MT9M111_OUTFMT_RGB555 (0x1 << 6) +#define MT9M111_OUTFMT_RGB444x (0x2 << 6) +#define MT9M111_OUTFMT_RGBx444 (0x3 << 6) +#define MT9M111_OUTFMT_TST_RAMP_OFF (0x0 << 4) +#define MT9M111_OUTFMT_TST_RAMP_COL (0x1 << 4) +#define MT9M111_OUTFMT_TST_RAMP_ROW (0x2 << 4) +#define MT9M111_OUTFMT_TST_RAMP_FRAME (0x3 << 4) +#define MT9M111_OUTFMT_SHIFT_3_UP (1 << 3) +#define MT9M111_OUTFMT_AVG_CHROMA (1 << 2) +#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y (1 << 1) +#define MT9M111_OUTFMT_SWAP_RGB_EVEN (1 << 1) +#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr (1 << 0) +/* + * mt9m111: Camera control register addresses (0x200..0x2ff not implemented) + */ + +#define reg_read(reg) mt9m111_reg_read(icd, MT9M111_##reg) +#define reg_write(reg, val) mt9m111_reg_write(icd, MT9M111_##reg, (val)) +#define reg_set(reg, val) mt9m111_reg_set(icd, MT9M111_##reg, (val)) +#define reg_clear(reg, val) mt9m111_reg_clear(icd, MT9M111_##reg, (val)) + +#define MT9M111_MIN_DARK_ROWS 8 +#define MT9M111_MIN_DARK_COLS 24 +#define MT9M111_MAX_HEIGHT 1024 +#define MT9M111_MAX_WIDTH 1280 + +#define COL_FMT(_name, _depth, _fourcc, _colorspace) \ + { .name = _name, .depth = _depth, .fourcc = _fourcc, \ + .colorspace = _colorspace } +#define RGB_FMT(_name, _depth, _fourcc) \ + COL_FMT(_name, _depth, _fourcc, V4L2_COLORSPACE_SRGB) + +static const struct soc_camera_data_format mt9m111_colour_formats[] = { + COL_FMT("YCrYCb 8 bit", 8, V4L2_PIX_FMT_YUYV, V4L2_COLORSPACE_JPEG), + RGB_FMT("RGB 565", 16, V4L2_PIX_FMT_RGB565), + RGB_FMT("RGB 555", 16, V4L2_PIX_FMT_RGB555), + RGB_FMT("Bayer (sRGB) 10 bit", 10, V4L2_PIX_FMT_SBGGR16), + RGB_FMT("Bayer (sRGB) 8 bit", 8, V4L2_PIX_FMT_SBGGR8), +}; + +enum mt9m111_context { + HIGHPOWER = 0, + LOWPOWER, +}; + +struct mt9m111 { + struct i2c_client *client; + struct soc_camera_device icd; + int model; /* V4L2_IDENT_MT9M111* codes from v4l2-chip-ident.h */ + enum mt9m111_context context; + unsigned int left, top, width, height; + u32 pixfmt; + unsigned char autoexposure; + unsigned char datawidth; + unsigned int powered:1; + unsigned int hflip:1; + unsigned int vflip:1; + unsigned int swap_rgb_even_odd:1; + unsigned int swap_rgb_red_blue:1; + unsigned int swap_yuv_y_chromas:1; + unsigned int swap_yuv_cb_cr:1; +}; + +static int reg_page_map_set(struct i2c_client *client, const u16 reg) +{ + int ret; + u16 page; + static int lastpage = -1; /* PageMap cache value */ + + page = (reg >> 8); + if (page == lastpage) + return 0; + if (page > 2) + return -EINVAL; + + ret = i2c_smbus_write_word_data(client, MT9M111_PAGE_MAP, swab16(page)); + if (!ret) + lastpage = page; + return ret; +} + +static int mt9m111_reg_read(struct soc_camera_device *icd, const u16 reg) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = mt9m111->client; + int ret; + + ret = reg_page_map_set(client, reg); + if (!ret) + ret = swab16(i2c_smbus_read_word_data(client, (reg & 0xff))); + + dev_dbg(&icd->dev, "read reg.%03x -> %04x\n", reg, ret); + return ret; +} + +static int mt9m111_reg_write(struct soc_camera_device *icd, const u16 reg, + const u16 data) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = mt9m111->client; + int ret; + + ret = reg_page_map_set(client, reg); + if (!ret) + ret = i2c_smbus_write_word_data(mt9m111->client, (reg & 0xff), + swab16(data)); + dev_dbg(&icd->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret); + return ret; +} + +static int mt9m111_reg_set(struct soc_camera_device *icd, const u16 reg, + const u16 data) +{ + int ret; + + ret = mt9m111_reg_read(icd, reg); + if (ret >= 0) + ret = mt9m111_reg_write(icd, reg, ret | data); + return ret; +} + +static int mt9m111_reg_clear(struct soc_camera_device *icd, const u16 reg, + const u16 data) +{ + int ret; + + ret = mt9m111_reg_read(icd, reg); + return mt9m111_reg_write(icd, reg, ret & ~data); +} + +static int mt9m111_set_context(struct soc_camera_device *icd, + enum mt9m111_context ctxt) +{ + int valB = MT9M111_CTXT_CTRL_RESTART | MT9M111_CTXT_CTRL_DEFECTCOR_B + | MT9M111_CTXT_CTRL_RESIZE_B | MT9M111_CTXT_CTRL_CTRL2_B + | MT9M111_CTXT_CTRL_GAMMA_B | MT9M111_CTXT_CTRL_READ_MODE_B + | MT9M111_CTXT_CTRL_VBLANK_SEL_B + | MT9M111_CTXT_CTRL_HBLANK_SEL_B; + int valA = MT9M111_CTXT_CTRL_RESTART; + + if (ctxt == HIGHPOWER) + return reg_write(CONTEXT_CONTROL, valB); + else + return reg_write(CONTEXT_CONTROL, valA); +} + +static int mt9m111_setup_rect(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret, is_raw_format; + int width = mt9m111->width; + int height = mt9m111->height; + + if ((mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8) + || (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16)) + is_raw_format = 1; + else + is_raw_format = 0; + + ret = reg_write(COLUMN_START, mt9m111->left); + if (!ret) + ret = reg_write(ROW_START, mt9m111->top); + + if (is_raw_format) { + if (!ret) + ret = reg_write(WINDOW_WIDTH, width); + if (!ret) + ret = reg_write(WINDOW_HEIGHT, height); + } else { + if (!ret) + ret = reg_write(REDUCER_XZOOM_B, MT9M111_MAX_WIDTH); + if (!ret) + ret = reg_write(REDUCER_YZOOM_B, MT9M111_MAX_HEIGHT); + if (!ret) + ret = reg_write(REDUCER_XSIZE_B, width); + if (!ret) + ret = reg_write(REDUCER_YSIZE_B, height); + if (!ret) + ret = reg_write(REDUCER_XZOOM_A, MT9M111_MAX_WIDTH); + if (!ret) + ret = reg_write(REDUCER_YZOOM_A, MT9M111_MAX_HEIGHT); + if (!ret) + ret = reg_write(REDUCER_XSIZE_A, width); + if (!ret) + ret = reg_write(REDUCER_YSIZE_A, height); + } + + return ret; +} + +static int mt9m111_setup_pixfmt(struct soc_camera_device *icd, u16 outfmt) +{ + int ret; + + ret = reg_write(OUTPUT_FORMAT_CTRL2_A, outfmt); + if (!ret) + ret = reg_write(OUTPUT_FORMAT_CTRL2_B, outfmt); + return ret; +} + +static int mt9m111_setfmt_bayer8(struct soc_camera_device *icd) +{ + return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_PROCESSED_BAYER); +} + +static int mt9m111_setfmt_bayer10(struct soc_camera_device *icd) +{ + return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_BYPASS_IFP); +} + +static int mt9m111_setfmt_rgb565(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int val = 0; + + if (mt9m111->swap_rgb_red_blue) + val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr; + if (mt9m111->swap_rgb_even_odd) + val |= MT9M111_OUTFMT_SWAP_RGB_EVEN; + val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565; + + return mt9m111_setup_pixfmt(icd, val); +} + +static int mt9m111_setfmt_rgb555(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int val = 0; + + if (mt9m111->swap_rgb_red_blue) + val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr; + if (mt9m111->swap_rgb_even_odd) + val |= MT9M111_OUTFMT_SWAP_RGB_EVEN; + val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555; + + return mt9m111_setup_pixfmt(icd, val); +} + +static int mt9m111_setfmt_yuv(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int val = 0; + + if (mt9m111->swap_yuv_cb_cr) + val |= MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr; + if (mt9m111->swap_yuv_y_chromas) + val |= MT9M111_OUTFMT_SWAP_YCbCr_C_Y; + + return mt9m111_setup_pixfmt(icd, val); +} + +static int mt9m111_enable(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct soc_camera_link *icl = mt9m111->client->dev.platform_data; + int ret; + + if (icl->power) { + ret = icl->power(&mt9m111->client->dev, 1); + if (ret < 0) { + dev_err(icd->vdev->parent, + "Platform failed to power-on the camera.\n"); + return ret; + } + } + + ret = reg_set(RESET, MT9M111_RESET_CHIP_ENABLE); + if (!ret) + mt9m111->powered = 1; + return ret; +} + +static int mt9m111_disable(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct soc_camera_link *icl = mt9m111->client->dev.platform_data; + int ret; + + ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE); + if (!ret) + mt9m111->powered = 0; + + if (icl->power) + icl->power(&mt9m111->client->dev, 0); + + return ret; +} + +static int mt9m111_reset(struct soc_camera_device *icd) +{ + int ret; + + ret = reg_set(RESET, MT9M111_RESET_RESET_MODE); + if (!ret) + ret = reg_set(RESET, MT9M111_RESET_RESET_SOC); + if (!ret) + ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE + | MT9M111_RESET_RESET_SOC); + return ret; +} + +static int mt9m111_start_capture(struct soc_camera_device *icd) +{ + return 0; +} + +static int mt9m111_stop_capture(struct soc_camera_device *icd) +{ + return 0; +} + +static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd) +{ + return SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_DATAWIDTH_8; +} + +static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f) +{ + return 0; +} + +static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + switch (pixfmt) { + case V4L2_PIX_FMT_SBGGR8: + ret = mt9m111_setfmt_bayer8(icd); + break; + case V4L2_PIX_FMT_SBGGR16: + ret = mt9m111_setfmt_bayer10(icd); + break; + case V4L2_PIX_FMT_RGB555: + ret = mt9m111_setfmt_rgb555(icd); + break; + case V4L2_PIX_FMT_RGB565: + ret = mt9m111_setfmt_rgb565(icd); + break; + case V4L2_PIX_FMT_YUYV: + ret = mt9m111_setfmt_yuv(icd); + break; + default: + dev_err(&icd->dev, "Pixel format not handled : %x\n", pixfmt); + ret = -EINVAL; + } + + if (!ret) + mt9m111->pixfmt = pixfmt; + + return ret; +} + +static int mt9m111_set_fmt_cap(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + mt9m111->left = rect->left; + mt9m111->top = rect->top; + mt9m111->width = rect->width; + mt9m111->height = rect->height; + + dev_dbg(&icd->dev, "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n", + __func__, pixfmt, mt9m111->left, mt9m111->top, mt9m111->width, + mt9m111->height); + + ret = mt9m111_setup_rect(icd); + if (!ret) + ret = mt9m111_set_pixfmt(icd, pixfmt); + return ret; +} + +static int mt9m111_try_fmt_cap(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + if (f->fmt.pix.height > MT9M111_MAX_HEIGHT) + f->fmt.pix.height = MT9M111_MAX_HEIGHT; + if (f->fmt.pix.width > MT9M111_MAX_WIDTH) + f->fmt.pix.width = MT9M111_MAX_WIDTH; + + return 0; +} + +static int mt9m111_get_chip_id(struct soc_camera_device *icd, + struct v4l2_chip_ident *id) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + + if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match_chip != mt9m111->client->addr) + return -ENODEV; + + id->ident = mt9m111->model; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9m111_get_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + int val; + + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) + return -EINVAL; + if (reg->match_chip != mt9m111->client->addr) + return -ENODEV; + + val = mt9m111_reg_read(icd, reg->reg); + reg->val = (u64)val; + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9m111_set_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) + return -EINVAL; + + if (reg->match_chip != mt9m111->client->addr) + return -ENODEV; + + if (mt9m111_reg_write(icd, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static const struct v4l2_queryctrl mt9m111_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Verticaly", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontaly", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { /* gain = 1/32*val (=>gain=1 if val==32) */ + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 63 * 2 * 2, + .step = 1, + .default_value = 32, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + } +}; + +static int mt9m111_video_probe(struct soc_camera_device *); +static void mt9m111_video_remove(struct soc_camera_device *); +static int mt9m111_get_control(struct soc_camera_device *, + struct v4l2_control *); +static int mt9m111_set_control(struct soc_camera_device *, + struct v4l2_control *); +static int mt9m111_resume(struct soc_camera_device *icd); +static int mt9m111_init(struct soc_camera_device *icd); +static int mt9m111_release(struct soc_camera_device *icd); + +static struct soc_camera_ops mt9m111_ops = { + .owner = THIS_MODULE, + .probe = mt9m111_video_probe, + .remove = mt9m111_video_remove, + .init = mt9m111_init, + .resume = mt9m111_resume, + .release = mt9m111_release, + .start_capture = mt9m111_start_capture, + .stop_capture = mt9m111_stop_capture, + .set_fmt_cap = mt9m111_set_fmt_cap, + .try_fmt_cap = mt9m111_try_fmt_cap, + .query_bus_param = mt9m111_query_bus_param, + .set_bus_param = mt9m111_set_bus_param, + .controls = mt9m111_controls, + .num_controls = ARRAY_SIZE(mt9m111_controls), + .get_control = mt9m111_get_control, + .set_control = mt9m111_set_control, + .get_chip_id = mt9m111_get_chip_id, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .get_register = mt9m111_get_register, + .set_register = mt9m111_set_register, +#endif +}; + +static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + if (mt9m111->context == HIGHPOWER) { + if (flip) + ret = reg_set(READ_MODE_B, mask); + else + ret = reg_clear(READ_MODE_B, mask); + } else { + if (flip) + ret = reg_set(READ_MODE_A, mask); + else + ret = reg_clear(READ_MODE_A, mask); + } + + return ret; +} + +static int mt9m111_get_global_gain(struct soc_camera_device *icd) +{ + unsigned int data, gain; + + data = reg_read(GLOBAL_GAIN); + if (data >= 0) + gain = ((data & (1 << 10)) * 2) + | ((data & (1 << 9)) * 2) + | (data & 0x2f); + else + gain = data; + + return gain; +} +static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain) +{ + u16 val; + + if (gain > 63 * 2 * 2) + return -EINVAL; + + icd->gain = gain; + if ((gain >= 64 * 2) && (gain < 63 * 2 * 2)) + val = (1 << 10) | (1 << 9) | (gain / 4); + else if ((gain >= 64) && (gain < 64 * 2)) + val = (1 << 9) | (gain / 2); + else + val = gain; + + return reg_write(GLOBAL_GAIN, val); +} + +static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + if (on) + ret = reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); + else + ret = reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); + + if (!ret) + mt9m111->autoexposure = on; + + return ret; +} +static int mt9m111_get_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (mt9m111->context == HIGHPOWER) + data = reg_read(READ_MODE_B); + else + data = reg_read(READ_MODE_A); + + if (data < 0) + return -EIO; + ctrl->value = !!(data & MT9M111_RMB_MIRROR_ROWS); + break; + case V4L2_CID_HFLIP: + if (mt9m111->context == HIGHPOWER) + data = reg_read(READ_MODE_B); + else + data = reg_read(READ_MODE_A); + + if (data < 0) + return -EIO; + ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS); + break; + case V4L2_CID_GAIN: + data = mt9m111_get_global_gain(icd); + if (data < 0) + return data; + ctrl->value = data; + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = mt9m111->autoexposure; + break; + } + return 0; +} + +static int mt9m111_set_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + const struct v4l2_queryctrl *qctrl; + int ret; + + qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id); + + if (!qctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + mt9m111->vflip = ctrl->value; + ret = mt9m111_set_flip(icd, ctrl->value, + MT9M111_RMB_MIRROR_ROWS); + break; + case V4L2_CID_HFLIP: + mt9m111->hflip = ctrl->value; + ret = mt9m111_set_flip(icd, ctrl->value, + MT9M111_RMB_MIRROR_COLS); + break; + case V4L2_CID_GAIN: + ret = mt9m111_set_global_gain(icd, ctrl->value); + break; + case V4L2_CID_EXPOSURE_AUTO: + ret = mt9m111_set_autoexposure(icd, ctrl->value); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int mt9m111_restore_state(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + + mt9m111_set_context(icd, mt9m111->context); + mt9m111_set_pixfmt(icd, mt9m111->pixfmt); + mt9m111_setup_rect(icd); + mt9m111_set_flip(icd, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS); + mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS); + mt9m111_set_global_gain(icd, icd->gain); + mt9m111_set_autoexposure(icd, mt9m111->autoexposure); + return 0; +} + +static int mt9m111_resume(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret = 0; + + if (mt9m111->powered) { + ret = mt9m111_enable(icd); + if (!ret) + ret = mt9m111_reset(icd); + if (!ret) + ret = mt9m111_restore_state(icd); + } + return ret; +} + +static int mt9m111_init(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + mt9m111->context = HIGHPOWER; + ret = mt9m111_enable(icd); + if (!ret) + ret = mt9m111_reset(icd); + if (!ret) + ret = mt9m111_set_context(icd, mt9m111->context); + if (!ret) + ret = mt9m111_set_autoexposure(icd, mt9m111->autoexposure); + if (ret) + dev_err(&icd->dev, "mt9m111 init failed: %d\n", ret); + return ret; +} + +static int mt9m111_release(struct soc_camera_device *icd) +{ + int ret; + + ret = mt9m111_disable(icd); + if (ret < 0) + dev_err(&icd->dev, "mt9m111 release failed: %d\n", ret); + + return ret; +} + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int mt9m111_video_probe(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + s32 data; + int ret; + + /* + * We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. + */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + ret = mt9m111_enable(icd); + if (ret) + goto ei2c; + ret = mt9m111_reset(icd); + if (ret) + goto ei2c; + + data = reg_read(CHIP_VERSION); + + switch (data) { + case 0x143a: + mt9m111->model = V4L2_IDENT_MT9M111; + icd->formats = mt9m111_colour_formats; + icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats); + break; + default: + ret = -ENODEV; + dev_err(&icd->dev, + "No MT9M111 chip detected, register read %x\n", data); + goto ei2c; + } + + dev_info(&icd->dev, "Detected a MT9M111 chip ID 0x143a\n"); + + ret = soc_camera_video_start(icd); + if (ret) + goto eisis; + + mt9m111->autoexposure = 1; + + mt9m111->swap_rgb_even_odd = 1; + mt9m111->swap_rgb_red_blue = 1; + + return 0; +eisis: +ei2c: + return ret; +} + +static void mt9m111_video_remove(struct soc_camera_device *icd) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + + dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m111->client->addr, + mt9m111->icd.dev.parent, mt9m111->icd.vdev); + soc_camera_video_stop(&mt9m111->icd); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) +static int mt9m111_probe(struct i2c_client *client) +#else +static int mt9m111_probe(struct i2c_client *client, + const struct i2c_device_id *did) +#endif +{ + struct mt9m111 *mt9m111; + struct soc_camera_device *icd; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl = client->dev.platform_data; + int ret; + + if (!icl) { + dev_err(&client->dev, "MT9M111 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9m111 = kzalloc(sizeof(struct mt9m111), GFP_KERNEL); + if (!mt9m111) + return -ENOMEM; + + mt9m111->client = client; + i2c_set_clientdata(client, mt9m111); + + /* Second stage probe - when a capture adapter is there */ + icd = &mt9m111->icd; + icd->ops = &mt9m111_ops; + icd->control = &client->dev; + icd->x_min = MT9M111_MIN_DARK_COLS; + icd->y_min = MT9M111_MIN_DARK_ROWS; + icd->x_current = icd->x_min; + icd->y_current = icd->y_min; + icd->width_min = MT9M111_MIN_DARK_ROWS; + icd->width_max = MT9M111_MAX_WIDTH; + icd->height_min = MT9M111_MIN_DARK_COLS; + icd->height_max = MT9M111_MAX_HEIGHT; + icd->y_skip_top = 0; + icd->iface = icl->bus_id; + + ret = soc_camera_device_register(icd); + if (ret) + goto eisdr; + return 0; + +eisdr: + kfree(mt9m111); + return ret; +} + +static int mt9m111_remove(struct i2c_client *client) +{ + struct mt9m111 *mt9m111 = i2c_get_clientdata(client); + soc_camera_device_unregister(&mt9m111->icd); + kfree(mt9m111); + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id mt9m111_id[] = { + { "mt9m111", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mt9m111_id); +#endif + +static struct i2c_driver mt9m111_i2c_driver = { + .driver = { + .name = "mt9m111", + }, + .probe = mt9m111_probe, + .remove = mt9m111_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = mt9m111_id, +#endif +}; + +static int __init mt9m111_mod_init(void) +{ + return i2c_add_driver(&mt9m111_i2c_driver); +} + +static void __exit mt9m111_mod_exit(void) +{ + i2c_del_driver(&mt9m111_i2c_driver); +} + +module_init(mt9m111_mod_init); +module_exit(mt9m111_mod_exit); + +MODULE_DESCRIPTION("Micron MT9M111 Camera driver"); +MODULE_AUTHOR("Robert Jarzmik"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/mt9v022.c b/linux/drivers/media/video/mt9v022.c index 1ff36edd7..bea695a2c 100644 --- a/linux/drivers/media/video/mt9v022.c +++ b/linux/drivers/media/video/mt9v022.c @@ -25,7 +25,7 @@ static char *sensor_type; module_param(sensor_type, charp, S_IRUGO); -MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"\n"); +MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); /* mt9v022 selected register addresses */ #define MT9V022_CHIP_VERSION 0x00 @@ -134,34 +134,56 @@ static int reg_clear(struct soc_camera_device *icd, const u8 reg, static int mt9v022_init(struct soc_camera_device *icd) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct soc_camera_link *icl = mt9v022->client->dev.platform_data; int ret; + if (icl->power) { + ret = icl->power(&mt9v022->client->dev, 1); + if (ret < 0) { + dev_err(icd->vdev->parent, + "Platform failed to power-on the camera.\n"); + return ret; + } + } + + /* + * The camera could have been already on, we hard-reset it additionally, + * if available. Soft reset is done in video_probe(). + */ + if (icl->reset) + icl->reset(&mt9v022->client->dev); + /* Almost the default mode: master, parallel, simultaneous, and an * undocumented bit 0x200, which is present in table 7, but not in 8, * plus snapshot mode to disable scan for now */ mt9v022->chip_control |= 0x10; ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (ret >= 0) - reg_write(icd, MT9V022_READ_MODE, 0x300); + if (!ret) + ret = reg_write(icd, MT9V022_READ_MODE, 0x300); /* All defaults */ - if (ret >= 0) + if (!ret) /* AEC, AGC on */ ret = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x3); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480); - if (ret >= 0) + if (!ret) /* default - auto */ ret = reg_clear(icd, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_DIGITAL_TEST_PATTERN, 0); - return ret >= 0 ? 0 : -EIO; + return ret; } static int mt9v022_release(struct soc_camera_device *icd) { - /* Nothing? */ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct soc_camera_link *icl = mt9v022->client->dev.platform_data; + + if (icl->power) + icl->power(&mt9v022->client->dev, 0); + return 0; } @@ -352,21 +374,21 @@ static int mt9v022_set_fmt_cap(struct soc_camera_device *icd, rect->height + icd->y_skip_top + 43); } /* Setup frame format: defaults apart from width and height */ - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_COLUMN_START, rect->left); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_ROW_START, rect->top); - if (ret >= 0) + if (!ret) /* Default 94, Phytec driver says: * "width + horizontal blank >= 660" */ ret = reg_write(icd, MT9V022_HORIZONTAL_BLANKING, rect->width > 660 - 43 ? 43 : 660 - rect->width); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_VERTICAL_BLANKING, 45); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_WINDOW_WIDTH, rect->width); - if (ret >= 0) + if (!ret) ret = reg_write(icd, MT9V022_WINDOW_HEIGHT, rect->height + icd->y_skip_top); @@ -717,7 +739,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) icd->num_formats = 1; } - if (ret >= 0) + if (!ret) ret = soc_camera_video_start(icd); if (ret < 0) goto eisis; diff --git a/linux/drivers/media/video/mxb.c b/linux/drivers/media/video/mxb.c index bf2462a58..8252ad94c 100644 --- a/linux/drivers/media/video/mxb.c +++ b/linux/drivers/media/video/mxb.c @@ -27,6 +27,7 @@ #include <media/tuner.h> #include <linux/video_decoder.h> #include <media/v4l2-common.h> +#include <media/saa7115.h> #include "compat.h" #include "mxb.h" @@ -123,6 +124,8 @@ static struct saa7146_extension_ioctls ioctls[] = { { VIDIOC_S_FREQUENCY, SAA7146_EXCLUSIVE }, { VIDIOC_G_AUDIO, SAA7146_EXCLUSIVE }, { VIDIOC_S_AUDIO, SAA7146_EXCLUSIVE }, + { VIDIOC_DBG_G_REGISTER, SAA7146_EXCLUSIVE }, + { VIDIOC_DBG_S_REGISTER, SAA7146_EXCLUSIVE }, { MXB_S_AUDIO_CD, SAA7146_EXCLUSIVE }, /* custom control */ { MXB_S_AUDIO_LINE, SAA7146_EXCLUSIVE }, /* custom control */ { 0, 0 } @@ -135,12 +138,12 @@ struct mxb struct i2c_adapter i2c_adapter; - struct i2c_client* saa7111a; - struct i2c_client* tda9840; - struct i2c_client* tea6415c; - struct i2c_client* tuner; - struct i2c_client* tea6420_1; - struct i2c_client* tea6420_2; + struct i2c_client *saa7111a; + struct i2c_client *tda9840; + struct i2c_client *tea6415c; + struct i2c_client *tuner; + struct i2c_client *tea6420_1; + struct i2c_client *tea6420_2; int cur_mode; /* current audio mode (mono, stereo, ...) */ int cur_input; /* current input */ @@ -152,23 +155,23 @@ static struct saa7146_extension extension; static int mxb_check_clients(struct device *dev, void *data) { - struct mxb* mxb = data; + struct mxb *mxb = data; struct i2c_client *client = i2c_verify_client(dev); - if( !client ) + if (!client) return 0; - if( I2C_ADDR_TEA6420_1 == client->addr ) + if (I2C_ADDR_TEA6420_1 == client->addr) mxb->tea6420_1 = client; - if( I2C_ADDR_TEA6420_2 == client->addr ) + if (I2C_ADDR_TEA6420_2 == client->addr) mxb->tea6420_2 = client; - if( I2C_TEA6415C_2 == client->addr ) + if (I2C_TEA6415C_2 == client->addr) mxb->tea6415c = client; - if( I2C_ADDR_TDA9840 == client->addr ) + if (I2C_ADDR_TDA9840 == client->addr) mxb->tda9840 = client; - if( I2C_SAA7111 == client->addr ) + if (I2C_SAA7111 == client->addr) mxb->saa7111a = client; - if( 0x60 == client->addr ) + if (0x60 == client->addr) mxb->tuner = client; return 0; @@ -179,23 +182,28 @@ static int mxb_probe(struct saa7146_dev* dev) struct mxb* mxb = NULL; int result; - if ((result = request_module("saa7111")) < 0) { + result = request_module("saa7115"); + if (result < 0) { printk("mxb: saa7111 i2c module not available.\n"); return -ENODEV; } - if ((result = request_module("tea6420")) < 0) { + result = request_module("tea6420"); + if (result < 0) { printk("mxb: tea6420 i2c module not available.\n"); return -ENODEV; } - if ((result = request_module("tea6415c")) < 0) { + result = request_module("tea6415c"); + if (result < 0) { printk("mxb: tea6415c i2c module not available.\n"); return -ENODEV; } - if ((result = request_module("tda9840")) < 0) { + result = request_module("tda9840"); + if (result < 0) { printk("mxb: tda9840 i2c module not available.\n"); return -ENODEV; } - if ((result = request_module("tuner")) < 0) { + result = request_module("tuner"); + if (result < 0) { printk("mxb: tuner i2c module not available.\n"); return -ENODEV; } @@ -208,9 +216,10 @@ static int mxb_probe(struct saa7146_dev* dev) mxb->i2c_adapter = (struct i2c_adapter) { .class = I2C_CLASS_TV_ANALOG, - .name = "mxb", }; + snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); + saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); if(i2c_add_adapter(&mxb->i2c_adapter) < 0) { DEB_S(("cannot register i2c-device. skipping.\n")); @@ -291,38 +300,7 @@ static struct { { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, { 3, { 0x80, 0xb3, 0x0a } }, - {-1, { 0} } -}; - -static const unsigned char mxb_saa7111_init[] = { - 0x00, 0x00, /* 00 - ID byte */ - 0x01, 0x00, /* 01 - reserved */ - - /*front end */ - 0x02, 0xd8, /* 02 - FUSE=x, GUDL=x, MODE=x */ - 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */ - 0x04, 0x00, /* 04 - GAI1=256 */ - 0x05, 0x00, /* 05 - GAI2=256 */ - - /* decoder */ - 0x06, 0xf0, /* 06 - HSB at xx(50Hz) / xx(60Hz) pixels after end of last line */ - 0x07, 0x30, /* 07 - HSS at xx(50Hz) / xx(60Hz) pixels after end of last line */ - 0x08, 0xa8, /* 08 - AUFD=x, FSEL=x, EXFIL=x, VTRC=x, HPLL=x, VNOI=x */ - 0x09, 0x02, /* 09 - BYPS=x, PREF=x, BPSS=x, VBLB=x, UPTCV=x, APER=x */ - 0x0a, 0x80, /* 0a - BRIG=128 */ - 0x0b, 0x47, /* 0b - CONT=1.109 */ - 0x0c, 0x40, /* 0c - SATN=1.0 */ - 0x0d, 0x00, /* 0d - HUE=0 */ - 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */ - 0x0f, 0x00, /* 0f - reserved */ - 0x10, 0xd0, /* 10 - OFTS=x, HDEL=x, VRLN=x, YDEL=x */ - 0x11, 0x8c, /* 11 - GPSW=x, CM99=x, FECO=x, COMPO=x, OEYC=1, OEHV=1, VIPB=0, COLO=0 */ - 0x12, 0x80, /* 12 - xx output control 2 */ - 0x13, 0x30, /* 13 - xx output control 3 */ - 0x14, 0x00, /* 14 - reserved */ - 0x15, 0x15, /* 15 - VBI */ - 0x16, 0x04, /* 16 - VBI */ - 0x17, 0x00, /* 17 - VBI */ + {-1, { 0 } } }; /* bring hardware to a sane state. this has to be done, just in case someone @@ -332,37 +310,28 @@ static const unsigned char mxb_saa7111_init[] = { static int mxb_init_done(struct saa7146_dev* dev) { struct mxb* mxb = (struct mxb*)dev->ext_priv; - struct video_decoder_init init; struct i2c_msg msg; struct tuner_setup tun_setup; v4l2_std_id std = V4L2_STD_PAL_BG; + struct v4l2_routing route; int i = 0, err = 0; - struct tea6415c_multiplex vm; + struct tea6415c_multiplex vm; /* select video mode in saa7111a */ - i = VIDEO_MODE_PAL; - /* fixme: currently pointless: gets overwritten by configuration below */ - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_NORM, &i); - - /* write configuration to saa7111a */ - init.data = mxb_saa7111_init; - init.len = sizeof(mxb_saa7111_init); - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_INIT, &init); + mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_S_STD, &std); /* select tuner-output on saa7111a */ i = 0; - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i); - - /* enable vbi bypass */ - i = 1; - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_VBI_BYPASS, &i); + route.input = SAA7115_COMPOSITE0; + route.output = SAA7111_FMT_CCIR | SAA7111_VBI_BYPASS; + mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route); /* select a tuner type */ tun_setup.mode_mask = T_ANALOG_TV; tun_setup.addr = ADDR_UNSET; tun_setup.type = TUNER_PHILIPS_PAL; - mxb->tuner->driver->command(mxb->tuner,TUNER_SET_TYPE_ADDR, &tun_setup); + mxb->tuner->driver->command(mxb->tuner, TUNER_SET_TYPE_ADDR, &tun_setup); /* tune in some frequency on tuner */ mxb->cur_freq.tuner = 0; mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV; @@ -374,27 +343,26 @@ static int mxb_init_done(struct saa7146_dev* dev) mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); /* mute audio on tea6420s */ - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]); - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[6][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[6][1]); + mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_line[6][0]); + mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_line[6][1]); + mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_cd[6][0]); + mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_cd[6][1]); /* switch to tuner-channel on tea6415c*/ vm.out = 17; vm.in = 3; - mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm); + mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm); /* select tuner-output on multicable on tea6415c*/ vm.in = 3; vm.out = 13; - mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm); + mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm); /* the rest for mxb */ mxb->cur_input = 0; mxb->cur_mute = 1; mxb->cur_mode = V4L2_TUNER_MODE_STEREO; - mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &mxb->cur_mode); /* check if the saa7740 (aka 'sound arena module') is present on the mxb. if so, we must initialize it. due to lack of @@ -405,21 +373,22 @@ static int mxb_init_done(struct saa7146_dev* dev) msg.len = mxb_saa7740_init[0].length; msg.buf = &mxb_saa7740_init[0].data[0]; - if( 1 == (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) { + err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); + if (err == 1) { /* the sound arena module is a pos, that's probably the reason philips refuses to hand out a datasheet for the saa7740... it seems to screw up the i2c bus, so we disable fast irq based i2c transactions here and rely on the slow and safe polling method ... */ extension.flags &= ~SAA7146_USE_I2C_IRQ; - for(i = 1;;i++) { - if( -1 == mxb_saa7740_init[i].length ) { + for (i = 1; ; i++) { + if (-1 == mxb_saa7740_init[i].length) break; - } msg.len = mxb_saa7740_init[i].length; msg.buf = &mxb_saa7740_init[i].data[0]; - if( 1 != (err = i2c_transfer(&mxb->i2c_adapter, &msg, 1))) { + err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); + if (err != 1) { DEB_D(("failed to initialize 'sound arena module'.\n")); goto err; } @@ -433,7 +402,8 @@ err: /* ext->saa has been filled by the core driver */ /* some stuff is done via variables */ - saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, input_port_selection[mxb->cur_input].hps_sync); + saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, + input_port_selection[mxb->cur_input].hps_sync); /* some stuff is done via direct write to the registers */ @@ -458,24 +428,24 @@ void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) static struct saa7146_ext_vv vv_data; /* this function only gets called when the probing was successful */ -static int mxb_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) +static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) { - struct mxb* mxb = (struct mxb*)dev->ext_priv; + struct mxb *mxb = (struct mxb *)dev->ext_priv; - DEB_EE(("dev:%p\n",dev)); + DEB_EE(("dev:%p\n", dev)); /* checking for i2c-devices can be omitted here, because we already did this in "mxb_vl42_probe" */ - saa7146_vv_init(dev,&vv_data); - if( 0 != saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { + saa7146_vv_init(dev, &vv_data); + if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { ERR(("cannot register capture v4l2 device. skipping.\n")); return -1; } /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ - if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) { - if( 0 != saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { + if (MXB_BOARD_CAN_DO_VBI(dev)) { + if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { ERR(("cannot register vbi v4l2 device. skipping.\n")); } } @@ -487,18 +457,18 @@ static int mxb_attach(struct saa7146_dev* dev, struct saa7146_pci_extension_data i2c_use_client(mxb->saa7111a); i2c_use_client(mxb->tuner); - printk("mxb: found 'Multimedia eXtension Board'-%d.\n",mxb_num); + printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num); mxb_num++; mxb_init_done(dev); return 0; } -static int mxb_detach(struct saa7146_dev* dev) +static int mxb_detach(struct saa7146_dev *dev) { - struct mxb* mxb = (struct mxb*)dev->ext_priv; + struct mxb *mxb = (struct mxb *)dev->ext_priv; - DEB_EE(("dev:%p\n",dev)); + DEB_EE(("dev:%p\n", dev)); i2c_release_client(mxb->tea6420_1); i2c_release_client(mxb->tea6420_2); @@ -508,9 +478,8 @@ static int mxb_detach(struct saa7146_dev* dev) i2c_release_client(mxb->tuner); saa7146_unregister_device(&mxb->video_dev,dev); - if( 0 != MXB_BOARD_CAN_DO_VBI(dev)) { - saa7146_unregister_device(&mxb->vbi_dev,dev); - } + if (MXB_BOARD_CAN_DO_VBI(dev)) + saa7146_unregister_device(&mxb->vbi_dev, dev); saa7146_vv_release(dev); mxb_num--; @@ -524,7 +493,7 @@ static int mxb_detach(struct saa7146_dev* dev) static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { struct saa7146_dev *dev = fh->dev; - struct mxb* mxb = (struct mxb*)dev->ext_priv; + struct mxb *mxb = (struct mxb *)dev->ext_priv; struct saa7146_vv *vv = dev->vv_data; switch(cmd) { @@ -533,11 +502,9 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) struct v4l2_input *i = arg; DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index)); - if( i->index < 0 || i->index >= MXB_INPUTS) { + if (i->index < 0 || i->index >= MXB_INPUTS) return -EINVAL; - } memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); - return 0; } /* the saa7146 provides some controls (brightness, contrast, saturation) @@ -551,7 +518,7 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) for (i = MAXCONTROLS - 1; i >= 0; i--) { if (mxb_controls[i].id == qc->id) { *qc = mxb_controls[i]; - DEB_D(("VIDIOC_QUERYCTRL %d.\n",qc->id)); + DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id)); return 0; } } @@ -563,56 +530,51 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) int i; for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) { + if (mxb_controls[i].id == vc->id) break; - } } - if( i < 0 ) { + if (i < 0) return -EAGAIN; - } - switch (vc->id ) { - case V4L2_CID_AUDIO_MUTE: { - vc->value = mxb->cur_mute; - DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value)); - return 0; - } + if (vc->id == V4L2_CID_AUDIO_MUTE) { + vc->value = mxb->cur_mute; + DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); + return 0; } - DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n",vc->value)); + DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); return 0; } case VIDIOC_S_CTRL: { - struct v4l2_control *vc = arg; + struct v4l2_control *vc = arg; int i = 0; for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) { + if (mxb_controls[i].id == vc->id) break; - } } - if( i < 0 ) { + if (i < 0) return -EAGAIN; - } - switch (vc->id ) { - case V4L2_CID_AUDIO_MUTE: { - mxb->cur_mute = vc->value; - if( 0 == vc->value ) { - /* switch the audio-source */ - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[mxb->cur_input]][1]); - } else { - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[6][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[6][1]); - } - DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n",vc->value)); - break; + if (vc->id == V4L2_CID_AUDIO_MUTE) { + mxb->cur_mute = vc->value; + if (!vc->value) { + /* switch the audio-source */ + mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, + &TEA6420_line[video_audio_connect[mxb->cur_input]][0]); + mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, + &TEA6420_line[video_audio_connect[mxb->cur_input]][1]); + } else { + mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, + &TEA6420_line[6][0]); + mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, + &TEA6420_line[6][1]); } + DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value)); } return 0; } @@ -621,106 +583,84 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) int *input = (int *)arg; *input = mxb->cur_input; - DEB_EE(("VIDIOC_G_INPUT %d.\n",*input)); + DEB_EE(("VIDIOC_G_INPUT %d.\n", *input)); return 0; } case VIDIOC_S_INPUT: { int input = *(int *)arg; - struct tea6415c_multiplex vm; + struct tea6415c_multiplex vm; + struct v4l2_routing route; int i = 0; - DEB_EE(("VIDIOC_S_INPUT %d.\n",input)); + DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); - if (input < 0 || input >= MXB_INPUTS) { + if (input < 0 || input >= MXB_INPUTS) return -EINVAL; - } - - /* fixme: locke das setzen des inputs mit hilfe des mutexes - mutex_lock(&dev->lock); - video_mux(dev,*i); - mutex_unlock(&dev->lock); - */ - - /* fixme: check if streaming capture - if ( 0 != dev->streaming ) { - DEB_D(("VIDIOC_S_INPUT illegal while streaming.\n")); - return -EPERM; - } - */ mxb->cur_input = input; - saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, input_port_selection[input].hps_sync); + saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, + input_port_selection[input].hps_sync); /* prepare switching of tea6415c and saa7111a; have a look at the 'background'-file for further informations */ - switch( input ) { - - case TUNER: - { - i = 0; - vm.in = 3; - vm.out = 17; - - if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) { - printk("VIDIOC_S_INPUT: could not address tea6415c #1\n"); - return -EFAULT; - } - /* connect tuner-output always to multicable */ - vm.in = 3; - vm.out = 13; - break; - } - case AUX3_YC: - { - /* nothing to be done here. aux3_yc is - directly connected to the saa711a */ - i = 5; - break; - } - case AUX3: - { - /* nothing to be done here. aux3 is - directly connected to the saa711a */ - i = 1; - break; - } - case AUX1: - { - i = 0; - vm.in = 1; - vm.out = 17; - break; + switch (input) { + case TUNER: + i = SAA7115_COMPOSITE0; + vm.in = 3; + vm.out = 17; + + if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) { + printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n"); + return -EFAULT; } + /* connect tuner-output always to multicable */ + vm.in = 3; + vm.out = 13; + break; + case AUX3_YC: + /* nothing to be done here. aux3_yc is + directly connected to the saa711a */ + i = SAA7115_SVIDEO1; + break; + case AUX3: + /* nothing to be done here. aux3 is + directly connected to the saa711a */ + i = SAA7115_COMPOSITE1; + break; + case AUX1: + i = SAA7115_COMPOSITE0; + vm.in = 1; + vm.out = 17; + break; } /* switch video in tea6415c only if necessary */ - switch( input ) { - case TUNER: - case AUX1: - { - if ( 0 != mxb->tea6415c->driver->command(mxb->tea6415c,TEA6415C_SWITCH, &vm)) { - printk("VIDIOC_S_INPUT: could not address tea6415c #3\n"); - return -EFAULT; - } - break; - } - default: - { - break; + switch (input) { + case TUNER: + case AUX1: + if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) { + printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n"); + return -EFAULT; } + break; + default: + break; } /* switch video in saa7111a */ - if ( 0 != mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_INPUT, &i)) { + route.input = i; + route.output = 0; + if (mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route)) printk("VIDIOC_S_INPUT: could not address saa7111a #1.\n"); - } /* switch the audio-source only if necessary */ if( 0 == mxb->cur_mute ) { - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[video_audio_connect[input]][1]); + mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, + &TEA6420_line[video_audio_connect[input]][0]); + mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, + &TEA6420_line[video_audio_connect[input]][1]); } return 0; @@ -728,114 +668,44 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) case VIDIOC_G_TUNER: { struct v4l2_tuner *t = arg; - int byte = 0; - if( 0 != t->index ) { + if (t->index) { DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index)); return -EINVAL; } DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index)); - memset(t,0,sizeof(*t)); - strcpy(t->name, "Television"); + memset(t, 0, sizeof(*t)); + i2c_clients_command(&mxb->i2c_adapter, cmd, arg); + strlcpy(t->name, "TV Tuner", sizeof(t->name)); t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */ - t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */ - /* FIXME: add the real signal strength here */ - t->signal = 0xffff; - t->afc = 0; - - mxb->tda9840->driver->command(mxb->tda9840,TDA9840_DETECT, &byte); + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | \ + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; t->audmode = mxb->cur_mode; - - if( byte < 0 ) { - t->rxsubchans = V4L2_TUNER_SUB_MONO; - } else { - switch(byte) { - case TDA9840_MONO_DETECT: { - t->rxsubchans = V4L2_TUNER_SUB_MONO; - DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_MONO.\n")); - break; - } - case TDA9840_DUAL_DETECT: { - t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_LANG1.\n")); - break; - } - case TDA9840_STEREO_DETECT: { - t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; - DEB_D(("VIDIOC_G_TUNER: V4L2_TUNER_MODE_STEREO.\n")); - break; - } - default: { /* TDA9840_INCORRECT_DETECT */ - t->rxsubchans = V4L2_TUNER_MODE_MONO; - DEB_D(("VIDIOC_G_TUNER: TDA9840_INCORRECT_DETECT => V4L2_TUNER_MODE_MONO\n")); - break; - } - } - } - return 0; } case VIDIOC_S_TUNER: { struct v4l2_tuner *t = arg; - int result = 0; - int byte = 0; - if( 0 != t->index ) { + if (t->index) { DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n",t->index)); return -EINVAL; } - switch(t->audmode) { - case V4L2_TUNER_MODE_STEREO: { - mxb->cur_mode = V4L2_TUNER_MODE_STEREO; - byte = TDA9840_SET_STEREO; - DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n")); - break; - } - case V4L2_TUNER_MODE_LANG1_LANG2: { - mxb->cur_mode = V4L2_TUNER_MODE_LANG1_LANG2; - byte = TDA9840_SET_BOTH; - DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n")); - break; - } - case V4L2_TUNER_MODE_LANG1: { - mxb->cur_mode = V4L2_TUNER_MODE_LANG1; - byte = TDA9840_SET_LANG1; - DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n")); - break; - } - case V4L2_TUNER_MODE_LANG2: { - mxb->cur_mode = V4L2_TUNER_MODE_LANG2; - byte = TDA9840_SET_LANG2; - DEB_D(("VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n")); - break; - } - default: { /* case V4L2_TUNER_MODE_MONO: {*/ - mxb->cur_mode = V4L2_TUNER_MODE_MONO; - byte = TDA9840_SET_MONO; - DEB_D(("VIDIOC_S_TUNER: TDA9840_SET_MONO\n")); - break; - } - } - - if( 0 != (result = mxb->tda9840->driver->command(mxb->tda9840, TDA9840_SWITCH, &byte))) { - printk("VIDIOC_S_TUNER error. result:%d, byte:%d\n",result,byte); - } - + mxb->cur_mode = t->audmode; + i2c_clients_command(&mxb->i2c_adapter, cmd, arg); return 0; } case VIDIOC_G_FREQUENCY: { struct v4l2_frequency *f = arg; - if(0 != mxb->cur_input) { - DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",mxb->cur_input)); + if (mxb->cur_input) { + DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n", + mxb->cur_input)); return -EINVAL; } @@ -848,14 +718,14 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { struct v4l2_frequency *f = arg; - if (0 != f->tuner) + if (f->tuner) return -EINVAL; if (V4L2_TUNER_ANALOG_TV != f->type) return -EINVAL; - if(0 != mxb->cur_input) { - DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n",mxb->cur_input)); + if (mxb->cur_input) { + DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input)); return -EINVAL; } @@ -876,7 +746,7 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { int i = *(int*)arg; - if( i < 0 || i >= MXB_AUDIOS ) { + if (i < 0 || i >= MXB_AUDIOS) { DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n",i)); return -EINVAL; } @@ -892,7 +762,7 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { int i = *(int*)arg; - if( i < 0 || i >= MXB_AUDIOS ) { + if (i < 0 || i >= MXB_AUDIOS) { DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n",i)); return -EINVAL; } @@ -907,12 +777,12 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) { struct v4l2_audio *a = arg; - if( a->index < 0 || a->index > MXB_INPUTS ) { - DEB_D(("VIDIOC_G_AUDIO %d out of range.\n",a->index)); + if (a->index < 0 || a->index > MXB_INPUTS) { + DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index)); return -EINVAL; } - DEB_EE(("VIDIOC_G_AUDIO %d.\n",a->index)); + DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index)); memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio)); return 0; @@ -920,9 +790,16 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) case VIDIOC_S_AUDIO: { struct v4l2_audio *a = arg; - DEB_D(("VIDIOC_S_AUDIO %d.\n",a->index)); + + DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index)); return 0; } +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_S_REGISTER: + case VIDIOC_DBG_G_REGISTER: + i2c_clients_command(&mxb->i2c_adapter, cmd, arg); + return 0; +#endif default: /* DEB2(printk("does not handle this ioctl.\n")); @@ -932,27 +809,29 @@ static int mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) return 0; } -static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) +static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard) { - struct mxb* mxb = (struct mxb*)dev->ext_priv; + struct mxb *mxb = (struct mxb *)dev->ext_priv; int zero = 0; int one = 1; - if(V4L2_STD_PAL_I == std->id ) { + if (V4L2_STD_PAL_I == standard->id) { v4l2_std_id std = V4L2_STD_PAL_I; + DEB_D(("VIDIOC_S_STD: setting mxb for PAL_I.\n")); /* set the 7146 gpio register -- I don't know what this does exactly */ saa7146_write(dev, GPIO_CTRL, 0x00404050); /* unset the 7111 gpio register -- I don't know what this does exactly */ - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_GPIO, &zero); + mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &zero); mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); } else { v4l2_std_id std = V4L2_STD_PAL_BG; + DEB_D(("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM.\n")); /* set the 7146 gpio register -- I don't know what this does exactly */ saa7146_write(dev, GPIO_CTRL, 0x00404050); /* set the 7111 gpio register -- I don't know what this does exactly */ - mxb->saa7111a->driver->command(mxb->saa7111a,DECODER_SET_GPIO, &one); + mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &one); mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); } return 0; @@ -1028,7 +907,7 @@ static struct saa7146_extension extension = { static int __init mxb_init_module(void) { - if( 0 != saa7146_register_extension(&extension)) { + if (saa7146_register_extension(&extension)) { DEB_S(("failed to register extension.\n")); return -ENODEV; } diff --git a/linux/drivers/media/video/ov511.c b/linux/drivers/media/video/ov511.c index c693b6fb0..3f1d7a660 100644 --- a/linux/drivers/media/video/ov511.c +++ b/linux/drivers/media/video/ov511.c @@ -626,9 +626,9 @@ ov511_i2c_write_internal(struct usb_ov511 *ov, break; /* Retry until idle */ - do + do { rc = reg_r(ov, R511_I2C_CTL); - while (rc > 0 && ((rc&1) == 0)); + } while (rc > 0 && ((rc&1) == 0)); if (rc < 0) break; @@ -703,9 +703,9 @@ ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) return rc; /* Retry until idle */ - do - rc = reg_r(ov, R511_I2C_CTL); - while (rc > 0 && ((rc&1) == 0)); + do { + rc = reg_r(ov, R511_I2C_CTL); + } while (rc > 0 && ((rc & 1) == 0)); if (rc < 0) return rc; @@ -729,9 +729,9 @@ ov511_i2c_read_internal(struct usb_ov511 *ov, unsigned char reg) return rc; /* Retry until idle */ - do + do { rc = reg_r(ov, R511_I2C_CTL); - while (rc > 0 && ((rc&1) == 0)); + } while (rc > 0 && ((rc&1) == 0)); if (rc < 0) return rc; @@ -3595,7 +3595,7 @@ static int ov51x_init_isoc(struct usb_ov511 *ov) { struct urb *urb; - int fx, err, n, size; + int fx, err, n, i, size; PDEBUG(3, "*** Initializing capture ***"); @@ -3666,6 +3666,8 @@ ov51x_init_isoc(struct usb_ov511 *ov) urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); if (!urb) { err("init isoc: usb_alloc_urb ret. NULL"); + for (i = 0; i < n; i++) + usb_free_urb(ov->sbuf[i].urb); return -ENOMEM; } ov->sbuf[n].urb = urb; @@ -5655,7 +5657,7 @@ static ssize_t show_exposure(struct device *cd, if (!ov->dev) return -ENODEV; sensor_get_exposure(ov, &exp); - return sprintf(buf, "%d\n", exp >> 8); + return sprintf(buf, "%d\n", exp); } static DEVICE_ATTR(exposure, S_IRUGO, show_exposure, NULL); diff --git a/linux/drivers/media/video/ovcamchip/ovcamchip_core.c b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c index 065c24541..2c4acbf5a 100644 --- a/linux/drivers/media/video/ovcamchip/ovcamchip_core.c +++ b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -49,12 +49,6 @@ MODULE_LICENSE("GPL"); #define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */ #define GENERIC_REG_COM_I 0x29 /* misc ID bits */ -extern struct ovcamchip_ops ov6x20_ops; -extern struct ovcamchip_ops ov6x30_ops; -extern struct ovcamchip_ops ov7x10_ops; -extern struct ovcamchip_ops ov7x20_ops; -extern struct ovcamchip_ops ov76be_ops; - static char *chip_names[NUM_CC_TYPES] = { [CC_UNKNOWN] = "Unknown chip", [CC_OV76BE] = "OV76BE", diff --git a/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h b/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h index 9afa4fe47..a05650fae 100644 --- a/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h +++ b/linux/drivers/media/video/ovcamchip/ovcamchip_priv.h @@ -53,6 +53,12 @@ struct ovcamchip { int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */ }; +extern struct ovcamchip_ops ov6x20_ops; +extern struct ovcamchip_ops ov6x30_ops; +extern struct ovcamchip_ops ov7x10_ops; +extern struct ovcamchip_ops ov7x20_ops; +extern struct ovcamchip_ops ov76be_ops; + /* --------------------------------- */ /* I2C I/O */ /* --------------------------------- */ diff --git a/linux/drivers/media/video/pms.c b/linux/drivers/media/video/pms.c index e36b39b44..00b623e00 100644 --- a/linux/drivers/media/video/pms.c +++ b/linux/drivers/media/video/pms.c @@ -48,6 +48,7 @@ struct pms_device struct video_picture picture; int height; int width; + unsigned long in_use; struct mutex lock; }; @@ -882,10 +883,27 @@ static ssize_t pms_read(struct file *file, char __user *buf, return len; } +static int pms_exclusive_open(struct inode *inode, struct file *file) +{ + struct video_device *v = video_devdata(file); + struct pms_device *pd = (struct pms_device *)v; + + return test_and_set_bit(0, &pd->in_use) ? -EBUSY : 0; +} + +static int pms_exclusive_release(struct inode *inode, struct file *file) +{ + struct video_device *v = video_devdata(file); + struct pms_device *pd = (struct pms_device *)v; + + clear_bit(0, &pd->in_use); + return 0; +} + static const struct file_operations pms_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = pms_exclusive_open, + .release = pms_exclusive_release, .ioctl = pms_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -898,6 +916,7 @@ static struct video_device pms_template= { .name = "Mediavision PMS", .fops = &pms_fops, + .release = video_device_release_empty, }; static struct pms_device pms_device; @@ -1020,10 +1039,23 @@ static int init_mediavision(void) * Initialization and module stuff */ +#ifndef MODULE +static int enable; +module_param(enable, int, 0); +#endif + static int __init init_pms_cards(void) { printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n"); +#ifndef MODULE + if (!enable) { + printk(KERN_INFO "PMS: not enabled, use pms.enable=1 to " + "probe\n"); + return -ENODEV; + } +#endif + data_port = io_port +1; if(init_mediavision()) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index 3b982d963..5fa8af50b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -135,13 +135,17 @@ int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr) /* Retrieve control's default value (any type) */ -int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr) +int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr, int *valptr) { int ret = 0; if (!cptr) return 0; LOCK_TAKE(cptr->hdw->big_lock); do { if (cptr->info->type == pvr2_ctl_int) { - ret = cptr->info->default_value; + if (cptr->info->get_def_value) { + ret = cptr->info->get_def_value(cptr, valptr); + } else { + *valptr = cptr->info->default_value; + } } } while(0); LOCK_GIVE(cptr->hdw->big_lock); return ret; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h index 0371ae6e6..794ff9012 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h @@ -49,7 +49,7 @@ int pvr2_ctrl_get_max(struct pvr2_ctrl *); int pvr2_ctrl_get_min(struct pvr2_ctrl *); /* Retrieve control's default value (any type) */ -int pvr2_ctrl_get_def(struct pvr2_ctrl *); +int pvr2_ctrl_get_def(struct pvr2_ctrl *, int *valptr); /* Retrieve control's enumeration count (enum only) */ int pvr2_ctrl_get_cnt(struct pvr2_ctrl *); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 88e175168..cbe2a3417 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -489,6 +489,8 @@ static const struct pvr2_device_desc pvr2_device_751xx = { struct usb_device_id pvr2_device_table[] = { { USB_DEVICE(0x2040, 0x2900), .driver_info = (kernel_ulong_t)&pvr2_device_29xxx}, + { USB_DEVICE(0x2040, 0x2950), /* Logically identical to 2900 */ + .driver_info = (kernel_ulong_t)&pvr2_device_29xxx}, { USB_DEVICE(0x2040, 0x2400), .driver_info = (kernel_ulong_t)&pvr2_device_24xxx}, { USB_DEVICE(0x1164, 0x0622), diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 657f86159..de7ee7264 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -82,6 +82,7 @@ struct pvr2_ctl_info { /* Control's implementation */ pvr2_ctlf_get_value get_value; /* Get its value */ + pvr2_ctlf_get_value get_def_value; /* Get its default value */ pvr2_ctlf_get_value get_min_value; /* Get minimum allowed value */ pvr2_ctlf_get_value get_max_value; /* Get maximum allowed value */ pvr2_ctlf_set_value set_value; /* Set its value */ @@ -307,6 +308,10 @@ struct pvr2_hdw { struct v4l2_tuner tuner_signal_info; int tuner_signal_stale; + /* Cropping capability info */ + struct v4l2_cropcap cropcap_info; + int cropcap_stale; + /* Video standard handling */ v4l2_std_id std_mask_eeprom; // Hardware supported selections v4l2_std_id std_mask_avail; // Which standards we may select from @@ -367,6 +372,10 @@ struct pvr2_hdw { VCREATE_DATA(bass); VCREATE_DATA(treble); VCREATE_DATA(mute); + VCREATE_DATA(cropl); + VCREATE_DATA(cropt); + VCREATE_DATA(cropw); + VCREATE_DATA(croph); VCREATE_DATA(input); VCREATE_DATA(audiomode); VCREATE_DATA(res_hor); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 419a473cf..b79829f8a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -299,6 +299,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, unsigned int timeout,int probe_fl, void *write_data,unsigned int write_len, void *read_data,unsigned int read_len); +static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw); static void trace_stbit(const char *name,int val) @@ -403,6 +404,194 @@ static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) return 0; } +static int ctrl_cropl_min_get(struct pvr2_ctrl *cptr, int *left) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *left = cap->bounds.left; + return 0; +} + +static int ctrl_cropl_max_get(struct pvr2_ctrl *cptr, int *left) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *left = cap->bounds.left; + if (cap->bounds.width > cptr->hdw->cropw_val) { + *left += cap->bounds.width - cptr->hdw->cropw_val; + } + return 0; +} + +static int ctrl_cropt_min_get(struct pvr2_ctrl *cptr, int *top) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *top = cap->bounds.top; + return 0; +} + +static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *top = cap->bounds.top; + if (cap->bounds.height > cptr->hdw->croph_val) { + *top += cap->bounds.height - cptr->hdw->croph_val; + } + return 0; +} + +static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = 0; + if (cap->bounds.width > cptr->hdw->cropl_val) { + *val = cap->bounds.width - cptr->hdw->cropl_val; + } + return 0; +} + +static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = 0; + if (cap->bounds.height > cptr->hdw->cropt_val) { + *val = cap->bounds.height - cptr->hdw->cropt_val; + } + return 0; +} + +static int ctrl_get_cropcapbl(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.left; + return 0; +} + +static int ctrl_get_cropcapbt(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.top; + return 0; +} + +static int ctrl_get_cropcapbw(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.width; + return 0; +} + +static int ctrl_get_cropcapbh(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->bounds.height; + return 0; +} + +static int ctrl_get_cropcapdl(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.left; + return 0; +} + +static int ctrl_get_cropcapdt(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.top; + return 0; +} + +static int ctrl_get_cropcapdw(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.width; + return 0; +} + +static int ctrl_get_cropcapdh(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->defrect.height; + return 0; +} + +static int ctrl_get_cropcappan(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->pixelaspect.numerator; + return 0; +} + +static int ctrl_get_cropcappad(struct pvr2_ctrl *cptr, int *val) +{ + struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; + int stat = pvr2_hdw_check_cropcap(cptr->hdw); + if (stat != 0) { + return stat; + } + *val = cap->pixelaspect.denominator; + return 0; +} + static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp) { /* Actual maximum depends on the video standard in effect. */ @@ -780,6 +969,10 @@ VCREATE_FUNCS(balance) VCREATE_FUNCS(bass) VCREATE_FUNCS(treble) VCREATE_FUNCS(mute) +VCREATE_FUNCS(cropl) +VCREATE_FUNCS(cropt) +VCREATE_FUNCS(cropw) +VCREATE_FUNCS(croph) VCREATE_FUNCS(audiomode) VCREATE_FUNCS(res_hor) VCREATE_FUNCS(res_ver) @@ -850,6 +1043,72 @@ static const struct pvr2_ctl_info control_defs[] = { .default_value = 0, DEFREF(mute), DEFBOOL, + }, { + .desc = "Capture crop left margin", + .name = "crop_left", + .internal_id = PVR2_CID_CROPL, + .default_value = 0, + DEFREF(cropl), + DEFINT(-129, 340), + .get_min_value = ctrl_cropl_min_get, + .get_max_value = ctrl_cropl_max_get, + .get_def_value = ctrl_get_cropcapdl, + }, { + .desc = "Capture crop top margin", + .name = "crop_top", + .internal_id = PVR2_CID_CROPT, + .default_value = 0, + DEFREF(cropt), + DEFINT(-35, 544), + .get_min_value = ctrl_cropt_min_get, + .get_max_value = ctrl_cropt_max_get, + .get_def_value = ctrl_get_cropcapdt, + }, { + .desc = "Capture crop width", + .name = "crop_width", + .internal_id = PVR2_CID_CROPW, + .default_value = 720, + DEFREF(cropw), + .get_max_value = ctrl_cropw_max_get, + .get_def_value = ctrl_get_cropcapdw, + }, { + .desc = "Capture crop height", + .name = "crop_height", + .internal_id = PVR2_CID_CROPH, + .default_value = 480, + DEFREF(croph), + .get_max_value = ctrl_croph_max_get, + .get_def_value = ctrl_get_cropcapdh, + }, { + .desc = "Capture capability pixel aspect numerator", + .name = "cropcap_pixel_numerator", + .internal_id = PVR2_CID_CROPCAPPAN, + .get_value = ctrl_get_cropcappan, + }, { + .desc = "Capture capability pixel aspect denominator", + .name = "cropcap_pixel_denominator", + .internal_id = PVR2_CID_CROPCAPPAD, + .get_value = ctrl_get_cropcappad, + }, { + .desc = "Capture capability bounds top", + .name = "cropcap_bounds_top", + .internal_id = PVR2_CID_CROPCAPBT, + .get_value = ctrl_get_cropcapbt, + }, { + .desc = "Capture capability bounds left", + .name = "cropcap_bounds_left", + .internal_id = PVR2_CID_CROPCAPBL, + .get_value = ctrl_get_cropcapbl, + }, { + .desc = "Capture capability bounds width", + .name = "cropcap_bounds_width", + .internal_id = PVR2_CID_CROPCAPBW, + .get_value = ctrl_get_cropcapbw, + }, { + .desc = "Capture capability bounds height", + .name = "cropcap_bounds_height", + .internal_id = PVR2_CID_CROPCAPBH, + .get_value = ctrl_get_cropcapbh, },{ .desc = "Video Source", .name = "input", @@ -1323,9 +1582,19 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE; memcpy(fw_ptr, fw_entry->data + fw_done, bcnt); /* Usbsnoop log shows that we must swap bytes... */ + /* Some background info: The data being swapped here is a + firmware image destined for the mpeg encoder chip that + lives at the other end of a USB endpoint. The encoder + chip always talks in 32 bit chunks and its storage is + organized into 32 bit words. However from the file + system to the encoder chip everything is purely a byte + stream. The firmware file's contents are always 32 bit + swapped from what the encoder expects. Thus the need + always exists to swap the bytes regardless of the endian + type of the host processor and therefore swab32() makes + the most sense. */ for (icnt = 0; icnt < bcnt/4 ; icnt++) - ((u32 *)fw_ptr)[icnt] = - ___swab32(((u32 *)fw_ptr)[icnt]); + ((u32 *)fw_ptr)[icnt] = swab32(((u32 *)fw_ptr)[icnt]); ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt, &actual_length, HZ); @@ -1922,7 +2191,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, const struct usb_device_id *devid) { unsigned int idx,cnt1,cnt2,m; - struct pvr2_hdw *hdw; + struct pvr2_hdw *hdw = NULL; int valid_std_mask; struct pvr2_ctrl *cptr; const struct pvr2_device_desc *hdw_desc; @@ -1932,6 +2201,16 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info); + if (hdw_desc == NULL) { + pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_create:" + " No device description pointer," + " unable to continue."); + pvr2_trace(PVR2_TRACE_INIT, "If you have a new device type," + " please contact Mike Isely <isely@pobox.com>" + " to get it included in the driver\n"); + goto fail; + } + hdw = kzalloc(sizeof(*hdw),GFP_KERNEL); pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", hdw,hdw_desc->description); @@ -2089,6 +2368,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, valid_std_mask; } + hdw->cropcap_stale = !0; hdw->eeprom_addr = -1; hdw->unit_number = -1; hdw->v4l_minor_number_video = -1; @@ -2532,6 +2812,28 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) /* Can't commit anything until pathway is ok. */ return 0; } + /* The broadcast decoder can only scale down, so if + * res_*_dirty && crop window < output format ==> enlarge crop. + * + * The mpeg encoder receives fields of res_hor_val dots and + * res_ver_val halflines. Limits: hor<=720, ver<=576. + */ + if (hdw->res_hor_dirty && hdw->cropw_val < hdw->res_hor_val) { + hdw->cropw_val = hdw->res_hor_val; + hdw->cropw_dirty = !0; + } else if (hdw->cropw_dirty) { + hdw->res_hor_dirty = !0; /* must rescale */ + hdw->res_hor_val = min(720, hdw->cropw_val); + } + if (hdw->res_ver_dirty && hdw->croph_val < hdw->res_ver_val) { + hdw->croph_val = hdw->res_ver_val; + hdw->croph_dirty = !0; + } else if (hdw->croph_dirty) { + int nvres = hdw->std_mask_cur & V4L2_STD_525_60 ? 480 : 576; + hdw->res_ver_dirty = !0; + hdw->res_ver_val = min(nvres, hdw->croph_val); + } + /* If any of the below has changed, then we can't do the update while the pipeline is running. Pipeline must be paused first and decoder -> encoder connection be made quiescent before we @@ -2542,6 +2844,8 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) hdw->srate_dirty || hdw->res_ver_dirty || hdw->res_hor_dirty || + hdw->cropw_dirty || + hdw->croph_dirty || hdw->input_dirty || (hdw->active_stream_type != hdw->desired_stream_type)); if (disruptive_change && !hdw->state_pipeline_idle) { @@ -2611,6 +2915,9 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) } hdw->state_pipeline_config = !0; + /* Hardware state may have changed in a way to cause the cropping + capabilities to have changed. So mark it stale, which will + cause a later re-fetch. */ trace_stbit("state_pipeline_config",hdw->state_pipeline_config); return !0; } @@ -2701,6 +3008,33 @@ void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw) } +static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw) +{ + if (!hdw->cropcap_stale) { + return 0; + } + pvr2_i2c_core_status_poll(hdw); + if (hdw->cropcap_stale) { + return -EIO; + } + return 0; +} + + +/* Return information about cropping capabilities */ +int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp) +{ + int stat = 0; + LOCK_TAKE(hdw->big_lock); + stat = pvr2_hdw_check_cropcap(hdw); + if (!stat) { + memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info)); + } + LOCK_GIVE(hdw->big_lock); + return stat; +} + + /* Return information about the tuner */ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 4e60f6262..29fb0729d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -37,6 +37,16 @@ #define PVR2_CID_FREQUENCY 6 #define PVR2_CID_HRES 7 #define PVR2_CID_VRES 8 +#define PVR2_CID_CROPL 9 +#define PVR2_CID_CROPT 10 +#define PVR2_CID_CROPW 11 +#define PVR2_CID_CROPH 12 +#define PVR2_CID_CROPCAPPAN 13 +#define PVR2_CID_CROPCAPPAD 14 +#define PVR2_CID_CROPCAPBL 15 +#define PVR2_CID_CROPCAPBT 16 +#define PVR2_CID_CROPCAPBW 17 +#define PVR2_CID_CROPCAPBH 18 /* Legal values for the INPUT state variable */ #define PVR2_CVAL_INPUT_TV 0 @@ -176,6 +186,9 @@ void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *); /* Return information about the tuner */ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *); +/* Return information about cropping capabilities */ +int pvr2_hdw_get_cropcap(struct pvr2_hdw *, struct v4l2_cropcap *); + /* Query device and see if it thinks it is on a high-speed USB link */ int pvr2_hdw_is_hsm(struct pvr2_hdw *); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index 25e611b2e..1b1424955 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -38,8 +38,9 @@ #define OP_VOLUME 3 #define OP_FREQ 4 #define OP_AUDIORATE 5 -#define OP_SIZE 6 -#define OP_LOG 7 +#define OP_CROP 6 +#define OP_SIZE 7 +#define OP_LOG 8 static const struct pvr2_i2c_op * const ops[] = { [OP_STANDARD] = &pvr2_i2c_op_v4l2_standard, @@ -47,6 +48,7 @@ static const struct pvr2_i2c_op * const ops[] = { [OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh, [OP_VOLUME] = &pvr2_i2c_op_v4l2_volume, [OP_FREQ] = &pvr2_i2c_op_v4l2_frequency, + [OP_CROP] = &pvr2_i2c_op_v4l2_crop, [OP_SIZE] = &pvr2_i2c_op_v4l2_size, [OP_LOG] = &pvr2_i2c_op_v4l2_log, }; @@ -60,6 +62,7 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) (1 << OP_BCSH) | (1 << OP_VOLUME) | (1 << OP_FREQ) | + (1 << OP_CROP) | (1 << OP_SIZE) | (1 << OP_LOG)); cp->status_poll = pvr2_v4l2_cmd_status_poll; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index d715c80ef..ab703d6d5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -38,6 +38,7 @@ static void set_standard(struct pvr2_hdw *hdw) pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs); } hdw->tuner_signal_stale = !0; + hdw->cropcap_stale = !0; } @@ -234,6 +235,37 @@ const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size = { }; +static void set_crop(struct pvr2_hdw *hdw) +{ + struct v4l2_crop crop; + + memset(&crop, 0, sizeof crop); + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop.c.left = hdw->cropl_val; + crop.c.top = hdw->cropt_val; + crop.c.height = hdw->croph_val; + crop.c.width = hdw->cropw_val; + + pvr2_trace(PVR2_TRACE_CHIPS, + "i2c v4l2 set_crop crop=%d:%d:%d:%d", + crop.c.width, crop.c.height, crop.c.left, crop.c.top); + + pvr2_i2c_core_cmd(hdw, VIDIOC_S_CROP, &crop); +} + +static int check_crop(struct pvr2_hdw *hdw) +{ + return (hdw->cropl_dirty || hdw->cropt_dirty || + hdw->cropw_dirty || hdw->croph_dirty); +} + +const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop = { + .check = check_crop, + .update = set_crop, + .name = "v4l2_crop", +}; + + static void do_log(struct pvr2_hdw *hdw) { pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 do_log()"); @@ -264,7 +296,19 @@ void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl) void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *cp) { - pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&cp->hdw->tuner_signal_info); + int stat; + struct pvr2_hdw *hdw = cp->hdw; + if (hdw->cropcap_stale) { + hdw->cropcap_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + stat = pvr2_i2c_client_cmd(cp, VIDIOC_CROPCAP, + &hdw->cropcap_info); + if (stat == 0) { + /* Check was successful, so the data is no + longer considered stale. */ + hdw->cropcap_stale = 0; + } + } + pvr2_i2c_client_cmd(cp, VIDIOC_G_TUNER, &hdw->tuner_signal_info); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h index 7fa38683b..eb744a206 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h @@ -29,6 +29,7 @@ extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_radio; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency; +extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 9159686c3..edce8fc34 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -902,7 +902,7 @@ static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp, if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) { unsigned int idx; unsigned long msk,sm; - int spcfl; + bcnt = scnprintf(buf,maxlen," ["); ccnt += bcnt; buf += bcnt; maxlen -= bcnt; sm = 0; @@ -966,6 +966,7 @@ static int pvr2_i2c_attach_inform(struct i2c_client *client) INIT_LIST_HEAD(&cp->list); cp->client = client; mutex_lock(&hdw->i2c_list_lock); do { + hdw->cropcap_stale = !0; list_add_tail(&cp->list,&hdw->i2c_clients); hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT; } while (0); mutex_unlock(&hdw->i2c_list_lock); @@ -980,6 +981,7 @@ static int pvr2_i2c_detach_inform(struct i2c_client *client) unsigned long amask = 0; int foundfl = 0; mutex_lock(&hdw->i2c_list_lock); do { + hdw->cropcap_stale = !0; list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) { if (cp->client == client) { trace_i2c("pvr2_i2c_detach" @@ -1021,22 +1023,32 @@ static struct i2c_adapter pvr2_i2c_adap_template = { .client_unregister = pvr2_i2c_detach_inform, }; -static void do_i2c_scan(struct pvr2_hdw *hdw) + +/* Return true if device exists at given address */ +static int do_i2c_probe(struct pvr2_hdw *hdw, int addr) { struct i2c_msg msg[1]; - int i,rc; + int rc; msg[0].addr = 0; msg[0].flags = I2C_M_RD; msg[0].len = 0; msg[0].buf = NULL; - printk("%s: i2c scan beginning\n",hdw->name); + msg[0].addr = addr; + rc = i2c_transfer(&hdw->i2c_adap, msg, ARRAY_SIZE(msg)); + return rc == 1; +} + +static void do_i2c_scan(struct pvr2_hdw *hdw) +{ + int i; + printk(KERN_INFO "%s: i2c scan beginning\n", hdw->name); for (i = 0; i < 128; i++) { - msg[0].addr = i; - rc = i2c_transfer(&hdw->i2c_adap,msg, ARRAY_SIZE(msg)); - if (rc != 1) continue; - printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i); + if (do_i2c_probe(hdw, i)) { + printk(KERN_INFO "%s: i2c scan: found device @ 0x%x\n", + hdw->name, i); + } } - printk("%s: i2c scan done.\n",hdw->name); + printk(KERN_INFO "%s: i2c scan done.\n", hdw->name); } void pvr2_i2c_core_init(struct pvr2_hdw *hdw) @@ -1055,8 +1067,6 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) hdw->i2c_func[0x18] = i2c_black_hole; } else if (ir_mode[hdw->unit_number] == 1) { if (hdw->hdw_desc->ir_scheme == PVR2_IR_SCHEME_24XXX) { - /* This comment is present PURELY to get - checkpatch.pl to STFU. Lovely, eh? */ hdw->i2c_func[0x18] = i2c_24xxx_ir; } } @@ -1081,6 +1091,16 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) mutex_init(&hdw->i2c_list_lock); hdw->i2c_linked = !0; i2c_add_adapter(&hdw->i2c_adap); + if (hdw->i2c_func[0x18] == i2c_24xxx_ir) { + /* Probe for a different type of IR receiver on this + device. If present, disable the emulated IR receiver. */ + if (do_i2c_probe(hdw, 0x71)) { + pvr2_trace(PVR2_TRACE_INFO, + "Device has newer IR hardware;" + " disabling unneeded virtual IR device"); + hdw->i2c_func[0x18] = NULL; + } + } if (i2c_scan) do_i2c_scan(hdw); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 90cb36435..16461dd92 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -66,6 +66,7 @@ struct pvr2_sysfs_ctl_item { struct device_attribute attr_type; struct device_attribute attr_min; struct device_attribute attr_max; + struct device_attribute attr_def; struct device_attribute attr_enum; struct device_attribute attr_bits; struct device_attribute attr_val; @@ -146,6 +147,23 @@ static ssize_t show_max(struct device *class_dev, return scnprintf(buf, PAGE_SIZE, "%ld\n", val); } +static ssize_t show_def(struct device *class_dev, + struct device_attribute *attr, + char *buf) +{ + struct pvr2_sysfs_ctl_item *cip; + int val; + int ret; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_def); + ret = pvr2_ctrl_get_def(cip->cptr, &val); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_def(cid=%d) is %d, stat=%d", + cip->chptr, cip->ctl_id, val, ret); + if (ret < 0) { + return ret; + } + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + static ssize_t show_val_norm(struct device *class_dev, struct device_attribute *attr, char *buf) @@ -321,6 +339,10 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_max.attr.mode = S_IRUGO; cip->attr_max.show = show_max; + cip->attr_def.attr.name = "def_val"; + cip->attr_def.attr.mode = S_IRUGO; + cip->attr_def.show = show_def; + cip->attr_val.attr.name = "cur_val"; cip->attr_val.attr.mode = S_IRUGO; @@ -344,6 +366,7 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_gen[acnt++] = &cip->attr_name.attr; cip->attr_gen[acnt++] = &cip->attr_type.attr; cip->attr_gen[acnt++] = &cip->attr_val.attr; + cip->attr_gen[acnt++] = &cip->attr_def.attr; cip->attr_val.show = show_val_norm; cip->attr_val.store = store_val_norm; if (pvr2_ctrl_has_custom_symbols(cptr)) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index c1cc0e88b..429c96d4f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -534,7 +534,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, lmin = pvr2_ctrl_get_min(hcp); lmax = pvr2_ctrl_get_max(hcp); - ldef = pvr2_ctrl_get_def(hcp); + pvr2_ctrl_get_def(hcp, &ldef); if (w == -1) { w = ldef; } else if (w < lmin) { @@ -544,7 +544,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, } lmin = pvr2_ctrl_get_min(vcp); lmax = pvr2_ctrl_get_max(vcp); - ldef = pvr2_ctrl_get_def(vcp); + pvr2_ctrl_get_def(vcp, &ldef); if (h == -1) { h = ldef; } else if (h < lmin) { @@ -605,6 +605,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_QUERYCTRL: { struct pvr2_ctrl *cptr; + int val; struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg; ret = 0; if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { @@ -628,7 +629,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, pvr2_ctrl_get_desc(cptr)); strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name)); vc->flags = pvr2_ctrl_get_v4lflags(cptr); - vc->default_value = pvr2_ctrl_get_def(cptr); + pvr2_ctrl_get_def(cptr, &val); + vc->default_value = val; switch (pvr2_ctrl_get_type(cptr)) { case pvr2_ctl_enum: vc->type = V4L2_CTRL_TYPE_MENU; @@ -754,6 +756,92 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, break; } + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *cap = (struct v4l2_cropcap *)arg; + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + ret = -EINVAL; + break; + } + ret = pvr2_hdw_get_cropcap(hdw, cap); + cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */ + break; + } + case VIDIOC_G_CROP: + { + struct v4l2_crop *crop = (struct v4l2_crop *)arg; + int val = 0; + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + ret = -EINVAL; + break; + } + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); + if (ret != 0) { + ret = -EINVAL; + break; + } + crop->c.left = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); + if (ret != 0) { + ret = -EINVAL; + break; + } + crop->c.top = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); + if (ret != 0) { + ret = -EINVAL; + break; + } + crop->c.width = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); + if (ret != 0) { + ret = -EINVAL; + break; + } + crop->c.height = val; + } + case VIDIOC_S_CROP: + { + struct v4l2_crop *crop = (struct v4l2_crop *)arg; + struct v4l2_cropcap cap; + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + ret = -EINVAL; + break; + } + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), + crop->c.left); + if (ret != 0) { + ret = -EINVAL; + break; + } + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), + crop->c.top); + if (ret != 0) { + ret = -EINVAL; + break; + } + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), + crop->c.width); + if (ret != 0) { + ret = -EINVAL; + break; + } + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), + crop->c.height); + if (ret != 0) { + ret = -EINVAL; + break; + } + } case VIDIOC_LOG_STATUS: { pvr2_hdw_trigger_module_log(hdw); @@ -933,7 +1021,6 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) unsigned int input_cnt,idx; int ret = 0; - lock_kernel(); dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase); vp = dip->v4lp; @@ -944,13 +1031,11 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) if (!pvr2_hdw_dev_ok(hdw)) { pvr2_trace(PVR2_TRACE_OPEN_CLOSE, "pvr2_v4l2_open: hardware not ready"); - unlock_kernel(); return -EIO; } fhp = kzalloc(sizeof(*fhp),GFP_KERNEL); if (!fhp) { - unlock_kernel(); return -ENOMEM; } @@ -980,7 +1065,6 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) fhp); kfree(fhp); - unlock_kernel(); return ret; } @@ -997,7 +1081,6 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) "Destroying pvr_v4l2_fh id=%p (input map failure)", fhp); kfree(fhp); - unlock_kernel(); return -ENOMEM; } input_cnt = 0; @@ -1021,7 +1104,6 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) v4l2_prio_open(&vp->prio,&fhp->prio); fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw); - unlock_kernel(); return 0; } diff --git a/linux/drivers/media/video/pwc/pwc-ctrl.c b/linux/drivers/media/video/pwc/pwc-ctrl.c index 1cccd5c77..c66530210 100644 --- a/linux/drivers/media/video/pwc/pwc-ctrl.c +++ b/linux/drivers/media/video/pwc/pwc-ctrl.c @@ -41,7 +41,6 @@ #include <asm/uaccess.h> #endif #include <asm/errno.h> -#include <linux/version.h> #include "pwc.h" #include "pwc-uncompress.h" @@ -1635,15 +1634,15 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) case VIDIOCPWCGVIDCMD: { - ARG_DEF(struct pwc_video_command, cmd); - - ARGR(cmd).type = pdev->type; - ARGR(cmd).release = pdev->release; - ARGR(cmd).command_len = pdev->cmd_len; - memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len); - ARGR(cmd).bandlength = pdev->vbandlength; - ARGR(cmd).frame_size = pdev->frame_size; - ARG_OUT(cmd) + ARG_DEF(struct pwc_video_command, vcmd); + + ARGR(vcmd).type = pdev->type; + ARGR(vcmd).release = pdev->release; + ARGR(vcmd).command_len = pdev->cmd_len; + memcpy(&ARGR(vcmd).command_buf, pdev->cmd_buf, pdev->cmd_len); + ARGR(vcmd).bandlength = pdev->vbandlength; + ARGR(vcmd).frame_size = pdev->frame_size; + ARG_OUT(vcmd) break; } /* diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c index f005e2338..69917b2f9 100644 --- a/linux/drivers/media/video/pwc/pwc-if.c +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -1116,7 +1116,7 @@ static int pwc_video_open(struct inode *inode, struct file *file) PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev); - pdev = (struct pwc_device *)vdev->priv; + pdev = video_get_drvdata(vdev); BUG_ON(!pdev); if (pdev->vopen) { PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n"); @@ -1237,7 +1237,7 @@ static int pwc_video_close(struct inode *inode, struct file *file) PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev); lock_kernel(); - pdev = (struct pwc_device *)vdev->priv; + pdev = video_get_drvdata(vdev); if (pdev->vopen == 0) PWC_DEBUG_MODULE("video_close() called on closed device?\n"); @@ -1308,7 +1308,7 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, vdev, buf, count); if (vdev == NULL) return -EFAULT; - pdev = vdev->priv; + pdev = video_get_drvdata(vdev); if (pdev == NULL) return -EFAULT; @@ -1390,7 +1390,7 @@ static unsigned int pwc_video_poll(struct file *file, poll_table *wait) if (vdev == NULL) return -EFAULT; - pdev = vdev->priv; + pdev = video_get_drvdata(vdev); if (pdev == NULL) return -EFAULT; @@ -1412,7 +1412,7 @@ static int pwc_video_ioctl(struct inode *inode, struct file *file, if (!vdev) goto out; - pdev = vdev->priv; + pdev = video_get_drvdata(vdev); mutex_lock(&pdev->modlock); if (!pdev->unplugged) @@ -1432,7 +1432,7 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) int index; PWC_DEBUG_MEMORY(">> %s\n", __func__); - pdev = vdev->priv; + pdev = video_get_drvdata(vdev); size = vma->vm_end - vma->vm_start; start = vma->vm_start; diff --git a/linux/drivers/media/video/pwc/pwc-v4l.c b/linux/drivers/media/video/pwc/pwc-v4l.c index 174288987..76a1376c9 100644 --- a/linux/drivers/media/video/pwc/pwc-v4l.c +++ b/linux/drivers/media/video/pwc/pwc-v4l.c @@ -346,7 +346,7 @@ int pwc_video_do_ioctl(struct inode *inode, struct file *file, if (vdev == NULL) return -EFAULT; - pdev = vdev->priv; + pdev = video_get_drvdata(vdev); if (pdev == NULL) return -EFAULT; diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index ead87ddaf..00aa421fe 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -36,8 +36,13 @@ #include <linux/videodev2.h> #include <asm/dma.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +#include <mach/pxa-regs.h> +#include <mach/camera.h> +#else #include <asm/arch/pxa-regs.h> #include <asm/arch/camera.h> +#endif #define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) #define PXA_CAM_DRV_NAME "pxa27x-camera" @@ -629,17 +634,6 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev) pdata->init(pcdev->dev); } - if (pdata && pdata->power) { - dev_dbg(pcdev->dev, "%s: Power on camera\n", __func__); - pdata->power(pcdev->dev, 1); - } - - if (pdata && pdata->reset) { - dev_dbg(pcdev->dev, "%s: Releasing camera reset\n", - __func__); - pdata->reset(pcdev->dev, 1); - } - CICR0 = 0x3FF; /* disable all interrupts */ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) @@ -660,20 +654,7 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev) static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev) { - struct pxacamera_platform_data *board = pcdev->pdata; - clk_disable(pcdev->clk); - - if (board && board->reset) { - dev_dbg(pcdev->dev, "%s: Asserting camera reset\n", - __func__); - board->reset(pcdev->dev, 0); - } - - if (board && board->power) { - dev_dbg(pcdev->dev, "%s: Power off camera\n", __func__); - board->power(pcdev->dev, 0); - } } static irqreturn_t pxa_camera_irq(int irq, void *data) @@ -1144,31 +1125,31 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->dev = &pdev->dev; /* request dma */ - pcdev->dma_chans[0] = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, - pxa_camera_dma_irq_y, pcdev); - if (pcdev->dma_chans[0] < 0) { + err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, + pxa_camera_dma_irq_y, pcdev); + if (err < 0) { dev_err(pcdev->dev, "Can't request DMA for Y\n"); - err = -ENOMEM; goto exit_iounmap; } + pcdev->dma_chans[0] = err; dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]); - pcdev->dma_chans[1] = pxa_request_dma("CI_U", DMA_PRIO_HIGH, - pxa_camera_dma_irq_u, pcdev); - if (pcdev->dma_chans[1] < 0) { + err = pxa_request_dma("CI_U", DMA_PRIO_HIGH, + pxa_camera_dma_irq_u, pcdev); + if (err < 0) { dev_err(pcdev->dev, "Can't request DMA for U\n"); - err = -ENOMEM; goto exit_free_dma_y; } + pcdev->dma_chans[1] = err; dev_dbg(pcdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]); - pcdev->dma_chans[2] = pxa_request_dma("CI_V", DMA_PRIO_HIGH, - pxa_camera_dma_irq_v, pcdev); - if (pcdev->dma_chans[0] < 0) { + err = pxa_request_dma("CI_V", DMA_PRIO_HIGH, + pxa_camera_dma_irq_v, pcdev); + if (err < 0) { dev_err(pcdev->dev, "Can't request DMA for V\n"); - err = -ENOMEM; goto exit_free_dma_u; } + pcdev->dma_chans[2] = err; dev_dbg(pcdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]); DRCMR68 = pcdev->dma_chans[0] | DRCMR_MAPVLD; diff --git a/linux/drivers/media/video/s2255drv.c b/linux/drivers/media/video/s2255drv.c index 939edff48..5a872787e 100644 --- a/linux/drivers/media/video/s2255drv.c +++ b/linux/drivers/media/video/s2255drv.c @@ -59,6 +59,8 @@ +/* default JPEG quality */ +#define S2255_DEF_JPEG_QUAL 50 /* vendor request in */ #define S2255_VR_IN 0 /* vendor request out */ @@ -68,20 +70,21 @@ /* USB endpoint number for configuring the device */ #define S2255_CONFIG_EP 2 /* maximum time for DSP to start responding after last FW word loaded(ms) */ -#define S2255_DSP_BOOTTIME 400 +#define S2255_DSP_BOOTTIME 800 /* maximum time to wait for firmware to load (ms) */ #define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME) #define S2255_DEF_BUFS 16 +#define S2255_SETMODE_TIMEOUT 500 #define MAX_CHANNELS 4 -#define FRAME_MARKER 0x2255DA4AL -#define MAX_PIPE_USBBLOCK (40 * 1024) -#define DEFAULT_PIPE_USBBLOCK (16 * 1024) +#define S2255_MARKER_FRAME 0x2255DA4AL +#define S2255_MARKER_RESPONSE 0x2255ACACL +#define S2255_USB_XFER_SIZE (16 * 1024) #define MAX_CHANNELS 4 #define MAX_PIPE_BUFFERS 1 #define SYS_FRAMES 4 /* maximum size is PAL full size plus room for the marker header(s) */ -#define SYS_FRAMES_MAXSIZE (720 * 288 * 2 * 2 + 4096) -#define DEF_USB_BLOCK (4096) +#define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096) +#define DEF_USB_BLOCK S2255_USB_XFER_SIZE #define LINE_SZ_4CIFS_NTSC 640 #define LINE_SZ_2CIFS_NTSC 640 #define LINE_SZ_1CIFS_NTSC 320 @@ -109,6 +112,9 @@ #define COLOR_YUVPL 1 /* YUV planar */ #define COLOR_YUVPK 2 /* YUV packed */ #define COLOR_Y8 4 /* monochrome */ +#define COLOR_JPG 5 /* JPEG */ +#define MASK_COLOR 0xff +#define MASK_JPG_QUALITY 0xff00 /* frame decimation. Not implemented by V4L yet(experimental in V4L) */ #define FDEC_1 1 /* capture every frame. default */ @@ -149,16 +155,14 @@ struct s2255_mode { u32 restart; /* if DSP requires restart */ }; -/* frame structure */ -#define FRAME_STATE_UNUSED 0 -#define FRAME_STATE_FILLING 1 -#define FRAME_STATE_FULL 2 +#define S2255_READ_IDLE 0 +#define S2255_READ_FRAME 1 +/* frame structure */ struct s2255_framei { unsigned long size; - - unsigned long ulState; /* ulState ==0 unused, 1 being filled, 2 full */ + unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/ void *lpvbits; /* image data */ unsigned long cur_size; /* current data copied to it */ }; @@ -189,6 +193,10 @@ struct s2255_dmaqueue { #define S2255_FW_FAILED 3 #define S2255_FW_DISCONNECTING 4 +#define S2255_FW_MARKER 0x22552f2f +/* 2255 read states */ +#define S2255_READ_IDLE 0 +#define S2255_READ_FRAME 1 struct s2255_fw { int fw_loaded; int fw_size; @@ -196,7 +204,6 @@ struct s2255_fw { atomic_t fw_state; void *pfw_data; wait_queue_head_t wait_fw; - struct timer_list dsp_wait; const struct firmware *fw; }; @@ -238,15 +245,27 @@ struct s2255_dev { struct s2255_pipeinfo pipes[MAX_PIPE_BUFFERS]; struct s2255_bufferi buffer[MAX_CHANNELS]; struct s2255_mode mode[MAX_CHANNELS]; + /* jpeg compression */ + struct v4l2_jpegcompression jc[MAX_CHANNELS]; const struct s2255_fmt *cur_fmt[MAX_CHANNELS]; int cur_frame[MAX_CHANNELS]; int last_frame[MAX_CHANNELS]; u32 cc; /* current channel */ int b_acquire[MAX_CHANNELS]; + /* allocated image size */ unsigned long req_image_size[MAX_CHANNELS]; + /* received packet size */ + unsigned long pkt_size[MAX_CHANNELS]; int bad_payload[MAX_CHANNELS]; unsigned long frame_count[MAX_CHANNELS]; int frame_ready; + /* if JPEG image */ + int jpg_size[MAX_CHANNELS]; + /* if channel configured to default state */ + int chn_configured[MAX_CHANNELS]; + wait_queue_head_t wait_setmode[MAX_CHANNELS]; + int setmode_ready[MAX_CHANNELS]; + int chn_ready; struct kref kref; spinlock_t slock; }; @@ -307,12 +326,16 @@ static void s2255_stop_readpipe(struct s2255_dev *dev); static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn); static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn); static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, - int chn); + int chn, int jpgsize); static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, struct s2255_mode *mode); static int s2255_board_shutdown(struct s2255_dev *dev); static void s2255_exit_v4l(struct s2255_dev *dev); -static void s2255_fwload_start(struct s2255_dev *dev); +static void s2255_fwload_start(struct s2255_dev *dev, int reset); +static void s2255_destroy(struct kref *kref); +static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, + u16 index, u16 value, void *buf, + s32 buf_len, int bOut); #define dprintk(level, fmt, arg...) \ do { \ @@ -408,6 +431,10 @@ static const struct s2255_fmt formats[] = { .fourcc = V4L2_PIX_FMT_UYVY, .depth = 16 }, { + .name = "JPG", + .fourcc = V4L2_PIX_FMT_JPEG, + .depth = 24 + }, { .name = "8bpp GREY", .fourcc = V4L2_PIX_FMT_GREY, .depth = 8 @@ -465,6 +492,13 @@ static void planar422p_to_yuv_packed(const unsigned char *in, return; } +static void s2255_reset_dsppower(struct s2255_dev *dev) +{ + s2255_vendor_req(dev, 0x40, 0x0b0b, 0x0b0b, NULL, 0, 1); + msleep(10); + s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1); + return; +} /* kickstarts the firmware loading. from probe */ @@ -481,18 +515,6 @@ static void s2255_timer(unsigned long user_data) } } -/* called when DSP is up and running. DSP is guaranteed to - be running after S2255_DSP_BOOTTIME */ -static void s2255_dsp_running(unsigned long user_data) -{ - struct s2255_fw *data = (struct s2255_fw *)user_data; - dprintk(1, "dsp running\n"); - atomic_set(&data->fw_state, S2255_FW_SUCCESS); - wake_up(&data->wait_fw); - printk(KERN_INFO "s2255: firmware loaded successfully\n"); - return; -} - /* this loads the firmware asynchronously. Originally this was done synchroously in probe. @@ -550,19 +572,14 @@ static void s2255_fwchunk_complete(struct urb *urb) } data->fw_loaded += len; } else { - init_timer(&data->dsp_wait); - data->dsp_wait.function = s2255_dsp_running; - data->dsp_wait.data = (unsigned long)data; atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); - mod_timer(&data->dsp_wait, msecs_to_jiffies(S2255_DSP_BOOTTIME) - + jiffies); } dprintk(100, "2255 complete done\n"); return; } -static int s2255_got_frame(struct s2255_dev *dev, int chn) +static int s2255_got_frame(struct s2255_dev *dev, int chn, int jpgsize) { struct s2255_dmaqueue *dma_q = &dev->vidq[chn]; struct s2255_buffer *buf; @@ -587,8 +604,7 @@ static int s2255_got_frame(struct s2255_dev *dev, int chn) list_del(&buf->vb.queue); do_gettimeofday(&buf->vb.ts); dprintk(100, "[%p/%d] wakeup\n", buf, buf->vb.i); - - s2255_fillbuff(dev, buf, dma_q->channel); + s2255_fillbuff(dev, buf, dma_q->channel, jpgsize); wake_up(&buf->vb.done); dprintk(2, "wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); unlock: @@ -622,7 +638,7 @@ static const struct s2255_fmt *format_by_fourcc(int fourcc) * */ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, - int chn) + int chn, int jpgsize) { int pos = 0; struct timeval ts; @@ -650,6 +666,10 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, case V4L2_PIX_FMT_GREY: memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height); break; + case V4L2_PIX_FMT_JPEG: + buf->vb.size = jpgsize; + memcpy(vbuf, tmpbuf, buf->vb.size); + break; case V4L2_PIX_FMT_YUV422P: memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height * 2); @@ -658,9 +678,6 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, printk(KERN_DEBUG "s2255: unknown format?\n"); } dev->last_frame[chn] = -1; - /* done with the frame, free it */ - frm->ulState = 0; - dprintk(4, "freeing buffer\n"); } else { printk(KERN_ERR "s2255: =======no frame\n"); return; @@ -670,7 +687,7 @@ static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, (unsigned long)vbuf, pos); /* tell v4l buffer was filled */ - buf->vb.field_count++; + buf->vb.field_count = dev->frame_count[chn] * 2; do_gettimeofday(&ts); buf->vb.ts = ts; buf->vb.state = VIDEOBUF_DONE; @@ -1022,6 +1039,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, case V4L2_PIX_FMT_GREY: fh->mode.color = COLOR_Y8; break; + case V4L2_PIX_FMT_JPEG: + fh->mode.color = COLOR_JPG | + (fh->dev->jc[fh->channel].quality << 8); + break; case V4L2_PIX_FMT_YUV422P: fh->mode.color = COLOR_YUVPL; break; @@ -1140,7 +1161,7 @@ static u32 get_transfer_size(struct s2255_mode *mode) } } outImageSize = linesPerFrame * pixelsPerLine; - if (mode->color != COLOR_Y8) { + if ((mode->color & MASK_COLOR) != COLOR_Y8) { /* 2 bytes/pixel if not monochrome */ outImageSize *= 2; } @@ -1186,12 +1207,17 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, u32 *buffer; unsigned long chn_rev; + mutex_lock(&dev->lock); chn_rev = G_chnmap[chn]; dprintk(3, "mode scale [%ld] %p %d\n", chn, mode, mode->scale); dprintk(3, "mode scale [%ld] %p %d\n", chn, &dev->mode[chn], dev->mode[chn].scale); dprintk(2, "mode contrast %x\n", mode->contrast); + /* if JPEG, set the quality */ + if ((mode->color & MASK_COLOR) == COLOR_JPG) + mode->color = (dev->jc[chn].quality << 8) | COLOR_JPG; + /* save the mode */ dev->mode[chn] = *mode; dev->req_image_size[chn] = get_transfer_size(mode); @@ -1200,6 +1226,7 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, buffer = kzalloc(512, GFP_KERNEL); if (buffer == NULL) { dev_err(&dev->udev->dev, "out of mem\n"); + mutex_unlock(&dev->lock); return -ENOMEM; } @@ -1215,12 +1242,20 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, dprintk(1, "set mode done chn %lu, %d\n", chn, res); /* wait at least 3 frames before continuing */ - if (mode->restart) - msleep(125); + if (mode->restart) { + dev->setmode_ready[chn] = 0; + wait_event_timeout(dev->wait_setmode[chn], + (dev->setmode_ready[chn] != 0), + msecs_to_jiffies(S2255_SETMODE_TIMEOUT)); + if (dev->setmode_ready[chn] != 1) { + printk(KERN_DEBUG "s2255: no set mode response\n"); + res = -EFAULT; + } + } /* clear the restart flag */ dev->mode[chn].restart = 0; - + mutex_unlock(&dev->lock); return res; } @@ -1269,8 +1304,9 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) dev->last_frame[chn] = -1; dev->bad_payload[chn] = 0; dev->cur_frame[chn] = 0; + dev->frame_count[chn] = 0; for (j = 0; j < SYS_FRAMES; j++) { - dev->buffer[chn].frame[j].ulState = 0; + dev->buffer[chn].frame[j].ulState = S2255_READ_IDLE; dev->buffer[chn].frame[j].cur_size = 0; } res = videobuf_streamon(&fh->vb_vidq); @@ -1446,6 +1482,27 @@ static int vidioc_s_ctrl(struct file *file, void *priv, return -EINVAL; } +static int vidioc_g_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jc) +{ + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + *jc = dev->jc[fh->channel]; + dprintk(2, "getting jpegcompression, quality %d\n", jc->quality); + return 0; +} + +static int vidioc_s_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jc) +{ + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + if (jc->quality < 0 || jc->quality > 100) + return -EINVAL; + dev->jc[fh->channel].quality = jc->quality; + dprintk(2, "setting jpeg quality %d\n", jc->quality); + return 0; +} static int s2255_open(struct inode *inode, struct file *file) { int minor = iminor(inode); @@ -1455,6 +1512,7 @@ static int s2255_open(struct inode *inode, struct file *file) enum v4l2_buf_type type = 0; int i = 0; int cur_channel = -1; + int state; dprintk(1, "s2255: open called (minor=%d)\n", minor); lock_kernel(); @@ -1471,47 +1529,77 @@ static int s2255_open(struct inode *inode, struct file *file) if ((NULL == dev) || (cur_channel == -1)) { unlock_kernel(); - dprintk(1, "s2255: openv4l no dev\n"); + printk(KERN_INFO "s2255: openv4l no dev\n"); return -ENODEV; } + if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_DISCONNECTING) { + unlock_kernel(); + printk(KERN_INFO "disconnecting\n"); + return -ENODEV; + } + kref_get(&dev->kref); mutex_lock(&dev->open_lock); dev->users[cur_channel]++; dprintk(4, "s2255: open_handles %d\n", dev->users[cur_channel]); - if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_FAILED) { + switch (atomic_read(&dev->fw_data->fw_state)) { + case S2255_FW_FAILED: err("2255 firmware load failed. retrying.\n"); - s2255_fwload_start(dev); + s2255_fwload_start(dev, 1); wait_event_timeout(dev->fw_data->wait_fw, - (atomic_read(&dev->fw_data->fw_state) - != S2255_FW_NOTLOADED), + ((atomic_read(&dev->fw_data->fw_state) + == S2255_FW_SUCCESS) || + (atomic_read(&dev->fw_data->fw_state) + == S2255_FW_DISCONNECTING)), msecs_to_jiffies(S2255_LOAD_TIMEOUT)); - if (atomic_read(&dev->fw_data->fw_state) - != S2255_FW_SUCCESS) { - printk(KERN_INFO "2255 FW load failed.\n"); - dev->users[cur_channel]--; - mutex_unlock(&dev->open_lock); - unlock_kernel(); - return -EFAULT; - } - } else if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_NOTLOADED) { + break; + case S2255_FW_NOTLOADED: + case S2255_FW_LOADED_DSPWAIT: /* give S2255_LOAD_TIMEOUT time for firmware to load in case driver loaded and then device immediately opened */ printk(KERN_INFO "%s waiting for firmware load\n", __func__); wait_event_timeout(dev->fw_data->wait_fw, - (atomic_read(&dev->fw_data->fw_state) - != S2255_FW_NOTLOADED), - msecs_to_jiffies(S2255_LOAD_TIMEOUT)); - if (atomic_read(&dev->fw_data->fw_state) - != S2255_FW_SUCCESS) { - printk(KERN_INFO "2255 firmware not loaded" - "try again\n"); - dev->users[cur_channel]--; - mutex_unlock(&dev->open_lock); - unlock_kernel(); - return -EBUSY; + ((atomic_read(&dev->fw_data->fw_state) + == S2255_FW_SUCCESS) || + (atomic_read(&dev->fw_data->fw_state) + == S2255_FW_DISCONNECTING)), + msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + break; + case S2255_FW_SUCCESS: + default: + break; + } + state = atomic_read(&dev->fw_data->fw_state); + if (state != S2255_FW_SUCCESS) { + int rc; + switch (state) { + case S2255_FW_FAILED: + printk(KERN_INFO "2255 FW load failed. %d\n", state); + rc = -ENODEV; + break; + case S2255_FW_DISCONNECTING: + printk(KERN_INFO "%s: disconnecting\n", __func__); + rc = -ENODEV; + break; + case S2255_FW_LOADED_DSPWAIT: + case S2255_FW_NOTLOADED: + printk(KERN_INFO "%s: firmware not loaded yet" + "please try again later\n", + __func__); + rc = -EAGAIN; + break; + default: + printk(KERN_INFO "%s: unknown state\n", __func__); + rc = -EFAULT; + break; } + dev->users[cur_channel]--; + mutex_unlock(&dev->open_lock); + kref_put(&dev->kref, s2255_destroy); + unlock_kernel(); + return rc; } /* allocate + initialize per filehandle data */ @@ -1519,6 +1607,7 @@ static int s2255_open(struct inode *inode, struct file *file) if (NULL == fh) { dev->users[cur_channel]--; mutex_unlock(&dev->open_lock); + kref_put(&dev->kref, s2255_destroy); unlock_kernel(); return -ENOMEM; } @@ -1533,6 +1622,13 @@ static int s2255_open(struct inode *inode, struct file *file) fh->height = NUM_LINES_4CIFS_NTSC * 2; fh->channel = cur_channel; + /* configure channel to default state */ + if (!dev->chn_configured[cur_channel]) { + s2255_set_mode(dev, cur_channel, &fh->mode); + dev->chn_configured[cur_channel] = 1; + } + + /* Put all controls at a sane state */ for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) qctl_regs[i] = s2255_qctrl[i].default_value; @@ -1551,7 +1647,6 @@ static int s2255_open(struct inode *inode, struct file *file) V4L2_FIELD_INTERLACED, sizeof(struct s2255_buffer), fh); - kref_get(&dev->kref); mutex_unlock(&dev->open_lock); unlock_kernel(); return 0; @@ -1575,30 +1670,24 @@ static unsigned int s2255_poll(struct file *file, static void s2255_destroy(struct kref *kref) { struct s2255_dev *dev = to_s2255_dev(kref); + struct list_head *list; + int i; if (!dev) { printk(KERN_ERR "s2255drv: kref problem\n"); return; } - - /* - * Wake up any firmware load waiting (only done in .open, - * which holds the open_lock mutex) - */ atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); wake_up(&dev->fw_data->wait_fw); - - /* prevent s2255_disconnect from racing s2255_open */ + for (i = 0; i < MAX_CHANNELS; i++) { + dev->setmode_ready[i] = 1; + wake_up(&dev->wait_setmode[i]); + } mutex_lock(&dev->open_lock); + /* reset the DSP so firmware can be reload next time */ + s2255_reset_dsppower(dev); s2255_exit_v4l(dev); - /* - * device unregistered so no longer possible to open. open_mutex - * can be unlocked and timers deleted afterwards. - */ - mutex_unlock(&dev->open_lock); - /* board shutdown stops the read pipe if it is running */ s2255_board_shutdown(dev); - /* make sure firmware still not trying to load */ del_timer(&dev->timer); /* only started in .probe and .open */ @@ -1608,23 +1697,19 @@ static void s2255_destroy(struct kref *kref) usb_free_urb(dev->fw_data->fw_urb); dev->fw_data->fw_urb = NULL; } - - /* - * delete the dsp_wait timer, which sets the firmware - * state on completion. This is done before fw_data - * is freed below. - */ - - del_timer(&dev->fw_data->dsp_wait); /* only started in .open */ - if (dev->fw_data->fw) release_firmware(dev->fw_data->fw); kfree(dev->fw_data->pfw_data); kfree(dev->fw_data); - usb_put_dev(dev->udev); dprintk(1, "%s", __func__); kfree(dev); + + while (!list_empty(&s2255_devlist)) { + list = s2255_devlist.next; + list_del(list); + } + mutex_unlock(&dev->open_lock); } static int s2255_close(struct inode *inode, struct file *file) @@ -1708,6 +1793,8 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = { #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidioc_cgmbuf, #endif + .vidioc_s_jpegcomp = vidioc_s_jpegcomp, + .vidioc_g_jpegcomp = vidioc_g_jpegcomp, }; static struct video_device template = { @@ -1746,7 +1833,7 @@ static int s2255_probe_v4l(struct s2255_dev *dev) ret = video_register_device(dev->vdev[i], VFL_TYPE_GRABBER, cur_nr + i); - dev->vdev[i]->priv = dev; + video_set_drvdata(dev->vdev[i], dev); if (ret != 0) { dev_err(&dev->udev->dev, @@ -1760,18 +1847,16 @@ static int s2255_probe_v4l(struct s2255_dev *dev) static void s2255_exit_v4l(struct s2255_dev *dev) { - struct list_head *list; + int i; - /* unregister the video devices */ - while (!list_empty(&s2255_devlist)) { - list = s2255_devlist.next; - list_del(list); - } for (i = 0; i < MAX_CHANNELS; i++) { - if (-1 != dev->vdev[i]->minor) + if (-1 != dev->vdev[i]->minor) { video_unregister_device(dev->vdev[i]); - else + printk(KERN_INFO "s2255 unregistered\n"); + } else { video_device_release(dev->vdev[i]); + printk(KERN_INFO "s2255 released\n"); + } } } @@ -1781,134 +1866,123 @@ static void s2255_exit_v4l(struct s2255_dev *dev) * function again). * * Received frame structure: - * bytes 0-3: marker : 0x2255DA4AL (FRAME_MARKER) + * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME) * bytes 4-7: channel: 0-3 * bytes 8-11: payload size: size of the frame * bytes 12-payloadsize+12: frame data */ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) { - static int dbgsync; /* = 0; */ char *pdest; u32 offset = 0; - int bsync = 0; - int btrunc = 0; + int bframe = 0; char *psrc; unsigned long copy_size; unsigned long size; s32 idx = -1; struct s2255_framei *frm; unsigned char *pdata; - unsigned long cur_size; - int bsearch = 0; - struct s2255_bufferi *buf; + dprintk(100, "buffer to user\n"); idx = dev->cur_frame[dev->cc]; - buf = &dev->buffer[dev->cc]; - frm = &buf->frame[idx]; - - if (frm->ulState == 0) { - frm->ulState = 1; - frm->cur_size = 0; - bsearch = 1; - } else if (frm->ulState == 2) { - /* system frame was not freed */ - dprintk(2, "sys frame not free. overrun ringbuf\n"); - bsearch = 1; - frm->ulState = 1; - frm->cur_size = 0; - } - - if (bsearch) { - if (*(s32 *) pipe_info->transfer_buffer != FRAME_MARKER) { - u32 jj; - if (dbgsync == 0) { - dprintk(3, "not synched, discarding all packets" - "until marker\n"); + frm = &dev->buffer[dev->cc].frame[idx]; - dbgsync++; - } - pdata = (unsigned char *)pipe_info->transfer_buffer; - for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); - jj++) { - if (*(s32 *) pdata == FRAME_MARKER) { - int cc; - dprintk(3, - "found frame marker at offset:" - " %d [%x %x]\n", jj, pdata[0], - pdata[1]); - offset = jj; - bsync = 1; - cc = *(u32 *) (pdata + sizeof(u32)); - if (cc >= MAX_CHANNELS) { - printk(KERN_ERR - "bad channel\n"); - return -EINVAL; - } - /* reverse it */ - dev->cc = G_chnmap[cc]; + if (frm->ulState == S2255_READ_IDLE) { + int jj; + unsigned int cc; + s32 *pdword; + int payload; + /* search for marker codes */ + pdata = (unsigned char *)pipe_info->transfer_buffer; + for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) { + switch (*(s32 *) pdata) { + case S2255_MARKER_FRAME: + pdword = (s32 *)pdata; + dprintk(4, "found frame marker at offset:" + " %d [%x %x]\n", jj, pdata[0], + pdata[1]); + offset = jj + PREFIX_SIZE; + bframe = 1; + cc = pdword[1]; + if (cc >= MAX_CHANNELS) { + printk(KERN_ERR + "bad channel\n"); + return -EINVAL; + } + /* reverse it */ + dev->cc = G_chnmap[cc]; + payload = pdword[3]; + if (payload > dev->req_image_size[dev->cc]) { + dev->bad_payload[dev->cc]++; + /* discard the bad frame */ + return -EINVAL; + } + dev->pkt_size[dev->cc] = payload; + dev->jpg_size[dev->cc] = pdword[4]; + break; + case S2255_MARKER_RESPONSE: + pdword = (s32 *)pdata; + pdata += DEF_USB_BLOCK; + jj += DEF_USB_BLOCK; + if (pdword[1] >= MAX_CHANNELS) + break; + cc = G_chnmap[pdword[1]]; + if (!(cc >= 0 && cc < MAX_CHANNELS)) + break; + switch (pdword[2]) { + case 0x01: + /* check if channel valid */ + /* set mode ready */ + dev->setmode_ready[cc] = 1; + wake_up(&dev->wait_setmode[cc]); + dprintk(5, "setmode ready %d\n", cc); break; + case 0x10: + + dev->chn_ready |= (1 << cc); + if ((dev->chn_ready & 0x0f) != 0x0f) + break; + /* all channels ready */ + printk(KERN_INFO "s2255: fw loaded\n"); + atomic_set(&dev->fw_data->fw_state, + S2255_FW_SUCCESS); + wake_up(&dev->fw_data->wait_fw); + break; + default: + printk(KERN_INFO "s2255 unknwn resp\n"); } + default: pdata++; + break; } - if (bsync == 0) - return -EINVAL; - } else { - u32 *pword; - u32 payload; - int cc; - dbgsync = 0; - bsync = 1; - pword = (u32 *) pipe_info->transfer_buffer; - cc = pword[1]; - - if (cc >= MAX_CHANNELS) { - printk("invalid channel found. " - "throwing out data!\n"); - return -EINVAL; - } - dev->cc = G_chnmap[cc]; - payload = pword[2]; - if (payload != dev->req_image_size[dev->cc]) { - dprintk(1, "[%d][%d]unexpected payload: %d" - "required: %lu \n", cc, dev->cc, - payload, dev->req_image_size[dev->cc]); - dev->bad_payload[dev->cc]++; - /* discard the bad frame */ - return -EINVAL; - } - - } - } - /* search done. now find out if should be acquiring - on this channel */ - if (!dev->b_acquire[dev->cc]) { - frm->ulState = 0; - return -EINVAL; + if (bframe) + break; + } /* for */ + if (!bframe) + return -EINVAL; } + idx = dev->cur_frame[dev->cc]; frm = &dev->buffer[dev->cc].frame[idx]; - if (frm->ulState == 0) { - frm->ulState = 1; - frm->cur_size = 0; - } else if (frm->ulState == 2) { - /* system frame ring buffer overrun */ - dprintk(2, "sys frame overrun. overwriting frame %d %d\n", - dev->cc, idx); - frm->ulState = 1; - frm->cur_size = 0; + /* search done. now find out if should be acquiring on this channel */ + if (!dev->b_acquire[dev->cc]) { + /* we found a frame, but this channel is turned off */ + frm->ulState = S2255_READ_IDLE; + return -EINVAL; } - if (bsync) { - /* skip the marker 512 bytes (and offset if out of sync) */ - psrc = (u8 *)pipe_info->transfer_buffer + offset + PREFIX_SIZE; - } else { - psrc = (u8 *)pipe_info->transfer_buffer; + if (frm->ulState == S2255_READ_IDLE) { + frm->ulState = S2255_READ_FRAME; + frm->cur_size = 0; } + /* skip the marker 512 bytes (and offset if out of sync) */ + psrc = (u8 *)pipe_info->transfer_buffer + offset; + + if (frm->lpvbits == NULL) { dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d", frm, dev, dev->cc, idx); @@ -1917,33 +1991,20 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) pdest = frm->lpvbits + frm->cur_size; - if (bsync) { - copy_size = - (pipe_info->cur_transfer_size - offset) - PREFIX_SIZE; - if (copy_size > pipe_info->cur_transfer_size) { - printk("invalid copy size, overflow!\n"); - return -ENOMEM; - } - } else { - copy_size = pipe_info->cur_transfer_size; - } + copy_size = (pipe_info->cur_transfer_size - offset); - cur_size = frm->cur_size; - size = dev->req_image_size[dev->cc]; + size = dev->pkt_size[dev->cc] - PREFIX_SIZE; - if ((copy_size + cur_size) > size) { - copy_size = size - cur_size; - btrunc = 1; - } + /* sanity check on pdest */ + if ((copy_size + frm->cur_size) < dev->req_image_size[dev->cc]) + memcpy(pdest, psrc, copy_size); - memcpy(pdest, psrc, copy_size); - cur_size += copy_size; frm->cur_size += copy_size; - dprintk(50, "cur_size size %lu size %lu \n", cur_size, size); + dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size); + + if (frm->cur_size >= size) { - if (cur_size >= (size - PREFIX_SIZE)) { u32 cc = dev->cc; - frm->ulState = 2; dprintk(2, "****************[%d]Buffer[%d]full*************\n", cc, idx); dev->last_frame[cc] = dev->cur_frame[cc]; @@ -1952,16 +2013,13 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) if ((dev->cur_frame[cc] == SYS_FRAMES) || (dev->cur_frame[cc] == dev->buffer[cc].dwFrames)) dev->cur_frame[cc] = 0; - - /* signal the semaphore for this channel */ + /* frame ready */ if (dev->b_acquire[cc]) - s2255_got_frame(dev, cc); + s2255_got_frame(dev, cc, dev->jpg_size[cc]); dev->frame_count[cc]++; - } - /* frame was truncated */ - if (btrunc) { - /* return more data to process */ - return EAGAIN; + frm->ulState = S2255_READ_IDLE; + frm->cur_size = 0; + } /* done successfully */ return 0; @@ -1980,8 +2038,8 @@ static void s2255_read_video_callback(struct s2255_dev *dev, } /* otherwise copy to the system buffers */ res = save_frame(dev, pipe_info); - if (res == EAGAIN) - save_frame(dev, pipe_info); + if (res != 0) + dprintk(4, "s2255: read callback failed\n"); dprintk(50, "callback read video done\n"); return; @@ -2101,11 +2159,9 @@ static int s2255_board_init(struct s2255_dev *dev) memset(pipe, 0, sizeof(*pipe)); pipe->dev = dev; - pipe->cur_transfer_size = DEFAULT_PIPE_USBBLOCK; - pipe->max_transfer_size = MAX_PIPE_USBBLOCK; + pipe->cur_transfer_size = S2255_USB_XFER_SIZE; + pipe->max_transfer_size = S2255_USB_XFER_SIZE; - if (pipe->cur_transfer_size > pipe->max_transfer_size) - pipe->cur_transfer_size = pipe->max_transfer_size; pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, GFP_KERNEL); if (pipe->transfer_buffer == NULL) { @@ -2125,6 +2181,7 @@ static int s2255_board_init(struct s2255_dev *dev) for (j = 0; j < MAX_CHANNELS; j++) { dev->b_acquire[j] = 0; dev->mode[j] = mode_def; + dev->jc[j].quality = S2255_DEF_JPEG_QUAL; dev->cur_fmt[j] = &formats[0]; dev->mode[j].restart = 1; dev->req_image_size[j] = get_transfer_size(&mode_def); @@ -2329,7 +2386,7 @@ static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn) kfree(buffer); dev->b_acquire[chn] = 0; - return 0; + return res; } static void s2255_stop_readpipe(struct s2255_dev *dev) @@ -2365,8 +2422,10 @@ static void s2255_stop_readpipe(struct s2255_dev *dev) return; } -static void s2255_fwload_start(struct s2255_dev *dev) +static void s2255_fwload_start(struct s2255_dev *dev, int reset) { + if (reset) + s2255_reset_dsppower(dev); dev->fw_data->fw_size = dev->fw_data->fw->size; atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED); memcpy(dev->fw_data->pfw_data, @@ -2389,6 +2448,8 @@ static int s2255_probe(struct usb_interface *interface, struct usb_endpoint_descriptor *endpoint; int i; int retval = -ENOMEM; + __le32 *pdata; + int fw_size; dprintk(2, "s2255: probe\n"); @@ -2443,6 +2504,8 @@ static int s2255_probe(struct usb_interface *interface, dev->timer.data = (unsigned long)dev->fw_data; init_waitqueue_head(&dev->fw_data->wait_fw); + for (i = 0; i < MAX_CHANNELS; i++) + init_waitqueue_head(&dev->wait_setmode[i]); dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); @@ -2462,16 +2525,30 @@ static int s2255_probe(struct usb_interface *interface, printk(KERN_ERR "sensoray 2255 failed to get firmware\n"); goto error; } + /* check the firmware is valid */ + fw_size = dev->fw_data->fw->size; + pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; + if (*pdata != S2255_FW_MARKER) { + printk(KERN_INFO "Firmware invalid.\n"); + retval = -ENODEV; + goto error; + } else { + /* make sure firmware is the latest */ + __le32 *pRel; + pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; + printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel); + } /* loads v4l specific */ s2255_probe_v4l(dev); + usb_reset_device(dev->udev); /* load 2255 board specific */ s2255_board_init(dev); dprintk(4, "before probe done %p\n", dev); spin_lock_init(&dev->slock); - s2255_fwload_start(dev); + s2255_fwload_start(dev, 0); dev_info(&interface->dev, "Sensoray 2255 detected\n"); return 0; error: @@ -2482,14 +2559,30 @@ error: static void s2255_disconnect(struct usb_interface *interface) { struct s2255_dev *dev = NULL; + int i; dprintk(1, "s2255: disconnect interface %p\n", interface); dev = usb_get_intfdata(interface); + + /* + * wake up any of the timers to allow open_lock to be + * acquired sooner + */ + atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); + wake_up(&dev->fw_data->wait_fw); + for (i = 0; i < MAX_CHANNELS; i++) { + dev->setmode_ready[i] = 1; + wake_up(&dev->wait_setmode[i]); + } + + mutex_lock(&dev->open_lock); + usb_set_intfdata(interface, NULL); + mutex_unlock(&dev->open_lock); + if (dev) { kref_put(&dev->kref, s2255_destroy); dprintk(1, "s2255drv: disconnect\n"); dev_info(&interface->dev, "s2255usb now disconnected\n"); } - usb_set_intfdata(interface, NULL); } static struct usb_driver s2255_driver = { diff --git a/linux/drivers/media/video/saa5246a.c b/linux/drivers/media/video/saa5246a.c index 1bfc49822..355490467 100644 --- a/linux/drivers/media/video/saa5246a.c +++ b/linux/drivers/media/video/saa5246a.c @@ -43,137 +43,364 @@ #include <linux/mm.h> #include <linux/init.h> #include <linux/i2c.h> -#include <linux/videotext.h> #include <linux/smp_lock.h> -#include "compat.h" +#include <linux/mutex.h> +#include <linux/videotext.h> #include <linux/videodev.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#include <linux/mutex.h> - -#include "saa5246a.h" +#include <media/v4l2-i2c-drv-legacy.h> +#include "compat.h" MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver"); MODULE_LICENSE("GPL"); -struct saa5246a_device -{ - u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; - int is_searching[NUM_DAUS]; - struct i2c_client *client; - struct mutex lock; -}; +#define MAJOR_VERSION 1 /* driver major version number */ +#define MINOR_VERSION 8 /* driver minor version number */ + +/* Number of DAUs = number of pages that can be searched at the same time. */ +#define NUM_DAUS 4 + +#define NUM_ROWS_PER_PAGE 40 + +/* first column is 0 (not 1) */ +#define POS_TIME_START 32 +#define POS_TIME_END 39 + +#define POS_HEADER_START 7 +#define POS_HEADER_END 31 + +/* Returns 'true' if the part of the videotext page described with req contains + (at least parts of) the time field */ +#define REQ_CONTAINS_TIME(p_req) \ + ((p_req)->start <= POS_TIME_END && \ + (p_req)->end >= POS_TIME_START) + +/* Returns 'true' if the part of the videotext page described with req contains + (at least parts of) the page header */ +#define REQ_CONTAINS_HEADER(p_req) \ + ((p_req)->start <= POS_HEADER_END && \ + (p_req)->end >= POS_HEADER_START) + +/*****************************************************************************/ +/* Mode register numbers of the SAA5246A */ +/*****************************************************************************/ +#define SAA5246A_REGISTER_R0 0 +#define SAA5246A_REGISTER_R1 1 +#define SAA5246A_REGISTER_R2 2 +#define SAA5246A_REGISTER_R3 3 +#define SAA5246A_REGISTER_R4 4 +#define SAA5246A_REGISTER_R5 5 +#define SAA5246A_REGISTER_R6 6 +#define SAA5246A_REGISTER_R7 7 +#define SAA5246A_REGISTER_R8 8 +#define SAA5246A_REGISTER_R9 9 +#define SAA5246A_REGISTER_R10 10 +#define SAA5246A_REGISTER_R11 11 +#define SAA5246A_REGISTER_R11B 11 + +/* SAA5246A mode registers often autoincrement to the next register. + Therefore we use variable argument lists. The following macro indicates + the end of a command list. */ +#define COMMAND_END (-1) + +/*****************************************************************************/ +/* Contents of the mode registers of the SAA5246A */ +/*****************************************************************************/ +/* Register R0 (Advanced Control) */ +#define R0_SELECT_R11 0x00 +#define R0_SELECT_R11B 0x01 + +#define R0_PLL_TIME_CONSTANT_LONG 0x00 +#define R0_PLL_TIME_CONSTANT_SHORT 0x02 + +#define R0_ENABLE_nODD_EVEN_OUTPUT 0x00 +#define R0_DISABLE_nODD_EVEN_OUTPUT 0x04 + +#define R0_ENABLE_HDR_POLL 0x00 +#define R0_DISABLE_HDR_POLL 0x10 + +#define R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x00 +#define R0_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x20 + +#define R0_NO_FREE_RUN_PLL 0x00 +#define R0_FREE_RUN_PLL 0x40 + +#define R0_NO_AUTOMATIC_FASTEXT_PROMPT 0x00 +#define R0_AUTOMATIC_FASTEXT_PROMPT 0x80 + +/* Register R1 (Mode) */ +#define R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES 0x00 +#define R1_NON_INTERLACED_312_313_LINES 0x01 +#define R1_NON_INTERLACED_312_312_LINES 0x02 +#define R1_FFB_LEADING_EDGE_IN_FIRST_BROAD_PULSE 0x03 +#define R1_FFB_LEADING_EDGE_IN_SECOND_BROAD_PULSE 0x07 + +#define R1_DEW 0x00 +#define R1_FULL_FIELD 0x08 + +#define R1_EXTENDED_PACKET_DISABLE 0x00 +#define R1_EXTENDED_PACKET_ENABLE 0x10 + +#define R1_DAUS_ALL_ON 0x00 +#define R1_DAUS_ALL_OFF 0x20 + +#define R1_7_BITS_PLUS_PARITY 0x00 +#define R1_8_BITS_NO_PARITY 0x40 + +#define R1_VCS_TO_SCS 0x00 +#define R1_NO_VCS_TO_SCS 0x80 + +/* Register R2 (Page request address) */ +#define R2_IN_R3_SELECT_PAGE_HUNDREDS 0x00 +#define R2_IN_R3_SELECT_PAGE_TENS 0x01 +#define R2_IN_R3_SELECT_PAGE_UNITS 0x02 +#define R2_IN_R3_SELECT_HOURS_TENS 0x03 +#define R2_IN_R3_SELECT_HOURS_UNITS 0x04 +#define R2_IN_R3_SELECT_MINUTES_TENS 0x05 +#define R2_IN_R3_SELECT_MINUTES_UNITS 0x06 + +#define R2_DAU_0 0x00 +#define R2_DAU_1 0x10 +#define R2_DAU_2 0x20 +#define R2_DAU_3 0x30 + +#define R2_BANK_0 0x00 +#define R2_BANK 1 0x40 + +#define R2_HAMMING_CHECK_ON 0x80 +#define R2_HAMMING_CHECK_OFF 0x00 + +/* Register R3 (Page request data) */ +#define R3_PAGE_HUNDREDS_0 0x00 +#define R3_PAGE_HUNDREDS_1 0x01 +#define R3_PAGE_HUNDREDS_2 0x02 +#define R3_PAGE_HUNDREDS_3 0x03 +#define R3_PAGE_HUNDREDS_4 0x04 +#define R3_PAGE_HUNDREDS_5 0x05 +#define R3_PAGE_HUNDREDS_6 0x06 +#define R3_PAGE_HUNDREDS_7 0x07 -static struct video_device saa_template; /* Declared near bottom */ +#define R3_HOLD_PAGE 0x00 +#define R3_UPDATE_PAGE 0x08 -/* Addresses to scan */ -static unsigned short normal_i2c[] = { I2C_ADDRESS, I2C_CLIENT_END }; +#define R3_PAGE_HUNDREDS_DO_NOT_CARE 0x00 +#define R3_PAGE_HUNDREDS_DO_CARE 0x10 -I2C_CLIENT_INSMOD; +#define R3_PAGE_TENS_DO_NOT_CARE 0x00 +#define R3_PAGE_TENS_DO_CARE 0x10 -static struct i2c_client client_template; +#define R3_PAGE_UNITS_DO_NOT_CARE 0x00 +#define R3_PAGE_UNITS_DO_CARE 0x10 -static int saa5246a_attach(struct i2c_adapter *adap, int addr, int kind) -{ - int pgbuf; - int err; - struct i2c_client *client; - struct video_device *vd; - struct saa5246a_device *t; +#define R3_HOURS_TENS_DO_NOT_CARE 0x00 +#define R3_HOURS_TENS_DO_CARE 0x10 - printk(KERN_INFO "saa5246a: teletext chip found.\n"); - client=kmalloc(sizeof(*client), GFP_KERNEL); - if(client==NULL) - return -ENOMEM; - client_template.adapter = adap; - client_template.addr = addr; - memcpy(client, &client_template, sizeof(*client)); - t = kzalloc(sizeof(*t), GFP_KERNEL); - if(t==NULL) - { - kfree(client); - return -ENOMEM; - } - strlcpy(client->name, IF_NAME, I2C_NAME_SIZE); - mutex_init(&t->lock); +#define R3_HOURS_UNITS_DO_NOT_CARE 0x00 +#define R3_HOURS_UNITS_DO_CARE 0x10 - /* - * Now create a video4linux device - */ +#define R3_MINUTES_TENS_DO_NOT_CARE 0x00 +#define R3_MINUTES_TENS_DO_CARE 0x10 - vd = video_device_alloc(); - if(vd==NULL) - { - kfree(t); - kfree(client); - return -ENOMEM; - } - i2c_set_clientdata(client, vd); - memcpy(vd, &saa_template, sizeof(*vd)); +#define R3_MINUTES_UNITS_DO_NOT_CARE 0x00 +#define R3_MINUTES_UNITS_DO_CARE 0x10 - for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) - { - memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); - t->is_searching[pgbuf] = false; - } - vd->priv=t; +/* Register R4 (Display chapter) */ +#define R4_DISPLAY_PAGE_0 0x00 +#define R4_DISPLAY_PAGE_1 0x01 +#define R4_DISPLAY_PAGE_2 0x02 +#define R4_DISPLAY_PAGE_3 0x03 +#define R4_DISPLAY_PAGE_4 0x04 +#define R4_DISPLAY_PAGE_5 0x05 +#define R4_DISPLAY_PAGE_6 0x06 +#define R4_DISPLAY_PAGE_7 0x07 +/* Register R5 (Normal display control) */ +#define R5_PICTURE_INSIDE_BOXING_OFF 0x00 +#define R5_PICTURE_INSIDE_BOXING_ON 0x01 - /* - * Register it - */ +#define R5_PICTURE_OUTSIDE_BOXING_OFF 0x00 +#define R5_PICTURE_OUTSIDE_BOXING_ON 0x02 - if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0) - { - kfree(t); - kfree(client); - video_device_release(vd); - return err; - } - t->client = client; - i2c_attach_client(client); - return 0; -} +#define R5_TEXT_INSIDE_BOXING_OFF 0x00 +#define R5_TEXT_INSIDE_BOXING_ON 0x04 -/* - * We do most of the hard work when we become a device on the i2c. - */ -static int saa5246a_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, saa5246a_attach); - return 0; -} +#define R5_TEXT_OUTSIDE_BOXING_OFF 0x00 +#define R5_TEXT_OUTSIDE_BOXING_ON 0x08 -static int saa5246a_detach(struct i2c_client *client) -{ - struct video_device *vd = i2c_get_clientdata(client); - i2c_detach_client(client); - video_unregister_device(vd); - kfree(vd->priv); - kfree(client); - return 0; -} +#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 +#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 -/* - * I2C interfaces - */ +#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 +#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 + +#define R5_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 +#define R5_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 + +#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 +#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 + +/* Register R6 (Newsflash display) */ +#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_ON 0x01 + +#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_ON 0x02 + +#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_ON 0x04 + +#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_ON 0x08 + +#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 + +#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 + +#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 + +#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 +#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 + +/* Register R7 (Display mode) */ +#define R7_BOX_OFF_ROW_0 0x00 +#define R7_BOX_ON_ROW_0 0x01 + +#define R7_BOX_OFF_ROW_1_TO_23 0x00 +#define R7_BOX_ON_ROW_1_TO_23 0x02 + +#define R7_BOX_OFF_ROW_24 0x00 +#define R7_BOX_ON_ROW_24 0x04 + +#define R7_SINGLE_HEIGHT 0x00 +#define R7_DOUBLE_HEIGHT 0x08 + +#define R7_TOP_HALF 0x00 +#define R7_BOTTOM_HALF 0x10 + +#define R7_REVEAL_OFF 0x00 +#define R7_REVEAL_ON 0x20 + +#define R7_CURSER_OFF 0x00 +#define R7_CURSER_ON 0x40 + +#define R7_STATUS_BOTTOM 0x00 +#define R7_STATUS_TOP 0x80 + +/* Register R8 (Active chapter) */ +#define R8_ACTIVE_CHAPTER_0 0x00 +#define R8_ACTIVE_CHAPTER_1 0x01 +#define R8_ACTIVE_CHAPTER_2 0x02 +#define R8_ACTIVE_CHAPTER_3 0x03 +#define R8_ACTIVE_CHAPTER_4 0x04 +#define R8_ACTIVE_CHAPTER_5 0x05 +#define R8_ACTIVE_CHAPTER_6 0x06 +#define R8_ACTIVE_CHAPTER_7 0x07 + +#define R8_CLEAR_MEMORY 0x08 +#define R8_DO_NOT_CLEAR_MEMORY 0x00 + +/* Register R9 (Curser row) */ +#define R9_CURSER_ROW_0 0x00 +#define R9_CURSER_ROW_1 0x01 +#define R9_CURSER_ROW_2 0x02 +#define R9_CURSER_ROW_25 0x19 + +/* Register R10 (Curser column) */ +#define R10_CURSER_COLUMN_0 0x00 +#define R10_CURSER_COLUMN_6 0x06 +#define R10_CURSER_COLUMN_8 0x08 + +/*****************************************************************************/ +/* Row 25 control data in column 0 to 9 */ +/*****************************************************************************/ +#define ROW25_COLUMN0_PAGE_UNITS 0x0F + +#define ROW25_COLUMN1_PAGE_TENS 0x0F -static struct i2c_driver i2c_driver_videotext = +#define ROW25_COLUMN2_MINUTES_UNITS 0x0F + +#define ROW25_COLUMN3_MINUTES_TENS 0x07 +#define ROW25_COLUMN3_DELETE_PAGE 0x08 + +#define ROW25_COLUMN4_HOUR_UNITS 0x0F + +#define ROW25_COLUMN5_HOUR_TENS 0x03 +#define ROW25_COLUMN5_INSERT_HEADLINE 0x04 +#define ROW25_COLUMN5_INSERT_SUBTITLE 0x08 + +#define ROW25_COLUMN6_SUPPRESS_HEADER 0x01 +#define ROW25_COLUMN6_UPDATE_PAGE 0x02 +#define ROW25_COLUMN6_INTERRUPTED_SEQUENCE 0x04 +#define ROW25_COLUMN6_SUPPRESS_DISPLAY 0x08 + +#define ROW25_COLUMN7_SERIAL_MODE 0x01 +#define ROW25_COLUMN7_CHARACTER_SET 0x0E + +#define ROW25_COLUMN8_PAGE_HUNDREDS 0x07 +#define ROW25_COLUMN8_PAGE_NOT_FOUND 0x10 + +#define ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR 0x20 + +#define ROW25_COLUMN0_TO_7_HAMMING_ERROR 0x10 + +/*****************************************************************************/ +/* Helper macros for extracting page, hour and minute digits */ +/*****************************************************************************/ +/* BYTE_POS 0 is at row 0, column 0, + BYTE_POS 1 is at row 0, column 1, + BYTE_POS 40 is at row 1, column 0, (with NUM_ROWS_PER_PAGE = 40) + BYTE_POS 41 is at row 1, column 1, (with NUM_ROWS_PER_PAGE = 40), + ... */ +#define ROW(BYTE_POS) (BYTE_POS / NUM_ROWS_PER_PAGE) +#define COLUMN(BYTE_POS) (BYTE_POS % NUM_ROWS_PER_PAGE) + +/*****************************************************************************/ +/* Helper macros for extracting page, hour and minute digits */ +/*****************************************************************************/ +/* Macros for extracting hundreds, tens and units of a page number which + must be in the range 0 ... 0x799. + Note that page is coded in hexadecimal, i.e. 0x123 means page 123. + page 0x.. means page 8.. */ +#define HUNDREDS_OF_PAGE(page) (((page) / 0x100) & 0x7) +#define TENS_OF_PAGE(page) (((page) / 0x10) & 0xF) +#define UNITS_OF_PAGE(page) ((page) & 0xF) + +/* Macros for extracting tens and units of a hour information which + must be in the range 0 ... 0x24. + Note that hour is coded in hexadecimal, i.e. 0x12 means 12 hours */ +#define TENS_OF_HOUR(hour) ((hour) / 0x10) +#define UNITS_OF_HOUR(hour) ((hour) & 0xF) + +/* Macros for extracting tens and units of a minute information which + must be in the range 0 ... 0x59. + Note that minute is coded in hexadecimal, i.e. 0x12 means 12 minutes */ +#define TENS_OF_MINUTE(minute) ((minute) / 0x10) +#define UNITS_OF_MINUTE(minute) ((minute) & 0xF) + +#define HOUR_MAX 0x23 +#define MINUTE_MAX 0x59 +#define PAGE_MAX 0x8FF + + +struct saa5246a_device { - .driver = { - .name = IF_NAME, /* name */ - }, - .id = I2C_DRIVERID_SAA5249, /* in i2c.h */ - .attach_adapter = saa5246a_probe, - .detach_client = saa5246a_detach, + u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; + int is_searching[NUM_DAUS]; + struct i2c_client *client; + unsigned long in_use; + struct mutex lock; }; -static struct i2c_client client_template = { - .driver = &i2c_driver_videotext, - .name = "(unset)", -}; +static struct video_device saa_template; /* Declared near bottom */ + +/* + * I2C interfaces + */ static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data) { @@ -581,8 +808,8 @@ static inline int saa5246a_stop_dau(struct saa5246a_device *t, static int do_saa5246a_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - struct video_device *vd = video_devdata(file); - struct saa5246a_device *t=vd->priv; + struct saa5246a_device *t = video_drvdata(file); + switch(cmd) { case VTXIOCGETINFO: @@ -722,8 +949,7 @@ static inline unsigned int vtx_fix_command(unsigned int cmd) static int saa5246a_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct video_device *vd = video_devdata(file); - struct saa5246a_device *t = vd->priv; + struct saa5246a_device *t = video_drvdata(file); int err; cmd = vtx_fix_command(cmd); @@ -735,24 +961,15 @@ static int saa5246a_ioctl(struct inode *inode, struct file *file, static int saa5246a_open(struct inode *inode, struct file *file) { - struct video_device *vd = video_devdata(file); - struct saa5246a_device *t = vd->priv; - int err; + struct saa5246a_device *t = video_drvdata(file); - lock_kernel(); - err = video_exclusive_open(inode,file); - if (err < 0) { - unlock_kernel(); - return err; - } + if (t->client == NULL) + return -ENODEV; - if (t->client==NULL) { - err = -ENODEV; - goto fail; - } + if (test_and_set_bit(0, &t->in_use)) + return -EBUSY; if (i2c_senddata(t, SAA5246A_REGISTER_R0, - R0_SELECT_R11 | R0_PLL_TIME_CONSTANT_LONG | R0_ENABLE_nODD_EVEN_OUTPUT | @@ -778,23 +995,15 @@ static int saa5246a_open(struct inode *inode, struct file *file) COMMAND_END)) { - err = -EIO; - goto fail; + clear_bit(0, &t->in_use); + return -EIO; } - unlock_kernel(); - return 0; - -fail: - video_exclusive_release(inode,file); - unlock_kernel(); - return err; } static int saa5246a_release(struct inode *inode, struct file *file) { - struct video_device *vd = video_devdata(file); - struct saa5246a_device *t = vd->priv; + struct saa5246a_device *t = video_drvdata(file); /* Stop all acquisition circuits. */ i2c_senddata(t, SAA5246A_REGISTER_R1, @@ -807,26 +1016,10 @@ static int saa5246a_release(struct inode *inode, struct file *file) R1_VCS_TO_SCS, COMMAND_END); - video_exclusive_release(inode,file); + clear_bit(0, &t->in_use); return 0; } -static int __init init_saa_5246a (void) -{ - printk(KERN_INFO - "SAA5246A (or compatible) Teletext decoder driver version %d.%d\n", - MAJOR_VERSION, MINOR_VERSION); - return i2c_add_driver(&i2c_driver_videotext); -} - -static void __exit cleanup_saa_5246a (void) -{ - i2c_del_driver(&i2c_driver_videotext); -} - -module_init(init_saa_5246a); -module_exit(cleanup_saa_5246a); - static const struct file_operations saa_fops = { .owner = THIS_MODULE, .open = saa5246a_open, @@ -837,8 +1030,83 @@ static const struct file_operations saa_fops = { static struct video_device saa_template = { - .name = IF_NAME, + .name = "saa5246a", .fops = &saa_fops, .release = video_device_release, .minor = -1, }; + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; + +static int saa5246a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int pgbuf; + int err; + struct video_device *vd; + struct saa5246a_device *t; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + v4l_info(client, "VideoText version %d.%d\n", + MAJOR_VERSION, MINOR_VERSION); + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (t == NULL) + return -ENOMEM; + mutex_init(&t->lock); + + /* Now create a video4linux device */ + vd = video_device_alloc(); + if (vd == NULL) { + kfree(t); + return -ENOMEM; + } + i2c_set_clientdata(client, vd); + memcpy(vd, &saa_template, sizeof(*vd)); + + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { + memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); + t->is_searching[pgbuf] = false; + } + video_set_drvdata(vd, t); + + /* Register it */ + err = video_register_device(vd, VFL_TYPE_VTX, -1); + if (err < 0) { + kfree(t); + video_device_release(vd); + return err; + } + t->client = client; + return 0; +} + +static int saa5246a_remove(struct i2c_client *client) +{ + struct video_device *vd = i2c_get_clientdata(client); + + video_unregister_device(vd); + kfree(video_get_drvdata(vd)); + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id saa5246a_id[] = { + { "saa5246a", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa5246a_id); +#endif + +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "saa5246a", + .driverid = I2C_DRIVERID_SAA5249, + .probe = saa5246a_probe, + .remove = saa5246a_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = saa5246a_id, +#endif +}; diff --git a/linux/drivers/media/video/saa5246a.h b/linux/drivers/media/video/saa5246a.h deleted file mode 100644 index 64394c036..000000000 --- a/linux/drivers/media/video/saa5246a.h +++ /dev/null @@ -1,359 +0,0 @@ -/* - Driver for the SAA5246A or SAA5281 Teletext (=Videotext) decoder chips from - Philips. - - Copyright (C) 2004 Michael Geng (linux@MichaelGeng.de) - - 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. - - */ -#ifndef __SAA5246A_H__ -#define __SAA5246A_H__ - -#define MAJOR_VERSION 1 /* driver major version number */ -#define MINOR_VERSION 8 /* driver minor version number */ - -#define IF_NAME "SAA5246A" - -#define I2C_ADDRESS 17 - -/* Number of DAUs = number of pages that can be searched at the same time. */ -#define NUM_DAUS 4 - -#define NUM_ROWS_PER_PAGE 40 - -/* first column is 0 (not 1) */ -#define POS_TIME_START 32 -#define POS_TIME_END 39 - -#define POS_HEADER_START 7 -#define POS_HEADER_END 31 - -/* Returns 'true' if the part of the videotext page described with req contains - (at least parts of) the time field */ -#define REQ_CONTAINS_TIME(p_req) \ - ((p_req)->start <= POS_TIME_END && \ - (p_req)->end >= POS_TIME_START) - -/* Returns 'true' if the part of the videotext page described with req contains - (at least parts of) the page header */ -#define REQ_CONTAINS_HEADER(p_req) \ - ((p_req)->start <= POS_HEADER_END && \ - (p_req)->end >= POS_HEADER_START) - -/*****************************************************************************/ -/* Mode register numbers of the SAA5246A */ -/*****************************************************************************/ -#define SAA5246A_REGISTER_R0 0 -#define SAA5246A_REGISTER_R1 1 -#define SAA5246A_REGISTER_R2 2 -#define SAA5246A_REGISTER_R3 3 -#define SAA5246A_REGISTER_R4 4 -#define SAA5246A_REGISTER_R5 5 -#define SAA5246A_REGISTER_R6 6 -#define SAA5246A_REGISTER_R7 7 -#define SAA5246A_REGISTER_R8 8 -#define SAA5246A_REGISTER_R9 9 -#define SAA5246A_REGISTER_R10 10 -#define SAA5246A_REGISTER_R11 11 -#define SAA5246A_REGISTER_R11B 11 - -/* SAA5246A mode registers often autoincrement to the next register. - Therefore we use variable argument lists. The following macro indicates - the end of a command list. */ -#define COMMAND_END (- 1) - -/*****************************************************************************/ -/* Contents of the mode registers of the SAA5246A */ -/*****************************************************************************/ -/* Register R0 (Advanced Control) */ -#define R0_SELECT_R11 0x00 -#define R0_SELECT_R11B 0x01 - -#define R0_PLL_TIME_CONSTANT_LONG 0x00 -#define R0_PLL_TIME_CONSTANT_SHORT 0x02 - -#define R0_ENABLE_nODD_EVEN_OUTPUT 0x00 -#define R0_DISABLE_nODD_EVEN_OUTPUT 0x04 - -#define R0_ENABLE_HDR_POLL 0x00 -#define R0_DISABLE_HDR_POLL 0x10 - -#define R0_DO_NOT_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x00 -#define R0_FORCE_nODD_EVEN_LOW_IF_PICTURE_DISPLAYED 0x20 - -#define R0_NO_FREE_RUN_PLL 0x00 -#define R0_FREE_RUN_PLL 0x40 - -#define R0_NO_AUTOMATIC_FASTEXT_PROMPT 0x00 -#define R0_AUTOMATIC_FASTEXT_PROMPT 0x80 - -/* Register R1 (Mode) */ -#define R1_INTERLACED_312_AND_HALF_312_AND_HALF_LINES 0x00 -#define R1_NON_INTERLACED_312_313_LINES 0x01 -#define R1_NON_INTERLACED_312_312_LINES 0x02 -#define R1_FFB_LEADING_EDGE_IN_FIRST_BROAD_PULSE 0x03 -#define R1_FFB_LEADING_EDGE_IN_SECOND_BROAD_PULSE 0x07 - -#define R1_DEW 0x00 -#define R1_FULL_FIELD 0x08 - -#define R1_EXTENDED_PACKET_DISABLE 0x00 -#define R1_EXTENDED_PACKET_ENABLE 0x10 - -#define R1_DAUS_ALL_ON 0x00 -#define R1_DAUS_ALL_OFF 0x20 - -#define R1_7_BITS_PLUS_PARITY 0x00 -#define R1_8_BITS_NO_PARITY 0x40 - -#define R1_VCS_TO_SCS 0x00 -#define R1_NO_VCS_TO_SCS 0x80 - -/* Register R2 (Page request address) */ -#define R2_IN_R3_SELECT_PAGE_HUNDREDS 0x00 -#define R2_IN_R3_SELECT_PAGE_TENS 0x01 -#define R2_IN_R3_SELECT_PAGE_UNITS 0x02 -#define R2_IN_R3_SELECT_HOURS_TENS 0x03 -#define R2_IN_R3_SELECT_HOURS_UNITS 0x04 -#define R2_IN_R3_SELECT_MINUTES_TENS 0x05 -#define R2_IN_R3_SELECT_MINUTES_UNITS 0x06 - -#define R2_DAU_0 0x00 -#define R2_DAU_1 0x10 -#define R2_DAU_2 0x20 -#define R2_DAU_3 0x30 - -#define R2_BANK_0 0x00 -#define R2_BANK 1 0x40 - -#define R2_HAMMING_CHECK_ON 0x80 -#define R2_HAMMING_CHECK_OFF 0x00 - -/* Register R3 (Page request data) */ -#define R3_PAGE_HUNDREDS_0 0x00 -#define R3_PAGE_HUNDREDS_1 0x01 -#define R3_PAGE_HUNDREDS_2 0x02 -#define R3_PAGE_HUNDREDS_3 0x03 -#define R3_PAGE_HUNDREDS_4 0x04 -#define R3_PAGE_HUNDREDS_5 0x05 -#define R3_PAGE_HUNDREDS_6 0x06 -#define R3_PAGE_HUNDREDS_7 0x07 - -#define R3_HOLD_PAGE 0x00 -#define R3_UPDATE_PAGE 0x08 - -#define R3_PAGE_HUNDREDS_DO_NOT_CARE 0x00 -#define R3_PAGE_HUNDREDS_DO_CARE 0x10 - -#define R3_PAGE_TENS_DO_NOT_CARE 0x00 -#define R3_PAGE_TENS_DO_CARE 0x10 - -#define R3_PAGE_UNITS_DO_NOT_CARE 0x00 -#define R3_PAGE_UNITS_DO_CARE 0x10 - -#define R3_HOURS_TENS_DO_NOT_CARE 0x00 -#define R3_HOURS_TENS_DO_CARE 0x10 - -#define R3_HOURS_UNITS_DO_NOT_CARE 0x00 -#define R3_HOURS_UNITS_DO_CARE 0x10 - -#define R3_MINUTES_TENS_DO_NOT_CARE 0x00 -#define R3_MINUTES_TENS_DO_CARE 0x10 - -#define R3_MINUTES_UNITS_DO_NOT_CARE 0x00 -#define R3_MINUTES_UNITS_DO_CARE 0x10 - -/* Register R4 (Display chapter) */ -#define R4_DISPLAY_PAGE_0 0x00 -#define R4_DISPLAY_PAGE_1 0x01 -#define R4_DISPLAY_PAGE_2 0x02 -#define R4_DISPLAY_PAGE_3 0x03 -#define R4_DISPLAY_PAGE_4 0x04 -#define R4_DISPLAY_PAGE_5 0x05 -#define R4_DISPLAY_PAGE_6 0x06 -#define R4_DISPLAY_PAGE_7 0x07 - -/* Register R5 (Normal display control) */ -#define R5_PICTURE_INSIDE_BOXING_OFF 0x00 -#define R5_PICTURE_INSIDE_BOXING_ON 0x01 - -#define R5_PICTURE_OUTSIDE_BOXING_OFF 0x00 -#define R5_PICTURE_OUTSIDE_BOXING_ON 0x02 - -#define R5_TEXT_INSIDE_BOXING_OFF 0x00 -#define R5_TEXT_INSIDE_BOXING_ON 0x04 - -#define R5_TEXT_OUTSIDE_BOXING_OFF 0x00 -#define R5_TEXT_OUTSIDE_BOXING_ON 0x08 - -#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 -#define R5_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 - -#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 -#define R5_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 - -#define R5_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 -#define R5_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 - -#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 -#define R5_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 - -/* Register R6 (Newsflash display) */ -#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_PICTURE_INSIDE_BOXING_ON 0x01 - -#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_PICTURE_OUTSIDE_BOXING_ON 0x02 - -#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_TEXT_INSIDE_BOXING_ON 0x04 - -#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_TEXT_OUTSIDE_BOXING_ON 0x08 - -#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_CONTRAST_REDUCTION_INSIDE_BOXING_ON 0x10 - -#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_CONTRAST_REDUCTION_OUTSIDE_BOXING_ON 0x20 - -#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_BACKGROUND_COLOR_INSIDE_BOXING_ON 0x40 - -#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_OFF 0x00 -#define R6_NEWSFLASH_BACKGROUND_COLOR_OUTSIDE_BOXING_ON 0x80 - -/* Register R7 (Display mode) */ -#define R7_BOX_OFF_ROW_0 0x00 -#define R7_BOX_ON_ROW_0 0x01 - -#define R7_BOX_OFF_ROW_1_TO_23 0x00 -#define R7_BOX_ON_ROW_1_TO_23 0x02 - -#define R7_BOX_OFF_ROW_24 0x00 -#define R7_BOX_ON_ROW_24 0x04 - -#define R7_SINGLE_HEIGHT 0x00 -#define R7_DOUBLE_HEIGHT 0x08 - -#define R7_TOP_HALF 0x00 -#define R7_BOTTOM_HALF 0x10 - -#define R7_REVEAL_OFF 0x00 -#define R7_REVEAL_ON 0x20 - -#define R7_CURSER_OFF 0x00 -#define R7_CURSER_ON 0x40 - -#define R7_STATUS_BOTTOM 0x00 -#define R7_STATUS_TOP 0x80 - -/* Register R8 (Active chapter) */ -#define R8_ACTIVE_CHAPTER_0 0x00 -#define R8_ACTIVE_CHAPTER_1 0x01 -#define R8_ACTIVE_CHAPTER_2 0x02 -#define R8_ACTIVE_CHAPTER_3 0x03 -#define R8_ACTIVE_CHAPTER_4 0x04 -#define R8_ACTIVE_CHAPTER_5 0x05 -#define R8_ACTIVE_CHAPTER_6 0x06 -#define R8_ACTIVE_CHAPTER_7 0x07 - -#define R8_CLEAR_MEMORY 0x08 -#define R8_DO_NOT_CLEAR_MEMORY 0x00 - -/* Register R9 (Curser row) */ -#define R9_CURSER_ROW_0 0x00 -#define R9_CURSER_ROW_1 0x01 -#define R9_CURSER_ROW_2 0x02 -#define R9_CURSER_ROW_25 0x19 - -/* Register R10 (Curser column) */ -#define R10_CURSER_COLUMN_0 0x00 -#define R10_CURSER_COLUMN_6 0x06 -#define R10_CURSER_COLUMN_8 0x08 - -/*****************************************************************************/ -/* Row 25 control data in column 0 to 9 */ -/*****************************************************************************/ -#define ROW25_COLUMN0_PAGE_UNITS 0x0F - -#define ROW25_COLUMN1_PAGE_TENS 0x0F - -#define ROW25_COLUMN2_MINUTES_UNITS 0x0F - -#define ROW25_COLUMN3_MINUTES_TENS 0x07 -#define ROW25_COLUMN3_DELETE_PAGE 0x08 - -#define ROW25_COLUMN4_HOUR_UNITS 0x0F - -#define ROW25_COLUMN5_HOUR_TENS 0x03 -#define ROW25_COLUMN5_INSERT_HEADLINE 0x04 -#define ROW25_COLUMN5_INSERT_SUBTITLE 0x08 - -#define ROW25_COLUMN6_SUPPRESS_HEADER 0x01 -#define ROW25_COLUMN6_UPDATE_PAGE 0x02 -#define ROW25_COLUMN6_INTERRUPTED_SEQUENCE 0x04 -#define ROW25_COLUMN6_SUPPRESS_DISPLAY 0x08 - -#define ROW25_COLUMN7_SERIAL_MODE 0x01 -#define ROW25_COLUMN7_CHARACTER_SET 0x0E - -#define ROW25_COLUMN8_PAGE_HUNDREDS 0x07 -#define ROW25_COLUMN8_PAGE_NOT_FOUND 0x10 - -#define ROW25_COLUMN9_PAGE_BEING_LOOKED_FOR 0x20 - -#define ROW25_COLUMN0_TO_7_HAMMING_ERROR 0x10 - -/*****************************************************************************/ -/* Helper macros for extracting page, hour and minute digits */ -/*****************************************************************************/ -/* BYTE_POS 0 is at row 0, column 0, - BYTE_POS 1 is at row 0, column 1, - BYTE_POS 40 is at row 1, column 0, (with NUM_ROWS_PER_PAGE = 40) - BYTE_POS 41 is at row 1, column 1, (with NUM_ROWS_PER_PAGE = 40), - ... */ -#define ROW(BYTE_POS) (BYTE_POS / NUM_ROWS_PER_PAGE) -#define COLUMN(BYTE_POS) (BYTE_POS % NUM_ROWS_PER_PAGE) - -/*****************************************************************************/ -/* Helper macros for extracting page, hour and minute digits */ -/*****************************************************************************/ -/* Macros for extracting hundreds, tens and units of a page number which - must be in the range 0 ... 0x799. - Note that page is coded in hexadecimal, i.e. 0x123 means page 123. - page 0x.. means page 8.. */ -#define HUNDREDS_OF_PAGE(page) (((page) / 0x100) & 0x7) -#define TENS_OF_PAGE(page) (((page) / 0x10) & 0xF) -#define UNITS_OF_PAGE(page) ((page) & 0xF) - -/* Macros for extracting tens and units of a hour information which - must be in the range 0 ... 0x24. - Note that hour is coded in hexadecimal, i.e. 0x12 means 12 hours */ -#define TENS_OF_HOUR(hour) ((hour) / 0x10) -#define UNITS_OF_HOUR(hour) ((hour) & 0xF) - -/* Macros for extracting tens and units of a minute information which - must be in the range 0 ... 0x59. - Note that minute is coded in hexadecimal, i.e. 0x12 means 12 minutes */ -#define TENS_OF_MINUTE(minute) ((minute) / 0x10) -#define UNITS_OF_MINUTE(minute) ((minute) & 0xF) - -#define HOUR_MAX 0x23 -#define MINUTE_MAX 0x59 -#define PAGE_MAX 0x8FF - -#endif /* __SAA5246A_H__ */ diff --git a/linux/drivers/media/video/saa5249.c b/linux/drivers/media/video/saa5249.c index 81d341a80..763e6441b 100644 --- a/linux/drivers/media/video/saa5249.c +++ b/linux/drivers/media/video/saa5249.c @@ -15,8 +15,6 @@ * * Copyright (c) 1998 Richard Guenther <richard.guenther@student.uni-tuebingen.de> * - * $Id: saa5249.c,v 1.1 1998/03/30 22:23:23 alan Exp $ - * * Derived From * * vtx.c: @@ -45,35 +43,28 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/sched.h> #include <linux/mm.h> -#include <linux/errno.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/slab.h> #include <linux/init.h> -#include <linux/smp_lock.h> -#include <stdarg.h> #include <linux/i2c.h> +#include <linux/smp_lock.h> +#include <linux/mutex.h> #include <linux/videotext.h> -#include "compat.h" #include <linux/videodev.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#include <linux/mutex.h> - +#include <media/v4l2-i2c-drv-legacy.h> +#include "compat.h" -#include <asm/io.h> -#include <asm/uaccess.h> +MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); +MODULE_DESCRIPTION("Philips SAA5249 Teletext decoder driver"); +MODULE_LICENSE("GPL"); #define VTX_VER_MAJ 1 #define VTX_VER_MIN 8 - #define NUM_DAUS 4 #define NUM_BUFS 8 -#define IF_NAME "SAA5249" static const int disp_modes[8][3] = { @@ -111,6 +102,7 @@ struct saa5249_device int disp_mode; int virtual_mode; struct i2c_client *client; + unsigned long in_use; struct mutex lock; }; @@ -125,125 +117,8 @@ struct saa5249_device #define VTX_DEV_MINOR 0 -/* General defines and debugging support */ - -#define RESCHED do { cond_resched(); } while(0) - static struct video_device saa_template; /* Declared near bottom */ -/* Addresses to scan */ -static unsigned short normal_i2c[] = {34>>1,I2C_CLIENT_END}; - -I2C_CLIENT_INSMOD; - -static struct i2c_client client_template; - -static int saa5249_attach(struct i2c_adapter *adap, int addr, int kind) -{ - int pgbuf; - int err; - struct i2c_client *client; - struct video_device *vd; - struct saa5249_device *t; - - printk(KERN_INFO "saa5249: teletext chip found.\n"); - client=kmalloc(sizeof(*client), GFP_KERNEL); - if(client==NULL) - return -ENOMEM; - client_template.adapter = adap; - client_template.addr = addr; - memcpy(client, &client_template, sizeof(*client)); - t = kzalloc(sizeof(*t), GFP_KERNEL); - if(t==NULL) - { - kfree(client); - return -ENOMEM; - } - strlcpy(client->name, IF_NAME, I2C_NAME_SIZE); - mutex_init(&t->lock); - - /* - * Now create a video4linux device - */ - - vd = kmalloc(sizeof(struct video_device), GFP_KERNEL); - if(vd==NULL) - { - kfree(t); - kfree(client); - return -ENOMEM; - } - i2c_set_clientdata(client, vd); - memcpy(vd, &saa_template, sizeof(*vd)); - - for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) - { - memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); - memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); - memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); - t->vdau[pgbuf].expire = 0; - t->vdau[pgbuf].clrfound = true; - t->vdau[pgbuf].stopped = true; - t->is_searching[pgbuf] = false; - } - vd->priv=t; - - - /* - * Register it - */ - - if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0) - { - kfree(t); - kfree(vd); - kfree(client); - return err; - } - t->client = client; - i2c_attach_client(client); - return 0; -} - -/* - * We do most of the hard work when we become a device on the i2c. - */ - -static int saa5249_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, saa5249_attach); - return 0; -} - -static int saa5249_detach(struct i2c_client *client) -{ - struct video_device *vd = i2c_get_clientdata(client); - i2c_detach_client(client); - video_unregister_device(vd); - kfree(vd->priv); - kfree(vd); - kfree(client); - return 0; -} - -/* new I2C driver support */ - -static struct i2c_driver i2c_driver_videotext = -{ - .driver = { - .name = IF_NAME, /* name */ - }, - .id = I2C_DRIVERID_SAA5249, /* in i2c.h */ - .attach_adapter = saa5249_probe, - .detach_client = saa5249_detach, -}; - -static struct i2c_client client_template = { - .driver = &i2c_driver_videotext, - .name = "(unset)", -}; - /* * Wait the given number of jiffies (10ms). This calls the scheduler, so the actual * delay may be longer. @@ -277,7 +152,7 @@ static int i2c_sendbuf(struct saa5249_device *t, int reg, int count, u8 *data) buf[0] = reg; memcpy(buf+1, data, count); - if(i2c_master_send(t->client, buf, count+1)==count+1) + if (i2c_master_send(t->client, buf, count + 1) == count + 1) return 0; return -1; } @@ -319,246 +194,236 @@ static int do_saa5249_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { static int virtual_mode = false; - struct video_device *vd = video_devdata(file); - struct saa5249_device *t=vd->priv; + struct saa5249_device *t = video_drvdata(file); - switch(cmd) + switch (cmd) { + case VTXIOCGETINFO: { - case VTXIOCGETINFO: - { - vtx_info_t *info = arg; - info->version_major = VTX_VER_MAJ; - info->version_minor = VTX_VER_MIN; - info->numpages = NUM_DAUS; - /*info->cct_type = CCT_TYPE;*/ - return 0; - } + vtx_info_t *info = arg; + info->version_major = VTX_VER_MAJ; + info->version_minor = VTX_VER_MIN; + info->numpages = NUM_DAUS; + /*info->cct_type = CCT_TYPE;*/ + return 0; + } - case VTXIOCCLRPAGE: - { - vtx_pagereq_t *req = arg; + case VTXIOCCLRPAGE: + { + vtx_pagereq_t *req = arg; - if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) - return -EINVAL; - memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); - t->vdau[req->pgbuf].clrfound = true; - return 0; - } + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); + t->vdau[req->pgbuf].clrfound = true; + return 0; + } - case VTXIOCCLRFOUND: - { - vtx_pagereq_t *req = arg; + case VTXIOCCLRFOUND: + { + vtx_pagereq_t *req = arg; - if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) - return -EINVAL; - t->vdau[req->pgbuf].clrfound = true; - return 0; - } + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req->pgbuf].clrfound = true; + return 0; + } - case VTXIOCPAGEREQ: - { - vtx_pagereq_t *req = arg; - if (!(req->pagemask & PGMASK_PAGE)) - req->page = 0; - if (!(req->pagemask & PGMASK_HOUR)) - req->hour = 0; - if (!(req->pagemask & PGMASK_MINUTE)) - req->minute = 0; - if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */ - return -EINVAL; - req->page &= 0x7ff; - if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f || - req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) - return -EINVAL; - t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100); - t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf); - t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf); - t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10); - t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf); - t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10); - t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf); - t->vdau[req->pgbuf].stopped = false; - t->vdau[req->pgbuf].clrfound = true; - t->is_searching[req->pgbuf] = true; - return 0; - } + case VTXIOCPAGEREQ: + { + vtx_pagereq_t *req = arg; + if (!(req->pagemask & PGMASK_PAGE)) + req->page = 0; + if (!(req->pagemask & PGMASK_HOUR)) + req->hour = 0; + if (!(req->pagemask & PGMASK_MINUTE)) + req->minute = 0; + if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */ + return -EINVAL; + req->page &= 0x7ff; + if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f || + req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100); + t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf); + t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf); + t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10); + t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf); + t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10); + t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf); + t->vdau[req->pgbuf].stopped = false; + t->vdau[req->pgbuf].clrfound = true; + t->is_searching[req->pgbuf] = true; + return 0; + } - case VTXIOCGETSTAT: - { - vtx_pagereq_t *req = arg; - u8 infobits[10]; - vtx_pageinfo_t info; - int a; - - if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) - return -EINVAL; - if (!t->vdau[req->pgbuf].stopped) - { - if (i2c_senddata(t, 2, 0, -1) || - i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) || - i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) || - i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) || - i2c_senddata(t, 8, 0, 25, 0, -1)) - return -EIO; - jdelay(PAGE_WAIT); - if (i2c_getdata(t, 10, infobits)) - return -EIO; + case VTXIOCGETSTAT: + { + vtx_pagereq_t *req = arg; + u8 infobits[10]; + vtx_pageinfo_t info; + int a; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + if (!t->vdau[req->pgbuf].stopped) { + if (i2c_senddata(t, 2, 0, -1) || + i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) || + i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) || + i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) || + i2c_senddata(t, 8, 0, 25, 0, -1)) + return -EIO; + jdelay(PAGE_WAIT); + if (i2c_getdata(t, 10, infobits)) + return -EIO; - if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */ - (memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) || - time_after_eq(jiffies, t->vdau[req->pgbuf].expire))) - { /* check if new page arrived */ - if (i2c_senddata(t, 8, 0, 0, 0, -1) || - i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf)) + if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */ + (memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) || + time_after_eq(jiffies, t->vdau[req->pgbuf].expire))) + { /* check if new page arrived */ + if (i2c_senddata(t, 8, 0, 0, 0, -1) || + i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf)) + return -EIO; + t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE; + memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE); + if (t->virtual_mode) { + /* Packet X/24 */ + if (i2c_senddata(t, 8, 0, 0x20, 0, -1) || + i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40)) + return -EIO; + /* Packet X/27/0 */ + if (i2c_senddata(t, 8, 0, 0x21, 0, -1) || + i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40)) + return -EIO; + /* Packet 8/30/0...8/30/15 + * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30, + * so we should undo this here. + */ + if (i2c_senddata(t, 8, 0, 0x22, 0, -1) || + i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40)) return -EIO; - t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE; - memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE); - if (t->virtual_mode) - { - /* Packet X/24 */ - if (i2c_senddata(t, 8, 0, 0x20, 0, -1) || - i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40)) - return -EIO; - /* Packet X/27/0 */ - if (i2c_senddata(t, 8, 0, 0x21, 0, -1) || - i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40)) - return -EIO; - /* Packet 8/30/0...8/30/15 - * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30, - * so we should undo this here. - */ - if (i2c_senddata(t, 8, 0, 0x22, 0, -1) || - i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40)) - return -EIO; - } - t->vdau[req->pgbuf].clrfound = false; - memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits)); - } - else - { - memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); } - } - else - { + t->vdau[req->pgbuf].clrfound = false; + memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits)); + } else { memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); } + } else { + memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); + } - info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f); - if (info.pagenum < 0x100) - info.pagenum += 0x800; - info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f); - info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f); - info.charset = ((infobits[7] >> 1) & 7); - info.delete = !!(infobits[3] & 8); - info.headline = !!(infobits[5] & 4); - info.subtitle = !!(infobits[5] & 8); - info.supp_header = !!(infobits[6] & 1); - info.update = !!(infobits[6] & 2); - info.inter_seq = !!(infobits[6] & 4); - info.dis_disp = !!(infobits[6] & 8); - info.serial = !!(infobits[7] & 1); - info.notfound = !!(infobits[8] & 0x10); - info.pblf = !!(infobits[9] & 0x20); - info.hamming = 0; - for (a = 0; a <= 7; a++) - { - if (infobits[a] & 0xf0) - { - info.hamming = 1; - break; - } + info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f); + if (info.pagenum < 0x100) + info.pagenum += 0x800; + info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f); + info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f); + info.charset = ((infobits[7] >> 1) & 7); + info.delete = !!(infobits[3] & 8); + info.headline = !!(infobits[5] & 4); + info.subtitle = !!(infobits[5] & 8); + info.supp_header = !!(infobits[6] & 1); + info.update = !!(infobits[6] & 2); + info.inter_seq = !!(infobits[6] & 4); + info.dis_disp = !!(infobits[6] & 8); + info.serial = !!(infobits[7] & 1); + info.notfound = !!(infobits[8] & 0x10); + info.pblf = !!(infobits[9] & 0x20); + info.hamming = 0; + for (a = 0; a <= 7; a++) { + if (infobits[a] & 0xf0) { + info.hamming = 1; + break; } - if (t->vdau[req->pgbuf].clrfound) - info.notfound = 1; - if(copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t))) - return -EFAULT; - if (!info.hamming && !info.notfound) - { - t->is_searching[req->pgbuf] = false; - } - return 0; } + if (t->vdau[req->pgbuf].clrfound) + info.notfound = 1; + if (copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t))) + return -EFAULT; + if (!info.hamming && !info.notfound) + t->is_searching[req->pgbuf] = false; + return 0; + } - case VTXIOCGETPAGE: - { - vtx_pagereq_t *req = arg; - int start, end; - - if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 || - req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE)) - return -EINVAL; - if(copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1)) + case VTXIOCGETPAGE: + { + vtx_pagereq_t *req = arg; + int start, end; + + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 || + req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE)) + return -EINVAL; + if (copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1)) + return -EFAULT; + + /* + * Always read the time directly from SAA5249 + */ + + if (req->start <= 39 && req->end >= 32) { + int len; + char buf[16]; + start = max(req->start, 32); + end = min(req->end, 39); + len = end - start + 1; + if (i2c_senddata(t, 8, 0, 0, start, -1) || + i2c_getdata(t, len, buf)) + return -EIO; + if (copy_to_user(req->buffer + start - req->start, buf, len)) return -EFAULT; - - /* - * Always read the time directly from SAA5249 - */ - - if (req->start <= 39 && req->end >= 32) - { - int len; - char buf[16]; - start = max(req->start, 32); - end = min(req->end, 39); - len=end-start+1; - if (i2c_senddata(t, 8, 0, 0, start, -1) || - i2c_getdata(t, len, buf)) - return -EIO; - if(copy_to_user(req->buffer+start-req->start, buf, len)) - return -EFAULT; - } - /* Insert the current header if DAU is still searching for a page */ - if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf]) - { - char buf[32]; - int len; - start = max(req->start, 7); - end = min(req->end, 31); - len=end-start+1; - if (i2c_senddata(t, 8, 0, 0, start, -1) || - i2c_getdata(t, len, buf)) - return -EIO; - if(copy_to_user(req->buffer+start-req->start, buf, len)) - return -EFAULT; - } - return 0; } + /* Insert the current header if DAU is still searching for a page */ + if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf]) { + char buf[32]; + int len; + + start = max(req->start, 7); + end = min(req->end, 31); + len = end - start + 1; + if (i2c_senddata(t, 8, 0, 0, start, -1) || + i2c_getdata(t, len, buf)) + return -EIO; + if (copy_to_user(req->buffer + start - req->start, buf, len)) + return -EFAULT; + } + return 0; + } - case VTXIOCSTOPDAU: - { - vtx_pagereq_t *req = arg; + case VTXIOCSTOPDAU: + { + vtx_pagereq_t *req = arg; - if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) - return -EINVAL; - t->vdau[req->pgbuf].stopped = true; - t->is_searching[req->pgbuf] = false; - return 0; - } + if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) + return -EINVAL; + t->vdau[req->pgbuf].stopped = true; + t->is_searching[req->pgbuf] = false; + return 0; + } - case VTXIOCPUTPAGE: - case VTXIOCSETDISP: - case VTXIOCPUTSTAT: - return 0; + case VTXIOCPUTPAGE: + case VTXIOCSETDISP: + case VTXIOCPUTSTAT: + return 0; - case VTXIOCCLRCACHE: - { - if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11, - ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', - ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', -1)) - return -EIO; - if (i2c_senddata(t, 3, 0x20, -1)) - return -EIO; - jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */ - return 0; - } + case VTXIOCCLRCACHE: + { + if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11, + ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', + -1)) + return -EIO; + if (i2c_senddata(t, 3, 0x20, -1)) + return -EIO; + jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */ + return 0; + } - case VTXIOCSETVIRT: - { - /* The SAA5249 has virtual-row reception turned on always */ - t->virtual_mode = (int)(long)arg; - return 0; - } + case VTXIOCSETVIRT: + { + /* The SAA5249 has virtual-row reception turned on always */ + t->virtual_mode = (int)(long)arg; + return 0; + } } return -EINVAL; } @@ -618,8 +483,7 @@ static inline unsigned int vtx_fix_command(unsigned int cmd) static int saa5249_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct video_device *vd = video_devdata(file); - struct saa5249_device *t=vd->priv; + struct saa5249_device *t = video_drvdata(file); int err; cmd = vtx_fix_command(cmd); @@ -631,35 +495,27 @@ static int saa5249_ioctl(struct inode *inode, struct file *file, static int saa5249_open(struct inode *inode, struct file *file) { - struct video_device *vd = video_devdata(file); - struct saa5249_device *t=vd->priv; - int err,pgbuf; + struct saa5249_device *t = video_drvdata(file); + int pgbuf; - lock_kernel(); - err = video_exclusive_open(inode,file); - if (err < 0) { - unlock_kernel(); - return err; - } + if (t->client == NULL) + return -ENODEV; - if (t->client==NULL) { - err = -ENODEV; - goto fail; - } + if (test_and_set_bit(0, &t->in_use)) + return -EBUSY; - if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */ - /* Turn off parity checks (we do this ourselves) */ + if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */ + /* Turn off parity checks (we do this ourselves) */ i2c_senddata(t, 1, disp_modes[t->disp_mode][0], 0, -1) || - /* Display TV-picture, no virtual rows */ - i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) /* Set display to page 4 */ - + /* Display TV-picture, no virtual rows */ + i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) + /* Set display to page 4 */ { - err = -EIO; - goto fail; + clear_bit(0, &t->in_use); + return -EIO; } - for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) - { + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); @@ -669,42 +525,21 @@ static int saa5249_open(struct inode *inode, struct file *file) t->is_searching[pgbuf] = false; } t->virtual_mode = false; - unlock_kernel(); return 0; - - fail: - video_exclusive_release(inode,file); - unlock_kernel(); - return err; } static int saa5249_release(struct inode *inode, struct file *file) { - struct video_device *vd = video_devdata(file); - struct saa5249_device *t=vd->priv; + struct saa5249_device *t = video_drvdata(file); + i2c_senddata(t, 1, 0x20, -1); /* Turn off CCT */ i2c_senddata(t, 5, 3, 3, -1); /* Turn off TV-display */ - video_exclusive_release(inode,file); + clear_bit(0, &t->in_use); return 0; } -static int __init init_saa_5249 (void) -{ - printk(KERN_INFO "SAA5249 driver (" IF_NAME " interface) for VideoText version %d.%d\n", - VTX_VER_MAJ, VTX_VER_MIN); - return i2c_add_driver(&i2c_driver_videotext); -} - -static void __exit cleanup_saa_5249 (void) -{ - i2c_del_driver(&i2c_driver_videotext); -} - -module_init(init_saa_5249); -module_exit(cleanup_saa_5249); - static const struct file_operations saa_fops = { .owner = THIS_MODULE, .open = saa5249_open, @@ -718,8 +553,88 @@ static const struct file_operations saa_fops = { static struct video_device saa_template = { - .name = IF_NAME, + .name = "saa5249", .fops = &saa_fops, + .release = video_device_release, }; -MODULE_LICENSE("GPL"); +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; + +static int saa5249_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int pgbuf; + int err; + struct video_device *vd; + struct saa5249_device *t; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + v4l_info(client, "VideoText version %d.%d\n", + VTX_VER_MAJ, VTX_VER_MIN); + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (t == NULL) + return -ENOMEM; + mutex_init(&t->lock); + + /* Now create a video4linux device */ + vd = kmalloc(sizeof(struct video_device), GFP_KERNEL); + if (vd == NULL) { + kfree(client); + return -ENOMEM; + } + i2c_set_clientdata(client, vd); + memcpy(vd, &saa_template, sizeof(*vd)); + + for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { + memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); + memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); + memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); + t->vdau[pgbuf].expire = 0; + t->vdau[pgbuf].clrfound = true; + t->vdau[pgbuf].stopped = true; + t->is_searching[pgbuf] = false; + } + video_set_drvdata(vd, t); + + /* Register it */ + err = video_register_device(vd, VFL_TYPE_VTX, -1); + if (err < 0) { + kfree(t); + kfree(vd); + return err; + } + t->client = client; + return 0; +} + +static int saa5249_remove(struct i2c_client *client) +{ + struct video_device *vd = i2c_get_clientdata(client); + + video_unregister_device(vd); + kfree(video_get_drvdata(vd)); + kfree(vd); + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id saa5249_id[] = { + { "saa5249", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa5249_id); +#endif + +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "saa5249", + .driverid = I2C_DRIVERID_SAA5249, + .probe = saa5249_probe, + .remove = saa5249_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = saa5249_id, +#endif +}; diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c index 5488679ed..6b9df354b 100644 --- a/linux/drivers/media/video/saa7115.c +++ b/linux/drivers/media/video/saa7115.c @@ -1310,10 +1310,13 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar case VIDIOC_INT_S_VIDEO_ROUTING: { struct v4l2_routing *route = arg; + u32 input = route->input; + u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0; v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output); - /* saa7113 does not have these inputs */ - if (state->ident == V4L2_IDENT_SAA7113 && + /* saa7111/3 does not have these inputs */ + if ((state->ident == V4L2_IDENT_SAA7113 || + state->ident == V4L2_IDENT_SAA7111) && (route->input == SAA7115_COMPOSITE4 || route->input == SAA7115_COMPOSITE5)) { return -EINVAL; @@ -1328,10 +1331,23 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); state->input = route->input; + /* saa7111 has slightly different input numbering */ + if (state->ident == V4L2_IDENT_SAA7111) { + if (input >= SAA7115_COMPOSITE4) + input -= 2; + /* saa7111 specific */ + saa711x_write(client, R_10_CHROMA_CNTL_2, + (saa711x_read(client, R_10_CHROMA_CNTL_2) & 0x3f) | + ((route->output & 0xc0) ^ 0x40)); + saa711x_write(client, R_13_RT_X_PORT_OUT_CNTL, + (saa711x_read(client, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | + ((route->output & 2) ? 0x0a : 0)); + } + /* select mode */ saa711x_write(client, R_02_INPUT_CNTL_1, - (saa711x_read(client, R_02_INPUT_CNTL_1) & 0xf0) | - state->input); + (saa711x_read(client, R_02_INPUT_CNTL_1) & mask) | + input); /* bypass chrominance trap for S-Video modes */ saa711x_write(client, R_09_LUMA_CNTL, @@ -1385,6 +1401,13 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar saa711x_writeregs(client, saa7115_cfg_reset_scaler); break; + case VIDIOC_INT_S_GPIO: + if (state->ident != V4L2_IDENT_SAA7111) + return -EINVAL; + saa711x_write(client, 0x11, (saa711x_read(client, 0x11) & 0x7f) | + (*(u32 *)arg ? 0x80 : 0)); + break; + case VIDIOC_INT_G_VBI_DATA: { struct v4l2_sliced_vbi_data *data = arg; @@ -1498,10 +1521,9 @@ static int saa7115_probe(struct i2c_client *client, client->addr << 1, client->adapter->name); state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); - i2c_set_clientdata(client, state); - if (state == NULL) { + if (state == NULL) return -ENOMEM; - } + i2c_set_clientdata(client, state); state->input = -1; state->output = SAA7115_IPORT_ON; state->enable = 1; @@ -1549,7 +1571,8 @@ static int saa7115_probe(struct i2c_client *client, state->crystal_freq = SAA7115_FREQ_32_11_MHZ; saa711x_writeregs(client, saa7115_init_auto_input); } - saa711x_writeregs(client, saa7115_init_misc); + if (state->ident != V4L2_IDENT_SAA7111) + saa711x_writeregs(client, saa7115_init_misc); saa711x_set_v4lstd(client, V4L2_STD_NTSC); v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n", diff --git a/linux/drivers/media/video/saa7134/saa6752hs.c b/linux/drivers/media/video/saa7134/saa6752hs.c index c5c19a2e3..8ccca4d56 100644 --- a/linux/drivers/media/video/saa7134/saa6752hs.c +++ b/linux/drivers/media/video/saa7134/saa6752hs.c @@ -1,3 +1,27 @@ + /* + saa6752hs - i2c-driver for the saa6752hs by Philips + + Copyright (C) 2004 Andrew de Quincey + + AC-3 support: + + Copyright (C) 2008 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 vs 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 Mvss Ave, Cambridge, MA 02139, USA. + */ + #include <linux/module.h> #include <linux/kernel.h> #include <linux/string.h> @@ -11,6 +35,8 @@ #include "compat.h" #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv-legacy.h> #include <linux/init.h> #include <linux/crc32.h> @@ -28,9 +54,6 @@ MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder"); MODULE_AUTHOR("Andrew de Quincey"); MODULE_LICENSE("GPL"); -static struct i2c_driver driver; -static struct i2c_client client_template; - enum saa6752hs_videoformat { SAA6752HS_VF_D1 = 0, /* standard D1 video format: 720x576 */ SAA6752HS_VF_2_3_D1 = 1,/* 2/3D1 video format: 480x576 */ @@ -47,7 +70,9 @@ struct saa6752hs_mpeg_params { __u16 ts_pid_pcr; /* audio */ - enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate; + enum v4l2_mpeg_audio_encoding au_encoding; + enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate; + enum v4l2_mpeg_audio_ac3_bitrate au_ac3_bitrate; /* video */ enum v4l2_mpeg_video_aspect vi_aspect; @@ -71,7 +96,9 @@ static const struct v4l2_format v4l2_format_table[] = }; struct saa6752hs_state { - struct i2c_client client; + int chip; + u32 revision; + int has_ac3; struct saa6752hs_mpeg_params params; enum saa6752hs_videoformat video_format; v4l2_std_id standard; @@ -146,6 +173,39 @@ static u8 PMT[] = { 0x00, 0x00, 0x00, 0x00 /* CRC32 */ }; +static u8 PMT_AC3[] = { + 0xc2, /* i2c register */ + 0x01, /* table number for encoder(1) */ + 0x47, /* sync */ + + 0x40, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0) */ + 0x10, /* PMT PID (0x0010) */ + 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ + + 0x00, /* PSI pointer to start of table */ + + 0x02, /* TID (2) */ + 0xb0, 0x1a, /* section_syntax_indicator(1), section_length(26) */ + + 0x00, 0x01, /* program_number(1) */ + + 0xc1, /* version_number(0), current_next_indicator(1) */ + + 0x00, 0x00, /* section_number(0), last_section_number(0) */ + + 0xe1, 0x04, /* PCR_PID (0x0104) */ + + 0xf0, 0x00, /* program_info_length(0) */ + + 0x02, 0xe1, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ + 0x06, 0xe1, 0x03, 0xf0, 0x03, /* audio stream type(6), pid */ + 0x6a, /* AC3 */ + 0x01, /* Descriptor_length(1) */ + 0x00, /* component_type_flag(0), bsid_flag(0), mainid_flag(0), asvc_flag(0), reserved flags(0) */ + + 0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */ +}; + static struct saa6752hs_mpeg_params param_defaults = { .ts_pid_pmt = 16, @@ -158,12 +218,14 @@ static struct saa6752hs_mpeg_params param_defaults = .vi_bitrate_peak = 6000, .vi_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + .au_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, .au_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_256K, + .au_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_256K, }; /* ---------------------------------------------------------------------- */ -static int saa6752hs_chip_command(struct i2c_client* client, +static int saa6752hs_chip_command(struct i2c_client *client, enum saa6752hs_command command) { unsigned char buf[3]; @@ -230,45 +292,61 @@ static int saa6752hs_chip_command(struct i2c_client* client, } -static int saa6752hs_set_bitrate(struct i2c_client* client, - struct saa6752hs_mpeg_params* params) +static inline void set_reg8(struct i2c_client *client, uint8_t reg, uint8_t val) +{ + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + i2c_master_send(client, buf, 2); +} + +static inline void set_reg16(struct i2c_client *client, uint8_t reg, uint16_t val) { u8 buf[3]; + + buf[0] = reg; + buf[1] = val >> 8; + buf[2] = val & 0xff; + i2c_master_send(client, buf, 3); +} + +static int saa6752hs_set_bitrate(struct i2c_client *client, + struct saa6752hs_state *h) +{ + struct saa6752hs_mpeg_params *params = &h->params; int tot_bitrate; + int is_384k; /* set the bitrate mode */ - buf[0] = 0x71; - buf[1] = (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) ? 0 : 1; - i2c_master_send(client, buf, 2); + set_reg8(client, 0x71, + params->vi_bitrate_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); /* set the video bitrate */ if (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) { /* set the target bitrate */ - buf[0] = 0x80; - buf[1] = params->vi_bitrate >> 8; - buf[2] = params->vi_bitrate & 0xff; - i2c_master_send(client, buf, 3); + set_reg16(client, 0x80, params->vi_bitrate); /* set the max bitrate */ - buf[0] = 0x81; - buf[1] = params->vi_bitrate_peak >> 8; - buf[2] = params->vi_bitrate_peak & 0xff; - i2c_master_send(client, buf, 3); + set_reg16(client, 0x81, params->vi_bitrate_peak); tot_bitrate = params->vi_bitrate_peak; } else { /* set the target bitrate (no max bitrate for CBR) */ - buf[0] = 0x81; - buf[1] = params->vi_bitrate >> 8; - buf[2] = params->vi_bitrate & 0xff; - i2c_master_send(client, buf, 3); + set_reg16(client, 0x81, params->vi_bitrate); tot_bitrate = params->vi_bitrate; } + /* set the audio encoding */ + set_reg8(client, 0x93, + params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3); + /* set the audio bitrate */ - buf[0] = 0x94; - buf[1] = (V4L2_MPEG_AUDIO_L2_BITRATE_256K == params->au_l2_bitrate) ? 0 : 1; - i2c_master_send(client, buf, 2); - tot_bitrate += (V4L2_MPEG_AUDIO_L2_BITRATE_256K == params->au_l2_bitrate) ? 256 : 384; + if (params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) + is_384k = V4L2_MPEG_AUDIO_AC3_BITRATE_384K == params->au_ac3_bitrate; + else + is_384k = V4L2_MPEG_AUDIO_L2_BITRATE_384K == params->au_l2_bitrate; + set_reg8(client, 0x94, is_384k); + tot_bitrate += is_384k ? 384 : 256; /* Note: the total max bitrate is determined by adding the video and audio bitrates together and also adding an extra 768kbit/s to stay on the @@ -279,16 +357,12 @@ static int saa6752hs_set_bitrate(struct i2c_client* client, tot_bitrate = MPEG_TOTAL_TARGET_BITRATE_MAX; /* set the total bitrate */ - buf[0] = 0xb1; - buf[1] = tot_bitrate >> 8; - buf[2] = tot_bitrate & 0xff; - i2c_master_send(client, buf, 3); - + set_reg16(client, 0xb1, tot_bitrate); return 0; } -static void saa6752hs_set_subsampling(struct i2c_client* client, - struct v4l2_format* f) +static void saa6752hs_set_subsampling(struct i2c_client *client, + struct v4l2_format *f) { struct saa6752hs_state *h = i2c_get_clientdata(client); int dist_352, dist_480, dist_720; @@ -333,7 +407,7 @@ static void saa6752hs_set_subsampling(struct i2c_client* client, } -static int handle_ctrl(struct saa6752hs_mpeg_params *params, +static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params, struct v4l2_ext_control *ctrl, unsigned int cmd) { int old = 0, new; @@ -380,8 +454,9 @@ static int handle_ctrl(struct saa6752hs_mpeg_params *params, params->ts_pid_pcr = new; break; case V4L2_CID_MPEG_AUDIO_ENCODING: - old = V4L2_MPEG_AUDIO_ENCODING_LAYER_2; - if (set && new != old) + old = params->au_encoding; + if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && + (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3)) return -ERANGE; new = old; break; @@ -396,6 +471,19 @@ static int handle_ctrl(struct saa6752hs_mpeg_params *params, new = V4L2_MPEG_AUDIO_L2_BITRATE_384K; params->au_l2_bitrate = new; break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!has_ac3) + return -EINVAL; + old = params->au_ac3_bitrate; + if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K && + new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K) + return -ERANGE; + if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K) + new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K; + else + new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K; + params->au_ac3_bitrate = new; + break; case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000; if (set && new != old) @@ -449,17 +537,19 @@ static int handle_ctrl(struct saa6752hs_mpeg_params *params, return 0; } -static int saa6752hs_qctrl(struct saa6752hs_mpeg_params *params, +static int saa6752hs_qctrl(struct saa6752hs_state *h, struct v4l2_queryctrl *qctrl) { + struct saa6752hs_mpeg_params *params = &h->params; int err; switch (qctrl->id) { case V4L2_CID_MPEG_AUDIO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return v4l2_ctrl_query_fill(qctrl, @@ -467,6 +557,14 @@ static int saa6752hs_qctrl(struct saa6752hs_mpeg_params *params, V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, V4L2_MPEG_AUDIO_L2_BITRATE_256K); + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!h->has_ac3) + return -EINVAL; + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_AC3_BITRATE_256K, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K, 1, + V4L2_MPEG_AUDIO_AC3_BITRATE_256K); + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, @@ -513,44 +611,57 @@ static int saa6752hs_qctrl(struct saa6752hs_mpeg_params *params, return -EINVAL; } -static int saa6752hs_qmenu(struct saa6752hs_mpeg_params *params, +static int saa6752hs_qmenu(struct saa6752hs_state *h, struct v4l2_querymenu *qmenu) { - static const char *mpeg_audio_l2_bitrate[] = { - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "256 kbps", - "", - "384 kbps", - NULL + static const u32 mpeg_audio_encoding[] = { + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_CTRL_MENU_IDS_END + }; + static const u32 mpeg_audio_ac3_encoding[] = { + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_AC3, + V4L2_CTRL_MENU_IDS_END + }; + static u32 mpeg_audio_l2_bitrate[] = { + V4L2_MPEG_AUDIO_L2_BITRATE_256K, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, + V4L2_CTRL_MENU_IDS_END + }; + static u32 mpeg_audio_ac3_bitrate[] = { + V4L2_MPEG_AUDIO_AC3_BITRATE_256K, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K, + V4L2_CTRL_MENU_IDS_END }; struct v4l2_queryctrl qctrl; int err; qctrl.id = qmenu->id; - err = saa6752hs_qctrl(params, &qctrl); + err = saa6752hs_qctrl(h, &qctrl); if (err) return err; - if (qmenu->id == V4L2_CID_MPEG_AUDIO_L2_BITRATE) - return v4l2_ctrl_query_menu(qmenu, &qctrl, + switch (qmenu->id) { + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + return v4l2_ctrl_query_menu_valid_items(qmenu, mpeg_audio_l2_bitrate); - return v4l2_ctrl_query_menu(qmenu, &qctrl, - v4l2_ctrl_get_menu(qmenu->id)); + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!h->has_ac3) + return -EINVAL; + return v4l2_ctrl_query_menu_valid_items(qmenu, + mpeg_audio_ac3_bitrate); + case V4L2_CID_MPEG_AUDIO_ENCODING: + return v4l2_ctrl_query_menu_valid_items(qmenu, + h->has_ac3 ? mpeg_audio_ac3_encoding : + mpeg_audio_encoding); + } + return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL); } -static int saa6752hs_init(struct i2c_client* client) +static int saa6752hs_init(struct i2c_client *client, u32 leading_null_bytes) { unsigned char buf[9], buf2[4]; struct saa6752hs_state *h; + unsigned size; u32 crc; unsigned char localPAT[256]; unsigned char localPMT[256]; @@ -558,45 +669,31 @@ static int saa6752hs_init(struct i2c_client* client) h = i2c_get_clientdata(client); /* Set video format - must be done first as it resets other settings */ - buf[0] = 0x41; - buf[1] = h->video_format; - i2c_master_send(client, buf, 2); + set_reg8(client, 0x41, h->video_format); /* Set number of lines in input signal */ - buf[0] = 0x40; - buf[1] = 0x00; - if (h->standard & V4L2_STD_525_60) - buf[1] = 0x01; - i2c_master_send(client, buf, 2); + set_reg8(client, 0x40, (h->standard & V4L2_STD_525_60) ? 1 : 0); /* set bitrate */ - saa6752hs_set_bitrate(client, &h->params); + saa6752hs_set_bitrate(client, h); /* Set GOP structure {3, 13} */ - buf[0] = 0x72; - buf[1] = 0x03; - buf[2] = 0x0D; - i2c_master_send(client,buf,3); + set_reg16(client, 0x72, 0x030d); /* Set minimum Q-scale {4} */ - buf[0] = 0x82; - buf[1] = 0x04; - i2c_master_send(client,buf,2); + set_reg8(client, 0x82, 0x04); /* Set maximum Q-scale {12} */ - buf[0] = 0x83; - buf[1] = 0x0C; - i2c_master_send(client,buf,2); + set_reg8(client, 0x83, 0x0c); /* Set Output Protocol */ - buf[0] = 0xD0; - buf[1] = 0x81; - i2c_master_send(client,buf,2); + set_reg8(client, 0xd0, 0x81); /* Set video output stream format {TS} */ - buf[0] = 0xB0; - buf[1] = 0x05; - i2c_master_send(client,buf,2); + set_reg8(client, 0xb0, 0x05); + + /* Set leading null byte for TS */ + set_reg16(client, 0xf6, leading_null_bytes); /* compute PAT */ memcpy(localPAT, PAT, sizeof(PAT)); @@ -609,7 +706,13 @@ static int saa6752hs_init(struct i2c_client* client) localPAT[sizeof(PAT) - 1] = crc & 0xFF; /* compute PMT */ - memcpy(localPMT, PMT, sizeof(PMT)); + if (h->params.au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { + size = sizeof(PMT_AC3); + memcpy(localPMT, PMT_AC3, size); + } else { + size = sizeof(PMT); + memcpy(localPMT, PMT, size); + } localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f); localPMT[4] = h->params.ts_pid_pmt & 0xff; localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F); @@ -618,40 +721,28 @@ static int saa6752hs_init(struct i2c_client* client) localPMT[21] = h->params.ts_pid_video & 0xFF; localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F); localPMT[26] = h->params.ts_pid_audio & 0xFF; - crc = crc32_be(~0, &localPMT[7], sizeof(PMT) - 7 - 4); - localPMT[sizeof(PMT) - 4] = (crc >> 24) & 0xFF; - localPMT[sizeof(PMT) - 3] = (crc >> 16) & 0xFF; - localPMT[sizeof(PMT) - 2] = (crc >> 8) & 0xFF; - localPMT[sizeof(PMT) - 1] = crc & 0xFF; + crc = crc32_be(~0, &localPMT[7], size - 7 - 4); + localPMT[size - 4] = (crc >> 24) & 0xFF; + localPMT[size - 3] = (crc >> 16) & 0xFF; + localPMT[size - 2] = (crc >> 8) & 0xFF; + localPMT[size - 1] = crc & 0xFF; /* Set Audio PID */ - buf[0] = 0xC1; - buf[1] = (h->params.ts_pid_audio >> 8) & 0xFF; - buf[2] = h->params.ts_pid_audio & 0xFF; - i2c_master_send(client,buf,3); + set_reg16(client, 0xc1, h->params.ts_pid_audio); /* Set Video PID */ - buf[0] = 0xC0; - buf[1] = (h->params.ts_pid_video >> 8) & 0xFF; - buf[2] = h->params.ts_pid_video & 0xFF; - i2c_master_send(client,buf,3); + set_reg16(client, 0xc0, h->params.ts_pid_video); /* Set PCR PID */ - buf[0] = 0xC4; - buf[1] = (h->params.ts_pid_pcr >> 8) & 0xFF; - buf[2] = h->params.ts_pid_pcr & 0xFF; - i2c_master_send(client,buf,3); + set_reg16(client, 0xc4, h->params.ts_pid_pcr); /* Send SI tables */ - i2c_master_send(client,localPAT,sizeof(PAT)); - i2c_master_send(client,localPMT,sizeof(PMT)); + i2c_master_send(client, localPAT, sizeof(PAT)); + i2c_master_send(client, localPMT, size); /* mute then unmute audio. This removes buzzing artefacts */ - buf[0] = 0xa4; - buf[1] = 1; - i2c_master_send(client, buf, 2); - buf[1] = 0; - i2c_master_send(client, buf, 2); + set_reg8(client, 0xa4, 1); + set_reg8(client, 0xa4, 0); /* start it going */ saa6752hs_chip_command(client, SAA6752HS_COMMAND_START); @@ -689,45 +780,6 @@ static int saa6752hs_init(struct i2c_client* client) return 0; } -static int saa6752hs_attach(struct i2c_adapter *adap, int addr, int kind) -{ - struct saa6752hs_state *h; - - - if (NULL == (h = kzalloc(sizeof(*h), GFP_KERNEL))) - return -ENOMEM; - h->client = client_template; - h->params = param_defaults; - h->client.adapter = adap; - h->client.addr = addr; - - /* Assume 625 input lines */ - h->standard = 0; - - i2c_set_clientdata(&h->client, h); - i2c_attach_client(&h->client); - - v4l_info(&h->client,"saa6752hs: chip found @ 0x%x\n", addr<<1); - return 0; -} - -static int saa6752hs_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, saa6752hs_attach); - return 0; -} - -static int saa6752hs_detach(struct i2c_client *client) -{ - struct saa6752hs_state *h; - - h = i2c_get_clientdata(client); - i2c_detach_client(client); - kfree(h); - return 0; -} - static int saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) { @@ -738,14 +790,13 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) int i; switch (cmd) { + case VIDIOC_INT_INIT: + /* apply settings and start encoder */ + saa6752hs_init(client, *(u32 *)arg); + break; case VIDIOC_S_EXT_CTRLS: if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) return -EINVAL; - if (ctrls->count == 0) { - /* apply settings and start encoder */ - saa6752hs_init(client); - break; - } /* fall through */ case VIDIOC_TRY_EXT_CTRLS: case VIDIOC_G_EXT_CTRLS: @@ -753,7 +804,8 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) return -EINVAL; params = h->params; for (i = 0; i < ctrls->count; i++) { - if ((err = handle_ctrl(¶ms, ctrls->controls + i, cmd))) { + err = handle_ctrl(h->has_ac3, ¶ms, ctrls->controls + i, cmd); + if (err) { ctrls->error_idx = i; return err; } @@ -761,9 +813,9 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) h->params = params; break; case VIDIOC_QUERYCTRL: - return saa6752hs_qctrl(&h->params, arg); + return saa6752hs_qctrl(h, arg); case VIDIOC_QUERYMENU: - return saa6752hs_qmenu(&h->params, arg); + return saa6752hs_qmenu(h, arg); case VIDIOC_G_FMT: { struct v4l2_format *f = arg; @@ -786,6 +838,11 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) case VIDIOC_S_STD: h->standard = *((v4l2_std_id *) arg); break; + + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, + arg, h->chip, h->revision); + default: /* nothing */ break; @@ -794,36 +851,59 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) return err; } -/* ----------------------------------------------------------------------- */ +static int saa6752hs_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL); + u8 addr = 0x13; + u8 data[12]; -static struct i2c_driver driver = { - .driver = { - .name = "saa6752hs", - }, - .id = I2C_DRIVERID_SAA6752HS, - .attach_adapter = saa6752hs_probe, - .detach_client = saa6752hs_detach, - .command = saa6752hs_command, -}; + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + if (h == NULL) + return -ENOMEM; -static struct i2c_client client_template = -{ - .name = "saa6752hs", - .driver = &driver, -}; + i2c_master_send(client, &addr, 1); + i2c_master_recv(client, data, sizeof(data)); + h->chip = V4L2_IDENT_SAA6752HS; + h->revision = (data[8] << 8) | data[9]; + h->has_ac3 = 0; + if (h->revision == 0x0206) { + h->chip = V4L2_IDENT_SAA6752HS_AC3; + h->has_ac3 = 1; + v4l_info(client, "support AC-3\n"); + } + h->params = param_defaults; + h->standard = 0; /* Assume 625 input lines */ -static int __init saa6752hs_init_module(void) -{ - return i2c_add_driver(&driver); + i2c_set_clientdata(client, h); + return 0; } -static void __exit saa6752hs_cleanup_module(void) +static int saa6752hs_remove(struct i2c_client *client) { - i2c_del_driver(&driver); + kfree(i2c_get_clientdata(client)); + return 0; } -module_init(saa6752hs_init_module); -module_exit(saa6752hs_cleanup_module); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id saa6752hs_id[] = { + { "saa6752hs", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa6752hs_id); + +#endif +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "saa6752hs", + .driverid = I2C_DRIVERID_SAA6752HS, + .command = saa6752hs_command, + .probe = saa6752hs_probe, + .remove = saa6752hs_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = saa6752hs_id, +#endif +}; /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index e02c3c1b1..ce8bcc967 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -264,7 +264,7 @@ unsigned long saa7134_buffer_base(struct saa7134_buf *buf) int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt) { __le32 *cpu; - dma_addr_t dma_addr; + dma_addr_t dma_addr = 0; cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr); if (NULL == cpu) @@ -408,32 +408,6 @@ void saa7134_buffer_timeout(unsigned long data) spin_unlock_irqrestore(&dev->slock,flags); } -/* resends a current buffer in queue after resume */ - -static int saa7134_buffer_requeue(struct saa7134_dev *dev, - struct saa7134_dmaqueue *q) -{ - struct saa7134_buf *buf, *next; - - assert_spin_locked(&dev->slock); - - buf = q->curr; - next = buf; - dprintk("buffer_requeue\n"); - - if (!buf) - return 0; - - dprintk("buffer_requeue : resending active buffers \n"); - - if (!list_empty(&q->queue)) - next = list_entry(q->queue.next, struct saa7134_buf, - vb.queue); - buf->activate(dev, buf, next); - - return 0; -} - /* ------------------------------------------------------------------ */ int saa7134_set_dmabits(struct saa7134_dev *dev) @@ -491,9 +465,7 @@ int saa7134_set_dmabits(struct saa7134_dev *dev) /* TS capture -- dma 5 */ if (dev->ts_q.curr) { ctrl |= SAA7134_MAIN_CTRL_TE5; - irq |= SAA7134_IRQ1_INTE_RA2_3 | - SAA7134_IRQ1_INTE_RA2_2 | - SAA7134_IRQ1_INTE_RA2_1 | + irq |= SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0; } @@ -1203,6 +1175,32 @@ static void __devexit saa7134_finidev(struct pci_dev *pci_dev) } #ifdef CONFIG_PM + +/* resends a current buffer in queue after resume */ +static int saa7134_buffer_requeue(struct saa7134_dev *dev, + struct saa7134_dmaqueue *q) +{ + struct saa7134_buf *buf, *next; + + assert_spin_locked(&dev->slock); + + buf = q->curr; + next = buf; + dprintk("buffer_requeue\n"); + + if (!buf) + return 0; + + dprintk("buffer_requeue : resending active buffers \n"); + + if (!list_empty(&q->queue)) + next = list_entry(q->queue.next, struct saa7134_buf, + vb.queue); + buf->activate(dev, buf, next); + + return 0; +} + static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state) { diff --git a/linux/drivers/media/video/saa7134/saa7134-empress.c b/linux/drivers/media/video/saa7134/saa7134-empress.c index a454968f7..9ebe166cc 100644 --- a/linux/drivers/media/video/saa7134/saa7134-empress.c +++ b/linux/drivers/media/video/saa7134/saa7134-empress.c @@ -29,6 +29,7 @@ #include <media/saa6752hs.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> /* ------------------------------------------------------------------ */ @@ -63,10 +64,19 @@ static void ts_reset_encoder(struct saa7134_dev* dev) static int ts_init_encoder(struct saa7134_dev* dev) { - struct v4l2_ext_controls ctrls = { V4L2_CTRL_CLASS_MPEG, 0 }; + u32 leading_null_bytes = 0; + /* If more cards start to need this, then this + should probably be added to the card definitions. */ + switch (dev->board) { + case SAA7134_BOARD_BEHOLD_M6: + case SAA7134_BOARD_BEHOLD_M63: + case SAA7134_BOARD_BEHOLD_M6_EXTRA: + leading_null_bytes = 1; + break; + } ts_reset_encoder(dev); - saa7134_i2c_call_clients(dev, VIDIOC_S_EXT_CTRLS, &ctrls); + saa7134_i2c_call_clients(dev, VIDIOC_INT_INIT, &leading_null_bytes); dev->empress_started = 1; return 0; } @@ -293,15 +303,6 @@ static int empress_streamoff(struct file *file, void *priv, return videobuf_streamoff(&dev->empress_tsq); } -static int saa7134_i2c_call_saa6752(struct saa7134_dev *dev, - unsigned int cmd, void *arg) -{ - if (dev->mpeg_i2c_client == NULL) - return -EINVAL; - return dev->mpeg_i2c_client->driver->command(dev->mpeg_i2c_client, - cmd, arg); -} - static int empress_s_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls) { @@ -403,6 +404,39 @@ static int empress_querymenu(struct file *file, void *priv, return saa7134_i2c_call_saa6752(dev, VIDIOC_QUERYMENU, c); } +static int empress_g_chip_ident(struct file *file, void *fh, + struct v4l2_chip_ident *chip) +{ + struct saa7134_dev *dev = file->private_data; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (dev->mpeg_i2c_client == NULL) + return -EINVAL; + if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match_chip == I2C_DRIVERID_SAA6752HS) + return saa7134_i2c_call_saa6752(dev, VIDIOC_G_CHIP_IDENT, chip); + if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR && + chip->match_chip == dev->mpeg_i2c_client->addr) + return saa7134_i2c_call_saa6752(dev, VIDIOC_G_CHIP_IDENT, chip); + return -EINVAL; +} + +static int empress_s_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_dev *dev = file->private_data; + + return saa7134_s_std_internal(dev, NULL, id); +} + +static int empress_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_dev *dev = file->private_data; + + *id = dev->tvnorm->id; + return 0; +} + static const struct file_operations ts_fops = { .owner = THIS_MODULE, @@ -431,11 +465,13 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { .vidioc_enum_input = empress_enum_input, .vidioc_g_input = empress_g_input, .vidioc_s_input = empress_s_input, - .vidioc_queryctrl = empress_queryctrl, .vidioc_querymenu = empress_querymenu, .vidioc_g_ctrl = empress_g_ctrl, .vidioc_s_ctrl = empress_s_ctrl, + .vidioc_g_chip_ident = empress_g_chip_ident, + .vidioc_s_std = empress_s_std, + .vidioc_g_std = empress_g_std, }; /* ----------------------------------------------------------- */ diff --git a/linux/drivers/media/video/saa7134/saa7134-i2c.c b/linux/drivers/media/video/saa7134/saa7134-i2c.c index bdec276e0..2ca03df8c 100644 --- a/linux/drivers/media/video/saa7134/saa7134-i2c.c +++ b/linux/drivers/media/video/saa7134/saa7134-i2c.c @@ -432,6 +432,16 @@ void saa7134_i2c_call_clients(struct saa7134_dev *dev, i2c_clients_command(&dev->i2c_adap, cmd, arg); } +int saa7134_i2c_call_saa6752(struct saa7134_dev *dev, + unsigned int cmd, void *arg) +{ + if (dev->mpeg_i2c_client == NULL) + return -EINVAL; + return dev->mpeg_i2c_client->driver->command(dev->mpeg_i2c_client, + cmd, arg); +} +EXPORT_SYMBOL_GPL(saa7134_i2c_call_saa6752); + int saa7134_i2c_register(struct saa7134_dev *dev) { dev->i2c_adap = saa7134_adap_template; diff --git a/linux/drivers/media/video/saa7134/saa7134-ts.c b/linux/drivers/media/video/saa7134/saa7134-ts.c index eae72fd60..ef55a59f0 100644 --- a/linux/drivers/media/video/saa7134/saa7134-ts.c +++ b/linux/drivers/media/video/saa7134/saa7134-ts.c @@ -66,11 +66,29 @@ static int buffer_activate(struct saa7134_dev *dev, saa7134_set_dmabits(dev); mod_timer(&dev->ts_q.timeout, jiffies+BUFFER_TIMEOUT); + + if (dev->ts_state == SAA7134_TS_BUFF_DONE) { + /* Clear TS cache */ + dev->buff_cnt = 0; + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + saa_writeb(SAA7134_TS_SERIAL1, 0x03); + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + saa_writeb(SAA7134_TS_SERIAL1, 0x01); + + /* TS clock non-inverted */ + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + + /* Start TS stream */ + saa_writeb(SAA7134_TS_SERIAL0, 0x40); + saa_writeb(SAA7134_TS_PARALLEL, 0xEC); + dev->ts_state = SAA7134_TS_STARTED; + } + return 0; } static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) + enum v4l2_field field) { struct saa7134_dev *dev = q->priv_data; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); @@ -110,16 +128,22 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, goto oops; } - /* dma: setup channel 5 (= TS) */ - control = SAA7134_RS_CONTROL_BURST_16 | - SAA7134_RS_CONTROL_ME | - (buf->pt->dma >> 12); - - saa_writeb(SAA7134_TS_DMA0, ((lines-1)&0xff)); - saa_writeb(SAA7134_TS_DMA1, (((lines-1)>>8)&0xff)); - saa_writeb(SAA7134_TS_DMA2, ((((lines-1)>>16)&0x3f) | 0x00)); /* TSNOPIT=0, TSCOLAP=0 */ - saa_writel(SAA7134_RS_PITCH(5),TS_PACKET_SIZE); - saa_writel(SAA7134_RS_CONTROL(5),control); + dev->buff_cnt++; + + if (dev->buff_cnt == dev->ts.nr_bufs) { + dev->ts_state = SAA7134_TS_BUFF_DONE; + /* dma: setup channel 5 (= TS) */ + control = SAA7134_RS_CONTROL_BURST_16 | + SAA7134_RS_CONTROL_ME | + (buf->pt->dma >> 12); + + saa_writeb(SAA7134_TS_DMA0, (lines - 1) & 0xff); + saa_writeb(SAA7134_TS_DMA1, ((lines - 1) >> 8) & 0xff); + /* TSNOPIT=0, TSCOLAP=0 */ + saa_writeb(SAA7134_TS_DMA2, (((lines - 1) >> 16) & 0x3f) | 0x00); + saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE); + saa_writel(SAA7134_RS_CONTROL(5), control); + } buf->vb.state = VIDEOBUF_PREPARED; buf->activate = buffer_activate; @@ -140,6 +164,8 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) if (0 == *count) *count = dev->ts.nr_bufs; *count = saa7134_buffer_count(*size,*count); + dev->buff_cnt = 0; + dev->ts_state = SAA7134_TS_STOPPED; return 0; } @@ -154,7 +180,13 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) { struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + struct saa7134_dev *dev = q->priv_data; + if (dev->ts_state == SAA7134_TS_STARTED) { + /* Stop TS transport */ + saa_writeb(SAA7134_TS_PARALLEL, 0x6c); + dev->ts_state = SAA7134_TS_STOPPED; + } saa7134_dma_free(q,buf); } @@ -182,7 +214,7 @@ int saa7134_ts_init_hw(struct saa7134_dev *dev) /* deactivate TS softreset */ saa_writeb(SAA7134_TS_SERIAL1, 0x00); /* TSSOP high active, TSVAL high active, TSLOCK ignored */ - saa_writeb(SAA7134_TS_PARALLEL, 0xec); + saa_writeb(SAA7134_TS_PARALLEL, 0x6c); saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1)); saa_writeb(SAA7134_TS_DMA0, ((dev->ts.nr_packets-1)&0xff)); saa_writeb(SAA7134_TS_DMA1, (((dev->ts.nr_packets-1)>>8)&0xff)); diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index 547c0401a..241b60e3f 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -628,6 +628,9 @@ void saa7134_set_tvnorm_hw(struct saa7134_dev *dev) if (card_in(dev, dev->ctl_input).tv) saa7134_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id); + /* Set the correct norm for the saa6752hs. This function + does nothing if there is no saa6752hs. */ + saa7134_i2c_call_saa6752(dev, VIDIOC_S_STD, &dev->tvnorm->id); } static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale) @@ -1803,18 +1806,25 @@ static int saa7134_querycap(struct file *file, void *priv, return 0; } -static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id) +int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id) { - struct saa7134_fh *fh = priv; - struct saa7134_dev *dev = fh->dev; unsigned long flags; unsigned int i; v4l2_std_id fixup; int err; - err = v4l2_prio_check(&dev->prio, &fh->prio); - if (0 != err) - return err; + /* When called from the empress code fh == NULL. + That needs to be fixed somehow, but for now this is + good enough. */ + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } else if (res_locked(dev, RESOURCE_OVERLAY)) { + /* Don't change the std from the mpeg device + if overlay is active. */ + return -EBUSY; + } for (i = 0; i < TVNORMS; i++) if (*id == tvnorms[i].id) @@ -1847,7 +1857,7 @@ static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id) *id = tvnorms[i].id; mutex_lock(&dev->lock); - if (res_check(fh, RESOURCE_OVERLAY)) { + if (fh && res_check(fh, RESOURCE_OVERLAY)) { spin_lock_irqsave(&dev->slock, flags); stop_preview(dev, fh); spin_unlock_irqrestore(&dev->slock, flags); @@ -1864,6 +1874,23 @@ static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id) mutex_unlock(&dev->lock); return 0; } +EXPORT_SYMBOL_GPL(saa7134_s_std_internal); + +static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_fh *fh = priv; + + return saa7134_s_std_internal(fh->dev, fh, id); +} + +static int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + *id = dev->tvnorm->id; + return 0; +} static int saa7134_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) @@ -2404,6 +2431,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_qbuf = saa7134_qbuf, .vidioc_dqbuf = saa7134_dqbuf, .vidioc_s_std = saa7134_s_std, + .vidioc_g_std = saa7134_g_std, .vidioc_enum_input = saa7134_enum_input, .vidioc_g_input = saa7134_g_input, .vidioc_s_input = saa7134_s_input, diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index e3ad9d9e7..50319493d 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -467,6 +467,12 @@ struct saa7134_mpeg_ops { void (*signal_change)(struct saa7134_dev *dev); }; +enum saa7134_ts_status { + SAA7134_TS_STOPPED, + SAA7134_TS_BUFF_DONE, + SAA7134_TS_STARTED, +}; + /* global device status */ struct saa7134_dev { struct list_head devlist; @@ -560,6 +566,8 @@ struct saa7134_dev { /* SAA7134_MPEG_* */ struct saa7134_ts ts; struct saa7134_dmaqueue ts_q; + enum saa7134_ts_status ts_state; + unsigned int buff_cnt; struct saa7134_mpeg_ops *mops; struct i2c_client *mpeg_i2c_client; @@ -659,6 +667,8 @@ int saa7134_i2c_register(struct saa7134_dev *dev); int saa7134_i2c_unregister(struct saa7134_dev *dev); void saa7134_i2c_call_clients(struct saa7134_dev *dev, unsigned int cmd, void *arg); +int saa7134_i2c_call_saa6752(struct saa7134_dev *dev, + unsigned int cmd, void *arg); /* ----------------------------------------------------------- */ @@ -671,6 +681,7 @@ extern struct video_device saa7134_radio_template; int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c); +int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id); int saa7134_videoport_init(struct saa7134_dev *dev); void saa7134_set_tvnorm_hw(struct saa7134_dev *dev); diff --git a/linux/drivers/media/video/se401.c b/linux/drivers/media/video/se401.c index 04897413d..4b55b7dd7 100644 --- a/linux/drivers/media/video/se401.c +++ b/linux/drivers/media/video/se401.c @@ -1244,6 +1244,7 @@ static const struct file_operations se401_fops = { static struct video_device se401_template = { .name = "se401 USB camera", .fops = &se401_fops, + .release = video_device_release_empty, }; @@ -1409,7 +1410,7 @@ static int se401_probe(struct usb_interface *intf, mutex_init(&se401->lock); wmb(); - if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { kfree(se401); err("video_register_device failed"); return -EIO; diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c index 318754e73..76838091d 100644 --- a/linux/drivers/media/video/sh_mobile_ceu_camera.c +++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c @@ -304,9 +304,6 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) "SuperH Mobile CEU driver attached to camera %d\n", icd->devnum); - if (pcdev->pdata->enable_camera) - pcdev->pdata->enable_camera(); - ret = icd->ops->init(icd); if (ret) goto err; @@ -333,8 +330,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) ceu_write(pcdev, CEIER, 0); ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ icd->ops->release(icd); - if (pcdev->pdata->disable_camera) - pcdev->pdata->disable_camera(); dev_info(&icd->dev, "SuperH Mobile CEU driver detached from camera %d\n", diff --git a/linux/drivers/media/video/sn9c102/sn9c102_core.c b/linux/drivers/media/video/sn9c102/sn9c102_core.c index e92e79da3..fd867dadf 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_core.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_core.c @@ -116,6 +116,26 @@ MODULE_PARM_DESC(debug, "\n"); #endif +/* + Add the probe entries to this table. Be sure to add the entry in the right + place, since, on failure, the next probing routine is called according to + the order of the list below, from top to bottom. +*/ +static int (*sn9c102_sensor_table[])(struct sn9c102_device *) = { + &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ + &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ + &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ +}; + /*****************************************************************************/ static u32 @@ -1750,7 +1770,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp) if (!down_read_trylock(&sn9c102_dev_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); if (wait_for_completion_interruptible(&cam->probe)) { up_read(&sn9c102_dev_lock); @@ -1847,7 +1867,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp) down_write(&sn9c102_dev_lock); - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); sn9c102_stop_transfer(cam); sn9c102_release_buffers(cam); @@ -1867,7 +1887,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp) static ssize_t sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) { - struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_device *cam = video_drvdata(filp); struct sn9c102_frame_t* f, * i; unsigned long lock_flags; long timeout; @@ -1991,7 +2011,7 @@ exit: static unsigned int sn9c102_poll(struct file *filp, poll_table *wait) { - struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_device *cam = video_drvdata(filp); struct sn9c102_frame_t* f; unsigned long lock_flags; unsigned int mask = 0; @@ -2067,7 +2087,7 @@ static struct vm_operations_struct sn9c102_vm_ops = { static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) { - struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_device *cam = video_drvdata(filp); unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start; void *pos; @@ -3079,7 +3099,7 @@ sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg) static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg) { - struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_device *cam = video_drvdata(filp); switch (cmd) { @@ -3183,7 +3203,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, static int sn9c102_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg) { - struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_device *cam = video_drvdata(filp); int err = 0; if (mutex_lock_interruptible(&cam->fileop_mutex)) @@ -3316,6 +3336,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->fops = &sn9c102_fops; cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; + cam->v4ldev->parent = &udev->dev; init_completion(&cam->probe); diff --git a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h index 6ff489baa..e23734f6d 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -40,11 +40,14 @@ struct sn9c102_device; static const struct usb_device_id sn9c102_id_table[] = { /* SN9C101 and SN9C102 */ +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, @@ -53,29 +56,33 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, /* SN9C103 */ { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, HY7131D/E */ { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, CISVF10 */ +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, - { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, PAS106 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, TAS5130 */ +/* { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, TAS5130 */ { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, @@ -105,7 +112,7 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, +/* { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, MO8000 */ { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, @@ -133,24 +140,4 @@ extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam); extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); -/* - Add the above entries to this table. Be sure to add the entry in the right - place, since, on failure, the next probing routine is called according to - the order of the list below, from top to bottom. -*/ -static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { - &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ - &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ - &sn9c102_probe_mt9v111, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ - &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ - &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ - &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ - &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ -}; - #endif /* _SN9C102_DEVTABLE_H_ */ diff --git a/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c index eaf9ad0dc..db2434948 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_hv7131d.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int hv7131d_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_hv7131r.c b/linux/drivers/media/video/sn9c102/sn9c102_hv7131r.c index 0fc401223..4295887ff 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_hv7131r.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_hv7131r.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int hv7131r_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c b/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c index 00b134ca0..1f5b09bec 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_mi0343.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int mi0343_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_mi0360.c b/linux/drivers/media/video/sn9c102/sn9c102_mi0360.c index f8d81d82e..d973fc197 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_mi0360.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_mi0360.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int mi0360_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_mt9v111.c b/linux/drivers/media/video/sn9c102/sn9c102_mt9v111.c index 3b98ac3bb..95986eb49 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_mt9v111.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_mt9v111.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int mt9v111_init(struct sn9c102_device *cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c b/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c index e4856fd77..803712c29 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_ov7630.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int ov7630_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_ov7660.c b/linux/drivers/media/video/sn9c102/sn9c102_ov7660.c index 8aae416ba..7977795d3 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_ov7660.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_ov7660.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int ov7660_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c b/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c index 360f2a848..81cd969c1 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_pas106b.c @@ -21,6 +21,7 @@ #include <linux/delay.h> #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int pas106b_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c index ca4a1506e..2782f94cf 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_pas202bcb.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int pas202bcb_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c index e7d2de2ba..04cdfdde8 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int tas5110c1b_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_tas5110d.c b/linux/drivers/media/video/sn9c102/sn9c102_tas5110d.c index d32fdbccd..9372e6f9f 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_tas5110d.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_tas5110d.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int tas5110d_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c index 56fb1d575..a30bbc438 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c @@ -20,6 +20,7 @@ ***************************************************************************/ #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" static int tas5130d1b_init(struct sn9c102_device* cam) diff --git a/linux/drivers/media/video/stk-webcam.c b/linux/drivers/media/video/stk-webcam.c index c6660b363..055e92549 100644 --- a/linux/drivers/media/video/stk-webcam.c +++ b/linux/drivers/media/video/stk-webcam.c @@ -73,7 +73,7 @@ static void stk_camera_cleanup(struct kref *kref) STK_INFO("Syntek USB2.0 Camera release resources" " video device /dev/video%d\n", dev->vdev.minor); video_unregister_device(&dev->vdev); - dev->vdev.priv = NULL; + video_set_drvdata(&dev->vdev, NULL); if (dev->sio_bufs != NULL || dev->isobufs != NULL) STK_ERROR("We are leaking memory\n"); @@ -1380,7 +1380,7 @@ static int stk_register_video_device(struct stk_camera *dev) dev->vdev = stk_v4l_data; dev->vdev.debug = debug; dev->vdev.parent = &dev->interface->dev; - dev->vdev.priv = dev; + video_set_drvdata(&dev->vdev, dev); err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); if (err) STK_ERROR("v4l registration failed\n"); diff --git a/linux/drivers/media/video/stradis.c b/linux/drivers/media/video/stradis.c index a081307c7..0e2b246c7 100644 --- a/linux/drivers/media/video/stradis.c +++ b/linux/drivers/media/video/stradis.c @@ -1930,6 +1930,7 @@ static struct video_device saa_template = { .name = "SAA7146A", .fops = &saa_fops, .minor = -1, + .release = video_device_release_empty, }; static int __devinit configure_saa7146(struct pci_dev *pdev, int num) diff --git a/linux/drivers/media/video/stv680.c b/linux/drivers/media/video/stv680.c index de32a5266..8df48a547 100644 --- a/linux/drivers/media/video/stv680.c +++ b/linux/drivers/media/video/stv680.c @@ -1469,7 +1469,7 @@ static int stv680_probe (struct usb_interface *intf, const struct usb_device_id mutex_init (&stv680->lock); wmb (); - if (video_register_device (stv680->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + if (video_register_device(stv680->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { PDEBUG (0, "STV(e): video_register_device failed"); retval = -EIO; goto error_vdev; diff --git a/linux/drivers/media/video/tda9840.c b/linux/drivers/media/video/tda9840.c index f4ebfd84b..72adcb450 100644 --- a/linux/drivers/media/video/tda9840.c +++ b/linux/drivers/media/video/tda9840.c @@ -2,6 +2,7 @@ tda9840 - i2c-driver for the tda9840 by SGS Thomson Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> The tda9840 is a stereo/dual sound processor with digital identification. It can be found at address 0x84 on the i2c-bus. @@ -28,60 +29,120 @@ #include <linux/module.h> #include <linux/ioctl.h> #include <linux/i2c.h> +#include <media/v4l2-common.h> +#include <media/v4l2-i2c-drv-legacy.h> +#include "tda9840.h" #include "compat.h" -#include "tda9840.h" +MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); +MODULE_DESCRIPTION("tda9840 driver"); +MODULE_LICENSE("GPL"); -static int debug; /* insmod parameter */ +static int debug; 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]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) +MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define SWITCH 0x00 #define LEVEL_ADJUST 0x02 #define STEREO_ADJUST 0x03 #define TEST 0x04 +#define TDA9840_SET_MUTE 0x00 +#define TDA9840_SET_MONO 0x10 +#define TDA9840_SET_STEREO 0x2a +#define TDA9840_SET_LANG1 0x12 +#define TDA9840_SET_LANG2 0x1e +#define TDA9840_SET_BOTH 0x1a +#define TDA9840_SET_BOTH_R 0x16 +#define TDA9840_SET_EXTERNAL 0x7a + /* addresses to scan, found only at 0x42 (7-Bit) */ static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END }; /* magic definition of all other variables and things */ I2C_CLIENT_INSMOD; -static struct i2c_driver driver; -static struct i2c_client client_template; +static void tda9840_write(struct i2c_client *client, u8 reg, u8 val) +{ + if (i2c_smbus_write_byte_data(client, reg, val)) + v4l_dbg(1, debug, client, "error writing %02x to %02x\n", + val, reg); +} -static int command(struct i2c_client *client, unsigned int cmd, void *arg) +static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) { - int result; + int result = 0; int byte = *(int *)arg; switch (cmd) { - case TDA9840_SWITCH: - - dprintk("TDA9840_SWITCH: 0x%02x\n", byte); - - if (byte != TDA9840_SET_MONO - && byte != TDA9840_SET_MUTE - && byte != TDA9840_SET_STEREO - && byte != TDA9840_SET_LANG1 - && byte != TDA9840_SET_LANG2 - && byte != TDA9840_SET_BOTH - && byte != TDA9840_SET_BOTH_R - && byte != TDA9840_SET_EXTERNAL) { + case VIDIOC_S_TUNER: { + struct v4l2_tuner *t = arg; + int byte; + + if (t->index) return -EINVAL; + + switch (t->audmode) { + case V4L2_TUNER_MODE_STEREO: + byte = TDA9840_SET_STEREO; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + byte = TDA9840_SET_BOTH; + break; + case V4L2_TUNER_MODE_LANG1: + byte = TDA9840_SET_LANG1; + break; + case V4L2_TUNER_MODE_LANG2: + byte = TDA9840_SET_LANG2; + break; + default: + byte = TDA9840_SET_MONO; + break; } + v4l_dbg(1, debug, client, "TDA9840_SWITCH: 0x%02x\n", byte); + tda9840_write(client, SWITCH, byte); + break; + } + + case VIDIOC_G_TUNER: { + struct v4l2_tuner *t = arg; + u8 byte; - result = i2c_smbus_write_byte_data(client, SWITCH, byte); - if (result) - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); + t->rxsubchans = V4L2_TUNER_SUB_MONO; + if (1 != i2c_master_recv(client, &byte, 1)) { + v4l_dbg(1, debug, client, + "i2c_master_recv() failed\n"); + return -EIO; + } + + if (byte & 0x80) { + v4l_dbg(1, debug, client, + "TDA9840_DETECT: register contents invalid\n"); + return -EINVAL; + } + + v4l_dbg(1, debug, client, "TDA9840_DETECT: byte: 0x%02x\n", byte); + + switch (byte & 0x60) { + case 0x00: + t->rxsubchans = V4L2_TUNER_SUB_MONO; + break; + case 0x20: + t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + break; + case 0x40: + t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; + break; + default: /* Incorrect detect */ + t->rxsubchans = V4L2_TUNER_MODE_MONO; + break; + } break; + } case TDA9840_LEVEL_ADJUST: - - dprintk("TDA9840_LEVEL_ADJUST: %d\n", byte); + v4l_dbg(1, debug, client, "TDA9840_LEVEL_ADJUST: %d\n", byte); /* check for correct range */ if (byte > 25 || byte < -20) @@ -93,15 +154,11 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) byte += 0x8; else byte = -byte; - - result = i2c_smbus_write_byte_data(client, LEVEL_ADJUST, byte); - if (result) - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); + tda9840_write(client, LEVEL_ADJUST, byte); break; case TDA9840_STEREO_ADJUST: - - dprintk("TDA9840_STEREO_ADJUST: %d\n", byte); + v4l_dbg(1, debug, client, "TDA9840_STEREO_ADJUST: %d\n", byte); /* check for correct range */ if (byte > 25 || byte < -24) @@ -114,39 +171,7 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) else byte = -byte; - result = i2c_smbus_write_byte_data(client, STEREO_ADJUST, byte); - if (result) - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); - break; - - case TDA9840_DETECT: { - int *ret = (int *)arg; - - byte = i2c_smbus_read_byte_data(client, STEREO_ADJUST); - if (byte == -1) { - dprintk("i2c_smbus_read_byte_data() failed\n"); - return -EIO; - } - - if (0 != (byte & 0x80)) { - dprintk("TDA9840_DETECT: register contents invalid\n"); - return -EINVAL; - } - - dprintk("TDA9840_DETECT: byte: 0x%02x\n", byte); - *ret = ((byte & 0x60) >> 5); - result = 0; - break; - } - case TDA9840_TEST: - dprintk("TDA9840_TEST: 0x%02x\n", byte); - - /* mask out irrelevant bits */ - byte &= 0x3; - - result = i2c_smbus_write_byte_data(client, TEST, byte); - if (result) - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", result); + tda9840_write(client, STEREO_ADJUST, byte); break; default: return -ENOIOCTLCMD; @@ -158,99 +183,54 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } -static int detect(struct i2c_adapter *adapter, int address, int kind) +static int tda9840_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct i2c_client *client; - int result = 0; - - int byte = 0x0; + int result; + int byte; /* let's see whether this adapter can support what we need */ - if (0 == i2c_check_functionality(adapter, - I2C_FUNC_SMBUS_READ_BYTE_DATA | - I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) { + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) return 0; - } - - /* allocate memory for client structure */ - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!client) { - printk("not enough kernel memory\n"); - return -ENOMEM; - } - - /* fill client structure */ - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->addr = address; - client->adapter = adapter; - /* tell the i2c layer a new client has arrived */ - if (0 != (result = i2c_attach_client(client))) { - kfree(client); - return result; - } + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); /* set initial values for level & stereo - adjustment, mode */ byte = 0; - result = command(client, TDA9840_LEVEL_ADJUST, &byte); - result += command(client, TDA9840_STEREO_ADJUST, &byte); - byte = TDA9840_SET_MONO; - result = command(client, TDA9840_SWITCH, &byte); + result = tda9840_command(client, TDA9840_LEVEL_ADJUST, &byte); + result += tda9840_command(client, TDA9840_STEREO_ADJUST, &byte); + tda9840_write(client, SWITCH, TDA9840_SET_STEREO); if (result) { - dprintk("could not initialize tda9840\n"); + v4l_dbg(1, debug, client, "could not initialize tda9840\n"); return -ENODEV; } - - printk("tda9840: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]); return 0; } -static int attach(struct i2c_adapter *adapter) +static int tda9840_legacy_probe(struct i2c_adapter *adapter) { - /* let's see whether this is a know adapter we can attach to */ - if (adapter->id != I2C_HW_SAA7146) { - dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id); - return -ENODEV; - } - - return i2c_probe(adapter, &addr_data, &detect); -} - -static int detach(struct i2c_client *client) -{ - int ret = i2c_detach_client(client); - kfree(client); - return ret; + /* Let's see whether this is a known adapter we can attach to. + Prevents conflicts with tvaudio.c. */ + return adapter->id == I2C_HW_SAA7146; } - -static struct i2c_driver driver = { - .driver = { - .name = "tda9840", - }, - .id = I2C_DRIVERID_TDA9840, - .attach_adapter = attach, - .detach_client = detach, - .command = command, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id tda9840_id[] = { + { "tda9840", 0 }, + { } }; +MODULE_DEVICE_TABLE(i2c, tda9840_id); +#endif -static struct i2c_client client_template = { +static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tda9840", - .driver = &driver, + .driverid = I2C_DRIVERID_TDA9840, + .command = tda9840_command, + .probe = tda9840_probe, + .legacy_probe = tda9840_legacy_probe, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = tda9840_id, +#endif }; - -static int __init this_module_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit this_module_exit(void) -{ - i2c_del_driver(&driver); -} - -module_init(this_module_init); -module_exit(this_module_exit); - -MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); -MODULE_DESCRIPTION("tda9840 driver"); -MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/tda9840.h b/linux/drivers/media/video/tda9840.h index 7da8432cd..dc12ae7ca 100644 --- a/linux/drivers/media/video/tda9840.h +++ b/linux/drivers/media/video/tda9840.h @@ -3,24 +3,6 @@ #define I2C_ADDR_TDA9840 0x42 -#define TDA9840_DETECT _IOR('v',1,int) -/* return values for TDA9840_DETCT */ -#define TDA9840_MONO_DETECT 0x0 -#define TDA9840_DUAL_DETECT 0x1 -#define TDA9840_STEREO_DETECT 0x2 -#define TDA9840_INCORRECT_DETECT 0x3 - -#define TDA9840_SWITCH _IOW('v',2,int) -/* modes than can be set with TDA9840_SWITCH */ -#define TDA9840_SET_MUTE 0x00 -#define TDA9840_SET_MONO 0x10 -#define TDA9840_SET_STEREO 0x2a -#define TDA9840_SET_LANG1 0x12 -#define TDA9840_SET_LANG2 0x1e -#define TDA9840_SET_BOTH 0x1a -#define TDA9840_SET_BOTH_R 0x16 -#define TDA9840_SET_EXTERNAL 0x7a - /* values may range between +2.5 and -2.0; the value has to be multiplied with 10 */ #define TDA9840_LEVEL_ADJUST _IOW('v',3,int) @@ -29,7 +11,4 @@ the value has to be multiplied with 10 */ #define TDA9840_STEREO_ADJUST _IOW('v',4,int) -/* currently not implemented */ -#define TDA9840_TEST _IOW('v',5,int) - #endif diff --git a/linux/drivers/media/video/tea6415c.c b/linux/drivers/media/video/tea6415c.c index da654bd15..073f020fa 100644 --- a/linux/drivers/media/video/tea6415c.c +++ b/linux/drivers/media/video/tea6415c.c @@ -2,6 +2,7 @@ tea6415c - i2c-driver for the tea6415c by SGS Thomson Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> The tea6415c is a bus controlled video-matrix-switch with 8 inputs and 6 outputs. @@ -30,19 +31,19 @@ #include <linux/module.h> #include <linux/ioctl.h> #include <linux/i2c.h> +#include <media/v4l2-common.h> +#include <media/v4l2-i2c-drv-legacy.h> +#include "tea6415c.h" #include "compat.h" -#include "tea6415c.h" +MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); +MODULE_DESCRIPTION("tea6415c driver"); +MODULE_LICENSE("GPL"); -static int debug; /* insmod parameter */ +static int debug; 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]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) -#define TEA6415C_NUM_INPUTS 8 -#define TEA6415C_NUM_OUTPUTS 6 +MODULE_PARM_DESC(debug, "Debug level (0-1)"); /* addresses to scan, found only at 0x03 and/or 0x43 (7-bit) */ static unsigned short normal_i2c[] = { I2C_TEA6415C_1, I2C_TEA6415C_2, I2C_CLIENT_END }; @@ -50,60 +51,6 @@ static unsigned short normal_i2c[] = { I2C_TEA6415C_1, I2C_TEA6415C_2, I2C_CLIEN /* magic definition of all other variables and things */ I2C_CLIENT_INSMOD; -static struct i2c_driver driver; -static struct i2c_client client_template; - -/* this function is called by i2c_probe */ -static int detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *client = NULL; - int err = 0; - - /* let's see whether this adapter can support what we need */ - if (0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) { - return 0; - } - - /* allocate memory for client structure */ - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!client) { - return -ENOMEM; - } - - /* fill client structure */ - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->addr = address; - client->adapter = adapter; - - /* tell the i2c layer a new client has arrived */ - if (0 != (err = i2c_attach_client(client))) { - kfree(client); - return err; - } - - printk("tea6415c: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]); - - return 0; -} - -static int attach(struct i2c_adapter *adapter) -{ - /* let's see whether this is a know adapter we can attach to */ - if (adapter->id != I2C_HW_SAA7146) { - dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id); - return -ENODEV; - } - - return i2c_probe(adapter, &addr_data, &detect); -} - -static int detach(struct i2c_client *client) -{ - int ret = i2c_detach_client(client); - kfree(client); - return ret; -} - /* makes a connection between the input-pin 'i' and the output-pin 'o' for the tea6415c-client 'client' */ static int switch_matrix(struct i2c_client *client, int i, int o) @@ -111,7 +58,7 @@ static int switch_matrix(struct i2c_client *client, int i, int o) u8 byte = 0; int ret; - dprintk("adr:0x%02x, i:%d, o:%d\n", client->addr, i, o); + v4l_dbg(1, debug, client, "i=%d, o=%d\n", i, o); /* check if the pins are valid */ if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i) @@ -169,14 +116,14 @@ static int switch_matrix(struct i2c_client *client, int i, int o) ret = i2c_smbus_write_byte(client, byte); if (ret) { - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", ret); + v4l_dbg(1, debug, client, + "i2c_smbus_write_byte() failed, ret:%d\n", ret); return -EIO; } - return ret; } -static int command(struct i2c_client *client, unsigned int cmd, void *arg) +static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg) { struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg; int result = 0; @@ -188,38 +135,44 @@ static int command(struct i2c_client *client, unsigned int cmd, void *arg) default: return -ENOIOCTLCMD; } - return result; } -static struct i2c_driver driver = { - .driver = { - .name = "tea6415c", - }, - .id = I2C_DRIVERID_TEA6415C, - .attach_adapter = attach, - .detach_client = detach, - .command = command, -}; - -static struct i2c_client client_template = { - .name = "tea6415c", - .driver = &driver, -}; - -static int __init this_module_init(void) +/* this function is called by i2c_probe */ +static int tea6415c_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - return i2c_add_driver(&driver); + /* let's see whether this adapter can support what we need */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) + return 0; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + return 0; } -static void __exit this_module_exit(void) +static int tea6415c_legacy_probe(struct i2c_adapter *adapter) { - i2c_del_driver(&driver); + /* Let's see whether this is a known adapter we can attach to. + Prevents conflicts with tvaudio.c. */ + return adapter->id == I2C_HW_SAA7146; } -module_init(this_module_init); -module_exit(this_module_exit); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id tea6415c_id[] = { + { "tea6415c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tea6415c_id); +#endif -MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); -MODULE_DESCRIPTION("tea6415c driver"); -MODULE_LICENSE("GPL"); +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "tea6415c", + .driverid = I2C_DRIVERID_TEA6415C, + .command = tea6415c_command, + .probe = tea6415c_probe, + .legacy_probe = tea6415c_legacy_probe, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = tea6415c_id, +#endif +}; diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c index d0c63703c..8ded72ade 100644 --- a/linux/drivers/media/video/tea6420.c +++ b/linux/drivers/media/video/tea6420.c @@ -2,6 +2,7 @@ tea6420 - i2c-driver for the tea6420 by SGS Thomson Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de> + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> The tea6420 is a bus controlled audio-matrix with 5 stereo inputs, 4 stereo outputs and gain control for each output. @@ -30,16 +31,19 @@ #include <linux/module.h> #include <linux/ioctl.h> #include <linux/i2c.h> +#include <media/v4l2-common.h> +#include <media/v4l2-i2c-drv-legacy.h> +#include "tea6420.h" #include "compat.h" -#include "tea6420.h" +MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); +MODULE_DESCRIPTION("tea6420 driver"); +MODULE_LICENSE("GPL"); -static int debug; /* insmod parameter */ +static int debug; 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]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) +MODULE_PARM_DESC(debug, "Debug level (0-1)"); /* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */ static unsigned short normal_i2c[] = { I2C_ADDR_TEA6420_1, I2C_ADDR_TEA6420_2, I2C_CLIENT_END }; @@ -47,23 +51,20 @@ static unsigned short normal_i2c[] = { I2C_ADDR_TEA6420_1, I2C_ADDR_TEA6420_2, I /* magic definition of all other variables and things */ I2C_CLIENT_INSMOD; -static struct i2c_driver driver; -static struct i2c_client client_template; - /* make a connection between the input 'i' and the output 'o' with gain 'g' for the tea6420-client 'client' (note: i = 6 means 'mute') */ static int tea6420_switch(struct i2c_client *client, int i, int o, int g) { - u8 byte = 0; + u8 byte; int ret; - dprintk("adr:0x%02x, i:%d, o:%d, g:%d\n", client->addr, i, o, g); + v4l_dbg(1, debug, client, "i=%d, o=%d, g=%d\n", i, o, g); /* check if the parameters are valid */ if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0) return -1; - byte = ((o - 1) << 5); + byte = ((o - 1) << 5); byte |= (i - 1); /* to understand this, have a look at the tea6420-specs (p.5) */ @@ -83,40 +84,41 @@ static int tea6420_switch(struct i2c_client *client, int i, int o, int g) ret = i2c_smbus_write_byte(client, byte); if (ret) { - dprintk("i2c_smbus_write_byte() failed, ret:%d\n", ret); + v4l_dbg(1, debug, client, + "i2c_smbus_write_byte() failed, ret:%d\n", ret); return -EIO; } - return 0; } -/* this function is called by i2c_probe */ -static int tea6420_detect(struct i2c_adapter *adapter, int address, int kind) +static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg) { - struct i2c_client *client; - int err = 0, i = 0; + struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg; + int result = 0; - /* let's see whether this adapter can support what we need */ - if (0 == i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) { - return 0; + switch (cmd) { + case TEA6420_SWITCH: + result = tea6420_switch(client, a->in, a->out, a->gain); + break; + default: + return -ENOIOCTLCMD; } - /* allocate memory for client structure */ - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!client) { - return -ENOMEM; - } + return result; +} - /* fill client structure */ - memcpy(client, &client_template, sizeof(struct i2c_client)); - client->addr = address; - client->adapter = adapter; +/* this function is called by i2c_probe */ +static int tea6420_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err, i; - /* tell the i2c layer a new client has arrived */ - if (0 != (err = i2c_attach_client(client))) { - kfree(client); - return err; - } + /* let's see whether this adapter can support what we need */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EIO; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); /* set initial values: set "mute"-input to all outputs at gain 0 */ err = 0; @@ -124,78 +126,35 @@ static int tea6420_detect(struct i2c_adapter *adapter, int address, int kind) err += tea6420_switch(client, 6, i, 0); } if (err) { - dprintk("could not initialize tea6420\n"); + v4l_dbg(1, debug, client, "could not initialize tea6420\n"); kfree(client); return -ENODEV; } - - printk("tea6420: detected @ 0x%02x on adapter %s\n", address, &client->adapter->name[0]); - return 0; } -static int attach(struct i2c_adapter *adapter) +static int tea6420_legacy_probe(struct i2c_adapter *adapter) { - /* let's see whether this is a know adapter we can attach to */ - if (adapter->id != I2C_HW_SAA7146) { - dprintk("refusing to probe on unknown adapter [name='%s',id=0x%x]\n", adapter->name, adapter->id); - return -ENODEV; - } - - return i2c_probe(adapter, &addr_data, &tea6420_detect); -} - -static int detach(struct i2c_client *client) -{ - int ret = i2c_detach_client(client); - kfree(client); - return ret; -} - -static int command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg; - int result = 0; - - switch (cmd) { - case TEA6420_SWITCH: - result = tea6420_switch(client, a->in, a->out, a->gain); - break; - default: - return -ENOIOCTLCMD; - } - - return result; + /* Let's see whether this is a known adapter we can attach to. + Prevents conflicts with tvaudio.c. */ + return adapter->id == I2C_HW_SAA7146; } -static struct i2c_driver driver = { - .driver = { - .name = "tea6420", - }, - .id = I2C_DRIVERID_TEA6420, - .attach_adapter = attach, - .detach_client = detach, - .command = command, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id tea6420_id[] = { + { "tea6420", 0 }, + { } }; +MODULE_DEVICE_TABLE(i2c, tea6420_id); +#endif -static struct i2c_client client_template = { +static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tea6420", - .driver = &driver, + .driverid = I2C_DRIVERID_TEA6420, + .command = tea6420_command, + .probe = tea6420_probe, + .legacy_probe = tea6420_legacy_probe, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = tea6420_id, +#endif }; - -static int __init this_module_init(void) -{ - return i2c_add_driver(&driver); -} - -static void __exit this_module_exit(void) -{ - i2c_del_driver(&driver); -} - -module_init(this_module_init); -module_exit(this_module_exit); - -MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); -MODULE_DESCRIPTION("tea6420 driver"); -MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/video/tuner-3036.c b/linux/drivers/media/video/tuner-3036.c deleted file mode 100644 index 8b87d75f4..000000000 --- a/linux/drivers/media/video/tuner-3036.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Driver for Philips SAB3036 "CITAC" tuner control chip. - * - * Author: Phil Blundell <philb@gnu.org> - * - * The SAB3036 is just about different enough from the chips that - * tuner.c copes with to make it not worth the effort to crowbar - * the support into that file. So instead we have a separate driver. - * - * 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. - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/init.h> - -#include <linux/i2c.h> -#include "compat.h" -#include <linux/videodev.h> -#include <media/v4l2-common.h> - -#include <media/tuner.h> - -static int debug; /* insmod parameter */ -static int this_adap; - -static struct i2c_client client_template; - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x60, 0x61, I2C_CLIENT_END }; -static unsigned short ignore = I2C_CLIENT_END; - -static struct i2c_client_address_data addr_data = { - .normal_i2c = normal_i2c, - .probe = &ignore, - .ignore = &ignore, -}; - -/* ---------------------------------------------------------------------- */ - -static unsigned char -tuner_getstatus (struct i2c_client *c) -{ - unsigned char byte; - if (i2c_master_recv(c, &byte, 1) != 1) - printk(KERN_ERR "tuner-3036: I/O error.\n"); - return byte; -} - -#define TUNER_FL 0x80 - -static int -tuner_islocked (struct i2c_client *c) -{ - return (tuner_getstatus(c) & TUNER_FL); -} - -/* ---------------------------------------------------------------------- */ - -static void -set_tv_freq(struct i2c_client *c, int freq) -{ - u16 div = ((freq * 20) / 16); - unsigned long give_up = jiffies + HZ; - unsigned char buffer[2]; - - if (debug) - printk(KERN_DEBUG "tuner: setting frequency %dMHz, divisor %x\n", freq / 16, div); - - /* Select high tuning current */ - buffer[0] = 0x29; - buffer[1] = 0x3e; - - if (i2c_master_send(c, buffer, 2) != 2) - printk("tuner: i2c i/o error 1\n"); - - buffer[0] = 0x80 | ((div>>8) & 0x7f); - buffer[1] = div & 0xff; - - if (i2c_master_send(c, buffer, 2) != 2) - printk("tuner: i2c i/o error 2\n"); - - while (!tuner_islocked(c) && time_before(jiffies, give_up)) - schedule(); - - if (!tuner_islocked(c)) - printk(KERN_WARNING "tuner: failed to achieve PLL lock\n"); - - /* Select low tuning current and engage AFC */ - buffer[0] = 0x29; - buffer[1] = 0xb2; - - if (i2c_master_send(c, buffer, 2) != 2) - printk("tuner: i2c i/o error 3\n"); - - if (debug) - printk(KERN_DEBUG "tuner: status %02x\n", tuner_getstatus(c)); -} - -/* ---------------------------------------------------------------------- */ - -static int -tuner_attach(struct i2c_adapter *adap, int addr, int kind) -{ - static unsigned char buffer[] = { 0x29, 0x32, 0x2a, 0, 0x2b, 0 }; - - struct i2c_client *client; - - if (this_adap > 0) - return -1; - this_adap++; - - client_template.adapter = adap; - client_template.addr = addr; - - client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == NULL) - return -ENOMEM; - memcpy(client, &client_template, sizeof(struct i2c_client)); - - printk("tuner: SAB3036 found, status %02x\n", tuner_getstatus(client)); - - i2c_attach_client(client); - - if (i2c_master_send(client, buffer, 2) != 2) - printk("tuner: i2c i/o error 1\n"); - if (i2c_master_send(client, buffer+2, 2) != 2) - printk("tuner: i2c i/o error 2\n"); - if (i2c_master_send(client, buffer+4, 2) != 2) - printk("tuner: i2c i/o error 3\n"); - return 0; -} - -static int -tuner_detach(struct i2c_client *c) -{ - return 0; -} - -static int -tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - int *iarg = (int*)arg; - - switch (cmd) - { - case VIDIOCSFREQ: - set_tv_freq(client, *iarg); - break; - - default: - return -EINVAL; - } - return 0; -} - -static int -tuner_probe(struct i2c_adapter *adap) -{ - this_adap = 0; - if (adap->id == I2C_HW_B_LP) - return i2c_probe(adap, &addr_data, tuner_attach); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static struct i2c_driver -i2c_driver_tuner = -{ - .driver = { - .name = "sab3036", - }, - .id = I2C_DRIVERID_SAB3036, - .attach_adapter = tuner_probe, - .detach_client = tuner_detach, - .command = tuner_command -}; - -static struct i2c_client client_template = -{ - .driver = &i2c_driver_tuner, - .name = "SAB3036", -}; - -static int __init -tuner3036_init(void) -{ - return i2c_add_driver(&i2c_driver_tuner); -} - -static void __exit -tuner3036_exit(void) -{ - i2c_del_driver(&i2c_driver_tuner); -} - -MODULE_DESCRIPTION("SAB3036 tuner driver"); -MODULE_AUTHOR("Philip Blundell <philb@gnu.org>"); -MODULE_LICENSE("GPL"); - -module_param(debug, int, 0); -MODULE_PARM_DESC(debug,"Enable debugging output"); - -module_init(tuner3036_init); -module_exit(tuner3036_exit); diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 9649281a9..d6ce6f6a5 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -453,8 +453,7 @@ static void set_type(struct i2c_client *c, unsigned int type, xc5000_cfg.if_khz = 5380; xc5000_cfg.tuner_callback = t->tuner_callback; if (!dvb_attach(xc5000_attach, - &t->fe, t->i2c->adapter, &xc5000_cfg, - c->adapter->algo_data)) + &t->fe, t->i2c->adapter, &xc5000_cfg)) goto attach_failed; xc_tuner_ops = &t->fe.ops.tuner_ops; diff --git a/linux/drivers/media/video/usbvideo/ibmcam.c b/linux/drivers/media/video/usbvideo/ibmcam.c index 1afbcc147..697e74a24 100644 --- a/linux/drivers/media/video/usbvideo/ibmcam.c +++ b/linux/drivers/media/video/usbvideo/ibmcam.c @@ -736,12 +736,12 @@ static enum ParseState ibmcam_model2_320x240_parse_lines( * make black color and quit the horizontal scanning loop. */ if (((frame->curline + 2) >= scanHeight) || (i >= scanLength)) { - const int j = i * V4L_BYTES_PER_PIXEL; + const int offset = i * V4L_BYTES_PER_PIXEL; #if USES_IBMCAM_PUTPIXEL /* Refresh 'f' because we don't use it much with PUTPIXEL */ - f = frame->data + (v4l_linesize * frame->curline) + j; + f = frame->data + (v4l_linesize * frame->curline) + offset; #endif - memset(f, 0, v4l_linesize - j); + memset(f, 0, v4l_linesize - offset); break; } diff --git a/linux/drivers/media/video/usbvideo/usbvideo.c b/linux/drivers/media/video/usbvideo/usbvideo.c index 5bda83f7d..2e29d95b6 100644 --- a/linux/drivers/media/video/usbvideo/usbvideo.c +++ b/linux/drivers/media/video/usbvideo/usbvideo.c @@ -1039,7 +1039,8 @@ int usbvideo_RegisterVideoDevice(struct uvd *uvd) return -EINVAL; } uvd->vdev.parent = &uvd->dev->dev; - if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { + uvd->vdev.release = video_device_release_empty; + if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { err("%s: video_register_device failed", __func__); return -EPIPE; } diff --git a/linux/drivers/media/video/usbvideo/vicam.c b/linux/drivers/media/video/usbvideo/vicam.c index e7330fe4f..bad26ccf8 100644 --- a/linux/drivers/media/video/usbvideo/vicam.c +++ b/linux/drivers/media/video/usbvideo/vicam.c @@ -782,9 +782,8 @@ vicam_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsign static int vicam_open(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct vicam_camera *cam = - (struct vicam_camera *) dev->priv; + struct vicam_camera *cam = video_drvdata(file); + DBG("open\n"); if (!cam) { @@ -1111,6 +1110,7 @@ static struct video_device vicam_template = { .name = "ViCam-based USB Camera", .fops = &vicam_fops, .minor = -1, + .release = video_device_release_empty, }; /* table of devices that work with this driver */ @@ -1175,14 +1175,13 @@ vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) mutex_init(&cam->cam_lock); - memcpy(&cam->vdev, &vicam_template, - sizeof (vicam_template)); - cam->vdev.priv = cam; // sort of a reverse mapping for those functions that get vdev only + memcpy(&cam->vdev, &vicam_template, sizeof(vicam_template)); + video_set_drvdata(&cam->vdev, cam); cam->udev = dev; cam->bulkEndpoint = bulkEndpoint; - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) == -1) { + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) < 0) { kfree(cam); printk(KERN_WARNING "video_register_device failed\n"); return -EIO; diff --git a/linux/drivers/media/video/usbvision/usbvision-video.c b/linux/drivers/media/video/usbvision/usbvision-video.c index b76295a5b..782ee6436 100644 --- a/linux/drivers/media/video/usbvision/usbvision-video.c +++ b/linux/drivers/media/video/usbvision/usbvision-video.c @@ -360,9 +360,7 @@ static void usbvision_remove_sysfs(struct video_device *vdev) */ static int usbvision_v4l2_open(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int errCode = 0; PDEBUG(DBG_IO, "open"); @@ -439,9 +437,7 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) */ static int usbvision_v4l2_close(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); PDEBUG(DBG_IO, "close"); mutex_lock(&usbvision->lock); @@ -486,9 +482,7 @@ static int usbvision_v4l2_close(struct inode *inode, struct file *file) static int vidioc_g_register (struct file *file, void *priv, struct v4l2_register *reg) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int errCode; if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) @@ -507,9 +501,7 @@ static int vidioc_g_register (struct file *file, void *priv, static int vidioc_s_register (struct file *file, void *priv, struct v4l2_register *reg) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int errCode; if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) @@ -528,9 +520,7 @@ static int vidioc_s_register (struct file *file, void *priv, static int vidioc_querycap (struct file *file, void *priv, struct v4l2_capability *vc) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); strlcpy(vc->card, @@ -550,9 +540,7 @@ static int vidioc_querycap (struct file *file, void *priv, static int vidioc_enum_input (struct file *file, void *priv, struct v4l2_input *vi) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int chan; if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) ) @@ -605,9 +593,7 @@ static int vidioc_enum_input (struct file *file, void *priv, static int vidioc_g_input (struct file *file, void *priv, unsigned int *input) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); *input = usbvision->ctl_input; return 0; @@ -615,9 +601,7 @@ static int vidioc_g_input (struct file *file, void *priv, unsigned int *input) static int vidioc_s_input (struct file *file, void *priv, unsigned int input) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); if ((input >= usbvision->video_inputs) || (input < 0) ) return -EINVAL; @@ -634,9 +618,8 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int input) static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); + usbvision->tvnormId=*id; mutex_lock(&usbvision->lock); @@ -652,9 +635,7 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) static int vidioc_g_tuner (struct file *file, void *priv, struct v4l2_tuner *vt) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); if (!usbvision->have_tuner || vt->index) // Only tuner 0 return -EINVAL; @@ -673,9 +654,7 @@ static int vidioc_g_tuner (struct file *file, void *priv, static int vidioc_s_tuner (struct file *file, void *priv, struct v4l2_tuner *vt) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); // Only no or one tuner for now if (!usbvision->have_tuner || vt->index) @@ -689,9 +668,7 @@ static int vidioc_s_tuner (struct file *file, void *priv, static int vidioc_g_frequency (struct file *file, void *priv, struct v4l2_frequency *freq) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); freq->tuner = 0; // Only one tuner if(usbvision->radio) { @@ -707,9 +684,7 @@ static int vidioc_g_frequency (struct file *file, void *priv, static int vidioc_s_frequency (struct file *file, void *priv, struct v4l2_frequency *freq) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); // Only no or one tuner for now if (!usbvision->have_tuner || freq->tuner) @@ -723,9 +698,7 @@ static int vidioc_s_frequency (struct file *file, void *priv, static int vidioc_g_audio (struct file *file, void *priv, struct v4l2_audio *a) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); memset(a,0,sizeof(*a)); if(usbvision->radio) { @@ -750,9 +723,7 @@ static int vidioc_s_audio (struct file *file, void *fh, static int vidioc_queryctrl (struct file *file, void *priv, struct v4l2_queryctrl *ctrl) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int id=ctrl->id; memset(ctrl,0,sizeof(*ctrl)); @@ -769,9 +740,7 @@ static int vidioc_queryctrl (struct file *file, void *priv, static int vidioc_g_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); return 0; @@ -780,9 +749,7 @@ static int vidioc_g_ctrl (struct file *file, void *priv, static int vidioc_s_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); return 0; @@ -791,9 +758,7 @@ static int vidioc_s_ctrl (struct file *file, void *priv, static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *vr) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int ret; RESTRICT_TO_RANGE(vr->count,1,USBVISION_NUMFRAMES); @@ -821,9 +786,7 @@ static int vidioc_reqbufs (struct file *file, static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *vb) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); struct usbvision_frame *frame; /* FIXME : must control @@ -859,9 +822,7 @@ static int vidioc_querybuf (struct file *file, static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *vb) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); struct usbvision_frame *frame; unsigned long lock_flags; @@ -898,9 +859,7 @@ static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *vb) static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int ret; struct usbvision_frame *f; unsigned long lock_flags; @@ -941,9 +900,7 @@ static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb) static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; usbvision->streaming = Stream_On; @@ -955,9 +912,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -990,9 +945,7 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *vf) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); vf->fmt.pix.width = usbvision->curwidth; vf->fmt.pix.height = usbvision->curheight; vf->fmt.pix.pixelformat = usbvision->palette.format; @@ -1008,9 +961,7 @@ static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *vf) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int formatIdx; /* Find requested format in available ones */ @@ -1038,9 +989,7 @@ static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int ret; if( 0 != (ret=vidioc_try_fmt_vid_cap (file, priv, vf)) ) { @@ -1068,9 +1017,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int noblock = file->f_flags & O_NONBLOCK; unsigned long lock_flags; @@ -1179,10 +1126,7 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) start = vma->vm_start; void *pos; u32 i; - - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); PDEBUG(DBG_MMAP, "mmap"); @@ -1239,9 +1183,7 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) */ static int usbvision_radio_open(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int errCode = 0; PDEBUG(DBG_IO, "%s:", __func__); @@ -1291,9 +1233,7 @@ out: static int usbvision_radio_close(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = - (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = video_drvdata(file); int errCode = 0; PDEBUG(DBG_IO, ""); diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 7e102034d..b2482502a 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -1663,7 +1663,7 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message) return uvc_video_suspend(&dev->video); } -static int uvc_resume(struct usb_interface *intf) +static int __uvc_resume(struct usb_interface *intf, int reset) { struct uvc_device *dev = usb_get_intfdata(intf); int ret; @@ -1672,7 +1672,7 @@ static int uvc_resume(struct usb_interface *intf) intf->cur_altsetting->desc.bInterfaceNumber); if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) { - if ((ret = uvc_ctrl_resume_device(dev)) < 0) + if (reset && (ret = uvc_ctrl_resume_device(dev)) < 0) return ret; return uvc_status_resume(dev); @@ -1687,6 +1687,18 @@ static int uvc_resume(struct usb_interface *intf) return uvc_video_resume(&dev->video); } +static int uvc_resume(struct usb_interface *intf) +{ + return __uvc_resume(intf, 0); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) +static int uvc_reset_resume(struct usb_interface *intf) +{ + return __uvc_resume(intf, 1); +} +#endif + /* ------------------------------------------------------------------------ * Driver initialization and cleanup */ @@ -1920,6 +1932,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Fujitsu Amilo SI2636 - Bison Electronics */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0202, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Bison Electronics */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1952,6 +1973,9 @@ struct uvc_driver uvc_driver = { .disconnect = uvc_disconnect, .suspend = uvc_suspend, .resume = uvc_resume, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) + .reset_resume = uvc_reset_resume, +#endif .id_table = uvc_ids, .supports_autosuspend = 1, }, diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c index d7bd71be4..d4758c8e1 100644 --- a/linux/drivers/media/video/uvc/uvc_v4l2.c +++ b/linux/drivers/media/video/uvc/uvc_v4l2.c @@ -400,15 +400,13 @@ static int uvc_has_privileges(struct uvc_fh *handle) static int uvc_v4l2_open(struct inode *inode, struct file *file) { - struct video_device *vdev; struct uvc_video_device *video; struct uvc_fh *handle; int ret = 0; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); mutex_lock(&uvc_driver.open_mutex); - vdev = video_devdata(file); - video = video_get_drvdata(vdev); + video = video_drvdata(file); if (video->dev->state & UVC_DEV_DISCONNECTED) { ret = -ENODEV; @@ -440,8 +438,7 @@ done: static int uvc_v4l2_release(struct inode *inode, struct file *file) { - struct video_device *vdev = video_devdata(file); - struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_video_device *video = video_drvdata(file); struct uvc_fh *handle = (struct uvc_fh *)file->private_data; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); @@ -1031,8 +1028,7 @@ static struct vm_operations_struct uvc_vm_ops = { static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) { - struct video_device *vdev = video_devdata(file); - struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_video_device *video = video_drvdata(file); struct uvc_buffer *uninitialized_var(buffer); struct page *page; unsigned long addr, start, size; @@ -1085,8 +1081,7 @@ done: static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) { - struct video_device *vdev = video_devdata(file); - struct uvc_video_device *video = video_get_drvdata(vdev); + struct uvc_video_device *video = video_drvdata(file); uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index 6854ac78a..593aebffe 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -455,7 +455,8 @@ static void uvc_video_decode_isoc(struct urb *urb, urb->iso_frame_desc[i].actual_length - ret); /* Process the header again. */ - uvc_video_decode_end(video, buf, mem, ret); + uvc_video_decode_end(video, buf, mem, + urb->iso_frame_desc[i].actual_length); if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) @@ -512,7 +513,7 @@ static void uvc_video_decode_bulk(struct urb *urb, video->bulk.payload_size >= video->bulk.max_payload_size) { if (!video->bulk.skip_payload && buf != NULL) { uvc_video_decode_end(video, buf, video->bulk.header, - video->bulk.header_size); + video->bulk.payload_size); if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) buf = uvc_queue_next_buffer(&video->queue, buf); @@ -655,7 +656,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, if (size > UVC_MAX_FRAME_SIZE) return -EINVAL; - npackets = (size + psize - 1) / psize; + npackets = DIV_ROUND_UP(size, psize); if (npackets > UVC_MAX_ISO_PACKETS) npackets = UVC_MAX_ISO_PACKETS; diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index a027d9baa..b5baf8f7e 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -188,9 +188,11 @@ const char **v4l2_ctrl_get_menu(u32 id) NULL }; static const char *mpeg_audio_encoding[] = { - "Layer I", - "Layer II", - "Layer III", + "MPEG-1/2 Layer I", + "MPEG-1/2 Layer II", + "MPEG-1/2 Layer III", + "MPEG-2/4 AAC", + "AC-3", NULL }; static const char *mpeg_audio_l1_bitrate[] = { @@ -244,6 +246,28 @@ const char **v4l2_ctrl_get_menu(u32 id) "320 kbps", NULL }; + static const char *mpeg_audio_ac3_bitrate[] = { + "32 kbps", + "40 kbps", + "48 kbps", + "56 kbps", + "64 kbps", + "80 kbps", + "96 kbps", + "112 kbps", + "128 kbps", + "160 kbps", + "192 kbps", + "224 kbps", + "256 kbps", + "320 kbps", + "384 kbps", + "448 kbps", + "512 kbps", + "576 kbps", + "640 kbps", + NULL + }; static const char *mpeg_audio_mode[] = { "Stereo", "Joint Stereo", @@ -272,6 +296,7 @@ const char **v4l2_ctrl_get_menu(u32 id) static const char *mpeg_video_encoding[] = { "MPEG-1", "MPEG-2", + "MPEG-4 AVC", NULL }; static const char *mpeg_video_aspect[] = { @@ -312,6 +337,8 @@ const char **v4l2_ctrl_get_menu(u32 id) return mpeg_audio_l2_bitrate; case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return mpeg_audio_l3_bitrate; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + return mpeg_audio_ac3_bitrate; case V4L2_CID_MPEG_AUDIO_MODE: return mpeg_audio_mode; case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: @@ -336,62 +363,73 @@ const char **v4l2_ctrl_get_menu(u32 id) } EXPORT_SYMBOL(v4l2_ctrl_get_menu); -/* Fill in a struct v4l2_queryctrl */ -int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def) +/* Return the control name. */ +const char *v4l2_ctrl_get_name(u32 id) { - const char *name; - - qctrl->flags = 0; - switch (qctrl->id) { + switch (id) { /* USER controls */ - case V4L2_CID_USER_CLASS: name = "User Controls"; break; - case V4L2_CID_AUDIO_VOLUME: name = "Volume"; break; - case V4L2_CID_AUDIO_MUTE: name = "Mute"; break; - case V4L2_CID_AUDIO_BALANCE: name = "Balance"; break; - case V4L2_CID_AUDIO_BASS: name = "Bass"; break; - case V4L2_CID_AUDIO_TREBLE: name = "Treble"; break; - case V4L2_CID_AUDIO_LOUDNESS: name = "Loudness"; break; - case V4L2_CID_BRIGHTNESS: name = "Brightness"; break; - case V4L2_CID_CONTRAST: name = "Contrast"; break; - case V4L2_CID_SATURATION: name = "Saturation"; break; - case V4L2_CID_HUE: name = "Hue"; break; + case V4L2_CID_USER_CLASS: return "User Controls"; + case V4L2_CID_AUDIO_VOLUME: return "Volume"; + case V4L2_CID_AUDIO_MUTE: return "Mute"; + case V4L2_CID_AUDIO_BALANCE: return "Balance"; + case V4L2_CID_AUDIO_BASS: return "Bass"; + case V4L2_CID_AUDIO_TREBLE: return "Treble"; + case V4L2_CID_AUDIO_LOUDNESS: return "Loudness"; + case V4L2_CID_BRIGHTNESS: return "Brightness"; + case V4L2_CID_CONTRAST: return "Contrast"; + case V4L2_CID_SATURATION: return "Saturation"; + case V4L2_CID_HUE: return "Hue"; /* MPEG controls */ - case V4L2_CID_MPEG_CLASS: name = "MPEG Encoder Controls"; break; - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: name = "Audio Sampling Frequency"; break; - case V4L2_CID_MPEG_AUDIO_ENCODING: name = "Audio Encoding Layer"; break; - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: name = "Audio Layer I Bitrate"; break; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: name = "Audio Layer II Bitrate"; break; - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: name = "Audio Layer III Bitrate"; break; - case V4L2_CID_MPEG_AUDIO_MODE: name = "Audio Stereo Mode"; break; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: name = "Audio Stereo Mode Extension"; break; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: name = "Audio Emphasis"; break; - case V4L2_CID_MPEG_AUDIO_CRC: name = "Audio CRC"; break; - case V4L2_CID_MPEG_AUDIO_MUTE: name = "Audio Mute"; break; - case V4L2_CID_MPEG_VIDEO_ENCODING: name = "Video Encoding"; break; - case V4L2_CID_MPEG_VIDEO_ASPECT: name = "Video Aspect"; break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: name = "Video B Frames"; break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: name = "Video GOP Size"; break; - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: name = "Video GOP Closure"; break; - case V4L2_CID_MPEG_VIDEO_PULLDOWN: name = "Video Pulldown"; break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: name = "Video Bitrate Mode"; break; - case V4L2_CID_MPEG_VIDEO_BITRATE: name = "Video Bitrate"; break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: name = "Video Peak Bitrate"; break; - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: name = "Video Temporal Decimation"; break; - case V4L2_CID_MPEG_VIDEO_MUTE: name = "Video Mute"; break; - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: name = "Video Mute YUV"; break; - case V4L2_CID_MPEG_STREAM_TYPE: name = "Stream Type"; break; - case V4L2_CID_MPEG_STREAM_PID_PMT: name = "Stream PMT Program ID"; break; - case V4L2_CID_MPEG_STREAM_PID_AUDIO: name = "Stream Audio Program ID"; break; - case V4L2_CID_MPEG_STREAM_PID_VIDEO: name = "Stream Video Program ID"; break; - case V4L2_CID_MPEG_STREAM_PID_PCR: name = "Stream PCR Program ID"; break; - case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: name = "Stream PES Audio ID"; break; - case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: name = "Stream PES Video ID"; break; - case V4L2_CID_MPEG_STREAM_VBI_FMT: name = "Stream VBI Format"; break; + case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency"; + case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding"; + case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate"; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate"; + case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate"; + case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate"; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate"; + case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode"; + case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension"; + case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis"; + case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC"; + case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute"; + case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding"; + case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect"; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames"; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size"; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure"; + case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown"; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode"; + case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate"; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate"; + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation"; + case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute"; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV"; + case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type"; + case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID"; + case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID"; + case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID"; + case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID"; + case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID"; + case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID"; + case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format"; default: - return -EINVAL; + return NULL; } +} +EXPORT_SYMBOL(v4l2_ctrl_get_name); + +/* Fill in a struct v4l2_queryctrl */ +int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def) +{ + const char *name = v4l2_ctrl_get_name(qctrl->id); + + qctrl->flags = 0; + if (name == NULL) + return -EINVAL; + switch (qctrl->id) { case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_LOUDNESS: @@ -408,6 +446,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_AUDIO_L1_BITRATE: case V4L2_CID_MPEG_AUDIO_L2_BITRATE: case V4L2_CID_MPEG_AUDIO_L3_BITRATE: + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: case V4L2_CID_MPEG_AUDIO_MODE: case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: case V4L2_CID_MPEG_AUDIO_EMPHASIS: @@ -494,7 +533,7 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) case V4L2_CID_MPEG_AUDIO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_ENCODING_LAYER_1, - V4L2_MPEG_AUDIO_ENCODING_LAYER_3, 1, + V4L2_MPEG_AUDIO_ENCODING_AC3, 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return v4l2_ctrl_query_fill(qctrl, @@ -511,6 +550,13 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) V4L2_MPEG_AUDIO_L3_BITRATE_32K, V4L2_MPEG_AUDIO_L3_BITRATE_320K, 1, V4L2_MPEG_AUDIO_L3_BITRATE_192K); + case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: + return v4l2_ctrl_query_fill(qctrl, 0, 6400, 1, 3200000); + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_AC3_BITRATE_32K, + V4L2_MPEG_AUDIO_AC3_BITRATE_640K, 1, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K); case V4L2_CID_MPEG_AUDIO_MODE: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_MODE_STEREO, @@ -536,7 +582,7 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) case V4L2_CID_MPEG_VIDEO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, V4L2_MPEG_VIDEO_ENCODING_MPEG_2); case V4L2_CID_MPEG_VIDEO_ASPECT: return v4l2_ctrl_query_fill(qctrl, @@ -595,12 +641,17 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) EXPORT_SYMBOL(v4l2_ctrl_query_fill_std); /* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and - the menu. The qctrl pointer may be NULL, in which case it is ignored. */ + the menu. The qctrl pointer may be NULL, in which case it is ignored. + If menu_items is NULL, then the menu items are retrieved using + v4l2_ctrl_get_menu. */ int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl, const char **menu_items) { int i; + qmenu->reserved = 0; + if (menu_items == NULL) + menu_items = v4l2_ctrl_get_menu(qmenu->id); if (menu_items == NULL || (qctrl && (qmenu->index < qctrl->minimum || qmenu->index > qctrl->maximum))) return -EINVAL; @@ -608,11 +659,31 @@ int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qc if (menu_items[i] == NULL || menu_items[i][0] == '\0') return -EINVAL; snprintf(qmenu->name, sizeof(qmenu->name), menu_items[qmenu->index]); - qmenu->reserved = 0; return 0; } EXPORT_SYMBOL(v4l2_ctrl_query_menu); +/* Fill in a struct v4l2_querymenu based on the specified array of valid + menu items (terminated by V4L2_CTRL_MENU_IDS_END). + Use this if there are 'holes' in the list of valid menu items. */ +int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids) +{ + const char **menu_items = v4l2_ctrl_get_menu(qmenu->id); + + qmenu->reserved = 0; + if (menu_items == NULL || ids == NULL) + return -EINVAL; + while (*ids != V4L2_CTRL_MENU_IDS_END) { + if (*ids++ == qmenu->index) { + snprintf(qmenu->name, sizeof(qmenu->name), + menu_items[qmenu->index]); + return 0; + } + } + return -EINVAL; +} +EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items); + /* ctrl_classes points to an array of u32 pointers, the last element is a NULL pointer. Each u32 array is a 0-terminated array of control IDs. Each array must be sorted low to high and belong to the same control diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c index 398376a40..a2daea31c 100644 --- a/linux/drivers/media/video/v4l2-dev.c +++ b/linux/drivers/media/video/v4l2-dev.c @@ -44,6 +44,7 @@ static ssize_t show_index(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vfd = container_of(cd, struct video_device, dev); + return sprintf(buf, "%i\n", vfd->index); } @@ -51,6 +52,7 @@ static ssize_t show_name(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vfd = container_of(cd, struct video_device, dev); + return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); } @@ -63,12 +65,14 @@ static struct device_attribute video_device_attrs[] = { static ssize_t show_index(struct class_device *cd, char *buf) { struct video_device *vfd = container_of(cd, struct video_device, dev); + return sprintf(buf, "%i\n", vfd->index); } static ssize_t show_name(struct class_device *cd, char *buf) { struct video_device *vfd = container_of(cd, struct video_device, dev); + return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); } @@ -76,12 +80,15 @@ static CLASS_DEVICE_ATTR(index, S_IRUGO, show_index, NULL); static CLASS_DEVICE_ATTR(name, S_IRUGO, show_name, NULL); #endif +/* + * Active devices + */ +static struct video_device *video_device[VIDEO_NUM_DEVICES]; +static DEFINE_MUTEX(videodev_lock); + struct video_device *video_device_alloc(void) { - struct video_device *vfd; - - vfd = kzalloc(sizeof(*vfd), GFP_KERNEL); - return vfd; + return kzalloc(sizeof(struct video_device), GFP_KERNEL); } EXPORT_SYMBOL(video_device_alloc); @@ -91,6 +98,42 @@ void video_device_release(struct video_device *vfd) } EXPORT_SYMBOL(video_device_release); +void video_device_release_empty(struct video_device *vfd) +{ + /* Do nothing */ + /* Only valid when the video_device struct is a static. */ +} +EXPORT_SYMBOL(video_device_release_empty); + +/* Called when the last user of the character device is gone. */ +static void v4l2_chardev_release(struct kobject *kobj) +{ + struct video_device *vfd = container_of(kobj, struct video_device, cdev.kobj); + + mutex_lock(&videodev_lock); + if (video_device[vfd->minor] != vfd) { + mutex_unlock(&videodev_lock); + BUG(); + return; + } + + /* Free up this device for reuse */ + video_device[vfd->minor] = NULL; + mutex_unlock(&videodev_lock); + + /* Release the character device */ + vfd->cdev_release(kobj); + /* Release video_device and perform other + cleanups as needed. */ + if (vfd->release) + vfd->release(vfd); +} + +/* The new kobj_type for the character device */ +static struct kobj_type v4l2_ktype_cdev_default = { + .release = v4l2_chardev_release, +}; + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) static void video_release(struct class_device *cd) #else @@ -99,12 +142,11 @@ static void video_release(struct device *cd) { struct video_device *vfd = container_of(cd, struct video_device, dev); -#if 1 /* keep */ - /* needed until all drivers are fixed */ - if (!vfd->release) - return; -#endif - vfd->release(vfd); + /* It's now safe to delete the char device. + This will either trigger the v4l2_chardev_release immediately (if + the refcount goes to 0) or later when the last user of the + character device closes it. */ + cdev_del(&vfd->cdev); } static struct class video_class = { @@ -117,13 +159,6 @@ static struct class video_class = { #endif }; -/* - * Active devices - */ - -static struct video_device *video_device[VIDEO_NUM_DEVICES]; -static DEFINE_MUTEX(videodev_lock); - struct video_device *video_devdata(struct file *file) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) @@ -134,79 +169,6 @@ struct video_device *video_devdata(struct file *file) } EXPORT_SYMBOL(video_devdata); -/* - * Open a video device - FIXME: Obsoleted - */ -static int video_open(struct inode *inode, struct file *file) -{ - unsigned int minor = iminor(inode); - int err = 0; - struct video_device *vfl; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - const struct file_operations *old_fops; -#else - struct file_operations *old_fops; -#endif - - if (minor >= VIDEO_NUM_DEVICES) - return -ENODEV; - mutex_lock(&videodev_lock); - vfl = video_device[minor]; - if (vfl == NULL) { - mutex_unlock(&videodev_lock); - request_module("char-major-%d-%d", VIDEO_MAJOR, minor); - mutex_lock(&videodev_lock); - vfl = video_device[minor]; - if (vfl == NULL) { - mutex_unlock(&videodev_lock); - return -ENODEV; - } - } - old_fops = file->f_op; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - file->f_op = fops_get(vfl->fops); -#else - file->f_op = (struct file_operations *)fops_get(vfl->fops); -#endif - if (file->f_op->open) - err = file->f_op->open(inode, file); - if (err) { - fops_put(file->f_op); - file->f_op = fops_get(old_fops); - } - fops_put(old_fops); - mutex_unlock(&videodev_lock); - return err; -} - -/* - * open/release helper functions -- handle exclusive opens - * Should be removed soon - */ -int video_exclusive_open(struct inode *inode, struct file *file) -{ - struct video_device *vfl = video_devdata(file); - int retval = 0; - - mutex_lock(&vfl->lock); - if (vfl->users) - retval = -EBUSY; - else - vfl->users++; - mutex_unlock(&vfl->lock); - return retval; -} -EXPORT_SYMBOL(video_exclusive_open); - -int video_exclusive_release(struct inode *inode, struct file *file) -{ - struct video_device *vfl = video_devdata(file); - - vfl->users--; - return 0; -} -EXPORT_SYMBOL(video_exclusive_release); - /** * get_index - assign stream number based on parent device * @vdev: video_device to assign index number to, vdev->dev should be assigned @@ -291,6 +253,13 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, int end; int ret; char *name_base; + void *priv = video_get_drvdata(vfd); + + /* the release callback MUST be present */ + BUG_ON(!vfd->release); + + if (vfd == NULL) + return -EINVAL; switch (type) { case VFL_TYPE_GRABBER: @@ -316,12 +285,19 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, default: printk(KERN_ERR "%s called with unknown type: %d\n", __func__, type); - return -1; + return -EINVAL; } + /* Initialize the character device */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) + cdev_init(&vfd->cdev, vfd->fops); +#else + cdev_init(&vfd->cdev, (struct file_operations *)vfd->fops); +#endif + vfd->cdev.owner = vfd->fops->owner; /* pick a minor number */ mutex_lock(&videodev_lock); - if (nr >= 0 && nr < end-base) { + if (nr >= 0 && nr < end-base) { /* use the one the driver asked for */ i = base + nr; if (NULL != video_device[i]) { @@ -352,14 +328,18 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, goto fail_minor; } - mutex_init(&vfd->lock); - + ret = cdev_add(&vfd->cdev, MKDEV(VIDEO_MAJOR, vfd->minor), 1); + if (ret < 0) { + printk(KERN_ERR "%s: cdev_add failed\n", __func__); + goto fail_minor; + } /* sysfs class */ - memset(&vfd->dev, 0x00, sizeof(vfd->dev)); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) + memset(&vfd->dev, 0, sizeof(vfd->dev)); + /* The memset above cleared the device's drvdata, so + put back the copy we made earlier. */ + video_set_drvdata(vfd, priv); vfd->dev.class = &video_class; vfd->dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); -#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) if (vfd->parent) vfd->dev.dev = vfd->parent; @@ -373,41 +353,38 @@ int video_register_device_index(struct video_device *vfd, int type, int nr, #endif if (ret < 0) { printk(KERN_ERR "%s: device_register failed\n", __func__); - goto fail_minor; + goto del_cdev; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) - ret = class_device_create_file(&vfd->dev, - &class_device_attr_name); + ret = class_device_create_file(&vfd->dev, &class_device_attr_name); if (ret < 0) { printk(KERN_ERR "%s: class_device_create_file 'name' failed\n", __func__); class_device_unregister(&vfd->dev); - goto fail_minor; + goto del_cdev; } - ret = class_device_create_file(&vfd->dev, - &class_device_attr_index); + ret = class_device_create_file(&vfd->dev, &class_device_attr_index); if (ret < 0) { printk(KERN_ERR "%s: class_device_create_file 'index' failed\n", __func__); class_device_unregister(&vfd->dev); - goto fail_minor; + goto del_cdev; } #endif - -#if 1 /* keep */ - /* 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 " - "http://lwn.net/Articles/36850/\n", vfd->name); -#endif + /* Remember the cdev's release function */ + vfd->cdev_release = vfd->cdev.kobj.ktype->release; + /* Install our own */ + vfd->cdev.kobj.ktype = &v4l2_ktype_cdev_default; return 0; +del_cdev: + cdev_del(&vfd->cdev); + fail_minor: mutex_lock(&videodev_lock); video_device[vfd->minor] = NULL; - vfd->minor = -1; mutex_unlock(&videodev_lock); + vfd->minor = -1; return ret; } EXPORT_SYMBOL(video_register_device_index); @@ -422,51 +399,33 @@ EXPORT_SYMBOL(video_register_device_index); void video_unregister_device(struct video_device *vfd) { - mutex_lock(&videodev_lock); - if (video_device[vfd->minor] != vfd) - panic("videodev: bad unregister"); - - video_device[vfd->minor] = NULL; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) class_device_unregister(&vfd->dev); #else device_unregister(&vfd->dev); #endif - mutex_unlock(&videodev_lock); } EXPORT_SYMBOL(video_unregister_device); /* - * Video fs operations - */ -static const struct file_operations video_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .open = video_open, -}; - -/* * Initialise video for linux */ - static int __init videodev_init(void) { + dev_t dev = MKDEV(VIDEO_MAJOR, 0); int ret; printk(KERN_INFO "Linux video capture interface: v2.00\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) - if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, &video_fops)) { -#else - if (register_chrdev(VIDEO_MAJOR, VIDEO_NAME, - (struct file_operations *)&video_fops)) { -#endif - printk(KERN_WARNING "video_dev: unable to get major %d\n", VIDEO_MAJOR); - return -EIO; + ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME); + if (ret < 0) { + printk(KERN_WARNING "videodev: unable to get major %d\n", + VIDEO_MAJOR); + return ret; } ret = class_register(&video_class); if (ret < 0) { - unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME); + unregister_chrdev_region(dev, VIDEO_NUM_DEVICES); printk(KERN_WARNING "video_dev: class_register failed\n"); return -EIO; } @@ -476,8 +435,10 @@ static int __init videodev_init(void) static void __exit videodev_exit(void) { + dev_t dev = MKDEV(VIDEO_MAJOR, 0); + class_unregister(&video_class); - unregister_chrdev(VIDEO_MAJOR, VIDEO_NAME); + unregister_chrdev_region(dev, VIDEO_NUM_DEVICES); } module_init(videodev_init) diff --git a/linux/drivers/media/video/v4l2-ioctl.c b/linux/drivers/media/video/v4l2-ioctl.c index a56e3643b..51569f888 100644 --- a/linux/drivers/media/video/v4l2-ioctl.c +++ b/linux/drivers/media/video/v4l2-ioctl.c @@ -500,7 +500,7 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd, p->timestamp.tv_sec / 3600, (int)(p->timestamp.tv_sec / 60) % 60, (int)(p->timestamp.tv_sec % 60), - p->timestamp.tv_usec, + (long)p->timestamp.tv_usec, p->index, prt_names(p->type, v4l2_type_names), p->bytesused, p->flags, @@ -675,7 +675,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, __video_do_ioctl will be called again, with one or more V4L2 ioctls. ********************************************************/ - if (_IOC_TYPE(cmd) == 'v') + if (_IOC_TYPE(cmd) == 'v' && _IOC_NR(cmd) < BASE_VIDIOCPRIVATE) return v4l_compat_translate_ioctl(inode, file, cmd, arg, __video_do_ioctl); #endif diff --git a/linux/drivers/media/video/vino.c b/linux/drivers/media/video/vino.c index 9ada3fff2..08eb57818 100644 --- a/linux/drivers/media/video/vino.c +++ b/linux/drivers/media/video/vino.c @@ -809,7 +809,7 @@ static void vino_free_buffer_with_count(struct vino_framebuffer *fb, dprintk("vino_free_buffer_with_count(): count = %d\n", count); for (i = 0; i < count; i++) { - ClearPageReserved(virt_to_page(fb->desc_table.virtual[i])); + ClearPageReserved(virt_to_page((void *)fb->desc_table.virtual[i])); dma_unmap_single(NULL, fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i], PAGE_SIZE, DMA_FROM_DEVICE); @@ -887,7 +887,7 @@ static int vino_allocate_buffer(struct vino_framebuffer *fb, dma_data_addr + VINO_PAGE_SIZE * j; } - SetPageReserved(virt_to_page(fb->desc_table.virtual[i])); + SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i])); } /* page_count needs to be set anyway, because the descriptor table has @@ -974,7 +974,7 @@ static int vino_prepare_user_buffer(struct vino_framebuffer *fb, dma_data_addr + VINO_PAGE_SIZE * j; } - SetPageReserved(virt_to_page(fb->desc_table.virtual[i])); + SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i])); } /* page_count needs to be set anyway, because the descriptor table has @@ -4028,8 +4028,7 @@ out: static int vino_open(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); int ret = 0; dprintk("open(): channel = %c\n", (vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B'); @@ -4060,8 +4059,7 @@ static int vino_open(struct inode *inode, struct file *file) static int vino_close(struct inode *inode, struct file *file) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); dprintk("close():\n"); mutex_lock(&vcs->mutex); @@ -4104,8 +4102,7 @@ static struct vm_operations_struct vino_vm_ops = { static int vino_mmap(struct file *file, struct vm_area_struct *vma) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long start = vma->vm_start; unsigned long size = vma->vm_end - vma->vm_start; @@ -4210,8 +4207,7 @@ out: static unsigned int vino_poll(struct file *file, poll_table *pt) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); unsigned int outgoing; unsigned int ret = 0; @@ -4251,8 +4247,7 @@ error: static int vino_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); #ifdef VINO_DEBUG switch (_IOC_TYPE(cmd)) { @@ -4359,8 +4354,7 @@ static int vino_do_ioctl(struct inode *inode, struct file *file, static int vino_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct video_device *dev = video_devdata(file); - struct vino_channel_settings *vcs = video_get_drvdata(dev); + struct vino_channel_settings *vcs = video_drvdata(file); int ret; if (mutex_lock_interruptible(&vcs->mutex)) diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index 2f18381b6..f6a12b184 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -1029,13 +1029,13 @@ static int vivi_release(void) dev = list_entry(list, struct vivi_dev, vivi_devlist); if (-1 != dev->vfd->minor) { - video_unregister_device(dev->vfd); - printk(KERN_INFO "%s: /dev/video%d unregistered.\n", + printk(KERN_INFO "%s: unregistering /dev/video%d\n", VIVI_MODULE_NAME, dev->vfd->minor); + video_unregister_device(dev->vfd); } else { - video_device_release(dev->vfd); - printk(KERN_INFO "%s: /dev/video%d released.\n", + printk(KERN_INFO "%s: releasing /dev/video%d\n", VIVI_MODULE_NAME, dev->vfd->minor); + video_device_release(dev->vfd); } kfree(dev); @@ -1112,19 +1112,29 @@ static struct video_device vivi_template = { Initialization and module stuff ------------------------------------------------------------------*/ +/* This routine allocates from 1 to n_devs virtual drivers. + + The real maximum number of virtual drivers will depend on how many drivers + will succeed. This is limited to the maximum number of devices that + videodev supports. Since there are 64 minors for video grabbers, this is + currently the theoretical maximum limit. However, a further limit does + exist at videodev that forbids any driver to register more than 32 video + grabbers. + */ static int __init vivi_init(void) { int ret = -ENOMEM, i; struct vivi_dev *dev; struct video_device *vfd; + if (n_devs <= 0) + n_devs = 1; + for (i = 0; i < n_devs; i++) { dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (NULL == dev) + if (!dev) break; - list_add_tail(&dev->vivi_devlist, &vivi_devlist); - /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); init_waitqueue_head(&dev->vidq.wq); @@ -1134,14 +1144,27 @@ static int __init vivi_init(void) mutex_init(&dev->mutex); vfd = video_device_alloc(); - if (NULL == vfd) + if (!vfd) { + kfree(dev); break; + } *vfd = vivi_template; ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); - if (ret < 0) + if (ret < 0) { + video_device_release(vfd); + kfree(dev); + + /* If some registers succeeded, keep driver */ + if (i) + ret = 0; + break; + } + + /* Now that everything is fine, let's add it to device list */ + list_add_tail(&dev->vivi_devlist, &vivi_devlist); snprintf(vfd->name, sizeof(vfd->name), "%s (%i)", vivi_template.name, vfd->minor); @@ -1157,11 +1180,16 @@ static int __init vivi_init(void) if (ret < 0) { vivi_release(); printk(KERN_INFO "Error %d while loading vivi driver\n", ret); - } else + } else { printk(KERN_INFO "Video Technology Magazine Virtual Video " "Capture Board ver %u.%u.%u successfully loaded.\n", (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF, VIVI_VERSION & 0xFF); + + /* n_devs will reflect the actual number of allocated devices */ + n_devs = i; + } + return ret; } @@ -1177,10 +1205,10 @@ MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board"); MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol"); MODULE_LICENSE("Dual BSD/GPL"); -module_param(video_nr, int, 0); +module_param(video_nr, uint, 0444); MODULE_PARM_DESC(video_nr, "video iminor start number"); -module_param(n_devs, int, 0); +module_param(n_devs, uint, 0444); MODULE_PARM_DESC(n_devs, "number of video devices to create"); module_param_named(debug, vivi_template.debug, int, 0444); diff --git a/linux/drivers/media/video/vpx3220.c b/linux/drivers/media/video/vpx3220.c index 989166eec..896af1a12 100644 --- a/linux/drivers/media/video/vpx3220.c +++ b/linux/drivers/media/video/vpx3220.c @@ -24,8 +24,6 @@ #include <linux/types.h> #include <linux/slab.h> -#include <linux/byteorder/swab.h> - #include <asm/io.h> #include <asm/uaccess.h> diff --git a/linux/drivers/media/video/w9966.c b/linux/drivers/media/video/w9966.c index 6aefc6566..f3f28c232 100644 --- a/linux/drivers/media/video/w9966.c +++ b/linux/drivers/media/video/w9966.c @@ -114,6 +114,7 @@ struct w9966_dev { signed char contrast; signed char color; signed char hue; + unsigned long in_use; }; /* @@ -185,10 +186,25 @@ static int w9966_v4l_ioctl(struct inode *inode, struct file *file, static ssize_t w9966_v4l_read(struct file *file, char __user *buf, size_t count, loff_t *ppos); +static int w9966_exclusive_open(struct inode *inode, struct file *file) +{ + struct w9966_dev *cam = video_drvdata(file); + + return test_and_set_bit(0, &cam->in_use) ? -EBUSY : 0; +} + +static int w9966_exclusive_release(struct inode *inode, struct file *file) +{ + struct w9966_dev *cam = video_drvdata(file); + + clear_bit(0, &cam->in_use); + return 0; +} + static const struct file_operations w9966_fops = { .owner = THIS_MODULE, - .open = video_exclusive_open, - .release = video_exclusive_release, + .open = w9966_exclusive_open, + .release = w9966_exclusive_release, .ioctl = w9966_v4l_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, @@ -199,6 +215,7 @@ static const struct file_operations w9966_fops = { static struct video_device w9966_template = { .name = W9966_DRIVERNAME, .fops = &w9966_fops, + .release = video_device_release_empty, }; /* @@ -333,9 +350,9 @@ static int w9966_init(struct w9966_dev* cam, struct parport* port) // Fill in the video_device struct and register us to v4l memcpy(&cam->vdev, &w9966_template, sizeof(struct video_device)); - cam->vdev.priv = cam; + video_set_drvdata(&cam->vdev, cam); - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) return -1; w9966_setState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); @@ -714,8 +731,7 @@ static int w9966_wReg_i2c(struct w9966_dev* cam, int reg, int data) static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - struct video_device *vdev = video_devdata(file); - struct w9966_dev *cam = vdev->priv; + struct w9966_dev *cam = video_drvdata(file); switch(cmd) { @@ -873,8 +889,7 @@ static int w9966_v4l_ioctl(struct inode *inode, struct file *file, static ssize_t w9966_v4l_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct video_device *vdev = video_devdata(file); - struct w9966_dev *cam = vdev->priv; + struct w9966_dev *cam = video_drvdata(file); unsigned char addr = 0xa0; // ECP, read, CCD-transfer, 00000 unsigned char __user *dest = (unsigned char __user *)buf; unsigned long dleft = count; diff --git a/linux/drivers/media/video/w9968cf.c b/linux/drivers/media/video/w9968cf.c index 666438e8d..4e7a8400b 100644 --- a/linux/drivers/media/video/w9968cf.c +++ b/linux/drivers/media/video/w9968cf.c @@ -919,7 +919,6 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam) for (i = 0; i < W9968CF_URBS; i++) { urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL); - cam->urb[i] = urb; if (!urb) { for (j = 0; j < i; j++) usb_free_urb(cam->urb[j]); @@ -927,6 +926,7 @@ static int w9968cf_start_transfer(struct w9968cf_device* cam) return -ENOMEM; } + cam->urb[i] = urb; urb->dev = udev; urb->context = (void*)cam; urb->pipe = usb_rcvisocpipe(udev, 1); diff --git a/linux/drivers/media/video/wm8739.c b/linux/drivers/media/video/wm8739.c index 3ad19cf13..f9d13b89a 100644 --- a/linux/drivers/media/video/wm8739.c +++ b/linux/drivers/media/video/wm8739.c @@ -280,10 +280,8 @@ static int wm8739_probe(struct i2c_client *client, client->addr << 1, client->adapter->name); state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL); - if (state == NULL) { - kfree(client); + if (state == NULL) return -ENOMEM; - } state->vol_l = 0x17; /* 0dB */ state->vol_r = 0x17; /* 0dB */ state->muted = 0; diff --git a/linux/drivers/media/video/zc0301/zc0301_core.c b/linux/drivers/media/video/zc0301/zc0301_core.c index e59145f65..0be0f095e 100644 --- a/linux/drivers/media/video/zc0301/zc0301_core.c +++ b/linux/drivers/media/video/zc0301/zc0301_core.c @@ -661,7 +661,7 @@ static int zc0301_open(struct inode* inode, struct file* filp) if (!down_read_trylock(&zc0301_dev_lock)) return -EAGAIN; - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); if (wait_for_completion_interruptible(&cam->probe)) { up_read(&zc0301_dev_lock); @@ -743,7 +743,7 @@ static int zc0301_release(struct inode* inode, struct file* filp) down_write(&zc0301_dev_lock); - cam = video_get_drvdata(video_devdata(filp)); + cam = video_drvdata(filp); zc0301_stop_transfer(cam); zc0301_release_buffers(cam); @@ -763,7 +763,7 @@ static int zc0301_release(struct inode* inode, struct file* filp) static ssize_t zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) { - struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_device *cam = video_drvdata(filp); struct zc0301_frame_t* f, * i; unsigned long lock_flags; long timeout; @@ -870,7 +870,7 @@ exit: static unsigned int zc0301_poll(struct file *filp, poll_table *wait) { - struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_device *cam = video_drvdata(filp); struct zc0301_frame_t* f; unsigned long lock_flags; unsigned int mask = 0; @@ -945,7 +945,7 @@ static struct vm_operations_struct zc0301_vm_ops = { static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) { - struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_device *cam = video_drvdata(filp); unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start; void *pos; @@ -1800,7 +1800,7 @@ zc0301_vidioc_s_parm(struct zc0301_device* cam, void __user * arg) static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg) { - struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_device *cam = video_drvdata(filp); switch (cmd) { @@ -1895,7 +1895,7 @@ static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, static int zc0301_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, unsigned long arg) { - struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_device *cam = video_drvdata(filp); int err = 0; if (mutex_lock_interruptible(&cam->fileop_mutex)) @@ -1992,6 +1992,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->fops = &zc0301_fops; cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; + cam->v4ldev->parent = &udev->dev; video_set_drvdata(cam->v4ldev, cam); init_completion(&cam->probe); diff --git a/linux/drivers/media/video/zc0301/zc0301_sensor.h b/linux/drivers/media/video/zc0301/zc0301_sensor.h index 8e8276f27..4dc95b5c2 100644 --- a/linux/drivers/media/video/zc0301/zc0301_sensor.h +++ b/linux/drivers/media/video/zc0301/zc0301_sensor.h @@ -61,27 +61,8 @@ zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); #define ZC0301_ID_TABLE \ static const struct usb_device_id zc0301_id_table[] = { \ - { ZC0301_USB_DEVICE(0x041e, 0x4017, 0xff), }, /* ICM105 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x401c, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x401e, 0xff), }, /* HV7131 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x401f, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x4022, 0xff), }, \ - { ZC0301_USB_DEVICE(0x041e, 0x4034, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x4035, 0xff), }, /* PAS106 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x4036, 0xff), }, /* HV7131 */ \ - { ZC0301_USB_DEVICE(0x041e, 0x403a, 0xff), }, /* HV7131 */ \ - { ZC0301_USB_DEVICE(0x0458, 0x7007, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x0458, 0x700c, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x0458, 0x700f, 0xff), }, /* TAS5130 */ \ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ - { ZC0301_USB_DEVICE(0x055f, 0xd003, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x055f, 0xd004, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x0ac8, 0x0301, 0xff), }, \ - { ZC0301_USB_DEVICE(0x0ac8, 0x301b, 0xff), }, /* PB-0330/HV7131 */ \ { ZC0301_USB_DEVICE(0x0ac8, 0x303b, 0xff), }, /* PB-0330 */ \ - { ZC0301_USB_DEVICE(0x10fd, 0x0128, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x10fd, 0x8050, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x10fd, 0x804e, 0xff), }, /* TAS5130 */ \ { } \ }; diff --git a/linux/drivers/media/video/zoran_card.c b/linux/drivers/media/video/zoran_card.c index 6678c6de9..081b72a45 100644 --- a/linux/drivers/media/video/zoran_card.c +++ b/linux/drivers/media/video/zoran_card.c @@ -989,7 +989,7 @@ zoran_open_init_params (struct zoran *zr) zr->v4l_grab_seq = 0; zr->v4l_settings.width = 192; zr->v4l_settings.height = 144; - zr->v4l_settings.format = &zoran_formats[4]; /* YUY2 - YUV-4:2:2 packed */ + zr->v4l_settings.format = &zoran_formats[7]; /* YUY2 - YUV-4:2:2 packed */ zr->v4l_settings.bytesperline = zr->v4l_settings.width * ((zr->v4l_settings.format->depth + 7) / 8); diff --git a/linux/drivers/media/video/zoran_device.c b/linux/drivers/media/video/zoran_device.c index b0a014d09..87f894fa2 100644 --- a/linux/drivers/media/video/zoran_device.c +++ b/linux/drivers/media/video/zoran_device.c @@ -59,8 +59,6 @@ ZR36057_ISR_GIRQ1 | \ ZR36057_ISR_JPEGRepIRQ ) -extern const struct zoran_format zoran_formats[]; - static int lml33dpath; /* default = 0 * 1 will use digital path in capture * mode instead of analog. It can be @@ -378,7 +376,7 @@ zr36057_set_vfe (struct zoran *zr, /* horizontal */ VidWinWid = video_width; - X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa; + X = DIV_ROUND_UP(VidWinWid * 64, tvn->Wa); We = (VidWinWid * 64) / X; HorDcm = 64 - X; hcrop1 = 2 * ((tvn->Wa - We) / 4); @@ -404,7 +402,7 @@ zr36057_set_vfe (struct zoran *zr, /* Vertical */ DispMode = !(video_height > BUZ_MAX_HEIGHT / 2); VidWinHt = DispMode ? video_height : video_height / 2; - Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha; + Y = DIV_ROUND_UP(VidWinHt * 64 * 2, tvn->Ha); He = (VidWinHt * 64) / Y; VerDcm = 64 - Y; vcrop1 = (tvn->Ha / 2 - He) / 2; diff --git a/linux/drivers/media/video/zoran_device.h b/linux/drivers/media/video/zoran_device.h index c46d1b076..d4e0b9b5a 100644 --- a/linux/drivers/media/video/zoran_device.h +++ b/linux/drivers/media/video/zoran_device.h @@ -84,6 +84,14 @@ extern void zoran_set_pci_master(struct zoran *zr, extern void zoran_init_hardware(struct zoran *zr); extern void zr36057_restart(struct zoran *zr); +extern const struct zoran_format zoran_formats[]; + +extern int v4l_nbufs; +extern int v4l_bufsize; +extern int jpg_nbufs; +extern int jpg_bufsize; +extern int pass_through; + /* i2c */ extern int decoder_command(struct zoran *zr, int cmd, diff --git a/linux/drivers/media/video/zoran_driver.c b/linux/drivers/media/video/zoran_driver.c index 78e8430f4..9d57a630c 100644 --- a/linux/drivers/media/video/zoran_driver.c +++ b/linux/drivers/media/video/zoran_driver.c @@ -135,7 +135,7 @@ const struct zoran_format zoran_formats[] = { }, { .name = "16-bit RGB BE", ZFMT(-1, - V4L2_PIX_FMT_RGB565, V4L2_COLORSPACE_SRGB), + V4L2_PIX_FMT_RGB565X, V4L2_COLORSPACE_SRGB), .depth = 16, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, @@ -202,12 +202,6 @@ const struct zoran_format zoran_formats[] = { # include <linux/bigphysarea.h> #endif -extern int v4l_nbufs; -extern int v4l_bufsize; -extern int jpg_nbufs; -extern int jpg_bufsize; -extern int pass_through; - static int lock_norm; /* 0 = default 1 = Don't change TV standard (norm) */ module_param(lock_norm, int, 0644); MODULE_PARM_DESC(lock_norm, "Prevent norm changes (1 = ignore, >1 = fail)"); @@ -2805,7 +2799,8 @@ zoran_do_ioctl (struct inode *inode, fh->v4l_settings.format->fourcc; fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace; - fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.bytesperline = + fh->v4l_settings.bytesperline; if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2)) fmt->fmt.pix.field = @@ -2901,13 +2896,6 @@ zoran_do_ioctl (struct inode *inode, fmt->fmt.pix.pixelformat, (char *) &printformat); - if (fmt->fmt.pix.bytesperline > 0) { - dprintk(5, - KERN_ERR "%s: bpl not supported\n", - ZR_DEVNAME(zr)); - return -EINVAL; - } - /* we can be requested to do JPEG/raw playback/capture */ if (! (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE || @@ -2991,8 +2979,11 @@ zoran_do_ioctl (struct inode *inode, fh->jpg_buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh-> jpg_settings); + fmt->fmt.pix.bytesperline = 0; fmt->fmt.pix.sizeimage = fh->jpg_buffers.buffer_size; + fmt->fmt.pix.colorspace = + V4L2_COLORSPACE_SMPTE170M; /* we hereby abuse this variable to show that * we're gonna do mjpeg capture */ @@ -3047,9 +3038,13 @@ zoran_do_ioctl (struct inode *inode, /* tell the user the * results/missing stuff */ + fmt->fmt.pix.bytesperline = + fh->v4l_settings.bytesperline; fmt->fmt.pix.sizeimage = fh->v4l_settings.height * fh->v4l_settings.bytesperline; + fmt->fmt.pix.colorspace = + fh->v4l_settings.format->colorspace; if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2)) fmt->fmt.pix.field = diff --git a/linux/drivers/media/video/zoran_procfs.c b/linux/drivers/media/video/zoran_procfs.c index d7bc097da..961950a21 100644 --- a/linux/drivers/media/video/zoran_procfs.c +++ b/linux/drivers/media/video/zoran_procfs.c @@ -162,10 +162,11 @@ static ssize_t zoran_write(struct file *file, const char __user *buffer, return -EFAULT; } string[count] = 0; - dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n", #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n", ZR_DEVNAME(zr), file->f_dentry->d_name.name, count, zr); #else + dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n", ZR_DEVNAME(zr), file->f_path.dentry->d_name.name, count, zr); #endif ldelim = " \t\n"; diff --git a/linux/drivers/media/video/zr364xx.c b/linux/drivers/media/video/zr364xx.c index dbfdd6966..a2a632534 100644 --- a/linux/drivers/media/video/zr364xx.c +++ b/linux/drivers/media/video/zr364xx.c @@ -117,6 +117,7 @@ struct zr364xx_camera { int height; int method; struct mutex lock; + int users; }; @@ -642,25 +643,22 @@ static int zr364xx_open(struct inode *inode, struct file *file) DBG("zr364xx_open"); - cam->skip = 2; + mutex_lock(&cam->lock); - lock_kernel(); - err = video_exclusive_open(inode, file); - if (err < 0) { - unlock_kernel(); - return err; + if (cam->users) { + err = -EBUSY; + goto out; } if (!cam->framebuf) { cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES); if (!cam->framebuf) { info("vmalloc_32 failed!"); - unlock_kernel(); - return -ENOMEM; + err = -ENOMEM; + goto out; } } - mutex_lock(&cam->lock); for (i = 0; init[cam->method][i].size != -1; i++) { err = send_control_msg(udev, 1, init[cam->method][i].value, @@ -668,22 +666,23 @@ static int zr364xx_open(struct inode *inode, struct file *file) init[cam->method][i].size); if (err < 0) { info("error during open sequence: %d", i); - mutex_unlock(&cam->lock); - unlock_kernel(); - return err; + goto out; } } + cam->skip = 2; + cam->users++; file->private_data = vdev; /* Added some delay here, since opening/closing the camera quickly, * like Ekiga does during its startup, can crash the webcam */ mdelay(100); + err = 0; +out: mutex_unlock(&cam->lock); - unlock_kernel(); - return 0; + return err; } @@ -704,6 +703,10 @@ static int zr364xx_release(struct inode *inode, struct file *file) udev = cam->udev; mutex_lock(&cam->lock); + + cam->users--; + file->private_data = NULL; + for (i = 0; i < 2; i++) { err = send_control_msg(udev, 1, init[cam->method][i].value, @@ -711,21 +714,19 @@ static int zr364xx_release(struct inode *inode, struct file *file) init[cam->method][i].size); if (err < 0) { info("error during release sequence"); - mutex_unlock(&cam->lock); - return err; + goto out; } } - file->private_data = NULL; - video_exclusive_release(inode, file); - /* Added some delay here, since opening/closing the camera quickly, * like Ekiga does during its startup, can crash the webcam */ mdelay(100); + err = 0; +out: mutex_unlock(&cam->lock); - return 0; + return err; } |