diff options
Diffstat (limited to 'linux/drivers/media/video')
53 files changed, 5674 insertions, 838 deletions
diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 9b8f79afe..dc71eaea6 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -501,10 +501,21 @@ config DISPLAY_DAVINCI_DM646X_EVM select VIDEO_ADV7343 select VIDEO_THS7303 help - Support for DaVinci based display device. + Support for DM6467 based display device. To compile this driver as a module, choose M here: the - module will be called davincihd_display. + module will be called vpif_display. + +config CAPTURE_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Capture" + depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG + select VIDEO_DAVINCI_VPIF + help + Support for DM6467 based capture device. + + To compile this driver as a module, choose M here: the + module will be called vpif_capture. config VIDEO_DAVINCI_VPIF tristate "DaVinci VPIF Driver" diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index ae13a1546..ede519748 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -231,7 +231,7 @@ MODULE_PARM_DESC(enc_pcm_bufs, "Number of encoder PCM buffers\n" "\t\t\tDefault is computed from other enc_pcm_* parameters"); -MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card"); +MODULE_PARM_DESC(cx18_first_minor, "Set device node number assigned to first card"); MODULE_AUTHOR("Hans Verkuil"); MODULE_DESCRIPTION("CX23418 driver"); diff --git a/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c index fd789fad1..b44220b68 100644 --- a/linux/drivers/media/video/cx18/cx18-i2c.c +++ b/linux/drivers/media/video/cx18/cx18-i2c.c @@ -99,12 +99,18 @@ static const char * const hw_devicenames[] = { "ir_rx_z8f0811_haup", }; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) +static const struct IR_i2c_init_data z8f0811_ir_init_data = { + .ir_codes = &ir_codes_hauppauge_new_table, + .internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR, + .type = IR_TYPE_RC5, + .name = "CX23418 Z8F0811 Hauppauge", +}; + static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, u8 addr) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) struct i2c_board_info info; - struct IR_i2c_init_data ir_init_data; unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; memset(&info, 0, sizeof(struct i2c_board_info)); @@ -113,22 +119,21 @@ static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, /* Our default information for ir-kbd-i2c.c to use */ switch (hw) { case CX18_HW_Z8F0811_IR_RX_HAUP: - memset(&ir_init_data, 0, sizeof(struct IR_i2c_init_data)); - ir_init_data.ir_codes = &ir_codes_hauppauge_new_table; - ir_init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; - ir_init_data.type = IR_TYPE_RC5; - ir_init_data.name = "CX23418 Z8F0811 Hauppauge"; - info.platform_data = &ir_init_data; + info.platform_data = (void *) &z8f0811_ir_init_data; break; default: break; } return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0; +} #else +static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, + u8 addr) +{ return -1; -#endif } +#endif int cx18_i2c_register(struct cx18 *cx, unsigned idx) { diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index c134927b3..dabe3fadc 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/drivers/media/video/cx18/cx18-streams.c @@ -250,9 +250,9 @@ static int cx18_reg_dev(struct cx18 *cx, int type) video_set_drvdata(s->video_dev, s); /* Register device. First try the desired minor, then any free one. */ - ret = video_register_device(s->video_dev, vfl_type, num); + ret = video_register_device_no_warn(s->video_dev, vfl_type, num); if (ret < 0) { - CX18_ERR("Couldn't register v4l2 device for %s kernel number %d\n", + CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n", s->name, num); video_device_release(s->video_dev); s->video_dev = NULL; diff --git a/linux/drivers/media/video/cx23885/cimax2.c b/linux/drivers/media/video/cx23885/cimax2.c index d717b9416..3143eb919 100644 --- a/linux/drivers/media/video/cx23885/cimax2.c +++ b/linux/drivers/media/video/cx23885/cimax2.c @@ -75,7 +75,6 @@ struct netup_ci_state { void *priv; }; -struct mutex gpio_mutex;/* Two CiMax's uses same GPIO lines */ int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg, u8 *buf, int len) @@ -183,10 +182,11 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, if (ret != 0) return ret; - mutex_lock(&gpio_mutex); + mutex_lock(&dev->gpio_lock); /* write addr */ cx_write(MC417_OEN, NETUP_EN_ALL); + msleep(2); cx_write(MC417_RWD, NETUP_CTRL_OFF | NETUP_ADLO | (0xff & addr)); cx_clear(MC417_RWD, NETUP_ADLO); @@ -194,9 +194,10 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, NETUP_ADHI | (0xff & (addr >> 8))); cx_clear(MC417_RWD, NETUP_ADHI); - if (read) /* data in */ + if (read) { /* data in */ cx_write(MC417_OEN, NETUP_EN_ALL | NETUP_DATA); - else /* data out */ + msleep(2); + } else /* data out */ cx_write(MC417_RWD, NETUP_CTRL_OFF | data); /* choose chip */ @@ -206,7 +207,7 @@ int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, cx_clear(MC417_RWD, (read) ? NETUP_RD : NETUP_WR); mem = netup_ci_get_mem(dev); - mutex_unlock(&gpio_mutex); + mutex_unlock(&dev->gpio_lock); if (!read) if (mem < 0) @@ -411,7 +412,6 @@ int netup_ci_init(struct cx23885_tsport *port) switch (port->nr) { case 1: state->ci_i2c_addr = 0x40; - mutex_init(&gpio_mutex); break; case 2: state->ci_i2c_addr = 0x41; diff --git a/linux/drivers/media/video/cx23885/cx23885-core.c b/linux/drivers/media/video/cx23885/cx23885-core.c index 4854d54f1..2479286ef 100644 --- a/linux/drivers/media/video/cx23885/cx23885-core.c +++ b/linux/drivers/media/video/cx23885/cx23885-core.c @@ -759,6 +759,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) int i; mutex_init(&dev->lock); + mutex_init(&dev->gpio_lock); atomic_inc(&dev->refcount); diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 9c690a9bd..6333d7d18 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -256,15 +256,18 @@ static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = { static struct tda18271_config hauppauge_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda18271_config hauppauge_hvr1200_tuner_config = { .std_map = &hauppauge_hvr1200_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda18271_config hauppauge_hvr1210_tuner_config = { .gate = TDA18271_GATE_DIGITAL, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda18271_std_map hauppauge_hvr127x_std_map = { @@ -276,6 +279,7 @@ static struct tda18271_std_map hauppauge_hvr127x_std_map = { static struct tda18271_config hauppauge_hvr127x_config = { .std_map = &hauppauge_hvr127x_std_map, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct lgdt3305_config hauppauge_lgdt3305_config = { diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index 6ddabc263..7605871ab 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -326,6 +326,7 @@ struct cx23885_dev { int nr; struct mutex lock; + struct mutex gpio_lock; /* board details */ unsigned int board; diff --git a/linux/drivers/media/video/cx23885/netup-eeprom.c b/linux/drivers/media/video/cx23885/netup-eeprom.c index 26b934cd7..747a0a57c 100644 --- a/linux/drivers/media/video/cx23885/netup-eeprom.c +++ b/linux/drivers/media/video/cx23885/netup-eeprom.c @@ -97,12 +97,12 @@ void netup_get_card_info(struct i2c_adapter *i2c_adap, { int i, j; - cinfo->rev = netup_eeprom_read(i2c_adap, 13); + cinfo->rev = netup_eeprom_read(i2c_adap, 63); - for (i = 0, j = 0; i < 6; i++, j++) + for (i = 64, j = 0; i < 70; i++, j++) cinfo->port[0].mac[j] = netup_eeprom_read(i2c_adap, i); - for (i = 6, j = 0; i < 12; i++, j++) + for (i = 70, j = 0; i < 76; i++, j++) cinfo->port[1].mac[j] = netup_eeprom_read(i2c_adap, i); #if 0 printk(KERN_INFO "NetUP Dual DVB-S2 CI card rev=0x%x MAC1=%02X:%02X:" diff --git a/linux/drivers/media/video/dabusb.c b/linux/drivers/media/video/dabusb.c index cb0a41c45..a0812ce91 100644 --- a/linux/drivers/media/video/dabusb.c +++ b/linux/drivers/media/video/dabusb.c @@ -813,8 +813,18 @@ static struct file_operations dabusb_fops = .release = dabusb_release, }; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) +static char *dabusb_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + +#endif static struct usb_class_driver dabusb_class = { .name = "dabusb%d", +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) + .nodename = dabusb_nodename, +#endif .fops = &dabusb_fops, .minor_base = DABUSB_MINOR, }; diff --git a/linux/drivers/media/video/davinci/Makefile b/linux/drivers/media/video/davinci/Makefile index f44cad2f5..1a8b8f3f1 100644 --- a/linux/drivers/media/video/davinci/Makefile +++ b/linux/drivers/media/video/davinci/Makefile @@ -7,6 +7,8 @@ obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o #DM646x EVM Display driver obj-$(CONFIG_DISPLAY_DAVINCI_DM646X_EVM) += vpif_display.o +#DM646x EVM Capture driver +obj-$(CONFIG_CAPTURE_DAVINCI_DM646X_EVM) += vpif_capture.o # Capture: DM6446 and DM355 obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o diff --git a/linux/drivers/media/video/davinci/vpif.c b/linux/drivers/media/video/davinci/vpif.c index aa771268a..3b8eac31e 100644 --- a/linux/drivers/media/video/davinci/vpif.c +++ b/linux/drivers/media/video/davinci/vpif.c @@ -19,7 +19,11 @@ #include <linux/init.h> #include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> #include <linux/kernel.h> +#include <linux/io.h> +#include <mach/hardware.h> #include "vpif.h" @@ -31,6 +35,12 @@ MODULE_LICENSE("GPL"); #define VPIF_CH2_MAX_MODES (15) #define VPIF_CH3_MAX_MODES (02) +static resource_size_t res_len; +static struct resource *res; +spinlock_t vpif_lock; + +void __iomem *vpif_base; + static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val) { if (val) @@ -151,17 +161,17 @@ static void config_vpif_params(struct vpif_params *vpifparams, else if (config->capture_format) { /* Set the polarity of various pins */ vpif_wr_bit(reg, VPIF_CH_FID_POLARITY_BIT, - vpifparams->params.raw_params.fid_pol); + vpifparams->iface.fid_pol); vpif_wr_bit(reg, VPIF_CH_V_VALID_POLARITY_BIT, - vpifparams->params.raw_params.vd_pol); + vpifparams->iface.vd_pol); vpif_wr_bit(reg, VPIF_CH_H_VALID_POLARITY_BIT, - vpifparams->params.raw_params.hd_pol); + vpifparams->iface.hd_pol); value = regr(reg); /* Set data width */ value &= ((~(unsigned int)(0x3)) << VPIF_CH_DATA_WIDTH_BIT); - value |= ((vpifparams->params.raw_params.data_sz) << + value |= ((vpifparams->params.data_sz) << VPIF_CH_DATA_WIDTH_BIT); regw(value, reg); } @@ -227,8 +237,60 @@ int vpif_channel_getfid(u8 channel_id) } EXPORT_SYMBOL(vpif_channel_getfid); -void vpif_base_addr_init(void __iomem *base) +static int __init vpif_probe(struct platform_device *pdev) +{ + int status = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + res_len = res->end - res->start + 1; + + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + vpif_base = ioremap(res->start, res_len); + if (!vpif_base) { + status = -EBUSY; + goto fail; + } + + spin_lock_init(&vpif_lock); + dev_info(&pdev->dev, "vpif probe success\n"); + return 0; + +fail: + release_mem_region(res->start, res_len); + return status; +} + +static int vpif_remove(struct platform_device *pdev) { - vpif_base = base; + iounmap(vpif_base); + release_mem_region(res->start, res_len); + return 0; } -EXPORT_SYMBOL(vpif_base_addr_init); + +static struct platform_driver vpif_driver = { + .driver = { + .name = "vpif", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(vpif_remove), + .probe = vpif_probe, +}; + +static void vpif_exit(void) +{ + platform_driver_unregister(&vpif_driver); +} + +static int __init vpif_init(void) +{ + return platform_driver_register(&vpif_driver); +} +subsys_initcall(vpif_init); +module_exit(vpif_exit); + diff --git a/linux/drivers/media/video/davinci/vpif.h b/linux/drivers/media/video/davinci/vpif.h index fca26dcb5..188841b47 100644 --- a/linux/drivers/media/video/davinci/vpif.h +++ b/linux/drivers/media/video/davinci/vpif.h @@ -19,6 +19,7 @@ #include <linux/io.h> #include <linux/videodev2.h> #include <mach/hardware.h> +#include <mach/dm646x.h> /* Maximum channel allowed */ #define VPIF_NUM_CHANNELS (4) @@ -26,7 +27,9 @@ #define VPIF_DISPLAY_NUM_CHANNELS (2) /* Macros to read/write registers */ -static void __iomem *vpif_base; +extern void __iomem *vpif_base; +extern spinlock_t vpif_lock; + #define regr(reg) readl((reg) + vpif_base) #define regw(value, reg) writel(value, (reg + vpif_base)) @@ -280,6 +283,10 @@ static inline void enable_channel1(int enable) /* inline function to enable interrupt for channel0 */ static inline void channel0_intr_enable(int enable) { + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + if (enable) { regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); @@ -292,11 +299,16 @@ static inline void channel0_intr_enable(int enable) regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0), VPIF_INTEN_SET); } + spin_unlock_irqrestore(&vpif_lock, flags); } /* inline function to enable interrupt for channel1 */ static inline void channel1_intr_enable(int enable) { + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + if (enable) { regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); @@ -309,6 +321,7 @@ static inline void channel1_intr_enable(int enable) regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1), VPIF_INTEN_SET); } + spin_unlock_irqrestore(&vpif_lock, flags); } /* inline function to set buffer addresses in case of Y/C non mux mode */ @@ -431,6 +444,10 @@ static inline void enable_channel3(int enable) /* inline function to enable interrupt for channel2 */ static inline void channel2_intr_enable(int enable) { + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + if (enable) { regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); @@ -442,11 +459,16 @@ static inline void channel2_intr_enable(int enable) regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2), VPIF_INTEN_SET); } + spin_unlock_irqrestore(&vpif_lock, flags); } /* inline function to enable interrupt for channel3 */ static inline void channel3_intr_enable(int enable) { + unsigned long flags; + + spin_lock_irqsave(&vpif_lock, flags); + if (enable) { regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); @@ -459,6 +481,7 @@ static inline void channel3_intr_enable(int enable) regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3), VPIF_INTEN_SET); } + spin_unlock_irqrestore(&vpif_lock, flags); } /* inline function to enable raw vbi data for channel2 */ @@ -571,7 +594,7 @@ struct vpif_channel_config_params { v4l2_std_id stdid; }; -struct vpif_interface; +struct vpif_video_params; struct vpif_params; struct vpif_vbi_params; @@ -579,13 +602,6 @@ int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id); void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams, u8 channel_id); int vpif_channel_getfid(u8 channel_id); -void vpif_base_addr_init(void __iomem *base); - -/* Enumerated data types */ -enum vpif_capture_pinpol { - VPIF_CAPTURE_PINPOL_SAME = 0, - VPIF_CAPTURE_PINPOL_INVERT = 1 -}; enum data_size { _8BITS = 0, @@ -593,13 +609,6 @@ enum data_size { _12BITS, }; -struct vpif_capture_params_raw { - enum data_size data_sz; - enum vpif_capture_pinpol fid_pol; - enum vpif_capture_pinpol vd_pol; - enum vpif_capture_pinpol hd_pol; -}; - /* Structure for vpif parameters for raw vbi data */ struct vpif_vbi_params { __u32 hstart0; /* Horizontal start of raw vbi data for first field */ @@ -613,18 +622,19 @@ struct vpif_vbi_params { }; /* structure for vpif parameters */ -struct vpif_interface { +struct vpif_video_params { __u8 storage_mode; /* Indicates field or frame mode */ unsigned long hpitch; v4l2_std_id stdid; }; struct vpif_params { - struct vpif_interface video_params; + struct vpif_interface iface; + struct vpif_video_params video_params; struct vpif_channel_config_params std_info; union param { struct vpif_vbi_params vbi_params; - struct vpif_capture_params_raw raw_params; + enum data_size data_sz; } params; }; diff --git a/linux/drivers/media/video/davinci/vpif_display.c b/linux/drivers/media/video/davinci/vpif_display.c index 0f0ec8dc6..c015da813 100644 --- a/linux/drivers/media/video/davinci/vpif_display.c +++ b/linux/drivers/media/video/davinci/vpif_display.c @@ -683,7 +683,7 @@ static int vpif_release(struct file *filep) static int vpif_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct vpif_config *config = vpif_dev->platform_data; + struct vpif_display_config *config = vpif_dev->platform_data; cap->version = VPIF_DISPLAY_VERSION_CODE; cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; @@ -1053,7 +1053,7 @@ static int vpif_streamon(struct file *file, void *priv, struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; struct vpif_params *vpif = &ch->vpifparams; - struct vpif_config *vpif_config_data = + struct vpif_display_config *vpif_config_data = vpif_dev->platform_data; unsigned long addr = 0; int ret = 0; @@ -1239,7 +1239,7 @@ static int vpif_enum_output(struct file *file, void *fh, struct v4l2_output *output) { - struct vpif_config *config = vpif_dev->platform_data; + struct vpif_display_config *config = vpif_dev->platform_data; if (output->index >= config->output_count) { vpif_dbg(1, debug, "Invalid output index\n"); @@ -1422,7 +1422,8 @@ vpif_init_free_channel_objects: */ static __init int vpif_probe(struct platform_device *pdev) { - const struct subdev_info *subdevdata; + struct vpif_subdev_info *subdevdata; + struct vpif_display_config *config; int i, j = 0, k, q, m, err = 0; struct i2c_adapter *i2c_adap; struct vpif_config *config; @@ -1433,30 +1434,14 @@ static __init int vpif_probe(struct platform_device *pdev) int subdev_count; vpif_dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - v4l2_err(vpif_dev->driver, - "Error getting platform resource\n"); - return -ENOENT; - } - if (!request_mem_region(res->start, res->end - res->start + 1, - vpif_dev->driver->name)) { - v4l2_err(vpif_dev->driver, "VPIF: failed request_mem_region\n"); - return -ENXIO; - } + err = initialize_vpif(); - vpif_base = ioremap_nocache(res->start, res->end - res->start + 1); - if (!vpif_base) { - v4l2_err(vpif_dev->driver, "Unable to ioremap VPIF reg\n"); - err = -ENXIO; - goto resource_exit; + if (err) { + v4l2_err(vpif_dev->driver, "Error initializing vpif\n"); + return err; } - vpif_base_addr_init(vpif_base); - - initialize_vpif(); - err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); if (err) { v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); @@ -1489,7 +1474,7 @@ static __init int vpif_probe(struct platform_device *pdev) video_device_release(ch->video_dev); } err = -ENOMEM; - goto video_dev_alloc_exit; + goto vpif_int_err; } /* Initialize field of video device */ @@ -1566,10 +1551,10 @@ static __init int vpif_probe(struct platform_device *pdev) } for (i = 0; i < subdev_count; i++) { - vpif_obj.sd[i] = v4l2_i2c_new_subdev(&vpif_obj.v4l2_dev, + vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, i2c_adap, subdevdata[i].name, - subdevdata[i].name, - 0, I2C_ADDRS(subdevdata[i].addr)); + &subdevdata[i].board_info, + NULL); if (!vpif_obj.sd[i]) { vpif_err("Error registering v4l2 subdevice\n"); goto probe_subdev_out; @@ -1599,11 +1584,6 @@ vpif_int_err: res = platform_get_resource(pdev, IORESOURCE_IRQ, k-1); m = res->end; } -video_dev_alloc_exit: - iounmap(vpif_base); -resource_exit: - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); return err; } @@ -1666,9 +1646,6 @@ static void vpif_cleanup(void) i++; } - iounmap(vpif_base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); platform_driver_unregister(&vpif_driver); kfree(vpif_obj.sd); for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) diff --git a/linux/drivers/media/video/em28xx/Kconfig b/linux/drivers/media/video/em28xx/Kconfig index 6524b493e..c7be0e097 100644 --- a/linux/drivers/media/video/em28xx/Kconfig +++ b/linux/drivers/media/video/em28xx/Kconfig @@ -36,6 +36,7 @@ config VIDEO_EM28XX_DVB depends on VIDEO_EM28XX && DVB_CORE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the diff --git a/linux/drivers/media/video/em28xx/Makefile b/linux/drivers/media/video/em28xx/Makefile index 8137a8c94..d0f093d1d 100644 --- a/linux/drivers/media/video/em28xx/Makefile +++ b/linux/drivers/media/video/em28xx/Makefile @@ -1,5 +1,5 @@ em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \ - em28xx-input.o + em28xx-input.o em28xx-vbi.o em28xx-alsa-objs := em28xx-audio.o diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 656604dc9..2a7e42673 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -183,6 +183,19 @@ static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = { { -1, -1, -1, -1}, }; +/* eb1a:2868 Reddo DVB-C USB TV Box + GPIO4 - CU1216L NIM + Other GPIOs seems to be don't care. */ +static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = { + {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, + {EM28XX_R08_GPIO, 0xde, 0xff, 10}, + {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {EM28XX_R08_GPIO, 0x7f, 0xff, 10}, + {EM28XX_R08_GPIO, 0x6f, 0xff, 10}, + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {-1, -1, -1, -1}, +}; /* Callback for the most boards */ static struct em28xx_reg_seq default_tuner_gpio[] = { @@ -314,6 +327,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_TERRATEC_CINERGY_250] = { .name = "Terratec Cinergy 250 USB", .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .has_ir_i2c = 1, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, .input = { { @@ -333,6 +347,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_PINNACLE_USB_2] = { .name = "Pinnacle PCTV USB 2", .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .has_ir_i2c = 1, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, .input = { { @@ -357,6 +372,7 @@ struct em28xx_board em28xx_boards[] = { TDA9887_PORT2_ACTIVE, .decoder = EM28XX_TVP5150, .has_msp34xx = 1, + .has_ir_i2c = 1, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1004,6 +1020,7 @@ struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_TERRATEC_CINERGY_200] = { .name = "Terratec Cinergy 200 USB", .is_em2800 = 1, + .has_ir_i2c = 1, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, @@ -1077,7 +1094,8 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_PINNACLE_DVC_90] = { - .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker", + .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker " + "/ Kworld DVD Maker 2", .tuner_type = TUNER_ABSENT, /* capture only board */ .decoder = EM28XX_SAA711X, .input = { { @@ -1625,6 +1643,14 @@ struct em28xx_board em28xx_boards[] = { .gpio = evga_indtube_analog, } }, }, + /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 + + Infineon TUA6034) */ + [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = { + .name = "Reddo DVB-C USB TV Box", + .tuner_type = TUNER_ABSENT, + .has_dvb = 1, + .dvb_gpio = reddo_dvb_c_usb_box, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1652,6 +1678,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2883), .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2868), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0xe300), .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U }, { USB_DEVICE(0xeb1a, 0xe303), @@ -1682,6 +1710,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2870_BOARD_KWORLD_355U }, { USB_DEVICE(0x1b80, 0xe302), .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */ + { USB_DEVICE(0x1b80, 0xe304), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */ { USB_DEVICE(0x0ccd, 0x0036), .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 }, { USB_DEVICE(0x0ccd, 0x004c), @@ -1757,6 +1787,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, + {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -2275,11 +2306,9 @@ void em28xx_register_i2c_ir(struct em28xx *dev) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (disable_ir) { ir->get_key = NULL; - return ; + return; } #else - struct i2c_board_info info; - struct IR_i2c_init_data init_data; const unsigned short addr_list[] = { 0x30, 0x47, I2C_CLIENT_END }; @@ -2287,68 +2316,56 @@ void em28xx_register_i2c_ir(struct em28xx *dev) if (disable_ir) return; - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&init_data, 0, sizeof(struct IR_i2c_init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + memset(&dev->info, 0, sizeof(&dev->info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE); #endif /* detect & configure */ switch (dev->model) { - case (EM2800_BOARD_UNKNOWN): - break; - case (EM2820_BOARD_UNKNOWN): - break; - case (EM2800_BOARD_TERRATEC_CINERGY_200): - case (EM2820_BOARD_TERRATEC_CINERGY_250): + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->ir_codes = &ir_codes_em_terratec_table; ir->get_key = em28xx_get_key_terratec; snprintf(ir->name, sizeof(ir->name), "i2c IR (EM28XX Terratec)"); #else - init_data.ir_codes = &ir_codes_em_terratec_table; - init_data.get_key = em28xx_get_key_terratec; - init_data.name = "i2c IR (EM28XX Terratec)"; + dev->init_data.ir_codes = &ir_codes_em_terratec_table; + dev->init_data.get_key = em28xx_get_key_terratec; + dev->init_data.name = "i2c IR (EM28XX Terratec)"; #endif break; - case (EM2820_BOARD_PINNACLE_USB_2): + case EM2820_BOARD_PINNACLE_USB_2: #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->ir_codes = &ir_codes_pinnacle_grey_table; ir->get_key = em28xx_get_key_pinnacle_usb_grey; snprintf(ir->name, sizeof(ir->name), "i2c IR (EM28XX Pinnacle PCTV)"); #else - init_data.ir_codes = &ir_codes_pinnacle_grey_table; - init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; + dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; + dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; + dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; #endif break; - case (EM2820_BOARD_HAUPPAUGE_WINTV_USB_2): + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->ir_codes = &ir_codes_hauppauge_new_table; ir->get_key = em28xx_get_key_em_haup; snprintf(ir->name, sizeof(ir->name), "i2c IR (EM2840 Hauppauge)"); #else - init_data.ir_codes = &ir_codes_hauppauge_new_table; - init_data.get_key = em28xx_get_key_em_haup; - init_data.name = "i2c IR (EM2840 Hauppauge)"; + dev->init_data.ir_codes = &ir_codes_hauppauge_new_table; + dev->init_data.get_key = em28xx_get_key_em_haup; + dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; #endif break; - case (EM2820_BOARD_MSI_VOX_USB_2): - break; - case (EM2800_BOARD_LEADTEK_WINFAST_USBII): - break; - case (EM2800_BOARD_KWORLD_USB2800): - break; - case (EM2800_BOARD_GRABBEEX_USB2800): - break; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) - if (init_data.name) - info.platform_data = &init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); + if (dev->init_data.name) + dev->info.platform_data = &dev->init_data; + i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list); #endif } @@ -2385,7 +2402,7 @@ void em28xx_card_setup(struct em28xx *dev) case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: { struct tveeprom tv; -#ifdef CONFIG_MODULES +#if defined(CONFIG_MODULES) && defined(MODULE) request_module("tveeprom"); #endif /* Call first TVeeprom */ @@ -2399,10 +2416,6 @@ void em28xx_card_setup(struct em28xx *dev) dev->i2s_speed = 2048000; dev->board.has_msp34xx = 1; } -#ifdef CONFIG_MODULES - if (tv.has_ir) - request_module("ir-kbd-i2c"); -#endif break; } case EM2882_BOARD_KWORLD_ATSC_315U: @@ -2443,6 +2456,10 @@ void em28xx_card_setup(struct em28xx *dev) break; } +#if defined(CONFIG_MODULES) && defined(MODULE) + if (dev->board.has_ir_i2c && !disable_ir) + request_module("ir-kbd-i2c"); +#endif if (dev->board.has_snapshot_button) em28xx_register_snapshot_button(dev); @@ -2700,7 +2717,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, * Default format, used for tvp5150 or saa711x output formats */ dev->vinmode = 0x10; - dev->vinctl = 0x11; + dev->vinctl = EM28XX_VINCTRL_INTERLACED | + EM28XX_VINCTRL_CCIR656_ENABLE; /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); @@ -2719,6 +2737,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); + INIT_LIST_HEAD(&dev->vbiq.active); + INIT_LIST_HEAD(&dev->vbiq.queued); #if 0 video_set_drvdata(dev->vbi_dev, dev); diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 46cb13182..1b9a0953e 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -54,6 +54,10 @@ static int alt = EM28XX_PINOUT; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); +static unsigned int disable_vbi; +module_param(disable_vbi, int, 0644); +MODULE_PARM_DESC(disable_vbi, "disable vbi support"); + /* FIXME */ #define em28xx_isocdbg(fmt, arg...) do {\ if (core_debug) \ @@ -648,9 +652,24 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } +int em28xx_vbi_supported(struct em28xx *dev) +{ + /* Modprobe option to manually disable */ + if (disable_vbi == 1) + return 0; + + if (dev->chip_id == CHIP_ID_EM2860 || + dev->chip_id == CHIP_ID_EM2883) + return 1; + + /* Version of em28xx that does not support VBI */ + return 0; +} + int em28xx_set_outfmt(struct em28xx *dev) { int ret; + u8 vinctrl; ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, dev->format->reg | 0x20, 0xff); @@ -661,7 +680,16 @@ int em28xx_set_outfmt(struct em28xx *dev) if (ret < 0) return ret; - return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, dev->vinctl); + vinctrl = dev->vinctl; + if (em28xx_vbi_supported(dev) == 1) { + vinctrl |= EM28XX_VINCTRL_VBI_RAW; + em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); + em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, 0xb4); + em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, 0x0c); + } + + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, @@ -732,7 +760,14 @@ int em28xx_resolution_set(struct em28xx *dev) em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); - em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); + + /* If we don't set the start position to 4 in VBI mode, we end up + with line 21 being YUYV encoded instead of being in 8-bit + greyscale */ + if (em28xx_vbi_supported(dev) == 1) + em28xx_capture_area_set(dev, 0, 4, width >> 2, height >> 2); + else + em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } @@ -853,8 +888,7 @@ static void em28xx_irq_callback(struct urb *urb, struct pt_regs *regs) static void em28xx_irq_callback(struct urb *urb) #endif { - struct em28xx_dmaqueue *dma_q = urb->context; - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + struct em28xx *dev = urb->context; int rc, i; switch (urb->status) { @@ -939,6 +973,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) { struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; int i; int sb_size, pipe; struct urb *urb; @@ -968,7 +1003,8 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, } dev->isoc_ctl.max_pkt_size = max_pkt_size; - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; + dev->isoc_ctl.vbi_buf = NULL; sb_size = max_packets * dev->isoc_ctl.max_pkt_size; @@ -1003,7 +1039,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, usb_fill_int_urb(urb, dev->udev, pipe, dev->isoc_ctl.transfer_buffer[i], sb_size, - em28xx_irq_callback, dma_q, 1); + em28xx_irq_callback, dev, 1); urb->number_of_packets = max_packets; urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; @@ -1018,6 +1054,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, } init_waitqueue_head(&dma_q->wq); + init_waitqueue_head(&vbi_dma_q->wq); em28xx_capture_start(dev, 1); @@ -1103,7 +1140,7 @@ struct em28xx *em28xx_get_device(int minor, list_for_each_entry(h, &em28xx_devlist, devlist) { if (h->vdev->minor == minor) dev = h; - if (h->vbi_dev->minor == minor) { + if (h->vbi_dev && h->vbi_dev->minor == minor) { dev = h; *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; } diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 3d522d547..cbdba11a7 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -34,6 +34,7 @@ #include "s5h1409.h" #include "mt352.h" #include "mt352_priv.h" /* FIXME */ +#include "tda1002x.h" MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -296,6 +297,11 @@ static struct mt352_config terratec_xs_mt352_cfg = { .demod_init = mt352_terratec_xs_init, }; +static struct tda10023_config em28xx_tda10023_config = { + .demod_address = 0x0c, + .invert = 1, +}; + /* ------------------------------------------------------------------ */ static int attach_xc3028(u8 addr, struct em28xx *dev) @@ -550,6 +556,19 @@ static int dvb_init(struct em28xx *dev) } break; #endif + case EM2870_BOARD_REDDO_DVB_C_USB_BOX: + /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */ + dvb->frontend = dvb_attach(tda10023_attach, + &em28xx_tda10023_config, + &dev->i2c_adap, 0x48); + if (dvb->frontend) { + if (!dvb_attach(simple_tuner_attach, dvb->frontend, + &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) { + result = -EINVAL; + goto out_free; + } + } + break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" " isn't supported yet\n", diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 6bf84bd78..ed12e7ffc 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -86,7 +86,19 @@ #define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b #define EM28XX_R10_VINMODE 0x10 + #define EM28XX_R11_VINCTRL 0x11 + +/* em28xx Video Input Control Register 0x11 */ +#define EM28XX_VINCTRL_VBI_SLICED 0x80 +#define EM28XX_VINCTRL_VBI_RAW 0x40 +#define EM28XX_VINCTRL_VOUT_MODE_IN 0x20 /* HREF,VREF,VACT in output */ +#define EM28XX_VINCTRL_CCIR656_ENABLE 0x10 +#define EM28XX_VINCTRL_VBI_16BIT_RAW 0x08 /* otherwise 8-bit raw */ +#define EM28XX_VINCTRL_FID_ON_HREF 0x04 +#define EM28XX_VINCTRL_DUAL_EDGE_STROBE 0x02 +#define EM28XX_VINCTRL_INTERLACED 0x01 + #define EM28XX_R12_VINENABLE 0x12 /* */ #define EM28XX_R14_GAMMA 0x14 @@ -135,6 +147,10 @@ #define EM28XX_R31_HSCALEHIGH 0x31 #define EM28XX_R32_VSCALELOW 0x32 #define EM28XX_R33_VSCALEHIGH 0x33 +#define EM28XX_R34_VBI_START_H 0x34 +#define EM28XX_R35_VBI_START_V 0x35 +#define EM28XX_R36_VBI_WIDTH 0x36 +#define EM28XX_R37_VBI_HEIGHT 0x37 #define EM28XX_R40_AC97LSB 0x40 #define EM28XX_R41_AC97MSB 0x41 diff --git a/linux/drivers/media/video/em28xx/em28xx-vbi.c b/linux/drivers/media/video/em28xx/em28xx-vbi.c new file mode 100644 index 000000000..94943e5a1 --- /dev/null +++ b/linux/drivers/media/video/em28xx/em28xx-vbi.c @@ -0,0 +1,142 @@ +/* + em28xx-vbi.c - VBI driver for em28xx + + Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> + + This work was sponsored by EyeMagnet Limited. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> + +#include "em28xx.h" + +static unsigned int vbibufs = 5; +module_param(vbibufs, int, 0644); +MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); + +static unsigned int vbi_debug; +module_param(vbi_debug, int, 0644); +MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); + +#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg) + +/* ------------------------------------------------------------------ */ + +static void +free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.vbi_buf == buf) + dev->isoc_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + *size = 720 * 12 * 2; + if (0 == *count) + *count = vbibufs; + if (*count < 2) + *count = 2; + if (*count > 32) + *count = 32; + return 0; +} + +static int +vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + int rc = 0; + unsigned int size; + + size = 720 * 12 * 2; + + buf->vb.size = size; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = 720; + buf->vb.height = 12; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(q, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(q, buf); + return rc; +} + +static void +vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, + struct em28xx_buffer, + vb); + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vbiq->active); +} + +static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + free_buffer(q, buf); +} + +struct videobuf_queue_ops em28xx_vbi_qops = { + .buf_setup = vbi_setup, + .buf_prepare = vbi_prepare, + .buf_queue = vbi_queue, + .buf_release = vbi_release, +}; diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index c7d723a03..edfe795ef 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -181,7 +181,24 @@ static inline void buffer_filled(struct em28xx *dev, buf->vb.field_count++; do_gettimeofday(&buf->vb.ts); - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +static inline void vbi_buffer_filled(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf) +{ + /* Advice that buffer was filled */ + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.vbi_buf = NULL; list_del(&buf->vb.queue); wake_up(&buf->vb.done); @@ -257,7 +274,8 @@ static void em28xx_copy_video(struct em28xx *dev, if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { - em28xx_isocdbg("Overflow of %zi bytes past buffer end (2)\n", + em28xx_isocdbg("Overflow of %zi bytes past buffer end" + "(2)\n", ((char *)startwrite + lencopy) - ((char *)outp + buf->vb.size)); lencopy = remain = (char *)outp + buf->vb.size - @@ -274,6 +292,67 @@ static void em28xx_copy_video(struct em28xx *dev, dma_q->pos += len; } +static void em28xx_copy_vbi(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + void *startwrite, *startread; + int offset; + int bytesperline = 720; + + if (dev == NULL) { + em28xx_isocdbg("dev is null\n"); + return; + } + + if (dma_q == NULL) { + em28xx_isocdbg("dma_q is null\n"); + return; + } + if (buf == NULL) { +#if 0 + /* Disable by default - too chatty */ + em28xx_isocdbg("buf is null\n"); +#endif + return; + } + if (p == NULL) { + em28xx_isocdbg("p is null\n"); + return; + } + if (outp == NULL) { + em28xx_isocdbg("outp is null\n"); + return; + } + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + if ((p[0] == 0x33 && p[1] == 0x95) || + (p[0] == 0x88 && p[1] == 0x88)) { + /* Header field, advance past it */ + p += 4; + } else { + len += 4; + } + + startread = p; + + startwrite = outp + dma_q->pos; + offset = dma_q->pos; + + /* Make sure the bottom field populates the second half of the frame */ + if (buf->top_field == 0) { + startwrite += bytesperline * 0x0c; + offset += bytesperline * 0x0c; + } + + memcpy(startwrite, startread, len); + dma_q->pos += len; +} + static inline void print_err_status(struct em28xx *dev, int packet, int status) { @@ -326,7 +405,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; *buf = NULL; return; } @@ -340,7 +419,38 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, memset(outp, 0, (*buf)->vb.size); #endif - dev->isoc_ctl.buf = *buf; + dev->isoc_ctl.vid_buf = *buf; + + return; +} + +/* + * video-buf generic routine to get the next available VBI buffer + */ +static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer **buf) +{ + struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq); +#if 1 + char *outp; +#endif + + if (list_empty(&dma_q->active)) { + em28xx_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.vbi_buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); +#if 1 + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0x00, (*buf)->vb.size); +#endif + + dev->isoc_ctl.vbi_buf = *buf; return; } @@ -351,7 +461,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf; - struct em28xx_dmaqueue *dma_q = urb->context; + struct em28xx_dmaqueue *dma_q = &dev->vidq; unsigned char *outp = NULL; int i, len = 0, rc = 1; unsigned char *p; @@ -368,7 +478,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) return 0; } - buf = dev->isoc_ctl.buf; + buf = dev->isoc_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); @@ -432,6 +542,153 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) return rc; } +/* Version of isoc handler that takes into account a mixture of video and + VBI data */ +static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) +{ + struct em28xx_buffer *buf, *vbi_buf; + struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + unsigned char *outp = NULL; + unsigned char *vbioutp = NULL; + int i, len = 0, rc = 1; + unsigned char *p; + int vbi_size; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->isoc_ctl.vid_buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + vbi_buf = dev->isoc_ctl.vbi_buf; + if (vbi_buf != NULL) + vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + len = urb->iso_frame_desc[i].actual_length - 4; + + if (urb->iso_frame_desc[i].actual_length <= 0) { + /* em28xx_isocdbg("packet %d is empty",i); - spammy */ + continue; + } + if (urb->iso_frame_desc[i].actual_length > + dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); + continue; + } + + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* capture type 0 = vbi start + capture type 1 = video start + capture type 2 = video in progress */ + if (p[0] == 0x33 && p[1] == 0x95) { + dev->capture_type = 0; + dev->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER!!!\n"); + dev->cur_field = p[2]; + } + + /* FIXME: get rid of hard-coded value */ + vbi_size = 720 * 0x0c; + + if (dev->capture_type == 0) { + if (dev->vbi_read >= vbi_size) { + /* We've already read all the VBI data, so + treat the rest as video */ + em28xx_isocdbg("dev->vbi_read > vbi_size\n"); + } else if ((dev->vbi_read + len) < vbi_size) { + /* This entire frame is VBI data */ + if (dev->vbi_read == 0 && + (!(dev->cur_field & 1))) { + /* Brand new frame */ + if (vbi_buf != NULL) + vbi_buffer_filled(dev, + vbi_dma_q, + vbi_buf); + vbi_get_next_buf(vbi_dma_q, &vbi_buf); + if (vbi_buf == NULL) + vbioutp = NULL; + else + vbioutp = videobuf_to_vmalloc( + &vbi_buf->vb); + } + + if (dev->vbi_read == 0) { + vbi_dma_q->pos = 0; + if (vbi_buf != NULL) { + if (dev->cur_field & 1) + vbi_buf->top_field = 0; + else + vbi_buf->top_field = 1; + } + } + + dev->vbi_read += len; + em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, len); + } else { + /* Some of this frame is VBI data and some is + video data */ + int vbi_data_len = vbi_size - dev->vbi_read; + dev->vbi_read += vbi_data_len; + em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, vbi_data_len); + dev->capture_type = 1; + p += vbi_data_len; + len -= vbi_data_len; + } + } + + if (dev->capture_type == 1) { + dev->capture_type = 2; + em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], + len, (p[2] & 1) ? "odd" : "even"); + + if (dev->progressive || !(dev->cur_field & 1)) { + if (buf != NULL) + buffer_filled(dev, dma_q, buf); + get_next_buf(dma_q, &buf); + if (buf == NULL) + outp = NULL; + else + outp = videobuf_to_vmalloc(&buf->vb); + } + if (buf != NULL) { + if (dev->cur_field & 1) + buf->top_field = 0; + else + buf->top_field = 1; + } + + dma_q->pos = 0; + } + if (buf != NULL && dev->capture_type == 2) + em28xx_copy_video(dev, dma_q, buf, p, outp, len); + } + return rc; +} + + /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ @@ -443,7 +700,8 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) struct em28xx *dev = fh->dev; struct v4l2_frequency f; - *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; + *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) + >> 3; if (0 == *count) *count = EM28XX_DEF_BUF; @@ -480,8 +738,8 @@ static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) VIDEOBUF_ACTIVE, it won't be, though. */ spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.buf == buf) - dev->isoc_ctl.buf = NULL; + if (dev->isoc_ctl.vid_buf == buf) + dev->isoc_ctl.vid_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); videobuf_vmalloc_free(&buf->vb); @@ -497,7 +755,8 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, struct em28xx *dev = fh->dev; int rc = 0, urb_init = 0; - buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; + buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + + 7) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; @@ -516,9 +775,16 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, urb_init = 1; if (urb_init) { - rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, dev->max_pkt_size, - em28xx_isoc_copy); + if (em28xx_vbi_supported(dev) == 1) + rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + em28xx_isoc_copy_vbi); + else + rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + em28xx_isoc_copy); if (rc < 0) goto fail; } @@ -600,34 +866,63 @@ static void video_mux(struct em28xx *dev, int index) } /* Usage lock check functions */ -static int res_get(struct em28xx_fh *fh) +static int res_get(struct em28xx_fh *fh, unsigned int bit) { struct em28xx *dev = fh->dev; - int rc = 0; - /* This instance already has stream_on */ - if (fh->stream_on) - return rc; + if (fh->resources & bit) + /* have it already allocated */ + return 1; - if (dev->stream_on) - return -EBUSY; + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->resources & bit) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->resources |= bit; + em28xx_videodbg("res: get %d\n", bit); + mutex_unlock(&dev->lock); + return 1; +} - dev->stream_on = 1; - fh->stream_on = 1; - return rc; +static int res_check(struct em28xx_fh *fh, unsigned int bit) +{ + return fh->resources & bit; } -static int res_check(struct em28xx_fh *fh) +static int res_locked(struct em28xx *dev, unsigned int bit) { - return fh->stream_on; + return dev->resources & bit; } -static void res_free(struct em28xx_fh *fh) +static void res_free(struct em28xx_fh *fh, unsigned int bits) { struct em28xx *dev = fh->dev; - fh->stream_on = 0; - dev->stream_on = 0; + BUG_ON((fh->resources & bits) != bits); + + mutex_lock(&dev->lock); + fh->resources &= ~bits; + dev->resources &= ~bits; + em28xx_videodbg("res: put %d\n", bits); + mutex_unlock(&dev->lock); +} + +static int get_ressource(struct em28xx_fh *fh) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return EM28XX_RESOURCE_VIDEO; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return EM28XX_RESOURCE_VBI; + default: + BUG(); + return 0; + } } /* @@ -804,7 +1099,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } else { /* width must even because of the YUYV format height must be even because of interlacing */ - v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); + v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, + 1, 0); } get_scale(dev, width, height, &hscale, &vscale); @@ -870,12 +1166,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, goto out; } - if (dev->stream_on && !fh->stream_on) { - em28xx_errdev("%s device in use by another fh\n", __func__); - rc = -EBUSY; - goto out; - } - rc = em28xx_set_video_format(dev, f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height); @@ -884,6 +1174,21 @@ out: return rc; } +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + *norm = dev->norm; + + return 0; +} + static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) { struct em28xx_fh *fh = priv; @@ -1441,20 +1746,25 @@ static int vidioc_streamon(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; + int rc = -EINVAL; rc = check_dev(dev); if (rc < 0) return rc; + if (unlikely(type != fh->type)) + return -EINVAL; - mutex_lock(&dev->lock); - rc = res_get(fh); + em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); - if (likely(rc >= 0)) - rc = videobuf_streamon(&fh->vb_vidq); + if (unlikely(!res_get(fh, get_ressource(fh)))) + return -EBUSY; - mutex_unlock(&dev->lock); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_streamon(&fh->vb_vidq); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_streamon(&fh->vb_vbiq); return rc; } @@ -1470,17 +1780,22 @@ static int vidioc_streamoff(struct file *file, void *priv, if (rc < 0) return rc; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) return -EINVAL; if (type != fh->type) return -EINVAL; - mutex_lock(&dev->lock); - - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); + em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); - mutex_unlock(&dev->lock); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + videobuf_streamoff(&fh->vb_vidq); + res_free(fh, EM28XX_RESOURCE_VIDEO); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + videobuf_streamoff(&fh->vb_vbiq); + res_free(fh, EM28XX_RESOURCE_VBI); + } return 0; } @@ -1498,13 +1813,13 @@ static int vidioc_querycap(struct file *file, void *priv, cap->version = EM28XX_VERSION_CODE; cap->capabilities = -#if 0 - V4L2_CAP_VBI_CAPTURE | -#endif V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (dev->vbi_dev) + cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + if (dev->audio_mode.has_audio) cap->capabilities |= V4L2_CAP_AUDIO; @@ -1572,40 +1887,45 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, return 0; } -#if 0 /* RAW VBI ioctls */ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *format) { - format->fmt.vbi.sampling_rate = 6750000 * 4; - format->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; + format->fmt.vbi.samples_per_line = 720; format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - format->fmt.vbi.offset = 64 * 4; - format->fmt.vbi.start[0] = norm->vbi_v_start_0; - format->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 + 1; - format->fmt.vbi.start[1] = norm->vbi_v_start_1; - format->fmt.vbi.count[1] = format->fmt.vbi.count[0]; - format->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */ + format->fmt.vbi.offset = 0; + format->fmt.vbi.flags = 0; + + /* Varies by video standard (NTSC, PAL, etc.) */ + /* FIXME: hard-coded for NTSC support */ + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */ + format->fmt.vbi.count[0] = 12; + format->fmt.vbi.count[1] = 12; + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; return 0; } -static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *format) { - format->type = V4L2_BUF_TYPE_VBI_CAPTURE; - format->fmt.vbi.sampling_rate = HZ; - format->fmt.vbi.samples_per_line = 2048; + format->fmt.vbi.samples_per_line = 720; format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - format->fmt.vbi.offset = 244; + format->fmt.vbi.offset = 0; format->fmt.vbi.flags = 0; - format->fmt.vbi.start[0] = 0; - format->fmt.vbi.start[1] = 0; + + /* Varies by video standard (NTSC, PAL, etc.) */ + /* FIXME: hard-coded for NTSC support */ + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */ + format->fmt.vbi.count[0] = 12; + format->fmt.vbi.count[1] = 12; + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; return 0; } -#endif static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) @@ -1618,7 +1938,10 @@ static int vidioc_reqbufs(struct file *file, void *priv, if (rc < 0) return rc; - return videobuf_reqbufs(&fh->vb_vidq, rb); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_reqbufs(&fh->vb_vidq, rb); + else + return videobuf_reqbufs(&fh->vb_vbiq, rb); } static int vidioc_querybuf(struct file *file, void *priv, @@ -1632,7 +1955,18 @@ static int vidioc_querybuf(struct file *file, void *priv, if (rc < 0) return rc; - return videobuf_querybuf(&fh->vb_vidq, b); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_querybuf(&fh->vb_vidq, b); + else { + /* FIXME: I'm not sure yet whether this is a bug in zvbi or + the videobuf framework, but we probably shouldn't be + returning a buffer larger than that which was asked for. + At a minimum, it causes a crash in zvbi since it does + a memcpy based on the source buffer length */ + int result = videobuf_querybuf(&fh->vb_vbiq, b); + b->length = 17280; + return result; + } } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) @@ -1645,7 +1979,10 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) if (rc < 0) return rc; - return videobuf_qbuf(&fh->vb_vidq, b); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_qbuf(&fh->vb_vidq, b); + else + return videobuf_qbuf(&fh->vb_vbiq, b); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) @@ -1658,7 +1995,12 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) if (rc < 0) return rc; - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & + O_NONBLOCK); + else + return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & + O_NONBLOCK); } #ifdef CONFIG_VIDEO_V4L1_COMPAT @@ -1666,7 +2008,10 @@ static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) { struct em28xx_fh *fh = priv; - return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); + else + return videobuf_cgmbuf(&fh->vb_vbiq, mbuf, 8); } #endif @@ -1798,7 +2143,7 @@ static int em28xx_v4l2_open(struct file *filp) #if 0 errCode = em28xx_set_mode(dev, EM28XX_ANALOG_MODE); if (errCode < 0) { - em28xx_errdev("Device locked on digital mode. Can't open analog\n"); + em28xx_errdev("Locked on digital mode. Can't open analog\n"); mutex_unlock(&dev->lock); return -EBUSY; } @@ -1846,8 +2191,15 @@ static int em28xx_v4l2_open(struct file *filp) field = V4L2_FIELD_INTERLACED; videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, - NULL, &dev->slock, fh->type, field, - sizeof(struct em28xx_buffer), fh); + NULL, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, field, + sizeof(struct em28xx_buffer), fh); + + videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops, + NULL, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct em28xx_buffer), fh); mutex_unlock(&dev->lock); @@ -1904,20 +2256,21 @@ static int em28xx_v4l2_close(struct file *filp) em28xx_videodbg("users=%d\n", dev->users); + if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { + videobuf_stop(&fh->vb_vidq); + res_free(fh, EM28XX_RESOURCE_VIDEO); + } - mutex_lock(&dev->lock); - if (res_check(fh)) - res_free(fh); + if (res_check(fh, EM28XX_RESOURCE_VBI)) { + videobuf_stop(&fh->vb_vbiq); + res_free(fh, EM28XX_RESOURCE_VBI); + } if (dev->users == 1) { - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - /* the device is already disconnect, free the remaining resources */ if (dev->state & DEV_DISCONNECTED) { em28xx_release_resources(dev); - mutex_unlock(&dev->lock); kfree(dev); return 0; } @@ -1938,10 +2291,12 @@ static int em28xx_v4l2_close(struct file *filp) "0 (error=%i)\n", errCode); } } + + videobuf_mmap_free(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vbiq); kfree(fh); dev->users--; wake_up_interruptible_nr(&dev->open, 1); - mutex_unlock(&dev->lock); return 0; } @@ -1966,16 +2321,22 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, */ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; + if (res_locked(dev, EM28XX_RESOURCE_VIDEO)) + return -EBUSY; return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, filp->f_flags & O_NONBLOCK); } + + + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + return -EBUSY; + + return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + return 0; } @@ -1993,17 +2354,17 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return POLLERR; - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); + } else { return POLLERR; - - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } } /* @@ -2019,14 +2380,10 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; - - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, @@ -2052,11 +2409,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, -#if 0 .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, - .vidioc_try_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, -#endif .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, @@ -2069,6 +2423,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, .vidioc_s_std = vidioc_s_std, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, @@ -2190,15 +2545,10 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->mute = 1; dev->volume = 0x1f; -#if 1 - /* enable vbi capturing */ - /* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK); em28xx_write_reg(dev, EM28XX_R0F_XCLK, (EM28XX_XCLK_AUDIO_UNMUTE | val)); - em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51); -#endif em28xx_set_outfmt(dev); em28xx_colorlevels_set_default(dev); @@ -2221,14 +2571,17 @@ int em28xx_register_analog_devices(struct em28xx *dev) } /* Allocate and fill vbi video_device struct */ - dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); + if (em28xx_vbi_supported(dev) == 1) { + dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, + "vbi"); - /* register v4l2 vbi video_device */ - ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, - vbi_nr[dev->devno]); - if (ret < 0) { - em28xx_errdev("unable to register vbi device\n"); - return ret; + /* register v4l2 vbi video_device */ + ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->devno]); + if (ret < 0) { + em28xx_errdev("unable to register vbi device\n"); + return ret; + } } if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { @@ -2248,8 +2601,12 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->radio_dev->num); } - em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", - dev->vdev->num, dev->vbi_dev->num); + em28xx_info("V4L2 video device registered as /dev/video%d\n", + dev->vdev->num); + + if (dev->vbi_dev) + em28xx_info("V4L2 VBI device registered as /dev/vbi%d\n", + dev->vbi_dev->num); return 0; } diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index a603d981b..67d220e3e 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -110,6 +110,7 @@ #define EM2882_BOARD_EVGA_INDTUBE 70 #define EM2820_BOARD_SILVERCREST_WEBCAM 71 #define EM2861_BOARD_GADMEI_UTV330PLUS 72 +#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -215,7 +216,8 @@ struct em28xx_usb_isoc_ctl { int tmp_buf_len; /* Stores already requested buffers */ - struct em28xx_buffer *buf; + struct em28xx_buffer *vid_buf; + struct em28xx_buffer *vbi_buf; /* Stores the number of received fields */ int nfields; @@ -401,6 +403,7 @@ struct em28xx_board { unsigned int is_webcam:1; unsigned int no_audio:1; unsigned int valid:1; + unsigned int has_ir_i2c:1; unsigned char xclk, i2c_speed; unsigned char radio_addr; @@ -444,6 +447,10 @@ enum em28xx_dev_state { #define EM28XX_AUDIO 0x10 #define EM28XX_DVB 0x20 +/* em28xx resource types (used for res_get/res_lock etc */ +#define EM28XX_RESOURCE_VIDEO 0x01 +#define EM28XX_RESOURCE_VBI 0x02 + struct em28xx_audio { char name[50]; char *transfer_buffer[EM28XX_AUDIO_BUFS]; @@ -472,10 +479,11 @@ struct em28xx; struct em28xx_fh { struct em28xx *dev; - unsigned int stream_on:1; /* Locks streams */ int radio; + unsigned int resources; struct videobuf_queue vb_vidq; + struct videobuf_queue vb_vbiq; enum v4l2_buf_type type; }; @@ -502,7 +510,6 @@ struct em28xx { /* Vinmode/Vinctl used at the driver */ int vinmode, vinctl; - unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; unsigned int has_alsa_audio:1; @@ -553,6 +560,12 @@ struct em28xx { enum em28xx_dev_state state; enum em28xx_io_method io; + /* vbi related state tracking */ + int capture_type; + int vbi_read; + unsigned char cur_field; + + struct work_struct request_module_wk; /* locks */ @@ -564,10 +577,14 @@ struct em28xx { struct video_device *vbi_dev; struct video_device *radio_dev; + /* resources in use */ + unsigned int resources; + unsigned char eedata[256]; /* Isoc control struct */ struct em28xx_dmaqueue vidq; + struct em28xx_dmaqueue vbiq; struct em28xx_usb_isoc_ctl isoc_ctl; spinlock_t slock; @@ -606,6 +623,12 @@ struct em28xx { struct delayed_work sbutton_query_work; struct em28xx_dvb *dvb; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + /* I2C keyboard data */ + struct i2c_board_info info; + struct IR_i2c_init_data init_data; +#endif }; struct em28xx_ops { @@ -644,6 +667,7 @@ int em28xx_audio_setup(struct em28xx *dev); int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); +int em28xx_vbi_supported(struct em28xx *dev); int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); @@ -691,6 +715,9 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev); int em28xx_ir_init(struct em28xx *dev); int em28xx_ir_fini(struct em28xx *dev); +/* Provided by em28xx-vbi.c */ +extern struct videobuf_queue_ops em28xx_vbi_qops; + /* printk macros */ #define em28xx_err(fmt, arg...) do {\ diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig index 8897283b0..fe2e490eb 100644 --- a/linux/drivers/media/video/gspca/Kconfig +++ b/linux/drivers/media/video/gspca/Kconfig @@ -19,6 +19,7 @@ if USB_GSPCA && VIDEO_V4L2 source "drivers/media/video/gspca/m5602/Kconfig" source "drivers/media/video/gspca/stv06xx/Kconfig" +source "drivers/media/video/gspca/gl860/Kconfig" config USB_GSPCA_CONEX tristate "Conexant Camera Driver" diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile index 035616b5e..b74208180 100644 --- a/linux/drivers/media/video/gspca/Makefile +++ b/linux/drivers/media/video/gspca/Makefile @@ -58,3 +58,4 @@ gspca_zc3xx-objs := zc3xx.o obj-$(CONFIG_USB_M5602) += m5602/ obj-$(CONFIG_USB_STV06XX) += stv06xx/ +obj-$(CONFIG_USB_GL860) += gl860/ diff --git a/linux/drivers/media/video/gspca/gl860/Kconfig b/linux/drivers/media/video/gspca/gl860/Kconfig new file mode 100644 index 000000000..22772f53e --- /dev/null +++ b/linux/drivers/media/video/gspca/gl860/Kconfig @@ -0,0 +1,8 @@ +config USB_GL860 + tristate "GL860 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the GL860 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_gl860. diff --git a/linux/drivers/media/video/gspca/gl860/Makefile b/linux/drivers/media/video/gspca/gl860/Makefile new file mode 100644 index 000000000..13c9403cc --- /dev/null +++ b/linux/drivers/media/video/gspca/gl860/Makefile @@ -0,0 +1,10 @@ +obj-$(CONFIG_USB_GL860) += gspca_gl860.o + +gspca_gl860-objs := gl860.o \ + gl860-mi1320.o \ + gl860-ov2640.o \ + gl860-ov9655.o \ + gl860-mi2020.o + +EXTRA_CFLAGS += -Idrivers/media/video/gspca + diff --git a/linux/drivers/media/video/gspca/gl860/gl860-mi1320.c b/linux/drivers/media/video/gspca/gl860/gl860-mi1320.c new file mode 100644 index 000000000..39f6261c1 --- /dev/null +++ b/linux/drivers/media/video/gspca/gl860/gl860-mi1320.c @@ -0,0 +1,537 @@ +/* @file gl860-mi1320.c + * @author Olivier LORIN from my logs + * @date 2009-08-27 + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* Sensor : MI1320 */ + +#include "gl860.h" + +static struct validx tbl_common[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba51, 0x0066}, {0xba02, 0x00f1}, + {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, + {0xffff, 0xffff}, + {0xba00, 0x00f0}, {0xba02, 0x00f1}, {0xbafa, 0x0028}, {0xba02, 0x00f1}, + {0xba00, 0x00f0}, {0xba01, 0x00f1}, {0xbaf0, 0x0006}, {0xba0e, 0x00f1}, + {0xba70, 0x0006}, {0xba0e, 0x00f1}, + {0xffff, 0xffff}, + {0xba74, 0x0006}, {0xba0e, 0x00f1}, + {0xffff, 0xffff}, + {0x0061, 0x0000}, {0x0068, 0x000d}, +}; + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, + {35, 0xffff}, + {0x0008, 0x00c0}, {0x0001, 0x00c1}, {0x0001, 0x00c2}, {0x0020, 0x0006}, + {0x006a, 0x000d}, +}; + +static struct validx tbl_sensor_settings_common[] = { + {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0040, 0x0000}, + {0x006a, 0x0007}, {0x006a, 0x000d}, {0x0063, 0x0006}, +}; +static struct validx tbl_sensor_settings_1280[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1}, + {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1}, +}; +static struct validx tbl_sensor_settings_800[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xba5a, 0x0066}, {0xba02, 0x00f1}, + {0xba05, 0x0067}, {0xba05, 0x00f1}, {0xba20, 0x0065}, {0xba00, 0x00f1}, +}; +static struct validx tbl_sensor_settings_640[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, + {0xba51, 0x0066}, {0xba02, 0x00f1}, {0xba05, 0x0067}, {0xba05, 0x00f1}, + {0xba20, 0x0065}, {0xba00, 0x00f1}, +}; +static struct validx tbl_post_unset_alt[] = { + {0xba00, 0x00f0}, {0xba00, 0x00f1}, {0xbaa0, 0x0065}, {0xba00, 0x00f1}, + {0x0061, 0x0000}, {0x0068, 0x000d}, +}; + +static u8 *tbl_1280[] = { + "\x0d\x80\xf1\x08\x03\x04\xf1\x00" "\x04\x05\xf1\x02\x05\x00\xf1\xf1" + "\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08" + "\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00" + "\xa9\x04\xf1\x00\xa1\x05\xf1\x00" "\xa4\x04\xf1\x00\xae\x0a\xf1\x08" + , + "\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47" + "\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c" + "\xd2\x00\xf1\xcf\xcb\x00\xf1\x01" + , + "\xd3\x02\xd4\x28\xd5\x01\xd0\x02" "\xd1\x18\xd2\xc1" +}; + +static u8 *tbl_800[] = { + "\x0d\x80\xf1\x08\x03\x03\xf1\xc0" "\x04\x05\xf1\x02\x05\x00\xf1\xf1" + "\x06\x00\xf1\x0d\x20\x01\xf1\x00" "\x21\x84\xf1\x00\x0d\x00\xf1\x08" + "\xf0\x00\xf1\x01\x34\x00\xf1\x00" "\x9b\x43\xf1\x00\xa6\x05\xf1\x00" + "\xa9\x03\xf1\xc0\xa1\x03\xf1\x20" "\xa4\x02\xf1\x5a\xae\x0a\xf1\x08" + , + "\xf0\x00\xf1\x02\x3a\x05\xf1\xf1" "\x3c\x05\xf1\xf1\x59\x01\xf1\x47" + "\x5a\x01\xf1\x88\x5c\x0a\xf1\x06" "\x5d\x0e\xf1\x0a\x64\x5e\xf1\x1c" + "\xd2\x00\xf1\xcf\xcb\x00\xf1\x01" + , + "\xd3\x02\xd4\x18\xd5\x21\xd0\x02" "\xd1\x10\xd2\x59" +}; + +static u8 *tbl_640[] = { + "\x0d\x80\xf1\x08\x03\x04\xf1\x04" "\x04\x05\xf1\x02\x07\x01\xf1\x7c" + "\x08\x00\xf1\x0e\x21\x80\xf1\x00" "\x0d\x00\xf1\x08\xf0\x00\xf1\x01" + "\x34\x10\xf1\x10\x3a\x43\xf1\x00" "\xa6\x05\xf1\x02\xa9\x04\xf1\x04" + "\xa7\x02\xf1\x81\xaa\x01\xf1\xe2" "\xae\x0c\xf1\x09" + , + "\xf0\x00\xf1\x02\x39\x03\xf1\xfc" "\x3b\x04\xf1\x04\x57\x01\xf1\xb6" + "\x58\x02\xf1\x0d\x5c\x1f\xf1\x19" "\x5d\x24\xf1\x1e\x64\x5e\xf1\x1c" + "\xd2\x00\xf1\x00\xcb\x00\xf1\x01" + , + "\xd3\x02\xd4\x10\xd5\x81\xd0\x02" "\xd1\x08\xd2\xe1" +}; + +static s32 tbl_sat[] = {0x25, 0x1d, 0x15, 0x0d, 0x05, 0x4d, 0x55, 0x5d, 0x2d}; +static s32 tbl_bright[] = {0, 8, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70}; +static s32 tbl_backlight[] = {0x0e, 0x06, 0x02}; + +static s32 tbl_cntr1[] = { + 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0, 0xc8, 0xd0, 0xe0, 0xf0}; +static s32 tbl_cntr2[] = { + 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40, 0x38, 0x30, 0x20, 0x10}; + +static u8 dat_wbalNL[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x3b\x04\xf1\x2a\x47\x10\xf1\x10" + "\x9d\x3c\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\x91\xf1\x20" + "\x9c\x91\xf1\x20\x37\x03\xf1\x00" "\x9d\xc5\xf1\x0f\xf0\x00\xf1\x00"; + +static u8 dat_wbalLL[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x0c" "\x3b\x04\xf1\x2a\x47\x40\xf1\x40" + "\x9d\x20\xf1\xae\xaf\x10\xf1\x00" "\xf0\x00\xf1\x02\x2f\xd1\xf1\x00" + "\x9c\xd1\xf1\x00\x37\x03\xf1\x00" "\x9d\xc5\xf1\x3f\xf0\x00\xf1\x00"; + +static u8 dat_wbalBL[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x47\x10\xf1\x30\x9d\x3c\xf1\xae" + "\xaf\x10\xf1\x00\xf0\x00\xf1\x02" "\x2f\x91\xf1\x20\x9c\x91\xf1\x20" + "\x37\x03\xf1\x00\x9d\xc5\xf1\x2f" "\xf0\x00\xf1\x00"; + +static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00}; + +static u8 s000[] = + "\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42" + "\xd8\x04\x58\x00\x04\x02"; +static u8 s001[] = + "\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d" + "\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0"; +static u8 s002[] = + "\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e" + "\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00" + "\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff"; +static u8 s003[] = + "\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda" + "\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c" + "\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c"; +static u8 s004[] = + "\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43"; +static u8 s005[] = + "\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68" + "\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82" + "\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b"; +static u8 s006[] = + "\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06" + "\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4" + "\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f"; +static u8 s007[] = + "\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72" + "\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03" + "\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea" + "\xe1\xff\xf1\x00"; +static u8 s008[] = + "\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7" + "\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06" + "\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a"; +static u8 s009[] = + "\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03" + "\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa" + "\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14"; +static u8 s010[] = + "\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00" + "\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f" + "\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01" + "\xc3\x0a\xf1\x07\xc4\x00\xf1\x10"; +static u8 s011[] = + "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10" + "\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00" + "\xa4\x03\xf1\xc0\xa7\x02\xf1\x81"; + +static int mi1320_init_at_startup(struct gspca_dev *gspca_dev); +static int mi1320_configure_alt(struct gspca_dev *gspca_dev); +static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev); +static int mi1320_init_post_alt(struct gspca_dev *gspca_dev); +static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev); +static int mi1320_sensor_settings(struct gspca_dev *gspca_dev); +static int mi1320_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void mi1320_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 0; + sd->vcur.brightness = 0; + sd->vcur.sharpness = 6; + sd->vcur.contrast = 10; + sd->vcur.gamma = 20; + sd->vcur.hue = 0; + sd->vcur.saturation = 6; + sd->vcur.whitebal = 0; + sd->vcur.mirror = 0; + sd->vcur.flip = 0; + sd->vcur.AC50Hz = 1; + + sd->vmax.backlight = 2; + sd->vmax.brightness = 8; + sd->vmax.sharpness = 7; + sd->vmax.contrast = 0; /* 10 but not working with tihs driver */ + sd->vmax.gamma = 40; + sd->vmax.hue = 5 + 1; + sd->vmax.saturation = 8; + sd->vmax.whitebal = 2; + sd->vmax.mirror = 1; + sd->vmax.flip = 1; + sd->vmax.AC50Hz = 1; + + sd->dev_camera_settings = mi1320_camera_settings; + sd->dev_init_at_startup = mi1320_init_at_startup; + sd->dev_configure_alt = mi1320_configure_alt; + sd->dev_init_pre_alt = mi1320_init_pre_alt; + sd->dev_post_unset_alt = mi1320_post_unset_alt; +} + +/*==========================================================================*/ + +static void common(struct gspca_dev *gspca_dev) +{ + s32 n; /* reserved for FETCH macros */ + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, s000); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, s001); + n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s002); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s003); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, s004); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s005); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, s006); + keep_on_fetching_validx(gspca_dev, tbl_common, + ARRAY_SIZE(tbl_common), n); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, s007); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s008); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s009); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, s010); + keep_on_fetching_validx(gspca_dev, tbl_common, + ARRAY_SIZE(tbl_common), n); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, s011); + keep_on_fetching_validx(gspca_dev, tbl_common, + ARRAY_SIZE(tbl_common), n); +} + +static int mi1320_init_at_startup(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + + common(gspca_dev); + +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ + + return 0; +} + +static int mi1320_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->mirrorMask = 0; + + sd->vold.backlight = -1; + sd->vold.brightness = -1; + sd->vold.sharpness = -1; + sd->vold.contrast = -1; + sd->vold.saturation = -1; + sd->vold.gamma = -1; + sd->vold.hue = -1; + sd->vold.whitebal = -1; + sd->vold.mirror = -1; + sd->vold.flip = -1; + sd->vold.AC50Hz = -1; + + common(gspca_dev); + + mi1320_sensor_settings(gspca_dev); + + mi1320_init_post_alt(gspca_dev); + + return 0; +} + +static int mi1320_init_post_alt(struct gspca_dev *gspca_dev) +{ + mi1320_camera_settings(gspca_dev); + + return 0; +} + +static int mi1320_sensor_settings(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + + fetch_validx(gspca_dev, tbl_sensor_settings_common, + ARRAY_SIZE(tbl_sensor_settings_common)); + + switch (reso) { + case IMAGE_1280: + fetch_validx(gspca_dev, tbl_sensor_settings_1280, + ARRAY_SIZE(tbl_sensor_settings_1280)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_1280[0]); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_1280[1]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_1280[2]); + break; + + case IMAGE_800: + fetch_validx(gspca_dev, tbl_sensor_settings_800, + ARRAY_SIZE(tbl_sensor_settings_800)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 64, tbl_800[0]); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_800[1]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_800[2]); + break; + + default: + fetch_validx(gspca_dev, tbl_sensor_settings_640, + ARRAY_SIZE(tbl_sensor_settings_640)); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 60, tbl_640[0]); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, tbl_640[1]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, tbl_640[2]); + break; + } + return 0; +} + +static int mi1320_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 3 + 1; + break; + + case IMAGE_800: + case IMAGE_1280: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +int mi1320_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + s32 backlight = sd->vcur.backlight; + s32 bright = sd->vcur.brightness; + s32 sharp = sd->vcur.sharpness; + s32 cntr = sd->vcur.contrast; + s32 gam = sd->vcur.gamma; + s32 hue = sd->vcur.hue; + s32 sat = sd->vcur.saturation; + s32 wbal = sd->vcur.whitebal; + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); + s32 freq = (sd->vcur.AC50Hz > 0); + s32 i; + + if (freq != sd->vold.AC50Hz) { + sd->vold.AC50Hz = freq; + + freq = 2 * (freq == 0); + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba02, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x005b, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01 + freq, 0x00f1, 0, NULL); + } + + if (wbal != sd->vold.whitebal) { + sd->vold.whitebal = wbal; + if (wbal < 0 || wbal > sd->vmax.whitebal) + wbal = 0; + + for (i = 0; i < 2; i++) { + if (wbal == 0) { /* Normal light */ + ctrl_out(gspca_dev, 0x40, 1, + 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0003, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0042, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, + 0xba00, 0x0200, 48, dat_wbalNL); + } + + if (wbal == 1) { /* Low light */ + ctrl_out(gspca_dev, 0x40, 1, + 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0004, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0043, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, + 0xba00, 0x0200, 48, dat_wbalLL); + } + + if (wbal == 2) { /* Back light */ + ctrl_out(gspca_dev, 0x40, 1, + 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0003, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, + 0x0042, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, + 0xba00, 0x0200, 44, dat_wbalBL); + } + } + } + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + bright = tbl_bright[bright]; + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x0034, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + bright, 0x00f1, 0, NULL); + } + + if (sat != sd->vold.saturation) { + sd->vold.saturation = sat; + if (sat < 0 || sat > sd->vmax.saturation) + sat = 0; + + sat = tbl_sat[sat]; + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0025, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sat, 0x00f1, 0, NULL); + } + + if (sharp != sd->vold.sharpness) { + sd->vold.sharpness = sharp; + if (sharp < 0 || sharp > sd->vmax.sharpness) + sharp = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 , 0x0005, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + sharp, 0x00f1, 0, NULL); + } + + if (hue != sd->vold.hue) { + /* 0=normal 1=NB 2="sepia" 3=negative 4=other 5=other2 */ + if (hue < 0 || hue > sd->vmax.hue) + hue = 0; + if (hue == sd->vmax.hue) + sd->swapRB = 1; + else + sd->swapRB = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1, + 0, NULL); + } + + if (backlight != sd->vold.backlight) { + sd->vold.backlight = backlight; + if (backlight < 0 || backlight > sd->vmax.backlight) + backlight = 0; + + backlight = tbl_backlight[backlight]; + for (i = 0; i < 2; i++) { + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba74, 0x0006, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba80 + backlight, 0x00f1, + 0, NULL); + } + } + + if (hue != sd->vold.hue) { + sd->vold.hue = hue; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba70, 0x00e2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + hue * (hue < 6), 0x00f1, + 0, NULL); + } + + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { + u8 dat_hvflip2[4] = {0x20, 0x01, 0xf1, 0x00}; + sd->vold.mirror = mirror; + sd->vold.flip = flip; + + dat_hvflip2[3] = flip + 2 * mirror; + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 4, dat_hvflip2); + } + + if (gam != sd->vold.gamma) { + sd->vold.gamma = gam; + if (gam < 0 || gam > sd->vmax.gamma) + gam = 0; + + gam = 2 * gam; + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba04 , 0x003b, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba02 + gam, 0x00f1, 0, NULL); + } + + if (cntr != sd->vold.contrast) { + sd->vold.contrast = cntr; + if (cntr < 0 || cntr > sd->vmax.contrast) + cntr = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0xba00, 0x00f0, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba01, 0x00f1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr1[cntr], 0x0035, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0xba00 + tbl_cntr2[cntr], 0x00f1, + 0, NULL); + } + + return 0; +} + +static void mi1320_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + + fetch_validx(gspca_dev, tbl_post_unset_alt, + ARRAY_SIZE(tbl_post_unset_alt)); +} diff --git a/linux/drivers/media/video/gspca/gl860/gl860-mi2020.c b/linux/drivers/media/video/gspca/gl860/gl860-mi2020.c new file mode 100644 index 000000000..ffb09fed3 --- /dev/null +++ b/linux/drivers/media/video/gspca/gl860/gl860-mi2020.c @@ -0,0 +1,937 @@ +/* @file gl860-mi2020.c + * @author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's + * logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist. + * @date 2009-08-27 + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* Sensor : MI2020 */ + +#include "gl860.h" + +static u8 dat_bright1[] = {0x8c, 0xa2, 0x06}; +static u8 dat_bright3[] = {0x8c, 0xa1, 0x02}; +static u8 dat_bright4[] = {0x90, 0x00, 0x0f}; +static u8 dat_bright5[] = {0x8c, 0xa1, 0x03}; +static u8 dat_bright6[] = {0x90, 0x00, 0x05}; + +static u8 dat_dummy1[] = {0x90, 0x00, 0x06}; +/*static u8 dummy2[] = {0x8c, 0xa1, 0x02};*/ +/*static u8 dummy3[] = {0x90, 0x00, 0x1f};*/ + +static u8 dat_hvflip1[] = {0x8c, 0x27, 0x19}; +static u8 dat_hvflip3[] = {0x8c, 0x27, 0x3b}; +static u8 dat_hvflip5[] = {0x8c, 0xa1, 0x03}; +static u8 dat_hvflip6[] = {0x90, 0x00, 0x06}; + +static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 }; + +static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 }; +static u8 dat_multi6[] = { 0x90, 0x00, 0x05 }; + +static struct validx tbl_common_a[] = { + {0x0000, 0x0000}, + {1, 0xffff}, /* msleep(35); */ + {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0}, + {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, {0x0004, 0x00d8}, + {0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000}, +}; + +static struct validx tbl_common_b[] = { + {0x006a, 0x0007}, + {35, 0xffff}, + {0x00ef, 0x0006}, + {35, 0xffff}, + {0x006a, 0x000d}, + {35, 0xffff}, + {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0003, 0x00c1}, {0x0042, 0x00c2}, + {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000}, +}; + +static struct idxdata tbl_common_c[] = { + {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, + {6, "\xff\xff\xff"}, /* 12 */ + {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, + {2, "\xff\xff\xff"}, /* - */ + {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\x22\x23"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0f"}, {0x33, "\x90\x00\x0d"}, + {0x33, "\x8c\xa2\x10"}, {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x11"}, + {0x33, "\x90\x00\x07"}, {0x33, "\xf4\x03\x1d"}, {0x35, "\xa2\x00\xe2"}, + {0x33, "\x8c\xab\x05"}, {0x33, "\x90\x00\x01"}, {0x32, "\x6e\x00\x86"}, + {0x32, "\x70\x0f\xaa"}, {0x32, "\x72\x0f\xe4"}, {0x33, "\x8c\xa3\x4a"}, + {0x33, "\x90\x00\x5a"}, {0x33, "\x8c\xa3\x4b"}, {0x33, "\x90\x00\xa6"}, + {0x33, "\x8c\xa3\x61"}, {0x33, "\x90\x00\xc8"}, {0x33, "\x8c\xa3\x62"}, + {0x33, "\x90\x00\xe1"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, + {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, + {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, + {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, + {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, + {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, + {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, + {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, + {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, + {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, + {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, + {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, + {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, + {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, + {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, + {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, + {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, + {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, + {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, + {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, + {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, + {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, + {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, + {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, + {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, + {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, + {1, "\xff\xff\xff"}, + {0x33, "\x78\x00\x00"}, + {1, "\xff\xff\xff"}, + {0x35, "\xb8\x1f\x20"}, {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x10"}, + {0x33, "\x8c\xa2\x07"}, {0x33, "\x90\x00\x08"}, {0x33, "\x8c\xa2\x42"}, + {0x33, "\x90\x00\x0b"}, {0x33, "\x8c\xa2\x4a"}, {0x33, "\x90\x00\x8c"}, + {0x35, "\xba\xfa\x08"}, {0x33, "\x8c\xa2\x02"}, {0x33, "\x90\x00\x22"}, + {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, +}; + +static struct idxdata tbl_common_d[] = { + {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"}, + {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"}, + {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"}, + {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\xa0"}, + {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc0"}, {0x33, "\x8c\x24\x15"}, + {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"}, +}; + +static struct idxdata tbl_common_e[] = { + {0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, + {0x33, "\x90\x00\x04"}, {0x33, "\x8c\xa2\x14"}, {0x33, "\x90\x00\x20"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, + /* msleep(53); */ + {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, + {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, {0x33, "\x8c\x27\x39"}, + {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, + {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, {0x33, "\x8c\x27\x03"}, + {0x33, "\x90\x02\x84"}, {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x01\xe2"}, + {0x33, "\x8c\x27\x07"}, {0x33, "\x90\x06\x40"}, {0x33, "\x8c\x27\x09"}, + {0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x27\x0d"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\x27\x0f"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x11"}, + {0x33, "\x90\x04\xbd"}, {0x33, "\x8c\x27\x13"}, {0x33, "\x90\x06\x4d"}, + {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x17"}, + {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, + {0x33, "\x8c\x27\x1b"}, {0x33, "\x90\x02\x4f"}, {0x33, "\x8c\x27\x1d"}, + {0x33, "\x90\x01\x02"}, {0x33, "\x8c\x27\x1f"}, {0x33, "\x90\x02\x79"}, + {0x33, "\x8c\x27\x21"}, {0x33, "\x90\x01\x55"}, {0x33, "\x8c\x27\x23"}, + {0x33, "\x90\x02\x85"}, {0x33, "\x8c\x27\x25"}, {0x33, "\x90\x06\x0f"}, + {0x33, "\x8c\x27\x27"}, {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x29"}, + {0x33, "\x90\x20\x20"}, {0x33, "\x8c\x27\x2b"}, {0x33, "\x90\x10\x20"}, + {0x33, "\x8c\x27\x2d"}, {0x33, "\x90\x20\x07"}, {0x33, "\x8c\x27\x2f"}, + {0x33, "\x90\x00\x04"}, {0x33, "\x8c\x27\x31"}, {0x33, "\x90\x00\x04"}, + {0x33, "\x8c\x27\x33"}, {0x33, "\x90\x04\xbb"}, {0x33, "\x8c\x27\x35"}, + {0x33, "\x90\x06\x4b"}, {0x33, "\x8c\x27\x37"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\x27\x39"}, {0x33, "\x90\x21\x11"}, {0x33, "\x8c\x27\x3b"}, + {0x33, "\x90\x00\x24"}, {0x33, "\x8c\x27\x3d"}, {0x33, "\x90\x01\x20"}, + {0x33, "\x8c\x27\x41"}, {0x33, "\x90\x01\x69"}, {0x33, "\x8c\x27\x45"}, + {0x33, "\x90\x04\xed"}, {0x33, "\x8c\x27\x47"}, {0x33, "\x90\x09\x4c"}, + {0x33, "\x8c\x27\x51"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x53"}, + {0x33, "\x90\x03\x20"}, {0x33, "\x8c\x27\x55"}, {0x33, "\x90\x00\x00"}, + {0x33, "\x8c\x27\x57"}, {0x33, "\x90\x02\x58"}, {0x33, "\x8c\x27\x5f"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x61"}, {0x33, "\x90\x06\x40"}, + {0x33, "\x8c\x27\x63"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x65"}, + {0x33, "\x90\x04\xb0"}, {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa1"}, + {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, + {0x33, "\x90\x00\x21"}, {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, + {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x27"}, {0x33, "\x8c\x24\x11"}, + {0x33, "\x90\x00\xa1"}, {0x33, "\x8c\x24\x13"}, {0x33, "\x90\x00\xc1"}, + {0x33, "\x8c\x24\x15"}, +}; + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, + {53, 0xffff}, + {0x0010, 0x0010}, + {53, 0xffff}, + {0x0008, 0x00c0}, + {53, 0xffff}, + {0x0001, 0x00c1}, + {53, 0xffff}, + {0x0001, 0x00c2}, + {53, 0xffff}, + {0x0020, 0x0006}, + {53, 0xffff}, + {0x006a, 0x000d}, + {53, 0xffff}, +}; + +static struct idxdata tbl_init_post_alt_low_a[] = { + {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"}, + {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"}, + {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"}, + {0x33, "\x90\x00\x1d"}, {0x33, "\x8c\xa4\x0b"}, {0x33, "\x90\x00\x20"}, + {0x33, "\x8c\x24\x11"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\x24\x13"}, + {0x33, "\x90\x00\x9b"}, +}; + +static struct idxdata tbl_init_post_alt_low_b[] = { + {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"}, + {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, +}; + +static struct idxdata tbl_init_post_alt_low_c[] = { + {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, + {2, "\xff\xff\xff"}, + {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x01"}, + {0x33, "\x2e\x01\x00"}, {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, + {0x33, "\x90\x00\x00"}, {0x33, "\x8c\x27\x95"}, {0x33, "\x90\x01\x00"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x20"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x01"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x00"}, + {2, "\xff\xff\xff"}, /* - * */ + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {1, "\xff\xff\xff"}, +}; + +static struct idxdata tbl_init_post_alt_low_d[] = { + {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, + {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, + {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, + {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, + {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, + {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, + {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, + {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, + {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, + {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, + {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, + {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, + {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, + {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, + {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, + {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, + {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, + {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, + {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, + {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, + {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, + {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, + {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, + {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, + {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, + {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, + /* Flip/Mirror h/v=1 */ + {0x33, "\x90\x00\x3c"}, {0x33, "\x8c\x27\x19"}, {0x33, "\x90\x04\x6c"}, + {0x33, "\x8c\x27\x3b"}, {0x33, "\x90\x00\x24"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x06"}, + {130, "\xff\xff\xff"}, + {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, + {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, {0x33, "\x90\x00\x06"}, + {100, "\xff\xff\xff"}, + /* ?? */ + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, + /* Brigthness=70 */ + {0x33, "\x8c\xa2\x06"}, {0x33, "\x90\x00\x46"}, {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x0f"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + /* Sharpness=20 */ + {0x32, "\x6c\x14\x08"}, +}; + +static struct idxdata tbl_init_post_alt_big_a[] = { + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, + {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, + {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x03"}, + {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, + {2, "\xff\xff\xff"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, {0x33, "\x8c\xa1\x20"}, + {0x33, "\x90\x00\x72"}, {0x33, "\x8c\xa1\x30"}, {0x33, "\x90\x00\x03"}, + {0x33, "\x8c\xa1\x31"}, {0x33, "\x90\x00\x02"}, {0x33, "\x8c\xa1\x32"}, + {0x33, "\x90\x00\x03"}, {0x33, "\x8c\xa1\x34"}, {0x33, "\x90\x00\x03"}, + {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x02"}, {0x33, "\x2e\x01\x00"}, + {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, +}; + +static struct idxdata tbl_init_post_alt_big_b[] = { + {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, + {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, + {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, + {0x34, "\xde\x01\x5b"}, {0x34, "\xe6\x01\x13"}, {0x34, "\xee\x0b\xf0"}, + {0x34, "\xf6\x0b\xa4"}, {0x35, "\x00\xf6\xe7"}, {0x35, "\x08\x0d\xfd"}, + {0x35, "\x10\x25\x63"}, {0x35, "\x18\x35\x6c"}, {0x35, "\x20\x42\x7e"}, + {0x35, "\x28\x19\x44"}, {0x35, "\x30\x39\xd4"}, {0x35, "\x38\xf5\xa8"}, + {0x35, "\x4c\x07\x90"}, {0x35, "\x44\x07\xb8"}, {0x35, "\x5c\x06\x88"}, + {0x35, "\x54\x07\xff"}, {0x34, "\xe0\x01\x52"}, {0x34, "\xe8\x00\xcc"}, + {0x34, "\xf0\x0d\x83"}, {0x34, "\xf8\x0c\xb3"}, {0x35, "\x02\xfe\xba"}, + {0x35, "\x0a\x04\xe0"}, {0x35, "\x12\x1c\x63"}, {0x35, "\x1a\x2b\x5a"}, + {0x35, "\x22\x32\x5e"}, {0x35, "\x2a\x0d\x28"}, {0x35, "\x32\x2c\x02"}, + {0x35, "\x3a\xf4\xfa"}, {0x35, "\x4e\x07\xef"}, {0x35, "\x46\x07\x88"}, + {0x35, "\x5e\x07\xc1"}, {0x35, "\x56\x04\x64"}, {0x34, "\xe4\x01\x15"}, + {0x34, "\xec\x00\x82"}, {0x34, "\xf4\x0c\xce"}, {0x34, "\xfc\x0c\xba"}, + {0x35, "\x06\x1f\x02"}, {0x35, "\x0e\x02\xe3"}, {0x35, "\x16\x1a\x50"}, + {0x35, "\x1e\x24\x39"}, {0x35, "\x26\x23\x4c"}, {0x35, "\x2e\xf9\x1b"}, + {0x35, "\x36\x23\x19"}, {0x35, "\x3e\x12\x08"}, {0x35, "\x52\x07\x22"}, + {0x35, "\x4a\x03\xd3"}, {0x35, "\x62\x06\x54"}, {0x35, "\x5a\x04\x5d"}, + {0x34, "\xe2\x01\x04"}, {0x34, "\xea\x00\xa0"}, {0x34, "\xf2\x0c\xbc"}, + {0x34, "\xfa\x0c\x5b"}, {0x35, "\x04\x17\xf2"}, {0x35, "\x0c\x02\x08"}, + {0x35, "\x14\x28\x43"}, {0x35, "\x1c\x28\x62"}, {0x35, "\x24\x2b\x60"}, + {0x35, "\x2c\x07\x33"}, {0x35, "\x34\x1f\xb0"}, {0x35, "\x3c\xed\xcd"}, + {0x35, "\x50\x00\x06"}, {0x35, "\x48\x07\xff"}, {0x35, "\x60\x05\x89"}, + {0x35, "\x58\x07\xff"}, {0x35, "\x40\x00\xa0"}, {0x35, "\x42\x00\x00"}, + {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, +}; + +static struct idxdata tbl_init_post_alt_big_c[] = { + {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x1f"}, + {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x1f"}, + {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x1f"}, + {0x33, "\x8c\xa1\x02"}, + {0x33, "\x90\x00\x1f"}, +}; + +static u8 *dat_640 = "\xd0\x02\xd1\x08\xd2\xe1\xd3\x02\xd4\x10\xd5\x81"; +static u8 *dat_800 = "\xd0\x02\xd1\x10\xd2\x57\xd3\x02\xd4\x18\xd5\x21"; +static u8 *dat_1280 = "\xd0\x02\xd1\x20\xd2\x01\xd3\x02\xd4\x28\xd5\x01"; +static u8 *dat_1600 = "\xd0\x02\xd1\x20\xd2\xaf\xd3\x02\xd4\x30\xd5\x41"; + +static int mi2020_init_at_startup(struct gspca_dev *gspca_dev); +static int mi2020_configure_alt(struct gspca_dev *gspca_dev); +static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev); +static int mi2020_init_post_alt(struct gspca_dev *gspca_dev); +static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev); +static int mi2020_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void mi2020_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 0; + sd->vcur.brightness = 70; + sd->vcur.sharpness = 20; + sd->vcur.contrast = 0; + sd->vcur.gamma = 0; + sd->vcur.hue = 0; + sd->vcur.saturation = 60; + sd->vcur.whitebal = 50; + sd->vcur.mirror = 0; + sd->vcur.flip = 0; + sd->vcur.AC50Hz = 1; + + sd->vmax.backlight = 64; + sd->vmax.brightness = 128; + sd->vmax.sharpness = 40; + sd->vmax.contrast = 3; + sd->vmax.gamma = 2; + sd->vmax.hue = 0 + 1; /* 200 */ + sd->vmax.saturation = 0; /* 100 */ + sd->vmax.whitebal = 0; /* 100 */ + sd->vmax.mirror = 1; + sd->vmax.flip = 1; + sd->vmax.AC50Hz = 1; + if (_MI2020b_) { + sd->vmax.contrast = 0; + sd->vmax.gamma = 0; + sd->vmax.backlight = 0; + } + + sd->dev_camera_settings = mi2020_camera_settings; + sd->dev_init_at_startup = mi2020_init_at_startup; + sd->dev_configure_alt = mi2020_configure_alt; + sd->dev_init_pre_alt = mi2020_init_pre_alt; + sd->dev_post_unset_alt = mi2020_post_unset_alt; +} + +/*==========================================================================*/ + +static void common(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + if (_MI2020b_) { + fetch_validx(gspca_dev, tbl_common_a, ARRAY_SIZE(tbl_common_a)); + } else { + if (_MI2020_) + ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x0004, 0, NULL); + else + ctrl_out(gspca_dev, 0x40, 1, 0x0002, 0x0004, 0, NULL); + msleep(35); + fetch_validx(gspca_dev, tbl_common_b, ARRAY_SIZE(tbl_common_b)); + } + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x01"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x00"); + msleep(2); /* - * */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0030, 3, "\x1a\x0a\xcc"); + if (reso == IMAGE_1600) + msleep(2); /* 1600 */ + fetch_idxdata(gspca_dev, tbl_common_c, ARRAY_SIZE(tbl_common_c)); + + if (_MI2020b_ || _MI2020_) + fetch_idxdata(gspca_dev, tbl_common_d, + ARRAY_SIZE(tbl_common_d)); + + fetch_idxdata(gspca_dev, tbl_common_e, ARRAY_SIZE(tbl_common_e)); + if (_MI2020b_ || _MI2020_) { + /* Different from fret */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78"); + /* Same as fret */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17"); + /* Different from fret */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x90"); + } else { + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x6a"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x24\x17"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x80"); + } + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x05"); + msleep(2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + if (reso == IMAGE_1600) + msleep(14); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x06"); + msleep(2); +} + +static int mi2020_init_at_startup(struct gspca_dev *gspca_dev) +{ + u8 c; + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &c); + + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + + common(gspca_dev); + + return 0; +} + +static int mi2020_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->mirrorMask = 0; + + sd->vold.backlight = -1; + sd->vold.brightness = -1; + sd->vold.sharpness = -1; + sd->vold.contrast = -1; + sd->vold.gamma = -1; + sd->vold.hue = -1; + sd->vold.mirror = -1; + sd->vold.flip = -1; + sd->vold.AC50Hz = -1; + + mi2020_init_post_alt(gspca_dev); + + return 0; +} + +static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + s32 backlight = sd->vcur.backlight; + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); + s32 freq = (sd->vcur.AC50Hz > 0); + + u8 dat_freq2[] = {0x90, 0x00, 0x80}; + u8 dat_multi1[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi2[] = {0x90, 0x00, 0x00}; + u8 dat_multi3[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi4[] = {0x90, 0x00, 0x00}; + u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; + u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; + u8 c; + + sd->nbIm = -1; + + dat_freq2[2] = freq ? 0xc0 : 0x80; + dat_multi1[2] = 0x9d; + dat_multi3[2] = dat_multi1[2] + 1; + dat_multi4[2] = dat_multi2[2] = backlight; + dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); + dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); + + msleep(200); + + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + msleep(3); /* 35 * */ + + common(gspca_dev); + + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); + msleep(70); + + if (_MI2020b_) + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + + ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0003, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0042, 0x00c2, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); + + switch (reso) { + case IMAGE_640: + case IMAGE_800: + if (reso != IMAGE_800) + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_640); + else + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_800); + + if (_MI2020c_) + fetch_idxdata(gspca_dev, tbl_init_post_alt_low_a, + ARRAY_SIZE(tbl_init_post_alt_low_a)); + + if (reso == IMAGE_800) + fetch_idxdata(gspca_dev, tbl_init_post_alt_low_b, + ARRAY_SIZE(tbl_init_post_alt_low_b)); + + fetch_idxdata(gspca_dev, tbl_init_post_alt_low_c, + ARRAY_SIZE(tbl_init_post_alt_low_c)); + + if (_MI2020b_) { + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(150); + } else if (_MI2020c_) { + ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(120); + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(30); + } else if (_MI2020_) { + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(120); + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(30); + } + + /* AC power frequency */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); + msleep(20); + /* backlight */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + /* at init time but not after */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17"); + /* finish the backlight */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + msleep(5);/* " */ + + if (_MI2020c_) { + fetch_idxdata(gspca_dev, tbl_init_post_alt_low_d, + ARRAY_SIZE(tbl_init_post_alt_low_d)); + } else { + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); + msleep(14); /* 0xd8 */ + + /* flip/mirror */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_hvflip6); + msleep(21); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + msleep(5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + msleep(5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + msleep(5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + msleep(5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + msleep(5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, dat_dummy1); + /* end of flip/mirror main part */ + msleep(246); /* 146 */ + + sd->nbIm = 0; + } + break; + + case IMAGE_1280: + case IMAGE_1600: + if (reso == IMAGE_1280) { + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1280); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x07"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x05\x04"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x09"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x04\x02"); + } else { + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1600); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x07"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x06\x40"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x8c\x27\x09"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, + 3, "\x90\x04\xb0"); + } + + fetch_idxdata(gspca_dev, tbl_init_post_alt_big_a, + ARRAY_SIZE(tbl_init_post_alt_big_a)); + + if (reso == IMAGE_1600) + msleep(13); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\x27\x97"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x01\x00"); + msleep(53); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); + if (reso == IMAGE_1600) + msleep(13); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); + msleep(53); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02"); + if (reso == IMAGE_1600) + msleep(13); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); + msleep(53); + + if (_MI2020b_) { + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); + if (reso == IMAGE_1600) + msleep(500); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(1850); + } else if (_MI2020c_ || _MI2020_) { + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x00c1, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x00c2, 0, NULL); + msleep(1850); + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(30); + } + + /* AC power frequency */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); + msleep(20); + /* backlight */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + /* at init time but not after */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa2\x0c"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x17"); + /* finish the backlight */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + msleep(6); /* " */ + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); + msleep(14); + + if (_MI2020c_) + fetch_idxdata(gspca_dev, tbl_init_post_alt_big_b, + ARRAY_SIZE(tbl_init_post_alt_big_b)); + + /* flip/mirror */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); + /* end of flip/mirror main part */ + msleep(16); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); + if (reso == IMAGE_1600) + msleep(25); /* 1600 */ + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x00"); + msleep(103); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x03"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x02"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa1\x20"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x72"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x8c\xa7\x02"); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x01"); + sd->nbIm = 0; + + if (_MI2020c_) + fetch_idxdata(gspca_dev, tbl_init_post_alt_big_c, + ARRAY_SIZE(tbl_init_post_alt_big_c)); + } + + sd->vold.mirror = mirror; + sd->vold.flip = flip; + sd->vold.AC50Hz = freq; + sd->vold.backlight = backlight; + + mi2020_camera_settings(gspca_dev); + + return 0; +} + +static int mi2020_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 3 + 1; + break; + + case IMAGE_800: + case IMAGE_1280: + case IMAGE_1600: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +int mi2020_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + s32 backlight = sd->vcur.backlight; + s32 bright = sd->vcur.brightness; + s32 sharp = sd->vcur.sharpness; + s32 cntr = sd->vcur.contrast; + s32 gam = sd->vcur.gamma; + s32 hue = (sd->vcur.hue > 0); + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) > 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) > 0); + s32 freq = (sd->vcur.AC50Hz > 0); + + u8 dat_sharp[] = {0x6c, 0x00, 0x08}; + u8 dat_bright2[] = {0x90, 0x00, 0x00}; + u8 dat_freq2[] = {0x90, 0x00, 0x80}; + u8 dat_multi1[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi2[] = {0x90, 0x00, 0x00}; + u8 dat_multi3[] = {0x8c, 0xa7, 0x00}; + u8 dat_multi4[] = {0x90, 0x00, 0x00}; + u8 dat_hvflip2[] = {0x90, 0x04, 0x6c}; + u8 dat_hvflip4[] = {0x90, 0x00, 0x24}; + + /* Less than 4 images received -> too early to set the settings */ + if (sd->nbIm < 4) { + sd->waitSet = 1; + return 0; + } + sd->waitSet = 0; + + if (freq != sd->vold.AC50Hz) { + sd->vold.AC50Hz = freq; + + dat_freq2[2] = freq ? 0xc0 : 0x80; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_freq2); + msleep(20); + } + + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { + sd->vold.mirror = mirror; + sd->vold.flip = flip; + + dat_hvflip2[2] = 0x6c + 2 * (1 - flip) + (1 - mirror); + dat_hvflip4[2] = 0x24 + 2 * (1 - flip) + (1 - mirror); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip6); + msleep(130); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_dummy1); + msleep(6); + + /* Sometimes present, sometimes not, useful? */ + /* ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy2); + * ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dummy3);*/ + } + + if (backlight != sd->vold.backlight) { + sd->vold.backlight = backlight; + if (backlight < 0 || backlight > sd->vmax.backlight) + backlight = 0; + + dat_multi1[2] = 0x9d; + dat_multi3[2] = dat_multi1[2] + 1; + dat_multi4[2] = dat_multi2[2] = backlight; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + } + + if (gam != sd->vold.gamma) { + sd->vold.gamma = gam; + if (gam < 0 || gam > sd->vmax.gamma) + gam = 0; + + dat_multi1[2] = 0x6d; + dat_multi3[2] = dat_multi1[2] + 1; + dat_multi4[2] = dat_multi2[2] = 0x40 + gam; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + } + + if (cntr != sd->vold.contrast) { + sd->vold.contrast = cntr; + if (cntr < 0 || cntr > sd->vmax.contrast) + cntr = 0; + + dat_multi1[2] = 0x6d; + dat_multi3[2] = dat_multi1[2] + 1; + dat_multi4[2] = dat_multi2[2] = 0x12 + 16 * cntr; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_multi6); + } + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + dat_bright2[2] = bright; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright1); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright2); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright3); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright4); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright5); + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_bright6); + } + + if (sharp != sd->vold.sharpness) { + sd->vold.sharpness = sharp; + if (sharp < 0 || sharp > sd->vmax.sharpness) + sharp = 0; + + dat_sharp[1] = sharp; + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0032, 3, dat_sharp); + } + + if (hue != sd->vold.hue) { + sd->swapRB = hue; + sd->vold.hue = hue; + } + + return 0; +} + +static void mi2020_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + msleep(20); + if (_MI2020c_ || _MI2020_) + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0000, 0, NULL); + else + ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); +} diff --git a/linux/drivers/media/video/gspca/gl860/gl860-ov2640.c b/linux/drivers/media/video/gspca/gl860/gl860-ov2640.c new file mode 100644 index 000000000..14b9c373f --- /dev/null +++ b/linux/drivers/media/video/gspca/gl860/gl860-ov2640.c @@ -0,0 +1,505 @@ +/* @file gl860-ov2640.c + * @author Olivier LORIN, from Malmostoso's logs + * @date 2009-08-27 + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* Sensor : OV2640 */ + +#include "gl860.h" + +static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01"; +static u8 dat_init2[] = {0x61}; /* expected */ +static u8 dat_init3[] = {0x51}; /* expected */ + +static u8 dat_post[] = + "\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01"; + +static u8 dat_640[] = "\xd0\x01\xd1\x08\xd2\xe0\xd3\x02\xd4\x10\xd5\x81"; +static u8 dat_800[] = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21"; +static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01"; +static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41"; + +static u8 c50[] = {0x50}; /* expected */ +static u8 c28[] = {0x28}; /* expected */ +static u8 ca8[] = {0xa8}; /* expected */ + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, + {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, + {0x0050, 0x0000}, {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0061, 0x0006}, + {0x006a, 0x000d}, {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, + {0x0041, 0x00c2}, {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, + {0x0041, 0x0000}, {0x0061, 0x0000}, +}; + +static struct validx tbl_common[] = { + {0x6000, 0x00ff}, {0x60ff, 0x002c}, {0x60df, 0x002e}, {0x6001, 0x00ff}, + {0x6080, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010}, + {0x6035, 0x003c}, {0x6000, 0x0011}, {0x6028, 0x0004}, {0x60e5, 0x0013}, + {0x6088, 0x0014}, {0x600c, 0x002c}, {0x6078, 0x0033}, {0x60f7, 0x003b}, + {0x6000, 0x003e}, {0x6011, 0x0043}, {0x6010, 0x0016}, {0x6082, 0x0039}, + {0x6088, 0x0035}, {0x600a, 0x0022}, {0x6040, 0x0037}, {0x6000, 0x0023}, + {0x60a0, 0x0034}, {0x601a, 0x0036}, {0x6002, 0x0006}, {0x60c0, 0x0007}, + {0x60b7, 0x000d}, {0x6001, 0x000e}, {0x6000, 0x004c}, {0x6081, 0x004a}, + {0x6099, 0x0021}, {0x6002, 0x0009}, {0x603e, 0x0024}, {0x6034, 0x0025}, + {0x6081, 0x0026}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010}, + {0x6000, 0x005c}, {0x6000, 0x0063}, {0x6000, 0x007c}, {0x6070, 0x0061}, + {0x6080, 0x0062}, {0x6080, 0x0020}, {0x6030, 0x0028}, {0x6000, 0x006c}, + {0x6000, 0x006e}, {0x6002, 0x0070}, {0x6094, 0x0071}, {0x60c1, 0x0073}, + {0x6034, 0x003d}, {0x6057, 0x005a}, {0x60bb, 0x004f}, {0x609c, 0x0050}, + {0x6080, 0x006d}, {0x6002, 0x0039}, {0x6033, 0x003a}, {0x60f1, 0x003b}, + {0x6031, 0x003c}, {0x6000, 0x00ff}, {0x6014, 0x00e0}, {0x60ff, 0x0076}, + {0x60a0, 0x0033}, {0x6020, 0x0042}, {0x6018, 0x0043}, {0x6000, 0x004c}, + {0x60d0, 0x0087}, {0x600f, 0x0088}, {0x6003, 0x00d7}, {0x6010, 0x00d9}, + {0x6005, 0x00da}, {0x6082, 0x00d3}, {0x60c0, 0x00f9}, {0x6006, 0x0044}, + {0x6007, 0x00d1}, {0x6002, 0x00d2}, {0x6000, 0x00d2}, {0x6011, 0x00d8}, + {0x6008, 0x00c8}, {0x6080, 0x00c9}, {0x6008, 0x007c}, {0x6020, 0x007d}, + {0x6020, 0x007d}, {0x6000, 0x0090}, {0x600e, 0x0091}, {0x601a, 0x0091}, + {0x6031, 0x0091}, {0x605a, 0x0091}, {0x6069, 0x0091}, {0x6075, 0x0091}, + {0x607e, 0x0091}, {0x6088, 0x0091}, {0x608f, 0x0091}, {0x6096, 0x0091}, + {0x60a3, 0x0091}, {0x60af, 0x0091}, {0x60c4, 0x0091}, {0x60d7, 0x0091}, + {0x60e8, 0x0091}, {0x6020, 0x0091}, {0x6000, 0x0092}, {0x6006, 0x0093}, + {0x60e3, 0x0093}, {0x6005, 0x0093}, {0x6005, 0x0093}, {0x6000, 0x0093}, + {0x6004, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, + {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, {0x6000, 0x0093}, + {0x6000, 0x0096}, {0x6008, 0x0097}, {0x6019, 0x0097}, {0x6002, 0x0097}, + {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097}, {0x6028, 0x0097}, + {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6098, 0x0097}, {0x6080, 0x0097}, + {0x6000, 0x0097}, {0x6000, 0x0097}, {0x60ed, 0x00c3}, {0x609a, 0x00c4}, + {0x6000, 0x00a4}, {0x6011, 0x00c5}, {0x6051, 0x00c6}, {0x6010, 0x00c7}, + {0x6066, 0x00b6}, {0x60a5, 0x00b8}, {0x6064, 0x00b7}, {0x607c, 0x00b9}, + {0x60af, 0x00b3}, {0x6097, 0x00b4}, {0x60ff, 0x00b5}, {0x60c5, 0x00b0}, + {0x6094, 0x00b1}, {0x600f, 0x00b2}, {0x605c, 0x00c4}, {0x6000, 0x00a8}, + {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x601d, 0x0086}, {0x6000, 0x0050}, + {0x6090, 0x0051}, {0x6018, 0x0052}, {0x6000, 0x0053}, {0x6000, 0x0054}, + {0x6088, 0x0055}, {0x6000, 0x0057}, {0x6090, 0x005a}, {0x6018, 0x005b}, + {0x6005, 0x005c}, {0x60ed, 0x00c3}, {0x6000, 0x007f}, {0x6005, 0x00da}, + {0x601f, 0x00e5}, {0x6067, 0x00e1}, {0x6000, 0x00e0}, {0x60ff, 0x00dd}, + {0x6000, 0x0005}, {0x6001, 0x00ff}, {0x6000, 0x0000}, {0x6000, 0x0045}, + {0x6000, 0x0010}, +}; + +static struct validx tbl_sensor_settings_common_a[] = { + {0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d}, + {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2}, + {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000}, + {50, 0xffff}, + {0x0061, 0x0000}, + {0xffff, 0xffff}, + {0x6000, 0x00ff}, {0x6000, 0x007c}, {0x6007, 0x007d}, + {30, 0xffff}, + {0x0040, 0x0000}, +}; + +static struct validx tbl_sensor_settings_common_b[] = { + {0x6001, 0x00ff}, {0x6038, 0x000c}, + {10, 0xffff}, + {0x6000, 0x0011}, + /* backlight=31/64 */ + {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025}, + /* bright=0/256 */ + {0x6000, 0x00ff}, {0x6009, 0x007c}, {0x6000, 0x007d}, + /* wbal=64/128 */ + {0x6000, 0x00ff}, {0x6003, 0x007c}, {0x6040, 0x007d}, + /* cntr=0/256 */ + {0x6000, 0x00ff}, {0x6007, 0x007c}, {0x6000, 0x007d}, + /* sat=128/256 */ + {0x6000, 0x00ff}, {0x6001, 0x007c}, {0x6080, 0x007d}, + /* sharpness=0/32 */ + {0x6000, 0x00ff}, {0x6001, 0x0092}, {0x60c0, 0x0093}, + /* hue=0/256 */ + {0x6000, 0x00ff}, {0x6002, 0x007c}, {0x6000, 0x007d}, + /* gam=32/64 */ + {0x6000, 0x00ff}, {0x6008, 0x007c}, {0x6020, 0x007d}, + /* image right up */ + {0xffff, 0xffff}, + {15, 0xffff}, + {0x6001, 0x00ff}, {0x6000, 0x8004}, + {0xffff, 0xffff}, + {0x60a8, 0x0004}, + {15, 0xffff}, + {0x6001, 0x00ff}, {0x6000, 0x8004}, + {0xffff, 0xffff}, + {0x60f8, 0x0004}, + /* image right up */ + {0xffff, 0xffff}, + /* backlight=31/64 */ + {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025}, +}; + +static struct validx tbl_640[] = { + {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1}, + {0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017}, + {0x6075, 0x0018}, {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032}, + {0x60bb, 0x004f}, {0x6057, 0x005a}, {0x609c, 0x0050}, {0x6080, 0x006d}, + {0x6092, 0x0026}, {0x60ff, 0x0020}, {0x6000, 0x0027}, {0x6000, 0x00ff}, + {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, {0x603d, 0x0086}, + {0x6089, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, {0x6000, 0x0053}, + {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, {0x60a0, 0x005a}, + {0x6078, 0x005b}, {0x6000, 0x005c}, {0x6004, 0x00d3}, {0x6000, 0x00e0}, + {0x60ff, 0x00dd}, {0x60a1, 0x005a}, +}; + +static struct validx tbl_800[] = { + {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6067, 0x00e1}, + {0x6004, 0x00da}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6040, 0x0012}, {0x6000, 0x0011}, {0x6011, 0x0017}, + {0x6043, 0x0018}, {0x6000, 0x0019}, {0x604b, 0x001a}, {0x6009, 0x0032}, + {0x60ca, 0x004f}, {0x60a8, 0x0050}, {0x6000, 0x006d}, {0x6038, 0x003d}, + {0x60c8, 0x0035}, {0x6000, 0x0022}, {0x6092, 0x0026}, {0x60ff, 0x0020}, + {0x6000, 0x0027}, {0x6000, 0x00ff}, {0x6064, 0x00c0}, {0x604b, 0x00c1}, + {0x6000, 0x008c}, {0x601d, 0x0086}, {0x6082, 0x00d3}, {0x6000, 0x00e0}, + {0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018}, +}; + +static struct validx tbl_big_a[] = { + {0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, + {0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018}, + {0x6001, 0x0019}, {0x6097, 0x001a}, {0x6036, 0x0032}, {0x60bb, 0x004f}, + {0x609c, 0x0050}, {0x6057, 0x005a}, {0x6080, 0x006d}, {0x6043, 0x000f}, + {0x608f, 0x0003}, {0x6005, 0x007c}, {0x6081, 0x0026}, {0x6000, 0x00ff}, + {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, +}; + +static struct validx tbl_big_b[] = { + {0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, + {0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, + {0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3}, + {0x6000, 0x008e}, +}; + +static struct validx tbl_big_c[] = { + {0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd}, + {0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, + {0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7}, + {0x6000, 0x0092}, {0x6006, 0x0093}, {0x60e3, 0x0093}, {0x6005, 0x0093}, + {0x6005, 0x0093}, {0x60ed, 0x00c3}, {0x6000, 0x00a4}, {0x60d0, 0x0087}, + {0x6003, 0x0096}, {0x600c, 0x0097}, {0x6024, 0x0097}, {0x6030, 0x0097}, + {0x6028, 0x0097}, {0x6026, 0x0097}, {0x6002, 0x0097}, {0x6001, 0x00ff}, + {0x6043, 0x000f}, {0x608f, 0x0003}, {0x6000, 0x002d}, {0x6000, 0x002e}, + {0x600a, 0x0022}, {0x6002, 0x0070}, {0x6008, 0x0014}, {0x6048, 0x0014}, + {0x6000, 0x00ff}, {0x6000, 0x00e0}, {0x60ff, 0x00dd}, +}; + +static struct validx tbl_post_unset_alt[] = { + {0x006a, 0x000d}, {0x6001, 0x00ff}, {0x6081, 0x0026}, {0x6000, 0x0000}, + {0x6000, 0x0045}, {0x6000, 0x0010}, {0x6068, 0x000d}, + {50, 0xffff}, + {0x0021, 0x0000}, +}; + +static int ov2640_init_at_startup(struct gspca_dev *gspca_dev); +static int ov2640_configure_alt(struct gspca_dev *gspca_dev); +static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev); +static int ov2640_init_post_alt(struct gspca_dev *gspca_dev); +static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev); +static int ov2640_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void ov2640_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 32; + sd->vcur.brightness = 0; + sd->vcur.sharpness = 6; + sd->vcur.contrast = 0; + sd->vcur.gamma = 32; + sd->vcur.hue = 0; + sd->vcur.saturation = 128; + sd->vcur.whitebal = 64; + + sd->vmax.backlight = 64; + sd->vmax.brightness = 255; + sd->vmax.sharpness = 31; + sd->vmax.contrast = 255; + sd->vmax.gamma = 64; + sd->vmax.hue = 255 + 1; + sd->vmax.saturation = 255; + sd->vmax.whitebal = 128; + sd->vmax.mirror = 0; + sd->vmax.flip = 0; + sd->vmax.AC50Hz = 0; + + sd->dev_camera_settings = ov2640_camera_settings; + sd->dev_init_at_startup = ov2640_init_at_startup; + sd->dev_configure_alt = ov2640_configure_alt; + sd->dev_init_pre_alt = ov2640_init_pre_alt; + sd->dev_post_unset_alt = ov2640_post_unset_alt; +} + +/*==========================================================================*/ + +static void common(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common)); +} + +static int ov2640_init_at_startup(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_init1); + + common(gspca_dev); + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, dat_init2); + + ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL); + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, dat_init3); + + ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL); +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ + + return 0; +} + +static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vold.backlight = -1; + sd->vold.brightness = -1; + sd->vold.sharpness = -1; + sd->vold.contrast = -1; + sd->vold.saturation = -1; + sd->vold.gamma = -1; + sd->vold.hue = -1; + sd->vold.whitebal = -1; + + ov2640_init_post_alt(gspca_dev); + + return 0; +} + +static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + s32 n; /* reserved for FETCH macros */ + + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + + n = fetch_validx(gspca_dev, tbl_sensor_settings_common_a, + ARRAY_SIZE(tbl_sensor_settings_common_a)); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post); + common(gspca_dev); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_a, + ARRAY_SIZE(tbl_sensor_settings_common_a), n); + + switch (reso) { + case IMAGE_640: + n = fetch_validx(gspca_dev, tbl_640, ARRAY_SIZE(tbl_640)); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_640); + break; + + case IMAGE_800: + n = fetch_validx(gspca_dev, tbl_800, ARRAY_SIZE(tbl_800)); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_800); + break; + + case IMAGE_1600: + case IMAGE_1280: + n = fetch_validx(gspca_dev, tbl_big_a, ARRAY_SIZE(tbl_big_a)); + + if (reso == IMAGE_1280) { + n = fetch_validx(gspca_dev, tbl_big_b, + ARRAY_SIZE(tbl_big_b)); + } else { + ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL); + } + + n = fetch_validx(gspca_dev, tbl_big_c, ARRAY_SIZE(tbl_big_c)); + + if (reso == IMAGE_1280) { + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1280); + } else { + ctrl_out(gspca_dev, 0x40, 1, 0x6020, 0x008c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6076, 0x0018, 0, NULL); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + 12, dat_1600); + } + break; + } + + n = fetch_validx(gspca_dev, tbl_sensor_settings_common_b, + ARRAY_SIZE(tbl_sensor_settings_common_b)); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, + ARRAY_SIZE(tbl_sensor_settings_common_b), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, + ARRAY_SIZE(tbl_sensor_settings_common_b), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, + ARRAY_SIZE(tbl_sensor_settings_common_b), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, + ARRAY_SIZE(tbl_sensor_settings_common_b), n); + + ov2640_camera_settings(gspca_dev); + + return 0; +} + +static int ov2640_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 3 + 1; + break; + + case IMAGE_800: + case IMAGE_1280: + case IMAGE_1600: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +static int ov2640_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + s32 backlight = sd->vcur.backlight; + s32 bright = sd->vcur.brightness; + s32 sharp = sd->vcur.sharpness; + s32 gam = sd->vcur.gamma; + s32 cntr = sd->vcur.contrast; + s32 sat = sd->vcur.saturation; + s32 hue = sd->vcur.hue; + s32 wbal = sd->vcur.whitebal; + + if (backlight != sd->vold.backlight) { + if (backlight < 0 || backlight > sd->vmax.backlight) + backlight = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025, + 0, NULL); + /* No sd->vold.backlight=backlight; (to be done again later) */ + } + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6009 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + bright, 0x007d, 0, NULL); + } + + if (wbal != sd->vold.whitebal) { + sd->vold.whitebal = wbal; + if (wbal < 0 || wbal > sd->vmax.whitebal) + wbal = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6003 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + wbal, 0x007d, 0, NULL); + } + + if (cntr != sd->vold.contrast) { + sd->vold.contrast = cntr; + if (cntr < 0 || cntr > sd->vmax.contrast) + cntr = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6007 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + cntr, 0x007d, 0, NULL); + } + + if (sat != sd->vold.saturation) { + sd->vold.saturation = sat; + if (sat < 0 || sat > sd->vmax.saturation) + sat = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + sat, 0x007d, 0, NULL); + } + + if (sharp != sd->vold.sharpness) { + sd->vold.sharpness = sharp; + if (sharp < 0 || sharp > sd->vmax.sharpness) + sharp = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x0092, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x60c0 + sharp, 0x0093, 0, NULL); + } + + if (hue != sd->vold.hue) { + sd->vold.hue = hue; + if (hue < 0 || hue > sd->vmax.hue) + hue = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6002 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d, + 0, NULL); + if (hue >= sd->vmax.hue) + sd->swapRB = 1; + else + sd->swapRB = 0; + } + + if (gam != sd->vold.gamma) { + sd->vold.gamma = gam; + if (gam < 0 || gam > sd->vmax.gamma) + gam = 0; + + ctrl_out(gspca_dev, 0x40, 1, 0x6000 , 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6008 , 0x007c, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL); + } + + if (backlight != sd->vold.backlight) { + sd->vold.backlight = backlight; + + ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024, + 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025, + 0, NULL); + } + + return 0; +} + +static void ov2640_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + msleep(20); + fetch_validx(gspca_dev, tbl_post_unset_alt, + ARRAY_SIZE(tbl_post_unset_alt)); +} diff --git a/linux/drivers/media/video/gspca/gl860/gl860-ov9655.c b/linux/drivers/media/video/gspca/gl860/gl860-ov9655.c new file mode 100644 index 000000000..eda3346f9 --- /dev/null +++ b/linux/drivers/media/video/gspca/gl860/gl860-ov9655.c @@ -0,0 +1,337 @@ +/* @file gl860-ov9655.c + * @author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt + * on dsd's weblog + * @date 2009-08-27 + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ + +/* Sensor : OV9655 */ + +#include "gl860.h" + +static struct validx tbl_init_at_startup[] = { + {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, + {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, + + {0x0040, 0x0000}, +}; + +static struct validx tbl_commmon[] = { + {0x0041, 0x0000}, {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, + {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2}, + {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0040, 0x0000}, + {0x00f3, 0x0006}, {0x0058, 0x0000}, {0x0048, 0x0000}, {0x0061, 0x0000}, +}; + +static s32 tbl_length[] = {12, 56, 52, 54, 56, 42, 32, 12}; + +static u8 *tbl_640[] = { + "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01" + , + "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x03\x0b\x57\x0e\x61" + "\x0f\x42\x11\x01\x12\x60\x13\x00" "\x14\x3a\x16\x24\x17\x14\x18\x00" + "\x19\x01\x1a\x3d\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08" + "\x29\x15\x2a\x00\x2b\x00\x2c\x08" + , + "\x32\xff\x33\x00\x34\x3d\x35\x00" "\x36\xfa\x38\x72\x39\x57\x3a\x00" + "\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc1" "\x40\xc0\x41\x00\x42\xc0\x43\x0a" + "\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xee\x4b\xe7\x4c\xe7" + "\x4d\xe7\x4e\xe7" + , + "\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85" + "\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0" + "\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x0a\x6b\x5a\x6c\x04" + "\x6d\x55\x6e\x00\x6f\x9d" + , + "\x70\x15\x71\x78\x72\x00\x73\x00" "\x74\x3a\x75\x35\x76\x01\x77\x02" + "\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52" + "\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5" + "\x8a\x23\x8c\x8d\x90\x7c\x91\x7b" + , + "\x9d\x02\x9e\x02\x9f\x74\xa0\x73" "\xa1\x40\xa4\x50\xa5\x68\xa6\x70" + "\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80" + "\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf" + , + "\xbb\xae\xbc\x4f\xbd\x4e\xbe\x6a" "\xbf\x68\xc0\xaa\xc1\xc0\xc2\x01" + "\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93" + , + "\xd0\x01\xd1\x08\xd2\xe0\xd3\x01" "\xd4\x10\xd5\x80" +}; + +static u8 *tbl_800[] = { + "\x00\x40\x07\x6a\x06\xf3\x0d\x6a" "\x10\x10\xc1\x01" + , + "\x12\x80\x00\x00\x01\x98\x02\x80" "\x03\x12\x04\x01\x0b\x57\x0e\x61" + "\x0f\x42\x11\x00\x12\x00\x13\x00" "\x14\x3a\x16\x24\x17\x1b\x18\xbb" + "\x19\x01\x1a\x81\x1e\x04\x24\x3c" "\x25\x36\x26\x72\x27\x08\x28\x08" + "\x29\x15\x2a\x00\x2b\x00\x2c\x08" + , + "\x32\xa4\x33\x00\x34\x3d\x35\x00" "\x36\xf8\x38\x72\x39\x57\x3a\x00" + "\x3b\x0c\x3d\x99\x3e\x0c\x3f\xc2" "\x40\xc0\x41\x00\x42\xc0\x43\x0a" + "\x44\xf0\x45\x46\x46\x62\x47\x2a" "\x48\x3c\x4a\xec\x4b\xe8\x4c\xe8" + "\x4d\xe8\x4e\xe8" + , + "\x4f\x98\x50\x98\x51\x00\x52\x28" "\x53\x70\x54\x98\x58\x1a\x59\x85" + "\x5a\xa9\x5b\x64\x5c\x84\x5d\x53" "\x5e\x0e\x5f\xf0\x60\xf0\x61\xf0" + "\x62\x00\x63\x00\x64\x02\x65\x20" "\x66\x00\x69\x02\x6b\x5a\x6c\x04" + "\x6d\x55\x6e\x00\x6f\x9d" + , + "\x70\x08\x71\x78\x72\x00\x73\x01" "\x74\x3a\x75\x35\x76\x01\x77\x02" + "\x7a\x24\x7b\x04\x7c\x07\x7d\x10" "\x7e\x28\x7f\x36\x80\x44\x81\x52" + "\x82\x60\x83\x6c\x84\x78\x85\x8c" "\x86\x9e\x87\xbb\x88\xd2\x89\xe5" + "\x8a\x23\x8c\x0d\x90\x90\x91\x90" + , + "\x9d\x02\x9e\x02\x9f\x94\xa0\x94" "\xa1\x01\xa4\x50\xa5\x68\xa6\x70" + "\xa8\xc1\xa9\xef\xaa\x92\xab\x04" "\xac\x80\xad\x80\xae\x80\xaf\x80" + "\xb2\xf2\xb3\x20\xb4\x20\xb5\x00" "\xb6\xaf" + , + "\xbb\xae\xbc\x38\xbd\x39\xbe\x01" "\xbf\x01\xc0\xe2\xc1\xc0\xc2\x01" + "\xc3\x4e\xc6\x85\xc7\x81\xc9\xe0" "\xca\xe8\xcb\xf0\xcc\xd8\xcd\x93" + , + "\xd0\x21\xd1\x18\xd2\xe0\xd3\x01" "\xd4\x28\xd5\x00" +}; + +static u8 c04[] = {0x04}; +static u8 dat_post_1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02"; +static u8 dat_post_2[] = "\x10\x10\xc1\x02"; +static u8 dat_post_3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04"; +static u8 dat_post_4[] = "\x10\x02\xc1\x06"; +static u8 dat_post_5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08"; +static u8 dat_post_6[] = "\x10\x10\xc1\x05"; +static u8 dat_post_7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08"; +static u8 dat_post_8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09"; + +static struct validx tbl_init_post_alt[] = { + {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff}, + {0x6003, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6001, 0x00ff}, + {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, + {0xffff, 0xffff}, + {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, + {0xffff, 0xffff}, + {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6000, 0x801e}, + {0xffff, 0xffff}, + {0x6004, 0x001e}, {0x6012, 0x0003}, +}; + +static int ov9655_init_at_startup(struct gspca_dev *gspca_dev); +static int ov9655_configure_alt(struct gspca_dev *gspca_dev); +static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev); +static int ov9655_init_post_alt(struct gspca_dev *gspca_dev); +static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev); +static int ov9655_camera_settings(struct gspca_dev *gspca_dev); +/*==========================================================================*/ + +void ov9655_init_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vcur.backlight = 0; + sd->vcur.brightness = 128; + sd->vcur.sharpness = 0; + sd->vcur.contrast = 0; + sd->vcur.gamma = 0; + sd->vcur.hue = 0; + sd->vcur.saturation = 0; + sd->vcur.whitebal = 0; + + sd->vmax.backlight = 0; + sd->vmax.brightness = 255; + sd->vmax.sharpness = 0; + sd->vmax.contrast = 0; + sd->vmax.gamma = 0; + sd->vmax.hue = 0 + 1; + sd->vmax.saturation = 0; + sd->vmax.whitebal = 0; + sd->vmax.mirror = 0; + sd->vmax.flip = 0; + sd->vmax.AC50Hz = 0; + + sd->dev_camera_settings = ov9655_camera_settings; + sd->dev_init_at_startup = ov9655_init_at_startup; + sd->dev_configure_alt = ov9655_configure_alt; + sd->dev_init_pre_alt = ov9655_init_pre_alt; + sd->dev_post_unset_alt = ov9655_post_unset_alt; +} + +/*==========================================================================*/ + +static int ov9655_init_at_startup(struct gspca_dev *gspca_dev) +{ + fetch_validx(gspca_dev, tbl_init_at_startup, + ARRAY_SIZE(tbl_init_at_startup)); + fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon)); +/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL);*/ + + return 0; +} + +static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vold.brightness = -1; + sd->vold.hue = -1; + + fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon)); + + ov9655_init_post_alt(gspca_dev); + + return 0; +} + +static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + s32 n; /* reserved for FETCH macros */ + s32 i; + u8 **tbl; + + ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); + + tbl = (reso == IMAGE_640) ? tbl_640 : tbl_800; + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + tbl_length[0], tbl[0]); + for (i = 1; i < 7; i++) + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, + tbl_length[i], tbl[i]); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, + tbl_length[7], tbl[7]); + + n = fetch_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt)); + + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); + keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, + ARRAY_SIZE(tbl_init_post_alt), n); + + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_2); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_3); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_4); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_5); + + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_6); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_7); + + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_8); + + ov9655_camera_settings(gspca_dev); + + return 0; +} + +static int ov9655_configure_alt(struct gspca_dev *gspca_dev) +{ + s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; + + switch (reso) { + case IMAGE_640: + gspca_dev->alt = 1 + 1; + break; + + default: + gspca_dev->alt = 1 + 1; + break; + } + return 0; +} + +static int ov9655_camera_settings(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + u8 dat_bright[] = "\x04\x00\x10\x7c\xa1\x00\x00\x70"; + + s32 bright = sd->vcur.brightness; + s32 hue = sd->vcur.hue; + + if (bright != sd->vold.brightness) { + sd->vold.brightness = bright; + if (bright < 0 || bright > sd->vmax.brightness) + bright = 0; + + dat_bright[3] = bright; + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_bright); + } + + if (hue != sd->vold.hue) { + sd->vold.hue = hue; + sd->swapRB = (hue != 0); + } + + return 0; +} + +static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev) +{ + ctrl_out(gspca_dev, 0x40, 5, 0x0000, 0x0000, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x0061, 0x0000, 0, NULL); +} diff --git a/linux/drivers/media/video/gspca/gl860/gl860.c b/linux/drivers/media/video/gspca/gl860/gl860.c new file mode 100644 index 000000000..62f4320fd --- /dev/null +++ b/linux/drivers/media/video/gspca/gl860/gl860.c @@ -0,0 +1,783 @@ +/* @file gl860.c + * @date 2009-08-27 + * + * Genesys Logic webcam with gl860 subdrivers + * + * Driver by Olivier Lorin <o.lorin@laposte.net> + * GSPCA by Jean-Francois Moine <http://moinejf.free.fr> + * Thanks BUGabundo and Malmostoso for your amazing help! + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ +#include "gspca.h" +#include "gl860.h" + +MODULE_AUTHOR("Olivier Lorin <lorin@laposte.net>"); +MODULE_DESCRIPTION("GSPCA/Genesys Logic GL860 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/*======================== static function declarations ====================*/ + +static void (*dev_init_settings)(struct gspca_dev *gspca_dev); + +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id); +static int sd_init(struct gspca_dev *gspca_dev); +static int sd_isoc_init(struct gspca_dev *gspca_dev); +static int sd_start(struct gspca_dev *gspca_dev); +static void sd_stop0(struct gspca_dev *gspca_dev); +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, u8 *data, s32 len); +static void sd_callback(struct gspca_dev *gspca_dev); + +static int gl860_guess_sensor(struct gspca_dev *gspca_dev, + s32 vendor_id, s32 product_id); + +/*============================ driver options ==============================*/ + +static s32 AC50Hz = 0xff; +module_param(AC50Hz, int, 0644); +MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)"); + +static char sensor[7]; +module_param_string(sensor, sensor, sizeof(sensor), 0644); +MODULE_PARM_DESC(sensor, + " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640'/'')"); + +/*============================ webcam controls =============================*/ + +/* Functions to get and set a control value */ +#define SD_SETGET(thename) \ +static int sd_set_##thename(struct gspca_dev *gspca_dev, s32 val)\ +{\ + struct sd *sd = (struct sd *) gspca_dev;\ +\ + sd->vcur.thename = val;\ + if (gspca_dev->streaming)\ + sd->dev_camera_settings(gspca_dev);\ + return 0;\ +} \ +static int sd_get_##thename(struct gspca_dev *gspca_dev, s32 *val)\ +{\ + struct sd *sd = (struct sd *) gspca_dev;\ +\ + *val = sd->vcur.thename;\ + return 0;\ +} + +SD_SETGET(mirror) +SD_SETGET(flip) +SD_SETGET(AC50Hz) +SD_SETGET(backlight) +SD_SETGET(brightness) +SD_SETGET(gamma) +SD_SETGET(hue) +SD_SETGET(saturation) +SD_SETGET(sharpness) +SD_SETGET(whitebal) +SD_SETGET(contrast) + +#define GL860_NCTRLS 11 + +/* control table */ +static struct ctrl sd_ctrls_mi1320[GL860_NCTRLS]; +static struct ctrl sd_ctrls_mi2020[GL860_NCTRLS]; +static struct ctrl sd_ctrls_mi2020b[GL860_NCTRLS]; +static struct ctrl sd_ctrls_ov2640[GL860_NCTRLS]; +static struct ctrl sd_ctrls_ov9655[GL860_NCTRLS]; + +#define SET_MY_CTRL(theid, \ + thetype, thelabel, thename) \ + if (sd->vmax.thename != 0) {\ + sd_ctrls[nCtrls].qctrl.id = theid;\ + sd_ctrls[nCtrls].qctrl.type = thetype;\ + strcpy(sd_ctrls[nCtrls].qctrl.name, thelabel);\ + sd_ctrls[nCtrls].qctrl.minimum = 0;\ + sd_ctrls[nCtrls].qctrl.maximum = sd->vmax.thename;\ + sd_ctrls[nCtrls].qctrl.default_value = sd->vcur.thename;\ + sd_ctrls[nCtrls].qctrl.step = \ + (sd->vmax.thename < 16) ? 1 : sd->vmax.thename/16;\ + sd_ctrls[nCtrls].set = sd_set_##thename;\ + sd_ctrls[nCtrls].get = sd_get_##thename;\ + nCtrls++;\ + } + +static int gl860_build_control_table(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct ctrl *sd_ctrls; + int nCtrls = 0; + + if (_MI1320_) + sd_ctrls = sd_ctrls_mi1320; + else if (_MI2020_) + sd_ctrls = sd_ctrls_mi2020; + else if (_MI2020b_) + sd_ctrls = sd_ctrls_mi2020b; + else if (_OV2640_) + sd_ctrls = sd_ctrls_ov2640; + else if (_OV9655_) + sd_ctrls = sd_ctrls_ov9655; + + memset(sd_ctrls, 0, GL860_NCTRLS * sizeof(struct ctrl)); + + SET_MY_CTRL(V4L2_CID_BRIGHTNESS, + V4L2_CTRL_TYPE_INTEGER, "Brightness", brightness) + SET_MY_CTRL(V4L2_CID_SHARPNESS, + V4L2_CTRL_TYPE_INTEGER, "Sharpness", sharpness) + SET_MY_CTRL(V4L2_CID_CONTRAST, + V4L2_CTRL_TYPE_INTEGER, "Contrast", contrast) + SET_MY_CTRL(V4L2_CID_GAMMA, + V4L2_CTRL_TYPE_INTEGER, "Gamma", gamma) + SET_MY_CTRL(V4L2_CID_HUE, + V4L2_CTRL_TYPE_INTEGER, "Palette", hue) + SET_MY_CTRL(V4L2_CID_SATURATION, + V4L2_CTRL_TYPE_INTEGER, "Saturation", saturation) + SET_MY_CTRL(V4L2_CID_WHITE_BALANCE_TEMPERATURE, + V4L2_CTRL_TYPE_INTEGER, "White Bal.", whitebal) + SET_MY_CTRL(V4L2_CID_BACKLIGHT_COMPENSATION, + V4L2_CTRL_TYPE_INTEGER, "Backlight" , backlight) + + SET_MY_CTRL(V4L2_CID_HFLIP, + V4L2_CTRL_TYPE_BOOLEAN, "Mirror", mirror) + SET_MY_CTRL(V4L2_CID_VFLIP, + V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip) + SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CTRL_TYPE_BOOLEAN, "50Hz", AC50Hz) + + return nCtrls; +} + +/*==================== sud-driver structure initialisation =================*/ + +static struct sd_desc sd_desc_mi1320 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_mi1320, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +static struct sd_desc sd_desc_mi2020 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_mi2020, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +static struct sd_desc sd_desc_mi2020b = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_mi2020b, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +static struct sd_desc sd_desc_ov2640 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_ov2640, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +static struct sd_desc sd_desc_ov9655 = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_ov9655, + .nctrls = GL860_NCTRLS, + .config = sd_config, + .init = sd_init, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = sd_callback, +}; + +/*=========================== sub-driver image sizes =======================*/ + +static struct v4l2_pix_format mi2020_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + {1280, 1024, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + }, + {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3 + }, +}; + +static struct v4l2_pix_format ov2640_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + }, + {1600, 1200, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3 + }, +}; + +static struct v4l2_pix_format mi1320_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + { 800, 600, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + }, +}; + +static struct v4l2_pix_format ov9655_mode[] = { + { 640, 480, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + {1280, 960, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 960, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, +}; + +/*========================= sud-driver functions ===========================*/ + +/* This function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + s32 vendor_id, product_id; + + /* Get USB VendorID and ProductID */ + vendor_id = le16_to_cpu(id->idVendor); + product_id = le16_to_cpu(id->idProduct); + + sd->nbRightUp = 1; + sd->nbIm = -1; + + sd->sensor = 0xff; + if (strcmp(sensor, "MI1320") == 0) + sd->sensor = ID_MI1320; + else if (strcmp(sensor, "OV2640") == 0) + sd->sensor = ID_OV2640; + else if (strcmp(sensor, "OV9655") == 0) + sd->sensor = ID_OV9655; + else if (strcmp(sensor, "MI2020") == 0) + sd->sensor = ID_MI2020; + else if (strcmp(sensor, "MI2020b") == 0) + sd->sensor = ID_MI2020b; + + /* Get sensor and set the suitable init/start/../stop functions */ + if (gl860_guess_sensor(gspca_dev, vendor_id, product_id) == -1) + return -1; + + cam = &gspca_dev->cam; + gspca_dev->nbalt = 4; + + switch (sd->sensor) { + case ID_MI1320: + gspca_dev->sd_desc = &sd_desc_mi1320; + cam->cam_mode = mi1320_mode; + cam->nmodes = ARRAY_SIZE(mi1320_mode); + dev_init_settings = mi1320_init_settings; + break; + + case ID_MI2020: + gspca_dev->sd_desc = &sd_desc_mi2020; + cam->cam_mode = mi2020_mode; + cam->nmodes = ARRAY_SIZE(mi2020_mode); + dev_init_settings = mi2020_init_settings; + break; + + case ID_MI2020b: + gspca_dev->sd_desc = &sd_desc_mi2020b; + cam->cam_mode = mi2020_mode; + cam->nmodes = ARRAY_SIZE(mi2020_mode); + dev_init_settings = mi2020_init_settings; + break; + + case ID_OV2640: + gspca_dev->sd_desc = &sd_desc_ov2640; + cam->cam_mode = ov2640_mode; + cam->nmodes = ARRAY_SIZE(ov2640_mode); + dev_init_settings = ov2640_init_settings; + break; + + case ID_OV9655: + gspca_dev->sd_desc = &sd_desc_ov9655; + cam->cam_mode = ov9655_mode; + cam->nmodes = ARRAY_SIZE(ov9655_mode); + dev_init_settings = ov9655_init_settings; + break; + } + + dev_init_settings(gspca_dev); + if (AC50Hz != 0xff) + ((struct sd *) gspca_dev)->vcur.AC50Hz = AC50Hz; + gl860_build_control_table(gspca_dev); + + return 0; +} + +/* This function is called at probe time after sd_config */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return sd->dev_init_at_startup(gspca_dev); +} + +/* This function is called before to choose the alt setting */ +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return sd->dev_configure_alt(gspca_dev); +} + +/* This function is called to start the webcam */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return sd->dev_init_pre_alt(gspca_dev); +} + +/* This function is called to stop the webcam */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return sd->dev_post_unset_alt(gspca_dev); +} + +/* This function is called when an image is being received */ +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, u8 *data, s32 len) +{ + struct sd *sd = (struct sd *) gspca_dev; + static s32 nSkipped; + + s32 mode = (s32) gspca_dev->curr_mode; + s32 nToSkip = + sd->swapRB * (gspca_dev->cam.cam_mode[mode].bytesperline + 1); + + /* Test only against 0202h, so endianess does not matter */ + switch (*(s16 *) data) { + case 0x0202: /* End of frame, start a new one */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); + nSkipped = 0; + if (sd->nbIm >= 0 && sd->nbIm < 10) + sd->nbIm++; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + break; + + default: + data += 2; + len -= 2; + if (nSkipped + len <= nToSkip) + nSkipped += len; + else { + if (nSkipped < nToSkip && nSkipped + len > nToSkip) { + data += nToSkip - nSkipped; + len -= nToSkip - nSkipped; + nSkipped = nToSkip + 1; + } + gspca_frame_add(gspca_dev, + INTER_PACKET, frame, data, len); + } + break; + } +} + +/* This function is called when an image has been read */ +/* This function is used to monitor webcam orientation */ +static void sd_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!_OV9655_) { + u8 state; + u8 upsideDown; + + /* Probe sensor orientation */ + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, (void *)&state); + + /* C8/40 means upside-down (looking backwards) */ + /* D8/50 means right-up (looking onwards) */ + upsideDown = (state == 0xc8 || state == 0x40); + + if (upsideDown && sd->nbRightUp > -4) { + if (sd->nbRightUp > 0) + sd->nbRightUp = 0; + if (sd->nbRightUp == -3) { + sd->mirrorMask = 1; + sd->waitSet = 1; + } + sd->nbRightUp--; + } + if (!upsideDown && sd->nbRightUp < 4) { + if (sd->nbRightUp < 0) + sd->nbRightUp = 0; + if (sd->nbRightUp == 3) { + sd->mirrorMask = 0; + sd->waitSet = 1; + } + sd->nbRightUp++; + } + } + + if (sd->waitSet) + sd->dev_camera_settings(gspca_dev); +} + +/*=================== USB driver structure initialisation ==================*/ + +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x05e3, 0x0503)}, + {USB_DEVICE(0x05e3, 0xf191)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct gspca_dev *gspca_dev; + s32 ret; + + ret = gspca_dev_probe(intf, id, + &sd_desc_mi1320, sizeof(struct sd), THIS_MODULE); + + if (ret >= 0) { + gspca_dev = usb_get_intfdata(intf); + + PDEBUG(D_PROBE, + "Camera is now controlling video device /dev/video%d", + gspca_dev->vdev.minor); + } + + return ret; +} + +static void sd_disconnect(struct usb_interface *intf) +{ + gspca_disconnect(intf); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = sd_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/*====================== Init and Exit module functions ====================*/ + +static int __init sd_mod_init(void) +{ + PDEBUG(D_PROBE, "driver startup - version %s", DRIVER_VERSION); + + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "driver registered"); + + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "driver deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); + +/*==========================================================================*/ + +int gl860_RTx(struct gspca_dev *gspca_dev, + unsigned char pref, u32 req, u16 val, u16 index, + s32 len, void *pdata) +{ + struct usb_device *udev = gspca_dev->dev; + s32 r = 0; + + if (pref == 0x40) { /* Send */ + if (len > 0) { + memcpy(gspca_dev->usb_buf, pdata, len); + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + req, pref, val, index, + gspca_dev->usb_buf, + len, 400 + 200 * (len > 1)); + } else { + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + req, pref, val, index, NULL, len, 400); + } + } else { /* Receive */ + if (len > 0) { + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + req, pref, val, index, + gspca_dev->usb_buf, + len, 400 + 200 * (len > 1)); + memcpy(pdata, gspca_dev->usb_buf, len); + } else { + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + req, pref, val, index, NULL, len, 400); + } + } + + if (r < 0) + PDEBUG(D_ERR, + "ctrl transfer failed %4d " + "[p%02x r%d v%04x i%04x len%d]", + r, pref, req, val, index, len); + else if (len > 1 && r < len) + PDEBUG(D_ERR, "short ctrl transfer %d/%d", r, len); + + if ((_MI2020_ || _MI2020b_ || _MI2020c_) && (val || index)) + msleep(1); + if (_OV2640_) + msleep(1); + + return r; +} + +int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len) +{ + int n; + + for (n = 0; n < len; n++) { + if (tbl[n].idx != 0xffff) + ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, + tbl[n].idx, 0, NULL); + else if (tbl[n].val == 0xffff) + break; + else + msleep(tbl[n].val); + } + return n; +} + +int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl, + int len, int n) +{ + while (++n < len) { + if (tbl[n].idx != 0xffff) + ctrl_out(gspca_dev, 0x40, 1, tbl[n].val, tbl[n].idx, + 0, NULL); + else if (tbl[n].val == 0xffff) + break; + else + msleep(tbl[n].val); + } + return n; +} + +void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len) +{ + int n; + + for (n = 0; n < len; n++) { + if (memcmp(tbl[n].data, "\xff\xff\xff", 3) != 0) + ctrl_out(gspca_dev, 0x40, 3, 0x7a00, tbl[n].idx, + 3, tbl[n].data); + else + msleep(tbl[n].idx); + } +} + +static int gl860_guess_sensor(struct gspca_dev *gspca_dev, + s32 vendor_id, s32 product_id) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 probe, nb26, nb96, nOV, ntry; + + if (product_id == 0xf191) + sd->sensor = ID_MI1320; + + if (sd->sensor == 0xff) { + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0004, 1, &probe); + + ctrl_out(gspca_dev, 0x40, 1, 0x0000, 0x0000, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0010, 0x0010, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x00c0, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c1, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x00c2, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0020, 0x0006, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); + msleep(56); + + nOV = 0; + for (ntry = 0; ntry < 4; ntry++) { + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x0063, 0x0006, 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); + msleep(10); + ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe); + PDEBUG(D_PROBE, "1st probe=%02x", probe); + if (probe == 0xff) + nOV++; + } + + if (nOV) { + PDEBUG(D_PROBE, "0xff -> sensor OVXXXX"); + PDEBUG(D_PROBE, "Probing for sensor OV2640 or OV9655"); + + nb26 = nb96 = 0; + for (ntry = 0; ntry < 4; ntry++) { + ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, + 0, NULL); + msleep(3); + ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a, + 0, NULL); + msleep(10); + /* Wait for 26(OV2640) or 96(OV9655) */ + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a, + 1, &probe); + + PDEBUG(D_PROBE, "2nd probe=%02x", probe); + if (probe == 0x00) + nb26++; + if (probe == 0x26 || probe == 0x40) { + sd->sensor = ID_OV2640; + nb26 += 4; + break; + } + if (probe == 0x96 || probe == 0x55) { + sd->sensor = ID_OV9655; + nb96 += 4; + break; + } + if (probe == 0xff) + nb96++; + msleep(3); + } + if (nb26 < 4 && nb96 < 4) { + PDEBUG(D_PROBE, "No relevant answer "); + PDEBUG(D_PROBE, "* 1.3Mpixels -> use OV9655"); + PDEBUG(D_PROBE, "* 2.0Mpixels -> use OV2640"); + PDEBUG(D_PROBE, + "To force a sensor, add that line to " + "/etc/modprobe.d/options.conf:"); + PDEBUG(D_PROBE, "options gspca_gl860 " + "sensor=\"OV2640\" or \"OV9655\""); + return -1; + } + } else { /* probe = 0 */ + PDEBUG(D_PROBE, "No 0xff -> sensor MI2020"); + sd->sensor = ID_MI2020; + } + } + + if (_MI1320_) { + PDEBUG(D_PROBE, "05e3:f191 sensor MI1320 (1.3M)"); + } else if (_MI2020_) { + PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 (2.0M)"); + } else if (_MI2020b_) { + PDEBUG(D_PROBE, "05e3:0503 sensor MI2020 alt. driver (2.0M)"); + } else if (_OV9655_) { + PDEBUG(D_PROBE, "05e3:0503 sensor OV9655 (1.3M)"); + } else if (_OV2640_) { + PDEBUG(D_PROBE, "05e3:0503 sensor OV2640 (2.0M)"); + } else { + PDEBUG(D_PROBE, "***** Unknown sensor *****"); + return -1; + } + + return 0; +} diff --git a/linux/drivers/media/video/gspca/gl860/gl860.h b/linux/drivers/media/video/gspca/gl860/gl860.h new file mode 100644 index 000000000..cef4e24c1 --- /dev/null +++ b/linux/drivers/media/video/gspca/gl860/gl860.h @@ -0,0 +1,108 @@ +/* @file gl860.h + * @author Olivier LORIN, tiré du pilote Syntek par Nicolas VIVIEN + * @date 2009-08-27 + * + * 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 + * 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef GL860_DEV_H +#define GL860_DEV_H +#include <linux/version.h> + +#include "gspca.h" + +#define MODULE_NAME "gspca_gl860" +#define DRIVER_VERSION "0.9d10" + +#define ctrl_in gl860_RTx +#define ctrl_out gl860_RTx + +#define ID_MI1320 1 +#define ID_OV2640 2 +#define ID_OV9655 4 +#define ID_MI2020 8 +#define ID_MI2020b 16 + +#define _MI1320_ (((struct sd *) gspca_dev)->sensor == ID_MI1320) +#define _MI2020_ (((struct sd *) gspca_dev)->sensor == ID_MI2020) +#define _MI2020b_ (((struct sd *) gspca_dev)->sensor == ID_MI2020b) +#define _MI2020c_ 0 +#define _OV2640_ (((struct sd *) gspca_dev)->sensor == ID_OV2640) +#define _OV9655_ (((struct sd *) gspca_dev)->sensor == ID_OV9655) + +#define IMAGE_640 0 +#define IMAGE_800 1 +#define IMAGE_1280 2 +#define IMAGE_1600 3 + +struct sd_gl860 { + u16 backlight; + u16 brightness; + u16 sharpness; + u16 contrast; + u16 gamma; + u16 hue; + u16 saturation; + u16 whitebal; + u8 mirror; + u8 flip; + u8 AC50Hz; +}; + +/* Specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct sd_gl860 vcur; + struct sd_gl860 vold; + struct sd_gl860 vmax; + + int (*dev_configure_alt) (struct gspca_dev *); + int (*dev_init_at_startup)(struct gspca_dev *); + int (*dev_init_pre_alt) (struct gspca_dev *); + void (*dev_post_unset_alt) (struct gspca_dev *); + int (*dev_camera_settings)(struct gspca_dev *); + + u8 swapRB; + u8 mirrorMask; + u8 sensor; + s32 nbIm; + s32 nbRightUp; + u8 waitSet; +}; + +struct validx { + u16 val; + u16 idx; +}; + +struct idxdata { + u8 idx; + u8 data[3]; +}; + +int fetch_validx(struct gspca_dev *gspca_dev, struct validx *tbl, int len); +int keep_on_fetching_validx(struct gspca_dev *gspca_dev, struct validx *tbl, + int len, int n); +void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len); + +int gl860_RTx(struct gspca_dev *gspca_dev, + unsigned char pref, u32 req, u16 val, u16 index, + s32 len, void *pdata); + +void mi1320_init_settings(struct gspca_dev *); +void ov2640_init_settings(struct gspca_dev *); +void ov9655_init_settings(struct gspca_dev *); +void mi2020_init_settings(struct gspca_dev *); + +#endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index 1dd7c161f..38a56294d 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -49,6 +49,12 @@ static DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550") } }, { + .ident = "Fujitsu-Siemens Amilo Pa 2548", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548") + } + }, { .ident = "MSI GX700", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), @@ -56,6 +62,13 @@ static DMI_MATCH(DMI_BIOS_DATE, "07/26/2007") } }, { + .ident = "MSI GX700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), + DMI_MATCH(DMI_BIOS_DATE, "07/19/2007") + } + }, { .ident = "MSI GX700/GX705/EX700", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), diff --git a/linux/drivers/media/video/gspca/mr97310a.c b/linux/drivers/media/video/gspca/mr97310a.c index 301325134..140c8f320 100644 --- a/linux/drivers/media/video/gspca/mr97310a.c +++ b/linux/drivers/media/video/gspca/mr97310a.c @@ -3,6 +3,21 @@ * * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com> * + * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+ + * and for the routines for detecting and classifying these various cameras, + * + * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> + * + * Acknowledgements: + * + * The MR97311A support in gspca/mars.c has been helpful in understanding some + * of the registers in these cameras. + * + * Hans de Goede <hdgoede@redhat.com> and + * Thomas Kaiser <thomas@kaiser-linux.li> + * have assisted with their experience. Each of them has also helped by + * testing a previously unsupported camera. + * * 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 @@ -22,18 +37,108 @@ #include "gspca.h" -MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>"); +#define CAM_TYPE_CIF 0 +#define CAM_TYPE_VGA 1 + +#define MR97310A_BRIGHTNESS_MIN -254 +#define MR97310A_BRIGHTNESS_MAX 255 +#define MR97310A_BRIGHTNESS_DEFAULT 0 + +#define MR97310A_EXPOSURE_MIN 300 +#define MR97310A_EXPOSURE_MAX 4095 +#define MR97310A_EXPOSURE_DEFAULT 1000 + +#define MR97310A_GAIN_MIN 0 +#define MR97310A_GAIN_MAX 31 +#define MR97310A_GAIN_DEFAULT 25 + +MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>," + "Theodore Kilgore <kilgota@auburn.edu>"); MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); MODULE_LICENSE("GPL"); +/* global parameters */ +int force_sensor_type = -1; +module_param(force_sensor_type, int, 0644); +MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)"); + /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ u8 sof_read; + u8 cam_type; /* 0 is CIF and 1 is VGA */ + u8 sensor_type; /* We use 0 and 1 here, too. */ + u8 do_lcd_stop; + + int brightness; + u16 exposure; + u8 gain; +}; + +struct sensor_w_data { + u8 reg; + u8 flags; + u8 data[16]; + int len; }; +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_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(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 void setbrightness(struct gspca_dev *gspca_dev); +static void setexposure(struct gspca_dev *gspca_dev); +static void setgain(struct gspca_dev *gspca_dev); + /* V4L2 controls supported by the driver */ static struct ctrl sd_ctrls[] = { + { +#define BRIGHTNESS_IDX 0 + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = MR97310A_BRIGHTNESS_MIN, + .maximum = MR97310A_BRIGHTNESS_MAX, + .step = 1, + .default_value = MR97310A_BRIGHTNESS_DEFAULT, + .flags = 0, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { +#define EXPOSURE_IDX 1 + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = MR97310A_EXPOSURE_MIN, + .maximum = MR97310A_EXPOSURE_MAX, + .step = 1, + .default_value = MR97310A_EXPOSURE_DEFAULT, + .flags = 0, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { +#define GAIN_IDX 2 + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = MR97310A_GAIN_MIN, + .maximum = MR97310A_GAIN_MAX, + .step = 1, + .default_value = MR97310A_GAIN_DEFAULT, + .flags = 0, + }, + .set = sd_setgain, + .get = sd_getgain, + }, }; static const struct v4l2_pix_format vga_mode[] = { @@ -65,7 +170,7 @@ static const struct v4l2_pix_format vga_mode[] = { }; /* the bytes to write are in gspca_dev->usb_buf */ -static int reg_w(struct gspca_dev *gspca_dev, int len) +static int mr_write(struct gspca_dev *gspca_dev, int len) { int rc; @@ -78,15 +183,249 @@ static int reg_w(struct gspca_dev *gspca_dev, int len) return rc; } +/* the bytes are read into gspca_dev->usb_buf */ +static int mr_read(struct gspca_dev *gspca_dev, int len) +{ + int rc; + + rc = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 3), + gspca_dev->usb_buf, len, NULL, 500); + if (rc < 0) + PDEBUG(D_ERR, "reg read [%02x] error %d", + gspca_dev->usb_buf[0], rc); + return rc; +} + +static int sensor_write_reg(struct gspca_dev *gspca_dev, u8 reg, u8 flags, + const u8 *data, int len) +{ + gspca_dev->usb_buf[0] = 0x1f; + gspca_dev->usb_buf[1] = flags; + gspca_dev->usb_buf[2] = reg; + memcpy(gspca_dev->usb_buf + 3, data, len); + + return mr_write(gspca_dev, len + 3); +} + +static int sensor_write_regs(struct gspca_dev *gspca_dev, + const struct sensor_w_data *data, int len) +{ + int i, rc; + + for (i = 0; i < len; i++) { + rc = sensor_write_reg(gspca_dev, data[i].reg, data[i].flags, + data[i].data, data[i].len); + if (rc < 0) + return rc; + } + + return 0; +} + +static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 buf, confirm_reg; + int rc; + + buf = data; + rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); + if (rc < 0) + return rc; + + buf = 0x01; + confirm_reg = sd->sensor_type ? 0x13 : 0x11; + rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1); + if (rc < 0) + return rc; + + return 0; +} + +static int cam_get_response16(struct gspca_dev *gspca_dev) +{ + __u8 *data = gspca_dev->usb_buf; + int err_code; + + data[0] = 0x21; + err_code = mr_write(gspca_dev, 1); + if (err_code < 0) + return err_code; + + err_code = mr_read(gspca_dev, 16); + return err_code; +} + +static int zero_the_pointer(struct gspca_dev *gspca_dev) +{ + __u8 *data = gspca_dev->usb_buf; + int err_code; + u8 status = 0; + int tries = 0; + + err_code = cam_get_response16(gspca_dev); + if (err_code < 0) + return err_code; + + err_code = mr_write(gspca_dev, 1); + data[0] = 0x19; + data[1] = 0x51; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev); + if (err_code < 0) + return err_code; + + data[0] = 0x19; + data[1] = 0xba; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev); + if (err_code < 0) + return err_code; + + data[0] = 0x19; + data[1] = 0x00; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev); + if (err_code < 0) + return err_code; + + data[0] = 0x19; + data[1] = 0x00; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + while (status != 0x0a && tries < 256) { + err_code = cam_get_response16(gspca_dev); + status = data[0]; + tries++; + if (err_code < 0) + return err_code; + } + if (status != 0x0a) + PDEBUG(D_ERR, "status is %02x", status); + + tries = 0; + while (tries < 4) { + data[0] = 0x19; + data[1] = 0x00; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + err_code = cam_get_response16(gspca_dev); + status = data[0]; + tries++; + if (err_code < 0) + return err_code; + } + + data[0] = 0x19; + err_code = mr_write(gspca_dev, 1); + if (err_code < 0) + return err_code; + + err_code = mr_read(gspca_dev, 16); + if (err_code < 0) + return err_code; + + return 0; +} + +static u8 get_sensor_id(struct gspca_dev *gspca_dev) +{ + int err_code; + + gspca_dev->usb_buf[0] = 0x1e; + err_code = mr_write(gspca_dev, 1); + if (err_code < 0) + return err_code; + + err_code = mr_read(gspca_dev, 16); + if (err_code < 0) + return err_code; + + PDEBUG(D_PROBE, "Byte zero reported is %01x", gspca_dev->usb_buf[0]); + + return gspca_dev->usb_buf[0]; +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { + struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; + __u8 *data = gspca_dev->usb_buf; + int err_code; cam = &gspca_dev->cam; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); + + if (id->idProduct == 0x010e) { + sd->cam_type = CAM_TYPE_CIF; + cam->nmodes--; + + data[0] = 0x01; + data[1] = 0x01; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; + + msleep(200); + data[0] = get_sensor_id(gspca_dev); + /* + * Known CIF cameras. If you have another to report, please do + * + * Name byte just read sd->sensor_type + * reported by + * Sakar Spy-shot 0x28 T. Kilgore 0 + * Innovage 0xf5 (unstable) T. Kilgore 0 + * Vivitar Mini 0x53 H. De Goede 0 + * Vivitar Mini 0x04 / 0x24 E. Rodriguez 0 + * Vivitar Mini 0x08 T. Kilgore 1 + * Elta-Media 8212dc 0x23 T. Kaiser 1 + * Philips dig. keych. 0x37 T. Kilgore 1 + */ + if ((data[0] & 0x78) == 8 || + ((data[0] & 0x2) == 0x2 && data[0] != 0x53)) + sd->sensor_type = 1; + else + sd->sensor_type = 0; + + PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d", + sd->sensor_type); + + if (force_sensor_type != -1) { + sd->sensor_type = !! force_sensor_type; + PDEBUG(D_PROBE, "Forcing sensor type to: %d", + sd->sensor_type); + } + + if (sd->sensor_type == 0) + gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); + } else { + sd->cam_type = CAM_TYPE_VGA; + PDEBUG(D_PROBE, "MR97310A VGA camera detected"); + gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) | + (1 << EXPOSURE_IDX) | (1 << GAIN_IDX); + } + + sd->brightness = MR97310A_BRIGHTNESS_DEFAULT; + sd->exposure = MR97310A_EXPOSURE_DEFAULT; + sd->gain = MR97310A_GAIN_DEFAULT; + return 0; } @@ -96,183 +435,462 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static int sd_start(struct gspca_dev *gspca_dev) +static int start_cif_cam(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u8 *data = gspca_dev->usb_buf; int err_code; - - sd->sof_read = 0; - - /* Note: register descriptions guessed from MR97113A driver */ - + const __u8 startup_string[] = { + 0x00, + 0x0d, + 0x01, + 0x00, /* Hsize/8 for 352 or 320 */ + 0x00, /* Vsize/4 for 288 or 240 */ + 0x13, /* or 0xbb, depends on sensor */ + 0x00, /* Hstart, depends on res. */ + 0x00, /* reserved ? */ + 0x00, /* Vstart, depends on res. and sensor */ + 0x50, /* 0x54 to get 176 or 160 */ + 0xc0 + }; + + /* Note: Some of the above descriptions guessed from MR97113A driver */ data[0] = 0x01; data[1] = 0x01; - err_code = reg_w(gspca_dev, 2); + err_code = mr_write(gspca_dev, 2); if (err_code < 0) return err_code; - data[0] = 0x00; - data[1] = 0x0d; - data[2] = 0x01; - data[5] = 0x2b; - data[7] = 0x00; - data[9] = 0x50; /* reg 8, no scale down */ - data[10] = 0xc0; + memcpy(data, startup_string, 11); + if (sd->sensor_type) + data[5] = 0xbb; switch (gspca_dev->width) { case 160: - data[9] |= 0x0c; /* reg 8, 4:1 scale down */ + data[9] |= 0x04; /* reg 8, 2:1 scale down from 320 */ /* fall thru */ case 320: - data[9] |= 0x04; /* reg 8, 2:1 scale down */ - /* fall thru */ - case 640: default: - data[3] = 0x50; /* reg 2, H size */ - data[4] = 0x78; /* reg 3, V size */ - data[6] = 0x04; /* reg 5, H start */ - data[8] = 0x03; /* reg 7, V start */ + data[3] = 0x28; /* reg 2, H size/8 */ + data[4] = 0x3c; /* reg 3, V size/4 */ + data[6] = 0x14; /* reg 5, H start */ + data[8] = 0x1a + sd->sensor_type; /* reg 7, V start */ break; - case 176: - data[9] |= 0x04; /* reg 8, 2:1 scale down */ + data[9] |= 0x04; /* reg 8, 2:1 scale down from 352 */ /* fall thru */ case 352: - data[3] = 0x2c; /* reg 2, H size */ - data[4] = 0x48; /* reg 3, V size */ - data[6] = 0x94; /* reg 5, H start */ - data[8] = 0x63; /* reg 7, V start */ + data[3] = 0x2c; /* reg 2, H size/8 */ + data[4] = 0x48; /* reg 3, V size/4 */ + data[6] = 0x06; /* reg 5, H start */ + data[8] = 0x06 + sd->sensor_type; /* reg 7, V start */ break; } - - err_code = reg_w(gspca_dev, 11); + err_code = mr_write(gspca_dev, 11); if (err_code < 0) return err_code; - data[0] = 0x0a; - data[1] = 0x80; - err_code = reg_w(gspca_dev, 2); + if (!sd->sensor_type) { + const struct sensor_w_data cif_sensor0_init_data[] = { + {0x02, 0x00, {0x03, 0x5a, 0xb5, 0x01, + 0x0f, 0x14, 0x0f, 0x10}, 8}, + {0x0c, 0x00, {0x04, 0x01, 0x01, 0x00, 0x1f}, 5}, + {0x12, 0x00, {0x07}, 1}, + {0x1f, 0x00, {0x06}, 1}, + {0x27, 0x00, {0x04}, 1}, + {0x29, 0x00, {0x0c}, 1}, + {0x40, 0x00, {0x40, 0x00, 0x04}, 3}, + {0x50, 0x00, {0x60}, 1}, + {0x60, 0x00, {0x06}, 1}, + {0x6b, 0x00, {0x85, 0x85, 0xc8, 0xc8, 0xc8, 0xc8}, 6}, + {0x72, 0x00, {0x1e, 0x56}, 2}, + {0x75, 0x00, {0x58, 0x40, 0xa2, 0x02, 0x31, 0x02, + 0x31, 0x80, 0x00}, 9}, + {0x11, 0x00, {0x01}, 1}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, cif_sensor0_init_data, + ARRAY_SIZE(cif_sensor0_init_data)); + } else { /* sd->sensor_type = 1 */ + const struct sensor_w_data cif_sensor1_init_data[] = { + /* Reg 3,4, 7,8 get set by the controls */ + {0x02, 0x00, {0x10}, 1}, + {0x05, 0x01, {0x22}, 1}, /* 5/6 also seen as 65h/32h */ + {0x06, 0x01, {0x00}, 1}, + {0x09, 0x02, {0x0e}, 1}, + {0x0a, 0x02, {0x05}, 1}, + {0x0b, 0x02, {0x05}, 1}, + {0x0c, 0x02, {0x0f}, 1}, + {0x0d, 0x02, {0x07}, 1}, + {0x0e, 0x02, {0x0c}, 1}, + {0x0f, 0x00, {0x00}, 1}, + {0x10, 0x00, {0x06}, 1}, + {0x11, 0x00, {0x07}, 1}, + {0x12, 0x00, {0x00}, 1}, + {0x13, 0x00, {0x01}, 1}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data, + ARRAY_SIZE(cif_sensor1_init_data)); + } if (err_code < 0) return err_code; - data[0] = 0x14; - data[1] = 0x0a; - err_code = reg_w(gspca_dev, 2); - if (err_code < 0) - return err_code; + setbrightness(gspca_dev); + setexposure(gspca_dev); + setgain(gspca_dev); - data[0] = 0x1b; - data[1] = 0x00; - err_code = reg_w(gspca_dev, 2); - if (err_code < 0) - return err_code; + msleep(200); - data[0] = 0x15; - data[1] = 0x16; - err_code = reg_w(gspca_dev, 2); + data[0] = 0x00; + data[1] = 0x4d; /* ISOC transfering enable... */ + err_code = mr_write(gspca_dev, 2); if (err_code < 0) return err_code; - data[0] = 0x16; - data[1] = 0x10; - err_code = reg_w(gspca_dev, 2); - if (err_code < 0) - return err_code; + return 0; +} - data[0] = 0x17; - data[1] = 0x3a; - err_code = reg_w(gspca_dev, 2); - if (err_code < 0) - return err_code; +static int start_vga_cam(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 *data = gspca_dev->usb_buf; + int err_code; + const __u8 startup_string[] = {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, + 0x00, 0x00, 0x00, 0x50, 0xc0}; - data[0] = 0x18; - data[1] = 0x68; - err_code = reg_w(gspca_dev, 2); - if (err_code < 0) - return err_code; + /* What some of these mean is explained in start_cif_cam(), above */ + sd->sof_read = 0; - data[0] = 0x1f; - data[1] = 0x00; - data[2] = 0x02; - data[3] = 0x06; - data[4] = 0x59; - data[5] = 0x0c; - data[6] = 0x16; - data[7] = 0x00; - data[8] = 0x07; - data[9] = 0x00; - data[10] = 0x01; - err_code = reg_w(gspca_dev, 11); + /* + * We have to know which camera we have, because the register writes + * depend upon the camera. This test, run before we actually enter + * the initialization routine, distinguishes most of the cameras, If + * needed, another routine is done later, too. + */ + memset(data, 0, 16); + data[0] = 0x20; + err_code = mr_write(gspca_dev, 1); if (err_code < 0) return err_code; - data[0] = 0x1f; - data[1] = 0x04; - data[2] = 0x11; - data[3] = 0x01; - err_code = reg_w(gspca_dev, 4); + err_code = mr_read(gspca_dev, 16); if (err_code < 0) return err_code; - data[0] = 0x1f; - data[1] = 0x00; - data[2] = 0x0a; - data[3] = 0x00; - data[4] = 0x01; - data[5] = 0x00; - data[6] = 0x00; - data[7] = 0x01; - data[8] = 0x00; - data[9] = 0x0a; - err_code = reg_w(gspca_dev, 10); - if (err_code < 0) - return err_code; + PDEBUG(D_PROBE, "Byte reported is %02x", data[0]); + + msleep(200); + /* + * Known VGA cameras. If you have another to report, please do + * + * Name byte just read sd->sensor_type + * sd->do_lcd_stop + * Aiptek Pencam VGA+ 0x31 0 1 + * ION digital 0x31 0 1 + * Argus DC-1620 0x30 1 0 + * Argus QuickClix 0x30 1 1 (not caught here) + */ + sd->sensor_type = data[0] & 1; + sd->do_lcd_stop = (~data[0]) & 1; + + - data[0] = 0x1f; - data[1] = 0x04; - data[2] = 0x11; - data[3] = 0x01; - err_code = reg_w(gspca_dev, 4); + /* Streaming setup begins here. */ + + + data[0] = 0x01; + data[1] = 0x01; + err_code = mr_write(gspca_dev, 2); if (err_code < 0) return err_code; - data[0] = 0x1f; - data[1] = 0x00; - data[2] = 0x12; - data[3] = 0x00; - data[4] = 0x63; - data[5] = 0x00; - data[6] = 0x70; - data[7] = 0x00; - data[8] = 0x00; - err_code = reg_w(gspca_dev, 9); + /* + * A second test can now resolve any remaining ambiguity in the + * identification of the camera type, + */ + if (!sd->sensor_type) { + data[0] = get_sensor_id(gspca_dev); + if (data[0] == 0x7f) { + sd->sensor_type = 1; + PDEBUG(D_PROBE, "sensor_type corrected to 1"); + } + msleep(200); + } + + if (force_sensor_type != -1) { + sd->sensor_type = !! force_sensor_type; + PDEBUG(D_PROBE, "Forcing sensor type to: %d", + sd->sensor_type); + } + + /* + * Known VGA cameras. + * This test is only run if the previous test returned 0x30, but + * here is the information for all others, too, just for reference. + * + * Name byte just read sd->sensor_type + * + * Aiptek Pencam VGA+ 0xfb (this test not run) 1 + * ION digital 0xbd (this test not run) 1 + * Argus DC-1620 0xe5 (no change) 0 + * Argus QuickClix 0x7f (reclassified) 1 + */ + memcpy(data, startup_string, 11); + if (!sd->sensor_type) { + data[5] = 0x00; + data[10] = 0x91; + } + + switch (gspca_dev->width) { + case 160: + data[9] |= 0x0c; /* reg 8, 4:1 scale down */ + /* fall thru */ + case 320: + data[9] |= 0x04; /* reg 8, 2:1 scale down */ + /* fall thru */ + case 640: + default: + data[3] = 0x50; /* reg 2, H size/8 */ + data[4] = 0x78; /* reg 3, V size/4 */ + data[6] = 0x04; /* reg 5, H start */ + data[8] = 0x03; /* reg 7, V start */ + if (sd->do_lcd_stop) + data[8] = 0x04; /* Bayer tile shifted */ + break; + + case 176: + data[9] |= 0x04; /* reg 8, 2:1 scale down */ + /* fall thru */ + case 352: + data[3] = 0x2c; /* reg 2, H size */ + data[4] = 0x48; /* reg 3, V size */ + data[6] = 0x94; /* reg 5, H start */ + data[8] = 0x63; /* reg 7, V start */ + if (sd->do_lcd_stop) + data[8] = 0x64; /* Bayer tile shifted */ + break; + } + + err_code = mr_write(gspca_dev, 11); if (err_code < 0) return err_code; - data[0] = 0x1f; - data[1] = 0x04; - data[2] = 0x11; - data[3] = 0x01; - err_code = reg_w(gspca_dev, 4); + if (!sd->sensor_type) { + /* The only known sensor_type 0 cam is the Argus DC-1620 */ + const struct sensor_w_data vga_sensor0_init_data[] = { + {0x01, 0x00, {0x0c, 0x00, 0x04}, 3}, + {0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4}, + {0x20, 0x00, {0x00, 0x80, 0x00, 0x08}, 4}, + {0x25, 0x00, {0x03, 0xa9, 0x80}, 3}, + {0x30, 0x00, {0x30, 0x18, 0x10, 0x18}, 4}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data, + ARRAY_SIZE(vga_sensor0_init_data)); + } else { /* sd->sensor_type = 1 */ + const struct sensor_w_data vga_sensor1_init_data[] = { + {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, + 0x07, 0x00, 0x01}, 8}, + {0x11, 0x04, {0x01}, 1}, + /*{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, */ + {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, + 0x00, 0x0a}, 7}, + {0x11, 0x04, {0x01}, 1}, + {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6}, + {0x11, 0x04, {0x01}, 1}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data, + ARRAY_SIZE(vga_sensor1_init_data)); + } if (err_code < 0) return err_code; + msleep(200); data[0] = 0x00; data[1] = 0x4d; /* ISOC transfering enable... */ - err_code = reg_w(gspca_dev, 2); + err_code = mr_write(gspca_dev, 2); + + return err_code; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err_code; + struct cam *cam; + + cam = &gspca_dev->cam; + sd->sof_read = 0; + /* + * Some of the supported cameras require the memory pointer to be + * set to 0, or else they will not stream. + */ + zero_the_pointer(gspca_dev); + msleep(200); + if (sd->cam_type == CAM_TYPE_CIF) { + err_code = start_cif_cam(gspca_dev); + } else { + err_code = start_vga_cam(gspca_dev); + } return err_code; } static void sd_stopN(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int result; gspca_dev->usb_buf[0] = 1; gspca_dev->usb_buf[1] = 0; - result = reg_w(gspca_dev, 2); + result = mr_write(gspca_dev, 2); if (result < 0) PDEBUG(D_ERR, "Camera Stop failed"); + + /* Not all the cams need this, but even if not, probably a good idea */ + zero_the_pointer(gspca_dev); + if (sd->do_lcd_stop) { + gspca_dev->usb_buf[0] = 0x19; + gspca_dev->usb_buf[1] = 0x54; + result = mr_write(gspca_dev, 2); + if (result < 0) + PDEBUG(D_ERR, "Camera Stop failed"); + } +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX)) + return; + + /* Note register 7 is also seen as 0x8x or 0xCx in dumps */ + if (sd->brightness > 0) { + sensor_write1(gspca_dev, 7, 0x00); + val = sd->brightness; + } else { + sensor_write1(gspca_dev, 7, 0x01); + val = 257 - sd->brightness; + } + sensor_write1(gspca_dev, 8, val); +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX)) + return; + + if (sd->sensor_type) { + val = sd->exposure >> 4; + sensor_write1(gspca_dev, 3, val); + val = sd->exposure & 0xf; + sensor_write1(gspca_dev, 4, val); + } else { + u8 clockdiv; + int exposure; + + /* We have both a clock divider and an exposure register. + We first calculate the clock divider, as that determines + the maximum exposure and then we calculayte the exposure + register setting (which goes from 0 - 511). + + Note our 0 - 4095 exposure is mapped to 0 - 511 + milliseconds exposure time */ + clockdiv = (60 * sd->exposure + 7999) / 8000; + + /* Limit framerate to not exceed usb bandwidth */ + if (clockdiv < 3 && gspca_dev->width >= 320) + clockdiv = 3; + else if (clockdiv < 2) + clockdiv = 2; + + /* Frame exposure time in ms = 1000 * clockdiv / 60 -> + exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */ + exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv); + if (exposure > 511) + exposure = 511; + + /* exposure register value is reversed! */ + exposure = 511 - exposure; + + sensor_write1(gspca_dev, 0x02, clockdiv); + sensor_write1(gspca_dev, 0x0e, exposure & 0xff); + sensor_write1(gspca_dev, 0x0f, exposure >> 8); + } +} + +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << GAIN_IDX)) + return; + + if (sd->sensor_type) { + sensor_write1(gspca_dev, 0x0e, sd->gain); + } else { + sensor_write1(gspca_dev, 0x10, sd->gain); + } +} + +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); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + 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_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; } /* Include pac common sof detection functions */ @@ -320,8 +938,9 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x08ca, 0x0111)}, - {USB_DEVICE(0x093a, 0x010f)}, + {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */ + {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */ + {USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */ {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/linux/drivers/media/video/gspca/pac207.c b/linux/drivers/media/video/gspca/pac207.c index 95a97ab68..96659433d 100644 --- a/linux/drivers/media/video/gspca/pac207.c +++ b/linux/drivers/media/video/gspca/pac207.c @@ -35,25 +35,17 @@ MODULE_LICENSE("GPL"); #define PAC207_BRIGHTNESS_MIN 0 #define PAC207_BRIGHTNESS_MAX 255 -#define PAC207_BRIGHTNESS_DEFAULT 4 /* power on default: 4 */ - -/* An exposure value of 4 also works (3 does not) but then we need to lower - the compression balance setting when in 352x288 mode, otherwise the usb - bandwidth is not enough and packets get dropped resulting in corrupt - frames. The problem with this is that when the compression balance gets - lowered below 0x80, the pac207 starts using a different compression - algorithm for some lines, these lines get prefixed with a 0x2dd2 prefix - and currently we do not know how to decompress these lines, so for now - we use a minimum exposure value of 5 */ -#define PAC207_EXPOSURE_MIN 5 +#define PAC207_BRIGHTNESS_DEFAULT 46 + +#define PAC207_EXPOSURE_MIN 3 #define PAC207_EXPOSURE_MAX 26 -#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 ?? */ -#define PAC207_EXPOSURE_KNEE 11 /* 4 = 30 fps, 11 = 8, 15 = 6 */ +#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */ +#define PAC207_EXPOSURE_KNEE 8 /* 4 = 30 fps, 11 = 8, 15 = 6 */ #define PAC207_GAIN_MIN 0 #define PAC207_GAIN_MAX 31 #define PAC207_GAIN_DEFAULT 9 /* power on default: 9 */ -#define PAC207_GAIN_KNEE 20 +#define PAC207_GAIN_KNEE 31 #define PAC207_AUTOGAIN_DEADZONE 30 @@ -166,16 +158,12 @@ static const struct v4l2_pix_format sif_mode[] = { }; static const __u8 pac207_sensor_init[][8] = { - {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0}, - {0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30}, + {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0x84}, + {0x49, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30}, {0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00}, - {0x00, 0x00, 0x32, 0x00, 0x96, 0x00, 0xa2, 0x02}, {0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xaf, 0x00}, }; - /* 48 reg_72 Rate Control end BalSize_4a =0x36 */ -static const __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 }; - static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, const u8 *buffer, u16 length) { @@ -274,7 +262,6 @@ static int sd_init(struct gspca_dev *gspca_dev) * 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 */ return 0; } @@ -289,15 +276,13 @@ static int sd_start(struct gspca_dev *gspca_dev) pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8); pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8); pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8); - pac207_write_regs(gspca_dev, 0x0040, pac207_sensor_init[3], 8); - pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[4], 8); - pac207_write_regs(gspca_dev, 0x0048, PacReg72, 4); + pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[3], 8); /* Compression Balance */ if (gspca_dev->width == 176) pac207_write_reg(gspca_dev, 0x4a, 0xff); else - pac207_write_reg(gspca_dev, 0x4a, 0x88); + pac207_write_reg(gspca_dev, 0x4a, 0x30); pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */ pac207_write_reg(gspca_dev, 0x08, sd->brightness); @@ -346,7 +331,7 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, - 100 + sd->brightness / 2, PAC207_AUTOGAIN_DEADZONE, + 100, PAC207_AUTOGAIN_DEADZONE, PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE)) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } diff --git a/linux/drivers/media/video/gspca/pac7311.c b/linux/drivers/media/video/gspca/pac7311.c index ed10134c9..356111956 100644 --- a/linux/drivers/media/video/gspca/pac7311.c +++ b/linux/drivers/media/video/gspca/pac7311.c @@ -1071,6 +1071,7 @@ static struct sd_desc sd_desc = { /* -- module initialisation -- */ static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x06f8, 0x3009), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2600), .driver_info = SENSOR_PAC7311}, {USB_DEVICE(0x093a, 0x2601), .driver_info = SENSOR_PAC7311}, {USB_DEVICE(0x093a, 0x2603), .driver_info = SENSOR_PAC7311}, diff --git a/linux/drivers/media/video/gspca/sn9c20x.c b/linux/drivers/media/video/gspca/sn9c20x.c index fc47f978e..99632a7d6 100644 --- a/linux/drivers/media/video/gspca/sn9c20x.c +++ b/linux/drivers/media/video/gspca/sn9c20x.c @@ -97,6 +97,16 @@ struct sd { #endif }; +struct i2c_reg_u8 { + u8 reg; + u8 val; +}; + +struct i2c_reg_u16 { + u8 reg; + u16 val; +}; + 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); @@ -406,7 +416,7 @@ static const struct v4l2_pix_format sxga_mode[] = { .priv = 3 | MODE_RAW | MODE_SXGA}, }; -static const int hsv_red_x[] = { +static const s16 hsv_red_x[] = { 41, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 81, 83, 85, 87, @@ -454,7 +464,7 @@ static const int hsv_red_x[] = { 24, 26, 28, 30, 33, 35, 37, 39, 41 }; -static const int hsv_red_y[] = { +static const s16 hsv_red_y[] = { 82, 80, 78, 76, 74, 73, 71, 69, 67, 65, 63, 61, 58, 56, 54, 52, 50, 48, 46, 44, 41, 39, 37, 35, @@ -502,7 +512,7 @@ static const int hsv_red_y[] = { 96, 94, 92, 91, 89, 87, 85, 84, 82 }; -static const int hsv_green_x[] = { +static const s16 hsv_green_x[] = { -124, -124, -125, -125, -125, -125, -125, -125, -125, -126, -126, -125, -125, -125, -125, -125, -125, -124, -124, -124, -123, -123, -122, -122, @@ -550,7 +560,7 @@ static const int hsv_green_x[] = { -120, -120, -121, -122, -122, -123, -123, -124, -124 }; -static const int hsv_green_y[] = { +static const s16 hsv_green_y[] = { -100, -99, -98, -97, -95, -94, -93, -91, -90, -89, -87, -86, -84, -83, -81, -80, -78, -76, -75, -73, -71, -70, -68, -66, @@ -598,7 +608,7 @@ static const int hsv_green_y[] = { -109, -108, -107, -106, -105, -104, -103, -102, -100 }; -static const int hsv_blue_x[] = { +static const s16 hsv_blue_x[] = { 112, 113, 114, 114, 115, 116, 117, 117, 118, 118, 119, 119, 120, 120, 120, 121, 121, 121, 122, 122, 122, 122, 122, 122, @@ -646,7 +656,7 @@ static const int hsv_blue_x[] = { 104, 105, 106, 107, 108, 109, 110, 111, 112 }; -static const int hsv_blue_y[] = { +static const s16 hsv_blue_y[] = { -11, -13, -15, -17, -19, -21, -23, -25, -27, -29, -31, -33, -35, -37, -39, -41, -43, -45, -46, -48, -50, -52, -54, -55, @@ -795,21 +805,21 @@ static u8 hv7131r_gain[] = { 0x78 /* 8x */ }; -static u8 soi968_init[][2] = { +static struct i2c_reg_u8 soi968_init[] = { {0x12, 0x80}, {0x0c, 0x00}, {0x0f, 0x1f}, {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, {0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff}, {0x3e, 0x00}, {0x3f, 0x00}, {0x3b, 0x20}, {0x3a, 0x96}, {0x3d, 0x0a}, {0x14, 0x8e}, - {0x13, 0x8a}, {0x12, 0x40}, {0x17, 0x13}, + {0x13, 0x8b}, {0x12, 0x40}, {0x17, 0x13}, {0x18, 0x63}, {0x19, 0x01}, {0x1a, 0x79}, {0x32, 0x24}, {0x03, 0x00}, {0x11, 0x40}, {0x2a, 0x10}, {0x2b, 0xe0}, {0x10, 0x32}, {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80}, }; -static u8 ov7660_init[][2] = { +static struct i2c_reg_u8 ov7660_init[] = { {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3}, {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40}, {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a}, @@ -818,7 +828,7 @@ static u8 ov7660_init[][2] = { {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50}, }; -static u8 ov7670_init[][2] = { +static struct i2c_reg_u8 ov7670_init[] = { {0x12, 0x80}, {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, @@ -875,7 +885,7 @@ static u8 ov7670_init[][2] = { {0x93, 0x00}, }; -static u8 ov9650_init[][2] = { +static struct i2c_reg_u8 ov9650_init[] = { {0x12, 0x80}, {0x00, 0x00}, {0x01, 0x78}, {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, @@ -905,7 +915,7 @@ static u8 ov9650_init[][2] = { {0xaa, 0x92}, {0xab, 0x0a}, }; -static u8 ov9655_init[][2] = { +static struct i2c_reg_u8 ov9655_init[] = { {0x12, 0x80}, {0x12, 0x01}, {0x0d, 0x00}, {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x1e, 0x04}, {0x1e, 0x04}, {0x27, 0x08}, @@ -942,7 +952,7 @@ static u8 ov9655_init[][2] = { {0x00, 0x03}, {0x00, 0x0a}, {0x00, 0x10}, {0x00, 0x13}, }; -static u16 mt9v112_init[][2] = { +static struct i2c_reg_u16 mt9v112_init[] = { {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020}, {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b}, {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001}, @@ -961,7 +971,7 @@ static u16 mt9v112_init[][2] = { {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae}, }; -static u16 mt9v111_init[][2] = { +static struct i2c_reg_u16 mt9v111_init[] = { {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x0001}, {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0004}, {0x07, 0x3002}, @@ -988,7 +998,7 @@ static u16 mt9v111_init[][2] = { {0x0e, 0x0008}, {0x06, 0x002d}, {0x05, 0x0004}, }; -static u16 mt9v011_init[][2] = { +static struct i2c_reg_u16 mt9v011_init[] = { {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006}, @@ -1015,7 +1025,7 @@ static u16 mt9v011_init[][2] = { {0x06, 0x0029}, {0x05, 0x0009}, }; -static u16 mt9m001_init[][2] = { +static struct i2c_reg_u16 mt9m001_init[] = { {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x000e}, {0x02, 0x0014}, {0x03, 0x03c1}, {0x04, 0x0501}, {0x05, 0x0083}, {0x06, 0x0006}, {0x0d, 0x0002}, @@ -1028,14 +1038,14 @@ static u16 mt9m001_init[][2] = { {0x2e, 0x0029}, {0x07, 0x0002}, }; -static u16 mt9m111_init[][2] = { - {0xf0, 0x0000}, {0x0d, 0x0008}, {0x0d, 0x0009}, - {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300}, - {0x9b, 0x4300}, {0xa1, 0x0280}, {0xa4, 0x0200}, - {0x06, 0x308e}, {0xf0, 0x0000}, +static struct i2c_reg_u16 mt9m111_init[] = { + {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, + {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, + {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, + {0xf0, 0x0000}, }; -static u8 hv7131r_init[][2] = { +static struct i2c_reg_u8 hv7131r_init[] = { {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08}, @@ -1046,7 +1056,7 @@ static u8 hv7131r_init[][2] = { {0x23, 0x09}, {0x01, 0x08}, }; -int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) +static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) { struct usb_device *dev = gspca_dev->dev; int result; @@ -1065,7 +1075,8 @@ int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length) return 0; } -int reg_w(struct gspca_dev *gspca_dev, u16 reg, const u8 *buffer, int length) +static int reg_w(struct gspca_dev *gspca_dev, u16 reg, + const u8 *buffer, int length) { struct usb_device *dev = gspca_dev->dev; int result; @@ -1085,13 +1096,13 @@ int reg_w(struct gspca_dev *gspca_dev, u16 reg, const u8 *buffer, int length) return 0; } -int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) +static int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value) { u8 data[1] = {value}; return reg_w(gspca_dev, reg, data, 1); } -int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) +static int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) { int i; reg_w(gspca_dev, 0x10c0, buffer, 8); @@ -1107,7 +1118,7 @@ int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) return -EIO; } -int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) +static int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1129,7 +1140,7 @@ int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) return i2c_w(gspca_dev, row); } -int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) +static int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) { struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; @@ -1206,8 +1217,8 @@ static int ov9650_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) { - if (i2c_w1(gspca_dev, ov9650_init[i][0], - ov9650_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, ov9650_init[i].reg, + ov9650_init[i].val) < 0) { err("OV9650 sensor initialization failed"); return -ENODEV; } @@ -1223,8 +1234,8 @@ static int ov9655_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) { - if (i2c_w1(gspca_dev, ov9655_init[i][0], - ov9655_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, ov9655_init[i].reg, + ov9655_init[i].val) < 0) { err("OV9655 sensor initialization failed"); return -ENODEV; } @@ -1242,14 +1253,14 @@ static int soi968_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(soi968_init); i++) { - if (i2c_w1(gspca_dev, soi968_init[i][0], - soi968_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, soi968_init[i].reg, + soi968_init[i].val) < 0) { err("SOI968 sensor initialization failed"); return -ENODEV; } } /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX); + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << EXPOSURE_IDX); sd->hstart = 60; sd->vstart = 11; return 0; @@ -1261,8 +1272,8 @@ static int ov7660_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) { - if (i2c_w1(gspca_dev, ov7660_init[i][0], - ov7660_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, ov7660_init[i].reg, + ov7660_init[i].val) < 0) { err("OV7660 sensor initialization failed"); return -ENODEV; } @@ -1280,8 +1291,8 @@ static int ov7670_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) { - if (i2c_w1(gspca_dev, ov7670_init[i][0], - ov7670_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, ov7670_init[i].reg, + ov7670_init[i].val) < 0) { err("OV7670 sensor initialization failed"); return -ENODEV; } @@ -1304,8 +1315,8 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) ret = i2c_r2(gspca_dev, 0xff, &value); if ((ret == 0) && (value == 0x8243)) { for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) { - if (i2c_w2(gspca_dev, mt9v011_init[i][0], - mt9v011_init[i][1]) < 0) { + if (i2c_w2(gspca_dev, mt9v011_init[i].reg, + mt9v011_init[i].val) < 0) { err("MT9V011 sensor initialization failed"); return -ENODEV; } @@ -1322,8 +1333,8 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) ret = i2c_r2(gspca_dev, 0xff, &value); if ((ret == 0) && (value == 0x823a)) { for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) { - if (i2c_w2(gspca_dev, mt9v111_init[i][0], - mt9v111_init[i][1]) < 0) { + if (i2c_w2(gspca_dev, mt9v111_init[i].reg, + mt9v111_init[i].val) < 0) { err("MT9V111 sensor initialization failed"); return -ENODEV; } @@ -1344,8 +1355,8 @@ static int mt9v_init_sensor(struct gspca_dev *gspca_dev) ret = i2c_r2(gspca_dev, 0x00, &value); if ((ret == 0) && (value == 0x1229)) { for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) { - if (i2c_w2(gspca_dev, mt9v112_init[i][0], - mt9v112_init[i][1]) < 0) { + if (i2c_w2(gspca_dev, mt9v112_init[i].reg, + mt9v112_init[i].val) < 0) { err("MT9V112 sensor initialization failed"); return -ENODEV; } @@ -1365,12 +1376,13 @@ static int mt9m111_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int i; for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) { - if (i2c_w2(gspca_dev, mt9m111_init[i][0], - mt9m111_init[i][1]) < 0) { + if (i2c_w2(gspca_dev, mt9m111_init[i].reg, + mt9m111_init[i].val) < 0) { err("MT9M111 sensor initialization failed"); return -ENODEV; } } + gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX) | (1 << GAIN_IDX); sd->hstart = 0; sd->vstart = 2; return 0; @@ -1381,8 +1393,8 @@ static int mt9m001_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int i; for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) { - if (i2c_w2(gspca_dev, mt9m001_init[i][0], - mt9m001_init[i][1]) < 0) { + if (i2c_w2(gspca_dev, mt9m001_init[i].reg, + mt9m001_init[i].val) < 0) { err("MT9M001 sensor initialization failed"); return -ENODEV; } @@ -1400,8 +1412,8 @@ static int hv7131r_init_sensor(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) { - if (i2c_w1(gspca_dev, hv7131r_init[i][0], - hv7131r_init[i][1]) < 0) { + if (i2c_w1(gspca_dev, hv7131r_init[i].reg, + hv7131r_init[i].val) < 0) { err("HV7131R Sensor initialization failed"); return -ENODEV; } @@ -1625,7 +1637,6 @@ static int set_exposure(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: - case SENSOR_SOI968: case SENSOR_OV9655: case SENSOR_OV9650: exp[0] |= (3 << 4); @@ -1634,7 +1645,6 @@ static int set_exposure(struct gspca_dev *gspca_dev) exp[4] = sd->exposure >> 8; break; case SENSOR_MT9M001: - case SENSOR_MT9M111: case SENSOR_MT9V112: case SENSOR_MT9V111: case SENSOR_MT9V011: @@ -1650,6 +1660,8 @@ static int set_exposure(struct gspca_dev *gspca_dev) exp[4] = ((sd->exposure * 0xffffff) / 0xffff) >> 8; exp[5] = ((sd->exposure * 0xffffff) / 0xffff) & 0xff; break; + default: + return 0; } i2c_w(gspca_dev, exp); return 0; @@ -1676,7 +1688,6 @@ static int set_gain(struct gspca_dev *gspca_dev) gain[4] = micron1_gain[sd->gain] & 0xff; break; case SENSOR_MT9V112: - case SENSOR_MT9M111: gain[0] |= (3 << 4); gain[2] = 0x2f; gain[3] = micron1_gain[sd->gain] >> 8; @@ -1693,6 +1704,8 @@ static int set_gain(struct gspca_dev *gspca_dev) gain[2] = 0x30; gain[3] = hv7131r_gain[sd->gain]; break; + default: + return 0; } i2c_w(gspca_dev, gain); return 0; @@ -1995,7 +2008,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->i2c_addr = id->driver_info & 0xff; switch (sd->sensor) { + case SENSOR_MT9M111: case SENSOR_OV9650: + case SENSOR_SOI968: cam->cam_mode = sxga_mode; cam->nmodes = ARRAY_SIZE(sxga_mode); break; @@ -2111,6 +2126,25 @@ static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) struct sd *sd = (struct sd *) gspca_dev; u8 value; switch (sd->sensor) { + case SENSOR_SOI968: + if (mode & MODE_SXGA) { + i2c_w1(gspca_dev, 0x17, 0x1d); + i2c_w1(gspca_dev, 0x18, 0xbd); + i2c_w1(gspca_dev, 0x19, 0x01); + i2c_w1(gspca_dev, 0x1a, 0x81); + i2c_w1(gspca_dev, 0x12, 0x00); + sd->hstart = 140; + sd->vstart = 19; + } else { + i2c_w1(gspca_dev, 0x17, 0x13); + i2c_w1(gspca_dev, 0x18, 0x63); + i2c_w1(gspca_dev, 0x19, 0x01); + i2c_w1(gspca_dev, 0x1a, 0x79); + i2c_w1(gspca_dev, 0x12, 0x40); + sd->hstart = 60; + sd->vstart = 11; + } + break; case SENSOR_OV9650: if (mode & MODE_SXGA) { i2c_w1(gspca_dev, 0x17, 0x1b); @@ -2128,6 +2162,17 @@ static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode) i2c_w1(gspca_dev, 0x12, (value & 0x7) | 0x40); } break; + case SENSOR_MT9M111: + if (mode & MODE_SXGA) { + i2c_w2(gspca_dev, 0xf0, 0x0002); + i2c_w2(gspca_dev, 0xc8, 0x970b); + i2c_w2(gspca_dev, 0xf0, 0x0000); + } else { + i2c_w2(gspca_dev, 0xf0, 0x0002); + i2c_w2(gspca_dev, 0xc8, 0x8000); + i2c_w2(gspca_dev, 0xf0, 0x0000); + } + break; } } @@ -2216,15 +2261,10 @@ static void sd_stop0(struct gspca_dev *gspca_dev) kfree(sd->jpeg_hdr); } -static void do_autoexposure(struct gspca_dev *gspca_dev) +static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) { struct sd *sd = (struct sd *) gspca_dev; - int avg_lum, new_exp; - - if (!sd->auto_exposure) - return; - - avg_lum = atomic_read(&sd->avg_lum); + s16 new_exp; /* * some hardcoded values are present @@ -2271,6 +2311,39 @@ static void do_autoexposure(struct gspca_dev *gspca_dev) } } +static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (avg_lum < MIN_AVG_LUM) { + if (sd->gain + 1 <= 28) { + sd->gain++; + set_gain(gspca_dev); + } + } + if (avg_lum > MAX_AVG_LUM) { + if (sd->gain - 1 >= 0) { + sd->gain--; + set_gain(gspca_dev); + } + } +} + +static void sd_dqcallback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum; + + if (!sd->auto_exposure) + return; + + avg_lum = atomic_read(&sd->avg_lum); + if (sd->sensor == SENSOR_SOI968) + do_autogain(gspca_dev, avg_lum); + else + do_autoexposure(gspca_dev, avg_lum); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ @@ -2338,7 +2411,7 @@ static const struct sd_desc sd_desc = { .stopN = sd_stopN, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, - .dq_callback = do_autoexposure, + .dq_callback = sd_dqcallback, #ifdef CONFIG_VIDEO_ADV_DEBUG .set_register = sd_dbg_s_register, .get_register = sd_dbg_g_register, diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 6a668deb1..3746ddbbe 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1177,7 +1177,7 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x17, 0x20); reg_w1(gspca_dev, 0x01, 0x62); reg_w1(gspca_dev, 0x01, 0x42); - mdelay(100); + msleep(100); reg_w1(gspca_dev, 0x02, 0x62); break; /* case SENSOR_HV7131R: */ @@ -2385,8 +2385,8 @@ static const __devinitdata struct usb_device_id device_table[] = { /* {USB_DEVICE(0x0c45, 0x60ef), BSI(SN9C105, ICM105C, 0x??)}, */ /* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */ {USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)}, {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)}, #endif {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/ diff --git a/linux/drivers/media/video/gspca/sunplus.c b/linux/drivers/media/video/gspca/sunplus.c index 33bac051d..32ff7cd32 100644 --- a/linux/drivers/media/video/gspca/sunplus.c +++ b/linux/drivers/media/video/gspca/sunplus.c @@ -32,22 +32,22 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; - unsigned char contrast; - unsigned char colors; - unsigned char autogain; + s8 brightness; + u8 contrast; + u8 colors; + u8 autogain; u8 quality; #define QUALITY_MIN 70 #define QUALITY_MAX 95 #define QUALITY_DEF 85 - char bridge; + u8 bridge; #define BRIDGE_SPCA504 0 #define BRIDGE_SPCA504B 1 #define BRIDGE_SPCA504C 2 #define BRIDGE_SPCA533 3 #define BRIDGE_SPCA536 4 - char subtype; + u8 subtype; #define AiptekMiniPenCam13 1 #define LogitechClickSmart420 2 #define LogitechClickSmart820 3 @@ -68,21 +68,20 @@ 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 = 0xff, + .minimum = -128, + .maximum = 127, .step = 1, - .default_value = 0, +#define BRIGHTNESS_DEF 0 + .default_value = BRIGHTNESS_DEF, }, .set = sd_setbrightness, .get = sd_getbrightness, }, -#define SD_CONTRAST 1 { { .id = V4L2_CID_CONTRAST, @@ -91,12 +90,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, - .default_value = 0x20, +#define CONTRAST_DEF 0x20 + .default_value = CONTRAST_DEF, }, .set = sd_setcontrast, .get = sd_getcontrast, }, -#define SD_COLOR 2 { { .id = V4L2_CID_SATURATION, @@ -105,12 +104,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0xff, .step = 1, - .default_value = 0x1a, +#define COLOR_DEF 0x1a + .default_value = COLOR_DEF, }, .set = sd_setcolors, .get = sd_getcolors, }, -#define SD_AUTOGAIN 3 { { .id = V4L2_CID_AUTOGAIN, @@ -119,7 +118,8 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, - .default_value = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, }, .set = sd_setautogain, .get = sd_getautogain, @@ -181,14 +181,20 @@ static const struct v4l2_pix_format vga_mode2[] = { #define SPCA504_PCCAM600_OFFSET_MODE 5 #define SPCA504_PCCAM600_OFFSET_DATA 14 /* Frame packet header offsets for the spca533 */ -#define SPCA533_OFFSET_DATA 16 +#define SPCA533_OFFSET_DATA 16 #define SPCA533_OFFSET_FRAMSEQ 15 /* Frame packet header offsets for the spca536 */ -#define SPCA536_OFFSET_DATA 4 -#define SPCA536_OFFSET_FRAMSEQ 1 +#define SPCA536_OFFSET_DATA 4 +#define SPCA536_OFFSET_FRAMSEQ 1 + +struct cmd { + u8 req; + u16 val; + u16 idx; +}; /* Initialisation data for the Creative PC-CAM 600 */ -static const __u16 spca504_pccam600_init_data[][3] = { +static const struct cmd spca504_pccam600_init_data[] = { /* {0xa0, 0x0000, 0x0503}, * capture mode */ {0x00, 0x0000, 0x2000}, {0x00, 0x0013, 0x2301}, @@ -219,22 +225,20 @@ static const __u16 spca504_pccam600_init_data[][3] = { {0x00, 0x0003, 0x2000}, {0x00, 0x0013, 0x2301}, {0x00, 0x0003, 0x2000}, - {} }; /* Creative PC-CAM 600 specific open data, sent before using the * generic initialisation data from spca504_open_data. */ -static const __u16 spca504_pccam600_open_data[][3] = { +static const struct cmd spca504_pccam600_open_data[] = { {0x00, 0x0001, 0x2501}, {0x20, 0x0500, 0x0001}, /* snapshot mode */ {0x00, 0x0003, 0x2880}, {0x00, 0x0001, 0x2881}, - {} }; /* Initialisation data for the logitech clicksmart 420 */ -static const __u16 spca504A_clicksmart420_init_data[][3] = { +static const struct cmd spca504A_clicksmart420_init_data[] = { /* {0xa0, 0x0000, 0x0503}, * capture mode */ {0x00, 0x0000, 0x2000}, {0x00, 0x0013, 0x2301}, @@ -261,7 +265,7 @@ static const __u16 spca504A_clicksmart420_init_data[][3] = { #endif #if 1 - {0x0a1, 0x0080, 0x0001}, + {0xa1, 0x0080, 0x0001}, {0x30, 0x0049, 0x0000}, {0x30, 0x0060, 0x0005}, {0x0c, 0x0004, 0x0000}, @@ -285,11 +289,10 @@ static const __u16 spca504A_clicksmart420_init_data[][3] = { {0x00, 0x0013, 0x2301}, {0x00, 0x0003, 0x2000}, #endif - {} }; /* clicksmart 420 open data ? */ -static const __u16 spca504A_clicksmart420_open_data[][3] = { +static const struct cmd spca504A_clicksmart420_open_data[] = { {0x00, 0x0001, 0x2501}, {0x20, 0x0502, 0x0000}, {0x06, 0x0000, 0x0000}, @@ -433,10 +436,9 @@ static const __u16 spca504A_clicksmart420_open_data[][3] = { {0x00, 0x0028, 0x287f}, {0xa0, 0x0000, 0x0503}, - {} }; -static const __u8 qtable_creative_pccam[2][64] = { +static const u8 qtable_creative_pccam[2][64] = { { /* Q-table Y-components */ 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, @@ -461,7 +463,7 @@ static const __u8 qtable_creative_pccam[2][64] = { * except for one byte. Possibly a typo? * NWG: 18/05/2003. */ -static const __u8 qtable_spca504_default[2][64] = { +static const u8 qtable_spca504_default[2][64] = { { /* Q-table Y-components */ 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, @@ -485,9 +487,9 @@ static const __u8 qtable_spca504_default[2][64] = { /* read <len> bytes to gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, - __u16 req, - __u16 index, - __u16 len) + u8 req, + u16 index, + u16 len) { #ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { @@ -505,31 +507,26 @@ static void reg_r(struct gspca_dev *gspca_dev, 500); } -/* 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) +/* write one byte */ +static void reg_w_1(struct gspca_dev *gspca_dev, + u8 req, + u16 value, + u16 index, + u16 byte) { -#ifdef GSPCA_DEBUG - if (len > USB_BUF_SZ) { - err("reg_w: buffer overflow"); - return; - } -#endif + gspca_dev->usb_buf[0] = byte; usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, - len ? gspca_dev->usb_buf : NULL, len, + gspca_dev->usb_buf, 1, 500); } /* write req / index / value */ static int reg_w_riv(struct usb_device *dev, - __u16 req, __u16 index, __u16 value) + u8 req, u16 index, u16 value) { int ret; @@ -547,7 +544,7 @@ static int reg_w_riv(struct usb_device *dev, /* read 1 byte */ static int reg_r_1(struct gspca_dev *gspca_dev, - __u16 value) /* wValue */ + u16 value) /* wValue */ { int ret; @@ -568,9 +565,9 @@ static int reg_r_1(struct gspca_dev *gspca_dev, /* read 1 or 2 bytes - returns < 0 if error */ static int reg_r_12(struct gspca_dev *gspca_dev, - __u16 req, /* bRequest */ - __u16 index, /* wIndex */ - __u16 length) /* wLength (1 or 2 only) */ + u8 req, /* bRequest */ + u16 index, /* wIndex */ + u16 length) /* wLength (1 or 2 only) */ { int ret; @@ -591,43 +588,40 @@ static int reg_r_12(struct gspca_dev *gspca_dev, } static int write_vector(struct gspca_dev *gspca_dev, - const __u16 data[][3]) + const struct cmd *data, int ncmds) { struct usb_device *dev = gspca_dev->dev; - int ret, i = 0; + int ret; - while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { - ret = reg_w_riv(dev, data[i][0], data[i][2], data[i][1]); + while (--ncmds >= 0) { + ret = reg_w_riv(dev, data->req, data->idx, data->val); if (ret < 0) { PDEBUG(D_ERR, - "Register write failed for 0x%x,0x%x,0x%x", - data[i][0], data[i][1], data[i][2]); + "Register write failed for 0x%02x, 0x%04x, 0x%04x", + data->req, data->val, data->idx); return ret; } - i++; + data++; } return 0; } static int spca50x_setup_qtable(struct gspca_dev *gspca_dev, - unsigned int request, - unsigned int ybase, - unsigned int cbase, - const __u8 qtable[2][64]) + const u8 qtable[2][64]) { struct usb_device *dev = gspca_dev->dev; int i, err; /* loop over y components */ for (i = 0; i < 64; i++) { - err = reg_w_riv(dev, request, ybase + i, qtable[0][i]); + err = reg_w_riv(dev, 0x00, 0x2800 + i, qtable[0][i]); if (err < 0) return err; } /* loop over c components */ for (i = 0; i < 64; i++) { - err = reg_w_riv(dev, request, cbase + i, qtable[1][i]); + err = reg_w_riv(dev, 0x00, 0x2840 + i, qtable[1][i]); if (err < 0) return err; } @@ -635,34 +629,34 @@ static int spca50x_setup_qtable(struct gspca_dev *gspca_dev, } static void spca504_acknowledged_command(struct gspca_dev *gspca_dev, - __u16 req, __u16 idx, __u16 val) + u8 req, u16 idx, u16 val) { struct usb_device *dev = gspca_dev->dev; - __u8 notdone; + int notdone; reg_w_riv(dev, req, idx, val); notdone = reg_r_12(gspca_dev, 0x01, 0x0001, 1); reg_w_riv(dev, req, idx, val); - PDEBUG(D_FRAM, "before wait 0x%x", notdone); + PDEBUG(D_FRAM, "before wait 0x%04x", notdone); msleep(200); notdone = reg_r_12(gspca_dev, 0x01, 0x0001, 1); - PDEBUG(D_FRAM, "after wait 0x%x", notdone); + PDEBUG(D_FRAM, "after wait 0x%04x", notdone); } static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, - __u16 req, - __u16 idx, __u16 val, __u8 stat, __u8 count) + u8 req, + u16 idx, u16 val, u8 stat, u8 count) { struct usb_device *dev = gspca_dev->dev; - __u8 status; - __u8 endcode; + int status; + u8 endcode; reg_w_riv(dev, req, idx, val); status = reg_r_12(gspca_dev, 0x01, 0x0001, 1); endcode = stat; - PDEBUG(D_FRAM, "Status 0x%x Need 0x%x", status, stat); + PDEBUG(D_FRAM, "Status 0x%x Need 0x%04x", status, stat); if (!count) return; count = 200; @@ -672,7 +666,7 @@ static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, /* reg_w_riv(dev, req, idx, val); */ status = reg_r_12(gspca_dev, 0x01, 0x0001, 1); if (status == endcode) { - PDEBUG(D_FRAM, "status 0x%x after wait 0x%x", + PDEBUG(D_FRAM, "status 0x%04x after wait %d", status, 200 - count); break; } @@ -699,8 +693,7 @@ static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) while (--count > 0) { reg_r(gspca_dev, 0x21, 1, 1); if (gspca_dev->usb_buf[0] != 0) { - gspca_dev->usb_buf[0] = 0; - reg_w(gspca_dev, 0x21, 0, 1, 1); + reg_w_1(gspca_dev, 0x21, 0, 1, 0); reg_r(gspca_dev, 0x21, 1, 1); spca504B_PollingDataReady(gspca_dev); break; @@ -711,7 +704,7 @@ static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) { - __u8 *data; + u8 *data; data = gspca_dev->usb_buf; reg_r(gspca_dev, 0x20, 0, 5); @@ -725,41 +718,34 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - __u8 Size; - __u8 Type; + u8 Size; int rc; - Size = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - Type = 0; + Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; switch (sd->bridge) { case BRIDGE_SPCA533: - reg_w(gspca_dev, 0x31, 0, 0, 0); + reg_w_riv(dev, 0x31, 0, 0); spca504B_WaitCmdStatus(gspca_dev); rc = spca504B_PollingDataReady(gspca_dev); spca50x_GetFirmware(gspca_dev); - gspca_dev->usb_buf[0] = 2; /* type */ - reg_w(gspca_dev, 0x24, 0, 8, 1); + reg_w_1(gspca_dev, 0x24, 0, 8, 2); /* type */ reg_r(gspca_dev, 0x24, 8, 1); - gspca_dev->usb_buf[0] = Size; - reg_w(gspca_dev, 0x25, 0, 4, 1); + reg_w_1(gspca_dev, 0x25, 0, 4, Size); 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(gspca_dev, 0x31, 0, 4, 0); + reg_w_riv(dev, 0x31, 0, 0x04); spca504B_WaitCmdStatus(gspca_dev); rc = spca504B_PollingDataReady(gspca_dev); break; default: /* case BRIDGE_SPCA504B: */ /* case BRIDGE_SPCA536: */ - gspca_dev->usb_buf[0] = Size; - reg_w(gspca_dev, 0x25, 0, 4, 1); + reg_w_1(gspca_dev, 0x25, 0, 4, Size); reg_r(gspca_dev, 0x25, 4, 1); /* size */ - Type = 6; - gspca_dev->usb_buf[0] = Type; - reg_w(gspca_dev, 0x27, 0, 0, 1); + reg_w_1(gspca_dev, 0x27, 0, 0, 6); reg_r(gspca_dev, 0x27, 0, 1); /* type */ rc = spca504B_PollingDataReady(gspca_dev); break; @@ -799,17 +785,51 @@ static void spca504_wait_status(struct gspca_dev *gspca_dev) static void spca504B_setQtable(struct gspca_dev *gspca_dev) { - gspca_dev->usb_buf[0] = 3; - reg_w(gspca_dev, 0x26, 0, 0, 1); + reg_w_1(gspca_dev, 0x26, 0, 0, 3); reg_r(gspca_dev, 0x26, 0, 1); spca504B_PollingDataReady(gspca_dev); } -static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev) +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + u16 reg; + + reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7; + reg_w_riv(dev, 0x00, reg, sd->brightness); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + u16 reg; + + reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8; + reg_w_riv(dev, 0x00, reg, sd->contrast); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + u16 reg; + + reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae; + reg_w_riv(dev, 0x00, reg, sd->colors); +} + +static void init_ctl_reg(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; int pollreg = 1; + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setcolors(gspca_dev); + switch (sd->bridge) { case BRIDGE_SPCA504: case BRIDGE_SPCA504C: @@ -818,20 +838,14 @@ static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev) default: /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA504B: */ - 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 */ + reg_w_riv(dev, 0, 0x00, 0x21ad); /* hue */ + reg_w_riv(dev, 0, 0x01, 0x21ac); /* sat/hue */ + reg_w_riv(dev, 0, 0x00, 0x21a3); /* gamma */ break; case BRIDGE_SPCA536: - 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); + reg_w_riv(dev, 0, 0x40, 0x20f5); + reg_w_riv(dev, 0, 0x01, 0x20f4); + reg_w_riv(dev, 0, 0x00, 0x2089); break; } if (pollreg) @@ -886,9 +900,10 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->nmodes = ARRAY_SIZE(vga_mode2); break; } - sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; - sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; - sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + sd->autogain = AUTOGAIN_DEF; sd->quality = QUALITY_DEF; return 0; } @@ -898,32 +913,29 @@ static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - int rc; - __u8 i; - __u8 info[6]; - int err_code; + int i, err_code; + u8 info[6]; switch (sd->bridge) { case BRIDGE_SPCA504B: - 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); + reg_w_riv(dev, 0x1d, 0x00, 0); + reg_w_riv(dev, 0, 0x01, 0x2306); + reg_w_riv(dev, 0, 0x00, 0x0d04); + reg_w_riv(dev, 0, 0x00, 0x2000); + reg_w_riv(dev, 0, 0x13, 0x2301); + reg_w_riv(dev, 0, 0x00, 0x2306); /* fall thru */ case BRIDGE_SPCA533: - rc = spca504B_PollingDataReady(gspca_dev); + spca504B_PollingDataReady(gspca_dev); spca50x_GetFirmware(gspca_dev); break; case BRIDGE_SPCA536: spca50x_GetFirmware(gspca_dev); reg_r(gspca_dev, 0x00, 0x5002, 1); - gspca_dev->usb_buf[0] = 0; - reg_w(gspca_dev, 0x24, 0, 0, 1); + reg_w_1(gspca_dev, 0x24, 0, 0, 0); reg_r(gspca_dev, 0x24, 0, 1); - rc = spca504B_PollingDataReady(gspca_dev); - reg_w(gspca_dev, 0x34, 0, 0, 0); + spca504B_PollingDataReady(gspca_dev); + reg_w_riv(dev, 0x34, 0, 0); spca504B_WaitCmdStatus(gspca_dev); break; case BRIDGE_SPCA504C: /* pccam600 */ @@ -933,12 +945,13 @@ static int sd_init(struct gspca_dev *gspca_dev) spca504_wait_status(gspca_dev); if (sd->subtype == LogitechClickSmart420) write_vector(gspca_dev, - spca504A_clicksmart420_open_data); + spca504A_clicksmart420_open_data, + ARRAY_SIZE(spca504A_clicksmart420_open_data)); else - write_vector(gspca_dev, spca504_pccam600_open_data); + write_vector(gspca_dev, spca504_pccam600_open_data, + ARRAY_SIZE(spca504_pccam600_open_data)); err_code = spca50x_setup_qtable(gspca_dev, - 0x00, 0x2800, - 0x2840, qtable_creative_pccam); + qtable_creative_pccam); if (err_code < 0) { PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed"); return err_code; @@ -976,8 +989,8 @@ static int sd_init(struct gspca_dev *gspca_dev) 6, 0, 0x86, 1); */ /* spca504A_acknowledged_command (gspca_dev, 0x24, 0, 0, 0x9D, 1); */ - reg_w_riv(dev, 0x0, 0x270c, 0x05); /* L92 sno1t.txt */ - reg_w_riv(dev, 0x0, 0x2310, 0x05); + reg_w_riv(dev, 0x00, 0x270c, 0x05); /* L92 sno1t.txt */ + reg_w_riv(dev, 0x00, 0x2310, 0x05); spca504A_acknowledged_command(gspca_dev, 0x01, 0x0f, 0, 0xff, 0); } @@ -985,8 +998,6 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w_riv(dev, 0, 0x2000, 0); reg_w_riv(dev, 0, 0x2883, 1); err_code = spca50x_setup_qtable(gspca_dev, - 0x00, 0x2800, - 0x2840, qtable_spca504_default); if (err_code < 0) { PDEBUG(D_ERR, "spca50x_setup_qtable failed"); @@ -1001,10 +1012,9 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - int rc; int enable; - __u8 i; - __u8 info[6]; + int i; + u8 info[6]; /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); @@ -1022,17 +1032,20 @@ static int sd_start(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA504B: */ /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA536: */ - if (sd->subtype == MegapixV4 || - sd->subtype == LogitechClickSmart820 || - sd->subtype == MegaImageVI) { - reg_w(gspca_dev, 0xf0, 0, 0, 0); + switch (sd->subtype) { + case MegapixV4: + case LogitechClickSmart820: + case MegaImageVI: + reg_w_riv(dev, 0xf0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); reg_r(gspca_dev, 0xf0, 4, 0); spca504B_WaitCmdStatus(gspca_dev); - } else { - reg_w(gspca_dev, 0x31, 0, 4, 0); + break; + default: + reg_w_riv(dev, 0x31, 0, 0x04); spca504B_WaitCmdStatus(gspca_dev); - rc = spca504B_PollingDataReady(gspca_dev); + spca504B_PollingDataReady(gspca_dev); + break; } break; case BRIDGE_SPCA504: @@ -1066,15 +1079,17 @@ static int sd_start(struct gspca_dev *gspca_dev) spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); } spca504B_SetSizeType(gspca_dev); - reg_w_riv(dev, 0x0, 0x270c, 0x05); /* L92 sno1t.txt */ - reg_w_riv(dev, 0x0, 0x2310, 0x05); + reg_w_riv(dev, 0x00, 0x270c, 0x05); /* L92 sno1t.txt */ + reg_w_riv(dev, 0x00, 0x2310, 0x05); break; case BRIDGE_SPCA504C: if (sd->subtype == LogitechClickSmart420) { write_vector(gspca_dev, - spca504A_clicksmart420_init_data); + spca504A_clicksmart420_init_data, + ARRAY_SIZE(spca504A_clicksmart420_init_data)); } else { - write_vector(gspca_dev, spca504_pccam600_init_data); + write_vector(gspca_dev, spca504_pccam600_init_data, + ARRAY_SIZE(spca504_pccam600_init_data)); } enable = (sd->autogain ? 0x04 : 0x01); reg_w_riv(dev, 0x0c, 0x0000, enable); /* auto exposure */ @@ -1086,7 +1101,7 @@ static int sd_start(struct gspca_dev *gspca_dev) spca504B_SetSizeType(gspca_dev); break; } - sp5xx_initContBrigHueRegisters(gspca_dev); + init_ctl_reg(gspca_dev); return 0; } @@ -1100,7 +1115,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA536: */ /* case BRIDGE_SPCA504B: */ - reg_w(gspca_dev, 0x31, 0, 0, 0); + reg_w_riv(dev, 0x31, 0, 0); spca504B_WaitCmdStatus(gspca_dev); spca504B_PollingDataReady(gspca_dev); break; @@ -1118,7 +1133,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) 0x0f, 0x00, 0xff, 1); } else { spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); - reg_w_riv(dev, 0x01, 0x000f, 0x00); + reg_w_riv(dev, 0x01, 0x000f, 0x0000); } break; } @@ -1133,12 +1148,12 @@ static void sd_stop0(struct gspca_dev *gspca_dev) static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; int i, sof = 0; - static unsigned char ffd9[] = {0xff, 0xd9}; + static u8 ffd9[] = {0xff, 0xd9}; /* frames are jpeg 4.1.1 without 0xff escape */ switch (sd->bridge) { @@ -1226,63 +1241,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; - - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA504: */ -/* case BRIDGE_SPCA504C: */ - reg_w_riv(dev, 0x0, 0x21a7, sd->brightness); - break; - case BRIDGE_SPCA536: - reg_w_riv(dev, 0x0, 0x20f0, sd->brightness); - break; - } -} - -static void setcontrast(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; - - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA504: */ -/* case BRIDGE_SPCA504C: */ - reg_w_riv(dev, 0x0, 0x21a8, sd->contrast); - break; - case BRIDGE_SPCA536: - reg_w_riv(dev, 0x0, 0x20f1, sd->contrast); - break; - } -} - -static void setcolors(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct usb_device *dev = gspca_dev->dev; - - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA504: */ -/* case BRIDGE_SPCA504C: */ - reg_w_riv(dev, 0x0, 0x21ae, sd->colors); - break; - case BRIDGE_SPCA536: - reg_w_riv(dev, 0x0, 0x20f6, sd->colors); - break; - } -} - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index 6f9aab89c..148d28802 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -1940,109 +1940,200 @@ static const u8 po3130_initQVGA_data[][4] = { }; static const u8 hv7131r_gamma[17] = { -/* 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, - * 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff */ +#if 1 + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +#else 0x04, 0x1a, 0x36, 0x55, 0x6f, 0x87, 0x9d, 0xb0, 0xc1, 0xcf, 0xda, 0xe4, 0xec, 0xf3, 0xf8, 0xfd, 0xff +#endif }; static const u8 hv7131r_matrix[9] = { 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 }; static const u8 hv7131r_initVGA_data[][4] = { - {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x20, 0xdd}, {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x01, 0x45, 0xcc}, {0xb3, 0x03, 0x0b, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, + {0xb3, 0x03, 0x0b, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x02, 0xcc}, {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0x91, 0xcc}, {0xb3, 0x00, 0x27, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0x91, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x73, 0xcc}, - {0xb8, 0x00, 0x23, 0xcc}, {0x00, 0x01, 0x0c, 0xaa}, - {0x00, 0x14, 0x01, 0xaa}, {0x00, 0x15, 0xe6, 0xaa}, - {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x17, 0x86, 0xaa}, {0x00, 0x23, 0x00, 0xaa}, - {0x00, 0x25, 0x09, 0xaa}, {0x00, 0x26, 0x27, 0xaa}, - {0x00, 0x27, 0xc0, 0xaa}, - {0xb8, 0x2c, 0x60, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, - {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x00, 0x23, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, {0xb8, 0x30, 0x50, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, {0xb8, 0x32, 0xf8, 0xcc}, - {0xb8, 0x33, 0xf8, 0xcc}, {0xb8, 0x34, 0x65, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x58, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x01, 0x7d, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x7d, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, {0xb8, 0xfe, 0x00, 0xcc}, - {0xb8, 0xff, 0x28, 0xcc}, {0xb9, 0x00, 0x28, 0xcc}, - {0xb9, 0x01, 0x28, 0xcc}, - {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, - {0xb9, 0x06, 0x3c, 0xcc}, - {0xb9, 0x07, 0x3c, 0xcc}, {0xb9, 0x08, 0x3c, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0x00, 0x01, 0x0c, 0xaa}, + {0x00, 0x14, 0x01, 0xaa}, + {0x00, 0x15, 0xe6, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x17, 0x86, 0xaa}, + {0x00, 0x23, 0x00, 0xaa}, + {0x00, 0x25, 0x03, 0xaa}, + {0x00, 0x26, 0xa9, 0xaa}, + {0x00, 0x27, 0x80, 0xaa}, {0x00, 0x30, 0x18, 0xaa}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {} }; static const u8 hv7131r_initQVGA_data[][4] = { - {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, - {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x20, 0xdd}, {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x03, 0x0b, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, + {0xb3, 0x03, 0x0b, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x02, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0x91, 0xcc}, - {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, - {0xb8, 0x00, 0x21, 0xcc}, - {0x00, 0x01, 0x0c, 0xaa}, {0x00, 0x14, 0x01, 0xaa}, - {0x00, 0x15, 0xe6, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, - {0x00, 0x17, 0x86, 0xaa}, - {0x00, 0x23, 0x00, 0xaa}, {0x00, 0x25, 0x01, 0xaa}, - {0x00, 0x26, 0xd4, 0xaa}, {0x00, 0x27, 0xc0, 0xaa}, - {0xbc, 0x02, 0x08, 0xcc}, - {0xbc, 0x03, 0x70, 0xcc}, {0xbc, 0x04, 0x08, 0xcc}, - {0xbc, 0x05, 0x00, 0xcc}, {0xbc, 0x06, 0x00, 0xcc}, - {0xbc, 0x08, 0x3c, 0xcc}, - {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x04, 0xcc}, - {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, - {0xb8, 0xfe, 0x02, 0xcc}, - {0xb8, 0xff, 0x07, 0xcc}, {0xb9, 0x00, 0x14, 0xcc}, - {0xb9, 0x01, 0x14, 0xcc}, {0xb9, 0x02, 0x14, 0xcc}, - {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x02, 0xcc}, {0xb9, 0x05, 0x05, 0xcc}, - {0xb9, 0x06, 0x0f, 0xcc}, {0xb9, 0x07, 0x0f, 0xcc}, - {0xb9, 0x08, 0x0f, 0xcc}, - {0xb8, 0x2c, 0x60, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, - {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0x91, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, + {0xbc, 0x00, 0xd3, 0xcc}, + {0xb8, 0x00, 0x23, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, + {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, + {0xb8, 0x2f, 0xf8, 0xcc}, {0xb8, 0x30, 0x50, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc}, - {0xb8, 0x34, 0x65, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x34, 0x58, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, {0xb8, 0xfe, 0x00, 0xcc}, - {0xb8, 0xff, 0x28, 0xcc}, - {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, - {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, - {0xb9, 0x04, 0x00, 0xcc}, - {0xb9, 0x05, 0x3c, 0xcc}, {0xb9, 0x06, 0x3c, 0xcc}, - {0xb9, 0x07, 0x3c, 0xcc}, {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x01, 0x7d, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, {0x00, 0x30, 0x18, 0xaa}, + {0xb8, 0x8f, 0xff, 0xcc}, + {0x00, 0x01, 0x0c, 0xaa}, + {0x00, 0x14, 0x01, 0xaa}, + {0x00, 0x15, 0xe6, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x17, 0x86, 0xaa}, + {0x00, 0x23, 0x00, 0xaa}, + {0x00, 0x25, 0x03, 0xaa}, + {0x00, 0x26, 0xa9, 0xaa}, + {0x00, 0x27, 0x80, 0xaa}, + {0x00, 0x30, 0x18, 0xaa}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, + {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, + {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, + {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x10, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xb9, 0x12, 0x00, 0xcc}, + {0xb9, 0x13, 0x0a, 0xcc}, + {0xb9, 0x14, 0x0a, 0xcc}, + {0xb9, 0x15, 0x0a, 0xcc}, + {0xb9, 0x16, 0x0a, 0xcc}, + {0xb9, 0x18, 0x00, 0xcc}, + {0xb9, 0x19, 0x0f, 0xcc}, + {0xb8, 0x0c, 0x20, 0xcc}, + {0xb8, 0x0d, 0x70, 0xcc}, + {0xb9, 0x1a, 0x0f, 0xcc}, + {0xb9, 0x1b, 0x0f, 0xcc}, + {0xb9, 0x1c, 0x0f, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x13, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {} }; @@ -3319,21 +3410,23 @@ static int sd_start(struct gspca_dev *gspca_dev) put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c); /* set the led on 0x0892 0x0896 */ - if (sd->sensor == SENSOR_PO1200) { - setsharpness(gspca_dev); - sethvflip(gspca_dev); + switch (sd->sensor) { + case SENSOR_PO1200: + case SENSOR_HV7131R: reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415); - } else if (sd->sensor == SENSOR_MI1310_SOC) { + break; + case SENSOR_MI1310_SOC: reg_w(gspca_dev->dev, 0x89, 0x058c, 0x0000); - msleep(100); - sethvflip(gspca_dev); - setlightfreq(gspca_dev); - } else { - reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); - msleep(100); - sethvflip(gspca_dev); - setlightfreq(gspca_dev); + break; + default: + if (!(sd->flags & FL_SAMSUNG)) + reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + break; } + msleep(100); + setsharpness(gspca_dev); + sethvflip(gspca_dev); + setlightfreq(gspca_dev); } return 0; } @@ -3345,7 +3438,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) if (sd->sensor == SENSOR_MI1310_SOC) reg_w(dev, 0x89, 0x058c, 0x00ff); - else + else if (!(sd->flags & FL_SAMSUNG)) reg_w(dev, 0x89, 0xffff, 0xffff); reg_w(dev, 0xa0, 0x01, 0xb301); reg_w(dev, 0xa0, 0x09, 0xb003); @@ -3362,7 +3455,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) /*fixme: is this useful?*/ if (sd->sensor == SENSOR_MI1310_SOC) reg_w(dev, 0x89, 0x058c, 0x00ff); - else + else if (!(sd->flags & FL_SAMSUNG)) reg_w(dev, 0x89, 0xffff, 0xffff); } diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c index fbb8586c5..383d401b4 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.c +++ b/linux/drivers/media/video/ivtv/ivtv-driver.c @@ -246,7 +246,7 @@ MODULE_PARM_DESC(newi2c, "\t\t\t-1 is autodetect, 0 is off, 1 is on\n" "\t\t\tDefault is autodetect"); -MODULE_PARM_DESC(ivtv_first_minor, "Set kernel number assigned to first card"); +MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card"); MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); MODULE_DESCRIPTION("CX23415/CX23416 driver"); diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c index 15da01710..67699e3f2 100644 --- a/linux/drivers/media/video/ivtv/ivtv-streams.c +++ b/linux/drivers/media/video/ivtv/ivtv-streams.c @@ -261,8 +261,8 @@ static int ivtv_reg_dev(struct ivtv *itv, int type) video_set_drvdata(s->vdev, s); /* Register device. First try the desired minor, then any free one. */ - if (video_register_device(s->vdev, vfl_type, num)) { - IVTV_ERR("Couldn't register v4l2 device for %s kernel number %d\n", + if (video_register_device_no_warn(s->vdev, vfl_type, num)) { + IVTV_ERR("Couldn't register v4l2 device for %s (device node number %d)\n", s->name, num); video_device_release(s->vdev); s->vdev = NULL; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 336a20ede..e4d7c13ca 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -298,6 +298,7 @@ static struct tda829x_config tda829x_no_probe = { static struct tda18271_config hauppauge_tda18271_dvb_config = { .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap) @@ -393,6 +394,7 @@ static struct tda18271_std_map hauppauge_tda18271_std_map = { static struct tda18271_config hauppauge_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap) diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 18223dafe..dc2428f12 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -1007,6 +1007,7 @@ static struct tda18271_config hcw_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, .config = 3, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda829x_config tda829x_no_probe = { diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c index 261c86639..153fb3c5b 100644 --- a/linux/drivers/media/video/saa7134/saa7134-input.c +++ b/linux/drivers/media/video/saa7134/saa7134-input.c @@ -741,8 +741,6 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) #endif { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) - struct i2c_board_info info; - struct IR_i2c_init_data init_data; const unsigned short addr_list[] = { 0x7a, 0x47, 0x71, 0x2d, I2C_CLIENT_END @@ -769,9 +767,9 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&init_data, 0, sizeof(struct IR_i2c_init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + memset(&dev->info, 0, sizeof(dev->info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE); #endif switch (dev->board) { @@ -780,25 +778,25 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) snprintf(ir->c.name, sizeof(ir->c.name), "Pinnacle PCTV"); #else - init_data.name = "Pinnacle PCTV"; + dev->init_data.name = "Pinnacle PCTV"; #endif if (pinnacle_remote == 0) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->get_key = get_key_pinnacle_color; ir->ir_codes = &ir_codes_pinnacle_color_table; #else - init_data.get_key = get_key_pinnacle_color; - init_data.ir_codes = &ir_codes_pinnacle_color_table; - info.addr = 0x47; + dev->init_data.get_key = get_key_pinnacle_color; + dev->init_data.ir_codes = &ir_codes_pinnacle_color_table; + dev->info.addr = 0x47; #endif } else { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->get_key = get_key_pinnacle_grey; ir->ir_codes = &ir_codes_pinnacle_grey_table; #else - init_data.get_key = get_key_pinnacle_grey; - init_data.ir_codes = &ir_codes_pinnacle_grey_table; - info.addr = 0x47; + dev->init_data.get_key = get_key_pinnacle_grey; + dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; + dev->info.addr = 0x47; #endif } break; @@ -808,9 +806,9 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) ir->get_key = get_key_purpletv; ir->ir_codes = &ir_codes_purpletv_table; #else - init_data.name = "Purple TV"; - init_data.get_key = get_key_purpletv; - init_data.ir_codes = &ir_codes_purpletv_table; + dev->init_data.name = "Purple TV"; + dev->init_data.get_key = get_key_purpletv; + dev->init_data.ir_codes = &ir_codes_purpletv_table; #endif break; case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: @@ -819,10 +817,10 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) ir->get_key = get_key_msi_tvanywhere_plus; ir->ir_codes = &ir_codes_msi_tvanywhere_plus_table; #else - init_data.name = "MSI TV@nywhere Plus"; - init_data.get_key = get_key_msi_tvanywhere_plus; - init_data.ir_codes = &ir_codes_msi_tvanywhere_plus_table; - info.addr = 0x30; + dev->init_data.name = "MSI TV@nywhere Plus"; + dev->init_data.get_key = get_key_msi_tvanywhere_plus; + dev->init_data.ir_codes = &ir_codes_msi_tvanywhere_plus_table; + dev->info.addr = 0x30; /* MSI TV@nywhere Plus controller doesn't seem to respond to probes unless we read something from an existing device. Weird... @@ -839,9 +837,9 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) ir->get_key = get_key_hvr1110; ir->ir_codes = &ir_codes_hauppauge_new_table; #else - init_data.name = "HVR 1110"; - init_data.get_key = get_key_hvr1110; - init_data.ir_codes = &ir_codes_hauppauge_new_table; + dev->init_data.name = "HVR 1110"; + dev->init_data.get_key = get_key_hvr1110; + dev->init_data.ir_codes = &ir_codes_hauppauge_new_table; #endif break; case SAA7134_BOARD_BEHOLD_607FM_MK3: @@ -862,9 +860,9 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) ir->get_key = get_key_beholdm6xx; ir->ir_codes = &ir_codes_behold_table; #else - init_data.name = "BeholdTV"; - init_data.get_key = get_key_beholdm6xx; - init_data.ir_codes = &ir_codes_behold_table; + dev->init_data.name = "BeholdTV"; + dev->init_data.get_key = get_key_beholdm6xx; + dev->init_data.ir_codes = &ir_codes_behold_table; #endif break; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) @@ -873,22 +871,22 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) #else case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: - info.addr = 0x40; + dev->info.addr = 0x40; #endif break; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) - if (init_data.name) - info.platform_data = &init_data; + if (dev->init_data.name) + dev->info.platform_data = &dev->init_data; /* No need to probe if address is known */ - if (info.addr) { - i2c_new_device(&dev->i2c_adap, &info); + if (dev->info.addr) { + i2c_new_device(&dev->i2c_adap, &dev->info); return; } /* Address not known, fallback to probing */ - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); + i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list); #endif } diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 8c164bc79..1bf2837b4 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -594,6 +594,12 @@ struct saa7134_dev { int nosignal; unsigned int insuspend; + /* I2C keyboard data */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + struct i2c_board_info info; + struct IR_i2c_init_data init_data; +#endif + /* SAA7134_MPEG_* */ struct saa7134_ts ts; struct saa7134_dmaqueue ts_q; diff --git a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h index 38a716020..36ee43a9e 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -123,8 +123,8 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, #if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, -#endif { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, +#endif { } }; diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index a4f6c19b7..507d6c7b0 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -30,6 +30,7 @@ #include "tuner-simple.h" #include "tda9887.h" #include "xc5000.h" +#include "tda18271.h" #define UNSET (-1U) @@ -434,6 +435,17 @@ static void set_type(struct i2c_client *c, unsigned int type, goto attach_failed; break; } + case TUNER_NXP_TDA18271: + { + struct tda18271_config cfg = { + .config = t->config, + }; + + if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr, + t->i2c->adapter, &cfg)) + goto attach_failed; + break; + } default: if (!dvb_attach(simple_tuner_attach, &t->fe, t->i2c->adapter, t->i2c->addr, t->type)) diff --git a/linux/drivers/media/video/v4l1-compat.c b/linux/drivers/media/video/v4l1-compat.c index 2225243ad..12e295750 100644 --- a/linux/drivers/media/video/v4l1-compat.c +++ b/linux/drivers/media/video/v4l1-compat.c @@ -565,10 +565,9 @@ static noinline long v4l1_compat_get_input_info( break; } chan->norm = 0; - err = drv(file, VIDIOC_G_STD, &sid); - if (err < 0) - dprintk("VIDIOCGCHAN / VIDIOC_G_STD: %ld\n", err); - if (err == 0) { + /* Note: G_STD might not be present for radio receivers, + * so we should ignore any errors. */ + if (drv(file, VIDIOC_G_STD, &sid) == 0) { if (sid & V4L2_STD_PAL) chan->norm = VIDEO_MODE_PAL; if (sid & V4L2_STD_NTSC) @@ -777,10 +776,9 @@ static noinline long v4l1_compat_get_tuner( tun->flags |= VIDEO_TUNER_SECAM; } - err = drv(file, VIDIOC_G_STD, &sid); - if (err < 0) - dprintk("VIDIOCGTUNER / VIDIOC_G_STD: %ld\n", err); - if (err == 0) { + /* Note: G_STD might not be present for radio receivers, + * so we should ignore any errors. */ + if (drv(file, VIDIOC_G_STD, &sid) == 0) { if (sid & V4L2_STD_PAL) tun->mode = VIDEO_MODE_PAL; if (sid & V4L2_STD_NTSC) diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c index 9f04a8e6d..a6d761dec 100644 --- a/linux/drivers/media/video/v4l2-dev.c +++ b/linux/drivers/media/video/v4l2-dev.c @@ -86,7 +86,49 @@ static CLASS_DEVICE_ATTR(name, S_IRUGO, show_name, NULL); */ static struct video_device *video_device[VIDEO_NUM_DEVICES]; static DEFINE_MUTEX(videodev_lock); -static DECLARE_BITMAP(video_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES); +static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES); + +/* Device node utility functions */ + +/* Note: these utility functions all assume that vfl_type is in the range + [0, VFL_TYPE_MAX-1]. */ + +#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES +/* Return the bitmap corresponding to vfl_type. */ +static inline unsigned long *devnode_bits(int vfl_type) +{ + /* Any types not assigned to fixed minor ranges must be mapped to + one single bitmap for the purposes of finding a free node number + since all those unassigned types use the same minor range. */ + int idx = (vfl_type > VFL_TYPE_VTX) ? VFL_TYPE_MAX - 1 : vfl_type; + + return devnode_nums[idx]; +} +#else +/* Return the bitmap corresponding to vfl_type. */ +static inline unsigned long *devnode_bits(int vfl_type) +{ + return devnode_nums[vfl_type]; +} +#endif + +/* Mark device node number vdev->num as used */ +static inline void devnode_set(struct video_device *vdev) +{ + set_bit(vdev->num, devnode_bits(vdev->vfl_type)); +} + +/* Mark device node number vdev->num as unused */ +static inline void devnode_clear(struct video_device *vdev) +{ + clear_bit(vdev->num, devnode_bits(vdev->vfl_type)); +} + +/* Try to find a free device node number in the range [from, to> */ +static inline int devnode_find(struct video_device *vdev, int from, int to) +{ + return find_next_zero_bit(devnode_bits(vdev->vfl_type), to, from); +} struct video_device *video_device_alloc(void) { @@ -151,8 +193,8 @@ static void v4l2_device_release(struct device *cd) the release() callback. */ vdev->cdev = NULL; - /* Mark minor as free */ - clear_bit(vdev->num, video_nums[vdev->vfl_type]); + /* Mark device node number as free */ + devnode_clear(vdev); mutex_unlock(&videodev_lock); @@ -376,13 +418,16 @@ static int get_index(struct video_device *vdev) * video_register_device - register video4linux devices * @vdev: video device structure we want to register * @type: type of device to register - * @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ... + * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ... * -1 == first free) + * @warn_if_nr_in_use: warn if the desired device node number + * was already in use and another number was chosen instead. * - * The registration code assigns minor numbers based on the type - * requested. -ENFILE is returned in all the device slots for this - * category are full. If not then the minor field is set and the - * driver initialize function is called (if non %NULL). + * The registration code assigns minor numbers and device node numbers + * based on the requested type and registers the new device node with + * the kernel. + * An error is returned if no free minor or device node number could be + * found, or if the registration of the device node failed. * * Zero is returned on success. * @@ -396,7 +441,8 @@ static int get_index(struct video_device *vdev) * * %VFL_TYPE_RADIO - A radio card */ -int video_register_device(struct video_device *vdev, int type, int nr) +static int __video_register_device(struct video_device *vdev, int type, int nr, + int warn_if_nr_in_use) { int i = 0; int ret; @@ -439,7 +485,7 @@ int video_register_device(struct video_device *vdev, int type, int nr) if (vdev->v4l2_dev && vdev->v4l2_dev->dev) vdev->parent = vdev->v4l2_dev->dev; - /* Part 2: find a free minor, kernel number and device index. */ + /* Part 2: find a free minor, device node number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES /* Keep the ranges for the first four types for historical * reasons. @@ -470,21 +516,22 @@ int video_register_device(struct video_device *vdev, int type, int nr) } #endif - /* Pick a minor number */ + /* Pick a device node number */ mutex_lock(&videodev_lock); - nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr); + nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt); if (nr == minor_cnt) - nr = find_first_zero_bit(video_nums[type], minor_cnt); + nr = devnode_find(vdev, 0, minor_cnt); if (nr == minor_cnt) { - printk(KERN_ERR "could not get a free kernel number\n"); + printk(KERN_ERR "could not get a free device node number\n"); mutex_unlock(&videodev_lock); return -ENFILE; } #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES - /* 1-on-1 mapping of kernel number to minor number */ + /* 1-on-1 mapping of device node number to minor number */ i = nr; #else - /* The kernel number and minor numbers are independent */ + /* The device node number and minor numbers are independent, so + we just find the first free minor number. */ for (i = 0; i < VIDEO_NUM_DEVICES; i++) if (video_device[i] == NULL) break; @@ -496,7 +543,8 @@ int video_register_device(struct video_device *vdev, int type, int nr) #endif vdev->minor = i + minor_offset; vdev->num = nr; - set_bit(nr, video_nums[type]); + devnode_set(vdev); + /* Should not happen since we thought this minor was free */ WARN_ON(video_device[vdev->minor] != NULL); vdev->index = get_index(vdev); @@ -538,12 +586,12 @@ int video_register_device(struct video_device *vdev, int type, int nr) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) if (vdev->parent) vdev->dev.dev = vdev->parent; - sprintf(vdev->dev.class_id, "%s%d", name_base, nr); + sprintf(vdev->dev.class_id, "%s%d", name_base, vdev->num); ret = class_device_register(&vdev->dev); #else if (vdev->parent) vdev->dev.parent = vdev->parent; - dev_set_name(&vdev->dev, "%s%d", name_base, nr); + dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); ret = device_register(&vdev->dev); #endif if (ret < 0) { @@ -570,6 +618,10 @@ int video_register_device(struct video_device *vdev, int type, int nr) reference to the device goes away. */ vdev->dev.release = v4l2_device_release; + if (nr != -1 && nr != vdev->num && warn_if_nr_in_use) + printk(KERN_WARNING "%s: requested %s%d, got %s%d\n", + __func__, name_base, nr, name_base, vdev->num); + /* Part 5: Activate this minor. The char device can now be used. */ mutex_lock(&videodev_lock); video_device[vdev->minor] = vdev; @@ -580,14 +632,25 @@ cleanup: mutex_lock(&videodev_lock); if (vdev->cdev) cdev_del(vdev->cdev); - clear_bit(vdev->num, video_nums[type]); + devnode_clear(vdev); mutex_unlock(&videodev_lock); /* Mark this video device as never having been registered. */ vdev->minor = -1; return ret; } + +int video_register_device(struct video_device *vdev, int type, int nr) +{ + return __video_register_device(vdev, type, nr, 1); +} EXPORT_SYMBOL(video_register_device); +int video_register_device_no_warn(struct video_device *vdev, int type, int nr) +{ + return __video_register_device(vdev, type, nr, 0); +} +EXPORT_SYMBOL(video_register_device_no_warn); + /** * video_unregister_device - unregister a video4linux device * @vdev: the device to unregister diff --git a/linux/drivers/media/video/videobuf-dma-contig.c b/linux/drivers/media/video/videobuf-dma-contig.c index 633a124cf..bd1b13e93 100644 --- a/linux/drivers/media/video/videobuf-dma-contig.c +++ b/linux/drivers/media/video/videobuf-dma-contig.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/mm.h> +#include <linux/pagemap.h> #include <linux/dma-mapping.h> #include <media/videobuf-dma-contig.h> #include "compat.h" @@ -26,6 +27,7 @@ struct videobuf_dma_contig_memory { void *vaddr; dma_addr_t dma_handle; unsigned long size; + int is_userptr; }; #define MAGIC_DC_MEM 0x0733ac61 @@ -109,6 +111,82 @@ static struct vm_operations_struct videobuf_vm_ops = { .close = videobuf_vm_close, }; +/** + * videobuf_dma_contig_user_put() - reset pointer to user space buffer + * @mem: per-buffer private videobuf-dma-contig data + * + * This function resets the user space pointer + */ +static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem) +{ + mem->is_userptr = 0; + mem->dma_handle = 0; + mem->size = 0; +} + +/** + * videobuf_dma_contig_user_get() - setup user space memory pointer + * @mem: per-buffer private videobuf-dma-contig data + * @vb: video buffer to map + * + * This function validates and sets up a pointer to user space memory. + * Only physically contiguous pfn-mapped memory is accepted. + * + * Returns 0 if successful. + */ +static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, + struct videobuf_buffer *vb) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + unsigned long prev_pfn, this_pfn; + unsigned long pages_done, user_address; + int ret; + + mem->size = PAGE_ALIGN(vb->size); + mem->is_userptr = 0; + ret = -EINVAL; + + down_read(&mm->mmap_sem); + + vma = find_vma(mm, vb->baddr); + if (!vma) + goto out_up; + + if ((vb->baddr + mem->size) > vma->vm_end) + goto out_up; + + pages_done = 0; + prev_pfn = 0; /* kill warning */ + user_address = vb->baddr; + + while (pages_done < (mem->size >> PAGE_SHIFT)) { + ret = follow_pfn(vma, user_address, &this_pfn); + if (ret) + break; + + if (pages_done == 0) + mem->dma_handle = this_pfn << PAGE_SHIFT; + else if (this_pfn != (prev_pfn + 1)) + ret = -EFAULT; + + if (ret) + break; + + prev_pfn = this_pfn; + user_address += PAGE_SIZE; + pages_done++; + } + + if (!ret) + mem->is_userptr = 1; + + out_up: + up_read(¤t->mm->mmap_sem); + + return ret; +} + static void *__videobuf_alloc(size_t size) { struct videobuf_dma_contig_memory *mem; @@ -155,12 +233,11 @@ static int __videobuf_iolock(struct videobuf_queue *q, case V4L2_MEMORY_USERPTR: dev_dbg(q->dev, "%s memory method USERPTR\n", __func__); - /* The only USERPTR currently supported is the one needed for - read() method. - */ + /* handle pointer from user space */ if (vb->baddr) - return -EINVAL; + return videobuf_dma_contig_user_get(mem, vb); + /* allocate memory for the read() method */ mem->size = PAGE_ALIGN(vb->size); mem->vaddr = dma_alloc_coherent(q->dev, mem->size, &mem->dma_handle, GFP_KERNEL); @@ -387,7 +464,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q, So, it should free memory only if the memory were allocated for read() operation. */ - if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr) + if (buf->memory != V4L2_MEMORY_USERPTR) return; if (!mem) @@ -395,6 +472,13 @@ void videobuf_dma_contig_free(struct videobuf_queue *q, MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + /* handle user space pointer case */ + if (buf->baddr) { + videobuf_dma_contig_user_put(mem); + return; + } + + /* read() method */ dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle); mem->vaddr = NULL; } diff --git a/linux/drivers/media/video/vino.c b/linux/drivers/media/video/vino.c index 8383e6f80..5a8dd5e19 100644 --- a/linux/drivers/media/video/vino.c +++ b/linux/drivers/media/video/vino.c @@ -50,6 +50,7 @@ #include <asm/sgi/ip22.h> #include <asm/sgi/mc.h> +#include "compat.h" #include "vino.h" #include "saa7191.h" #include "indycam.h" |