diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-20 21:14:42 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-20 21:14:42 -0700 |
commit | f894d18380e7e7ff05f6622ccb75d2881922c6e9 (patch) | |
tree | e3c11b831b68096239a49dec539a49e49c1d90b7 /drivers/media | |
parent | d13ff0559fea73f237a01669887d2c10e11d7662 (diff) | |
parent | d20b27478d6ccf7c4c8de4f09db2bdbaec82a6c0 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb
* git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb: (277 commits)
V4L/DVB (8415): gspca: Infinite loop in i2c_w() of etoms.
V4L/DVB (8414): videodev/cx18: fix get_index bug and error-handling lock-ups
V4L/DVB (8411): videobuf-dma-contig.c: fix 64-bit build for pre-2.6.24 kernels
V4L/DVB (8410): sh_mobile_ceu_camera: fix 64-bit compiler warnings
V4L/DVB (8397): video: convert select VIDEO_ZORAN_ZR36060 into depends on
V4L/DVB (8396): video: Fix Kbuild dependency for VIDEO_IR_I2C
V4L/DVB (8395): saa7134: Fix Kbuild dependency of ir-kbd-i2c
V4L/DVB (8394): ir-common: CodingStyle fix: move EXPORT_SYMBOL_GPL to their proper places
V4L/DVB (8393): media/video: Fix depencencies for VIDEOBUF
V4L/DVB (8392): media/Kconfig: Convert V4L1_COMPAT select into "depends on"
V4L/DVB (8390): videodev: add comment and remove magic number.
V4L/DVB (8389): videodev: simplify get_index()
V4L/DVB (8387): Some cosmetic changes
V4L/DVB (8381): ov7670: fix compile warnings
V4L/DVB (8380): saa7115: use saa7115_auto instead of saa711x as the autodetect driver name.
V4L/DVB (8379): saa7127: Make device detection optional
V4L/DVB (8378): cx18: move cx18_av_vbi_setup to av-core.c and rename to cx18_av_std_setup
V4L/DVB (8377): ivtv/cx18: ensure the default control values are correct
V4L/DVB (8376): cx25840: move cx25840_vbi_setup to core.c and rename to cx25840_std_setup
V4L/DVB (8374): gspca: No conflict of 0c45:6011 with the sn9c102 driver.
...
Diffstat (limited to 'drivers/media')
271 files changed, 47012 insertions, 5099 deletions
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 7a7803b5d49..93ea201f426 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -38,7 +38,6 @@ config VIDEO_ALLOW_V4L1 bool "Enable Video For Linux API 1 (DEPRECATED)" depends on VIDEO_DEV && VIDEO_V4L2_COMMON default VIDEO_DEV && VIDEO_V4L2_COMMON - select VIDEO_V4L1_COMPAT ---help--- Enables drivers based on the legacy V4L1 API. @@ -49,9 +48,9 @@ config VIDEO_ALLOW_V4L1 If you are unsure as to whether this is required, answer Y. config VIDEO_V4L1_COMPAT - bool "Enable Video For Linux API 1 compatible Layer" + bool "Enable Video For Linux API 1 compatible Layer" if !VIDEO_ALLOW_V4L1 depends on VIDEO_DEV - default VIDEO_DEV + default y ---help--- Enables a compatibility API used by most V4L2 devices to allow its usage with legacy applications that supports only V4L1 api. diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index 26650520792..16792a68a44 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -66,7 +66,6 @@ void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, if (ir_codes) memcpy(ir->ir_codes, ir_codes, sizeof(ir->ir_codes)); - dev->keycode = ir->ir_codes; dev->keycodesize = sizeof(IR_KEYTAB_TYPE); dev->keycodemax = IR_KEYTAB_SIZE; @@ -78,6 +77,7 @@ void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, if (repeat) set_bit(EV_REP, dev->evbit); } +EXPORT_SYMBOL_GPL(ir_input_init); void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir) { @@ -86,6 +86,7 @@ void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir) ir_input_key_event(dev,ir); } } +EXPORT_SYMBOL_GPL(ir_input_nokey); void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, u32 ir_key, u32 ir_raw) @@ -104,6 +105,7 @@ void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, ir_input_key_event(dev,ir); } } +EXPORT_SYMBOL_GPL(ir_input_keydown); /* -------------------------------------------------------------------------- */ /* extract mask bits out of data and pack them into the result */ @@ -122,6 +124,7 @@ u32 ir_extract_bits(u32 data, u32 mask) return value; } +EXPORT_SYMBOL_GPL(ir_extract_bits); static int inline getbit(u32 *samples, int bit) { @@ -146,6 +149,7 @@ int ir_dump_samples(u32 *samples, int count) printk("\n"); return 0; } +EXPORT_SYMBOL_GPL(ir_dump_samples); /* decode raw samples, pulse distance coding used by NEC remotes */ int ir_decode_pulsedistance(u32 *samples, int count, int low, int high) @@ -212,6 +216,7 @@ int ir_decode_pulsedistance(u32 *samples, int count, int low, int high) return value; } +EXPORT_SYMBOL_GPL(ir_decode_pulsedistance); /* decode raw samples, biphase coding, used by rc5 for example */ int ir_decode_biphase(u32 *samples, int count, int low, int high) @@ -253,6 +258,7 @@ int ir_decode_biphase(u32 *samples, int count, int low, int high) } return value; } +EXPORT_SYMBOL_GPL(ir_decode_biphase); /* RC5 decoding stuff, moved from bttv-input.c to share it with * saa7134 */ @@ -353,6 +359,7 @@ void ir_rc5_timer_end(unsigned long data) } } } +EXPORT_SYMBOL_GPL(ir_rc5_timer_end); void ir_rc5_timer_keyup(unsigned long data) { @@ -361,21 +368,4 @@ void ir_rc5_timer_keyup(unsigned long data) dprintk(1, "ir-common: key released\n"); ir_input_nokey(ir->dev, &ir->ir); } - -EXPORT_SYMBOL_GPL(ir_input_init); -EXPORT_SYMBOL_GPL(ir_input_nokey); -EXPORT_SYMBOL_GPL(ir_input_keydown); - -EXPORT_SYMBOL_GPL(ir_extract_bits); -EXPORT_SYMBOL_GPL(ir_dump_samples); -EXPORT_SYMBOL_GPL(ir_decode_biphase); -EXPORT_SYMBOL_GPL(ir_decode_pulsedistance); - -EXPORT_SYMBOL_GPL(ir_rc5_timer_end); EXPORT_SYMBOL_GPL(ir_rc5_timer_keyup); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c index 89c7660b85d..d01965e9692 100644 --- a/drivers/media/common/saa7146_core.c +++ b/drivers/media/common/saa7146_core.c @@ -233,7 +233,7 @@ void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt) int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt) { - u32 *cpu; + __le32 *cpu; dma_addr_t dma_addr; cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr); @@ -250,7 +250,7 @@ int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt) int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt, struct scatterlist *list, int sglen ) { - u32 *ptr, fill; + __le32 *ptr, fill; int nr_pages = 0; int i,p; diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c index 9c905399a23..05bde9ccb77 100644 --- a/drivers/media/common/saa7146_hlp.c +++ b/drivers/media/common/saa7146_hlp.c @@ -338,7 +338,7 @@ static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct sa struct saa7146_video_dma *vdma2, u32* clip_format, u32* arbtr_ctrl, enum v4l2_field field) { struct saa7146_vv *vv = dev->vv_data; - u32 *clipping = vv->d_clipping.cpu_addr; + __le32 *clipping = vv->d_clipping.cpu_addr; int width = fh->ov.win.w.width; int height = fh->ov.win.w.height; diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c index 35b01ec40a5..c11da4d09cd 100644 --- a/drivers/media/common/saa7146_i2c.c +++ b/drivers/media/common/saa7146_i2c.c @@ -24,7 +24,7 @@ static inline u32 saa7146_i2c_status(struct saa7146_dev *dev) sent through the saa7146. have a look at the specifications p. 122 ff to understand this. it returns the number of u32s to send, or -1 in case of an error. */ -static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op) +static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, __le32 *op) { int h1, h2; int i, j, addr; @@ -47,7 +47,7 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op) } /* be careful: clear out the i2c-mem first */ - memset(op,0,sizeof(u32)*mem); + memset(op,0,sizeof(__le32)*mem); /* loop through all messages */ for(i = 0; i < num; i++) { @@ -57,16 +57,16 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op) so we have to perform a translation */ addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0); h1 = op_count/3; h2 = op_count%3; - op[h1] |= ( (u8)addr << ((3-h2)*8)); - op[h1] |= (SAA7146_I2C_START << ((3-h2)*2)); + op[h1] |= cpu_to_le32( (u8)addr << ((3-h2)*8)); + op[h1] |= cpu_to_le32(SAA7146_I2C_START << ((3-h2)*2)); op_count++; /* loop through all bytes of message i */ for(j = 0; j < m[i].len; j++) { /* insert the data bytes */ h1 = op_count/3; h2 = op_count%3; - op[h1] |= ( (u32)((u8)m[i].buf[j]) << ((3-h2)*8)); - op[h1] |= ( SAA7146_I2C_CONT << ((3-h2)*2)); + op[h1] |= cpu_to_le32( (u32)((u8)m[i].buf[j]) << ((3-h2)*8)); + op[h1] |= cpu_to_le32( SAA7146_I2C_CONT << ((3-h2)*2)); op_count++; } @@ -75,9 +75,9 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op) /* have a look at the last byte inserted: if it was: ...CONT change it to ...STOP */ h1 = (op_count-1)/3; h2 = (op_count-1)%3; - if ( SAA7146_I2C_CONT == (0x3 & (op[h1] >> ((3-h2)*2))) ) { - op[h1] &= ~(0x2 << ((3-h2)*2)); - op[h1] |= (SAA7146_I2C_STOP << ((3-h2)*2)); + if ( SAA7146_I2C_CONT == (0x3 & (le32_to_cpu(op[h1]) >> ((3-h2)*2))) ) { + op[h1] &= ~cpu_to_le32(0x2 << ((3-h2)*2)); + op[h1] |= cpu_to_le32(SAA7146_I2C_STOP << ((3-h2)*2)); } /* return the number of u32s to send */ @@ -88,7 +88,7 @@ static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op) which bytes were read through the adapter and write them back to the corresponding i2c-message. but instead, we simply write back all bytes. fixme: this could be improved. */ -static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, u32 *op) +static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, __le32 *op) { int i, j; int op_count = 0; @@ -101,7 +101,7 @@ static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, u32 *op) /* loop throgh all bytes of message i */ for(j = 0; j < m[i].len; j++) { /* write back all bytes that could have been read */ - m[i].buf[j] = (op[op_count/3] >> ((3-(op_count%3))*8)); + m[i].buf[j] = (le32_to_cpu(op[op_count/3]) >> ((3-(op_count%3))*8)); op_count++; } } @@ -174,7 +174,7 @@ static int saa7146_i2c_reset(struct saa7146_dev *dev) /* this functions writes out the data-byte 'dword' to the i2c-device. it returns 0 if ok, -1 if the transfer failed, -2 if the transfer failed badly (e.g. address error) */ -static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_delay) +static int saa7146_i2c_writeout(struct saa7146_dev *dev, __le32 *dword, int short_delay) { u32 status = 0, mc2 = 0; int trial = 0; @@ -186,7 +186,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) { saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, I2C_TRANSFER, *dword); + saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); dev->i2c_op = 1; SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); @@ -209,7 +209,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d status = saa7146_read(dev, I2C_STATUS); } else { saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate); - saa7146_write(dev, I2C_TRANSFER, *dword); + saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*dword)); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); /* do not poll for i2c-status before upload is complete */ @@ -282,7 +282,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d } /* read back data, just in case we were reading ... */ - *dword = saa7146_read(dev, I2C_TRANSFER); + *dword = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER)); DEB_I2C(("after: 0x%08x\n",*dword)); return 0; @@ -291,7 +291,7 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries) { int i = 0, count = 0; - u32* buffer = dev->d_i2c.cpu_addr; + __le32 *buffer = dev->d_i2c.cpu_addr; int err = 0; int address_err = 0; int short_delay = 0; @@ -376,7 +376,7 @@ out: /* another bug in revision 0: the i2c-registers get uploaded randomly by other uploads, so we better clear them out before continueing */ if( 0 == dev->revision ) { - u32 zero = 0; + __le32 zero = 0; saa7146_i2c_reset(dev); if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) { INFO(("revision 0 error. this should never happen.\n")); diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 3cbc6ebbe64..a5e62750eea 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -605,8 +605,8 @@ static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *bu struct saa7146_pgtable *pt1 = &buf->pt[0]; struct saa7146_pgtable *pt2 = &buf->pt[1]; struct saa7146_pgtable *pt3 = &buf->pt[2]; - u32 *ptr1, *ptr2, *ptr3; - u32 fill; + __le32 *ptr1, *ptr2, *ptr3; + __le32 fill; int size = buf->fmt->width*buf->fmt->height; int i,p,m1,m2,m3,o1,o2; diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 85482960d01..850d5689b14 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -34,6 +34,7 @@ config MEDIA_TUNER menuconfig MEDIA_TUNER_CUSTOMIZE bool "Customize analog and hybrid tuner modules to build" depends on MEDIA_TUNER + default n help This allows the user to deselect tuner drivers unnecessary for their hardware from the build. Use this option with care diff --git a/drivers/media/common/tuners/tda18271-maps.c b/drivers/media/common/tuners/tda18271-maps.c index 83e7561960c..ab14ceb9e0c 100644 --- a/drivers/media/common/tuners/tda18271-maps.c +++ b/drivers/media/common/tuners/tda18271-maps.c @@ -1,5 +1,5 @@ /* - tda18271-tables.c - driver for the Philips / NXP TDA18271 silicon tuner + tda18271-maps.c - driver for the Philips / NXP TDA18271 silicon tuner Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index 30eb07b7f9e..4dd1d2421cc 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -15,6 +15,7 @@ #include <linux/delay.h> #include <media/tuner.h> #include <linux/mutex.h> +#include <asm/unaligned.h> #include "tuner-i2c.h" #include "tuner-xc2028.h" #include "tuner-xc2028-types.h" @@ -292,10 +293,10 @@ static int load_all_firmwares(struct dvb_frontend *fe) name[sizeof(name) - 1] = 0; p += sizeof(name) - 1; - priv->firm_version = le16_to_cpu(*(__u16 *) p); + priv->firm_version = get_unaligned_le16(p); p += 2; - n_array = le16_to_cpu(*(__u16 *) p); + n_array = get_unaligned_le16(p); p += 2; tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n", @@ -324,26 +325,26 @@ static int load_all_firmwares(struct dvb_frontend *fe) } /* Checks if there's enough bytes to read */ - if (p + sizeof(type) + sizeof(id) + sizeof(size) > endp) { - tuner_err("Firmware header is incomplete!\n"); - goto corrupt; - } + if (endp - p < sizeof(type) + sizeof(id) + sizeof(size)) + goto header; - type = le32_to_cpu(*(__u32 *) p); + type = get_unaligned_le32(p); p += sizeof(type); - id = le64_to_cpu(*(v4l2_std_id *) p); + id = get_unaligned_le64(p); p += sizeof(id); if (type & HAS_IF) { - int_freq = le16_to_cpu(*(__u16 *) p); + int_freq = get_unaligned_le16(p); p += sizeof(int_freq); + if (endp - p < sizeof(size)) + goto header; } - size = le32_to_cpu(*(__u32 *) p); + size = get_unaligned_le32(p); p += sizeof(size); - if ((!size) || (size + p > endp)) { + if (!size || size > endp - p) { tuner_err("Firmware type "); dump_firm_type(type); printk("(%x), id %llx is corrupted " @@ -382,6 +383,8 @@ static int load_all_firmwares(struct dvb_frontend *fe) goto done; +header: + tuner_err("Firmware header is incomplete!\n"); corrupt: rc = -EINVAL; tuner_err("Error: firmware file is corrupted!\n"); diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index 4878d6477a8..5f99de0ad61 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -36,6 +36,10 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +static int xc5000_load_fw_on_attach; +module_param_named(init_fw, xc5000_load_fw_on_attach, int, 0644); +MODULE_PARM_DESC(init_fw, "Load firmware during driver initialization."); + #define dprintk(level,fmt, arg...) if (debug >= level) \ printk(KERN_INFO "%s: " fmt, "xc5000", ## arg) @@ -972,6 +976,9 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, fe->tuner_priv = priv; + if (xc5000_load_fw_on_attach) + xc5000_init(fe); + return fe; } EXPORT_SYMBOL(xc5000_attach); diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index 7b21b49f194..8bc1445bd33 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -21,6 +21,7 @@ source "drivers/media/dvb/dvb-usb/Kconfig" source "drivers/media/dvb/ttusb-budget/Kconfig" source "drivers/media/dvb/ttusb-dec/Kconfig" source "drivers/media/dvb/cinergyT2/Kconfig" +source "drivers/media/dvb/siano/Kconfig" comment "Supported FlexCopII (B2C2) Adapters" depends on DVB_CORE && (PCI || USB) && I2C diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index a7ad0841e6f..d6ba4d19520 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile @@ -2,4 +2,4 @@ # Makefile for the kernel multimedia device drivers. # -obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ +obj-y := dvb-core/ frontends/ ttpci/ ttusb-dec/ ttusb-budget/ b2c2/ bt8xx/ cinergyT2/ dvb-usb/ pluto2/ siano/ diff --git a/drivers/media/dvb/bt8xx/bt878.h b/drivers/media/dvb/bt8xx/bt878.h index 375fd2892a1..d19b59299d7 100644 --- a/drivers/media/dvb/bt8xx/bt878.h +++ b/drivers/media/dvb/bt8xx/bt878.h @@ -128,7 +128,7 @@ struct bt878 { dma_addr_t buf_dma; u32 risc_size; - u32 *risc_cpu; + __le32 *risc_cpu; dma_addr_t risc_dma; u32 risc_pos; diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h index b0d347daae4..eb91fd808c1 100644 --- a/drivers/media/dvb/dvb-core/demux.h +++ b/drivers/media/dvb/dvb-core/demux.h @@ -247,7 +247,7 @@ struct dmx_demux { void* priv; /* Pointer to private data of the API client */ int (*open) (struct dmx_demux* demux); int (*close) (struct dmx_demux* demux); - int (*write) (struct dmx_demux* demux, const char* buf, size_t count); + int (*write) (struct dmx_demux* demux, const char __user *buf, size_t count); int (*allocate_ts_feed) (struct dmx_demux* demux, struct dmx_ts_feed** feed, dmx_ts_cb callback); diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index df5bef6a251..1cf9fcb6f51 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c @@ -96,7 +96,7 @@ static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src, if (avail > todo) avail = todo; - ret = dvb_ringbuffer_read(src, (u8 *)buf, avail, 1); + ret = dvb_ringbuffer_read_user(src, buf, avail); if (ret < 0) break; diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c index 588fbe105c2..8e5dd7b1f03 100644 --- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c @@ -1357,7 +1357,7 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca, idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen); while (idx != -1) { - dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0); + dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2); if (connection_id == -1) connection_id = hdr[0]; if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) { @@ -1438,7 +1438,7 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf, goto exit; } - dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0); + dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2); if (connection_id == -1) connection_id = hdr[0]; if (hdr[0] == connection_id) { @@ -1449,8 +1449,8 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf, fraglen -= 2; } - if ((status = dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 2, - (u8 *)buf + pktlen, fraglen, 1)) < 0) { + if ((status = dvb_ringbuffer_pkt_read_user(&ca->slot_info[slot].rx_buffer, idx, 2, + buf + pktlen, fraglen)) < 0) { goto exit; } pktlen += fraglen; diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index 934e15fffc5..e2eca0b1fe7 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -1056,16 +1056,27 @@ static int dvbdmx_close(struct dmx_demux *demux) return 0; } -static int dvbdmx_write(struct dmx_demux *demux, const char *buf, size_t count) +static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count) { struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; + void *p; if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE)) return -EINVAL; - if (mutex_lock_interruptible(&dvbdemux->mutex)) + p = kmalloc(count, GFP_USER); + if (!p) + return -ENOMEM; + if (copy_from_user(p, buf, count)) { + kfree(p); + return -EFAULT; + } + if (mutex_lock_interruptible(&dvbdemux->mutex)) { + kfree(p); return -ERESTARTSYS; - dvb_dmx_swfilter(dvbdemux, (u8 *)buf, count); + } + dvb_dmx_swfilter(dvbdemux, p, count); + kfree(p); mutex_unlock(&dvbdemux->mutex); if (signal_pending(current)) diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index c2c033722a9..c93019ca519 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -606,7 +606,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (priv->ule_dbit) { /* Set D-bit for CRC32 verification, * if it was set originally. */ - ulen |= 0x0080; + ulen |= htons(0x8000); } ule_crc = iov_crc32(ule_crc, iov, 3); diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c index 872985b7912..584bbd194dc 100644 --- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c +++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c @@ -107,35 +107,43 @@ void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf) wake_up(&rbuf->queue); } - - -ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len, int usermem) +ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len) { size_t todo = len; size_t split; split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; if (split > 0) { - if (!usermem) - memcpy(buf, rbuf->data+rbuf->pread, split); - else - if (copy_to_user(buf, rbuf->data+rbuf->pread, split)) - return -EFAULT; + if (copy_to_user(buf, rbuf->data+rbuf->pread, split)) + return -EFAULT; buf += split; todo -= split; rbuf->pread = 0; } - if (!usermem) - memcpy(buf, rbuf->data+rbuf->pread, todo); - else - if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) - return -EFAULT; + if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) + return -EFAULT; rbuf->pread = (rbuf->pread + todo) % rbuf->size; return len; } +void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len) +{ + size_t todo = len; + size_t split; + + split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; + if (split > 0) { + memcpy(buf, rbuf->data+rbuf->pread, split); + buf += split; + todo -= split; + rbuf->pread = 0; + } + memcpy(buf, rbuf->data+rbuf->pread, todo); + + rbuf->pread = (rbuf->pread + todo) % rbuf->size; +} ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len) @@ -171,8 +179,8 @@ ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t le return status; } -ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, - int offset, u8* buf, size_t len, int usermem) +ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, + int offset, u8 __user *buf, size_t len) { size_t todo; size_t split; @@ -187,21 +195,40 @@ ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, todo = len; split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; if (split > 0) { - if (!usermem) - memcpy(buf, rbuf->data+idx, split); - else - if (copy_to_user(buf, rbuf->data+idx, split)) - return -EFAULT; + if (copy_to_user(buf, rbuf->data+idx, split)) + return -EFAULT; buf += split; todo -= split; idx = 0; } - if (!usermem) - memcpy(buf, rbuf->data+idx, todo); - else - if (copy_to_user(buf, rbuf->data+idx, todo)) - return -EFAULT; + if (copy_to_user(buf, rbuf->data+idx, todo)) + return -EFAULT; + + return len; +} +ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, + int offset, u8* buf, size_t len) +{ + size_t todo; + size_t split; + size_t pktlen; + + pktlen = rbuf->data[idx] << 8; + pktlen |= rbuf->data[(idx + 1) % rbuf->size]; + if (offset > pktlen) return -EINVAL; + if ((offset + len) > pktlen) len = pktlen - offset; + + idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; + todo = len; + split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; + if (split > 0) { + memcpy(buf, rbuf->data+idx, split); + buf += split; + todo -= split; + idx = 0; + } + memcpy(buf, rbuf->data+idx, todo); return len; } @@ -266,5 +293,6 @@ EXPORT_SYMBOL(dvb_ringbuffer_empty); EXPORT_SYMBOL(dvb_ringbuffer_free); EXPORT_SYMBOL(dvb_ringbuffer_avail); EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup); +EXPORT_SYMBOL(dvb_ringbuffer_read_user); EXPORT_SYMBOL(dvb_ringbuffer_read); EXPORT_SYMBOL(dvb_ringbuffer_write); diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h index 89082626296..41f04dae69b 100644 --- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h +++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h @@ -61,7 +61,7 @@ struct dvb_ringbuffer { ** *** read min. 1000, max. <bufsize> bytes *** ** avail = dvb_ringbuffer_avail(rbuf); ** if (avail >= 1000) -** count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize), 0); +** count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize)); ** else ** ... ** @@ -114,8 +114,10 @@ extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf); ** <usermem> specifies whether <buf> resides in user space ** returns number of bytes transferred or -EFAULT */ -extern ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, - size_t len, int usermem); +extern ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, + u8 __user *buf, size_t len); +extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, + u8 *buf, size_t len); /* write routines & macros */ @@ -157,8 +159,10 @@ extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, * <usermem> Set to 1 if <buf> is in userspace. * returns Number of bytes read, or -EFAULT. */ +extern ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, + int offset, u8 __user *buf, size_t len); extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, - int offset, u8* buf, size_t len, int usermem); + int offset, u8 *buf, size_t len); /** * Dispose of a packet in the ring buffer. diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index f00a0eb4042..a577c0f89f6 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -76,6 +76,7 @@ config DVB_USB_DIB0700 select DVB_DIB3000MC select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE select DVB_TUNER_DIB0070 help Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The @@ -107,6 +108,8 @@ config DVB_USB_CXUSB select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE help Say Y here to support the Conexant USB2.0 hybrid reference design. Currently, only DVB and ATSC modes are supported, analog mode @@ -120,6 +123,8 @@ config DVB_USB_M920X depends on DVB_USB select DVB_MT352 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE + select DVB_TDA1004X if !DVB_FE_CUSTOMISE help Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. Currently, only devices with a product id of @@ -241,3 +246,13 @@ config DVB_USB_AF9005_REMOTE Say Y here to support the default remote control decoding for the Afatech AF9005 based receiver. +config DVB_USB_ANYSEE + tristate "Anysee DVB-T/C USB2.0 support" + depends on DVB_USB + select DVB_PLL if !DVB_FE_CUSTOMISE + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE + help + Say Y here to support the Anysee E30, Anysee E30 Plus or + Anysee E30 C Plus DVB USB2.0 receiver. diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index c6511a6c0ab..44c11e45e56 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -61,6 +61,9 @@ obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o dvb-usb-af9005-remote-objs = af9005-remote.o obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o +dvb-usb-anysee-objs = anysee.o +obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c new file mode 100644 index 00000000000..adfd4fc82ef --- /dev/null +++ b/drivers/media/dvb/dvb-usb/anysee.c @@ -0,0 +1,553 @@ +/* + * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * TODO: + * - add smart card reader support for Conditional Access (CA) + * + * Card reader in Anysee is nothing more than ISO 7816 card reader. + * There is no hardware CAM in any Anysee device sold. + * In my understanding it should be implemented by making own module + * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This + * module registers serial interface that can be used to communicate + * with any ISO 7816 smart card. + * + * Any help according to implement serial smart card reader support + * is highly welcome! + */ + +#include "anysee.h" +#include "tda1002x.h" +#include "mt352.h" +#include "mt352_priv.h" +#include "zl10353.h" + +/* debug */ +static int dvb_usb_anysee_debug; +module_param_named(debug, dvb_usb_anysee_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct mutex anysee_usb_mutex; + +static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, + u8 *rbuf, u8 rlen) +{ + struct anysee_state *state = d->priv; + int act_len, ret; + u8 buf[64]; + + if (slen > sizeof(buf)) + slen = sizeof(buf); + memcpy(&buf[0], sbuf, slen); + buf[60] = state->seq++; + + if (mutex_lock_interruptible(&anysee_usb_mutex) < 0) + return -EAGAIN; + + /* We need receive one message more after dvb_usb_generic_rw due + to weird transaction flow, which is 1 x send + 2 x receive. */ + ret = dvb_usb_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf), 0); + + if (!ret) { + /* receive 2nd answer */ + ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint), buf, sizeof(buf), + &act_len, 2000); + if (ret) + err("%s: recv bulk message failed: %d", __func__, ret); + else { + deb_xfer("<<< "); + debug_dump(buf, act_len, deb_xfer); + } + } + + /* read request, copy returned data to return buf */ + if (!ret && rbuf && rlen) + memcpy(rbuf, buf, rlen); + + mutex_unlock(&anysee_usb_mutex); + + return ret; +} + +static int anysee_read_reg(struct dvb_usb_device *d, u16 reg, u8 *val) +{ + u8 buf[] = {CMD_REG_READ, reg >> 8, reg & 0xff, 0x01}; + int ret; + ret = anysee_ctrl_msg(d, buf, sizeof(buf), val, 1); + deb_info("%s: reg:%04x val:%02x\n", __func__, reg, *val); + return ret; +} + +static int anysee_write_reg(struct dvb_usb_device *d, u16 reg, u8 val) +{ + u8 buf[] = {CMD_REG_WRITE, reg >> 8, reg & 0xff, 0x01, val}; + deb_info("%s: reg:%04x val:%02x\n", __func__, reg, val); + return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +} + +static int anysee_get_hw_info(struct dvb_usb_device *d, u8 *id) +{ + u8 buf[] = {CMD_GET_HW_INFO}; + return anysee_ctrl_msg(d, buf, sizeof(buf), id, 3); +} + +static int anysee_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + u8 buf[] = {CMD_STREAMING_CTRL, (u8)onoff, 0x00}; + deb_info("%s: onoff:%02x\n", __func__, onoff); + return anysee_ctrl_msg(adap->dev, buf, sizeof(buf), NULL, 0); +} + +static int anysee_led_ctrl(struct dvb_usb_device *d, u8 mode, u8 interval) +{ + u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x01, mode, interval}; + deb_info("%s: state:%02x interval:%02x\n", __func__, mode, interval); + return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +} + +static int anysee_ir_ctrl(struct dvb_usb_device *d, u8 onoff) +{ + u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x02, onoff}; + deb_info("%s: onoff:%02x\n", __func__, onoff); + return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +} + +static int anysee_init(struct dvb_usb_device *d) +{ + int ret; + /* LED light */ + ret = anysee_led_ctrl(d, 0x01, 0x03); + if (ret) + return ret; + + /* enable IR */ + ret = anysee_ir_ctrl(d, 1); + if (ret) + return ret; + + return 0; +} + +/* I2C */ +static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret, inc, i = 0; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + while (i < num) { + if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { + u8 buf[6]; + buf[0] = CMD_I2C_READ; + buf[1] = msg[i].addr + 1; + buf[2] = msg[i].buf[0]; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x01; + ret = anysee_ctrl_msg(d, buf, sizeof(buf), msg[i+1].buf, + msg[i+1].len); + inc = 2; + } else { + u8 buf[4+msg[i].len]; + buf[0] = CMD_I2C_WRITE; + buf[1] = msg[i].addr; + buf[2] = msg[i].len; + buf[3] = 0x01; + memcpy(&buf[4], msg[i].buf, msg[i].len); + ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); + inc = 1; + } + if (ret) + return ret; + + i += inc; + } + + mutex_unlock(&d->i2c_mutex); + + return i; +} + +static u32 anysee_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm anysee_i2c_algo = { + .master_xfer = anysee_master_xfer, + .functionality = anysee_i2c_func, +}; + +static int anysee_mt352_demod_init(struct dvb_frontend *fe) +{ + static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; + static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + + return 0; +} + +/* Callbacks for DVB USB */ +static struct tda10023_config anysee_tda10023_config = { + .demod_address = 0x1a, + .invert = 0, + .xtal = 16000000, + .pll_m = 11, + .pll_p = 3, + .pll_n = 1, + .output_mode = TDA10023_OUTPUT_MODE_PARALLEL_C, + .deltaf = 0xfeeb, +}; + +static struct mt352_config anysee_mt352_config = { + .demod_address = 0x1e, + .demod_init = anysee_mt352_demod_init, +}; + +static struct zl10353_config anysee_zl10353_config = { + .demod_address = 0x1e, + .parallel_ts = 1, +}; + +static int anysee_frontend_attach(struct dvb_usb_adapter *adap) +{ + int ret; + struct anysee_state *state = adap->dev->priv; + u8 hw_info[3]; + u8 io_d; /* IO port D */ + + /* check which hardware we have + We must do this call two times to get reliable values (hw bug). */ + ret = anysee_get_hw_info(adap->dev, hw_info); + if (ret) + return ret; + ret = anysee_get_hw_info(adap->dev, hw_info); + if (ret) + return ret; + + /* Meaning of these info bytes are guessed. */ + info("firmware version:%d.%d.%d hardware id:%d", + 0, hw_info[1], hw_info[2], hw_info[0]); + + ret = anysee_read_reg(adap->dev, 0xb0, &io_d); /* IO port D */ + if (ret) + return ret; + deb_info("%s: IO port D:%02x\n", __func__, io_d); + + /* Select demod using trial and error method. */ + + /* Try to attach demodulator in following order: + model demod hw firmware + 1. E30 MT352 02 0.2.1 + 2. E30 ZL10353 02 0.2.1 + 3. E30 Plus ZL10353 06 0.1.0 + 4. E30C Plus TDA10023 0a 0.1.0 rev 0.2 + 4. E30C Plus TDA10023 0f 0.1.2 rev 0.4 + */ + + /* Zarlink MT352 DVB-T demod inside of Samsung DNOS404ZH102A NIM */ + adap->fe = dvb_attach(mt352_attach, &anysee_mt352_config, + &adap->dev->i2c_adap); + if (adap->fe != NULL) { + state->tuner = DVB_PLL_THOMSON_DTT7579; + return 0; + } + + /* Zarlink ZL10353 DVB-T demod inside of Samsung DNOS404ZH103A NIM */ + adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config, + &adap->dev->i2c_adap); + if (adap->fe != NULL) { + state->tuner = DVB_PLL_THOMSON_DTT7579; + return 0; + } + + /* connect demod on IO port D for TDA10023 & ZL10353 */ + ret = anysee_write_reg(adap->dev, 0xb0, 0x25); + if (ret) + return ret; + + /* Zarlink ZL10353 DVB-T demod inside of Samsung DNOS404ZH103A NIM */ + adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config, + &adap->dev->i2c_adap); + if (adap->fe != NULL) { + state->tuner = DVB_PLL_THOMSON_DTT7579; + return 0; + } + + /* IO port E - E30C rev 0.4 board requires this */ + ret = anysee_write_reg(adap->dev, 0xb1, 0xa7); + if (ret) + return ret; + + /* Philips TDA10023 DVB-C demod */ + adap->fe = dvb_attach(tda10023_attach, &anysee_tda10023_config, + &adap->dev->i2c_adap, 0x48); + if (adap->fe != NULL) { + state->tuner = DVB_PLL_SAMSUNG_DTOS403IH102A; + return 0; + } + + /* return IO port D to init value for safe */ + ret = anysee_write_reg(adap->dev, 0xb0, io_d); + if (ret) + return ret; + + err("Unkown Anysee version: %02x %02x %02x. "\ + "Please report the <linux-dvb@linuxtv.org>.", + hw_info[0], hw_info[1], hw_info[2]); + + return -ENODEV; +} + +static int anysee_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct anysee_state *state = adap->dev->priv; + deb_info("%s: \n", __func__); + + switch (state->tuner) { + case DVB_PLL_THOMSON_DTT7579: + /* Thomson dtt7579 (not sure) PLL inside of: + Samsung DNOS404ZH102A NIM + Samsung DNOS404ZH103A NIM */ + dvb_attach(dvb_pll_attach, adap->fe, 0x61, + NULL, DVB_PLL_THOMSON_DTT7579); + break; + case DVB_PLL_SAMSUNG_DTOS403IH102A: + /* Unknown PLL inside of Samsung DTOS403IH102A tuner module */ + dvb_attach(dvb_pll_attach, adap->fe, 0xc0, + &adap->dev->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A); + break; + } + + return 0; +} + +static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + u8 buf[] = {CMD_GET_IR_CODE}; + struct dvb_usb_rc_key *keymap = d->props.rc_key_map; + u8 ircode[2]; + int i, ret; + + ret = anysee_ctrl_msg(d, buf, sizeof(buf), &ircode[0], 2); + if (ret) + return ret; + + *event = 0; + *state = REMOTE_NO_KEY_PRESSED; + + for (i = 0; i < d->props.rc_key_map_size; i++) { + if (keymap[i].custom == ircode[0] && + keymap[i].data == ircode[1]) { + *event = keymap[i].event; + *state = REMOTE_KEY_PRESSED; + return 0; + } + } + return 0; +} + +static struct dvb_usb_rc_key anysee_rc_keys[] = { + { 0x01, 0x00, KEY_0 }, + { 0x01, 0x01, KEY_1 }, + { 0x01, 0x02, KEY_2 }, + { 0x01, 0x03, KEY_3 }, + { 0x01, 0x04, KEY_4 }, + { 0x01, 0x05, KEY_5 }, + { 0x01, 0x06, KEY_6 }, + { 0x01, 0x07, KEY_7 }, + { 0x01, 0x08, KEY_8 }, + { 0x01, 0x09, KEY_9 }, + { 0x01, 0x0a, KEY_POWER }, + { 0x01, 0x0b, KEY_DOCUMENTS }, /* * */ + { 0x01, 0x19, KEY_FAVORITES }, + { 0x01, 0x20, KEY_SLEEP }, + { 0x01, 0x21, KEY_MODE }, /* 4:3 / 16:9 select */ + { 0x01, 0x22, KEY_ZOOM }, + { 0x01, 0x47, KEY_TEXT }, + { 0x01, 0x16, KEY_TV }, /* TV / radio select */ + { 0x01, 0x1e, KEY_LANGUAGE }, /* Second Audio Program */ + { 0x01, 0x1a, KEY_SUBTITLE }, + { 0x01, 0x1b, KEY_CAMERA }, /* screenshot */ + { 0x01, 0x42, KEY_MUTE }, + { 0x01, 0x0e, KEY_MENU }, + { 0x01, 0x0f, KEY_EPG }, + { 0x01, 0x17, KEY_INFO }, + { 0x01, 0x10, KEY_EXIT }, + { 0x01, 0x13, KEY_VOLUMEUP }, + { 0x01, 0x12, KEY_VOLUMEDOWN }, + { 0x01, 0x11, KEY_CHANNELUP }, + { 0x01, 0x14, KEY_CHANNELDOWN }, + { 0x01, 0x15, KEY_OK }, + { 0x01, 0x1d, KEY_RED }, + { 0x01, 0x1f, KEY_GREEN }, + { 0x01, 0x1c, KEY_YELLOW }, + { 0x01, 0x44, KEY_BLUE }, + { 0x01, 0x0c, KEY_SHUFFLE }, /* snapshot */ + { 0x01, 0x48, KEY_STOP }, + { 0x01, 0x50, KEY_PLAY }, + { 0x01, 0x51, KEY_PAUSE }, + { 0x01, 0x49, KEY_RECORD }, + { 0x01, 0x18, KEY_PREVIOUS }, /* |<< */ + { 0x01, 0x0d, KEY_NEXT }, /* >>| */ + { 0x01, 0x24, KEY_PROG1 }, /* F1 */ + { 0x01, 0x25, KEY_PROG2 }, /* F2 */ +}; + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties anysee_properties; + +static int anysee_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + struct usb_host_interface *alt; + int ret; + + mutex_init(&anysee_usb_mutex); + + /* There is one interface with two alternate settings. + Alternate setting 0 is for bulk transfer. + Alternate setting 1 is for isochronous transfer. + We use bulk transfer (alternate setting 0). */ + if (intf->num_altsetting < 1) + return -ENODEV; + + ret = dvb_usb_device_init(intf, &anysee_properties, THIS_MODULE, &d, + adapter_nr); + if (ret) + return ret; + + alt = usb_altnum_to_altsetting(intf, 0); + if (alt == NULL) { + deb_info("%s: no alt found!\n", __func__); + return -ENODEV; + } + + ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); + if (ret) + return ret; + + if (d) + ret = anysee_init(d); + + return ret; +} + +static struct usb_device_id anysee_table [] = { + { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ANYSEE) }, + { USB_DEVICE(USB_VID_AMT, USB_PID_ANYSEE) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, anysee_table); + +static struct dvb_usb_device_properties anysee_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = sizeof(struct anysee_state), + + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = anysee_streaming_ctrl, + .frontend_attach = anysee_frontend_attach, + .tuner_attach = anysee_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + } + }, + + .rc_key_map = anysee_rc_keys, + .rc_key_map_size = ARRAY_SIZE(anysee_rc_keys), + .rc_query = anysee_rc_query, + .rc_interval = 200, /* windows driver uses 500ms */ + + .i2c_algo = &anysee_i2c_algo, + + .generic_bulk_ctrl_endpoint = 1, + + .num_device_descs = 1, + .devices = { + { + .name = "Anysee DVB USB2.0", + .cold_ids = {NULL}, + .warm_ids = {&anysee_table[0], + &anysee_table[1], NULL}, + }, + } +}; + +static struct usb_driver anysee_driver = { + .name = "dvb_usb_anysee", + .probe = anysee_probe, + .disconnect = dvb_usb_device_exit, + .id_table = anysee_table, +}; + +/* module stuff */ +static int __init anysee_module_init(void) +{ + int ret; + + ret = usb_register(&anysee_driver); + if (ret) + err("%s: usb_register failed. Error number %d", __func__, ret); + + return ret; +} + +static void __exit anysee_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&anysee_driver); +} + +module_init(anysee_module_init); +module_exit(anysee_module_exit); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Driver Anysee E30 DVB-C & DVB-T USB2.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/anysee.h b/drivers/media/dvb/dvb-usb/anysee.h new file mode 100644 index 00000000000..7ca01ff6e13 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/anysee.h @@ -0,0 +1,304 @@ +/* + * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver + * + * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * TODO: + * - add smart card reader support for Conditional Access (CA) + * + * Card reader in Anysee is nothing more than ISO 7816 card reader. + * There is no hardware CAM in any Anysee device sold. + * In my understanding it should be implemented by making own module + * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This + * module registers serial interface that can be used to communicate + * with any ISO 7816 smart card. + * + * Any help according to implement serial smart card reader support + * is highly welcome! + */ + +#ifndef _DVB_USB_ANYSEE_H_ +#define _DVB_USB_ANYSEE_H_ + +#define DVB_USB_LOG_PREFIX "anysee" +#include "dvb-usb.h" + +#define deb_info(args...) dprintk(dvb_usb_anysee_debug, 0x01, args) +#define deb_xfer(args...) dprintk(dvb_usb_anysee_debug, 0x02, args) +#define deb_rc(args...) dprintk(dvb_usb_anysee_debug, 0x04, args) +#define deb_reg(args...) dprintk(dvb_usb_anysee_debug, 0x08, args) +#define deb_i2c(args...) dprintk(dvb_usb_anysee_debug, 0x10, args) +#define deb_fw(args...) dprintk(dvb_usb_anysee_debug, 0x20, args) + +enum cmd { + CMD_I2C_READ = 0x33, + CMD_I2C_WRITE = 0x31, + CMD_REG_READ = 0xb0, + CMD_REG_WRITE = 0xb1, + CMD_STREAMING_CTRL = 0x12, + CMD_LED_AND_IR_CTRL = 0x16, + CMD_GET_IR_CODE = 0x41, + CMD_GET_HW_INFO = 0x19, + CMD_SMARTCARD = 0x34, +}; + +struct anysee_state { + u8 tuner; + u8 seq; +}; + +#endif + +/*************************************************************************** + * USB API description (reverse engineered) + *************************************************************************** + +Transaction flow: +================= +BULK[00001] >>> REQUEST PACKET 64 bytes +BULK[00081] <<< REPLY PACKET #1 64 bytes (PREVIOUS TRANSACTION REPLY) +BULK[00081] <<< REPLY PACKET #2 64 bytes (CURRENT TRANSACTION REPLY) + +General reply packet(s) are always used if not own reply defined. + +============================================================================ +| 00-63 | GENERAL REPLY PACKET #1 (PREVIOUS REPLY) +============================================================================ +| 00 | reply data (if any) from previous transaction +| | Just same reply packet as returned during previous transaction. +| | Needed only if reply is missed in previous transaction. +| | Just skip normally. +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | GENERAL REPLY PACKET #2 (CURRENT REPLY) +============================================================================ +| 00 | reply data (if any) +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | I2C WRITE REQUEST PACKET +============================================================================ +| 00 | 0x31 I2C write command +---------------------------------------------------------------------------- +| 01 | i2c address +---------------------------------------------------------------------------- +| 02 | data length +| | 0x02 (for typical I2C reg / val pair) +---------------------------------------------------------------------------- +| 03 | 0x01 +---------------------------------------------------------------------------- +| 04- | data +---------------------------------------------------------------------------- +| -59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | I2C READ REQUEST PACKET +============================================================================ +| 00 | 0x33 I2C read command +---------------------------------------------------------------------------- +| 01 | i2c address + 1 +---------------------------------------------------------------------------- +| 02 | register +---------------------------------------------------------------------------- +| 03 | 0x00 +---------------------------------------------------------------------------- +| 04 | 0x00 +---------------------------------------------------------------------------- +| 05 | 0x01 +---------------------------------------------------------------------------- +| 06-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | USB CONTROLLER REGISTER WRITE REQUEST PACKET +============================================================================ +| 00 | 0xb1 register write command +---------------------------------------------------------------------------- +| 01-02 | register +---------------------------------------------------------------------------- +| 03 | 0x01 +---------------------------------------------------------------------------- +| 04 | value +---------------------------------------------------------------------------- +| 05-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | USB CONTROLLER REGISTER READ REQUEST PACKET +============================================================================ +| 00 | 0xb0 register read command +---------------------------------------------------------------------------- +| 01-02 | register +---------------------------------------------------------------------------- +| 03 | 0x01 +---------------------------------------------------------------------------- +| 04-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | LED CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x16 LED and IR control command +---------------------------------------------------------------------------- +| 01 | 0x01 (LED) +---------------------------------------------------------------------------- +| 03 | 0x00 blink +| | 0x01 lights continuously +---------------------------------------------------------------------------- +| 04 | blink interval +| | 0x00 fastest (looks like LED lights continuously) +| | 0xff slowest +---------------------------------------------------------------------------- +| 05-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | IR CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x16 LED and IR control command +---------------------------------------------------------------------------- +| 01 | 0x02 (IR) +---------------------------------------------------------------------------- +| 03 | 0x00 IR disabled +| | 0x01 IR enabled +---------------------------------------------------------------------------- +| 04-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | STREAMING CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x12 streaming control command +---------------------------------------------------------------------------- +| 01 | 0x00 streaming disabled +| | 0x01 streaming enabled +---------------------------------------------------------------------------- +| 02 | 0x00 +---------------------------------------------------------------------------- +| 03-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | REMOTE CONTROL REQUEST PACKET +============================================================================ +| 00 | 0x41 remote control command +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | REMOTE CONTROL REPLY PACKET +============================================================================ +| 00 | 0x00 code not received +| | 0x01 code received +---------------------------------------------------------------------------- +| 01 | remote control code +---------------------------------------------------------------------------- +| 02-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | GET HARDWARE INFO REQUEST PACKET +============================================================================ +| 00 | 0x19 get hardware info command +---------------------------------------------------------------------------- +| 01-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | GET HARDWARE INFO REPLY PACKET +============================================================================ +| 00 | hardware id +---------------------------------------------------------------------------- +| 01-02 | firmware version +---------------------------------------------------------------------------- +| 03-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +============================================================================ +| 00-63 | SMART CARD READER PACKET +============================================================================ +| 00 | 0x34 smart card reader command +---------------------------------------------------------------------------- +| xx | +---------------------------------------------------------------------------- +| xx-59 | don't care +---------------------------------------------------------------------------- +| 60 | packet sequence number +---------------------------------------------------------------------------- +| 61-63 | don't care +---------------------------------------------------------------------------- + +*/ diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c index 2ccb90fa60c..eb34cc3894e 100644 --- a/drivers/media/dvb/dvb-usb/au6610.c +++ b/drivers/media/dvb/dvb-usb/au6610.c @@ -1,24 +1,31 @@ -/* DVB USB compliant linux driver for Sigmatek DVB-110 DVB-T USB2.0 receiver +/* + * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0. * * Copyright (C) 2006 Antti Palosaari <crope@iki.fi> * - * 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, version 2. + * 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. * - * see Documentation/dvb/README.dvb-usb for more information + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "au6610.h" - #include "zl10353.h" #include "qt1010.h" /* debug */ static int dvb_usb_au6610_debug; module_param_named(debug, dvb_usb_au6610_debug, int, 0644); -MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); - +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr, @@ -42,9 +49,8 @@ static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr, } ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation, - USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, usb_buf, - sizeof(usb_buf), AU6610_USB_TIMEOUT); - + USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, + usb_buf, sizeof(usb_buf), AU6610_USB_TIMEOUT); if (ret < 0) return ret; @@ -116,15 +122,6 @@ static struct i2c_algorithm au6610_i2c_algo = { }; /* Callbacks for DVB USB */ -static int au6610_identify_state(struct usb_device *udev, - struct dvb_usb_device_properties *props, - struct dvb_usb_device_description **desc, - int *cold) -{ - *cold = 0; - return 0; -} - static struct zl10353_config au6610_zl10353_config = { .demod_address = 0x0f, .no_tuner = 1, @@ -133,12 +130,12 @@ static struct zl10353_config au6610_zl10353_config = { static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap) { - if ((adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config, - &adap->dev->i2c_adap)) != NULL) { - return 0; - } + adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config, + &adap->dev->i2c_adap); + if (adap->fe == NULL) + return -ENODEV; - return -EIO; + return 0; } static struct qt1010_config au6610_qt1010_config = { @@ -171,7 +168,7 @@ static int au6610_probe(struct usb_interface *intf, alt = usb_altnum_to_altsetting(intf, AU6610_ALTSETTING); if (alt == NULL) { - deb_rc("no alt found!\n"); + deb_info("%s: no alt found!\n", __func__); return -ENODEV; } ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, @@ -181,18 +178,19 @@ static int au6610_probe(struct usb_interface *intf, return ret; } - static struct usb_device_id au6610_table [] = { { USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, au6610_table); +MODULE_DEVICE_TABLE(usb, au6610_table); static struct dvb_usb_device_properties au6610_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, - .size_of_priv = 0, - .identify_state = au6610_identify_state, + + .size_of_priv = 0, + .num_adapters = 1, .adapter = { { @@ -206,20 +204,22 @@ static struct dvb_usb_device_properties au6610_properties = { .u = { .isoc = { .framesperurb = 40, - .framesize = 942, /* maximum packet size */ - .interval = 1.25, /* 125 us */ + .framesize = 942, + .interval = 1, } } }, } }, + .i2c_algo = &au6610_i2c_algo, + .num_device_descs = 1, .devices = { { - "Sigmatek DVB-110 DVB-T USB2.0", - { &au6610_table[0], NULL }, - { NULL }, + .name = "Sigmatek DVB-110 DVB-T USB2.0", + .cold_ids = {NULL}, + .warm_ids = {&au6610_table[0], NULL}, }, } }; @@ -236,12 +236,11 @@ static int __init au6610_module_init(void) { int ret; - if ((ret = usb_register(&au6610_driver))) { + ret = usb_register(&au6610_driver); + if (ret) err("usb_register failed. Error number %d", ret); - return ret; - } - return 0; + return ret; } static void __exit au6610_module_exit(void) @@ -250,10 +249,10 @@ static void __exit au6610_module_exit(void) usb_deregister(&au6610_driver); } -module_init (au6610_module_init); -module_exit (au6610_module_exit); +module_init(au6610_module_init); +module_exit(au6610_module_exit); MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); -MODULE_DESCRIPTION("Driver Sigmatek DVB-110 DVB-T USB2.0 / AU6610"); +MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/au6610.h b/drivers/media/dvb/dvb-usb/au6610.h index 4161b054c71..7849abe2c61 100644 --- a/drivers/media/dvb/dvb-usb/au6610.h +++ b/drivers/media/dvb/dvb-usb/au6610.h @@ -1,10 +1,30 @@ +/* + * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0. + * + * Copyright (C) 2006 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + #ifndef _DVB_USB_AU6610_H_ #define _DVB_USB_AU6610_H_ #define DVB_USB_LOG_PREFIX "au6610" #include "dvb-usb.h" -#define deb_rc(args...) dprintk(dvb_usb_au6610_debug,0x01,args) +#define deb_info(args...) dprintk(dvb_usb_au6610_debug, 0x01, args) #define AU6610_REQ_I2C_WRITE 0x14 #define AU6610_REQ_I2C_READ 0x13 diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 0286156704f..578afce6884 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -35,6 +35,7 @@ #include "zl10353.h" #include "tuner-xc2028.h" #include "tuner-simple.h" +#include "mxl5005s.h" /* debug */ static int dvb_usb_cxusb_debug; @@ -43,9 +44,8 @@ MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_ST DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -#define deb_info(args...) dprintk(dvb_usb_cxusb_debug,0x01,args) -#define deb_i2c(args...) if (d->udev->descriptor.idVendor == USB_VID_MEDION) \ - dprintk(dvb_usb_cxusb_debug,0x01,args) +#define deb_info(args...) dprintk(dvb_usb_cxusb_debug, 0x03, args) +#define deb_i2c(args...) dprintk(dvb_usb_cxusb_debug, 0x02, args) static int cxusb_ctrl_msg(struct dvb_usb_device *d, u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen) @@ -202,6 +202,46 @@ static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) return cxusb_ctrl_msg(d, CMD_POWER_OFF, &b, 1, NULL, 0); } +static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + int ret; + if (!onoff) + return cxusb_ctrl_msg(d, CMD_POWER_OFF, NULL, 0, NULL, 0); + if (d->state == DVB_USB_STATE_INIT && + usb_set_interface(d->udev, 0, 0) < 0) + err("set interface failed"); + do; while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) && + !(ret = cxusb_ctrl_msg(d, 0x15, NULL, 0, NULL, 0)) && + !(ret = cxusb_ctrl_msg(d, 0x17, NULL, 0, NULL, 0)) && 0); + if (!ret) { + /* FIXME: We don't know why, but we need to configure the + * lgdt3303 with the register settings below on resume */ + int i; + u8 buf, bufs[] = { + 0x0e, 0x2, 0x00, 0x7f, + 0x0e, 0x2, 0x02, 0xfe, + 0x0e, 0x2, 0x02, 0x01, + 0x0e, 0x2, 0x00, 0x03, + 0x0e, 0x2, 0x0d, 0x40, + 0x0e, 0x2, 0x0e, 0x87, + 0x0e, 0x2, 0x0f, 0x8e, + 0x0e, 0x2, 0x10, 0x01, + 0x0e, 0x2, 0x14, 0xd7, + 0x0e, 0x2, 0x47, 0x88, + }; + msleep(20); + for (i = 0; i < sizeof(bufs)/sizeof(u8); i += 4/sizeof(u8)) { + ret = cxusb_ctrl_msg(d, CMD_I2C_WRITE, + bufs+i, 4, &buf, 1); + if (ret) + break; + if (buf != 0x8) + return -EREMOTEIO; + } + } + return ret; +} + static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff) { u8 b = 0; @@ -233,6 +273,16 @@ static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) return 0; } +static int cxusb_aver_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + if (onoff) + cxusb_ctrl_msg(adap->dev, CMD_AVER_STREAM_ON, NULL, 0, NULL, 0); + else + cxusb_ctrl_msg(adap->dev, CMD_AVER_STREAM_OFF, + NULL, 0, NULL, 0); + return 0; +} + static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { struct dvb_usb_rc_key *keymap = d->props.rc_key_map; @@ -423,6 +473,12 @@ static struct lgdt330x_config cxusb_lgdt3303_config = { .demod_chip = LGDT3303, }; +static struct lgdt330x_config cxusb_aver_lgdt3303_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, + .clock_polarity_flip = 2, +}; + static struct mt352_config cxusb_dee1601_config = { .demod_address = 0x0f, .demod_init = cxusb_dee1601_demod_init, @@ -453,6 +509,24 @@ static struct mt352_config cxusb_mt352_xc3028_config = { .demod_init = cxusb_mt352_demod_init, }; +/* FIXME: needs tweaking */ +static struct mxl5005s_config aver_a868r_tuner = { + .i2c_address = 0x63, + .if_freq = 6000000UL, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_C, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + /* Callbacks for DVB USB */ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) { @@ -533,6 +607,13 @@ static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) return 0; } +static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(mxl5005s_attach, adap->fe, + &adap->dev->i2c_adap, &aver_a868r_tuner); + return 0; +} + static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) { u8 b; @@ -562,6 +643,16 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) return -EIO; } +static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) +{ + adap->fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config, + &adap->dev->i2c_adap); + if (adap->fe != NULL) + return 0; + + return -EIO; +} + static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap) { /* used in both lgz201 and th7579 */ @@ -736,6 +827,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties; static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties; static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties; static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties; +static struct dvb_usb_device_properties cxusb_aver_a868r_properties; static int cxusb_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -756,7 +848,10 @@ static int cxusb_probe(struct usb_interface *intf, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_bluebird_nano2_needsfirmware_properties, - THIS_MODULE, NULL, adapter_nr)) + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties, + THIS_MODULE, NULL, adapter_nr) || + 0) return 0; return -EINVAL; @@ -779,6 +874,7 @@ static struct usb_device_id cxusb_table [] = { { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4) }, { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) }, { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) }, + { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, cxusb_table); @@ -1182,6 +1278,48 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_prope } }; +static struct dvb_usb_device_properties cxusb_aver_a868r_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = cxusb_aver_streaming_ctrl, + .frontend_attach = cxusb_aver_lgdt3303_frontend_attach, + .tuner_attach = cxusb_mxl5003s_tuner_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + + }, + }, + .power_ctrl = cxusb_aver_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "AVerMedia AVerTVHD Volar (A868R)", + { NULL }, + { &cxusb_table[16], NULL }, + }, + } +}; + static struct usb_driver cxusb_driver = { .name = "dvb_usb_cxusb", .probe = cxusb_probe, diff --git a/drivers/media/dvb/dvb-usb/cxusb.h b/drivers/media/dvb/dvb-usb/cxusb.h index 4768a2c3551..1a51eafd31b 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.h +++ b/drivers/media/dvb/dvb-usb/cxusb.h @@ -20,6 +20,9 @@ #define CMD_STREAMING_ON 0x36 #define CMD_STREAMING_OFF 0x37 +#define CMD_AVER_STREAM_ON 0x18 +#define CMD_AVER_STREAM_OFF 0x19 + #define CMD_GET_IR_CODE 0x47 #define CMD_ANALOG 0x50 diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index c4d40fe01d5..3dd20bfbed3 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -1117,6 +1117,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_HT_EXPRESS) }, { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS) }, { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P_2) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -1372,7 +1373,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { } }, - .num_device_descs = 2, + .num_device_descs = 3, .devices = { { "DiBcom STK7070PD reference design", { &dib0700_usb_id_table[17], NULL }, @@ -1381,6 +1382,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { "Pinnacle PCTV Dual DVB-T Diversity Stick", { &dib0700_usb_id_table[18], NULL }, { NULL }, + }, + { "Hauppauge Nova-TD Stick (52009)", + { &dib0700_usb_id_table[35], NULL }, + { NULL }, } } }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c index 23428cd3075..326f7608954 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c @@ -20,11 +20,7 @@ int dvb_usb_i2c_init(struct dvb_usb_device *d) } strncpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name)); -#ifdef I2C_ADAP_CLASS_TV_DIGITAL - d->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL, -#else d->i2c_adap.class = I2C_CLASS_TV_DIGITAL, -#endif d->i2c_adap.algo = d->props.i2c_algo; d->i2c_adap.algo_data = NULL; d->i2c_adap.dev.parent = &d->udev->dev; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 34245d1b7dd..e5238b31e94 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -14,6 +14,7 @@ #define USB_VID_AFATECH 0x15a4 #define USB_VID_ALCOR_MICRO 0x058f #define USB_VID_ALINK 0x05e3 +#define USB_VID_AMT 0x1c73 #define USB_VID_ANCHOR 0x0547 #define USB_VID_ANSONIC 0x10b9 #define USB_VID_ANUBIS_ELECTRONIC 0x10fd @@ -57,6 +58,7 @@ #define USB_PID_AFATECH_AF9005 0x9020 #define USB_VID_ALINK_DTU 0xf170 #define USB_PID_ANSONIC_DVBT_USB 0x6000 +#define USB_PID_ANYSEE 0x861f #define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 #define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 #define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800 @@ -132,9 +134,15 @@ #define USB_PID_HAUPPAUGE_NOVA_T_STICK_3 0x7070 #define USB_PID_HAUPPAUGE_MYTV_T 0x7080 #define USB_PID_HAUPPAUGE_NOVA_TD_STICK 0x9580 +#define USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009 0x5200 #define USB_PID_AVERMEDIA_EXPRESS 0xb568 #define USB_PID_AVERMEDIA_VOLAR 0xa807 #define USB_PID_AVERMEDIA_VOLAR_2 0xb808 +#define USB_PID_AVERMEDIA_VOLAR_A868R 0xa868 +#define USB_PID_AVERMEDIA_MCE_USB_M038 0x1228 +#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R 0x0039 +#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC 0x1039 +#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT 0x2039 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a #define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058 diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c index 037f7ffb47b..6f596ed4176 100644 --- a/drivers/media/dvb/dvb-usb/gl861.c +++ b/drivers/media/dvb/dvb-usb/gl861.c @@ -1,8 +1,8 @@ /* DVB USB compliant linux driver for GL861 USB2.0 devices. * * 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, version 2. + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, version 2. * * see Documentation/dvb/README.dvb-usb for more information */ @@ -13,9 +13,9 @@ /* debug */ static int dvb_usb_gl861_debug; -module_param_named(debug,dvb_usb_gl861_debug, int, 0644); -MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); - +module_param_named(debug, dvb_usb_gl861_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." + DVB_USB_DEBUG_STATUS); DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, @@ -70,7 +70,7 @@ static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], /* write/read request */ if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, - msg[i].len, msg[i+1].buf, msg[i+1].len) < 0) + msg[i].len, msg[i+1].buf, msg[i+1].len) < 0) break; i++; } else @@ -102,12 +102,13 @@ static struct zl10353_config gl861_zl10353_config = { static int gl861_frontend_attach(struct dvb_usb_adapter *adap) { - if ((adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config, - &adap->dev->i2c_adap)) != NULL) { - return 0; - } - return -EIO; + adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config, + &adap->dev->i2c_adap); + if (adap->fe == NULL) + return -EIO; + + return 0; } static struct qt1010_config gl861_qt1010_config = { @@ -156,7 +157,7 @@ static struct usb_device_id gl861_table [] = { { USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, gl861_table); +MODULE_DEVICE_TABLE(usb, gl861_table); static struct dvb_usb_device_properties gl861_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, @@ -180,7 +181,7 @@ static struct dvb_usb_device_properties gl861_properties = { } } }, - }}, + } }, .i2c_algo = &gl861_i2c_algo, .num_device_descs = 2, @@ -210,12 +211,11 @@ static int __init gl861_module_init(void) { int ret; - if ((ret = usb_register(&gl861_driver))) { + ret = usb_register(&gl861_driver); + if (ret) err("usb_register failed. Error number %d", ret); - return ret; - } - return 0; + return ret; } static void __exit gl861_module_exit(void) @@ -224,8 +224,8 @@ static void __exit gl861_module_exit(void) usb_deregister(&gl861_driver); } -module_init (gl861_module_init); -module_exit (gl861_module_exit); +module_init(gl861_module_init); +module_exit(gl861_module_exit); MODULE_AUTHOR("Carl Lundqvist <comabug@gmail.com>"); MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / GL861"); diff --git a/drivers/media/dvb/dvb-usb/gl861.h b/drivers/media/dvb/dvb-usb/gl861.h index 72a51afd5ee..c54855e2c23 100644 --- a/drivers/media/dvb/dvb-usb/gl861.h +++ b/drivers/media/dvb/dvb-usb/gl861.h @@ -4,7 +4,7 @@ #define DVB_USB_LOG_PREFIX "gl861" #include "dvb-usb.h" -#define deb_rc(args...) dprintk(dvb_usb_gl861_debug,0x01,args) +#define deb_rc(args...) dprintk(dvb_usb_gl861_debug, 0x01, args) #define GL861_WRITE 0x40 #define GL861_READ 0xc0 diff --git a/drivers/media/dvb/frontends/au8522.c b/drivers/media/dvb/frontends/au8522.c index 03900d241a7..f7b71657f0f 100644 --- a/drivers/media/dvb/frontends/au8522.c +++ b/drivers/media/dvb/frontends/au8522.c @@ -26,7 +26,6 @@ #include <linux/slab.h> #include <linux/delay.h> #include "dvb_frontend.h" -#include "dvb-pll.h" #include "au8522.h" struct au8522_state { diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index a054894ff48..ea058153ebf 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -343,6 +343,52 @@ static struct dvb_pll_desc dvb_pll_opera1 = { } }; +static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf, + const struct dvb_frontend_parameters *params) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + struct i2c_msg msg = { + .addr = priv->pll_i2c_address, + .flags = 0, + .buf = buf, + .len = 4 + }; + int result; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + result = i2c_transfer(priv->i2c, &msg, 1); + if (result != 1) + printk(KERN_ERR "%s: i2c_transfer failed:%d", + __func__, result); + + buf[2] = 0x9e; + buf[3] = 0x90; + + return; +} + +/* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */ +static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { + .name = "Samsung DTOS403IH102A", + .min = 44250000, + .max = 858000000, + .iffreq = 36125000, + .count = 8, + .set = samsung_dtos403ih102a_set, + .entries = { + { 135000000, 62500, 0xbe, 0x01 }, + { 177000000, 62500, 0xf6, 0x01 }, + { 370000000, 62500, 0xbe, 0x02 }, + { 450000000, 62500, 0xf6, 0x02 }, + { 466000000, 62500, 0xfe, 0x02 }, + { 538000000, 62500, 0xbe, 0x08 }, + { 826000000, 62500, 0xf6, 0x08 }, + { 999999999, 62500, 0xfe, 0x08 }, + } +}; + /* ----------------------------------------------------------- */ static struct dvb_pll_desc *pll_list[] = { @@ -360,6 +406,7 @@ static struct dvb_pll_desc *pll_list[] = { [DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv, [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261, [DVB_PLL_OPERA1] = &dvb_pll_opera1, + [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a, }; /* ----------------------------------------------------------- */ diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h index 872ca29e7cf..05239f579cc 100644 --- a/drivers/media/dvb/frontends/dvb-pll.h +++ b/drivers/media/dvb/frontends/dvb-pll.h @@ -22,6 +22,7 @@ #define DVB_PLL_SAMSUNG_TBMV 11 #define DVB_PLL_PHILIPS_SD1878_TDA8261 12 #define DVB_PLL_OPERA1 13 +#define DVB_PLL_SAMSUNG_DTOS403IH102A 14 /** * Attach a dvb-pll to the supplied frontend structure. diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c index f0195c8272f..056387b41a8 100644 --- a/drivers/media/dvb/frontends/lgdt330x.c +++ b/drivers/media/dvb/frontends/lgdt330x.c @@ -226,11 +226,16 @@ static int lgdt330x_init(struct dvb_frontend* fe) 0x4c, 0x14 }; - static u8 flip_lgdt3303_init_data[] = { + static u8 flip_1_lgdt3303_init_data[] = { 0x4c, 0x14, 0x87, 0xf3 }; + static u8 flip_2_lgdt3303_init_data[] = { + 0x4c, 0x14, + 0x87, 0xda + }; + struct lgdt330x_state* state = fe->demodulator_priv; char *chip_name; int err; @@ -243,10 +248,19 @@ static int lgdt330x_init(struct dvb_frontend* fe) break; case LGDT3303: chip_name = "LGDT3303"; - if (state->config->clock_polarity_flip) { - err = i2c_write_demod_bytes(state, flip_lgdt3303_init_data, - sizeof(flip_lgdt3303_init_data)); - } else { + switch (state->config->clock_polarity_flip) { + case 2: + err = i2c_write_demod_bytes(state, + flip_2_lgdt3303_init_data, + sizeof(flip_2_lgdt3303_init_data)); + break; + case 1: + err = i2c_write_demod_bytes(state, + flip_1_lgdt3303_init_data, + sizeof(flip_1_lgdt3303_init_data)); + break; + case 0: + default: err = i2c_write_demod_bytes(state, lgdt3303_init_data, sizeof(lgdt3303_init_data)); } diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c index b999ec424ff..5ddb2dca305 100644 --- a/drivers/media/dvb/frontends/s5h1409.c +++ b/drivers/media/dvb/frontends/s5h1409.c @@ -26,7 +26,6 @@ #include <linux/slab.h> #include <linux/delay.h> #include "dvb_frontend.h" -#include "dvb-pll.h" #include "s5h1409.h" struct s5h1409_state { diff --git a/drivers/media/dvb/frontends/s5h1411.c b/drivers/media/dvb/frontends/s5h1411.c index eb5bfc99d4e..cff360ce1ba 100644 --- a/drivers/media/dvb/frontends/s5h1411.c +++ b/drivers/media/dvb/frontends/s5h1411.c @@ -26,7 +26,6 @@ #include <linux/slab.h> #include <linux/delay.h> #include "dvb_frontend.h" -#include "dvb-pll.h" #include "s5h1411.h" struct s5h1411_state { diff --git a/drivers/media/dvb/frontends/tda10023.c b/drivers/media/dvb/frontends/tda10023.c index c6ff5b82ff8..a3c34eecdee 100644 --- a/drivers/media/dvb/frontends/tda10023.c +++ b/drivers/media/dvb/frontends/tda10023.c @@ -38,75 +38,29 @@ #include "dvb_frontend.h" #include "tda1002x.h" +#define REG0_INIT_VAL 0x23 struct tda10023_state { struct i2c_adapter* i2c; /* configuration settings */ - const struct tda1002x_config* config; + const struct tda10023_config *config; struct dvb_frontend frontend; u8 pwm; u8 reg0; -}; + /* clock settings */ + u32 xtal; + u8 pll_m; + u8 pll_p; + u8 pll_n; + u32 sysclk; +}; #define dprintk(x...) static int verbose; -#define XTAL 28920000UL -#define PLL_M 8UL -#define PLL_P 4UL -#define PLL_N 1UL -#define SYSCLK (XTAL*PLL_M/(PLL_N*PLL_P)) // -> 57840000 - -static u8 tda10023_inittab[]={ - // reg mask val - 0x2a,0xff,0x02, // PLL3, Bypass, Power Down - 0xff,0x64,0x00, // Sleep 100ms - 0x2a,0xff,0x03, // PLL3, Bypass, Power Down - 0xff,0x64,0x00, // Sleep 100ms - 0x28,0xff,PLL_M-1, // PLL1 M=8 - 0x29,0xff,((PLL_P-1)<<6)|(PLL_N-1), // PLL2 - 0x00,0xff,0x23, // GPR FSAMPLING=1 - 0x2a,0xff,0x08, // PLL3 PSACLK=1 - 0xff,0x64,0x00, // Sleep 100ms - 0x1f,0xff,0x00, // RESET - 0xff,0x64,0x00, // Sleep 100ms - 0xe6,0x0c,0x04, // RSCFG_IND - 0x10,0xc0,0x80, // DECDVBCFG1 PBER=1 - - 0x0e,0xff,0x82, // GAIN1 - 0x03,0x08,0x08, // CLKCONF DYN=1 - 0x2e,0xbf,0x30, // AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 PPWMTUN=0 PPWMIF=0 - 0x01,0xff,0x30, // AGCREF - 0x1e,0x84,0x84, // CONTROL SACLK_ON=1 - 0x1b,0xff,0xc8, // ADC TWOS=1 - 0x3b,0xff,0xff, // IFMAX - 0x3c,0xff,0x00, // IFMIN - 0x34,0xff,0x00, // PWMREF - 0x35,0xff,0xff, // TUNMAX - 0x36,0xff,0x00, // TUNMIN - 0x06,0xff,0x7f, // EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 // 0x77 - 0x1c,0x30,0x30, // EQCONF2 STEPALGO=SGNALGO=1 - 0x37,0xff,0xf6, // DELTAF_LSB - 0x38,0xff,0xff, // DELTAF_MSB - 0x02,0xff,0x93, // AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3 - 0x2d,0xff,0xf6, // SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 - 0x04,0x10,0x00, // SWRAMP=1 - 0x12,0xff,0xa1, // INTP1 POCLKP=1 FEL=1 MFS=0 - 0x2b,0x01,0xa1, // INTS1 - 0x20,0xff,0x04, // INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? - 0x2c,0xff,0x0d, // INTP/S TRIP=0 TRIS=0 - 0xc4,0xff,0x00, - 0xc3,0x30,0x00, - 0xb5,0xff,0x19, // ERAGC_THD - 0x00,0x03,0x01, // GPR, CLBS soft reset - 0x00,0x03,0x03, // GPR, CLBS soft reset - 0xff,0x64,0x00, // Sleep 100ms - 0xff,0xff,0xff -}; - static u8 tda10023_readreg (struct tda10023_state* state, u8 reg) { u8 b0 [] = { reg }; @@ -219,30 +173,34 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr) s16 SFIL=0; u16 NDEC = 0; - if (sr < (u32)(SYSCLK/98.40)) { + /* avoid floating point operations multiplying syscloc and divider + by 10 */ + u32 sysclk_x_10 = state->sysclk * 10; + + if (sr < (u32)(sysclk_x_10/984)) { NDEC=3; SFIL=1; - } else if (sr<(u32)(SYSCLK/64.0)) { + } else if (sr < (u32)(sysclk_x_10/640)) { NDEC=3; SFIL=0; - } else if (sr<(u32)(SYSCLK/49.2)) { + } else if (sr < (u32)(sysclk_x_10/492)) { NDEC=2; SFIL=1; - } else if (sr<(u32)(SYSCLK/32.0)) { + } else if (sr < (u32)(sysclk_x_10/320)) { NDEC=2; SFIL=0; - } else if (sr<(u32)(SYSCLK/24.6)) { + } else if (sr < (u32)(sysclk_x_10/246)) { NDEC=1; SFIL=1; - } else if (sr<(u32)(SYSCLK/16.0)) { + } else if (sr < (u32)(sysclk_x_10/160)) { NDEC=1; SFIL=0; - } else if (sr<(u32)(SYSCLK/12.3)) { + } else if (sr < (u32)(sysclk_x_10/123)) { NDEC=0; SFIL=1; } - BDRI=SYSCLK*16; + BDRI = (state->sysclk)*16; BDRI>>=NDEC; BDRI +=sr/2; BDRI /=sr; @@ -255,11 +213,12 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr) BDRX=1<<(24+NDEC); BDRX*=sr; - do_div(BDRX,SYSCLK); // BDRX/=SYSCLK; + do_div(BDRX, state->sysclk); /* BDRX/=SYSCLK; */ BDR=(s32)BDRX; } -// printk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n",sr,BDR,BDRI,NDEC); + dprintk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n", + sr, BDR, BDRI, NDEC); tda10023_writebit (state, 0x03, 0xc0, NDEC<<6); tda10023_writereg (state, 0x0a, BDR&255); tda10023_writereg (state, 0x0b, (BDR>>8)&255); @@ -272,8 +231,67 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr) static int tda10023_init (struct dvb_frontend *fe) { struct tda10023_state* state = fe->demodulator_priv; + u8 tda10023_inittab[] = { +/* reg mask val */ +/* 000 */ 0x2a, 0xff, 0x02, /* PLL3, Bypass, Power Down */ +/* 003 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 006 */ 0x2a, 0xff, 0x03, /* PLL3, Bypass, Power Down */ +/* 009 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ + /* PLL1 */ +/* 012 */ 0x28, 0xff, (state->pll_m-1), + /* PLL2 */ +/* 015 */ 0x29, 0xff, ((state->pll_p-1)<<6)|(state->pll_n-1), + /* GPR FSAMPLING=1 */ +/* 018 */ 0x00, 0xff, REG0_INIT_VAL, +/* 021 */ 0x2a, 0xff, 0x08, /* PLL3 PSACLK=1 */ +/* 024 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 027 */ 0x1f, 0xff, 0x00, /* RESET */ +/* 030 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 033 */ 0xe6, 0x0c, 0x04, /* RSCFG_IND */ +/* 036 */ 0x10, 0xc0, 0x80, /* DECDVBCFG1 PBER=1 */ + +/* 039 */ 0x0e, 0xff, 0x82, /* GAIN1 */ +/* 042 */ 0x03, 0x08, 0x08, /* CLKCONF DYN=1 */ +/* 045 */ 0x2e, 0xbf, 0x30, /* AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 + PPWMTUN=0 PPWMIF=0 */ +/* 048 */ 0x01, 0xff, 0x30, /* AGCREF */ +/* 051 */ 0x1e, 0x84, 0x84, /* CONTROL SACLK_ON=1 */ +/* 054 */ 0x1b, 0xff, 0xc8, /* ADC TWOS=1 */ +/* 057 */ 0x3b, 0xff, 0xff, /* IFMAX */ +/* 060 */ 0x3c, 0xff, 0x00, /* IFMIN */ +/* 063 */ 0x34, 0xff, 0x00, /* PWMREF */ +/* 066 */ 0x35, 0xff, 0xff, /* TUNMAX */ +/* 069 */ 0x36, 0xff, 0x00, /* TUNMIN */ +/* 072 */ 0x06, 0xff, 0x7f, /* EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 */ +/* 075 */ 0x1c, 0x30, 0x30, /* EQCONF2 STEPALGO=SGNALGO=1 */ +/* 078 */ 0x37, 0xff, 0xf6, /* DELTAF_LSB */ +/* 081 */ 0x38, 0xff, 0xff, /* DELTAF_MSB */ +/* 084 */ 0x02, 0xff, 0x93, /* AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3 */ +/* 087 */ 0x2d, 0xff, 0xf6, /* SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 */ +/* 090 */ 0x04, 0x10, 0x00, /* SWRAMP=1 */ +/* 093 */ 0x12, 0xff, TDA10023_OUTPUT_MODE_PARALLEL_B, /* + INTP1 POCLKP=1 FEL=1 MFS=0 */ +/* 096 */ 0x2b, 0x01, 0xa1, /* INTS1 */ +/* 099 */ 0x20, 0xff, 0x04, /* INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? */ +/* 102 */ 0x2c, 0xff, 0x0d, /* INTP/S TRIP=0 TRIS=0 */ +/* 105 */ 0xc4, 0xff, 0x00, +/* 108 */ 0xc3, 0x30, 0x00, +/* 111 */ 0xb5, 0xff, 0x19, /* ERAGC_THD */ +/* 114 */ 0x00, 0x03, 0x01, /* GPR, CLBS soft reset */ +/* 117 */ 0x00, 0x03, 0x03, /* GPR, CLBS soft reset */ +/* 120 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +/* 123 */ 0xff, 0xff, 0xff +}; + dprintk("DVB: TDA10023(%d): init chip\n", fe->dvb->num); + + /* override default values if set in config */ + if (state->config->deltaf) { + tda10023_inittab[80] = (state->config->deltaf & 0xff); + tda10023_inittab[83] = (state->config->deltaf >> 8); + } - dprintk("DVB: TDA10023(%d): init chip\n", fe->adapter->num); + if (state->config->output_mode) + tda10023_inittab[95] = state->config->output_mode; tda10023_writetab(state, tda10023_inittab); @@ -460,12 +478,11 @@ static void tda10023_release(struct dvb_frontend* fe) static struct dvb_frontend_ops tda10023_ops; -struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, - struct i2c_adapter* i2c, +struct dvb_frontend *tda10023_attach(const struct tda10023_config *config, + struct i2c_adapter *i2c, u8 pwm) { struct tda10023_state* state = NULL; - int i; /* allocate memory for the internal state */ state = kzalloc(sizeof(struct tda10023_state), GFP_KERNEL); @@ -474,22 +491,40 @@ struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, /* setup the state */ state->config = config; state->i2c = i2c; - memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); - state->pwm = pwm; - for (i=0; i < ARRAY_SIZE(tda10023_inittab);i+=3) { - if (tda10023_inittab[i] == 0x00) { - state->reg0 = tda10023_inittab[i+2]; - break; - } - } - // Wakeup if in standby + /* wakeup if in standby */ tda10023_writereg (state, 0x00, 0x33); /* check if the demod is there */ if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error; /* create dvb_frontend */ memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); + state->pwm = pwm; + state->reg0 = REG0_INIT_VAL; + if (state->config->xtal) { + state->xtal = state->config->xtal; + state->pll_m = state->config->pll_m; + state->pll_p = state->config->pll_p; + state->pll_n = state->config->pll_n; + } else { + /* set default values if not defined in config */ + state->xtal = 28920000; + state->pll_m = 8; + state->pll_p = 4; + state->pll_n = 1; + } + + /* calc sysclk */ + state->sysclk = (state->xtal * state->pll_m / \ + (state->pll_n * state->pll_p)); + + state->frontend.ops.info.symbol_rate_min = (state->sysclk/2)/64; + state->frontend.ops.info.symbol_rate_max = (state->sysclk/2)/4; + + dprintk("DVB: TDA10023 %s: xtal:%d pll_m:%d pll_p:%d pll_n:%d\n", + __func__, state->xtal, state->pll_m, state->pll_p, + state->pll_n); + state->frontend.demodulator_priv = state; return &state->frontend; @@ -504,10 +539,10 @@ static struct dvb_frontend_ops tda10023_ops = { .name = "Philips TDA10023 DVB-C", .type = FE_QAM, .frequency_stepsize = 62500, - .frequency_min = 47000000, + .frequency_min = 47000000, .frequency_max = 862000000, - .symbol_rate_min = (SYSCLK/2)/64, /* SACLK/64 == (SYSCLK/2)/64 */ - .symbol_rate_max = (SYSCLK/2)/4, /* SACLK/4 */ + .symbol_rate_min = 0, /* set in tda10023_attach */ + .symbol_rate_max = 0, /* set in tda10023_attach */ .caps = 0x400 | //FE_CAN_QAM_4 FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 | diff --git a/drivers/media/dvb/frontends/tda1002x.h b/drivers/media/dvb/frontends/tda1002x.h index 1bcc0d44b90..04d19418bf2 100644 --- a/drivers/media/dvb/frontends/tda1002x.h +++ b/drivers/media/dvb/frontends/tda1002x.h @@ -26,13 +26,37 @@ #include <linux/dvb/frontend.h> -struct tda1002x_config -{ +struct tda1002x_config { /* the demodulator's i2c address */ u8 demod_address; u8 invert; }; +enum tda10023_output_mode { + TDA10023_OUTPUT_MODE_PARALLEL_A = 0xe0, + TDA10023_OUTPUT_MODE_PARALLEL_B = 0xa1, + TDA10023_OUTPUT_MODE_PARALLEL_C = 0xa0, + TDA10023_OUTPUT_MODE_SERIAL, /* TODO: not implemented */ +}; + +struct tda10023_config { + /* the demodulator's i2c address */ + u8 demod_address; + u8 invert; + + /* clock settings */ + u32 xtal; /* defaults: 28920000 */ + u8 pll_m; /* defaults: 8 */ + u8 pll_p; /* defaults: 4 */ + u8 pll_n; /* defaults: 1 */ + + /* MPEG2 TS output mode */ + u8 output_mode; + + /* input freq offset + baseband conversion type */ + u16 deltaf; +}; + #if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE)) extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, struct i2c_adapter* i2c, u8 pwm); @@ -45,12 +69,15 @@ static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config* } #endif // CONFIG_DVB_TDA10021 -#if defined(CONFIG_DVB_TDA10023) || (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE)) -extern struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, - struct i2c_adapter* i2c, u8 pwm); +#if defined(CONFIG_DVB_TDA10023) || \ + (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE)) +extern struct dvb_frontend *tda10023_attach( + const struct tda10023_config *config, + struct i2c_adapter *i2c, u8 pwm); #else -static inline struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, - struct i2c_adapter* i2c, u8 pwm) +static inline struct dvb_frontend *tda10023_attach( + const struct tda10023_config *config, + struct i2c_adapter *i2c, u8 pwm) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c index 960ed5763ae..1360403b88b 100644 --- a/drivers/media/dvb/pluto2/pluto2.c +++ b/drivers/media/dvb/pluto2/pluto2.c @@ -234,7 +234,7 @@ static void pluto_reset_ts(struct pluto *pluto, int reenable) static void pluto_set_dma_addr(struct pluto *pluto) { - pluto_writereg(pluto, REG_PCAR, cpu_to_le32(pluto->dma_addr)); + pluto_writereg(pluto, REG_PCAR, pluto->dma_addr); } static int __devinit pluto_dma_map(struct pluto *pluto) diff --git a/drivers/media/dvb/siano/Kconfig b/drivers/media/dvb/siano/Kconfig new file mode 100644 index 00000000000..dd863f26167 --- /dev/null +++ b/drivers/media/dvb/siano/Kconfig @@ -0,0 +1,26 @@ +# +# Siano Mobile Silicon Digital TV device configuration +# + +config DVB_SIANO_SMS1XXX + tristate "Siano SMS1XXX USB dongle support" + depends on DVB_CORE && USB + ---help--- + Choose Y here if you have a USB dongle with a SMS1XXX chipset. + + To compile this driver as a module, choose M here: the + module will be called sms1xxx. + +config DVB_SIANO_SMS1XXX_SMS_IDS + bool "Enable support for Siano Mobile Silicon default USB IDs" + depends on DVB_SIANO_SMS1XXX + default y + ---help--- + Choose Y here if you have a USB dongle with a SMS1XXX chipset + that uses Siano Mobile Silicon's default usb vid:pid. + + Choose N here if you would prefer to use Siano's external driver. + + Further documentation on this driver can be found on the WWW at + <http://www.siano-ms.com/>. + diff --git a/drivers/media/dvb/siano/Makefile b/drivers/media/dvb/siano/Makefile new file mode 100644 index 00000000000..ee0737af98c --- /dev/null +++ b/drivers/media/dvb/siano/Makefile @@ -0,0 +1,8 @@ +sms1xxx-objs := smscoreapi.o smsusb.o smsdvb.o sms-cards.o + +obj-$(CONFIG_DVB_SIANO_SMS1XXX) += sms1xxx.o + +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core + +EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) + diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c new file mode 100644 index 00000000000..e7a8ac0c404 --- /dev/null +++ b/drivers/media/dvb/siano/sms-cards.c @@ -0,0 +1,102 @@ +/* + * Card-specific functions for the Siano SMS1xxx USB dongle + * + * Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "sms-cards.h" + +struct usb_device_id smsusb_id_table[] = { +#ifdef CONFIG_DVB_SIANO_SMS1XXX_SMS_IDS + { USB_DEVICE(0x187f, 0x0010), + .driver_info = SMS1XXX_BOARD_SIANO_STELLAR }, + { USB_DEVICE(0x187f, 0x0100), + .driver_info = SMS1XXX_BOARD_SIANO_STELLAR }, + { USB_DEVICE(0x187f, 0x0200), + .driver_info = SMS1XXX_BOARD_SIANO_NOVA_A }, + { USB_DEVICE(0x187f, 0x0201), + .driver_info = SMS1XXX_BOARD_SIANO_NOVA_B }, + { USB_DEVICE(0x187f, 0x0300), + .driver_info = SMS1XXX_BOARD_SIANO_VEGA }, +#endif + { USB_DEVICE(0x2040, 0x1700), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT }, + { USB_DEVICE(0x2040, 0x1800), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A }, + { USB_DEVICE(0x2040, 0x1801), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B }, + { USB_DEVICE(0x2040, 0x5500), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0x5580), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0x5590), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, smsusb_id_table); + +static struct sms_board sms_boards[] = { + [SMS_BOARD_UNKNOWN] = { + .name = "Unknown board", + }, + [SMS1XXX_BOARD_SIANO_STELLAR] = { + .name = "Siano Stellar Digital Receiver", + .type = SMS_STELLAR, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-stellar-dvbt-01.fw", + }, + [SMS1XXX_BOARD_SIANO_NOVA_A] = { + .name = "Siano Nova A Digital Receiver", + .type = SMS_NOVA_A0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-a-dvbt-01.fw", + }, + [SMS1XXX_BOARD_SIANO_NOVA_B] = { + .name = "Siano Nova B Digital Receiver", + .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw", + }, + [SMS1XXX_BOARD_SIANO_VEGA] = { + .name = "Siano Vega Digital Receiver", + .type = SMS_VEGA, + }, + [SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT] = { + .name = "Hauppauge Catamount", + .type = SMS_STELLAR, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-stellar-dvbt-01.fw", + }, + [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A] = { + .name = "Hauppauge Okemo-A", + .type = SMS_NOVA_A0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-a-dvbt-01.fw", + }, + [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B] = { + .name = "Hauppauge Okemo-B", + .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw", + }, + [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { + .name = "Hauppauge WinTV-Nova-T-MiniStick", + .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-01.fw", + }, +}; + +struct sms_board *sms_get_board(int id) +{ + BUG_ON(id >= ARRAY_SIZE(sms_boards)); + + return &sms_boards[id]; +} + diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/dvb/siano/sms-cards.h new file mode 100644 index 00000000000..83b39bc203f --- /dev/null +++ b/drivers/media/dvb/siano/sms-cards.h @@ -0,0 +1,45 @@ +/* + * Card-specific functions for the Siano SMS1xxx USB dongle + * + * Copyright (c) 2008 Michael Krufky <mkrufky@linuxtv.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SMS_CARDS_H__ +#define __SMS_CARDS_H__ + +#include <linux/usb.h> +#include "smscoreapi.h" + +#define SMS_BOARD_UNKNOWN 0 +#define SMS1XXX_BOARD_SIANO_STELLAR 1 +#define SMS1XXX_BOARD_SIANO_NOVA_A 2 +#define SMS1XXX_BOARD_SIANO_NOVA_B 3 +#define SMS1XXX_BOARD_SIANO_VEGA 4 +#define SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT 5 +#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A 6 +#define SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B 7 +#define SMS1XXX_BOARD_HAUPPAUGE_WINDHAM 8 + +struct sms_board { + enum sms_device_type_st type; + char *name, *fw[DEVICE_MODE_MAX]; +}; + +struct sms_board *sms_get_board(int id); + +extern struct usb_device_id smsusb_id_table[]; + +#endif /* __SMS_CARDS_H__ */ diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c new file mode 100644 index 00000000000..b4b8ed795c9 --- /dev/null +++ b/drivers/media/dvb/siano/smscoreapi.c @@ -0,0 +1,1251 @@ +/* + * Siano core API module + * + * This file contains implementation for the interface to sms core component + * + * author: Anatoly Greenblat + * + * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include <linux/firmware.h> + +#include "smscoreapi.h" +#include "sms-cards.h" + +int sms_debug; +module_param_named(debug, sms_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); + +struct smscore_device_notifyee_t { + struct list_head entry; + hotplug_t hotplug; +}; + +struct smscore_idlist_t { + struct list_head entry; + int id; + int data_type; +}; + +struct smscore_client_t { + struct list_head entry; + struct smscore_device_t *coredev; + void *context; + struct list_head idlist; + onresponse_t onresponse_handler; + onremove_t onremove_handler; +}; + +struct smscore_device_t { + struct list_head entry; + + struct list_head clients; + struct list_head subclients; + spinlock_t clientslock; + + struct list_head buffers; + spinlock_t bufferslock; + int num_buffers; + + void *common_buffer; + int common_buffer_size; + dma_addr_t common_buffer_phys; + + void *context; + struct device *device; + + char devpath[32]; + unsigned long device_flags; + + setmode_t setmode_handler; + detectmode_t detectmode_handler; + sendrequest_t sendrequest_handler; + preload_t preload_handler; + postload_t postload_handler; + + int mode, modes_supported; + + struct completion version_ex_done, data_download_done, trigger_done; + struct completion init_device_done, reload_start_done, resume_done; + + int board_id; +}; + +void smscore_set_board_id(struct smscore_device_t *core, int id) +{ + core->board_id = id; +} + +int smscore_get_board_id(struct smscore_device_t *core) +{ + return core->board_id; +} + +struct smscore_registry_entry_t { + struct list_head entry; + char devpath[32]; + int mode; + enum sms_device_type_st type; +}; + +struct list_head g_smscore_notifyees; +struct list_head g_smscore_devices; +struct mutex g_smscore_deviceslock; + +struct list_head g_smscore_registry; +struct mutex g_smscore_registrylock; + +static int default_mode = 4; + +module_param(default_mode, int, 0644); +MODULE_PARM_DESC(default_mode, "default firmware id (device mode)"); + +static struct smscore_registry_entry_t *smscore_find_registry(char *devpath) +{ + struct smscore_registry_entry_t *entry; + struct list_head *next; + + kmutex_lock(&g_smscore_registrylock); + for (next = g_smscore_registry.next; + next != &g_smscore_registry; + next = next->next) { + entry = (struct smscore_registry_entry_t *) next; + if (!strcmp(entry->devpath, devpath)) { + kmutex_unlock(&g_smscore_registrylock); + return entry; + } + } + entry = (struct smscore_registry_entry_t *) + kmalloc(sizeof(struct smscore_registry_entry_t), + GFP_KERNEL); + if (entry) { + entry->mode = default_mode; + strcpy(entry->devpath, devpath); + list_add(&entry->entry, &g_smscore_registry); + } else + sms_err("failed to create smscore_registry."); + kmutex_unlock(&g_smscore_registrylock); + return entry; +} + +int smscore_registry_getmode(char *devpath) +{ + struct smscore_registry_entry_t *entry; + + entry = smscore_find_registry(devpath); + if (entry) + return entry->mode; + else + sms_err("No registry found."); + + return default_mode; +} + +static enum sms_device_type_st smscore_registry_gettype(char *devpath) +{ + struct smscore_registry_entry_t *entry; + + entry = smscore_find_registry(devpath); + if (entry) + return entry->type; + else + sms_err("No registry found."); + + return -1; +} + +void smscore_registry_setmode(char *devpath, int mode) +{ + struct smscore_registry_entry_t *entry; + + entry = smscore_find_registry(devpath); + if (entry) + entry->mode = mode; + else + sms_err("No registry found."); +} + +static void smscore_registry_settype(char *devpath, + enum sms_device_type_st type) +{ + struct smscore_registry_entry_t *entry; + + entry = smscore_find_registry(devpath); + if (entry) + entry->type = type; + else + sms_err("No registry found."); +} + + +static void list_add_locked(struct list_head *new, struct list_head *head, + spinlock_t *lock) +{ + unsigned long flags; + + spin_lock_irqsave(lock, flags); + + list_add(new, head); + + spin_unlock_irqrestore(lock, flags); +} + +/** + * register a client callback that called when device plugged in/unplugged + * NOTE: if devices exist callback is called immediately for each device + * + * @param hotplug callback + * + * @return 0 on success, <0 on error. + */ +int smscore_register_hotplug(hotplug_t hotplug) +{ + struct smscore_device_notifyee_t *notifyee; + struct list_head *next, *first; + int rc = 0; + + kmutex_lock(&g_smscore_deviceslock); + + notifyee = kmalloc(sizeof(struct smscore_device_notifyee_t), + GFP_KERNEL); + if (notifyee) { + /* now notify callback about existing devices */ + first = &g_smscore_devices; + for (next = first->next; + next != first && !rc; + next = next->next) { + struct smscore_device_t *coredev = + (struct smscore_device_t *) next; + rc = hotplug(coredev, coredev->device, 1); + } + + if (rc >= 0) { + notifyee->hotplug = hotplug; + list_add(¬ifyee->entry, &g_smscore_notifyees); + } else + kfree(notifyee); + } else + rc = -ENOMEM; + + kmutex_unlock(&g_smscore_deviceslock); + + return rc; +} + +/** + * unregister a client callback that called when device plugged in/unplugged + * + * @param hotplug callback + * + */ +void smscore_unregister_hotplug(hotplug_t hotplug) +{ + struct list_head *next, *first; + + kmutex_lock(&g_smscore_deviceslock); + + first = &g_smscore_notifyees; + + for (next = first->next; next != first;) { + struct smscore_device_notifyee_t *notifyee = + (struct smscore_device_notifyee_t *) next; + next = next->next; + + if (notifyee->hotplug == hotplug) { + list_del(¬ifyee->entry); + kfree(notifyee); + } + } + + kmutex_unlock(&g_smscore_deviceslock); +} + +static void smscore_notify_clients(struct smscore_device_t *coredev) +{ + struct smscore_client_t *client; + + /* the client must call smscore_unregister_client from remove handler */ + while (!list_empty(&coredev->clients)) { + client = (struct smscore_client_t *) coredev->clients.next; + client->onremove_handler(client->context); + } +} + +static int smscore_notify_callbacks(struct smscore_device_t *coredev, + struct device *device, int arrival) +{ + struct list_head *next, *first; + int rc = 0; + + /* note: must be called under g_deviceslock */ + + first = &g_smscore_notifyees; + + for (next = first->next; next != first; next = next->next) { + rc = ((struct smscore_device_notifyee_t *) next)-> + hotplug(coredev, device, arrival); + if (rc < 0) + break; + } + + return rc; +} + +static struct +smscore_buffer_t *smscore_createbuffer(u8 *buffer, void *common_buffer, + dma_addr_t common_buffer_phys) +{ + struct smscore_buffer_t *cb = + kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL); + if (!cb) { + sms_info("kmalloc(...) failed"); + return NULL; + } + + cb->p = buffer; + cb->offset_in_common = buffer - (u8 *) common_buffer; + cb->phys = common_buffer_phys + cb->offset_in_common; + + return cb; +} + +/** + * creates coredev object for a device, prepares buffers, + * creates buffer mappings, notifies registered hotplugs about new device. + * + * @param params device pointer to struct with device specific parameters + * and handlers + * @param coredev pointer to a value that receives created coredev object + * + * @return 0 on success, <0 on error. + */ +int smscore_register_device(struct smsdevice_params_t *params, + struct smscore_device_t **coredev) +{ + struct smscore_device_t *dev; + u8 *buffer; + + dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL); + if (!dev) { + sms_info("kzalloc(...) failed"); + return -ENOMEM; + } + + /* init list entry so it could be safe in smscore_unregister_device */ + INIT_LIST_HEAD(&dev->entry); + + /* init queues */ + INIT_LIST_HEAD(&dev->clients); + INIT_LIST_HEAD(&dev->buffers); + + /* init locks */ + spin_lock_init(&dev->clientslock); + spin_lock_init(&dev->bufferslock); + + /* init completion events */ + init_completion(&dev->version_ex_done); + init_completion(&dev->data_download_done); + init_completion(&dev->trigger_done); + init_completion(&dev->init_device_done); + init_completion(&dev->reload_start_done); + init_completion(&dev->resume_done); + + /* alloc common buffer */ + dev->common_buffer_size = params->buffer_size * params->num_buffers; + dev->common_buffer = dma_alloc_coherent(NULL, dev->common_buffer_size, + &dev->common_buffer_phys, + GFP_KERNEL | GFP_DMA); + if (!dev->common_buffer) { + smscore_unregister_device(dev); + return -ENOMEM; + } + + /* prepare dma buffers */ + for (buffer = dev->common_buffer; + dev->num_buffers < params->num_buffers; + dev->num_buffers++, buffer += params->buffer_size) { + struct smscore_buffer_t *cb = + smscore_createbuffer(buffer, dev->common_buffer, + dev->common_buffer_phys); + if (!cb) { + smscore_unregister_device(dev); + return -ENOMEM; + } + + smscore_putbuffer(dev, cb); + } + + sms_info("allocated %d buffers", dev->num_buffers); + + dev->mode = DEVICE_MODE_NONE; + dev->context = params->context; + dev->device = params->device; + dev->setmode_handler = params->setmode_handler; + dev->detectmode_handler = params->detectmode_handler; + dev->sendrequest_handler = params->sendrequest_handler; + dev->preload_handler = params->preload_handler; + dev->postload_handler = params->postload_handler; + + dev->device_flags = params->flags; + strcpy(dev->devpath, params->devpath); + + smscore_registry_settype(dev->devpath, params->device_type); + + /* add device to devices list */ + kmutex_lock(&g_smscore_deviceslock); + list_add(&dev->entry, &g_smscore_devices); + kmutex_unlock(&g_smscore_deviceslock); + + *coredev = dev; + + sms_info("device %p created", dev); + + return 0; +} + +/** + * sets initial device mode and notifies client hotplugs that device is ready + * + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * + * @return 0 on success, <0 on error. + */ +int smscore_start_device(struct smscore_device_t *coredev) +{ + int rc = smscore_set_device_mode( + coredev, smscore_registry_getmode(coredev->devpath)); + if (rc < 0) { + sms_info("set device mode faile , rc %d", rc); + return rc; + } + + kmutex_lock(&g_smscore_deviceslock); + + rc = smscore_notify_callbacks(coredev, coredev->device, 1); + + sms_info("device %p started, rc %d", coredev, rc); + + kmutex_unlock(&g_smscore_deviceslock); + + return rc; +} + +static int smscore_sendrequest_and_wait(struct smscore_device_t *coredev, + void *buffer, size_t size, + struct completion *completion) +{ + int rc = coredev->sendrequest_handler(coredev->context, buffer, size); + if (rc < 0) { + sms_info("sendrequest returned error %d", rc); + return rc; + } + + return wait_for_completion_timeout(completion, + msecs_to_jiffies(10000)) ? + 0 : -ETIME; +} + +static int smscore_load_firmware_family2(struct smscore_device_t *coredev, + void *buffer, size_t size) +{ + struct SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer; + struct SmsMsgHdr_ST *msg; + u32 mem_address = firmware->StartAddress; + u8 *payload = firmware->Payload; + int rc = 0; + + sms_info("loading FW to addr 0x%x size %d", + mem_address, firmware->Length); + if (coredev->preload_handler) { + rc = coredev->preload_handler(coredev->context); + if (rc < 0) + return rc; + } + + /* PAGE_SIZE buffer shall be enough and dma aligned */ + msg = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA); + if (!msg) + return -ENOMEM; + + if (coredev->mode != DEVICE_MODE_NONE) { + sms_debug("sending reload command."); + SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, + sizeof(struct SmsMsgHdr_ST)); + rc = smscore_sendrequest_and_wait(coredev, msg, + msg->msgLength, + &coredev->reload_start_done); + mem_address = *(u32 *) &payload[20]; + } + + while (size && rc >= 0) { + struct SmsDataDownload_ST *DataMsg = + (struct SmsDataDownload_ST *) msg; + int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE); + + SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, + (u16)(sizeof(struct SmsMsgHdr_ST) + + sizeof(u32) + payload_size)); + + DataMsg->MemAddr = mem_address; + memcpy(DataMsg->Payload, payload, payload_size); + + if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) && + (coredev->mode == DEVICE_MODE_NONE)) + rc = coredev->sendrequest_handler( + coredev->context, DataMsg, + DataMsg->xMsgHeader.msgLength); + else + rc = smscore_sendrequest_and_wait( + coredev, DataMsg, + DataMsg->xMsgHeader.msgLength, + &coredev->data_download_done); + + payload += payload_size; + size -= payload_size; + mem_address += payload_size; + } + + if (rc >= 0) { + if (coredev->mode == DEVICE_MODE_NONE) { + struct SmsMsgData_ST *TriggerMsg = + (struct SmsMsgData_ST *) msg; + + SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, + sizeof(struct SmsMsgHdr_ST) + + sizeof(u32) * 5); + + TriggerMsg->msgData[0] = firmware->StartAddress; + /* Entry point */ + TriggerMsg->msgData[1] = 5; /* Priority */ + TriggerMsg->msgData[2] = 0x200; /* Stack size */ + TriggerMsg->msgData[3] = 0; /* Parameter */ + TriggerMsg->msgData[4] = 4; /* Task ID */ + + if (coredev->device_flags & SMS_ROM_NO_RESPONSE) { + rc = coredev->sendrequest_handler( + coredev->context, TriggerMsg, + TriggerMsg->xMsgHeader.msgLength); + msleep(100); + } else + rc = smscore_sendrequest_and_wait( + coredev, TriggerMsg, + TriggerMsg->xMsgHeader.msgLength, + &coredev->trigger_done); + } else { + SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, + sizeof(struct SmsMsgHdr_ST)); + + rc = coredev->sendrequest_handler(coredev->context, + msg, msg->msgLength); + } + msleep(500); + } + + sms_debug("rc=%d, postload=%p ", rc, + coredev->postload_handler); + + kfree(msg); + + return ((rc >= 0) && coredev->postload_handler) ? + coredev->postload_handler(coredev->context) : + rc; +} + +/** + * loads specified firmware into a buffer and calls device loadfirmware_handler + * + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * @param filename null-terminated string specifies firmware file name + * @param loadfirmware_handler device handler that loads firmware + * + * @return 0 on success, <0 on error. + */ +static int smscore_load_firmware_from_file(struct smscore_device_t *coredev, + char *filename, + loadfirmware_t loadfirmware_handler) +{ + int rc = -ENOENT; + const struct firmware *fw; + u8 *fw_buffer; + + if (loadfirmware_handler == NULL && !(coredev->device_flags & + SMS_DEVICE_FAMILY2)) + return -EINVAL; + + rc = request_firmware(&fw, filename, coredev->device); + if (rc < 0) { + sms_info("failed to open \"%s\"", filename); + return rc; + } + sms_info("read FW %s, size=%zd", filename, fw->size); + fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), + GFP_KERNEL | GFP_DMA); + if (fw_buffer) { + memcpy(fw_buffer, fw->data, fw->size); + + rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ? + smscore_load_firmware_family2(coredev, + fw_buffer, + fw->size) : + loadfirmware_handler(coredev->context, + fw_buffer, fw->size); + + kfree(fw_buffer); + } else { + sms_info("failed to allocate firmware buffer"); + rc = -ENOMEM; + } + + release_firmware(fw); + + return rc; +} + +/** + * notifies all clients registered with the device, notifies hotplugs, + * frees all buffers and coredev object + * + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * + * @return 0 on success, <0 on error. + */ +void smscore_unregister_device(struct smscore_device_t *coredev) +{ + struct smscore_buffer_t *cb; + int num_buffers = 0; + int retry = 0; + + kmutex_lock(&g_smscore_deviceslock); + + smscore_notify_clients(coredev); + smscore_notify_callbacks(coredev, NULL, 0); + + /* at this point all buffers should be back + * onresponse must no longer be called */ + + while (1) { + while ((cb = smscore_getbuffer(coredev))) { + kfree(cb); + num_buffers++; + } + if (num_buffers == coredev->num_buffers) + break; + if (++retry > 10) { + sms_info("exiting although " + "not all buffers released."); + break; + } + + sms_info("waiting for %d buffer(s)", + coredev->num_buffers - num_buffers); + msleep(100); + } + + sms_info("freed %d buffers", num_buffers); + + if (coredev->common_buffer) + dma_free_coherent(NULL, coredev->common_buffer_size, + coredev->common_buffer, + coredev->common_buffer_phys); + + list_del(&coredev->entry); + kfree(coredev); + + kmutex_unlock(&g_smscore_deviceslock); + + sms_info("device %p destroyed", coredev); +} + +static int smscore_detect_mode(struct smscore_device_t *coredev) +{ + void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, + GFP_KERNEL | GFP_DMA); + struct SmsMsgHdr_ST *msg = + (struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer); + int rc; + + if (!buffer) + return -ENOMEM; + + SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, + sizeof(struct SmsMsgHdr_ST)); + + rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, + &coredev->version_ex_done); + if (rc == -ETIME) { + sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try"); + + if (wait_for_completion_timeout(&coredev->resume_done, + msecs_to_jiffies(5000))) { + rc = smscore_sendrequest_and_wait( + coredev, msg, msg->msgLength, + &coredev->version_ex_done); + if (rc < 0) + sms_err("MSG_SMS_GET_VERSION_EX_REQ failed " + "second try, rc %d", rc); + } else + rc = -ETIME; + } + + kfree(buffer); + + return rc; +} + +static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = { + /*Stellar NOVA A0 Nova B0 VEGA*/ + /*DVBT*/ + {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, + /*DVBH*/ + {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, + /*TDMB*/ + {"none", "tdmb_nova_12mhz.inp", "none", "none"}, + /*DABIP*/ + {"none", "none", "none", "none"}, + /*BDA*/ + {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, + /*ISDBT*/ + {"none", "isdbt_nova_12mhz.inp", "dvb_nova_12mhz.inp", "none"}, + /*ISDBTBDA*/ + {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, + /*CMMB*/ + {"none", "none", "none", "cmmb_vega_12mhz.inp"} +}; + +static inline char *sms_get_fw_name(struct smscore_device_t *coredev, + int mode, enum sms_device_type_st type) +{ + char **fw = sms_get_board(smscore_get_board_id(coredev))->fw; + return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type]; +} + +/** + * calls device handler to change mode of operation + * NOTE: stellar/usb may disconnect when changing mode + * + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * @param mode requested mode of operation + * + * @return 0 on success, <0 on error. + */ +int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) +{ + void *buffer; + int rc = 0; + enum sms_device_type_st type; + + sms_debug("set device mode to %d", mode); + if (coredev->device_flags & SMS_DEVICE_FAMILY2) { + if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER) { + sms_err("invalid mode specified %d", mode); + return -EINVAL; + } + + smscore_registry_setmode(coredev->devpath, mode); + + if (!(coredev->device_flags & SMS_DEVICE_NOT_READY)) { + rc = smscore_detect_mode(coredev); + if (rc < 0) { + sms_err("mode detect failed %d", rc); + return rc; + } + } + + if (coredev->mode == mode) { + sms_info("device mode %d already set", mode); + return 0; + } + + if (!(coredev->modes_supported & (1 << mode))) { + char *fw_filename; + + type = smscore_registry_gettype(coredev->devpath); + fw_filename = sms_get_fw_name(coredev, mode, type); + + rc = smscore_load_firmware_from_file(coredev, + fw_filename, NULL); + if (rc < 0) { + sms_warn("error %d loading firmware: %s, " + "trying again with default firmware", + rc, fw_filename); + + /* try again with the default firmware */ + fw_filename = smscore_fw_lkup[mode][type]; + rc = smscore_load_firmware_from_file(coredev, + fw_filename, NULL); + + if (rc < 0) { + sms_warn("error %d loading " + "firmware: %s", rc, + fw_filename); + return rc; + } + } + sms_log("firmware download success: %s", fw_filename); + } else + sms_info("mode %d supported by running " + "firmware", mode); + + buffer = kmalloc(sizeof(struct SmsMsgData_ST) + + SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); + if (buffer) { + struct SmsMsgData_ST *msg = + (struct SmsMsgData_ST *) + SMS_ALIGN_ADDRESS(buffer); + + SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, + sizeof(struct SmsMsgData_ST)); + msg->msgData[0] = mode; + + rc = smscore_sendrequest_and_wait( + coredev, msg, msg->xMsgHeader.msgLength, + &coredev->init_device_done); + + kfree(buffer); + } else { + sms_err("Could not allocate buffer for " + "init device message."); + rc = -ENOMEM; + } + } else { + if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { + sms_err("invalid mode specified %d", mode); + return -EINVAL; + } + + smscore_registry_setmode(coredev->devpath, mode); + + if (coredev->detectmode_handler) + coredev->detectmode_handler(coredev->context, + &coredev->mode); + + if (coredev->mode != mode && coredev->setmode_handler) + rc = coredev->setmode_handler(coredev->context, mode); + } + + if (rc >= 0) { + coredev->mode = mode; + coredev->device_flags &= ~SMS_DEVICE_NOT_READY; + } + + if (rc != 0) + sms_err("return error code %d.", rc); + return rc; +} + +/** + * calls device handler to get current mode of operation + * + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * + * @return current mode + */ +int smscore_get_device_mode(struct smscore_device_t *coredev) +{ + return coredev->mode; +} + +/** + * find client by response id & type within the clients list. + * return client handle or NULL. + * + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * @param data_type client data type (SMS_DONT_CARE for all types) + * @param id client id (SMS_DONT_CARE for all id) + * + */ +static struct +smscore_client_t *smscore_find_client(struct smscore_device_t *coredev, + int data_type, int id) +{ + struct smscore_client_t *client = NULL; + struct list_head *next, *first; + unsigned long flags; + struct list_head *firstid, *nextid; + + + spin_lock_irqsave(&coredev->clientslock, flags); + first = &coredev->clients; + for (next = first->next; + (next != first) && !client; + next = next->next) { + firstid = &((struct smscore_client_t *)next)->idlist; + for (nextid = firstid->next; + nextid != firstid; + nextid = nextid->next) { + if ((((struct smscore_idlist_t *)nextid)->id == id) && + (((struct smscore_idlist_t *)nextid)->data_type == data_type || + (((struct smscore_idlist_t *)nextid)->data_type == 0))) { + client = (struct smscore_client_t *) next; + break; + } + } + } + spin_unlock_irqrestore(&coredev->clientslock, flags); + return client; +} + +/** + * find client by response id/type, call clients onresponse handler + * return buffer to pool on error + * + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * @param cb pointer to response buffer descriptor + * + */ +void smscore_onresponse(struct smscore_device_t *coredev, + struct smscore_buffer_t *cb) +{ + struct SmsMsgHdr_ST *phdr = + (struct SmsMsgHdr_ST *)((u8 *) cb->p + cb->offset); + struct smscore_client_t *client = + smscore_find_client(coredev, phdr->msgType, phdr->msgDstId); + int rc = -EBUSY; + + static unsigned long last_sample_time; /* = 0; */ + static int data_total; /* = 0; */ + unsigned long time_now = jiffies_to_msecs(jiffies); + + if (!last_sample_time) + last_sample_time = time_now; + + if (time_now - last_sample_time > 10000) { + sms_debug("\ndata rate %d bytes/secs", + (int)((data_total * 1000) / + (time_now - last_sample_time))); + + last_sample_time = time_now; + data_total = 0; + } + + data_total += cb->size; + /* If no client registered for type & id, + * check for control client where type is not registered */ + if (client) + rc = client->onresponse_handler(client->context, cb); + + if (rc < 0) { + switch (phdr->msgType) { + case MSG_SMS_GET_VERSION_EX_RES: + { + struct SmsVersionRes_ST *ver = + (struct SmsVersionRes_ST *) phdr; + sms_debug("MSG_SMS_GET_VERSION_EX_RES " + "id %d prots 0x%x ver %d.%d", + ver->FirmwareId, ver->SupportedProtocols, + ver->RomVersionMajor, ver->RomVersionMinor); + + coredev->mode = ver->FirmwareId == 255 ? + DEVICE_MODE_NONE : ver->FirmwareId; + coredev->modes_supported = ver->SupportedProtocols; + + complete(&coredev->version_ex_done); + break; + } + case MSG_SMS_INIT_DEVICE_RES: + sms_debug("MSG_SMS_INIT_DEVICE_RES"); + complete(&coredev->init_device_done); + break; + case MSG_SW_RELOAD_START_RES: + sms_debug("MSG_SW_RELOAD_START_RES"); + complete(&coredev->reload_start_done); + break; + case MSG_SMS_DATA_DOWNLOAD_RES: + complete(&coredev->data_download_done); + break; + case MSG_SW_RELOAD_EXEC_RES: + sms_debug("MSG_SW_RELOAD_EXEC_RES"); + break; + case MSG_SMS_SWDOWNLOAD_TRIGGER_RES: + sms_debug("MSG_SMS_SWDOWNLOAD_TRIGGER_RES"); + complete(&coredev->trigger_done); + break; + case MSG_SMS_SLEEP_RESUME_COMP_IND: + complete(&coredev->resume_done); + break; + default: + break; + } + smscore_putbuffer(coredev, cb); + } +} + +/** + * return pointer to next free buffer descriptor from core pool + * + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * + * @return pointer to descriptor on success, NULL on error. + */ +struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev) +{ + struct smscore_buffer_t *cb = NULL; + unsigned long flags; + + spin_lock_irqsave(&coredev->bufferslock, flags); + + if (!list_empty(&coredev->buffers)) { + cb = (struct smscore_buffer_t *) coredev->buffers.next; + list_del(&cb->entry); + } + + spin_unlock_irqrestore(&coredev->bufferslock, flags); + + return cb; +} + +/** + * return buffer descriptor to a pool + * + * @param coredev pointer to a coredev object returned by + * smscore_register_device + * @param cb pointer buffer descriptor + * + */ +void smscore_putbuffer(struct smscore_device_t *coredev, + struct smscore_buffer_t *cb) +{ + list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock); +} + +static int smscore_validate_client(struct smscore_device_t *coredev, + struct smscore_client_t *client, + int data_type, int id) +{ + struct smscore_idlist_t *listentry; + struct smscore_client_t *registered_client; + + if (!client) { + sms_err("bad parameter."); + return -EFAULT; + } + registered_client = smscore_find_client(coredev, data_type, id); + if (registered_client == client) + return 0; + + if (registered_client) { + sms_err("The msg ID already registered to another client."); + return -EEXIST; + } + listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL); + if (!listentry) { + sms_err("Can't allocate memory for client id."); + return -ENOMEM; + } + listentry->id = id; + listentry->data_type = data_type; + list_add_locked(&listentry->entry, &client->idlist, + &coredev->clientslock); + return 0; +} + +/** + * creates smsclient object, check that id is taken by another client + * + * @param coredev pointer to a coredev object from clients hotplug + * @param initial_id all messages with this id would be sent to this client + * @param data_type all messages of this type would be sent to this client + * @param onresponse_handler client handler that is called to + * process incoming messages + * @param onremove_handler client handler that is called when device is removed + * @param context client-specific context + * @param client pointer to a value that receives created smsclient object + * + * @return 0 on success, <0 on error. + */ +int smscore_register_client(struct smscore_device_t *coredev, + struct smsclient_params_t *params, + struct smscore_client_t **client) +{ + struct smscore_client_t *newclient; + /* check that no other channel with same parameters exists */ + if (smscore_find_client(coredev, params->data_type, + params->initial_id)) { + sms_err("Client already exist."); + return -EEXIST; + } + + newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL); + if (!newclient) { + sms_err("Failed to allocate memory for client."); + return -ENOMEM; + } + + INIT_LIST_HEAD(&newclient->idlist); + newclient->coredev = coredev; + newclient->onresponse_handler = params->onresponse_handler; + newclient->onremove_handler = params->onremove_handler; + newclient->context = params->context; + list_add_locked(&newclient->entry, &coredev->clients, + &coredev->clientslock); + smscore_validate_client(coredev, newclient, params->data_type, + params->initial_id); + *client = newclient; + sms_debug("%p %d %d", params->context, params->data_type, + params->initial_id); + + return 0; +} + +/** + * frees smsclient object and all subclients associated with it + * + * @param client pointer to smsclient object returned by + * smscore_register_client + * + */ +void smscore_unregister_client(struct smscore_client_t *client) +{ + struct smscore_device_t *coredev = client->coredev; + unsigned long flags; + + spin_lock_irqsave(&coredev->clientslock, flags); + + + while (!list_empty(&client->idlist)) { + struct smscore_idlist_t *identry = + (struct smscore_idlist_t *) client->idlist.next; + list_del(&identry->entry); + kfree(identry); + } + + sms_info("%p", client->context); + + list_del(&client->entry); + kfree(client); + + spin_unlock_irqrestore(&coredev->clientslock, flags); +} + +/** + * verifies that source id is not taken by another client, + * calls device handler to send requests to the device + * + * @param client pointer to smsclient object returned by + * smscore_register_client + * @param buffer pointer to a request buffer + * @param size size (in bytes) of request buffer + * + * @return 0 on success, <0 on error. + */ +int smsclient_sendrequest(struct smscore_client_t *client, + void *buffer, size_t size) +{ + struct smscore_device_t *coredev; + struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer; + int rc; + + if (client == NULL) { + sms_err("Got NULL client"); + return -EINVAL; + } + + coredev = client->coredev; + + /* check that no other channel with same id exists */ + if (coredev == NULL) { + sms_err("Got NULL coredev"); + return -EINVAL; + } + + rc = smscore_validate_client(client->coredev, client, 0, + phdr->msgSrcId); + if (rc < 0) + return rc; + + return coredev->sendrequest_handler(coredev->context, buffer, size); +} + + +int smscore_module_init(void) +{ + int rc = 0; + + INIT_LIST_HEAD(&g_smscore_notifyees); + INIT_LIST_HEAD(&g_smscore_devices); + kmutex_init(&g_smscore_deviceslock); + + INIT_LIST_HEAD(&g_smscore_registry); + kmutex_init(&g_smscore_registrylock); + + /* USB Register */ + rc = smsusb_register(); + + /* DVB Register */ + rc = smsdvb_register(); + + sms_debug("rc %d", rc); + + return rc; +} + +void smscore_module_exit(void) +{ + + kmutex_lock(&g_smscore_deviceslock); + while (!list_empty(&g_smscore_notifyees)) { + struct smscore_device_notifyee_t *notifyee = + (struct smscore_device_notifyee_t *) + g_smscore_notifyees.next; + + list_del(¬ifyee->entry); + kfree(notifyee); + } + kmutex_unlock(&g_smscore_deviceslock); + + kmutex_lock(&g_smscore_registrylock); + while (!list_empty(&g_smscore_registry)) { + struct smscore_registry_entry_t *entry = + (struct smscore_registry_entry_t *) + g_smscore_registry.next; + + list_del(&entry->entry); + kfree(entry); + } + kmutex_unlock(&g_smscore_registrylock); + + /* DVB UnRegister */ + smsdvb_unregister(); + + /* Unregister USB */ + smsusb_unregister(); + + sms_debug(""); +} + +module_init(smscore_module_init); +module_exit(smscore_module_exit); + +MODULE_DESCRIPTION("Driver for the Siano SMS1XXX USB dongle"); +MODULE_AUTHOR("Siano Mobile Silicon,,, (doronc@siano-ms.com)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h new file mode 100644 index 00000000000..c1f8f1dccb1 --- /dev/null +++ b/drivers/media/dvb/siano/smscoreapi.h @@ -0,0 +1,434 @@ +/* + * Driver for the Siano SMS1xxx USB dongle + * + * author: Anatoly Greenblat + * + * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __smscoreapi_h__ +#define __smscoreapi_h__ + +#include <linux/version.h> +#include <linux/device.h> +#include <linux/list.h> +#include <linux/mm.h> +#include <linux/scatterlist.h> +#include <linux/types.h> +#include <asm/page.h> + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" + +#include <linux/mutex.h> + +#define kmutex_init(_p_) mutex_init(_p_) +#define kmutex_lock(_p_) mutex_lock(_p_) +#define kmutex_trylock(_p_) mutex_trylock(_p_) +#define kmutex_unlock(_p_) mutex_unlock(_p_) + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#define SMS_ALLOC_ALIGNMENT 128 +#define SMS_DMA_ALIGNMENT 16 +#define SMS_ALIGN_ADDRESS(addr) \ + ((((uintptr_t)(addr)) + (SMS_DMA_ALIGNMENT-1)) & ~(SMS_DMA_ALIGNMENT-1)) + +#define SMS_DEVICE_FAMILY2 1 +#define SMS_ROM_NO_RESPONSE 2 +#define SMS_DEVICE_NOT_READY 0x8000000 + +enum sms_device_type_st { + SMS_STELLAR = 0, + SMS_NOVA_A0, + SMS_NOVA_B0, + SMS_VEGA, + SMS_NUM_OF_DEVICE_TYPES +}; + +struct smscore_device_t; +struct smscore_client_t; +struct smscore_buffer_t; + +typedef int (*hotplug_t)(struct smscore_device_t *coredev, + struct device *device, int arrival); + +typedef int (*setmode_t)(void *context, int mode); +typedef void (*detectmode_t)(void *context, int *mode); +typedef int (*sendrequest_t)(void *context, void *buffer, size_t size); +typedef int (*loadfirmware_t)(void *context, void *buffer, size_t size); +typedef int (*preload_t)(void *context); +typedef int (*postload_t)(void *context); + +typedef int (*onresponse_t)(void *context, struct smscore_buffer_t *cb); +typedef void (*onremove_t)(void *context); + +struct smscore_buffer_t { + /* public members, once passed to clients can be changed freely */ + struct list_head entry; + int size; + int offset; + + /* private members, read-only for clients */ + void *p; + dma_addr_t phys; + unsigned long offset_in_common; +}; + +struct smsdevice_params_t { + struct device *device; + + int buffer_size; + int num_buffers; + + char devpath[32]; + unsigned long flags; + + setmode_t setmode_handler; + detectmode_t detectmode_handler; + sendrequest_t sendrequest_handler; + preload_t preload_handler; + postload_t postload_handler; + + void *context; + enum sms_device_type_st device_type; +}; + +struct smsclient_params_t { + int initial_id; + int data_type; + onresponse_t onresponse_handler; + onremove_t onremove_handler; + + void *context; +}; + +/* GPIO definitions for antenna frequency domain control (SMS8021) */ +#define SMS_ANTENNA_GPIO_0 1 +#define SMS_ANTENNA_GPIO_1 0 + +#define BW_8_MHZ 0 +#define BW_7_MHZ 1 +#define BW_6_MHZ 2 +#define BW_5_MHZ 3 +#define BW_ISDBT_1SEG 4 +#define BW_ISDBT_3SEG 5 + +#define MSG_HDR_FLAG_SPLIT_MSG 4 + +#define MAX_GPIO_PIN_NUMBER 31 + +#define HIF_TASK 11 +#define SMS_HOST_LIB 150 +#define DVBT_BDA_CONTROL_MSG_ID 201 + +#define SMS_MAX_PAYLOAD_SIZE 240 +#define SMS_TUNE_TIMEOUT 500 + +#define MSG_SMS_GPIO_CONFIG_REQ 507 +#define MSG_SMS_GPIO_CONFIG_RES 508 +#define MSG_SMS_GPIO_SET_LEVEL_REQ 509 +#define MSG_SMS_GPIO_SET_LEVEL_RES 510 +#define MSG_SMS_GPIO_GET_LEVEL_REQ 511 +#define MSG_SMS_GPIO_GET_LEVEL_RES 512 +#define MSG_SMS_RF_TUNE_REQ 561 +#define MSG_SMS_RF_TUNE_RES 562 +#define MSG_SMS_INIT_DEVICE_REQ 578 +#define MSG_SMS_INIT_DEVICE_RES 579 +#define MSG_SMS_ADD_PID_FILTER_REQ 601 +#define MSG_SMS_ADD_PID_FILTER_RES 602 +#define MSG_SMS_REMOVE_PID_FILTER_REQ 603 +#define MSG_SMS_REMOVE_PID_FILTER_RES 604 +#define MSG_SMS_DAB_CHANNEL 607 +#define MSG_SMS_GET_PID_FILTER_LIST_REQ 608 +#define MSG_SMS_GET_PID_FILTER_LIST_RES 609 +#define MSG_SMS_GET_STATISTICS_REQ 615 +#define MSG_SMS_GET_STATISTICS_RES 616 +#define MSG_SMS_SET_ANTENNA_CONFIG_REQ 651 +#define MSG_SMS_SET_ANTENNA_CONFIG_RES 652 +#define MSG_SMS_GET_STATISTICS_EX_REQ 653 +#define MSG_SMS_GET_STATISTICS_EX_RES 654 +#define MSG_SMS_SLEEP_RESUME_COMP_IND 655 +#define MSG_SMS_DATA_DOWNLOAD_REQ 660 +#define MSG_SMS_DATA_DOWNLOAD_RES 661 +#define MSG_SMS_SWDOWNLOAD_TRIGGER_REQ 664 +#define MSG_SMS_SWDOWNLOAD_TRIGGER_RES 665 +#define MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ 666 +#define MSG_SMS_SWDOWNLOAD_BACKDOOR_RES 667 +#define MSG_SMS_GET_VERSION_EX_REQ 668 +#define MSG_SMS_GET_VERSION_EX_RES 669 +#define MSG_SMS_SET_CLOCK_OUTPUT_REQ 670 +#define MSG_SMS_I2C_SET_FREQ_REQ 685 +#define MSG_SMS_GENERIC_I2C_REQ 687 +#define MSG_SMS_GENERIC_I2C_RES 688 +#define MSG_SMS_DVBT_BDA_DATA 693 +#define MSG_SW_RELOAD_REQ 697 +#define MSG_SMS_DATA_MSG 699 +#define MSG_SW_RELOAD_START_REQ 702 +#define MSG_SW_RELOAD_START_RES 703 +#define MSG_SW_RELOAD_EXEC_REQ 704 +#define MSG_SW_RELOAD_EXEC_RES 705 +#define MSG_SMS_SPI_INT_LINE_SET_REQ 710 +#define MSG_SMS_ISDBT_TUNE_REQ 776 +#define MSG_SMS_ISDBT_TUNE_RES 777 + +#define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \ + (ptr)->msgType = type; (ptr)->msgSrcId = src; (ptr)->msgDstId = dst; \ + (ptr)->msgLength = len; (ptr)->msgFlags = 0; \ +} while (0) +#define SMS_INIT_MSG(ptr, type, len) \ + SMS_INIT_MSG_EX(ptr, type, 0, HIF_TASK, len) + +enum SMS_DEVICE_MODE { + DEVICE_MODE_NONE = -1, + DEVICE_MODE_DVBT = 0, + DEVICE_MODE_DVBH, + DEVICE_MODE_DAB_TDMB, + DEVICE_MODE_DAB_TDMB_DABIP, + DEVICE_MODE_DVBT_BDA, + DEVICE_MODE_ISDBT, + DEVICE_MODE_ISDBT_BDA, + DEVICE_MODE_CMMB, + DEVICE_MODE_RAW_TUNER, + DEVICE_MODE_MAX, +}; + +struct SmsMsgHdr_ST { + u16 msgType; + u8 msgSrcId; + u8 msgDstId; + u16 msgLength; /* Length of entire message, including header */ + u16 msgFlags; +}; + +struct SmsMsgData_ST { + struct SmsMsgHdr_ST xMsgHeader; + u32 msgData[1]; +}; + +struct SmsDataDownload_ST { + struct SmsMsgHdr_ST xMsgHeader; + u32 MemAddr; + u8 Payload[SMS_MAX_PAYLOAD_SIZE]; +}; + +struct SmsVersionRes_ST { + struct SmsMsgHdr_ST xMsgHeader; + + u16 ChipModel; /* e.g. 0x1102 for SMS-1102 "Nova" */ + u8 Step; /* 0 - Step A */ + u8 MetalFix; /* 0 - Metal 0 */ + + u8 FirmwareId; /* 0xFF � ROM, otherwise the + * value indicated by + * SMSHOSTLIB_DEVICE_MODES_E */ + u8 SupportedProtocols; /* Bitwise OR combination of + * supported protocols */ + + u8 VersionMajor; + u8 VersionMinor; + u8 VersionPatch; + u8 VersionFieldPatch; + + u8 RomVersionMajor; + u8 RomVersionMinor; + u8 RomVersionPatch; + u8 RomVersionFieldPatch; + + u8 TextLabel[34]; +}; + +struct SmsFirmware_ST { + u32 CheckSum; + u32 Length; + u32 StartAddress; + u8 Payload[1]; +}; + +struct SMSHOSTLIB_STATISTICS_ST { + u32 Reserved; /* Reserved */ + + /* Common parameters */ + u32 IsRfLocked; /* 0 - not locked, 1 - locked */ + u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ + u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ + + /* Reception quality */ + s32 SNR; /* dB */ + u32 BER; /* Post Viterbi BER [1E-5] */ + u32 FIB_CRC; /* CRC errors percentage, valid only for DAB */ + u32 TS_PER; /* Transport stream PER, 0xFFFFFFFF indicate N/A, + * valid only for DVB-T/H */ + u32 MFER; /* DVB-H frame error rate in percentage, + * 0xFFFFFFFF indicate N/A, valid only for DVB-H */ + s32 RSSI; /* dBm */ + s32 InBandPwr; /* In band power in dBM */ + s32 CarrierOffset; /* Carrier Offset in bin/1024 */ + + /* Transmission parameters, valid only for DVB-T/H */ + u32 Frequency; /* Frequency in Hz */ + u32 Bandwidth; /* Bandwidth in MHz */ + u32 TransmissionMode; /* Transmission Mode, for DAB modes 1-4, + * for DVB-T/H FFT mode carriers in Kilos */ + u32 ModemState; /* from SMS_DvbModemState_ET */ + u32 GuardInterval; /* Guard Interval, 1 divided by value */ + u32 CodeRate; /* Code Rate from SMS_DvbModemState_ET */ + u32 LPCodeRate; /* Low Priority Code Rate from SMS_DvbModemState_ET */ + u32 Hierarchy; /* Hierarchy from SMS_Hierarchy_ET */ + u32 Constellation; /* Constellation from SMS_Constellation_ET */ + + /* Burst parameters, valid only for DVB-H */ + u32 BurstSize; /* Current burst size in bytes */ + u32 BurstDuration; /* Current burst duration in mSec */ + u32 BurstCycleTime; /* Current burst cycle time in mSec */ + u32 CalculatedBurstCycleTime; /* Current burst cycle time in mSec, + * as calculated by demodulator */ + u32 NumOfRows; /* Number of rows in MPE table */ + u32 NumOfPaddCols; /* Number of padding columns in MPE table */ + u32 NumOfPunctCols; /* Number of puncturing columns in MPE table */ + /* Burst parameters */ + u32 ErrorTSPackets; /* Number of erroneous transport-stream packets */ + u32 TotalTSPackets; /* Total number of transport-stream packets */ + u32 NumOfValidMpeTlbs; /* Number of MPE tables which do not include + * errors after MPE RS decoding */ + u32 NumOfInvalidMpeTlbs; /* Number of MPE tables which include errors + * after MPE RS decoding */ + u32 NumOfCorrectedMpeTlbs; /* Number of MPE tables which were corrected + * by MPE RS decoding */ + + /* Common params */ + u32 BERErrorCount; /* Number of errornous SYNC bits. */ + u32 BERBitCount; /* Total number of SYNC bits. */ + + /* Interface information */ + u32 SmsToHostTxErrors; /* Total number of transmission errors. */ + + /* DAB/T-DMB */ + u32 PreBER; /* DAB/T-DMB only: Pre Viterbi BER [1E-5] */ + + /* DVB-H TPS parameters */ + u32 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; + * if set to 0xFFFFFFFF cell_id not yet recovered */ + +}; + +struct SmsMsgStatisticsInfo_ST { + u32 RequestResult; + + struct SMSHOSTLIB_STATISTICS_ST Stat; + + /* Split the calc of the SNR in DAB */ + u32 Signal; /* dB */ + u32 Noise; /* dB */ + +}; + + +struct smsdvb_client_t { + struct list_head entry; + + struct smscore_device_t *coredev; + struct smscore_client_t *smsclient; + + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dvb_frontend frontend; + + fe_status_t fe_status; + int fe_ber, fe_snr, fe_signal_strength; + + struct completion tune_done, stat_done; + + /* todo: save freq/band instead whole struct */ + struct dvb_frontend_parameters fe_params; + +}; + +extern void smscore_registry_setmode(char *devpath, int mode); +extern int smscore_registry_getmode(char *devpath); + +extern int smscore_register_hotplug(hotplug_t hotplug); +extern void smscore_unregister_hotplug(hotplug_t hotplug); + +extern int smscore_register_device(struct smsdevice_params_t *params, + struct smscore_device_t **coredev); +extern void smscore_unregister_device(struct smscore_device_t *coredev); + +extern int smscore_start_device(struct smscore_device_t *coredev); +extern int smscore_load_firmware(struct smscore_device_t *coredev, + char *filename, + loadfirmware_t loadfirmware_handler); + +extern int smscore_set_device_mode(struct smscore_device_t *coredev, int mode); +extern int smscore_get_device_mode(struct smscore_device_t *coredev); + +extern int smscore_register_client(struct smscore_device_t *coredev, + struct smsclient_params_t *params, + struct smscore_client_t **client); +extern void smscore_unregister_client(struct smscore_client_t *client); + +extern int smsclient_sendrequest(struct smscore_client_t *client, + void *buffer, size_t size); +extern void smscore_onresponse(struct smscore_device_t *coredev, + struct smscore_buffer_t *cb); + + +extern +struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev); +extern void smscore_putbuffer(struct smscore_device_t *coredev, + struct smscore_buffer_t *cb); + +void smscore_set_board_id(struct smscore_device_t *core, int id); +int smscore_get_board_id(struct smscore_device_t *core); + +/* smsdvb.c */ +int smsdvb_register(void); +void smsdvb_unregister(void); + +/* smsusb.c */ +int smsusb_register(void); +void smsusb_unregister(void); + +/* ------------------------------------------------------------------------ */ + +extern int sms_debug; + +#define DBG_INFO 1 +#define DBG_ADV 2 + +#define sms_printk(kern, fmt, arg...) \ + printk(kern "%s: " fmt "\n", __func__, ##arg) + +#define dprintk(kern, lvl, fmt, arg...) do {\ + if (sms_debug & lvl) \ + sms_printk(kern, fmt, ##arg); } while (0) + +#define sms_log(fmt, arg...) sms_printk(KERN_INFO, fmt, ##arg) +#define sms_err(fmt, arg...) \ + sms_printk(KERN_ERR, "line: %d: " fmt, __LINE__, ##arg) +#define sms_warn(fmt, arg...) sms_printk(KERN_WARNING, fmt, ##arg) +#define sms_info(fmt, arg...) \ + dprintk(KERN_INFO, DBG_INFO, fmt, ##arg) +#define sms_debug(fmt, arg...) \ + dprintk(KERN_DEBUG, DBG_ADV, fmt, ##arg) + + +#endif /* __smscoreapi_h__ */ diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c new file mode 100644 index 00000000000..6f9c1856386 --- /dev/null +++ b/drivers/media/dvb/siano/smsdvb.c @@ -0,0 +1,449 @@ +/* + * Driver for the Siano SMS1xxx USB dongle + * + * author: Anatoly Greenblat + * + * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> + +#include "smscoreapi.h" +#include "sms-cards.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct list_head g_smsdvb_clients; +struct mutex g_smsdvb_clientslock; + +static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) +{ + struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; + struct SmsMsgHdr_ST *phdr = + (struct SmsMsgHdr_ST *)(((u8 *) cb->p) + cb->offset); + + switch (phdr->msgType) { + case MSG_SMS_DVBT_BDA_DATA: + dvb_dmx_swfilter(&client->demux, (u8 *)(phdr + 1), + cb->size - sizeof(struct SmsMsgHdr_ST)); + break; + + case MSG_SMS_RF_TUNE_RES: + complete(&client->tune_done); + break; + + case MSG_SMS_GET_STATISTICS_RES: + { + struct SmsMsgStatisticsInfo_ST *p = + (struct SmsMsgStatisticsInfo_ST *)(phdr + 1); + + if (p->Stat.IsDemodLocked) { + client->fe_status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; + + client->fe_snr = p->Stat.SNR; + client->fe_ber = p->Stat.BER; + + if (p->Stat.InBandPwr < -95) + client->fe_signal_strength = 0; + else if (p->Stat.InBandPwr > -29) + client->fe_signal_strength = 100; + else + client->fe_signal_strength = + (p->Stat.InBandPwr + 95) * 3 / 2; + } else { + client->fe_status = 0; + client->fe_snr = + client->fe_ber = + client->fe_signal_strength = 0; + } + + complete(&client->stat_done); + break; + } } + + smscore_putbuffer(client->coredev, cb); + + return 0; +} + +static void smsdvb_unregister_client(struct smsdvb_client_t *client) +{ + /* must be called under clientslock */ + + list_del(&client->entry); + + smscore_unregister_client(client->smsclient); + dvb_unregister_frontend(&client->frontend); + dvb_dmxdev_release(&client->dmxdev); + dvb_dmx_release(&client->demux); + dvb_unregister_adapter(&client->adapter); + kfree(client); +} + +static void smsdvb_onremove(void *context) +{ + kmutex_lock(&g_smsdvb_clientslock); + + smsdvb_unregister_client((struct smsdvb_client_t *) context); + + kmutex_unlock(&g_smsdvb_clientslock); +} + +static int smsdvb_start_feed(struct dvb_demux_feed *feed) +{ + struct smsdvb_client_t *client = + container_of(feed->demux, struct smsdvb_client_t, demux); + struct SmsMsgData_ST PidMsg; + + sms_debug("add pid %d(%x)", + feed->pid, feed->pid); + + PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + PidMsg.xMsgHeader.msgDstId = HIF_TASK; + PidMsg.xMsgHeader.msgFlags = 0; + PidMsg.xMsgHeader.msgType = MSG_SMS_ADD_PID_FILTER_REQ; + PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); + PidMsg.msgData[0] = feed->pid; + + return smsclient_sendrequest(client->smsclient, + &PidMsg, sizeof(PidMsg)); +} + +static int smsdvb_stop_feed(struct dvb_demux_feed *feed) +{ + struct smsdvb_client_t *client = + container_of(feed->demux, struct smsdvb_client_t, demux); + struct SmsMsgData_ST PidMsg; + + sms_debug("remove pid %d(%x)", + feed->pid, feed->pid); + + PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + PidMsg.xMsgHeader.msgDstId = HIF_TASK; + PidMsg.xMsgHeader.msgFlags = 0; + PidMsg.xMsgHeader.msgType = MSG_SMS_REMOVE_PID_FILTER_REQ; + PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); + PidMsg.msgData[0] = feed->pid; + + return smsclient_sendrequest(client->smsclient, + &PidMsg, sizeof(PidMsg)); +} + +static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, + void *buffer, size_t size, + struct completion *completion) +{ + int rc = smsclient_sendrequest(client->smsclient, buffer, size); + if (rc < 0) + return rc; + + return wait_for_completion_timeout(completion, + msecs_to_jiffies(2000)) ? + 0 : -ETIME; +} + +static int smsdvb_send_statistics_request(struct smsdvb_client_t *client) +{ + struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, + DVBT_BDA_CONTROL_MSG_ID, + HIF_TASK, sizeof(struct SmsMsgHdr_ST), 0 }; + return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), + &client->stat_done); +} + +static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + int rc = smsdvb_send_statistics_request(client); + + if (!rc) + *stat = client->fe_status; + + return rc; +} + +static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + int rc = smsdvb_send_statistics_request(client); + + if (!rc) + *ber = client->fe_ber; + + return rc; +} + +static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + int rc = smsdvb_send_statistics_request(client); + + if (!rc) + *strength = client->fe_signal_strength; + + return rc; +} + +static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + int rc = smsdvb_send_statistics_request(client); + + if (!rc) + *snr = client->fe_snr; + + return rc; +} + +static int smsdvb_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + sms_debug(""); + + tune->min_delay_ms = 400; + tune->step_size = 250000; + tune->max_drift = 0; + return 0; +} + +static int smsdvb_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + + struct { + struct SmsMsgHdr_ST Msg; + u32 Data[3]; + } Msg; + + Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + Msg.Msg.msgDstId = HIF_TASK; + Msg.Msg.msgFlags = 0; + Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ; + Msg.Msg.msgLength = sizeof(Msg); + Msg.Data[0] = fep->frequency; + Msg.Data[2] = 12000000; + + sms_debug("freq %d band %d", + fep->frequency, fep->u.ofdm.bandwidth); + + switch (fep->u.ofdm.bandwidth) { + case BANDWIDTH_8_MHZ: Msg.Data[1] = BW_8_MHZ; break; + case BANDWIDTH_7_MHZ: Msg.Data[1] = BW_7_MHZ; break; + case BANDWIDTH_6_MHZ: Msg.Data[1] = BW_6_MHZ; break; + case BANDWIDTH_AUTO: return -EOPNOTSUPP; + default: return -EINVAL; + } + + return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), + &client->tune_done); +} + +static int smsdvb_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + + sms_debug(""); + + /* todo: */ + memcpy(fep, &client->fe_params, + sizeof(struct dvb_frontend_parameters)); + return 0; +} + +static void smsdvb_release(struct dvb_frontend *fe) +{ + /* do nothing */ +} + +static struct dvb_frontend_ops smsdvb_fe_ops = { + .info = { + .name = "Siano Mobile Digital SMS1xxx", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 250000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = smsdvb_release, + + .set_frontend = smsdvb_set_frontend, + .get_frontend = smsdvb_get_frontend, + .get_tune_settings = smsdvb_get_tune_settings, + + .read_status = smsdvb_read_status, + .read_ber = smsdvb_read_ber, + .read_signal_strength = smsdvb_read_signal_strength, + .read_snr = smsdvb_read_snr, +}; + +static int smsdvb_hotplug(struct smscore_device_t *coredev, + struct device *device, int arrival) +{ + struct smsclient_params_t params; + struct smsdvb_client_t *client; + int rc; + + /* device removal handled by onremove callback */ + if (!arrival) + return 0; + + if (smscore_get_device_mode(coredev) != 4) { + sms_err("SMS Device mode is not set for " + "DVB operation."); + return 0; + } + + client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL); + if (!client) { + sms_err("kmalloc() failed"); + return -ENOMEM; + } + + /* register dvb adapter */ + rc = dvb_register_adapter(&client->adapter, + sms_get_board( + smscore_get_board_id(coredev))->name, + THIS_MODULE, device, adapter_nr); + if (rc < 0) { + sms_err("dvb_register_adapter() failed %d", rc); + goto adapter_error; + } + + /* init dvb demux */ + client->demux.dmx.capabilities = DMX_TS_FILTERING; + client->demux.filternum = 32; /* todo: nova ??? */ + client->demux.feednum = 32; + client->demux.start_feed = smsdvb_start_feed; + client->demux.stop_feed = smsdvb_stop_feed; + + rc = dvb_dmx_init(&client->demux); + if (rc < 0) { + sms_err("dvb_dmx_init failed %d", rc); + goto dvbdmx_error; + } + + /* init dmxdev */ + client->dmxdev.filternum = 32; + client->dmxdev.demux = &client->demux.dmx; + client->dmxdev.capabilities = 0; + + rc = dvb_dmxdev_init(&client->dmxdev, &client->adapter); + if (rc < 0) { + sms_err("dvb_dmxdev_init failed %d", rc); + goto dmxdev_error; + } + + /* init and register frontend */ + memcpy(&client->frontend.ops, &smsdvb_fe_ops, + sizeof(struct dvb_frontend_ops)); + + rc = dvb_register_frontend(&client->adapter, &client->frontend); + if (rc < 0) { + sms_err("frontend registration failed %d", rc); + goto frontend_error; + } + + params.initial_id = 1; + params.data_type = MSG_SMS_DVBT_BDA_DATA; + params.onresponse_handler = smsdvb_onresponse; + params.onremove_handler = smsdvb_onremove; + params.context = client; + + rc = smscore_register_client(coredev, ¶ms, &client->smsclient); + if (rc < 0) { + sms_err("smscore_register_client() failed %d", rc); + goto client_error; + } + + client->coredev = coredev; + + init_completion(&client->tune_done); + init_completion(&client->stat_done); + + kmutex_lock(&g_smsdvb_clientslock); + + list_add(&client->entry, &g_smsdvb_clients); + + kmutex_unlock(&g_smsdvb_clientslock); + + sms_info("success"); + + return 0; + +client_error: + dvb_unregister_frontend(&client->frontend); + +frontend_error: + dvb_dmxdev_release(&client->dmxdev); + +dmxdev_error: + dvb_dmx_release(&client->demux); + +dvbdmx_error: + dvb_unregister_adapter(&client->adapter); + +adapter_error: + kfree(client); + return rc; +} + +int smsdvb_register(void) +{ + int rc; + + INIT_LIST_HEAD(&g_smsdvb_clients); + kmutex_init(&g_smsdvb_clientslock); + + rc = smscore_register_hotplug(smsdvb_hotplug); + + sms_debug(""); + + return rc; +} + +void smsdvb_unregister(void) +{ + smscore_unregister_hotplug(smsdvb_hotplug); + + kmutex_lock(&g_smsdvb_clientslock); + + while (!list_empty(&g_smsdvb_clients)) + smsdvb_unregister_client( + (struct smsdvb_client_t *) g_smsdvb_clients.next); + + kmutex_unlock(&g_smsdvb_clientslock); +} diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c new file mode 100644 index 00000000000..c10b1849c6a --- /dev/null +++ b/drivers/media/dvb/siano/smsusb.c @@ -0,0 +1,459 @@ +/* + * Driver for the Siano SMS1xxx USB dongle + * + * author: Anatoly Greenblat + * + * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/firmware.h> + +#include "smscoreapi.h" +#include "sms-cards.h" + +#define USB1_BUFFER_SIZE 0x1000 +#define USB2_BUFFER_SIZE 0x4000 + +#define MAX_BUFFERS 50 +#define MAX_URBS 10 + +struct smsusb_device_t; + +struct smsusb_urb_t { + struct smscore_buffer_t *cb; + struct smsusb_device_t *dev; + + struct urb urb; +}; + +struct smsusb_device_t { + struct usb_device *udev; + struct smscore_device_t *coredev; + + struct smsusb_urb_t surbs[MAX_URBS]; + + int response_alignment; + int buffer_size; +}; + +static int smsusb_submit_urb(struct smsusb_device_t *dev, + struct smsusb_urb_t *surb); + +static void smsusb_onresponse(struct urb *urb) +{ + struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context; + struct smsusb_device_t *dev = surb->dev; + + if (urb->status < 0) { + sms_err("error, urb status %d, %d bytes", + urb->status, urb->actual_length); + return; + } + + if (urb->actual_length > 0) { + struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) surb->cb->p; + + if (urb->actual_length >= phdr->msgLength) { + surb->cb->size = phdr->msgLength; + + if (dev->response_alignment && + (phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG)) { + + surb->cb->offset = + dev->response_alignment + + ((phdr->msgFlags >> 8) & 3); + + /* sanity check */ + if (((int) phdr->msgLength + + surb->cb->offset) > urb->actual_length) { + sms_err("invalid response " + "msglen %d offset %d " + "size %d", + phdr->msgLength, + surb->cb->offset, + urb->actual_length); + goto exit_and_resubmit; + } + + /* move buffer pointer and + * copy header to its new location */ + memcpy((char *) phdr + surb->cb->offset, + phdr, sizeof(struct SmsMsgHdr_ST)); + } else + surb->cb->offset = 0; + + smscore_onresponse(dev->coredev, surb->cb); + surb->cb = NULL; + } else { + sms_err("invalid response " + "msglen %d actual %d", + phdr->msgLength, urb->actual_length); + } + } + +exit_and_resubmit: + smsusb_submit_urb(dev, surb); +} + +static int smsusb_submit_urb(struct smsusb_device_t *dev, + struct smsusb_urb_t *surb) +{ + if (!surb->cb) { + surb->cb = smscore_getbuffer(dev->coredev); + if (!surb->cb) { + sms_err("smscore_getbuffer(...) returned NULL"); + return -ENOMEM; + } + } + + usb_fill_bulk_urb( + &surb->urb, + dev->udev, + usb_rcvbulkpipe(dev->udev, 0x81), + surb->cb->p, + dev->buffer_size, + smsusb_onresponse, + surb + ); + surb->urb.transfer_dma = surb->cb->phys; + surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + return usb_submit_urb(&surb->urb, GFP_ATOMIC); +} + +static void smsusb_stop_streaming(struct smsusb_device_t *dev) +{ + int i; + + for (i = 0; i < MAX_URBS; i++) { + usb_kill_urb(&dev->surbs[i].urb); + + if (dev->surbs[i].cb) { + smscore_putbuffer(dev->coredev, dev->surbs[i].cb); + dev->surbs[i].cb = NULL; + } + } +} + +static int smsusb_start_streaming(struct smsusb_device_t *dev) +{ + int i, rc; + + for (i = 0; i < MAX_URBS; i++) { + rc = smsusb_submit_urb(dev, &dev->surbs[i]); + if (rc < 0) { + sms_err("smsusb_submit_urb(...) failed"); + smsusb_stop_streaming(dev); + break; + } + } + + return rc; +} + +static int smsusb_sendrequest(void *context, void *buffer, size_t size) +{ + struct smsusb_device_t *dev = (struct smsusb_device_t *) context; + int dummy; + + return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), + buffer, size, &dummy, 1000); +} + +static char *smsusb1_fw_lkup[] = { + "dvbt_stellar_usb.inp", + "dvbh_stellar_usb.inp", + "tdmb_stellar_usb.inp", + "none", + "dvbt_bda_stellar_usb.inp", +}; + +static inline char *sms_get_fw_name(int mode, int board_id) +{ + char **fw = sms_get_board(board_id)->fw; + return (fw && fw[mode]) ? fw[mode] : smsusb1_fw_lkup[mode]; +} + +static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id) +{ + const struct firmware *fw; + u8 *fw_buffer; + int rc, dummy; + char *fw_filename; + + if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) { + sms_err("invalid firmware id specified %d", id); + return -EINVAL; + } + + fw_filename = sms_get_fw_name(id, board_id); + + rc = request_firmware(&fw, fw_filename, &udev->dev); + if (rc < 0) { + sms_warn("failed to open \"%s\" mode %d, " + "trying again with default firmware", fw_filename, id); + + fw_filename = smsusb1_fw_lkup[id]; + rc = request_firmware(&fw, fw_filename, &udev->dev); + if (rc < 0) { + sms_warn("failed to open \"%s\" mode %d", + fw_filename, id); + + return rc; + } + } + + fw_buffer = kmalloc(fw->size, GFP_KERNEL); + if (fw_buffer) { + memcpy(fw_buffer, fw->data, fw->size); + + rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2), + fw_buffer, fw->size, &dummy, 1000); + + sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc); + + kfree(fw_buffer); + } else { + sms_err("failed to allocate firmware buffer"); + rc = -ENOMEM; + } + sms_info("read FW %s, size=%zd", fw_filename, fw->size); + + release_firmware(fw); + + return rc; +} + +static void smsusb1_detectmode(void *context, int *mode) +{ + char *product_string = + ((struct smsusb_device_t *) context)->udev->product; + + *mode = DEVICE_MODE_NONE; + + if (!product_string) { + product_string = "none"; + sms_err("product string not found"); + } else if (strstr(product_string, "DVBH")) + *mode = 1; + else if (strstr(product_string, "BDA")) + *mode = 4; + else if (strstr(product_string, "DVBT")) + *mode = 0; + else if (strstr(product_string, "TDMB")) + *mode = 2; + + sms_info("%d \"%s\"", *mode, product_string); +} + +static int smsusb1_setmode(void *context, int mode) +{ + struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK, + sizeof(struct SmsMsgHdr_ST), 0 }; + + if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { + sms_err("invalid firmware id specified %d", mode); + return -EINVAL; + } + + return smsusb_sendrequest(context, &Msg, sizeof(Msg)); +} + +static void smsusb_term_device(struct usb_interface *intf) +{ + struct smsusb_device_t *dev = + (struct smsusb_device_t *) usb_get_intfdata(intf); + + if (dev) { + smsusb_stop_streaming(dev); + + /* unregister from smscore */ + if (dev->coredev) + smscore_unregister_device(dev->coredev); + + kfree(dev); + + sms_info("device %p destroyed", dev); + } + + usb_set_intfdata(intf, NULL); +} + +static int smsusb_init_device(struct usb_interface *intf, int board_id) +{ + struct smsdevice_params_t params; + struct smsusb_device_t *dev; + int i, rc; + + /* create device object */ + dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL); + if (!dev) { + sms_err("kzalloc(sizeof(struct smsusb_device_t) failed"); + return -ENOMEM; + } + + memset(¶ms, 0, sizeof(params)); + usb_set_intfdata(intf, dev); + dev->udev = interface_to_usbdev(intf); + + params.device_type = sms_get_board(board_id)->type; + + switch (params.device_type) { + case SMS_STELLAR: + dev->buffer_size = USB1_BUFFER_SIZE; + + params.setmode_handler = smsusb1_setmode; + params.detectmode_handler = smsusb1_detectmode; + break; + default: + sms_err("Unspecified sms device type!"); + /* fall-thru */ + case SMS_NOVA_A0: + case SMS_NOVA_B0: + case SMS_VEGA: + dev->buffer_size = USB2_BUFFER_SIZE; + dev->response_alignment = + dev->udev->ep_in[1]->desc.wMaxPacketSize - + sizeof(struct SmsMsgHdr_ST); + + params.flags |= SMS_DEVICE_FAMILY2; + break; + } + + params.device = &dev->udev->dev; + params.buffer_size = dev->buffer_size; + params.num_buffers = MAX_BUFFERS; + params.sendrequest_handler = smsusb_sendrequest; + params.context = dev; + snprintf(params.devpath, sizeof(params.devpath), + "usb\\%d-%s", dev->udev->bus->busnum, dev->udev->devpath); + + /* register in smscore */ + rc = smscore_register_device(¶ms, &dev->coredev); + if (rc < 0) { + sms_err("smscore_register_device(...) failed, rc %d", rc); + smsusb_term_device(intf); + return rc; + } + + smscore_set_board_id(dev->coredev, board_id); + + /* initialize urbs */ + for (i = 0; i < MAX_URBS; i++) { + dev->surbs[i].dev = dev; + usb_init_urb(&dev->surbs[i].urb); + } + + sms_info("smsusb_start_streaming(...)."); + rc = smsusb_start_streaming(dev); + if (rc < 0) { + sms_err("smsusb_start_streaming(...) failed"); + smsusb_term_device(intf); + return rc; + } + + rc = smscore_start_device(dev->coredev); + if (rc < 0) { + sms_err("smscore_start_device(...) failed"); + smsusb_term_device(intf); + return rc; + } + + sms_info("device %p created", dev); + + return rc; +} + +static int smsusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + char devpath[32]; + int i, rc; + + rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81)); + rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02)); + + if (intf->num_altsetting > 0) { + rc = usb_set_interface( + udev, intf->cur_altsetting->desc.bInterfaceNumber, 0); + if (rc < 0) { + sms_err("usb_set_interface failed, rc %d", rc); + return rc; + } + } + + sms_info("smsusb_probe %d", + intf->cur_altsetting->desc.bInterfaceNumber); + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) + sms_info("endpoint %d %02x %02x %d", i, + intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, + intf->cur_altsetting->endpoint[i].desc.bmAttributes, + intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize); + + if ((udev->actconfig->desc.bNumInterfaces == 2) && + (intf->cur_altsetting->desc.bInterfaceNumber == 0)) { + sms_err("rom interface 0 is not used"); + return -ENODEV; + } + + if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { + snprintf(devpath, sizeof(devpath), "usb\\%d-%s", + udev->bus->busnum, udev->devpath); + sms_info("stellar device was found."); + return smsusb1_load_firmware( + udev, smscore_registry_getmode(devpath), + id->driver_info); + } + + rc = smsusb_init_device(intf, id->driver_info); + sms_info("rc %d", rc); + return rc; +} + +static void smsusb_disconnect(struct usb_interface *intf) +{ + smsusb_term_device(intf); +} + +static struct usb_driver smsusb_driver = { + .name = "sms1xxx", + .probe = smsusb_probe, + .disconnect = smsusb_disconnect, + .id_table = smsusb_id_table, +}; + +int smsusb_register(void) +{ + int rc = usb_register(&smsusb_driver); + if (rc) + sms_err("usb_register failed. Error number %d", rc); + + sms_debug(""); + + return rc; +} + +void smsusb_unregister(void) +{ + sms_debug(""); + /* Regular USB Cleanup */ + usb_deregister(&smsusb_driver); +} + diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index 07643e01009..87c973ac668 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -106,6 +106,8 @@ config DVB_BUDGET_CI select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE select VIDEO_IR help Support for simple SAA7146 based DVB cards diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile index d7483f1a9b3..71451237294 100644 --- a/drivers/media/dvb/ttpci/Makefile +++ b/drivers/media/dvb/ttpci/Makefile @@ -3,7 +3,11 @@ # and the AV7110 DVB device driver # -dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o av7110_ir.o +dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o + +ifdef CONFIG_INPUT_EVDEV +dvb-ttpci-objs += av7110_ir.o +endif obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o @@ -14,6 +18,7 @@ obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ +EXTRA_CFLAGS += -Idrivers/media/common/tuners hostprogs-y := fdump diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index f05d43d8b5c..0777e8f9544 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -587,7 +587,7 @@ static void gpioirq(unsigned long data) } DVB_RINGBUFFER_SKIP(cibuf, 2); - dvb_ringbuffer_read(cibuf, av7110->debi_virt, len, 0); + dvb_ringbuffer_read(cibuf, av7110->debi_virt, len); iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); @@ -1198,7 +1198,6 @@ static int start_ts_capture(struct av7110 *budget) if (budget->feeding1) return ++budget->feeding1; memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH); - budget->tsf = 0xff; budget->ttbp = 0; SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ @@ -2403,18 +2402,18 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, saa7146_write(dev, MC1, MASK_29); /* RPS1 timeout disable */ saa7146_write(dev, RPS_TOV1, 0); - WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_VBI_B)); - WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2))); - WRITE_RPS1(cpu_to_le32(GPIO3_MSK)); - WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24)); + WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); #if RPS_IRQ /* issue RPS1 interrupt to increment counter */ - WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT)); + WRITE_RPS1(CMD_INTERRUPT); #endif - WRITE_RPS1(cpu_to_le32(CMD_STOP)); + WRITE_RPS1(CMD_STOP); /* Jump to begin of RPS program as safety measure (p37) */ - WRITE_RPS1(cpu_to_le32(CMD_JUMP)); - WRITE_RPS1(cpu_to_le32(dev->d_rps1.dma_handle)); + WRITE_RPS1(CMD_JUMP); + WRITE_RPS1(dev->d_rps1.dma_handle); #if RPS_IRQ /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) @@ -2472,11 +2471,7 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, get recognized before the main driver is fully loaded */ saa7146_write(dev, GPIO_CTRL, 0x500000); -#ifdef I2C_ADAP_CLASS_TV_DIGITAL - av7110->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL; -#else av7110->i2c_adap.class = I2C_CLASS_TV_DIGITAL; -#endif strlcpy(av7110->i2c_adap.name, pci_ext->ext_priv, sizeof(av7110->i2c_adap.name)); saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */ @@ -2527,28 +2522,28 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, count = 0; /* Wait Source Line Counter Threshold (p36) */ - WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_HS)); + WRITE_RPS1(CMD_PAUSE | EVT_HS); /* Set GPIO3=1 (p42) */ - WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2))); - WRITE_RPS1(cpu_to_le32(GPIO3_MSK)); - WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTHI<<24)); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); #if RPS_IRQ /* issue RPS1 interrupt */ - WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT)); + WRITE_RPS1(CMD_INTERRUPT); #endif /* Wait reset Source Line Counter Threshold (p36) */ - WRITE_RPS1(cpu_to_le32(CMD_PAUSE | RPS_INV | EVT_HS)); + WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); /* Set GPIO3=0 (p42) */ - WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2))); - WRITE_RPS1(cpu_to_le32(GPIO3_MSK)); - WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24)); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); #if RPS_IRQ /* issue RPS1 interrupt */ - WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT)); + WRITE_RPS1(CMD_INTERRUPT); #endif /* Jump to begin of RPS program (p37) */ - WRITE_RPS1(cpu_to_le32(CMD_JUMP)); - WRITE_RPS1(cpu_to_le32(dev->d_rps1.dma_handle)); + WRITE_RPS1(CMD_JUMP); + WRITE_RPS1(dev->d_rps1.dma_handle); /* Fix VSYNC level */ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h index e494e04eeee..55f23ddcb99 100644 --- a/drivers/media/dvb/ttpci/av7110.h +++ b/drivers/media/dvb/ttpci/av7110.h @@ -188,7 +188,6 @@ struct av7110 { struct dvb_net dvb_net1; spinlock_t feedlock1; int feeding1; - u8 tsf; u32 ttbp; unsigned char *grabbing; struct saa7146_pgtable pt; diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c index ec55a968f20..184647ad1c7 100644 --- a/drivers/media/dvb/ttpci/av7110_av.c +++ b/drivers/media/dvb/ttpci/av7110_av.c @@ -269,7 +269,7 @@ int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) return -1; } - dvb_ringbuffer_read(buf, dest, (size_t) blen, 0); + dvb_ringbuffer_read(buf, dest, (size_t) blen); dprintk(2, "pread=0x%08lx, pwrite=0x%08lx\n", (unsigned long) buf->pread, (unsigned long) buf->pwrite); diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/dvb/ttpci/av7110_ca.c index c58e3fc509e..261135ded48 100644 --- a/drivers/media/dvb/ttpci/av7110_ca.c +++ b/drivers/media/dvb/ttpci/av7110_ca.c @@ -208,7 +208,7 @@ static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file, return -EINVAL; DVB_RINGBUFFER_SKIP(cibuf, 2); - return dvb_ringbuffer_read(cibuf, (u8 *)buf, len, 1); + return dvb_ringbuffer_read_user(cibuf, buf, len); } static int dvb_ca_open(struct inode *inode, struct file *file) diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h index 74d940f75da..ca99e5c1fc8 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.h +++ b/drivers/media/dvb/ttpci/av7110_hw.h @@ -305,7 +305,6 @@ enum av7110_command_type { #define IRQ_STATE (DPRAM_BASE + 0x0F4) #define IRQ_STATE_EXT (DPRAM_BASE + 0x0F6) #define MSGSTATE (DPRAM_BASE + 0x0F8) -#define FILT_STATE (DPRAM_BASE + 0x0FA) #define COMMAND (DPRAM_BASE + 0x0FC) #define COM_BUFF (DPRAM_BASE + 0x100) #define COM_BUFF_SIZE 0x20 @@ -332,8 +331,6 @@ enum av7110_command_type { /* firmware status area */ #define STATUS_BASE (DPRAM_BASE + 0x1FC0) -#define STATUS_SCR (STATUS_BASE + 0x00) -#define STATUS_MODES (STATUS_BASE + 0x04) #define STATUS_LOOPS (STATUS_BASE + 0x08) #define STATUS_MPEG_WIDTH (STATUS_BASE + 0x0C) diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index b30a5288e48..b7d1f2f18d3 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -667,6 +667,11 @@ static struct tda1002x_config philips_cu1216_config_altaddress = { .invert = 0, }; +static struct tda10023_config philips_cu1216_tda10023_config = { + .demod_address = 0x0c, + .invert = 1, +}; + static int philips_tu1216_tuner_init(struct dvb_frontend *fe) { struct budget *budget = (struct budget *) fe->dvb->priv; @@ -1019,9 +1024,10 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBC_KNC1_PLUS_MK3: budget_av->reinitialise_demod = 1; budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; - fe = dvb_attach(tda10023_attach, &philips_cu1216_config, - &budget_av->budget.i2c_adap, - read_pwm(budget_av)); + fe = dvb_attach(tda10023_attach, + &philips_cu1216_tda10023_config, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); if (fe) { fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; } diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 6530323d540..060e7c78532 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -46,6 +46,8 @@ #include "lnbp21.h" #include "bsbe1.h" #include "bsru6.h" +#include "tda1002x.h" +#include "tda827x.h" /* * Regarding DEBIADDR_IR: @@ -225,6 +227,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) break; case 0x1010: case 0x1017: + case 0x101a: /* for the Technotrend 1500 bundled remote */ ir_input_init(input_dev, &budget_ci->ir.state, IR_TYPE_RC5, ir_codes_tt_1500); @@ -1056,6 +1059,15 @@ static struct stv0297_config dvbc_philips_tdm1316l_config = { .stop_during_read = 1, }; +static struct tda10023_config tda10023_config = { + .demod_address = 0xc, + .invert = 0, + .xtal = 16000000, + .pll_m = 11, + .pll_p = 3, + .pll_n = 1, + .deltaf = 0xa511, +}; @@ -1126,7 +1138,17 @@ static void frontend_init(struct budget_ci *budget_ci) budget_ci->budget.dvb_frontend = NULL; } } + break; + case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */ + budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48); + if (budget_ci->budget.dvb_frontend) { + if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, NULL) == NULL) { + printk(KERN_ERR "%s: No tda827x found!\n", __func__); + dvb_frontend_detach(budget_ci->budget.dvb_frontend); + budget_ci->budget.dvb_frontend = NULL; + } + } break; } @@ -1216,6 +1238,7 @@ MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC); MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT); MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT); +MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT); static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c), @@ -1224,6 +1247,7 @@ static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011), MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012), MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017), + MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a), { .vendor = 0, } diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c index 18cac4b12ab..6f4ddb643fe 100644 --- a/drivers/media/dvb/ttpci/budget-core.c +++ b/drivers/media/dvb/ttpci/budget-core.c @@ -497,11 +497,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, if (bi->type != BUDGET_FS_ACTIVY) saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */ -#ifdef I2C_ADAP_CLASS_TV_DIGITAL - budget->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL; -#else budget->i2c_adap.class = I2C_CLASS_TV_DIGITAL; -#endif strlcpy(budget->i2c_adap.name, budget->card->name, sizeof(budget->i2c_adap.name)); diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/dvb/ttpci/budget-patch.c index 9a155396d6a..39bd0a20f53 100644 --- a/drivers/media/dvb/ttpci/budget-patch.c +++ b/drivers/media/dvb/ttpci/budget-patch.c @@ -431,22 +431,22 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte // in budget patch GPIO3 is connected to VSYNC_B count = 0; #if 0 - WRITE_RPS1(cpu_to_le32(CMD_UPLOAD | - MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 )); + WRITE_RPS1(CMD_UPLOAD | + MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 ); #endif - WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_VBI_B)); - WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2))); - WRITE_RPS1(cpu_to_le32(GPIO3_MSK)); - WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24)); + WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); #if RPS_IRQ // issue RPS1 interrupt to increment counter - WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT)); + WRITE_RPS1(CMD_INTERRUPT); // at least a NOP is neede between two interrupts - WRITE_RPS1(cpu_to_le32(CMD_NOP)); + WRITE_RPS1(CMD_NOP); // interrupt again - WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT)); + WRITE_RPS1(CMD_INTERRUPT); #endif - WRITE_RPS1(cpu_to_le32(CMD_STOP)); + WRITE_RPS1(CMD_STOP); #if RPS_IRQ // set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) @@ -558,28 +558,28 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte // Wait Source Line Counter Threshold (p36) - WRITE_RPS1(cpu_to_le32(CMD_PAUSE | EVT_HS)); + WRITE_RPS1(CMD_PAUSE | EVT_HS); // Set GPIO3=1 (p42) - WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2))); - WRITE_RPS1(cpu_to_le32(GPIO3_MSK)); - WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTHI<<24)); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); #if RPS_IRQ // issue RPS1 interrupt - WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT)); + WRITE_RPS1(CMD_INTERRUPT); #endif // Wait reset Source Line Counter Threshold (p36) - WRITE_RPS1(cpu_to_le32(CMD_PAUSE | RPS_INV | EVT_HS)); + WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); // Set GPIO3=0 (p42) - WRITE_RPS1(cpu_to_le32(CMD_WR_REG_MASK | (GPIO_CTRL>>2))); - WRITE_RPS1(cpu_to_le32(GPIO3_MSK)); - WRITE_RPS1(cpu_to_le32(SAA7146_GPIO_OUTLO<<24)); + WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); + WRITE_RPS1(GPIO3_MSK); + WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); #if RPS_IRQ // issue RPS1 interrupt - WRITE_RPS1(cpu_to_le32(CMD_INTERRUPT)); + WRITE_RPS1(CMD_INTERRUPT); #endif // Jump to begin of RPS program (p37) - WRITE_RPS1(cpu_to_le32(CMD_JUMP)); - WRITE_RPS1(cpu_to_le32(dev->d_rps1.dma_handle)); + WRITE_RPS1(CMD_JUMP); + WRITE_RPS1(dev->d_rps1.dma_handle); // Fix VSYNC level saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c index bc2043e44eb..e6c9cd2e3b9 100644 --- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/wait.h> +#include <linux/fs.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/delay.h> @@ -990,22 +991,9 @@ static int stc_open(struct inode *inode, struct file *file) } static ssize_t stc_read(struct file *file, char *buf, size_t count, - loff_t * offset) + loff_t *offset) { - int tc = count; - - if ((tc + *offset) > 8192) - tc = 8192 - *offset; - - if (tc < 0) - return 0; - - if (copy_to_user(buf, stc_firmware + *offset, tc)) - return -EFAULT; - - *offset += tc; - - return tc; + return simple_read_from_buffer(buf, count, offset, stc_firmware, 8192); } static int stc_release(struct inode *inode, struct file *file) @@ -1693,11 +1681,7 @@ static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *i i2c_set_adapdata(&ttusb->i2c_adap, ttusb); -#ifdef I2C_ADAP_CLASS_TV_DIGITAL - ttusb->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL; -#else ttusb->i2c_adap.class = I2C_CLASS_TV_DIGITAL; -#endif ttusb->i2c_adap.algo = &ttusb_dec_algo; ttusb->i2c_adap.algo_data = NULL; ttusb->i2c_adap.dev.parent = &udev->dev; diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 77354ca6e8e..dc93a882b38 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c @@ -24,6 +24,19 @@ /* + * User Notes: + * - USB Audio is provided by the alsa snd_usb_audio module. + * For listing you have to redirect the sound, for example using: + * arecord -D hw:1,0 -r96000 -c2 -f S16_LE | artsdsp aplay -B - + * - regarding module parameters in /sys/module/radio_si470x/parameters: + * the contents of read-only files (0444) are not updated, even if + * space, band and de are changed using private video controls + * - increase tune_timeout, if you often get -EIO errors + * - hw_freq_seek returns -EAGAIN, when timed out or band limit is reached + */ + + +/* * History: * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net> * Version 1.0.0 @@ -85,10 +98,14 @@ * Oliver Neukum <oliver@neukum.org> * Version 1.0.7 * - usb autosuspend support - * - unplugging fixed + * - unplugging fixed + * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net> + * Version 1.0.8 + * - hardware frequency seek support + * - afc indication + * - more safety checks, let si470x_get_freq return errno * * ToDo: - * - add seeking support * - add firmware download/update support * - RDS support: interrupt mode, instead of polling * - add LED status output (check if that's not already done in firmware) @@ -98,10 +115,10 @@ /* driver definitions */ #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" #define DRIVER_NAME "radio-si470x" -#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 7) +#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 8) #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" -#define DRIVER_VERSION "1.0.7" +#define DRIVER_VERSION "1.0.8" /* kernel includes */ @@ -175,6 +192,11 @@ static unsigned int tune_timeout = 3000; module_param(tune_timeout, uint, 0); MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*"); +/* Seek timeout */ +static unsigned int seek_timeout = 5000; +module_param(seek_timeout, uint, 0); +MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*"); + /* RDS buffer blocks */ static unsigned int rds_buf = 100; module_param(rds_buf, uint, 0); @@ -425,7 +447,8 @@ struct si470x_device { /* driver management */ unsigned int users; - unsigned char disconnected; + unsigned char disconnected; + struct mutex disconnect_lock; /* Silabs internal registers (0..15) */ unsigned short registers[RADIO_REGISTER_NUM]; @@ -442,12 +465,6 @@ struct si470x_device { /* - * Lock to prevent kfree of data before all users have releases the device. - */ -static DEFINE_MUTEX(open_close_lock); - - -/* * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, * 62.5 kHz otherwise. * The tuner is able to have a channel spacing of 50, 100 or 200 kHz. @@ -476,11 +493,11 @@ static int si470x_get_report(struct si470x_device *radio, void *buf, int size) USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, report[0], 2, buf, size, usb_timeout); + if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": si470x_get_report: usb_control_msg returned %d\n", retval); - return retval; } @@ -499,11 +516,11 @@ static int si470x_set_report(struct si470x_device *radio, void *buf, int size) USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, report[0], 2, buf, size, usb_timeout); + if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": si470x_set_report: usb_control_msg returned %d\n", retval); - return retval; } @@ -521,8 +538,7 @@ static int si470x_get_register(struct si470x_device *radio, int regnr) retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); if (retval >= 0) - radio->registers[regnr] = be16_to_cpu(get_unaligned( - (unsigned short *) &buf[1])); + radio->registers[regnr] = get_unaligned_be16(&buf[1]); return (retval < 0) ? -EINVAL : 0; } @@ -537,8 +553,7 @@ static int si470x_set_register(struct si470x_device *radio, int regnr) int retval; buf[0] = REGISTER_REPORT(regnr); - put_unaligned(cpu_to_be16(radio->registers[regnr]), - (unsigned short *) &buf[1]); + put_unaligned_be16(radio->registers[regnr], &buf[1]); retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); @@ -561,9 +576,8 @@ static int si470x_get_all_registers(struct si470x_device *radio) if (retval >= 0) for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) - radio->registers[regnr] = be16_to_cpu(get_unaligned( - (unsigned short *) - &buf[regnr * RADIO_REGISTER_SIZE + 1])); + radio->registers[regnr] = get_unaligned_be16( + &buf[regnr * RADIO_REGISTER_SIZE + 1]); return (retval < 0) ? -EINVAL : 0; } @@ -585,7 +599,7 @@ static int si470x_get_rds_registers(struct si470x_device *radio) usb_rcvintpipe(radio->usbdev, 1), (void *) &buf, sizeof(buf), &size, usb_timeout); if (size != sizeof(buf)) - printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " + printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " "return size differs: %d != %zu\n", size, sizeof(buf)); if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " @@ -594,8 +608,8 @@ static int si470x_get_rds_registers(struct si470x_device *radio) if (retval >= 0) for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) radio->registers[STATUSRSSI + regnr] = - be16_to_cpu(get_unaligned((unsigned short *) - &buf[regnr * RADIO_REGISTER_SIZE + 1])); + get_unaligned_be16( + &buf[regnr * RADIO_REGISTER_SIZE + 1]); return (retval < 0) ? -EINVAL : 0; } @@ -615,33 +629,39 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; retval = si470x_set_register(radio, CHANNEL); if (retval < 0) - return retval; + goto done; - /* wait till seek operation has completed */ + /* wait till tune operation has completed */ timeout = jiffies + msecs_to_jiffies(tune_timeout); do { retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) - return retval; + goto stop; timed_out = time_after(jiffies, timeout); } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && (!timed_out)); + if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) + printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n"); if (timed_out) printk(KERN_WARNING DRIVER_NAME - ": seek does not finish after %u ms\n", tune_timeout); + ": tune timed out after %u ms\n", tune_timeout); +stop: /* stop tuning */ radio->registers[CHANNEL] &= ~CHANNEL_TUNE; - return si470x_set_register(radio, CHANNEL); + retval = si470x_set_register(radio, CHANNEL); + +done: + return retval; } /* * si470x_get_freq - get the frequency */ -static unsigned int si470x_get_freq(struct si470x_device *radio) +static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) { - unsigned int spacing, band_bottom, freq; + unsigned int spacing, band_bottom; unsigned short chan; int retval; @@ -667,14 +687,12 @@ static unsigned int si470x_get_freq(struct si470x_device *radio) /* read channel */ retval = si470x_get_register(radio, READCHAN); - if (retval < 0) - return retval; chan = radio->registers[READCHAN] & READCHAN_READCHAN; /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ - freq = chan * spacing + band_bottom; + *freq = chan * spacing + band_bottom; - return freq; + return retval; } @@ -714,6 +732,62 @@ static int si470x_set_freq(struct si470x_device *radio, unsigned int freq) /* + * si470x_set_seek - set seek + */ +static int si470x_set_seek(struct si470x_device *radio, + unsigned int wrap_around, unsigned int seek_upward) +{ + int retval = 0; + unsigned long timeout; + bool timed_out = 0; + + /* start seeking */ + radio->registers[POWERCFG] |= POWERCFG_SEEK; + if (wrap_around == 1) + radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; + else + radio->registers[POWERCFG] |= POWERCFG_SKMODE; + if (seek_upward == 1) + radio->registers[POWERCFG] |= POWERCFG_SEEKUP; + else + radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; + retval = si470x_set_register(radio, POWERCFG); + if (retval < 0) + goto done; + + /* wait till seek operation has completed */ + timeout = jiffies + msecs_to_jiffies(seek_timeout); + do { + retval = si470x_get_register(radio, STATUSRSSI); + if (retval < 0) + goto stop; + timed_out = time_after(jiffies, timeout); + } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && + (!timed_out)); + if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) + printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n"); + if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) + printk(KERN_WARNING DRIVER_NAME + ": seek failed / band limit reached\n"); + if (timed_out) + printk(KERN_WARNING DRIVER_NAME + ": seek timed out after %u ms\n", seek_timeout); + +stop: + /* stop seeking */ + radio->registers[POWERCFG] &= ~POWERCFG_SEEK; + retval = si470x_set_register(radio, POWERCFG); + +done: + /* try again, if timed out */ + if ((retval == 0) && timed_out) + retval = -EAGAIN; + + return retval; +} + + +/* * si470x_start - switch on radio */ static int si470x_start(struct si470x_device *radio) @@ -725,27 +799,30 @@ static int si470x_start(struct si470x_device *radio) POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM; retval = si470x_set_register(radio, POWERCFG); if (retval < 0) - return retval; + goto done; /* sysconfig 1 */ radio->registers[SYSCONFIG1] = SYSCONFIG1_DE; retval = si470x_set_register(radio, SYSCONFIG1); if (retval < 0) - return retval; + goto done; /* sysconfig 2 */ radio->registers[SYSCONFIG2] = - (0x3f << 8) | /* SEEKTH */ - (band << 6) | /* BAND */ - (space << 4) | /* SPACE */ - 15; /* VOLUME (max) */ + (0x3f << 8) | /* SEEKTH */ + ((band << 6) & SYSCONFIG2_BAND) | /* BAND */ + ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */ + 15; /* VOLUME (max) */ retval = si470x_set_register(radio, SYSCONFIG2); if (retval < 0) - return retval; + goto done; /* reset last channel */ - return si470x_set_chan(radio, + retval = si470x_set_chan(radio, radio->registers[CHANNEL] & CHANNEL_CHAN); + +done: + return retval; } @@ -760,13 +837,16 @@ static int si470x_stop(struct si470x_device *radio) radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; retval = si470x_set_register(radio, SYSCONFIG1); if (retval < 0) - return retval; + goto done; /* powercfg */ radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; /* POWERCFG_ENABLE has to automatically go low */ radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE; - return si470x_set_register(radio, POWERCFG); + retval = si470x_set_register(radio, POWERCFG); + +done: + return retval; } @@ -843,7 +923,7 @@ static void si470x_rds(struct si470x_device *radio) }; /* Fill the V4L2 RDS buffer */ - put_unaligned(cpu_to_le16(rds), (unsigned short *) &tmpbuf); + put_unaligned_le16(rds, &tmpbuf); tmpbuf[2] = blocknum; /* offset name */ tmpbuf[2] |= blocknum << 3; /* received offset */ if (bler > max_rds_errors) @@ -883,8 +963,9 @@ static void si470x_work(struct work_struct *work) struct si470x_device *radio = container_of(work, struct si470x_device, work.work); - if (radio->disconnected) - return; + /* safety checks */ + if (radio->disconnected) + return; if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) return; @@ -917,11 +998,15 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, /* block if no new data available */ while (radio->wr_index == radio->rd_index) { - if (file->f_flags & O_NONBLOCK) - return -EWOULDBLOCK; + if (file->f_flags & O_NONBLOCK) { + retval = -EWOULDBLOCK; + goto done; + } if (wait_event_interruptible(radio->read_queue, - radio->wr_index != radio->rd_index) < 0) - return -EINTR; + radio->wr_index != radio->rd_index) < 0) { + retval = -EINTR; + goto done; + } } /* calculate block count from byte count */ @@ -950,6 +1035,7 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, } mutex_unlock(&radio->lock); +done: return retval; } @@ -961,6 +1047,7 @@ static unsigned int si470x_fops_poll(struct file *file, struct poll_table_struct *pts) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; /* switch on rds reception */ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { @@ -972,9 +1059,9 @@ static unsigned int si470x_fops_poll(struct file *file, poll_wait(file, &radio->read_queue, pts); if (radio->rd_index != radio->wr_index) - return POLLIN | POLLRDNORM; + retval = POLLIN | POLLRDNORM; - return 0; + return retval; } @@ -991,17 +1078,18 @@ static int si470x_fops_open(struct inode *inode, struct file *file) retval = usb_autopm_get_interface(radio->intf); if (retval < 0) { radio->users--; - return -EIO; + retval = -EIO; + goto done; } if (radio->users == 1) { retval = si470x_start(radio); if (retval < 0) usb_autopm_put_interface(radio->intf); - return retval; } - return 0; +done: + return retval; } @@ -1011,20 +1099,23 @@ static int si470x_fops_open(struct inode *inode, struct file *file) static int si470x_fops_release(struct inode *inode, struct file *file) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - int retval = 0; + int retval = 0; - if (!radio) - return -ENODEV; + /* safety check */ + if (!radio) { + retval = -ENODEV; + goto done; + } - mutex_lock(&open_close_lock); + mutex_lock(&radio->disconnect_lock); radio->users--; if (radio->users == 0) { - if (radio->disconnected) { - video_unregister_device(radio->videodev); - kfree(radio->buffer); - kfree(radio); - goto done; - } + if (radio->disconnected) { + video_unregister_device(radio->videodev); + kfree(radio->buffer); + kfree(radio); + goto unlock; + } /* stop rds reception */ cancel_delayed_work_sync(&radio->work); @@ -1036,9 +1127,11 @@ static int si470x_fops_release(struct inode *inode, struct file *file) usb_autopm_put_interface(radio->intf); } +unlock: + mutex_unlock(&radio->disconnect_lock); + done: - mutex_unlock(&open_close_lock); - return retval; + return retval; } @@ -1116,7 +1209,8 @@ static int si470x_vidioc_querycap(struct file *file, void *priv, strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); sprintf(capability->bus_info, "USB"); capability->version = DRIVER_KERNEL_VERSION; - capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | + V4L2_CAP_TUNER | V4L2_CAP_RADIO; return 0; } @@ -1125,7 +1219,7 @@ static int si470x_vidioc_querycap(struct file *file, void *priv, /* * si470x_vidioc_g_input - get input */ -static int si470x_vidioc_g_input(struct file *filp, void *priv, +static int si470x_vidioc_g_input(struct file *file, void *priv, unsigned int *i) { *i = 0; @@ -1137,12 +1231,18 @@ static int si470x_vidioc_g_input(struct file *filp, void *priv, /* * si470x_vidioc_s_input - set input */ -static int si470x_vidioc_s_input(struct file *filp, void *priv, unsigned int i) +static int si470x_vidioc_s_input(struct file *file, void *priv, unsigned int i) { + int retval = 0; + + /* safety checks */ if (i != 0) - return -EINVAL; + retval = -EINVAL; - return 0; + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": set input failed with %d\n", retval); + return retval; } @@ -1155,17 +1255,22 @@ static int si470x_vidioc_queryctrl(struct file *file, void *priv, unsigned char i; int retval = -EINVAL; + /* safety checks */ + if (!qc->id) + goto done; + for (i = 0; i < ARRAY_SIZE(si470x_v4l2_queryctrl); i++) { - if (qc->id && qc->id == si470x_v4l2_queryctrl[i].id) { + if (qc->id == si470x_v4l2_queryctrl[i].id) { memcpy(qc, &(si470x_v4l2_queryctrl[i]), sizeof(*qc)); retval = 0; break; } } + +done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME - ": query control failed with %d\n", retval); - + ": query controls failed with %d\n", retval); return retval; } @@ -1177,9 +1282,13 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; - if (radio->disconnected) - return -EIO; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -1190,9 +1299,15 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv, ctrl->value = ((radio->registers[POWERCFG] & POWERCFG_DMUTE) == 0) ? 1 : 0; break; + default: + retval = -EINVAL; } - return 0; +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": get control failed with %d\n", retval); + return retval; } @@ -1203,10 +1318,13 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - int retval; + int retval = 0; - if (radio->disconnected) - return -EIO; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -1224,10 +1342,11 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, default: retval = -EINVAL; } + +done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": set control failed with %d\n", retval); - return retval; } @@ -1238,13 +1357,22 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, static int si470x_vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *audio) { - if (audio->index > 1) - return -EINVAL; + int retval = 0; + + /* safety checks */ + if (audio->index != 0) { + retval = -EINVAL; + goto done; + } strcpy(audio->name, "Radio"); audio->capability = V4L2_AUDCAP_STEREO; - return 0; +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": get audio failed with %d\n", retval); + return retval; } @@ -1254,10 +1382,19 @@ static int si470x_vidioc_g_audio(struct file *file, void *priv, static int si470x_vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *audio) { - if (audio->index != 0) - return -EINVAL; + int retval = 0; - return 0; + /* safety checks */ + if (audio->index != 0) { + retval = -EINVAL; + goto done; + } + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": set audio failed with %d\n", retval); + return retval; } @@ -1268,20 +1405,23 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - int retval; + int retval = 0; - if (radio->disconnected) - return -EIO; - if (tuner->index > 0) - return -EINVAL; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) { + retval = -EINVAL; + goto done; + } - /* read status rssi */ retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) - return retval; + goto done; strcpy(tuner->name, "FM"); - tuner->type = V4L2_TUNER_RADIO; switch (band) { /* 0: 87.5 - 108 MHz (USA, Europe, default) */ default: @@ -1313,9 +1453,14 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, * 0x0101; /* automatic frequency control: -1: freq to low, 1 freq to high */ - tuner->afc = 0; + /* AFCRL does only indicate that freq. differs, not if too low/high */ + tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; - return 0; +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": get tuner failed with %d\n", retval); + return retval; } @@ -1326,12 +1471,17 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - int retval; + int retval = 0; - if (radio->disconnected) - return -EIO; - if (tuner->index > 0) - return -EINVAL; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) { + retval = -EINVAL; + goto done; + } if (tuner->audmode == V4L2_TUNER_MODE_MONO) radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ @@ -1339,10 +1489,11 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ retval = si470x_set_register(radio, POWERCFG); + +done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": set tuner failed with %d\n", retval); - return retval; } @@ -1354,14 +1505,25 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; - if (radio->disconnected) - return -EIO; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) { + retval = -EINVAL; + goto done; + } - freq->type = V4L2_TUNER_RADIO; - freq->frequency = si470x_get_freq(radio); + retval = si470x_get_freq(radio, &freq->frequency); - return 0; +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": get frequency failed with %d\n", retval); + return retval; } @@ -1372,19 +1534,55 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { struct si470x_device *radio = video_get_drvdata(video_devdata(file)); - int retval; + int retval = 0; - if (radio->disconnected) - return -EIO; - if (freq->type != V4L2_TUNER_RADIO) - return -EINVAL; + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) { + retval = -EINVAL; + goto done; + } retval = si470x_set_freq(radio, freq->frequency); + +done: if (retval < 0) printk(KERN_WARNING DRIVER_NAME ": set frequency failed with %d\n", retval); + return retval; +} - return 0; + +/* + * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek + */ +static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, + struct v4l2_hw_freq_seek *seek) +{ + struct si470x_device *radio = video_get_drvdata(video_devdata(file)); + int retval = 0; + + /* safety checks */ + if (radio->disconnected) { + retval = -EIO; + goto done; + } + if ((seek->tuner != 0) && (seek->type != V4L2_TUNER_RADIO)) { + retval = -EINVAL; + goto done; + } + + retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); + +done: + if (retval < 0) + printk(KERN_WARNING DRIVER_NAME + ": set hardware frequency seek failed with %d\n", + retval); + return retval; } @@ -1408,6 +1606,7 @@ static struct video_device si470x_viddev_template = { .vidioc_s_tuner = si470x_vidioc_s_tuner, .vidioc_g_frequency = si470x_vidioc_g_frequency, .vidioc_s_frequency = si470x_vidioc_s_frequency, + .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, .owner = THIS_MODULE, }; @@ -1424,31 +1623,36 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct si470x_device *radio; - int retval = -ENOMEM; + int retval = 0; - /* private data allocation */ + /* private data allocation and initialization */ radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); - if (!radio) + if (!radio) { + retval = -ENOMEM; goto err_initial; + } + radio->users = 0; + radio->disconnected = 0; + radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; + mutex_init(&radio->disconnect_lock); + mutex_init(&radio->lock); - /* video device allocation */ + /* video device allocation and initialization */ radio->videodev = video_device_alloc(); - if (!radio->videodev) + if (!radio->videodev) { + retval = -ENOMEM; goto err_radio; - - /* initial configuration */ + } memcpy(radio->videodev, &si470x_viddev_template, sizeof(si470x_viddev_template)); - radio->users = 0; - radio->usbdev = interface_to_usbdev(intf); - radio->intf = intf; - mutex_init(&radio->lock); video_set_drvdata(radio->videodev, radio); /* show some infos about the specific device */ - retval = -EIO; - if (si470x_get_all_registers(radio) < 0) + if (si470x_get_all_registers(radio) < 0) { + retval = -EIO; goto err_all; + } printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); @@ -1474,8 +1678,10 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, /* rds buffer allocation */ radio->buf_size = rds_buf * 3; radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); - if (!radio->buffer) + if (!radio->buffer) { + retval = -EIO; goto err_all; + } /* rds buffer configuration */ radio->wr_index = 0; @@ -1487,6 +1693,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, /* register video device */ if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) { + retval = -EIO; printk(KERN_WARNING DRIVER_NAME ": Could not register video device\n"); goto err_all; @@ -1546,16 +1753,16 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) { struct si470x_device *radio = usb_get_intfdata(intf); - mutex_lock(&open_close_lock); - radio->disconnected = 1; + mutex_lock(&radio->disconnect_lock); + radio->disconnected = 1; cancel_delayed_work_sync(&radio->work); usb_set_intfdata(intf, NULL); - if (radio->users == 0) { - video_unregister_device(radio->videodev); - kfree(radio->buffer); - kfree(radio); - } - mutex_unlock(&open_close_lock); + if (radio->users == 0) { + video_unregister_device(radio->videodev); + kfree(radio->buffer); + kfree(radio); + } + mutex_unlock(&radio->disconnect_lock); } diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 5ccb0aeca8c..f606d2951fd 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -24,21 +24,21 @@ config VIDEOBUF_VMALLOC select VIDEOBUF_GEN tristate +config VIDEOBUF_DMA_CONTIG + depends on HAS_DMA + select VIDEOBUF_GEN + tristate + config VIDEOBUF_DVB tristate select VIDEOBUF_GEN - select VIDEOBUF_DMA_SG config VIDEO_BTCX tristate -config VIDEO_IR_I2C - tristate - config VIDEO_IR tristate depends on INPUT - select VIDEO_IR_I2C if I2C config VIDEO_TVEEPROM tristate @@ -84,6 +84,19 @@ config VIDEO_HELPER_CHIPS_AUTO In doubt, say Y. +config VIDEO_IR_I2C + tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO + depends on I2C && VIDEO_IR + default y + ---help--- + Most boards have an IR chip directly connected via GPIO. However, + some video boards have the IR connected via I2C bus. + + If your board doesn't have an I2C IR chip, you may disable this + option. + + In doubt, say Y. + # # Encoder / Decoder module configuration # @@ -600,9 +613,6 @@ config VIDEO_STRADIS driver for PCI. There is a product page at <http://www.stradis.com/>. -config VIDEO_ZORAN_ZR36060 - tristate - config VIDEO_ZORAN tristate "Zoran ZR36057/36067 Video For Linux" depends on PCI && I2C_ALGOBIT && VIDEO_V4L1 && VIRT_TO_BUS @@ -616,61 +626,64 @@ config VIDEO_ZORAN To compile this driver as a module, choose M here: the module will be called zr36067. +config VIDEO_ZORAN_DC30 + tristate "Pinnacle/Miro DC30(+) support" + depends on VIDEO_ZORAN + select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO + help + Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback + card. This also supports really old DC10 cards based on the + zr36050 MJPEG codec and zr36016 VFE. + +config VIDEO_ZORAN_ZR36060 + tristate "Zoran ZR36060" + depends on VIDEO_ZORAN + help + Say Y to support Zoran boards based on 36060 chips. + This includes Iomega Bus, Pinnacle DC10, Linux media Labs 33 + and 33 R10 and AverMedia 6 boards. + config VIDEO_ZORAN_BUZ tristate "Iomega Buz support" - depends on VIDEO_ZORAN + depends on VIDEO_ZORAN_ZR36060 select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_ZORAN_ZR36060 help Support for the Iomega Buz MJPEG capture/playback card. config VIDEO_ZORAN_DC10 tristate "Pinnacle/Miro DC10(+) support" - depends on VIDEO_ZORAN - select VIDEO_SAA7110 + depends on VIDEO_ZORAN_ZR36060 + select VIDEO_SAA7110 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_ZORAN_ZR36060 help Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback card. -config VIDEO_ZORAN_DC30 - tristate "Pinnacle/Miro DC30(+) support" - depends on VIDEO_ZORAN - select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO - help - Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback - card. This also supports really old DC10 cards based on the - zr36050 MJPEG codec and zr36016 VFE. - config VIDEO_ZORAN_LML33 tristate "Linux Media Labs LML33 support" - depends on VIDEO_ZORAN + depends on VIDEO_ZORAN_ZR36060 select VIDEO_BT819 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_ZORAN_ZR36060 help Support for the Linux Media Labs LML33 MJPEG capture/playback card. config VIDEO_ZORAN_LML33R10 tristate "Linux Media Labs LML33R10 support" - depends on VIDEO_ZORAN + depends on VIDEO_ZORAN_ZR36060 select VIDEO_SAA7114 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_ZORAN_ZR36060 help support for the Linux Media Labs LML33R10 MJPEG capture/playback card. config VIDEO_ZORAN_AVS6EYES tristate "AverMedia 6 Eyes support (EXPERIMENTAL)" - depends on VIDEO_ZORAN && EXPERIMENTAL && VIDEO_V4L1 + depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL && VIDEO_V4L1 select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_ZORAN_ZR36060 help Support for the AverMedia 6 Eyes video surveillance card. @@ -801,6 +814,8 @@ config USB_VIDEO_CLASS For more information see: <http://linux-uvc.berlios.de/> +source "drivers/media/video/gspca/Kconfig" + source "drivers/media/video/pvrusb2/Kconfig" source "drivers/media/video/em28xx/Kconfig" @@ -905,12 +920,21 @@ config USB_STKWEBCAM To compile this driver as a module, choose M here: the module will be called stkwebcam. +config USB_S2255 + tristate "USB Sensoray 2255 video capture device" + depends on VIDEO_V4L2 + select VIDEOBUF_VMALLOC + default n + help + Say Y here if you want support for the Sensoray 2255 USB device. + This driver can be compiled as a module, called s2255drv. + endif # V4L_USB_DRIVERS config SOC_CAMERA tristate "SoC camera support" depends on VIDEO_V4L2 && HAS_DMA - select VIDEOBUF_DMA_SG + select VIDEOBUF_GEN help SoC Camera is a common API to several cameras, not connecting over a bus like PCI or USB. For example some i2c camera connected @@ -945,11 +969,26 @@ config MT9V022_PCA9536_SWITCH Select this if your MT9V022 camera uses a PCA9536 I2C GPIO extender to switch between 8 and 10 bit datawidth modes +config SOC_CAMERA_PLATFORM + tristate "platform camera support" + depends on SOC_CAMERA + help + This is a generic SoC camera platform driver, useful for testing + config VIDEO_PXA27x tristate "PXA27x Quick Capture Interface driver" depends on VIDEO_DEV && PXA27x select SOC_CAMERA + select VIDEOBUF_DMA_SG ---help--- This is a v4l2 driver for the PXA27x Quick Capture Interface +config VIDEO_SH_MOBILE_CEU + tristate "SuperH Mobile CEU Interface driver" + depends on VIDEO_DEV + select SOC_CAMERA + select VIDEOBUF_DMA_CONTIG + ---help--- + This is a v4l2 driver for the SuperH Mobile CEU Interface + endif # VIDEO_CAPTURE_DRIVERS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index ecbbfaab24d..45d5db5abb1 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o +obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o @@ -117,11 +118,13 @@ obj-$(CONFIG_USB_SN9C102) += sn9c102/ obj-$(CONFIG_USB_ET61X251) += et61x251/ obj-$(CONFIG_USB_PWC) += pwc/ obj-$(CONFIG_USB_ZC0301) += zc0301/ +obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_USB_IBMCAM) += usbvideo/ obj-$(CONFIG_USB_KONICAWC) += usbvideo/ obj-$(CONFIG_USB_VICAM) += usbvideo/ obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += usbvideo/ +obj-$(CONFIG_USB_S2255) += s2255drv.o obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_CX18) += cx18/ @@ -130,9 +133,11 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o +obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o +obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o obj-$(CONFIG_VIDEO_AU0828) += au0828/ diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index 8bfd5c75cb3..ddd2a7964de 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -516,7 +516,7 @@ bt819_detect_client (struct i2c_adapter *adapter, dprintk(1, KERN_INFO - "saa7111.c: detecting bt819 client on address 0x%x\n", + "bt819: detecting bt819 client on address 0x%x\n", address << 1); /* Check if the adapter supports the needed features */ diff --git a/drivers/media/video/bt8xx/bt832.c b/drivers/media/video/bt8xx/bt832.c index f92f06dec0d..216fc9680e8 100644 --- a/drivers/media/video/bt8xx/bt832.c +++ b/drivers/media/video/bt8xx/bt832.c @@ -179,7 +179,6 @@ static int bt832_attach(struct i2c_adapter *adap, int addr, int kind) v4l_info(&t->client,"chip found @ 0x%x\n", addr<<1); - if(! bt832_init(&t->client)) { bt832_detach(&t->client); return -1; diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 0165aac533b..0ea559a7fe5 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -2448,7 +2448,7 @@ pix_format_set_size (struct v4l2_pix_format * f, } } -static int bttv_g_fmt_cap(struct file *file, void *priv, +static int bttv_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct bttv_fh *fh = priv; @@ -2461,7 +2461,7 @@ static int bttv_g_fmt_cap(struct file *file, void *priv, return 0; } -static int bttv_g_fmt_overlay(struct file *file, void *priv, +static int bttv_g_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f) { struct bttv_fh *fh = priv; @@ -2472,7 +2472,7 @@ static int bttv_g_fmt_overlay(struct file *file, void *priv, return 0; } -static int bttv_try_fmt_cap(struct file *file, void *priv, +static int bttv_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { const struct bttv_format *fmt; @@ -2532,7 +2532,7 @@ static int bttv_try_fmt_cap(struct file *file, void *priv, return 0; } -static int bttv_try_fmt_overlay(struct file *file, void *priv, +static int bttv_try_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f) { struct bttv_fh *fh = priv; @@ -2542,7 +2542,7 @@ static int bttv_try_fmt_overlay(struct file *file, void *priv, /* adjust_crop */ 0); } -static int bttv_s_fmt_cap(struct file *file, void *priv, +static int bttv_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { int retval; @@ -2556,7 +2556,7 @@ static int bttv_s_fmt_cap(struct file *file, void *priv, if (0 != retval) return retval; - retval = bttv_try_fmt_cap(file, priv, f); + retval = bttv_try_fmt_vid_cap(file, priv, f); if (0 != retval) return retval; @@ -2591,7 +2591,7 @@ static int bttv_s_fmt_cap(struct file *file, void *priv, return 0; } -static int bttv_s_fmt_overlay(struct file *file, void *priv, +static int bttv_s_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f) { struct bttv_fh *fh = priv; @@ -2661,7 +2661,7 @@ static int bttv_querycap(struct file *file, void *priv, return 0; } -static int bttv_enum_fmt_vbi(struct file *file, void *priv, +static int bttv_enum_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (0 != f->index) @@ -2692,7 +2692,7 @@ static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f) return i; } -static int bttv_enum_fmt_cap(struct file *file, void *priv, +static int bttv_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { int rc = bttv_enum_fmt_cap_ovr(f); @@ -2703,7 +2703,7 @@ static int bttv_enum_fmt_cap(struct file *file, void *priv, return 0; } -static int bttv_enum_fmt_overlay(struct file *file, void *priv, +static int bttv_enum_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_fmtdesc *f) { int rc; @@ -3362,18 +3362,18 @@ static struct video_device bttv_video_template = .fops = &bttv_fops, .minor = -1, .vidioc_querycap = bttv_querycap, - .vidioc_enum_fmt_cap = bttv_enum_fmt_cap, - .vidioc_g_fmt_cap = bttv_g_fmt_cap, - .vidioc_try_fmt_cap = bttv_try_fmt_cap, - .vidioc_s_fmt_cap = bttv_s_fmt_cap, - .vidioc_enum_fmt_overlay = bttv_enum_fmt_overlay, - .vidioc_g_fmt_overlay = bttv_g_fmt_overlay, - .vidioc_try_fmt_overlay = bttv_try_fmt_overlay, - .vidioc_s_fmt_overlay = bttv_s_fmt_overlay, - .vidioc_enum_fmt_vbi = bttv_enum_fmt_vbi, - .vidioc_g_fmt_vbi = bttv_g_fmt_vbi, - .vidioc_try_fmt_vbi = bttv_try_fmt_vbi, - .vidioc_s_fmt_vbi = bttv_s_fmt_vbi, + .vidioc_enum_fmt_vid_cap = bttv_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = bttv_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = bttv_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = bttv_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_overlay = bttv_enum_fmt_vid_overlay, + .vidioc_g_fmt_vid_overlay = bttv_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_overlay = bttv_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = bttv_s_fmt_vid_overlay, + .vidioc_enum_fmt_vbi_cap = bttv_enum_fmt_vbi_cap, + .vidioc_g_fmt_vbi_cap = bttv_g_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = bttv_try_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = bttv_s_fmt_vbi_cap, .vidioc_g_audio = bttv_g_audio, .vidioc_s_audio = bttv_s_audio, .vidioc_cropcap = bttv_cropcap, @@ -3705,7 +3705,7 @@ static void bttv_risc_disasm(struct bttv *btv, for (i = 0; i < (risc->size >> 2); i += n) { printk("%s: 0x%lx: ", btv->c.name, (unsigned long)(risc->dma + (i<<2))); - n = bttv_risc_decode(risc->cpu[i]); + n = bttv_risc_decode(le32_to_cpu(risc->cpu[i])); for (j = 1; j < n; j++) printk("%s: 0x%lx: 0x%08x [ arg #%d ]\n", btv->c.name, (unsigned long)(risc->dma + ((i+j)<<2)), @@ -3774,8 +3774,8 @@ static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc) printk("bttv%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n", btv->c.nr, (unsigned long)btv->main.dma, - (unsigned long)btv->main.cpu[RISC_SLOT_O_VBI+1], - (unsigned long)btv->main.cpu[RISC_SLOT_O_FIELD+1], + (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]), + (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]), (unsigned long)rc); if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) { @@ -4188,6 +4188,7 @@ static struct video_device *vdev_init(struct bttv *btv, vfd->dev = &btv->c.pci->dev; vfd->release = video_device_release; vfd->type = type; + vfd->debug = bttv_debug; snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)", btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "", type_name, bttv_tvcards[btv->c.type].name); diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index 4d5b8035e46..bcd2cd240a1 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -36,11 +36,6 @@ #include <linux/jiffies.h> #include <asm/io.h> -static struct i2c_algo_bit_data bttv_i2c_algo_bit_template; -static struct i2c_adapter bttv_i2c_adap_sw_template; -static struct i2c_adapter bttv_i2c_adap_hw_template; -static struct i2c_client bttv_i2c_client_template; - static int attach_inform(struct i2c_client *client); static int i2c_debug; @@ -104,7 +99,7 @@ static int bttv_bit_getsda(void *data) return state; } -static struct i2c_algo_bit_data bttv_i2c_algo_bit_template = { +static struct i2c_algo_bit_data __devinitdata bttv_i2c_algo_bit_template = { .setsda = bttv_bit_setsda, .setscl = bttv_bit_setscl, .getsda = bttv_bit_getsda, @@ -113,14 +108,6 @@ static struct i2c_algo_bit_data bttv_i2c_algo_bit_template = { .timeout = 200, }; -static struct i2c_adapter bttv_i2c_adap_sw_template = { - .owner = THIS_MODULE, - .class = I2C_CLASS_TV_ANALOG, - .name = "bttv", - .id = I2C_HW_B_BT848, - .client_register = attach_inform, -}; - /* ----------------------------------------------------------------------- */ /* I2C functions - hardware i2c */ @@ -270,20 +257,11 @@ static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int return retval; } -static struct i2c_algorithm bttv_algo = { +static const struct i2c_algorithm bttv_algo = { .master_xfer = bttv_i2c_xfer, .functionality = functionality, }; -static struct i2c_adapter bttv_i2c_adap_hw_template = { - .owner = THIS_MODULE, - .class = I2C_CLASS_TV_ANALOG, - .name = "bt878", - .id = I2C_HW_B_BT848 /* FIXME */, - .algo = &bttv_algo, - .client_register = attach_inform, -}; - /* ----------------------------------------------------------------------- */ /* I2C functions - common stuff */ @@ -332,10 +310,6 @@ void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg) i2c_clients_command(&btv->c.i2c_adap, cmd, arg); } -static struct i2c_client bttv_i2c_client_template = { - .name = "bttv internal", -}; - /* read I2C */ int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) @@ -417,29 +391,34 @@ static void do_i2c_scan(char *name, struct i2c_client *c) /* init + register i2c algo-bit adapter */ int __devinit init_bttv_i2c(struct bttv *btv) { - memcpy(&btv->i2c_client, &bttv_i2c_client_template, - sizeof(bttv_i2c_client_template)); + strlcpy(btv->i2c_client.name, "bttv internal", I2C_NAME_SIZE); if (i2c_hw) btv->use_i2c_hw = 1; if (btv->use_i2c_hw) { /* bt878 */ - memcpy(&btv->c.i2c_adap, &bttv_i2c_adap_hw_template, - sizeof(bttv_i2c_adap_hw_template)); + strlcpy(btv->c.i2c_adap.name, "bt878", + sizeof(btv->c.i2c_adap.name)); + btv->c.i2c_adap.id = I2C_HW_B_BT848; /* FIXME */ + btv->c.i2c_adap.algo = &bttv_algo; } else { /* bt848 */ /* Prevents usage of invalid delay values */ if (i2c_udelay<5) i2c_udelay=5; - bttv_i2c_algo_bit_template.udelay=i2c_udelay; - memcpy(&btv->c.i2c_adap, &bttv_i2c_adap_sw_template, - sizeof(bttv_i2c_adap_sw_template)); + strlcpy(btv->c.i2c_adap.name, "bttv", + sizeof(btv->c.i2c_adap.name)); + btv->c.i2c_adap.id = I2C_HW_B_BT848; memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template, sizeof(bttv_i2c_algo_bit_template)); + btv->i2c_algo.udelay = i2c_udelay; btv->i2c_algo.data = btv; btv->c.i2c_adap.algo_data = &btv->i2c_algo; } + btv->c.i2c_adap.owner = THIS_MODULE; + btv->c.i2c_adap.class = I2C_CLASS_TV_ANALOG; + btv->c.i2c_adap.client_register = attach_inform; btv->c.i2c_adap.dev.parent = &btv->c.pci->dev; snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name), diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c index bfdbc469e30..68f28e5fa04 100644 --- a/drivers/media/video/bt8xx/bttv-vbi.c +++ b/drivers/media/video/bt8xx/bttv-vbi.c @@ -303,7 +303,7 @@ static int try_fmt(struct v4l2_vbi_format *f, const struct bttv_tvnorm *tvnorm, return 0; } -int bttv_try_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt) +int bttv_try_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; @@ -321,7 +321,7 @@ int bttv_try_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt) } -int bttv_s_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt) +int bttv_s_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; @@ -369,7 +369,7 @@ int bttv_s_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt) } -int bttv_g_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt) +int bttv_g_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) { struct bttv_fh *fh = f; const struct bttv_tvnorm *tvnorm; diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index f2393202904..6d93d16c96e 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -299,7 +299,6 @@ extern int bttv_write_gpio(unsigned int card, /* ---------------------------------------------------------- */ /* sysfs/driver-moded based gpio access interface */ - struct bttv_sub_device { struct device dev; struct bttv_core *core; diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index 27da7b42327..08ef54a22c9 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -39,7 +39,6 @@ #include <linux/scatterlist.h> #include <asm/io.h> #include <media/v4l2-common.h> - #include <linux/device.h> #include <media/videobuf-dma-sg.h> #include <media/tveeprom.h> @@ -254,21 +253,19 @@ int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov, /* ---------------------------------------------------------- */ /* bttv-vbi.c */ -int bttv_try_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f); -int bttv_g_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f); -int bttv_s_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f); +int bttv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f); +int bttv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f); +int bttv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f); extern struct videobuf_queue_ops bttv_vbi_qops; /* ---------------------------------------------------------- */ /* bttv-gpio.c */ - extern struct bus_type bttv_sub_bus_type; int bttv_sub_add_device(struct bttv_core *core, char *name); int bttv_sub_del_devices(struct bttv_core *core); - /* ---------------------------------------------------------- */ /* bttv-driver.c */ diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 5195b1f3378..d99453faaab 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -1593,7 +1593,7 @@ static struct v4l2_pix_format cafe_def_pix_format = { .sizeimage = VGA_WIDTH*VGA_HEIGHT*2, }; -static int cafe_vidioc_enum_fmt_cap(struct file *filp, +static int cafe_vidioc_enum_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_fmtdesc *fmt) { struct cafe_camera *cam = priv; @@ -1608,7 +1608,7 @@ static int cafe_vidioc_enum_fmt_cap(struct file *filp, } -static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv, +static int cafe_vidioc_try_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_format *fmt) { struct cafe_camera *cam = priv; @@ -1620,7 +1620,7 @@ static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv, return ret; } -static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv, +static int cafe_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_format *fmt) { struct cafe_camera *cam = priv; @@ -1635,7 +1635,7 @@ static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv, /* * See if the formatting works in principle. */ - ret = cafe_vidioc_try_fmt_cap(filp, priv, fmt); + ret = cafe_vidioc_try_fmt_vid_cap(filp, priv, fmt); if (ret) return ret; /* @@ -1670,7 +1670,7 @@ static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv, * The V4l2 spec wants us to be smarter, and actually get this from * the camera (and not mess with it at open time). Someday. */ -static int cafe_vidioc_g_fmt_cap(struct file *filp, void *priv, +static int cafe_vidioc_g_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_format *f) { struct cafe_camera *cam = priv; @@ -1780,10 +1780,10 @@ static struct video_device cafe_v4l_template = { .release = cafe_v4l_dev_release, .vidioc_querycap = cafe_vidioc_querycap, - .vidioc_enum_fmt_cap = cafe_vidioc_enum_fmt_cap, - .vidioc_try_fmt_cap = cafe_vidioc_try_fmt_cap, - .vidioc_s_fmt_cap = cafe_vidioc_s_fmt_cap, - .vidioc_g_fmt_cap = cafe_vidioc_g_fmt_cap, + .vidioc_enum_fmt_vid_cap = cafe_vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = cafe_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = cafe_vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = cafe_vidioc_g_fmt_vid_cap, .vidioc_enum_input = cafe_vidioc_enum_input, .vidioc_g_input = cafe_vidioc_g_input, .vidioc_s_input = cafe_vidioc_s_input, diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/compat_ioctl32.c index cefd1381e8d..54de0cd482e 100644 --- a/drivers/media/video/compat_ioctl32.c +++ b/drivers/media/video/compat_ioctl32.c @@ -884,6 +884,7 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_G_INPUT32: case VIDIOC_S_INPUT32: case VIDIOC_TRY_FMT32: + case VIDIOC_S_HW_FREQ_SEEK: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c index 03411503457..1c3fa3a7470 100644 --- a/drivers/media/video/cs5345.c +++ b/drivers/media/video/cs5345.c @@ -173,4 +173,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .probe = cs5345_probe, .id_table = cs5345_id, }; - diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index d965af860ab..645b339152d 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -43,7 +43,6 @@ MODULE_PARM_DESC(debug, "Debugging messages\n\t\t\t0=Off (default), 1=On"); static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; - I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ @@ -189,4 +188,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .probe = cs53l32a_probe, .id_table = cs53l32a_id, }; - diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c index 1adc404d955..6d5b94fc708 100644 --- a/drivers/media/video/cx18/cx18-audio.c +++ b/drivers/media/video/cx18/cx18-audio.c @@ -26,13 +26,17 @@ #include "cx18-cards.h" #include "cx18-audio.h" +#define CX18_AUDIO_ENABLE 0xc72014 + /* Selects the audio input and output according to the current settings. */ int cx18_audio_set_io(struct cx18 *cx) { struct v4l2_routing route; u32 audio_input; + u32 val; int mux_input; + int err; /* Determine which input to use */ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { @@ -51,8 +55,17 @@ int cx18_audio_set_io(struct cx18 *cx) cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route); route.input = audio_input; - return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, + err = cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_INT_S_AUDIO_ROUTING, &route); + if (err) + return err; + + val = read_reg(CX18_AUDIO_ENABLE) & ~0x30; + val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 : + (audio_input << 4); + write_reg(val | 0xb00, CX18_AUDIO_ENABLE); + cx18_vapi(cx, CX18_APU_RESETAI, 1, 0); + return 0; } void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route) diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c index 2dc3a5dd170..c40a286de1b 100644 --- a/drivers/media/video/cx18/cx18-av-audio.c +++ b/drivers/media/video/cx18/cx18-av-audio.c @@ -34,7 +34,7 @@ static int set_audclk_freq(struct cx18 *cx, u32 freq) /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ cx18_av_write(cx, 0x127, 0x50); - if (state->aud_input != CX18_AV_AUDIO_SERIAL) { + if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { switch (freq) { case 32000: /* VID_PLL and AUX_PLL */ @@ -148,7 +148,7 @@ void cx18_av_audio_set_path(struct cx18 *cx) /* Mute everything to prevent the PFFT! */ cx18_av_write(cx, 0x8d3, 0x1f); - if (state->aud_input == CX18_AV_AUDIO_SERIAL) { + if (state->aud_input <= CX18_AV_AUDIO_SERIAL2) { /* Set Path1 to Serial Audio Input */ cx18_av_write4(cx, 0x8d0, 0x01011012); @@ -165,7 +165,7 @@ void cx18_av_audio_set_path(struct cx18 *cx) /* deassert soft reset */ cx18_av_and_or(cx, 0x810, ~0x1, 0x00); - if (state->aud_input != CX18_AV_AUDIO_SERIAL) { + if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { /* When the microcontroller detects the * audio format, it will unmute the lines */ cx18_av_and_or(cx, 0x803, ~0x10, 0x10); @@ -271,7 +271,7 @@ static void set_mute(struct cx18 *cx, int mute) { struct cx18_av_state *state = &cx->av_state; - if (state->aud_input != CX18_AV_AUDIO_SERIAL) { + if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { /* Must turn off microcontroller in order to mute sound. * Not sure if this is the best method, but it does work. * If the microcontroller is running, then it will undo any @@ -298,14 +298,14 @@ int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg) switch (cmd) { case VIDIOC_INT_AUDIO_CLOCK_FREQ: - if (state->aud_input != CX18_AV_AUDIO_SERIAL) { + if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { cx18_av_and_or(cx, 0x803, ~0x10, 0); cx18_av_write(cx, 0x8d3, 0x1f); } cx18_av_and_or(cx, 0x810, ~0x1, 1); retval = set_audclk_freq(cx, *(u32 *)arg); cx18_av_and_or(cx, 0x810, ~0x1, 0); - if (state->aud_input != CX18_AV_AUDIO_SERIAL) + if (state->aud_input > CX18_AV_AUDIO_SERIAL2) cx18_av_and_or(cx, 0x803, ~0x10, 0x10); return retval; diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index faca43eb940..3b0a2c45060 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -69,58 +69,6 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, or_value); } -int cx18_av_write_no_acfg(struct cx18 *cx, u16 addr, u8 value, int no_acfg_mask) -{ - int retval; - u32 saved_reg[8] = {0}; - - if (no_acfg_mask & CXADEC_NO_ACFG_AFE) { - saved_reg[0] = cx18_av_read4(cx, CXADEC_CHIP_CTRL); - saved_reg[1] = cx18_av_read4(cx, CXADEC_AFE_CTRL); - } - - if (no_acfg_mask & CXADEC_NO_ACFG_PLL) { - saved_reg[2] = cx18_av_read4(cx, CXADEC_PLL_CTRL1); - saved_reg[3] = cx18_av_read4(cx, CXADEC_VID_PLL_FRAC); - } - - if (no_acfg_mask & CXADEC_NO_ACFG_VID) { - saved_reg[4] = cx18_av_read4(cx, CXADEC_HORIZ_TIM_CTRL); - saved_reg[5] = cx18_av_read4(cx, CXADEC_VERT_TIM_CTRL); - saved_reg[6] = cx18_av_read4(cx, CXADEC_SRC_COMB_CFG); - saved_reg[7] = cx18_av_read4(cx, CXADEC_CHROMA_VBIOFF_CFG); - } - - retval = cx18_av_write(cx, addr, value); - - if (no_acfg_mask & CXADEC_NO_ACFG_AFE) { - cx18_av_write4(cx, CXADEC_CHIP_CTRL, saved_reg[0]); - cx18_av_write4(cx, CXADEC_AFE_CTRL, saved_reg[1]); - } - - if (no_acfg_mask & CXADEC_NO_ACFG_PLL) { - cx18_av_write4(cx, CXADEC_PLL_CTRL1, saved_reg[2]); - cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, saved_reg[3]); - } - - if (no_acfg_mask & CXADEC_NO_ACFG_VID) { - cx18_av_write4(cx, CXADEC_HORIZ_TIM_CTRL, saved_reg[4]); - cx18_av_write4(cx, CXADEC_VERT_TIM_CTRL, saved_reg[5]); - cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, saved_reg[6]); - cx18_av_write4(cx, CXADEC_CHROMA_VBIOFF_CFG, saved_reg[7]); - } - - return retval; -} - -int cx18_av_and_or_no_acfg(struct cx18 *cx, u16 addr, unsigned and_mask, - u8 or_value, int no_acfg_mask) -{ - return cx18_av_write_no_acfg(cx, addr, - (cx18_av_read(cx, addr) & and_mask) | - or_value, no_acfg_mask); -} - /* ----------------------------------------------------------------------- */ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, @@ -132,6 +80,7 @@ static void log_video_status(struct cx18 *cx); static void cx18_av_initialize(struct cx18 *cx) { + struct cx18_av_state *state = &cx->av_state; u32 v; cx18_av_loadfw(cx); @@ -211,6 +160,149 @@ static void cx18_av_initialize(struct cx18 *cx) /* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */ /* } */ cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F); + state->default_volume = 228 - cx18_av_read(cx, 0x8d4); + state->default_volume = ((state->default_volume / 2) + 23) << 9; +} + +/* ----------------------------------------------------------------------- */ + +void cx18_av_std_setup(struct cx18 *cx) +{ + struct cx18_av_state *state = &cx->av_state; + v4l2_std_id std = state->std; + int hblank, hactive, burst, vblank, vactive, sc; + int vblank656, src_decimation; + int luma_lpf, uv_lpf, comb; + u32 pll_int, pll_frac, pll_post; + + /* datasheet startup, step 8d */ + if (std & ~V4L2_STD_NTSC) + cx18_av_write(cx, 0x49f, 0x11); + else + cx18_av_write(cx, 0x49f, 0x14); + + if (std & V4L2_STD_625_50) { + hblank = 132; + hactive = 720; + burst = 93; + vblank = 36; + vactive = 580; + vblank656 = 40; + src_decimation = 0x21f; + + luma_lpf = 2; + if (std & V4L2_STD_PAL) { + uv_lpf = 1; + comb = 0x20; + sc = 688739; + } else if (std == V4L2_STD_PAL_Nc) { + uv_lpf = 1; + comb = 0x20; + sc = 556453; + } else { /* SECAM */ + uv_lpf = 0; + comb = 0; + sc = 672351; + } + } else { + hactive = 720; + hblank = 122; + vactive = 487; + luma_lpf = 1; + uv_lpf = 1; + vblank = 26; + vblank656 = 26; + + src_decimation = 0x21f; + if (std == V4L2_STD_PAL_60) { + burst = 0x5b; + luma_lpf = 2; + comb = 0x20; + sc = 688739; + } else if (std == V4L2_STD_PAL_M) { + burst = 0x61; + comb = 0x20; + sc = 555452; + } else { + burst = 0x5b; + comb = 0x66; + sc = 556063; + } + } + + /* DEBUG: Displays configured PLL frequency */ + pll_int = cx18_av_read(cx, 0x108); + pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff; + pll_post = cx18_av_read(cx, 0x109); + CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n", + pll_int, pll_frac, pll_post); + + if (pll_post) { + int fin, fsc; + int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac); + + pll >>= 25; + pll /= pll_post; + CX18_DEBUG_INFO("PLL = %d.%06d MHz\n", + pll / 1000000, pll % 1000000); + CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n", + pll / 8000000, (pll / 8) % 1000000); + + fin = ((u64)src_decimation * pll) >> 12; + CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n", + fin / 1000000, fin % 1000000); + + fsc = (((u64)sc) * pll) >> 24L; + CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n", + fsc / 1000000, fsc % 1000000); + + CX18_DEBUG_INFO("hblank %i, hactive %i, " + "vblank %i , vactive %i, vblank656 %i, src_dec %i," + "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x," + " sc 0x%06x\n", + hblank, hactive, vblank, vactive, vblank656, + src_decimation, burst, luma_lpf, uv_lpf, comb, sc); + } + + /* Sets horizontal blanking delay and active lines */ + cx18_av_write(cx, 0x470, hblank); + cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) | + (hactive << 4))); + cx18_av_write(cx, 0x472, hactive >> 4); + + /* Sets burst gate delay */ + cx18_av_write(cx, 0x473, burst); + + /* Sets vertical blanking delay and active duration */ + cx18_av_write(cx, 0x474, vblank); + cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) | + (vactive << 4))); + cx18_av_write(cx, 0x476, vactive >> 4); + cx18_av_write(cx, 0x477, vblank656); + + /* Sets src decimation rate */ + cx18_av_write(cx, 0x478, 0xff & src_decimation); + cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8)); + + /* Sets Luma and UV Low pass filters */ + cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); + + /* Enables comb filters */ + cx18_av_write(cx, 0x47b, comb); + + /* Sets SC Step*/ + cx18_av_write(cx, 0x47c, sc); + cx18_av_write(cx, 0x47d, 0xff & sc >> 8); + cx18_av_write(cx, 0x47e, 0xff & sc >> 16); + + /* Sets VBI parameters */ + if (std & V4L2_STD_625_50) { + cx18_av_write(cx, 0x47f, 0x01); + state->vbi_line_offset = 5; + } else { + cx18_av_write(cx, 0x47f, 0x00); + state->vbi_line_offset = 8; + } } /* ----------------------------------------------------------------------- */ @@ -221,16 +313,9 @@ static void input_change(struct cx18 *cx) v4l2_std_id std = state->std; /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */ - if (std & V4L2_STD_SECAM) - cx18_av_write_no_acfg(cx, 0x402, 0, CXADEC_NO_ACFG_ALL); - else { - cx18_av_write_no_acfg(cx, 0x402, 0x04, CXADEC_NO_ACFG_ALL); - cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); - } - cx18_av_and_or_no_acfg(cx, 0x401, ~0x60, 0, - CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); - cx18_av_and_or_no_acfg(cx, 0x401, ~0x60, 0x60, - CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); + cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); + cx18_av_and_or(cx, 0x401, ~0x60, 0); + cx18_av_and_or(cx, 0x401, ~0x60, 0x60); if (std & V4L2_STD_525_60) { if (std == V4L2_STD_NTSC_M_JP) { @@ -300,7 +385,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, } switch (aud_input) { - case CX18_AV_AUDIO_SERIAL: + case CX18_AV_AUDIO_SERIAL1: + case CX18_AV_AUDIO_SERIAL2: /* do nothing, use serial audio input */ break; case CX18_AV_AUDIO4: reg &= ~0x30; break; @@ -316,8 +402,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, cx18_av_write(cx, 0x103, reg); /* Set INPUT_MODE to Composite (0) or S-Video (1) */ - cx18_av_and_or_no_acfg(cx, 0x401, ~0x6, is_composite ? 0 : 0x02, - CXADEC_NO_ACFG_PLL | CXADEC_NO_ACFG_VID); + cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02); /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ @@ -373,13 +458,13 @@ static int set_v4lstd(struct cx18 *cx) This happens for example with the Yuan MPC622. */ if (fmt >= 4 && fmt < 8) { /* Set format to NTSC-M */ - cx18_av_and_or_no_acfg(cx, 0x400, ~0xf, 1, CXADEC_NO_ACFG_AFE); + cx18_av_and_or(cx, 0x400, ~0xf, 1); /* Turn off LCOMB */ cx18_av_and_or(cx, 0x47b, ~6, 0); } - cx18_av_and_or_no_acfg(cx, 0x400, ~0xf, fmt, CXADEC_NO_ACFG_AFE); - cx18_av_and_or_no_acfg(cx, 0x403, ~0x3, pal_m, CXADEC_NO_ACFG_ALL); - cx18_av_vbi_setup(cx); + cx18_av_and_or(cx, 0x400, ~0x2f, fmt | 0x20); + cx18_av_and_or(cx, 0x403, ~0x3, pal_m); + cx18_av_std_setup(cx); input_change(cx); return 0; } @@ -618,6 +703,8 @@ int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg) switch (qc->id) { case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, + 65535 / 100, state->default_volume); case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index c172823ce1d..eb61fa1e096 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -62,7 +62,8 @@ enum cx18_av_video_input { enum cx18_av_audio_input { /* Audio inputs: serial or In4-In8 */ - CX18_AV_AUDIO_SERIAL, + CX18_AV_AUDIO_SERIAL1, + CX18_AV_AUDIO_SERIAL2, CX18_AV_AUDIO4 = 4, CX18_AV_AUDIO5, CX18_AV_AUDIO6, @@ -78,6 +79,7 @@ struct cx18_av_state { u32 audclk_freq; int audmode; int vbi_line_offset; + int default_volume; u32 id; u32 rev; int is_initialized; @@ -295,25 +297,16 @@ struct cx18_av_state { #define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */ #define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */ -/* Flags on what to preserve on write to 0x400-0x403 with cx18_av_.*_no_acfg()*/ -#define CXADEC_NO_ACFG_AFE 0x01 /* Preserve 0x100-0x107 */ -#define CXADEC_NO_ACFG_PLL 0x02 /* Preserve 0x108-0x10f */ -#define CXADEC_NO_ACFG_VID 0x04 /* Preserve 0x470-0x47f */ -#define CXADEC_NO_ACFG_ALL 0x07 - /* ----------------------------------------------------------------------- */ /* cx18_av-core.c */ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value); -int cx18_av_write_no_acfg(struct cx18 *cx, u16 addr, u8 value, - int no_acfg_mask); u8 cx18_av_read(struct cx18 *cx, u16 addr); u32 cx18_av_read4(struct cx18 *cx, u16 addr); int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value); int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value); -int cx18_av_and_or_no_acfg(struct cx18 *cx, u16 addr, unsigned mask, u8 value, - int no_acfg_mask); int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg); +void cx18_av_std_setup(struct cx18 *cx); /* ----------------------------------------------------------------------- */ /* cx18_av-firmware.c */ @@ -326,7 +319,6 @@ void cx18_av_audio_set_path(struct cx18 *cx); /* ----------------------------------------------------------------------- */ /* cx18_av-vbi.c */ -void cx18_av_vbi_setup(struct cx18 *cx); int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg); #endif diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c index a1a6af6c1c8..834b9248242 100644 --- a/drivers/media/video/cx18/cx18-av-firmware.c +++ b/drivers/media/video/cx18/cx18-av-firmware.c @@ -22,6 +22,7 @@ #include "cx18-driver.h" #include <linux/firmware.h> +#define CX18_AUDIO_ENABLE 0xc72014 #define FWFILE "v4l-cx23418-dig.fw" int cx18_av_loadfw(struct cx18 *cx) @@ -31,40 +32,58 @@ int cx18_av_loadfw(struct cx18 *cx) u32 v; const u8 *ptr; int i; + int retries = 0; if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) { CX18_ERR("unable to open firmware %s\n", FWFILE); return -EINVAL; } - cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000); - cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); /* Byte 0 */ - - /* Reset the Mako core (Register is undocumented.) */ - cx18_av_write4(cx, 0x8100, 0x00010000); - - /* Put the 8051 in reset and enable firmware upload */ - cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000); - - ptr = fw->data; - size = fw->size; - - for (i = 0; i < size; i++) { - u32 dl_control = 0x0F000000 | ((u32)ptr[i] << 16); - u32 value = 0; - int retries; - - for (retries = 0; retries < 5; retries++) { - cx18_av_write4(cx, CXADEC_DL_CTL, dl_control); - value = cx18_av_read4(cx, CXADEC_DL_CTL); - if ((value & 0x3F00) == (dl_control & 0x3F00)) + /* The firmware load often has byte errors, so allow for several + retries, both at byte level and at the firmware load level. */ + while (retries < 5) { + cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000); + cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); + + /* Reset the Mako core (Register is undocumented.) */ + cx18_av_write4(cx, 0x8100, 0x00010000); + + /* Put the 8051 in reset and enable firmware upload */ + cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000); + + ptr = fw->data; + size = fw->size; + + for (i = 0; i < size; i++) { + u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16); + u32 value = 0; + int retries; + + for (retries = 0; retries < 5; retries++) { + cx18_av_write4(cx, CXADEC_DL_CTL, dl_control); + udelay(10); + value = cx18_av_read4(cx, CXADEC_DL_CTL); + if (value == dl_control) + break; + /* Check if we can correct the byte by changing + the address. We can only write the lower + address byte of the address. */ + if ((value & 0x3F00) != (dl_control & 0x3F00)) { + retries = 5; + break; + } + } + if (retries >= 5) break; } - if (retries >= 5) { - CX18_ERR("unable to load firmware %s\n", FWFILE); - release_firmware(fw); - return -EIO; - } + if (i == size) + break; + retries++; + } + if (retries >= 5) { + CX18_ERR("unable to load firmware %s\n", FWFILE); + release_firmware(fw); + return -EIO; } cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size); @@ -100,7 +119,6 @@ int cx18_av_loadfw(struct cx18 *cx) have a name in the spec. */ cx18_av_write4(cx, 0x09CC, 1); -#define CX18_AUDIO_ENABLE 0xc72014 v = read_reg(CX18_AUDIO_ENABLE); /* If bit 11 is 1 */ if (v & 0x800) diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c index d09f1daf4eb..02fdf57bb67 100644 --- a/drivers/media/video/cx18/cx18-av-vbi.c +++ b/drivers/media/video/cx18/cx18-av-vbi.c @@ -83,150 +83,6 @@ static int decode_vps(u8 *dst, u8 *p) return err & 0xf0; } -void cx18_av_vbi_setup(struct cx18 *cx) -{ - struct cx18_av_state *state = &cx->av_state; - v4l2_std_id std = state->std; - int hblank, hactive, burst, vblank, vactive, sc; - int vblank656, src_decimation; - int luma_lpf, uv_lpf, comb; - u32 pll_int, pll_frac, pll_post; - - /* datasheet startup, step 8d */ - if (std & ~V4L2_STD_NTSC) - cx18_av_write(cx, 0x49f, 0x11); - else - cx18_av_write(cx, 0x49f, 0x14); - - if (std & V4L2_STD_625_50) { - hblank = 0x084; - hactive = 0x2d0; - burst = 0x5d; - vblank = 0x024; - vactive = 0x244; - vblank656 = 0x28; - src_decimation = 0x21f; - - luma_lpf = 2; - if (std & V4L2_STD_SECAM) { - uv_lpf = 0; - comb = 0; - sc = 0x0a425f; - } else if (std == V4L2_STD_PAL_Nc) { - uv_lpf = 1; - comb = 0x20; - sc = 556453; - } else { - uv_lpf = 1; - comb = 0x20; - sc = 0x0a8263; - } - } else { - hactive = 720; - hblank = 122; - vactive = 487; - luma_lpf = 1; - uv_lpf = 1; - - src_decimation = 0x21f; - if (std == V4L2_STD_PAL_60) { - vblank = 26; - vblank656 = 26; - burst = 0x5b; - luma_lpf = 2; - comb = 0x20; - sc = 0x0a8263; - } else if (std == V4L2_STD_PAL_M) { - vblank = 20; - vblank656 = 24; - burst = 0x61; - comb = 0x20; - - sc = 555452; - } else { - vblank = 26; - vblank656 = 26; - burst = 0x5b; - comb = 0x66; - sc = 556063; - } - } - - /* DEBUG: Displays configured PLL frequency */ - pll_int = cx18_av_read(cx, 0x108); - pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff; - pll_post = cx18_av_read(cx, 0x109); - CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n", - pll_int, pll_frac, pll_post); - - if (pll_post) { - int fin, fsc; - int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac); - - pll >>= 25; - pll /= pll_post; - CX18_DEBUG_INFO("PLL = %d.%06d MHz\n", - pll / 1000000, pll % 1000000); - CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n", - pll / 8000000, (pll / 8) % 1000000); - - fin = ((u64)src_decimation * pll) >> 12; - CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n", - fin / 1000000, fin % 1000000); - - fsc = (((u64)sc) * pll) >> 24L; - CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n", - fsc / 1000000, fsc % 1000000); - - CX18_DEBUG_INFO("hblank %i, hactive %i, " - "vblank %i , vactive %i, vblank656 %i, src_dec %i," - "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x," - " sc 0x%06x\n", - hblank, hactive, vblank, vactive, vblank656, - src_decimation, burst, luma_lpf, uv_lpf, comb, sc); - } - - /* Sets horizontal blanking delay and active lines */ - cx18_av_write(cx, 0x470, hblank); - cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) | - (hactive << 4))); - cx18_av_write(cx, 0x472, hactive >> 4); - - /* Sets burst gate delay */ - cx18_av_write(cx, 0x473, burst); - - /* Sets vertical blanking delay and active duration */ - cx18_av_write(cx, 0x474, vblank); - cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) | - (vactive << 4))); - cx18_av_write(cx, 0x476, vactive >> 4); - cx18_av_write(cx, 0x477, vblank656); - - /* Sets src decimation rate */ - cx18_av_write(cx, 0x478, 0xff & src_decimation); - cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8)); - - /* Sets Luma and UV Low pass filters */ - cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); - - /* Enables comb filters */ - cx18_av_write(cx, 0x47b, comb); - - /* Sets SC Step*/ - cx18_av_write(cx, 0x47c, sc); - cx18_av_write(cx, 0x47d, 0xff & sc >> 8); - cx18_av_write(cx, 0x47e, 0xff & sc >> 16); - - /* Sets VBI parameters */ - if (std & V4L2_STD_625_50) { - cx18_av_write(cx, 0x47f, 0x01); - state->vbi_line_offset = 5; - } else { - cx18_av_write(cx, 0x47f, 0x00); - state->vbi_line_offset = 8; - } -} - int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) { struct cx18_av_state *state = &cx->av_state; @@ -292,8 +148,8 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) /* raw VBI */ memset(svbi, 0, sizeof(*svbi)); - /* Setup VBI */ - cx18_av_vbi_setup(cx); + /* Setup standard */ + cx18_av_std_setup(cx); /* VBI Offset */ cx18_av_write(cx, 0x47f, vbi_offset); @@ -304,8 +160,8 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) for (x = 0; x <= 23; x++) lcr[x] = 0x00; - /* Setup VBI */ - cx18_av_vbi_setup(cx); + /* Setup standard */ + cx18_av_std_setup(cx); /* Sliced VBI */ cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */ diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index c26e0ef5b07..8fe5f38c4d7 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -27,6 +27,8 @@ #include "cx18-i2c.h" #include <media/cs5345.h> +#define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM) + /********************** card configuration *******************************/ /* usual i2c tuner addresses to probe */ @@ -65,12 +67,12 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, { CX18_CARD_INPUT_LINE_IN1, - CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 }, { CX18_CARD_INPUT_LINE_IN2, - CX18_AV_AUDIO_SERIAL, CS5345_IN_3 }, + CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX18_AV_AUDIO_SERIAL, CS5345_IN_4 }, + CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 }, .ddr = { /* ESMT M13S128324A-5B memory */ .chip_config = 0x003, @@ -86,6 +88,7 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { .active_lo_mask = 0x3001, .msecs_asserted = 10, .msecs_recovery = 40, + .ir_reset_mask = 0x0001, }, .i2c = &cx18_i2c_std, }; @@ -110,12 +113,12 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, { CX18_CARD_INPUT_LINE_IN1, - CX18_AV_AUDIO_SERIAL, CS5345_IN_2 }, + CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 }, { CX18_CARD_INPUT_LINE_IN2, - CX18_AV_AUDIO_SERIAL, CS5345_IN_3 }, + CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX18_AV_AUDIO_SERIAL, CS5345_IN_4 }, + CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 }, .ddr = { /* Samsung K4D263238G-VC33 memory */ .chip_config = 0x003, @@ -131,6 +134,7 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { .active_lo_mask = 0x3001, .msecs_asserted = 10, .msecs_recovery = 40, + .ir_reset_mask = 0x0001, }, .i2c = &cx18_i2c_std, }; @@ -161,10 +165,10 @@ static const struct cx18_card cx18_card_h900 = { { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO8, 0 }, { CX18_CARD_INPUT_LINE_IN1, - CX18_AV_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL1, 0 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, - CX18_AV_AUDIO_SERIAL, 0 }, + CX18_AV_AUDIO_SERIAL1, 0 }, .tuners = { { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, }, @@ -194,7 +198,7 @@ static const struct cx18_card_pci_info cx18_pci_mpc718[] = { static const struct cx18_card cx18_card_mpc718 = { .type = CX18_CARD_YUAN_MPC718, .name = "Yuan MPC718", - .comment = "Some Composite and S-Video inputs are currently working.\n", + .comment = "Analog video capture works; some audio line in may not.\n", .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_CX23418, .hw_all = CX18_HW_TUNER, @@ -209,11 +213,11 @@ static const struct cx18_card cx18_card_mpc718 = { { CX18_CARD_INPUT_COMPOSITE3, 2, CX18_AV_COMPOSITE3 }, }, .audio_inputs = { - { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, - { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL, 0 }, - { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL, 0 }, + { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, + { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 0 }, + { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL1, 0 }, }, - .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL, 0 }, + .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL1, 0 }, .tuners = { /* XC3028 tuner */ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, @@ -227,16 +231,73 @@ static const struct cx18_card cx18_card_mpc718 = { .tune_lane = 0, .initial_emrs = 2, }, - .xceive_pin = 15, + .xceive_pin = 0, .pci_list = cx18_pci_mpc718, .i2c = &cx18_i2c_std, }; +/* ------------------------------------------------------------------------- */ + +/* Conexant Raptor PAL/SECAM: note that this card is analog only! */ + +static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = { + { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_CONEXANT, 0x0009 }, + { 0, 0, 0 } +}; + +static const struct cx18_card cx18_card_cnxt_raptor_pal = { + .type = CX18_CARD_CNXT_RAPTOR_PAL, + .name = "Conexant Raptor PAL/SECAM", + .comment = "VBI is not yet supported\n", + .v4l2_capabilities = CX18_CAP_ENCODER, + .hw_audio_ctrl = CX18_HW_CX23418, + .hw_muxer = CX18_HW_GPIO, + .hw_all = CX18_HW_TUNER | CX18_HW_GPIO, + .video_inputs = { + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, + { CX18_CARD_INPUT_SVIDEO1, 1, + CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, + { CX18_CARD_INPUT_SVIDEO2, 2, + CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 }, + { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 }, + }, + .audio_inputs = { + { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, + { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, + { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL2, 1 }, + }, + .tuners = { + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + }, + .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL1, 2 }, + .ddr = { + /* MT 46V16M16 memory */ + .chip_config = 0x50306, + .refresh = 0x753, + .timing1 = 0x33220953, + .timing2 = 0x09, + .tune_lane = 0, + .initial_emrs = 0, + }, + .gpio_init.initial_value = 0x1002, + .gpio_init.direction = 0xf002, + .gpio_audio_input = { .mask = 0xf002, + .tuner = 0x1002, /* LED D1 Tuner AF */ + .linein = 0x2000, /* LED D2 Line In 1 */ + .radio = 0x4002 }, /* LED D3 Tuner AF */ + .pci_list = cx18_pci_cnxt_raptor_pal, + .i2c = &cx18_i2c_std, +}; + +/* ------------------------------------------------------------------------- */ + static const struct cx18_card *cx18_card_list[] = { &cx18_card_hvr1600_esmt, &cx18_card_hvr1600_samsung, &cx18_card_h900, &cx18_card_mpc718, + &cx18_card_cnxt_raptor_pal, }; const struct cx18_card *cx18_get_card(u16 index) diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index dc2dd945d4c..32155f6e6fe 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -83,6 +83,14 @@ struct cx18_gpio_i2c_slave_reset { u32 active_hi_mask; /* GPIO outputs that reset i2c chips when high */ int msecs_asserted; /* time period reset must remain asserted */ int msecs_recovery; /* time after deassert for chips to be ready */ + u32 ir_reset_mask; /* GPIO to reset the Zilog Z8F0811 IR contoller */ +}; + +struct cx18_gpio_audio_input { /* select tuner/line in input */ + u32 mask; /* leave to 0 if not supported */ + u32 tuner; + u32 linein; + u32 radio; }; struct cx18_card_tuner { @@ -123,6 +131,7 @@ struct cx18_card { u8 xceive_pin; /* XCeive tuner GPIO reset pin */ struct cx18_gpio_init gpio_init; struct cx18_gpio_i2c_slave_reset gpio_i2c_slave_reset; + struct cx18_gpio_audio_input gpio_audio_input; struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS]; struct cx18_card_tuner_i2c *i2c; diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index 87cf4102166..f46c7e5ed74 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c @@ -51,12 +51,11 @@ static const u32 *ctrl_classes[] = { NULL }; -static int cx18_queryctrl(struct cx18 *cx, struct v4l2_queryctrl *qctrl) +int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) { + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; const char *name; - CX18_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id); - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); if (qctrl->id == 0) return -EINVAL; @@ -91,21 +90,35 @@ static int cx18_queryctrl(struct cx18 *cx, struct v4l2_queryctrl *qctrl) return 0; } -static int cx18_querymenu(struct cx18 *cx, struct v4l2_querymenu *qmenu) +int cx18_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu) { + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; struct v4l2_queryctrl qctrl; qctrl.id = qmenu->id; - cx18_queryctrl(cx, &qctrl); - return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id)); + cx18_queryctrl(file, fh, &qctrl); + return v4l2_ctrl_query_menu(qmenu, &qctrl, + cx2341x_ctrl_get_menu(&cx->params, qmenu->id)); } -static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) +static int cx18_try_ctrl(struct file *file, void *fh, + struct v4l2_ext_control *vctrl) { - s32 v = vctrl->value; - - CX18_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v); + struct v4l2_queryctrl qctrl; + const char **menu_items = NULL; + int err; + + qctrl.id = vctrl->id; + err = cx18_queryctrl(file, fh, &qctrl); + if (err) + return err; + if (qctrl.type == V4L2_CTRL_TYPE_MENU) + menu_items = v4l2_ctrl_get_menu(qctrl.id); + return v4l2_ctrl_check(vctrl, &qctrl, menu_items); +} +static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) +{ switch (vctrl->id) { /* Standard V4L2 controls */ case V4L2_CID_BRIGHTNESS: @@ -123,7 +136,7 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl); default: - CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); return -EINVAL; } return 0; @@ -131,8 +144,6 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) { - CX18_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id); - switch (vctrl->id) { /* Standard V4L2 controls */ case V4L2_CID_BRIGHTNESS: @@ -149,7 +160,7 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) case V4L2_CID_AUDIO_LOUDNESS: return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl); default: - CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); return -EINVAL; } return 0; @@ -194,113 +205,110 @@ static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt return 0; } -int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg) +int cx18_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) { + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; struct v4l2_control ctrl; - switch (cmd) { - case VIDIOC_QUERYMENU: - CX18_DEBUG_IOCTL("VIDIOC_QUERYMENU\n"); - return cx18_querymenu(cx, arg); - - case VIDIOC_QUERYCTRL: - return cx18_queryctrl(cx, arg); - - case VIDIOC_S_CTRL: - return cx18_s_ctrl(cx, arg); - - case VIDIOC_G_CTRL: - return cx18_g_ctrl(cx, arg); - - case VIDIOC_S_EXT_CTRLS: - { - struct v4l2_ext_controls *c = arg; - - if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { - int i; - int err = 0; - - for (i = 0; i < c->count; i++) { - ctrl.id = c->controls[i].id; - ctrl.value = c->controls[i].value; - err = cx18_s_ctrl(cx, &ctrl); - c->controls[i].value = ctrl.value; - if (err) { - c->error_idx = i; - break; - } + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = cx18_g_ctrl(cx, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; } - return err; } - CX18_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n"); - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - struct cx2341x_mpeg_params p = cx->params; - int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing), arg, cmd); + return err; + } + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&cx->params, 0, c, VIDIOC_G_EXT_CTRLS); + return -EINVAL; +} - if (err) - return err; +int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) +{ + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; + int ret; + struct v4l2_control ctrl; - if (p.video_encoding != cx->params.video_encoding) { - int is_mpeg1 = p.video_encoding == - V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - struct v4l2_format fmt; + ret = v4l2_prio_check(&cx->prio, &id->prio); + if (ret) + return ret; - /* fix videodecoder resolution */ - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = cx->params.width / (is_mpeg1 ? 2 : 1); - fmt.fmt.pix.height = cx->params.height; - cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt); + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = cx18_s_ctrl(cx, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; } - err = cx2341x_update(cx, cx18_api_func, &cx->params, &p); - if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt) - err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt); - cx->params = p; - cx->dualwatch_stereo_mode = p.audio_properties & 0x0300; - cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03); - return err; } - return -EINVAL; + return err; } + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + struct cx2341x_mpeg_params p = cx->params; + int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing), + c, VIDIOC_S_EXT_CTRLS); - case VIDIOC_G_EXT_CTRLS: - { - struct v4l2_ext_controls *c = arg; - - if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { - int i; - int err = 0; - - for (i = 0; i < c->count; i++) { - ctrl.id = c->controls[i].id; - ctrl.value = c->controls[i].value; - err = cx18_g_ctrl(cx, &ctrl); - c->controls[i].value = ctrl.value; - if (err) { - c->error_idx = i; - break; - } - } + if (err) return err; + + if (p.video_encoding != cx->params.video_encoding) { + int is_mpeg1 = p.video_encoding == + V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + struct v4l2_format fmt; + + /* fix videodecoder resolution */ + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = cx->params.width + / (is_mpeg1 ? 2 : 1); + fmt.fmt.pix.height = cx->params.height; + cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt); } - CX18_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n"); - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) - return cx2341x_ext_ctrls(&cx->params, 0, arg, cmd); - return -EINVAL; + err = cx2341x_update(cx, cx18_api_func, &cx->params, &p); + if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt) + err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt); + cx->params = p; + cx->dualwatch_stereo_mode = p.audio_properties & 0x0300; + cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03); + return err; } + return -EINVAL; +} - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *c = arg; +int cx18_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - CX18_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n"); - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) - return cx2341x_ext_ctrls(&cx->params, - atomic_read(&cx->ana_capturing), arg, cmd); - return -EINVAL; - } + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; - default: - return -EINVAL; + for (i = 0; i < c->count; i++) { + err = cx18_try_ctrl(file, fh, &c->controls[i]); + if (err) { + c->error_idx = i; + break; + } + } + return err; } - return 0; + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&cx->params, + atomic_read(&cx->ana_capturing), + c, VIDIOC_TRY_EXT_CTRLS); + return -EINVAL; } diff --git a/drivers/media/video/cx18/cx18-controls.h b/drivers/media/video/cx18/cx18-controls.h index 6e985cf422a..e46323700b8 100644 --- a/drivers/media/video/cx18/cx18-controls.h +++ b/drivers/media/video/cx18/cx18-controls.h @@ -21,4 +21,9 @@ * 02111-1307 USA */ -int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg); +int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a); +int cx18_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a); +int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a); +int cx18_try_ext_ctrls(struct file *file, void *fh, + struct v4l2_ext_controls *a); +int cx18_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a); diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 2b810bb2a4c..22434aadde3 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -120,6 +120,7 @@ MODULE_PARM_DESC(cardtype, "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n" "\t\t\t 3 = Compro VideoMate H900\n" "\t\t\t 4 = Yuan MPC718\n" + "\t\t\t 5 = Conexant Raptor PAL/SECAM\n" "\t\t\t 0 = Autodetect (default)\n" "\t\t\t-1 = Ignore this card\n\t\t"); MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); @@ -420,6 +421,7 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) mutex_init(&cx->serialize_lock); mutex_init(&cx->i2c_bus_lock[0]); mutex_init(&cx->i2c_bus_lock[1]); + mutex_init(&cx->gpio_lock); spin_lock_init(&cx->lock); spin_lock_init(&cx->dma_reg_lock); @@ -435,7 +437,7 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) (cx->params.video_temporal_filter_mode << 1) | (cx->params.video_median_filter_type << 2); cx->params.port = CX2341X_PORT_MEMORY; - cx->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI; + cx->params.capabilities = CX2341X_CAP_HAS_TS; init_waitqueue_head(&cx->cap_w); init_waitqueue_head(&cx->mb_apu_waitq); init_waitqueue_head(&cx->mb_cpu_waitq); @@ -614,7 +616,7 @@ static int __devinit cx18_probe(struct pci_dev *dev, cx18_cards[cx18_cards_active] = cx; cx->dev = dev; cx->num = cx18_cards_active++; - snprintf(cx->name, sizeof(cx->name) - 1, "cx18-%d", cx->num); + snprintf(cx->name, sizeof(cx->name), "cx18-%d", cx->num); CX18_INFO("Initializing card #%d\n", cx->num); spin_unlock(&cx18_cards_lock); @@ -721,6 +723,12 @@ static int __devinit cx18_probe(struct pci_dev *dev, /* if no tuner was found, then pick the first tuner in the card list */ if (cx->options.tuner == -1 && cx->card->tuners[0].std) { cx->std = cx->card->tuners[0].std; + if (cx->std & V4L2_STD_PAL) + cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H; + else if (cx->std & V4L2_STD_NTSC) + cx->std = V4L2_STD_NTSC_M; + else if (cx->std & V4L2_STD_SECAM) + cx->std = V4L2_STD_SECAM_L; cx->options.tuner = cx->card->tuners[0].tuner; } if (cx->options.radio == -1) @@ -818,6 +826,9 @@ int cx18_init_on_first_open(struct cx18 *cx) int video_input; int fw_retry_count = 3; struct v4l2_frequency vf; + struct cx18_open_id fh; + + fh.cx = cx; if (test_bit(CX18_F_I_FAILED, &cx->i_flags)) return -ENXIO; @@ -869,13 +880,13 @@ int cx18_init_on_first_open(struct cx18 *cx) video_input = cx->active_input; cx->active_input++; /* Force update of input */ - cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_INPUT, &video_input); + cx18_s_input(NULL, &fh, video_input); /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code in one place. */ cx->std++; /* Force full standard initialization */ - cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_STD, &cx->tuner_std); - cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_FREQUENCY, &vf); + cx18_s_std(NULL, &fh, &cx->tuner_std); + cx18_s_frequency(NULL, &fh, &vf); return 0; } diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index de14ab59a20..45e31b04730 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -75,7 +75,8 @@ #define CX18_CARD_HVR_1600_SAMSUNG 1 /* Hauppauge HVR 1600 (Samsung memory) */ #define CX18_CARD_COMPRO_H900 2 /* Compro VideoMate H900 */ #define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */ -#define CX18_CARD_LAST 3 +#define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */ +#define CX18_CARD_LAST 4 #define CX18_ENC_STREAM_TYPE_MPG 0 #define CX18_ENC_STREAM_TYPE_TS 1 @@ -94,6 +95,7 @@ #define CX18_PCI_ID_HAUPPAUGE 0x0070 #define CX18_PCI_ID_COMPRO 0x185b #define CX18_PCI_ID_YUAN 0x12ab +#define CX18_PCI_ID_CONEXANT 0x14f1 /* ======================================================================== */ /* ========================== START USER SETTABLE DMA VARIABLES =========== */ @@ -228,9 +230,7 @@ struct cx18_dvb { struct dvb_net dvbnet; int enabled; int feeding; - struct mutex feedlock; - }; struct cx18; /* forward reference */ @@ -427,6 +427,7 @@ struct cx18 { /* gpio */ u32 gpio_dir; u32 gpio_val; + struct mutex gpio_lock; /* v4l2 and User settings */ diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index 2694ce35063..2d630d9f749 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/drivers/media/video/cx18/cx18-firmware.c @@ -41,9 +41,6 @@ #define CX18_REG_BUS_TIMEOUT_EN 0xc72024 -#define CX18_AUDIO_ENABLE 0xc72014 -#define CX18_REG_BUS_TIMEOUT_EN 0xc72024 - #define CX18_FAST_CLOCK_PLL_INT 0xc78000 #define CX18_FAST_CLOCK_PLL_FRAC 0xc78004 #define CX18_FAST_CLOCK_PLL_POST 0xc78008 @@ -90,7 +87,7 @@ #define CX18_DSP0_INTERRUPT_MASK 0xd0004C /* Encoder/decoder firmware sizes */ -#define CX18_FW_CPU_SIZE (174716) +#define CX18_FW_CPU_SIZE (158332) #define CX18_FW_APU_SIZE (141200) #define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */ @@ -345,6 +342,11 @@ int cx18_firmware_init(struct cx18 *cx) int sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx, CX18_FW_APU_SIZE); + write_enc(0xE51FF004, 0); + write_enc(0xa00000, 4); /* todo: not hardcoded */ + write_reg(0x00010000, CX18_PROC_SOFT_RESET); /* Start APU */ + cx18_msleep_timeout(500, 0); + sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx, CX18_FW_CPU_SIZE); diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c index b302833f6f9..3d495dba498 100644 --- a/drivers/media/video/cx18/cx18-gpio.c +++ b/drivers/media/video/cx18/cx18-gpio.c @@ -69,6 +69,7 @@ void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) /* Assuming that the masks are a subset of the bits in gpio_dir */ /* Assert */ + mutex_lock(&cx->gpio_lock); cx->gpio_val = (cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask); gpio_write(cx); @@ -79,10 +80,53 @@ void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) (cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask); gpio_write(cx); schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery)); + mutex_unlock(&cx->gpio_lock); } +void cx18_reset_ir_gpio(void *data) +{ + struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; + const struct cx18_gpio_i2c_slave_reset *p; + + p = &cx->card->gpio_i2c_slave_reset; + + if (p->ir_reset_mask == 0) + return; + + CX18_DEBUG_INFO("Resetting IR microcontroller\n"); + + /* + Assert timing for the Z8F0811 on HVR-1600 boards: + 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to initiate + 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock cycles + (6,601,085 nanoseconds ~= 7 milliseconds) + 3. DBG pin must be high before chip exits reset for normal operation. + DBG is open drain and hopefully pulled high since we don't + normally drive it (GPIO 1?) for the HVR-1600 + 4. Z8F0811 won't exit reset until RESET is deasserted + */ + mutex_lock(&cx->gpio_lock); + cx->gpio_val = cx->gpio_val & ~p->ir_reset_mask; + gpio_write(cx); + mutex_unlock(&cx->gpio_lock); + schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted)); + + /* + Zilog comes out of reset, loads reset vector address and executes + from there. Required recovery delay unknown. + */ + mutex_lock(&cx->gpio_lock); + cx->gpio_val = cx->gpio_val | p->ir_reset_mask; + gpio_write(cx); + mutex_unlock(&cx->gpio_lock); + schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery)); +} +EXPORT_SYMBOL(cx18_reset_ir_gpio); +/* This symbol is exported for use by an infrared module for the IR-blaster */ + void cx18_gpio_init(struct cx18 *cx) { + mutex_lock(&cx->gpio_lock); cx->gpio_dir = cx->card->gpio_init.direction; cx->gpio_val = cx->card->gpio_init.initial_value; @@ -91,14 +135,17 @@ void cx18_gpio_init(struct cx18 *cx) cx->gpio_val |= 1 << cx->card->xceive_pin; } - if (cx->gpio_dir == 0) + if (cx->gpio_dir == 0) { + mutex_unlock(&cx->gpio_lock); return; + } CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n", read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_DIR2), read_reg(CX18_REG_GPIO_OUT1), read_reg(CX18_REG_GPIO_OUT2)); gpio_write(cx); + mutex_unlock(&cx->gpio_lock); } /* Xceive tuner reset function */ @@ -112,13 +159,52 @@ int cx18_reset_tuner_gpio(void *dev, int cmd, int value) return 0; CX18_DEBUG_INFO("Resetting tuner\n"); + mutex_lock(&cx->gpio_lock); cx->gpio_val &= ~(1 << cx->card->xceive_pin); - gpio_write(cx); + mutex_unlock(&cx->gpio_lock); schedule_timeout_interruptible(msecs_to_jiffies(1)); + mutex_lock(&cx->gpio_lock); cx->gpio_val |= 1 << cx->card->xceive_pin; gpio_write(cx); + mutex_unlock(&cx->gpio_lock); schedule_timeout_interruptible(msecs_to_jiffies(1)); return 0; } + +int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg) +{ + struct v4l2_routing *route = arg; + u32 mask, data; + + switch (command) { + case VIDIOC_INT_S_AUDIO_ROUTING: + if (route->input > 2) + return -EINVAL; + mask = cx->card->gpio_audio_input.mask; + switch (route->input) { + case 0: + data = cx->card->gpio_audio_input.tuner; + break; + case 1: + data = cx->card->gpio_audio_input.linein; + break; + case 2: + default: + data = cx->card->gpio_audio_input.radio; + break; + } + break; + + default: + return -EINVAL; + } + if (mask) { + mutex_lock(&cx->gpio_lock); + cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask); + gpio_write(cx); + mutex_unlock(&cx->gpio_lock); + } + return 0; +} diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h index 525c328f748..22cd7ddf855 100644 --- a/drivers/media/video/cx18/cx18-gpio.h +++ b/drivers/media/video/cx18/cx18-gpio.h @@ -22,4 +22,6 @@ void cx18_gpio_init(struct cx18 *cx); void cx18_reset_i2c_slaves_gpio(struct cx18 *cx); +void cx18_reset_ir_gpio(void *data); int cx18_reset_tuner_gpio(void *dev, int cmd, int value); +int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg); diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 680bc4e35b7..6023ba3bd3a 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -39,10 +39,6 @@ #define GETSCL_BIT 0x0004 #define GETSDL_BIT 0x0008 -#ifndef I2C_ADAP_CLASS_TV_ANALOG -#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG -#endif - #define CX18_CS5345_I2C_ADDR 0x4c /* This array should match the CX18_HW_ defines */ @@ -311,8 +307,12 @@ int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg) { int addr; - if (hw == CX18_HW_GPIO || hw == 0) + if (hw == 0) return 0; + + if (hw == CX18_HW_GPIO) + return cx18_gpio(cx, cmd, arg); + if (hw == CX18_HW_CX23418) return cx18_av_cmd(cx, cmd, arg); @@ -350,6 +350,8 @@ void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg) cx18_av_cmd(cx, cmd, arg); i2c_clients_command(&cx->i2c_adap[0], cmd, arg); i2c_clients_command(&cx->i2c_adap[1], cmd, arg); + if (cx->hw_flags & CX18_HW_GPIO) + cx18_gpio(cx, cmd, arg); } /* init + register i2c algo-bit adapter */ @@ -358,6 +360,18 @@ int init_cx18_i2c(struct cx18 *cx) int i; CX18_DEBUG_I2C("i2c init\n"); + /* Sanity checks for the I2C hardware arrays. They must be the + * same size and GPIO/CX23418 must be the last entries. + */ + if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) || + ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || + CX18_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 2)) || + CX18_HW_CX23418 != (1 << (ARRAY_SIZE(hw_addrs) - 1)) || + hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) { + CX18_ERR("Mismatched I2C hardware arrays\n"); + return -ENODEV; + } + for (i = 0; i < 2; i++) { memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, sizeof(struct i2c_adapter)); @@ -391,6 +405,7 @@ int init_cx18_i2c(struct cx18 *cx) write_reg_sync(0x00c000c0, 0xc7001c); mdelay(10); write_reg_sync(0x00c00000, 0xc7001c); + mdelay(10); write_reg_sync(0x00c00000, 0xc730c8); /* Set to edge-triggered intrs. */ write_reg_sync(0x00c00000, 0xc730c4); /* Clear any stale intrs */ diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 4151f1e5493..0d74e59e503 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -100,19 +100,6 @@ void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) } } -static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) -{ - int f, l; - u16 set = 0; - - for (f = 0; f < 2; f++) { - for (l = 0; l < 24; l++) { - fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal); - set |= fmt->service_lines[f][l]; - } - } - return set != 0; -} u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt) { @@ -126,35 +113,167 @@ u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt) return set; } -static const struct { - v4l2_std_id std; - char *name; -} enum_stds[] = { - { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" }, - { V4L2_STD_PAL_DK, "PAL-DK" }, - { V4L2_STD_PAL_I, "PAL-I" }, - { V4L2_STD_PAL_M, "PAL-M" }, - { V4L2_STD_PAL_N, "PAL-N" }, - { V4L2_STD_PAL_Nc, "PAL-Nc" }, - { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" }, - { V4L2_STD_SECAM_DK, "SECAM-DK" }, - { V4L2_STD_SECAM_L, "SECAM-L" }, - { V4L2_STD_SECAM_LC, "SECAM-L'" }, - { V4L2_STD_NTSC_M, "NTSC-M" }, - { V4L2_STD_NTSC_M_JP, "NTSC-J" }, - { V4L2_STD_NTSC_M_KR, "NTSC-K" }, -}; - -static const struct v4l2_standard cx18_std_60hz = { - .frameperiod = {.numerator = 1001, .denominator = 30000}, - .framelines = 525, -}; - -static const struct v4l2_standard cx18_std_50hz = { - .frameperiod = { .numerator = 1, .denominator = 25 }, - .framelines = 625, -}; +static int cx18_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + + pixfmt->width = cx->params.width; + pixfmt->height = cx->params.height; + pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + pixfmt->field = V4L2_FIELD_INTERLACED; + pixfmt->priv = 0; + if (id->type == CX18_ENC_STREAM_TYPE_YUV) { + pixfmt->pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + pixfmt->sizeimage = + pixfmt->height * pixfmt->width + + pixfmt->height * (pixfmt->width / 2); + pixfmt->bytesperline = 720; + } else { + pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; + pixfmt->sizeimage = 128 * 1024; + pixfmt->bytesperline = 0; + } + return 0; +} + +static int cx18_g_fmt_vbi_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi; + + vbifmt->sampling_rate = 27000000; + vbifmt->offset = 248; + vbifmt->samples_per_line = cx->vbi.raw_decoder_line_size - 4; + vbifmt->sample_format = V4L2_PIX_FMT_GREY; + vbifmt->start[0] = cx->vbi.start[0]; + vbifmt->start[1] = cx->vbi.start[1]; + vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count; + vbifmt->flags = 0; + vbifmt->reserved[0] = 0; + vbifmt->reserved[1] = 0; + return 0; +} + +static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + return -EINVAL; +} + +static int cx18_try_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; + + int w = fmt->fmt.pix.width; + int h = fmt->fmt.pix.height; + + w = min(w, 720); + w = max(w, 1); + h = min(h, cx->is_50hz ? 576 : 480); + h = max(h, 2); + cx18_g_fmt_vid_cap(file, fh, fmt); + fmt->fmt.pix.width = w; + fmt->fmt.pix.height = h; + return 0; +} + +static int cx18_try_fmt_vbi_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + return cx18_g_fmt_vbi_cap(file, fh, fmt); +} + +static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + return -EINVAL; +} + +static int cx18_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; + int ret; + int w = fmt->fmt.pix.width; + int h = fmt->fmt.pix.height; + + ret = v4l2_prio_check(&cx->prio, &id->prio); + if (ret) + return ret; + + ret = cx18_try_fmt_vid_cap(file, fh, fmt); + if (ret) + return ret; + + if (cx->params.width == w && cx->params.height == h) + return 0; + + if (atomic_read(&cx->ana_capturing) > 0) + return -EBUSY; + + cx->params.width = w; + cx->params.height = h; + cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); + return cx18_g_fmt_vid_cap(file, fh, fmt); +} + +static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; + int ret; + + ret = v4l2_prio_check(&cx->prio, &id->prio); + if (ret) + return ret; + + if (id->type == CX18_ENC_STREAM_TYPE_VBI && + cx->vbi.sliced_in->service_set && + atomic_read(&cx->ana_capturing) > 0) + return -EBUSY; + + cx->vbi.sliced_in->service_set = 0; + cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); + return cx18_g_fmt_vbi_cap(file, fh, fmt); +} + +static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, + struct v4l2_format *fmt) +{ + return -EINVAL; +} +static int cx18_g_chip_ident(struct file *file, void *fh, + struct v4l2_chip_ident *chip) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (chip->match_type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(chip->match_type, chip->match_chip)) + chip->ident = V4L2_IDENT_CX23418; + return 0; + } + if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return cx18_i2c_id(cx, chip->match_chip, VIDIOC_G_CHIP_IDENT, + chip); + if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR) + return cx18_call_i2c_client(cx, chip->match_chip, + VIDIOC_G_CHIP_IDENT, chip); + return -EINVAL; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) { struct v4l2_register *regs = arg; @@ -174,665 +293,478 @@ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) return 0; } -static int cx18_get_fmt(struct cx18 *cx, int streamtype, struct v4l2_format *fmt) -{ - switch (fmt->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - fmt->fmt.pix.width = cx->params.width; - fmt->fmt.pix.height = cx->params.height; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - if (streamtype == CX18_ENC_STREAM_TYPE_YUV) { - fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; - /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ - fmt->fmt.pix.sizeimage = - fmt->fmt.pix.height * fmt->fmt.pix.width + - fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); - } else { - fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - fmt->fmt.pix.sizeimage = 128 * 1024; - } - break; - - case V4L2_BUF_TYPE_VBI_CAPTURE: - fmt->fmt.vbi.sampling_rate = 27000000; - fmt->fmt.vbi.offset = 248; - fmt->fmt.vbi.samples_per_line = cx->vbi.raw_decoder_line_size - 4; - fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - fmt->fmt.vbi.start[0] = cx->vbi.start[0]; - fmt->fmt.vbi.start[1] = cx->vbi.start[1]; - fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = cx->vbi.count; - break; +static int cx18_g_register(struct file *file, void *fh, + struct v4l2_register *reg) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER, + reg); + return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_G_REGISTER, + reg); +} - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - { - struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; +static int cx18_s_register(struct file *file, void *fh, + struct v4l2_register *reg) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return cx18_i2c_id(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER, + reg); + return cx18_call_i2c_client(cx, reg->match_chip, VIDIOC_DBG_S_REGISTER, + reg); +} +#endif - vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; - memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); - memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); +static int cx18_g_priority(struct file *file, void *fh, enum v4l2_priority *p) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - cx18_av_cmd(cx, VIDIOC_G_FMT, fmt); - vbifmt->service_set = cx18_get_service_set(vbifmt); - break; - } - default: - return -EINVAL; - } + *p = v4l2_prio_max(&cx->prio); return 0; } -static int cx18_try_or_set_fmt(struct cx18 *cx, int streamtype, - struct v4l2_format *fmt, int set_fmt) +static int cx18_s_priority(struct file *file, void *fh, enum v4l2_priority prio) { - struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; - u16 set; + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; - /* set window size */ - if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - int w = fmt->fmt.pix.width; - int h = fmt->fmt.pix.height; + return v4l2_prio_change(&cx->prio, &id->prio, prio); +} - if (w > 720) - w = 720; - else if (w < 1) - w = 1; - if (h > (cx->is_50hz ? 576 : 480)) - h = (cx->is_50hz ? 576 : 480); - else if (h < 2) - h = 2; - cx18_get_fmt(cx, streamtype, fmt); - fmt->fmt.pix.width = w; - fmt->fmt.pix.height = h; +static int cx18_querycap(struct file *file, void *fh, + struct v4l2_capability *vcap) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - if (!set_fmt || (cx->params.width == w && cx->params.height == h)) - return 0; - if (atomic_read(&cx->ana_capturing) > 0) - return -EBUSY; - - cx->params.width = w; - cx->params.height = h; - if (w != 720 || h != (cx->is_50hz ? 576 : 480)) - cx->params.video_temporal_filter = 0; - else - cx->params.video_temporal_filter = 8; - cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); - return cx18_get_fmt(cx, streamtype, fmt); - } + strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver)); + strlcpy(vcap->card, cx->card_name, sizeof(vcap->card)); + strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info)); + vcap->version = CX18_DRIVER_VERSION; /* version */ + vcap->capabilities = cx->v4l2_cap; /* capabilities */ + return 0; +} - /* set raw VBI format */ - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (set_fmt && streamtype == CX18_ENC_STREAM_TYPE_VBI && - cx->vbi.sliced_in->service_set && - atomic_read(&cx->ana_capturing) > 0) - return -EBUSY; - if (set_fmt) { - cx->vbi.sliced_in->service_set = 0; - cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); - } - return cx18_get_fmt(cx, streamtype, fmt); - } +static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - /* any else but sliced VBI capture is an error */ - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; + return cx18_get_audio_input(cx, vin->index, vin); +} - /* TODO: implement sliced VBI, for now silently return 0 */ - return 0; +static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - /* set sliced VBI capture format */ - vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; - memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + vin->index = cx->audio_input; + return cx18_get_audio_input(cx, vin->index, vin); +} - if (vbifmt->service_set) - cx18_expand_service_set(vbifmt, cx->is_50hz); - set = check_service_set(vbifmt, cx->is_50hz); - vbifmt->service_set = cx18_get_service_set(vbifmt); +static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - if (!set_fmt) - return 0; - if (set == 0) + if (vout->index >= cx->nof_audio_inputs) return -EINVAL; - if (atomic_read(&cx->ana_capturing) > 0 && cx->vbi.sliced_in->service_set == 0) - return -EBUSY; - cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); - memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in)); + cx->audio_input = vout->index; + cx18_audio_set_io(cx); return 0; } -static int cx18_debug_ioctls(struct file *filp, unsigned int cmd, void *arg) +static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin) { - struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data; - struct cx18 *cx = id->cx; - struct v4l2_register *reg = arg; + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - switch (cmd) { - /* ioctls to allow direct access to the encoder registers for testing */ - case VIDIOC_DBG_G_REGISTER: - if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) - return cx18_cxc(cx, cmd, arg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return cx18_i2c_id(cx, reg->match_chip, cmd, arg); - return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg); - - case VIDIOC_DBG_S_REGISTER: - if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) - return cx18_cxc(cx, cmd, arg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return cx18_i2c_id(cx, reg->match_chip, cmd, arg); - return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg); - - case VIDIOC_G_CHIP_IDENT: { - struct v4l2_chip_ident *chip = arg; - - chip->ident = V4L2_IDENT_NONE; - chip->revision = 0; - if (reg->match_type == V4L2_CHIP_MATCH_HOST) { - if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) { - struct v4l2_chip_ident *chip = arg; - - chip->ident = V4L2_IDENT_CX23418; - } - return 0; - } - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return cx18_i2c_id(cx, reg->match_chip, cmd, arg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR) - return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg); - return -EINVAL; - } - - case VIDIOC_INT_S_AUDIO_ROUTING: { - struct v4l2_routing *route = arg; + /* set it to defaults from our table */ + return cx18_get_input(cx, vin->index, vin); +} - cx18_audio_set_route(cx, route); - break; - } +static int cx18_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - default: + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - } + cropcap->bounds.top = cropcap->bounds.left = 0; + cropcap->bounds.width = 720; + cropcap->bounds.height = cx->is_50hz ? 576 : 480; + cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10; + cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11; + cropcap->defrect = cropcap->bounds; return 0; } -int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, void *arg) +static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) { - struct cx18_open_id *id = NULL; - - if (filp) - id = (struct cx18_open_id *)filp->private_data; - - switch (cmd) { - case VIDIOC_G_PRIORITY: - { - enum v4l2_priority *p = arg; + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; + int ret; - *p = v4l2_prio_max(&cx->prio); - break; - } + ret = v4l2_prio_check(&cx->prio, &id->prio); + if (ret) + return ret; - case VIDIOC_S_PRIORITY: - { - enum v4l2_priority *prio = arg; + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return cx18_av_cmd(cx, VIDIOC_S_CROP, crop); +} - return v4l2_prio_change(&cx->prio, &id->prio, *prio); - } +static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - case VIDIOC_QUERYCAP:{ - struct v4l2_capability *vcap = arg; + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return cx18_av_cmd(cx, VIDIOC_G_CROP, crop); +} - memset(vcap, 0, sizeof(*vcap)); - strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver)); - strlcpy(vcap->card, cx->card_name, sizeof(vcap->card)); - strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info)); - vcap->version = CX18_DRIVER_VERSION; /* version */ - vcap->capabilities = cx->v4l2_cap; /* capabilities */ +static int cx18_enum_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *fmt) +{ + static struct v4l2_fmtdesc formats[] = { + { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, + "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 } + }, + { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED, + "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 } + } + }; - /* reserved.. must set to 0! */ - vcap->reserved[0] = vcap->reserved[1] = - vcap->reserved[2] = vcap->reserved[3] = 0; - break; - } + if (fmt->index > 1) + return -EINVAL; + *fmt = formats[fmt->index]; + return 0; +} - case VIDIOC_ENUMAUDIO:{ - struct v4l2_audio *vin = arg; +static int cx18_g_input(struct file *file, void *fh, unsigned int *i) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - return cx18_get_audio_input(cx, vin->index, vin); - } + *i = cx->active_input; + return 0; +} - case VIDIOC_G_AUDIO:{ - struct v4l2_audio *vin = arg; +int cx18_s_input(struct file *file, void *fh, unsigned int inp) +{ + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; + int ret; - vin->index = cx->audio_input; - return cx18_get_audio_input(cx, vin->index, vin); - } + ret = v4l2_prio_check(&cx->prio, &id->prio); + if (ret) + return ret; - case VIDIOC_S_AUDIO:{ - struct v4l2_audio *vout = arg; + if (inp < 0 || inp >= cx->nof_inputs) + return -EINVAL; - if (vout->index >= cx->nof_audio_inputs) - return -EINVAL; - cx->audio_input = vout->index; - cx18_audio_set_io(cx); - break; + if (inp == cx->active_input) { + CX18_DEBUG_INFO("Input unchanged\n"); + return 0; } - case VIDIOC_ENUMINPUT:{ - struct v4l2_input *vin = arg; - - /* set it to defaults from our table */ - return cx18_get_input(cx, vin->index, vin); - } + CX18_DEBUG_INFO("Changing input from %d to %d\n", + cx->active_input, inp); - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: { - struct v4l2_format *fmt = arg; + cx->active_input = inp; + /* Set the audio input to whatever is appropriate for the input type. */ + cx->audio_input = cx->card->video_inputs[inp].audio_index; - return cx18_try_or_set_fmt(cx, id->type, fmt, cmd == VIDIOC_S_FMT); - } + /* prevent others from messing with the streams until + we're finished changing inputs. */ + cx18_mute(cx); + cx18_video_set_io(cx); + cx18_audio_set_io(cx); + cx18_unmute(cx); + return 0; +} - case VIDIOC_G_FMT: { - struct v4l2_format *fmt = arg; - int type = fmt->type; +static int cx18_g_frequency(struct file *file, void *fh, + struct v4l2_frequency *vf) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - memset(fmt, 0, sizeof(*fmt)); - fmt->type = type; - return cx18_get_fmt(cx, id->type, fmt); - } + if (vf->tuner != 0) + return -EINVAL; - case VIDIOC_CROPCAP: { - struct v4l2_cropcap *cropcap = arg; - - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - cropcap->bounds.top = cropcap->bounds.left = 0; - cropcap->bounds.width = 720; - cropcap->bounds.height = cx->is_50hz ? 576 : 480; - cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10; - cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11; - cropcap->defrect = cropcap->bounds; - return 0; - } + cx18_call_i2c_clients(cx, VIDIOC_G_FREQUENCY, vf); + return 0; +} - case VIDIOC_S_CROP: { - struct v4l2_crop *crop = arg; +int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) +{ + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; + int ret; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - return cx18_av_cmd(cx, VIDIOC_S_CROP, arg); - } + ret = v4l2_prio_check(&cx->prio, &id->prio); + if (ret) + return ret; - case VIDIOC_G_CROP: { - struct v4l2_crop *crop = arg; + if (vf->tuner != 0) + return -EINVAL; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - return cx18_av_cmd(cx, VIDIOC_G_CROP, arg); - } + cx18_mute(cx); + CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); + cx18_call_i2c_clients(cx, VIDIOC_S_FREQUENCY, vf); + cx18_unmute(cx); + return 0; +} - case VIDIOC_ENUM_FMT: { - static struct v4l2_fmtdesc formats[] = { - { 0, 0, 0, - "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, - { 0, 0, 0, 0 } - }, - { 1, 0, V4L2_FMT_FLAG_COMPRESSED, - "MPEG", V4L2_PIX_FMT_MPEG, - { 0, 0, 0, 0 } - } - }; - struct v4l2_fmtdesc *fmt = arg; - enum v4l2_buf_type type = fmt->type; - - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - break; - default: - return -EINVAL; - } - if (fmt->index > 1) - return -EINVAL; - *fmt = formats[fmt->index]; - fmt->type = type; - return 0; - } +static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - case VIDIOC_G_INPUT:{ - *(int *)arg = cx->active_input; - break; - } + *std = cx->std; + return 0; +} - case VIDIOC_S_INPUT:{ - int inp = *(int *)arg; +int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; + int ret; - if (inp < 0 || inp >= cx->nof_inputs) - return -EINVAL; + ret = v4l2_prio_check(&cx->prio, &id->prio); + if (ret) + return ret; - if (inp == cx->active_input) { - CX18_DEBUG_INFO("Input unchanged\n"); - break; - } - CX18_DEBUG_INFO("Changing input from %d to %d\n", - cx->active_input, inp); + if ((*std & V4L2_STD_ALL) == 0) + return -EINVAL; - cx->active_input = inp; - /* Set the audio input to whatever is appropriate for the - input type. */ - cx->audio_input = cx->card->video_inputs[inp].audio_index; + if (*std == cx->std) + return 0; - /* prevent others from messing with the streams until - we're finished changing inputs. */ - cx18_mute(cx); - cx18_video_set_io(cx); - cx18_audio_set_io(cx); - cx18_unmute(cx); - break; + if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) || + atomic_read(&cx->ana_capturing) > 0) { + /* Switching standard would turn off the radio or mess + with already running streams, prevent that by + returning EBUSY. */ + return -EBUSY; } - case VIDIOC_G_FREQUENCY:{ - struct v4l2_frequency *vf = arg; + cx->std = *std; + cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; + cx->params.is_50hz = cx->is_50hz = !cx->is_60hz; + cx->params.width = 720; + cx->params.height = cx->is_50hz ? 576 : 480; + cx->vbi.count = cx->is_50hz ? 18 : 12; + cx->vbi.start[0] = cx->is_50hz ? 6 : 10; + cx->vbi.start[1] = cx->is_50hz ? 318 : 273; + cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284; + CX18_DEBUG_INFO("Switching standard to %llx.\n", + (unsigned long long) cx->std); + + /* Tuner */ + cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std); + return 0; +} - if (vf->tuner != 0) - return -EINVAL; - cx18_call_i2c_clients(cx, cmd, arg); - break; - } +static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) +{ + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; + int ret; - case VIDIOC_S_FREQUENCY:{ - struct v4l2_frequency vf = *(struct v4l2_frequency *)arg; + ret = v4l2_prio_check(&cx->prio, &id->prio); + if (ret) + return ret; - if (vf.tuner != 0) - return -EINVAL; + if (vt->index != 0) + return -EINVAL; - cx18_mute(cx); - CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency); - cx18_call_i2c_clients(cx, cmd, &vf); - cx18_unmute(cx); - break; - } + /* Setting tuner can only set audio mode */ + cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt); - case VIDIOC_ENUMSTD:{ - struct v4l2_standard *vs = arg; - int idx = vs->index; + return 0; +} - if (idx < 0 || idx >= ARRAY_SIZE(enum_stds)) - return -EINVAL; +static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - *vs = (enum_stds[idx].std & V4L2_STD_525_60) ? - cx18_std_60hz : cx18_std_50hz; - vs->index = idx; - vs->id = enum_stds[idx].std; - strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name)); - break; - } + if (vt->index != 0) + return -EINVAL; - case VIDIOC_G_STD:{ - *(v4l2_std_id *) arg = cx->std; - break; + cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt); + + if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { + strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name)); + vt->type = V4L2_TUNER_RADIO; + } else { + strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name)); + vt->type = V4L2_TUNER_ANALOG_TV; } - case VIDIOC_S_STD: { - v4l2_std_id std = *(v4l2_std_id *) arg; + return 0; +} - if ((std & V4L2_STD_ALL) == 0) - return -EINVAL; +static int cx18_g_sliced_vbi_cap(struct file *file, void *fh, + struct v4l2_sliced_vbi_cap *cap) +{ + return -EINVAL; +} - if (std == cx->std) - break; +static int cx18_g_enc_index(struct file *file, void *fh, + struct v4l2_enc_idx *idx) +{ + return -EINVAL; +} - if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) || - atomic_read(&cx->ana_capturing) > 0) { - /* Switching standard would turn off the radio or mess - with already running streams, prevent that by - returning EBUSY. */ - return -EBUSY; - } +static int cx18_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *enc) +{ + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; - cx->std = std; - cx->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0; - cx->params.is_50hz = cx->is_50hz = !cx->is_60hz; - cx->params.width = 720; - cx->params.height = cx->is_50hz ? 576 : 480; - cx->vbi.count = cx->is_50hz ? 18 : 12; - cx->vbi.start[0] = cx->is_50hz ? 6 : 10; - cx->vbi.start[1] = cx->is_50hz ? 318 : 273; - cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284; - CX18_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)cx->std); - - /* Tuner */ - cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std); + switch (enc->cmd) { + case V4L2_ENC_CMD_START: + CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); + enc->flags = 0; + return cx18_start_capture(id); + + case V4L2_ENC_CMD_STOP: + CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); + enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; + cx18_stop_capture(id, + enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); break; - } - - case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */ - struct v4l2_tuner *vt = arg; - if (vt->index != 0) - return -EINVAL; - - cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt); + case V4L2_ENC_CMD_PAUSE: + CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); + enc->flags = 0; + if (!atomic_read(&cx->ana_capturing)) + return -EPERM; + if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) + return 0; + cx18_mute(cx); + cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx)); break; - } - - case VIDIOC_G_TUNER: { - struct v4l2_tuner *vt = arg; - - if (vt->index != 0) - return -EINVAL; - - memset(vt, 0, sizeof(*vt)); - cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt); - if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { - strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name)); - vt->type = V4L2_TUNER_RADIO; - } else { - strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name)); - vt->type = V4L2_TUNER_ANALOG_TV; - } + case V4L2_ENC_CMD_RESUME: + CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); + enc->flags = 0; + if (!atomic_read(&cx->ana_capturing)) + return -EPERM; + if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) + return 0; + cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx)); + cx18_unmute(cx); break; - } - case VIDIOC_G_SLICED_VBI_CAP: { - struct v4l2_sliced_vbi_cap *cap = arg; - int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; - int f, l; - enum v4l2_buf_type type = cap->type; - - memset(cap, 0, sizeof(*cap)); - cap->type = type; - if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - for (f = 0; f < 2; f++) { - for (l = 0; l < 24; l++) { - if (valid_service_line(f, l, cx->is_50hz)) - cap->service_lines[f][l] = set; - } - } - return 0; - } + default: + CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); return -EINVAL; } + return 0; +} - case VIDIOC_ENCODER_CMD: - case VIDIOC_TRY_ENCODER_CMD: { - struct v4l2_encoder_cmd *enc = arg; - int try = cmd == VIDIOC_TRY_ENCODER_CMD; - - memset(&enc->raw, 0, sizeof(enc->raw)); - switch (enc->cmd) { - case V4L2_ENC_CMD_START: - enc->flags = 0; - if (try) - return 0; - return cx18_start_capture(id); - - case V4L2_ENC_CMD_STOP: - enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; - if (try) - return 0; - cx18_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); - return 0; +static int cx18_try_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *enc) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - case V4L2_ENC_CMD_PAUSE: - enc->flags = 0; - if (try) - return 0; - if (!atomic_read(&cx->ana_capturing)) - return -EPERM; - if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) - return 0; - cx18_mute(cx); - cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx)); - break; - - case V4L2_ENC_CMD_RESUME: - enc->flags = 0; - if (try) - return 0; - if (!atomic_read(&cx->ana_capturing)) - return -EPERM; - if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) - return 0; - cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx)); - cx18_unmute(cx); - break; - default: - return -EINVAL; - } + switch (enc->cmd) { + case V4L2_ENC_CMD_START: + CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); + enc->flags = 0; break; - } - case VIDIOC_LOG_STATUS: - { - struct v4l2_input vidin; - struct v4l2_audio audin; - int i; + case V4L2_ENC_CMD_STOP: + CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); + enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; + break; - CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num); - if (cx->hw_flags & CX18_HW_TVEEPROM) { - struct tveeprom tv; + case V4L2_ENC_CMD_PAUSE: + CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); + enc->flags = 0; + break; - cx18_read_eeprom(cx, &tv); - } - cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL); - cx18_get_input(cx, cx->active_input, &vidin); - cx18_get_audio_input(cx, cx->audio_input, &audin); - CX18_INFO("Video Input: %s\n", vidin.name); - CX18_INFO("Audio Input: %s\n", audin.name); - CX18_INFO("Tuner: %s\n", - test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? - "Radio" : "TV"); - cx2341x_log_status(&cx->params, cx->name); - CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags); - for (i = 0; i < CX18_MAX_STREAMS; i++) { - struct cx18_stream *s = &cx->streams[i]; - - if (s->v4l2dev == NULL || s->buffers == 0) - continue; - CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", - s->name, s->s_flags, - (s->buffers - s->q_free.buffers) * 100 / s->buffers, - (s->buffers * s->buf_size) / 1024, s->buffers); - } - CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", - (long long)cx->mpg_data_received, - (long long)cx->vbi_data_inserted); - CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num); + case V4L2_ENC_CMD_RESUME: + CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); + enc->flags = 0; break; - } default: + CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); return -EINVAL; } return 0; } -static int cx18_v4l2_do_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, void *arg) +static int cx18_log_status(struct file *file, void *fh) { - struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data; - struct cx18 *cx = id->cx; - int ret; + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct v4l2_input vidin; + struct v4l2_audio audin; + int i; + + CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num); + if (cx->hw_flags & CX18_HW_TVEEPROM) { + struct tveeprom tv; + + cx18_read_eeprom(cx, &tv); + } + cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL); + cx18_get_input(cx, cx->active_input, &vidin); + cx18_get_audio_input(cx, cx->audio_input, &audin); + CX18_INFO("Video Input: %s\n", vidin.name); + CX18_INFO("Audio Input: %s\n", audin.name); + mutex_lock(&cx->gpio_lock); + CX18_INFO("GPIO: direction 0x%08x, value 0x%08x\n", + cx->gpio_dir, cx->gpio_val); + mutex_unlock(&cx->gpio_lock); + CX18_INFO("Tuner: %s\n", + test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV"); + cx2341x_log_status(&cx->params, cx->name); + CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags); + for (i = 0; i < CX18_MAX_STREAMS; i++) { + struct cx18_stream *s = &cx->streams[i]; + + if (s->v4l2dev == NULL || s->buffers == 0) + continue; + CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", + s->name, s->s_flags, + (s->buffers - s->q_free.buffers) * 100 / s->buffers, + (s->buffers * s->buf_size) / 1024, s->buffers); + } + CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", + (long long)cx->mpg_data_received, + (long long)cx->vbi_data_inserted); + CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num); + return 0; +} + +static int cx18_default(struct file *file, void *fh, int cmd, void *arg) +{ + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; - /* check priority */ switch (cmd) { - case VIDIOC_S_CTRL: - case VIDIOC_S_STD: - case VIDIOC_S_INPUT: - case VIDIOC_S_TUNER: - case VIDIOC_S_FREQUENCY: - case VIDIOC_S_FMT: - case VIDIOC_S_CROP: - case VIDIOC_S_EXT_CTRLS: - ret = v4l2_prio_check(&cx->prio, &id->prio); - if (ret) - return ret; + case VIDIOC_INT_S_AUDIO_ROUTING: { + struct v4l2_routing *route = arg; + + CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n", + route->input, route->output); + cx18_audio_set_route(cx, route); + break; } - switch (cmd) { - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - case VIDIOC_G_CHIP_IDENT: - case VIDIOC_INT_S_AUDIO_ROUTING: - case VIDIOC_INT_RESET: - if (cx18_debug & CX18_DBGFLG_IOCTL) { - printk(KERN_INFO "cx18%d ioctl: ", cx->num); - v4l_printk_ioctl(cmd); - } - return cx18_debug_ioctls(filp, cmd, arg); - - case VIDIOC_G_PRIORITY: - case VIDIOC_S_PRIORITY: - case VIDIOC_QUERYCAP: - case VIDIOC_ENUMINPUT: - case VIDIOC_G_INPUT: - case VIDIOC_S_INPUT: - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: - case VIDIOC_ENUM_FMT: - case VIDIOC_CROPCAP: - case VIDIOC_G_CROP: - case VIDIOC_S_CROP: - case VIDIOC_G_FREQUENCY: - case VIDIOC_S_FREQUENCY: - case VIDIOC_ENUMSTD: - case VIDIOC_G_STD: - case VIDIOC_S_STD: - case VIDIOC_S_TUNER: - case VIDIOC_G_TUNER: - case VIDIOC_ENUMAUDIO: - case VIDIOC_S_AUDIO: - case VIDIOC_G_AUDIO: - case VIDIOC_G_SLICED_VBI_CAP: - case VIDIOC_LOG_STATUS: - case VIDIOC_G_ENC_INDEX: - case VIDIOC_ENCODER_CMD: - case VIDIOC_TRY_ENCODER_CMD: - if (cx18_debug & CX18_DBGFLG_IOCTL) { - printk(KERN_INFO "cx18%d ioctl: ", cx->num); - v4l_printk_ioctl(cmd); - } - return cx18_v4l2_ioctls(cx, filp, cmd, arg); - - case VIDIOC_QUERYMENU: - case VIDIOC_QUERYCTRL: - case VIDIOC_S_CTRL: - case VIDIOC_G_CTRL: - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_G_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: - if (cx18_debug & CX18_DBGFLG_IOCTL) { - printk(KERN_INFO "cx18%d ioctl: ", cx->num); - v4l_printk_ioctl(cmd); - } - return cx18_control_ioctls(cx, cmd, arg); + case VIDIOC_INT_RESET: { + u32 val = *(u32 *)arg; + + if ((val == 0) || (val & 0x01)) + cx18_reset_ir_gpio(&cx->i2c_algo_cb_data[0]); + break; + } - case 0x00005401: /* Handle isatty() calls */ - return -EINVAL; default: - return v4l_compat_translate_ioctl(inode, filp, cmd, arg, - cx18_v4l2_do_ioctl); + return -EINVAL; } return 0; } @@ -840,12 +772,65 @@ static int cx18_v4l2_do_ioctl(struct inode *inode, struct file *filp, int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { + struct video_device *vfd = video_devdata(filp); struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data; struct cx18 *cx = id->cx; int res; mutex_lock(&cx->serialize_lock); - res = video_usercopy(inode, filp, cmd, arg, cx18_v4l2_do_ioctl); + + if (cx18_debug & CX18_DBGFLG_IOCTL) + vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; + res = video_ioctl2(inode, filp, cmd, arg); + vfd->debug = 0; mutex_unlock(&cx->serialize_lock); return res; } + +void cx18_set_funcs(struct video_device *vdev) +{ + vdev->vidioc_querycap = cx18_querycap; + vdev->vidioc_g_priority = cx18_g_priority; + vdev->vidioc_s_priority = cx18_s_priority; + vdev->vidioc_s_audio = cx18_s_audio; + vdev->vidioc_g_audio = cx18_g_audio; + vdev->vidioc_enumaudio = cx18_enumaudio; + vdev->vidioc_enum_input = cx18_enum_input; + vdev->vidioc_cropcap = cx18_cropcap; + vdev->vidioc_s_crop = cx18_s_crop; + vdev->vidioc_g_crop = cx18_g_crop; + vdev->vidioc_g_input = cx18_g_input; + vdev->vidioc_s_input = cx18_s_input; + vdev->vidioc_g_frequency = cx18_g_frequency; + vdev->vidioc_s_frequency = cx18_s_frequency; + vdev->vidioc_s_tuner = cx18_s_tuner; + vdev->vidioc_g_tuner = cx18_g_tuner; + vdev->vidioc_g_enc_index = cx18_g_enc_index; + vdev->vidioc_g_std = cx18_g_std; + vdev->vidioc_s_std = cx18_s_std; + vdev->vidioc_log_status = cx18_log_status; + vdev->vidioc_enum_fmt_vid_cap = cx18_enum_fmt_vid_cap; + vdev->vidioc_encoder_cmd = cx18_encoder_cmd; + vdev->vidioc_try_encoder_cmd = cx18_try_encoder_cmd; + vdev->vidioc_g_fmt_vid_cap = cx18_g_fmt_vid_cap; + vdev->vidioc_g_fmt_vbi_cap = cx18_g_fmt_vbi_cap; + vdev->vidioc_g_fmt_sliced_vbi_cap = cx18_g_fmt_sliced_vbi_cap; + vdev->vidioc_s_fmt_vid_cap = cx18_s_fmt_vid_cap; + vdev->vidioc_s_fmt_vbi_cap = cx18_s_fmt_vbi_cap; + vdev->vidioc_s_fmt_sliced_vbi_cap = cx18_s_fmt_sliced_vbi_cap; + vdev->vidioc_try_fmt_vid_cap = cx18_try_fmt_vid_cap; + vdev->vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap; + vdev->vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap; + vdev->vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap; + vdev->vidioc_g_chip_ident = cx18_g_chip_ident; +#ifdef CONFIG_VIDEO_ADV_DEBUG + vdev->vidioc_g_register = cx18_g_register; + vdev->vidioc_s_register = cx18_s_register; +#endif + vdev->vidioc_default = cx18_default; + vdev->vidioc_queryctrl = cx18_queryctrl; + vdev->vidioc_querymenu = cx18_querymenu; + vdev->vidioc_g_ext_ctrls = cx18_g_ext_ctrls; + vdev->vidioc_s_ext_ctrls = cx18_s_ext_ctrls; + vdev->vidioc_try_ext_ctrls = cx18_try_ext_ctrls; +} diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h index 9f4c7eb2897..2222f679d86 100644 --- a/drivers/media/video/cx18/cx18-ioctl.h +++ b/drivers/media/video/cx18/cx18-ioctl.h @@ -24,7 +24,9 @@ u16 cx18_service2vbi(int type); void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt); +void cx18_set_funcs(struct video_device *vdev); +int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std); +int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); +int cx18_s_input(struct file *file, void *fh, unsigned int inp); int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); -int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, - void *arg); diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 2a5ccef9185..93177514e84 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -81,6 +81,7 @@ static const struct cx18_api_info api_info[] = { API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), + API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST), API_ENTRY(0, 0, 0), }; diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 1b921a33609..1728b1d832a 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -39,6 +39,7 @@ static struct file_operations cx18_v4l2_enc_fops = { .owner = THIS_MODULE, .read = cx18_v4l2_read, .open = cx18_v4l2_open, + /* FIXME change to video_ioctl2 if serialization lock can be removed */ .ioctl = cx18_v4l2_ioctl, .compat_ioctl = v4l_compat_ioctl32, .release = cx18_v4l2_close, @@ -189,14 +190,15 @@ static int cx18_prep_dev(struct cx18 *cx, int type) s->v4l2dev->type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT | VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER; - snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18%d %s", - cx->num, s->name); + snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d", + cx->num); s->v4l2dev->minor = minor; s->v4l2dev->dev = &cx->dev->dev; s->v4l2dev->fops = cx18_stream_info[type].fops; s->v4l2dev->release = video_device_release; - + s->v4l2dev->tvnorms = V4L2_STD_ALL; + cx18_set_funcs(s->v4l2dev); return 0; } @@ -309,8 +311,10 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister) /* Teardown all streams */ for (type = 0; type < CX18_MAX_STREAMS; type++) { - if (cx->streams[type].dvb.enabled) + if (cx->streams[type].dvb.enabled) { cx18_dvb_unregister(&cx->streams[type]); + cx->streams[type].dvb.enabled = false; + } vdev = cx->streams[type].v4l2dev; diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h index 33f78da9dba..e7ed053059a 100644 --- a/drivers/media/video/cx18/cx23418.h +++ b/drivers/media/video/cx18/cx23418.h @@ -52,6 +52,11 @@ #define EPU_CMD_MASK_DEBUG (EPU_CMD_MASK | 0x000000) #define EPU_CMD_MASK_DE (EPU_CMD_MASK | 0x040000) +#define APU_CMD_MASK 0x10000000 +#define APU_CMD_MASK_ACK (APU_CMD_MASK | 0x80000000) + +#define CX18_APU_RESETAI (APU_CMD_MASK | 0x05) + /* Description: This command indicates that a Memory Descriptor List has been filled with the requested channel type IN[0] - Task handle. Handle of the task diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index c592899a231..22847a0444f 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -77,10 +77,65 @@ const u32 cx2341x_mpeg_ctrls[] = { }; EXPORT_SYMBOL(cx2341x_mpeg_ctrls); +static const struct cx2341x_mpeg_params default_params = { + /* misc */ + .capabilities = 0, + .port = CX2341X_PORT_MEMORY, + .width = 720, + .height = 480, + .is_50hz = 0, + + /* stream */ + .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS, + .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE, + .stream_insert_nav_packets = 0, + + /* audio */ + .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, + .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K, + .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO, + .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, + .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, + .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE, + .audio_mute = 0, + + /* video */ + .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2, + .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, + .video_b_frames = 2, + .video_gop_size = 12, + .video_gop_closure = 1, + .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + .video_bitrate = 6000000, + .video_bitrate_peak = 8000000, + .video_temporal_decimation = 0, + .video_mute = 0, + .video_mute_yuv = 0x008080, /* YCbCr value for black */ + + /* encoding filters */ + .video_spatial_filter_mode = + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, + .video_spatial_filter = 0, + .video_luma_spatial_filter_type = + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, + .video_chroma_spatial_filter_type = + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, + .video_temporal_filter_mode = + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, + .video_temporal_filter = 8, + .video_median_filter_type = + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, + .video_luma_median_filter_top = 255, + .video_luma_median_filter_bottom = 0, + .video_chroma_median_filter_top = 255, + .video_chroma_median_filter_bottom = 0, +}; + /* Map the control ID to the correct field in the cx2341x_mpeg_params struct. Return -EINVAL if the ID is unknown, else return 0. */ -static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params, +static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params, struct v4l2_ext_control *ctrl) { switch (ctrl->id) { @@ -420,7 +475,7 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, return 0; } -int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, +int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, struct v4l2_queryctrl *qctrl) { int err; @@ -430,13 +485,13 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_ENCODING_LAYER_2, V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + default_params.audio_encoding); case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_L2_BITRATE_192K, V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, - V4L2_MPEG_AUDIO_L2_BITRATE_224K); + default_params.audio_l2_bitrate); case V4L2_CID_MPEG_AUDIO_L1_BITRATE: case V4L2_CID_MPEG_AUDIO_L3_BITRATE: @@ -479,17 +534,22 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, return cx2341x_ctrl_query_fill(qctrl, V4L2_MPEG_STREAM_VBI_FMT_NONE, V4L2_MPEG_STREAM_VBI_FMT_NONE, 1, - V4L2_MPEG_STREAM_VBI_FMT_NONE); + default_params.stream_vbi_fmt); + + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, + params->is_50hz ? 12 : 15); /* CX23415/6 specific */ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: return cx2341x_ctrl_query_fill(qctrl, V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO, 1, - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL); + default_params.video_spatial_filter_mode); case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER: - cx2341x_ctrl_query_fill(qctrl, 0, 15, 1, 0); + cx2341x_ctrl_query_fill(qctrl, 0, 15, 1, + default_params.video_spatial_filter); qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; if (params->video_spatial_filter_mode == V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) @@ -501,7 +561,7 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF, V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE, 1, - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF); + default_params.video_luma_spatial_filter_type); if (params->video_spatial_filter_mode == V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; @@ -512,7 +572,7 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF, V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, 1, - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF); + default_params.video_chroma_spatial_filter_type); if (params->video_spatial_filter_mode == V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO) qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; @@ -522,10 +582,11 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, return cx2341x_ctrl_query_fill(qctrl, V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO, 1, - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL); + default_params.video_temporal_filter_mode); case V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER: - cx2341x_ctrl_query_fill(qctrl, 0, 31, 1, 0); + cx2341x_ctrl_query_fill(qctrl, 0, 31, 1, + default_params.video_temporal_filter); qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; if (params->video_temporal_filter_mode == V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO) @@ -536,10 +597,11 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, return cx2341x_ctrl_query_fill(qctrl, V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG, 1, - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF); + default_params.video_median_filter_type); case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 255); + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_luma_median_filter_top); qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) @@ -547,7 +609,8 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, return 0; case V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 0); + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_luma_median_filter_bottom); qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) @@ -555,7 +618,8 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, return 0; case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 255); + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_chroma_median_filter_top); qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) @@ -563,7 +627,8 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, return 0; case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: - cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, 0); + cx2341x_ctrl_query_fill(qctrl, 0, 255, 1, + default_params.video_chroma_median_filter_bottom); qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; if (params->video_median_filter_type == V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF) @@ -571,7 +636,8 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, return 0; case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: - return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, 0); + return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, + default_params.stream_insert_nav_packets); default: return v4l2_ctrl_query_fill_std(qctrl); @@ -580,9 +646,9 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, } EXPORT_SYMBOL(cx2341x_ctrl_query); -const char **cx2341x_ctrl_get_menu(u32 id) +const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) { - static const char *mpeg_stream_type[] = { + static const char *mpeg_stream_type_without_ts[] = { "MPEG-2 Program Stream", "", "MPEG-1 System Stream", @@ -592,6 +658,16 @@ const char **cx2341x_ctrl_get_menu(u32 id) NULL }; + static const char *mpeg_stream_type_with_ts[] = { + "MPEG-2 Program Stream", + "MPEG-2 Transport Stream", + "MPEG-1 System Stream", + "MPEG-2 DVD-compatible Stream", + "MPEG-1 VCD-compatible Stream", + "MPEG-2 SVCD-compatible Stream", + NULL + }; + static const char *cx2341x_video_spatial_filter_mode_menu[] = { "Manual", "Auto", @@ -630,7 +706,8 @@ const char **cx2341x_ctrl_get_menu(u32 id) switch (id) { case V4L2_CID_MPEG_STREAM_TYPE: - return mpeg_stream_type; + return (p->capabilities & CX2341X_CAP_HAS_TS) ? + mpeg_stream_type_with_ts : mpeg_stream_type_without_ts; case V4L2_CID_MPEG_AUDIO_L1_BITRATE: case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return NULL; @@ -690,7 +767,7 @@ int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy, if (err) break; if (qctrl.type == V4L2_CTRL_TYPE_MENU) - menu_items = cx2341x_ctrl_get_menu(qctrl.id); + menu_items = cx2341x_ctrl_get_menu(params, qctrl.id); err = v4l2_ctrl_check(ctrl, &qctrl, menu_items); if (err) break; @@ -714,61 +791,6 @@ EXPORT_SYMBOL(cx2341x_ext_ctrls); void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) { - static struct cx2341x_mpeg_params default_params = { - /* misc */ - .capabilities = 0, - .port = CX2341X_PORT_MEMORY, - .width = 720, - .height = 480, - .is_50hz = 0, - - /* stream */ - .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE, - .stream_insert_nav_packets = 0, - - /* audio */ - .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, - .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, - .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K, - .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO, - .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, - .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, - .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE, - .audio_mute = 0, - - /* video */ - .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2, - .video_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, - .video_b_frames = 2, - .video_gop_size = 12, - .video_gop_closure = 1, - .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - .video_bitrate = 6000000, - .video_bitrate_peak = 8000000, - .video_temporal_decimation = 0, - .video_mute = 0, - .video_mute_yuv = 0x008080, /* YCbCr value for black */ - - /* encoding filters */ - .video_spatial_filter_mode = - V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, - .video_spatial_filter = 0, - .video_luma_spatial_filter_type = - V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR, - .video_chroma_spatial_filter_type = - V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR, - .video_temporal_filter_mode = - V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL, - .video_temporal_filter = 8, - .video_median_filter_type = - V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF, - .video_luma_median_filter_top = 255, - .video_luma_median_filter_bottom = 0, - .video_chroma_median_filter_top = 255, - .video_chroma_median_filter_bottom = 0, - }; - *p = default_params; cx2341x_calc_audio_properties(p); } @@ -933,9 +955,9 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, } EXPORT_SYMBOL(cx2341x_update); -static const char *cx2341x_menu_item(struct cx2341x_mpeg_params *p, u32 id) +static const char *cx2341x_menu_item(const struct cx2341x_mpeg_params *p, u32 id) { - const char **menu = cx2341x_ctrl_get_menu(id); + const char **menu = cx2341x_ctrl_get_menu(p, id); struct v4l2_ext_control ctrl; if (menu == NULL) @@ -952,7 +974,7 @@ invalid: return "<invalid>"; } -void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix) +void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix) { int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; int temporal = p->video_temporal_filter; diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index 7bf14c9a15c..5cfb46bbdaa 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -9,11 +9,13 @@ config VIDEO_CX23885 select VIDEO_TVEEPROM select VIDEO_IR select VIDEOBUF_DVB + select VIDEOBUF_DMA_SG select VIDEO_CX25840 select VIDEO_CX2341X select DVB_DIB7000P if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2131 if !DVB_FE_CUSTOMISE select DVB_S5H1409 if !DVB_FE_CUSTOMISE + select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index acdd3b6b3e7..e7ef093265a 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -1173,379 +1173,404 @@ static int cx23885_querymenu(struct cx23885_dev *dev, qctrl.id = qmenu->id; cx23885_queryctrl(dev, &qctrl); return v4l2_ctrl_query_menu(qmenu, &qctrl, - cx2341x_ctrl_get_menu(qmenu->id)); + cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id)); } -int cx23885_do_ioctl(struct inode *inode, struct file *file, int radio, - struct cx23885_dev *dev, unsigned int cmd, void *arg, - v4l2_kioctl driver_ioctl) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) { - int err; + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + unsigned int i; - switch (cmd) { - /* ---------- tv norms ---------- */ - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *e = arg; - unsigned int i; - - i = e->index; - if (i >= ARRAY_SIZE(cx23885_tvnorms)) - return -EINVAL; - err = v4l2_video_std_construct(e, - cx23885_tvnorms[e->index].id, - cx23885_tvnorms[e->index].name); - e->index = i; - if (err < 0) - return err; - return 0; - } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; + for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++) + if (*id & cx23885_tvnorms[i].id) + break; + if (i == ARRAY_SIZE(cx23885_tvnorms)) + return -EINVAL; + dev->encodernorm = cx23885_tvnorms[i]; + return 0; +} - *id = dev->encodernorm.id; - return 0; - } - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - unsigned int i; +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + struct cx23885_input *input; + unsigned int n; - for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++) - if (*id & cx23885_tvnorms[i].id) - break; - if (i == ARRAY_SIZE(cx23885_tvnorms)) - return -EINVAL; - dev->encodernorm = cx23885_tvnorms[i]; + n = i->index; - return 0; - } + if (n >= 4) + return -EINVAL; - /* ------ input switching ---------- */ - case VIDIOC_ENUMINPUT: - { - struct cx23885_input *input; - struct v4l2_input *i = arg; - unsigned int n; - - n = i->index; - if (n >= 4) - return -EINVAL; - input = &cx23885_boards[dev->board].input[n]; - if (input->type == 0) - return -EINVAL; - memset(i, 0, sizeof(*i)); - i->index = n; - /* FIXME - * strcpy(i->name, input->name); */ - strcpy(i->name, "unset"); - if (input->type == CX23885_VMUX_TELEVISION || - input->type == CX23885_VMUX_CABLE) - i->type = V4L2_INPUT_TYPE_TUNER; - else - i->type = V4L2_INPUT_TYPE_CAMERA; + input = &cx23885_boards[dev->board].input[n]; - for (n = 0; n < ARRAY_SIZE(cx23885_tvnorms); n++) - i->std |= cx23885_tvnorms[n].id; - return 0; - } - case VIDIOC_G_INPUT: - { - unsigned int *i = arg; + if (input->type == 0) + return -EINVAL; - *i = dev->input; - return 0; - } - case VIDIOC_S_INPUT: - { - unsigned int *i = arg; + memset(i, 0, sizeof(*i)); + i->index = n; - if (*i >= 4) - return -EINVAL; + /* FIXME + * strcpy(i->name, input->name); */ + strcpy(i->name, "unset"); - return 0; - } + if (input->type == CX23885_VMUX_TELEVISION || + input->type == CX23885_VMUX_CABLE) + i->type = V4L2_INPUT_TYPE_TUNER; + else + i->type = V4L2_INPUT_TYPE_CAMERA; - /* --- tuner ioctls ------------------------------------------ */ - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; + for (n = 0; n < ARRAY_SIZE(cx23885_tvnorms); n++) + i->std |= cx23885_tvnorms[n].id; + return 0; +} - if (UNSET == dev->tuner_type) - return -EINVAL; - if (0 != t->index) - return -EINVAL; - memset(t, 0, sizeof(*t)); - strcpy(t->name, "Television"); - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_TUNER, t); - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t); +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; - dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type); + *i = dev->input; + return 0; +} - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *t = arg; +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i >= 4) + return -EINVAL; - if (UNSET == dev->tuner_type) - return -EINVAL; + return 0; +} - /* Update the A/V core */ - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_TUNER, t); +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + if (UNSET == dev->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + memset(t, 0, sizeof(*t)); + strcpy(t->name, "Television"); + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_TUNER, t); + cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t); - memset(f, 0, sizeof(*f)); - if (UNSET == dev->tuner_type) - return -EINVAL; - f->type = V4L2_TUNER_ANALOG_TV; - f->frequency = dev->freq; + dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type); - /* Assumption that tuner is always on bus 1 */ - cx23885_call_i2c_clients(&dev->i2c_bus[1], - VIDIOC_G_FREQUENCY, f); + return 0; +} - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - dprintk(1, "VIDIOC_S_FREQUENCY: dev type %d, f\n", - dev->tuner_type); - dprintk(1, "VIDIOC_S_FREQUENCY: f tuner %d, f type %d\n", - f->tuner, f->type); - if (UNSET == dev->tuner_type) - return -EINVAL; - if (f->tuner != 0) - return -EINVAL; - if (f->type != V4L2_TUNER_ANALOG_TV) - return -EINVAL; - dev->freq = f->frequency; - - /* Assumption that tuner is always on bus 1 */ - cx23885_call_i2c_clients(&dev->i2c_bus[1], - VIDIOC_S_FREQUENCY, f); - return 0; - } - case VIDIOC_S_CTRL: - { - /* Update the A/V core */ - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_CTRL, arg); - return 0; - } - default: - /* Convert V4L ioctl to V4L2 and call mpeg_do_ioctl - * (driver_ioctl) */ - return v4l_compat_translate_ioctl(inode, file, cmd, arg, - driver_ioctl); - } +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -EINVAL; + + /* Update the A/V core */ + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_TUNER, t); + + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + memset(f, 0, sizeof(*f)); + if (UNSET == dev->tuner_type) + return -EINVAL; + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = dev->freq; + + /* Assumption that tuner is always on bus 1 */ + cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_FREQUENCY, f); + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + CX23885_END_NOW, CX23885_MPEG_CAPTURE, + CX23885_RAW_BITS_NONE); + + dprintk(1, "VIDIOC_S_FREQUENCY: dev type %d, f\n", + dev->tuner_type); + dprintk(1, "VIDIOC_S_FREQUENCY: f tuner %d, f type %d\n", + f->tuner, f->type); + if (UNSET == dev->tuner_type) + return -EINVAL; + if (f->tuner != 0) + return -EINVAL; + if (f->type != V4L2_TUNER_ANALOG_TV) + return -EINVAL; + dev->freq = f->frequency; + + /* Assumption that tuner is always on bus 1 */ + cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, f); + + cx23885_initialize_codec(dev); return 0; } -static int mpeg_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + /* Update the A/V core */ + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_CTRL, ctl); + return 0; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) { struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; struct cx23885_tsport *tsport = &dev->ts1; - if (v4l_debug > 1) - v4l_print_ioctl(dev->name, cmd); + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, dev->name); + strlcpy(cap->card, cx23885_boards[tsport->dev->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cap->version = CX23885_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + 0; + if (UNSET != dev->tuner_type) + cap->capabilities |= V4L2_CAP_TUNER; - switch (cmd) { + return 0; +} - /* --- capabilities ------------------------------------------ */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap, 0, sizeof(*cap)); - strcpy(cap->driver, dev->name); - strlcpy(cap->card, cx23885_boards[tsport->dev->board].name, - sizeof(cap->card)); - sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); - cap->version = CX23885_VERSION_CODE; - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - 0; - if (UNSET != dev->tuner_type) - cap->capabilities |= V4L2_CAP_TUNER; - - return 0; - } +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int index; - /* --- capture ioctls ---------------------------------------- */ - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *f = arg; - int index; - - index = f->index; - if (index != 0) - return -EINVAL; - - memset(f, 0, sizeof(*f)); - f->index = index; - strlcpy(f->description, "MPEG", sizeof(f->description)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->pixelformat = V4L2_PIX_FMT_MPEG; - return 0; - } - case VIDIOC_G_FMT: - { - struct v4l2_format *f = arg; - - memset(f, 0, sizeof(*f)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; - f->fmt.pix.colorspace = 0; - f->fmt.pix.width = dev->ts1.width; - f->fmt.pix.height = dev->ts1.height; - f->fmt.pix.field = fh->mpegq.field; - dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", - dev->ts1.width, dev->ts1.height, fh->mpegq.field); - return 0; - } - case VIDIOC_TRY_FMT: - { - struct v4l2_format *f = arg; - - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; - f->fmt.pix.sizeimage = - f->fmt.pix.colorspace = 0; - dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", - dev->ts1.width, dev->ts1.height, fh->mpegq.field); - return 0; - } - case VIDIOC_S_FMT: - { - struct v4l2_format *f = arg; - - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = - dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; - f->fmt.pix.colorspace = 0; - dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", - f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); - return 0; - } + index = f->index; + if (index != 0) + return -EINVAL; - /* --- streaming capture ------------------------------------- */ - case VIDIOC_REQBUFS: - return videobuf_reqbufs(&fh->mpegq, arg); + memset(f, 0, sizeof(*f)); + f->index = index; + strlcpy(f->description, "MPEG", sizeof(f->description)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->pixelformat = V4L2_PIX_FMT_MPEG; - case VIDIOC_QUERYBUF: - return videobuf_querybuf(&fh->mpegq, arg); + return 0; +} - case VIDIOC_QBUF: - return videobuf_qbuf(&fh->mpegq, arg); +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; - case VIDIOC_DQBUF: - return videobuf_dqbuf(&fh->mpegq, arg, - file->f_flags & O_NONBLOCK); + memset(f, 0, sizeof(*f)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; + f->fmt.pix.colorspace = 0; + f->fmt.pix.width = dev->ts1.width; + f->fmt.pix.height = dev->ts1.height; + f->fmt.pix.field = fh->mpegq.field; + dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", + dev->ts1.width, dev->ts1.height, fh->mpegq.field); + return 0; +} - case VIDIOC_STREAMON: - return videobuf_streamon(&fh->mpegq); +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; - case VIDIOC_STREAMOFF: - return videobuf_streamoff(&fh->mpegq); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; + f->fmt.pix.sizeimage = + f->fmt.pix.colorspace = 0; + dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", + dev->ts1.width, dev->ts1.height, fh->mpegq.field); + return 0; +} - case VIDIOC_G_EXT_CTRLS: - { - struct v4l2_ext_controls *f = arg; +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, cmd); - } - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *f = arg; - struct cx2341x_mpeg_params p; - int err; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - p = dev->mpeg_params; - err = cx2341x_ext_ctrls(&p, 0, f, cmd); - if (err == 0 && cmd == VIDIOC_S_EXT_CTRLS) { - err = cx2341x_update(dev, cx23885_mbox_func, - &dev->mpeg_params, &p); - dev->mpeg_params = p; - } - return err; - } - case VIDIOC_S_FREQUENCY: - { - cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, - CX23885_END_NOW, CX23885_MPEG_CAPTURE, - CX23885_RAW_BITS_NONE); - cx23885_do_ioctl(inode, file, 0, dev, cmd, arg, - mpeg_do_ioctl); - cx23885_initialize_codec(dev); - - return 0; - } - case VIDIOC_LOG_STATUS: - { - char name[32 + 2]; - - snprintf(name, sizeof(name), "%s/2", dev->name); - printk(KERN_INFO - "%s/2: ============ START LOG STATUS ============\n", - dev->name); - cx23885_call_i2c_clients(&dev->i2c_bus[0], VIDIOC_LOG_STATUS, - NULL); - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_LOG_STATUS, - NULL); - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_LOG_STATUS, - NULL); - cx2341x_log_status(&dev->mpeg_params, name); - printk(KERN_INFO - "%s/2: ============= END LOG STATUS =============\n", - dev->name); - return 0; - } - case VIDIOC_QUERYMENU: - return cx23885_querymenu(dev, arg); - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *c = arg; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; + f->fmt.pix.colorspace = 0; + dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); + return 0; +} - return cx23885_queryctrl(dev, c); - } +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct cx23885_fh *fh = file->private_data; - default: - return cx23885_do_ioctl(inode, file, 0, dev, cmd, arg, - mpeg_do_ioctl); + return videobuf_reqbufs(&fh->mpegq, p); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct cx23885_fh *fh = file->private_data; + + return videobuf_querybuf(&fh->mpegq, p); +} + +static int vidioc_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct cx23885_fh *fh = file->private_data; + + return videobuf_qbuf(&fh->mpegq, p); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx23885_fh *fh = priv; + + return videobuf_dqbuf(&fh->mpegq, b, file->f_flags & O_NONBLOCK); +} + + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct cx23885_fh *fh = file->private_data; + + return videobuf_streamon(&fh->mpegq); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx23885_fh *fh = file->private_data; + + return videobuf_streamoff(&fh->mpegq); +} + +static int vidioc_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx23885_fh *fh = priv; + struct cx23885_dev *dev = fh->dev; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS); +} + +static int vidioc_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx23885_fh *fh = priv; + struct cx23885_dev *dev = fh->dev; + struct cx2341x_mpeg_params p; + int err; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + + p = dev->mpeg_params; + err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS); + + if (err == 0) { + err = cx2341x_update(dev, cx23885_mbox_func, + &dev->mpeg_params, &p); + dev->mpeg_params = p; } + return err; +} + +static int vidioc_try_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx23885_fh *fh = priv; + struct cx23885_dev *dev = fh->dev; + struct cx2341x_mpeg_params p; + int err; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + + p = dev->mpeg_params; + err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS); + return err; +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx23885_fh *fh = priv; + struct cx23885_dev *dev = fh->dev; + char name[32 + 2]; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO + "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx23885_call_i2c_clients(&dev->i2c_bus[0], VIDIOC_LOG_STATUS, + NULL); + cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_LOG_STATUS, + NULL); + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_LOG_STATUS, + NULL); + cx2341x_log_status(&dev->mpeg_params, name); + printk(KERN_INFO + "%s/2: ============= END LOG STATUS =============\n", + dev->name); return 0; } -static int mpeg_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *a) +{ + struct cx23885_fh *fh = priv; + struct cx23885_dev *dev = fh->dev; + + return cx23885_querymenu(dev, a); +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) { - return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl); + struct cx23885_fh *fh = priv; + struct cx23885_dev *dev = fh->dev; + + return cx23885_queryctrl(dev, c); } static int mpeg_open(struct inode *inode, struct file *file) @@ -1670,7 +1695,7 @@ static struct file_operations mpeg_fops = { .read = mpeg_read, .poll = mpeg_poll, .mmap = mpeg_mmap, - .ioctl = mpeg_ioctl, + .ioctl = video_ioctl2, .llseek = no_llseek, }; @@ -1682,6 +1707,32 @@ static struct video_device cx23885_mpeg_template = { VID_TYPE_MPEG_ENCODER, .fops = &mpeg_fops, .minor = -1, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .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, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, + .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, + .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, + .vidioc_log_status = vidioc_log_status, + .vidioc_querymenu = vidioc_querymenu, + .vidioc_queryctrl = vidioc_queryctrl, }; void cx23885_417_unregister(struct cx23885_dev *dev) diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 20e05f23054..fd7112c11d3 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -143,6 +143,10 @@ struct cx23885_board cx23885_boards[] = { .name = "Hauppauge WinTV-HVR1400", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP] = { + .name = "DViCO FusionHDTV7 Dual Express", + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -210,6 +214,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0070, .subdevice = 0x8010, .card = CX23885_BOARD_HAUPPAUGE_HVR1400, + },{ + .subvendor = 0x18ac, + .subdevice = 0xd618, + .card = CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -477,6 +485,11 @@ void cx23885_card_setup(struct cx23885_dev *dev) } switch (dev->board) { + case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + /* break omitted intentionally */ case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP: ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index c4cc2f3b887..d17343ea0d3 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -291,9 +291,9 @@ int cx23885_sram_channel_setup(struct cx23885_dev *dev, lines = 6; BUG_ON(lines < 2); - cx_write(8 + 0, cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC) ); - cx_write(8 + 4, cpu_to_le32(8) ); - cx_write(8 + 8, cpu_to_le32(0) ); + cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + cx_write(8 + 4, 8); + cx_write(8 + 8, 0); /* write CDT */ for (i = 0; i < lines; i++) { @@ -408,11 +408,11 @@ static void cx23885_risc_disasm(struct cx23885_tsport *port, dev->name, risc->cpu, (unsigned long)risc->dma); for (i = 0; i < (risc->size >> 2); i += n) { printk("%s: %04d: ", dev->name, i); - n = cx23885_risc_decode(risc->cpu[i]); + n = cx23885_risc_decode(le32_to_cpu(risc->cpu[i])); for (j = 1; j < n; j++) printk("%s: %04d: 0x%08x [ arg #%d ]\n", dev->name, i + j, risc->cpu[i + j], j); - if (risc->cpu[i] == RISC_JUMP) + if (risc->cpu[i] == cpu_to_le32(RISC_JUMP)) break; } } diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 022aa391937..0a2e6558cd6 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -31,6 +31,7 @@ #include <media/v4l2-common.h> #include "s5h1409.h" +#include "s5h1411.h" #include "mt2131.h" #include "tda8290.h" #include "tda18271.h" @@ -164,12 +165,38 @@ static struct s5h1409_config hauppauge_hvr1500q_config = { .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, }; +static struct s5h1409_config dvico_s5h1409_config = { + .demod_address = 0x32 >> 1, + .output_mode = S5H1409_SERIAL_OUTPUT, + .gpio = S5H1409_GPIO_ON, + .qam_if = 44000, + .inversion = S5H1409_INVERSION_OFF, + .status_mode = S5H1409_DEMODLOCKING, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; + +static struct s5h1411_config dvico_s5h1411_config = { + .output_mode = S5H1411_SERIAL_OUTPUT, + .gpio = S5H1411_GPIO_ON, + .qam_if = S5H1411_IF_44000, + .vsb_if = S5H1411_IF_44000, + .inversion = S5H1411_INVERSION_OFF, + .status_mode = S5H1411_DEMODLOCKING, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; + static struct xc5000_config hauppauge_hvr1500q_tunerconfig = { .i2c_address = 0x61, .if_khz = 5380, .tuner_callback = cx23885_tuner_callback }; +static struct xc5000_config dvico_xc5000_tunerconfig = { + .i2c_address = 0x64, + .if_khz = 5380, + .tuner_callback = cx23885_tuner_callback +}; + static struct tda829x_config tda829x_no_probe = { .probe_tuner = TDA829X_DONT_PROBE, }; @@ -453,6 +480,21 @@ static int dvb_register(struct cx23885_tsport *port) fe->ops.tuner_ops.set_config(fe, &ctl); } break; + case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: + i2c_bus = &dev->i2c_bus[port->nr - 1]; + + port->dvb.frontend = dvb_attach(s5h1409_attach, + &dvico_s5h1409_config, + &i2c_bus->i2c_adap); + if (port->dvb.frontend == NULL) + port->dvb.frontend = dvb_attach(s5h1411_attach, + &dvico_s5h1411_config, + &i2c_bus->i2c_adap); + if (port->dvb.frontend != NULL) + dvb_attach(xc5000_attach, port->dvb.frontend, + &i2c_bus->i2c_adap, + &dvico_xc5000_tunerconfig, i2c_bus); + break; default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->name); diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 84652210a28..043fc4e5c58 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -915,7 +915,7 @@ static void init_controls(struct cx23885_dev *dev) /* ------------------------------------------------------------------ */ /* VIDEO IOCTLS */ -static int vidioc_g_fmt_cap(struct file *file, void *priv, +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cx23885_fh *fh = priv; @@ -932,7 +932,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv, return 0; } -static int vidioc_try_fmt_cap(struct file *file, void *priv, +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; @@ -983,7 +983,7 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, return 0; } -static int vidioc_s_fmt_cap(struct file *file, void *priv, +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cx23885_fh *fh = priv; @@ -991,7 +991,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, int err; dprintk(2, "%s()\n", __func__); - err = vidioc_try_fmt_cap(file, priv, f); + err = vidioc_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -1025,7 +1025,7 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static int vidioc_enum_fmt_cap(struct file *file, void *priv, +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (unlikely(f->index >= ARRAY_SIZE(formats))) @@ -1440,13 +1440,13 @@ static struct video_device cx23885_video_template = { .fops = &video_fops, .minor = -1, .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, - .vidioc_g_fmt_cap = vidioc_g_fmt_cap, - .vidioc_try_fmt_cap = vidioc_try_fmt_cap, - .vidioc_s_fmt_cap = vidioc_s_fmt_cap, - .vidioc_g_fmt_vbi = cx23885_vbi_fmt, - .vidioc_try_fmt_vbi = cx23885_vbi_fmt, - .vidioc_s_fmt_vbi = cx23885_vbi_fmt, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .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, + .vidioc_g_fmt_vbi_cap = cx23885_vbi_fmt, + .vidioc_try_fmt_vbi_cap = cx23885_vbi_fmt, + .vidioc_s_fmt_vbi_cap = cx23885_vbi_fmt, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 32af87f25e7..00dfdc89d64 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -63,6 +63,7 @@ #define CX23885_BOARD_HAUPPAUGE_HVR1200 7 #define CX23885_BOARD_HAUPPAUGE_HVR1700 8 #define CX23885_BOARD_HAUPPAUGE_HVR1400 9 +#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 1da6f134888..e7bf4f4c131 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -50,7 +50,6 @@ MODULE_LICENSE("GPL"); static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; - int cx25840_debug; module_param_named(debug,cx25840_debug, int, 0644); @@ -238,7 +237,7 @@ static void cx25840_initialize(struct i2c_client *client) cx25840_write(client, 0x8d3, 0x1f); cx25840_write(client, 0x8e3, 0x03); - cx25840_vbi_setup(client); + cx25840_std_setup(client); /* trial and error says these are needed to get audio */ cx25840_write(client, 0x914, 0xa0); @@ -338,7 +337,7 @@ static void cx23885_initialize(struct i2c_client *client) finish_wait(&state->fw_wait, &wait); destroy_workqueue(q); - cx25840_vbi_setup(client); + cx25840_std_setup(client); /* (re)set input */ set_input(client, state->vid_input, state->aud_input); @@ -349,6 +348,153 @@ static void cx23885_initialize(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +void cx25840_std_setup(struct i2c_client *client) +{ + struct cx25840_state *state = i2c_get_clientdata(client); + v4l2_std_id std = state->std; + int hblank, hactive, burst, vblank, vactive, sc; + int vblank656, src_decimation; + int luma_lpf, uv_lpf, comb; + u32 pll_int, pll_frac, pll_post; + + /* datasheet startup, step 8d */ + if (std & ~V4L2_STD_NTSC) + cx25840_write(client, 0x49f, 0x11); + else + cx25840_write(client, 0x49f, 0x14); + + if (std & V4L2_STD_625_50) { + hblank = 132; + hactive = 720; + burst = 93; + vblank = 36; + vactive = 580; + vblank656 = 40; + src_decimation = 0x21f; + luma_lpf = 2; + + if (std & V4L2_STD_SECAM) { + uv_lpf = 0; + comb = 0; + sc = 0x0a425f; + } else if (std == V4L2_STD_PAL_Nc) { + uv_lpf = 1; + comb = 0x20; + sc = 556453; + } else { + uv_lpf = 1; + comb = 0x20; + sc = 688739; + } + } else { + hactive = 720; + hblank = 122; + vactive = 487; + luma_lpf = 1; + uv_lpf = 1; + + src_decimation = 0x21f; + if (std == V4L2_STD_PAL_60) { + vblank = 26; + vblank656 = 26; + burst = 0x5b; + luma_lpf = 2; + comb = 0x20; + sc = 688739; + } else if (std == V4L2_STD_PAL_M) { + vblank = 20; + vblank656 = 24; + burst = 0x61; + comb = 0x20; + sc = 555452; + } else { + vblank = 26; + vblank656 = 26; + burst = 0x5b; + comb = 0x66; + sc = 556063; + } + } + + /* DEBUG: Displays configured PLL frequency */ + pll_int = cx25840_read(client, 0x108); + pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; + pll_post = cx25840_read(client, 0x109); + v4l_dbg(1, cx25840_debug, client, + "PLL regs = int: %u, frac: %u, post: %u\n", + pll_int, pll_frac, pll_post); + + if (pll_post) { + int fin, fsc; + int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L; + + pll /= pll_post; + v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", + pll / 1000000, pll % 1000000); + v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", + pll / 8000000, (pll / 8) % 1000000); + + fin = ((u64)src_decimation * pll) >> 12; + v4l_dbg(1, cx25840_debug, client, + "ADC Sampling freq = %d.%06d MHz\n", + fin / 1000000, fin % 1000000); + + fsc = (((u64)sc) * pll) >> 24L; + v4l_dbg(1, cx25840_debug, client, + "Chroma sub-carrier freq = %d.%06d MHz\n", + fsc / 1000000, fsc % 1000000); + + v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, " + "vblank %i, vactive %i, vblank656 %i, src_dec %i, " + "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, " + "sc 0x%06x\n", + hblank, hactive, vblank, vactive, vblank656, + src_decimation, burst, luma_lpf, uv_lpf, comb, sc); + } + + /* Sets horizontal blanking delay and active lines */ + cx25840_write(client, 0x470, hblank); + cx25840_write(client, 0x471, + 0xff & (((hblank >> 8) & 0x3) | (hactive << 4))); + cx25840_write(client, 0x472, hactive >> 4); + + /* Sets burst gate delay */ + cx25840_write(client, 0x473, burst); + + /* Sets vertical blanking delay and active duration */ + cx25840_write(client, 0x474, vblank); + cx25840_write(client, 0x475, + 0xff & (((vblank >> 8) & 0x3) | (vactive << 4))); + cx25840_write(client, 0x476, vactive >> 4); + cx25840_write(client, 0x477, vblank656); + + /* Sets src decimation rate */ + cx25840_write(client, 0x478, 0xff & src_decimation); + cx25840_write(client, 0x479, 0xff & (src_decimation >> 8)); + + /* Sets Luma and UV Low pass filters */ + cx25840_write(client, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); + + /* Enables comb filters */ + cx25840_write(client, 0x47b, comb); + + /* Sets SC Step*/ + cx25840_write(client, 0x47c, sc); + cx25840_write(client, 0x47d, 0xff & sc >> 8); + cx25840_write(client, 0x47e, 0xff & sc >> 16); + + /* Sets VBI parameters */ + if (std & V4L2_STD_625_50) { + cx25840_write(client, 0x47f, 0x01); + state->vbi_line_offset = 5; + } else { + cx25840_write(client, 0x47f, 0x00); + state->vbi_line_offset = 8; + } +} + +/* ----------------------------------------------------------------------- */ + static void input_change(struct i2c_client *client) { struct cx25840_state *state = i2c_get_clientdata(client); @@ -566,7 +712,7 @@ static int set_v4lstd(struct i2c_client *client) } cx25840_and_or(client, 0x400, ~0xf, fmt); cx25840_and_or(client, 0x403, ~0x3, pal_m); - cx25840_vbi_setup(client); + cx25840_std_setup(client); if (!state->is_cx25836) input_change(client); return 0; @@ -1058,6 +1204,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, switch (qc->id) { case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, + 65535 / 100, state->default_volume); case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: @@ -1265,6 +1413,8 @@ static int cx25840_probe(struct i2c_client *client, state->pvr150_workaround = 0; state->audmode = V4L2_TUNER_MODE_LANG1; state->unmute_volume = -1; + state->default_volume = 228 - cx25840_read(client, 0x8d4); + state->default_volume = ((state->default_volume / 2) + 23) << 9; state->vbi_line_offset = 8; state->id = id; state->rev = device_id; diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 8bf797f48b0..72916ba975a 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -44,6 +44,7 @@ struct cx25840_state { u32 audclk_freq; int audmode; int unmute_volume; /* -1 if not muted */ + int default_volume; int vbi_line_offset; u32 id; u32 rev; @@ -61,6 +62,7 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value); u8 cx25840_read(struct i2c_client *client, u16 addr); u32 cx25840_read4(struct i2c_client *client, u16 addr); int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value); +void cx25840_std_setup(struct i2c_client *client); /* ----------------------------------------------------------------------- */ /* cx25850-firmware.c */ @@ -73,7 +75,6 @@ void cx25840_audio_set_path(struct i2c_client *client); /* ----------------------------------------------------------------------- */ /* cx25850-vbi.c */ -void cx25840_vbi_setup(struct i2c_client *client); int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg); #endif diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c index c754b9d1336..69f2bbdbb92 100644 --- a/drivers/media/video/cx25840/cx25840-vbi.c +++ b/drivers/media/video/cx25840/cx25840-vbi.c @@ -82,150 +82,6 @@ static int decode_vps(u8 * dst, u8 * p) return err & 0xf0; } -void cx25840_vbi_setup(struct i2c_client *client) -{ - struct cx25840_state *state = i2c_get_clientdata(client); - v4l2_std_id std = state->std; - int hblank,hactive,burst,vblank,vactive,sc,vblank656,src_decimation; - int luma_lpf,uv_lpf, comb; - u32 pll_int,pll_frac,pll_post; - - /* datasheet startup, step 8d */ - if (std & ~V4L2_STD_NTSC) { - cx25840_write(client, 0x49f, 0x11); - } else { - cx25840_write(client, 0x49f, 0x14); - } - - if (std & V4L2_STD_625_50) { - hblank=0x084; - hactive=0x2d0; - burst=0x5d; - vblank=0x024; - vactive=0x244; - vblank656=0x28; - src_decimation=0x21f; - - luma_lpf=2; - if (std & V4L2_STD_SECAM) { - uv_lpf=0; - comb=0; - sc=0x0a425f; - } else if (std == V4L2_STD_PAL_Nc) { - uv_lpf=1; - comb=0x20; - sc=556453; - } else { - uv_lpf=1; - comb=0x20; - sc=0x0a8263; - } - } else { - hactive=720; - hblank=122; - vactive=487; - luma_lpf=1; - uv_lpf=1; - - src_decimation=0x21f; - if (std == V4L2_STD_PAL_60) { - vblank=26; - vblank656=26; - burst=0x5b; - luma_lpf=2; - comb=0x20; - sc=0x0a8263; - } else if (std == V4L2_STD_PAL_M) { - vblank=20; - vblank656=24; - burst=0x61; - comb=0x20; - - sc=555452; - } else { - vblank=26; - vblank656=26; - burst=0x5b; - comb=0x66; - sc=556063; - } - } - - /* DEBUG: Displays configured PLL frequency */ - pll_int=cx25840_read(client, 0x108); - pll_frac=cx25840_read4(client, 0x10c)&0x1ffffff; - pll_post=cx25840_read(client, 0x109); - v4l_dbg(1, cx25840_debug, client, - "PLL regs = int: %u, frac: %u, post: %u\n", - pll_int,pll_frac,pll_post); - - if (pll_post) { - int fin, fsc; - int pll= (28636363L*((((u64)pll_int)<<25L)+pll_frac)) >>25L; - - pll/=pll_post; - v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", - pll/1000000, pll%1000000); - v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", - pll/8000000, (pll/8)%1000000); - - fin=((u64)src_decimation*pll)>>12; - v4l_dbg(1, cx25840_debug, client, "ADC Sampling freq = " - "%d.%06d MHz\n", - fin/1000000,fin%1000000); - - fsc= (((u64)sc)*pll) >> 24L; - v4l_dbg(1, cx25840_debug, client, "Chroma sub-carrier freq = " - "%d.%06d MHz\n", - fsc/1000000,fsc%1000000); - - v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, " - "vblank %i, vactive %i, vblank656 %i, src_dec %i, " - "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x," - " sc 0x%06x\n", - hblank, hactive, vblank, vactive, vblank656, - src_decimation, burst, luma_lpf, uv_lpf, comb, sc); - } - - /* Sets horizontal blanking delay and active lines */ - cx25840_write(client, 0x470, hblank); - cx25840_write(client, 0x471, 0xff&(((hblank>>8)&0x3)|(hactive <<4))); - cx25840_write(client, 0x472, hactive>>4); - - /* Sets burst gate delay */ - cx25840_write(client, 0x473, burst); - - /* Sets vertical blanking delay and active duration */ - cx25840_write(client, 0x474, vblank); - cx25840_write(client, 0x475, 0xff&(((vblank>>8)&0x3)|(vactive <<4))); - cx25840_write(client, 0x476, vactive>>4); - cx25840_write(client, 0x477, vblank656); - - /* Sets src decimation rate */ - cx25840_write(client, 0x478, 0xff&src_decimation); - cx25840_write(client, 0x479, 0xff&(src_decimation>>8)); - - /* Sets Luma and UV Low pass filters */ - cx25840_write(client, 0x47a, luma_lpf<<6|((uv_lpf<<4)&0x30)); - - /* Enables comb filters */ - cx25840_write(client, 0x47b, comb); - - /* Sets SC Step*/ - cx25840_write(client, 0x47c, sc); - cx25840_write(client, 0x47d, 0xff&sc>>8); - cx25840_write(client, 0x47e, 0xff&sc>>16); - - /* Sets VBI parameters */ - if (std & V4L2_STD_625_50) { - cx25840_write(client, 0x47f, 0x01); - state->vbi_line_offset = 5; - } else { - cx25840_write(client, 0x47f, 0x00); - state->vbi_line_offset = 8; - } -} - int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) { struct cx25840_state *state = i2c_get_clientdata(client); @@ -292,8 +148,8 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) /* raw VBI */ memset(svbi, 0, sizeof(*svbi)); - /* Setup VBI */ - cx25840_vbi_setup(client); + /* Setup standard */ + cx25840_std_setup(client); /* VBI Offset */ cx25840_write(client, 0x47f, vbi_offset); @@ -304,8 +160,8 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) for (x = 0; x <= 23; x++) lcr[x] = 0x00; - /* Setup VBI */ - cx25840_vbi_setup(client); + /* Setup standard */ + cx25840_std_setup(client); /* Sliced VBI */ cx25840_write(client, 0x404, 0x32); /* Ancillary data */ diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 80c8883e54b..06f171ab614 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -82,7 +82,6 @@ typedef struct cx88_audio_dev snd_cx88_card_t; - /**************************************************************************** Module global static vars ****************************************************************************/ diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 6c0c94c5ef9..bfdca584776 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -715,7 +715,8 @@ static int vidioc_querymenu (struct file *file, void *priv, qctrl.id = qmenu->id; blackbird_queryctrl(dev, &qctrl); - return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id)); + return v4l2_ctrl_query_menu(qmenu, &qctrl, + cx2341x_ctrl_get_menu(&dev->params, qmenu->id)); } static int vidioc_querycap (struct file *file, void *priv, @@ -737,7 +738,7 @@ static int vidioc_querycap (struct file *file, void *priv, return 0; } -static int vidioc_enum_fmt_cap (struct file *file, void *priv, +static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (f->index != 0) @@ -749,7 +750,7 @@ static int vidioc_enum_fmt_cap (struct file *file, void *priv, return 0; } -static int vidioc_g_fmt_cap (struct file *file, void *priv, +static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *f) { struct cx8802_fh *fh = priv; @@ -768,7 +769,7 @@ static int vidioc_g_fmt_cap (struct file *file, void *priv, return 0; } -static int vidioc_try_fmt_cap (struct file *file, void *priv, +static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *f) { struct cx8802_fh *fh = priv; @@ -784,7 +785,7 @@ static int vidioc_try_fmt_cap (struct file *file, void *priv, return 0; } -static int vidioc_s_fmt_cap (struct file *file, void *priv, +static int vidioc_s_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *f) { struct cx8802_fh *fh = priv; @@ -1181,10 +1182,10 @@ static struct video_device cx8802_mpeg_template = .minor = -1, .vidioc_querymenu = vidioc_querymenu, .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, - .vidioc_g_fmt_cap = vidioc_g_fmt_cap, - .vidioc_try_fmt_cap = vidioc_try_fmt_cap, - .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .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, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index cb6a096069c..d7406a994f0 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -103,7 +103,6 @@ static int attach_inform(struct i2c_client *client) dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", client->driver->driver.name, client->addr, client->name); - return 0; } diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index eea23f95edb..0fed5cd2cce 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1045,7 +1045,7 @@ static void init_controls(struct cx88_core *core) /* ------------------------------------------------------------------ */ /* VIDEO IOCTLS */ -static int vidioc_g_fmt_cap (struct file *file, void *priv, +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cx8800_fh *fh = priv; @@ -1061,7 +1061,7 @@ static int vidioc_g_fmt_cap (struct file *file, void *priv, return 0; } -static int vidioc_try_fmt_cap (struct file *file, void *priv, +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; @@ -1112,11 +1112,11 @@ static int vidioc_try_fmt_cap (struct file *file, void *priv, return 0; } -static int vidioc_s_fmt_cap (struct file *file, void *priv, +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct cx8800_fh *fh = priv; - int err = vidioc_try_fmt_cap (file,priv,f); + int err = vidioc_try_fmt_vid_cap (file,priv,f); if (0 != err) return err; @@ -1147,7 +1147,7 @@ static int vidioc_querycap (struct file *file, void *priv, return 0; } -static int vidioc_enum_fmt_cap (struct file *file, void *priv, +static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (unlikely(f->index >= ARRAY_SIZE(formats))) @@ -1690,13 +1690,13 @@ static struct video_device cx8800_video_template = .fops = &video_fops, .minor = -1, .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, - .vidioc_g_fmt_cap = vidioc_g_fmt_cap, - .vidioc_try_fmt_cap = vidioc_try_fmt_cap, - .vidioc_s_fmt_cap = vidioc_s_fmt_cap, - .vidioc_g_fmt_vbi = cx8800_vbi_fmt, - .vidioc_try_fmt_vbi = cx8800_vbi_fmt, - .vidioc_s_fmt_vbi = cx8800_vbi_fmt, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .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, + .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt, + .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt, + .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.c b/drivers/media/video/cx88/cx88-vp3054-i2c.c index 6ce5af48847..20800425c51 100644 --- a/drivers/media/video/cx88/cx88-vp3054-i2c.c +++ b/drivers/media/video/cx88/cx88-vp3054-i2c.c @@ -30,7 +30,6 @@ #include "cx88.h" #include "cx88-vp3054-i2c.h" - MODULE_DESCRIPTION("driver for cx2388x VP3054 design"); MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 8cbda43727c..05f0d5a1505 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -173,6 +173,27 @@ struct em28xx_board em28xx_boards[] = { .amux = 1, } }, }, + [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = { + .name = "Hauppauge WinTV HVR 900 (R2)", + .vchannels = 3, + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_XC2028, + .mts_firmware = 1, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950] = { .name = "Hauppauge WinTV HVR 950", .vchannels = 3, @@ -196,6 +217,29 @@ struct em28xx_board em28xx_boards[] = { .amux = 1, } }, }, + [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = { + .name = "Pinnacle PCTV HD Pro Stick", + .vchannels = 3, + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_XC2028, + .mts_firmware = 1, + .has_12mhz_i2s = 1, + .has_dvb = 1, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = 0, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = 1, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = 1, + } }, + }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS", .vchannels = 3, @@ -382,6 +426,19 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA] = { + .name = "PointNix Intra-Oral Camera", + .has_snapshot_button = 1, + .vchannels = 1, + .tda9887_conf = TDA9887_PRESENT, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA7113, + .input = { { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = 0, + } }, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -417,10 +474,12 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, { USB_DEVICE(0x2304, 0x021a), .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, + { USB_DEVICE(0x2304, 0x0227), + .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO }, { USB_DEVICE(0x2040, 0x6500), .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 }, { USB_DEVICE(0x2040, 0x6502), - .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 }, + .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 }, { USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 }, { USB_DEVICE(0x2040, 0x6517), /* HP HVR-950 */ @@ -476,6 +535,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash [] = { static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC}, {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, + {0x1ba50080, EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA, TUNER_ABSENT}, }; int em28xx_tuner_callback(void *ptr, int command, int arg) @@ -508,6 +568,7 @@ static void em28xx_set_model(struct em28xx *dev) dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s; dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; dev->has_dvb = em28xx_boards[dev->model].has_dvb; + dev->has_snapshot_button = em28xx_boards[dev->model].has_snapshot_button; } /* Since em28xx_pre_card_setup() requires a proper dev->model, @@ -542,8 +603,10 @@ void em28xx_pre_card_setup(struct em28xx *dev) switch (dev->model) { case EM2880_BOARD_TERRATEC_PRODIGY_XS: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: case EM2880_BOARD_TERRATEC_HYBRID_XS: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); msleep(50); @@ -576,7 +639,12 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: ctl->demod = XC3028_FE_ZARLINK456; break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: + /* djh - Not sure which demod we need here */ + ctl->demod = XC3028_FE_DEFAULT; + break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: /* FIXME: Better to specify the needed IF */ ctl->demod = XC3028_FE_DEFAULT; break; @@ -754,6 +822,7 @@ void em28xx_card_setup(struct em28xx *dev) switch (dev->model) { case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: { struct tveeprom tv; @@ -787,6 +856,9 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_set_model(dev); } + if (dev->has_snapshot_button) + em28xx_register_snapshot_button(dev); + /* Allow override tuner type by a module parameter */ if (tuner >= 0) dev->tuner_type = tuner; diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 0b2333ee07f..cc61cfb23a4 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -5,6 +5,7 @@ (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com> - Fixes for the driver to properly work with HVR-950 + - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick (c) 2008 Aidan Thornton <makosoft@googlemail.com> @@ -26,6 +27,9 @@ #include "lgdt330x.h" #include "zl10353.h" +#ifdef EM28XX_DRX397XD_SUPPORT +#include "drx397xD.h" +#endif MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -227,6 +231,13 @@ static struct zl10353_config em28xx_zl10353_with_xc3028 = { .if2 = 45600, }; +#ifdef EM28XX_DRX397XD_SUPPORT +/* [TODO] djh - not sure yet what the device config needs to contain */ +static struct drx397xD_config em28xx_drx397xD_with_xc3028 = { + .demod_address = (0xe0 >> 1), +}; +#endif + /* ------------------------------------------------------------------ */ static int attach_xc3028(u8 addr, struct em28xx *dev) @@ -399,6 +410,7 @@ static int dvb_init(struct em28xx *dev) /* init frontend */ switch (dev->model) { case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + case EM2880_BOARD_PINNACLE_PCTV_HD_PRO: dvb->frontend = dvb_attach(lgdt330x_attach, &em2880_lgdt3303_dev, &dev->i2c_adap); @@ -416,6 +428,19 @@ static int dvb_init(struct em28xx *dev) goto out_free; } break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: +#ifdef EM28XX_DRX397XD_SUPPORT + /* We don't have the config structure properly populated, so + this is commented out for now */ + dvb->frontend = dvb_attach(drx397xD_attach, + &em28xx_drx397xD_with_xc3028, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; +#endif default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" " isn't supported yet\n", diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index 6a78fd294ca..97853384c94 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -432,7 +432,6 @@ static u32 functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL; } - /* * attach_inform() * gets called when a device attaches to the i2c bus diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index bb5807159b8..eab3d9511af 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -30,6 +30,10 @@ #include "em28xx.h" +#define EM28XX_SNAPSHOT_KEY KEY_CAMERA +#define EM28XX_SBUTTON_QUERY_INTERVAL 500 +#define EM28XX_R0C_USBSUSP_SNAPSHOT 0x20 + static unsigned int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); @@ -124,6 +128,89 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, return 1; } +static void em28xx_query_sbutton(struct work_struct *work) +{ + /* Poll the register and see if the button is depressed */ + struct em28xx *dev = + container_of(work, struct em28xx, sbutton_query_work.work); + int ret; + + ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); + + if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { + u8 cleared; + /* Button is depressed, clear the register */ + cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; + em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); + + /* Not emulate the keypress */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 1); + /* Now unpress the key */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 0); + } + + /* Schedule next poll */ + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); +} + +void em28xx_register_snapshot_button(struct em28xx *dev) +{ + struct input_dev *input_dev; + int err; + + em28xx_info("Registering snapshot button...\n"); + input_dev = input_allocate_device(); + if (!input_dev) { + em28xx_errdev("input_allocate_device failed\n"); + return; + } + + usb_make_path(dev->udev, dev->snapshot_button_path, + sizeof(dev->snapshot_button_path)); + strlcat(dev->snapshot_button_path, "/sbutton", + sizeof(dev->snapshot_button_path)); + INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); + + input_dev->name = "em28xx snapshot button"; + input_dev->phys = dev->snapshot_button_path; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); + input_dev->keycodesize = 0; + input_dev->keycodemax = 0; + input_dev->id.bustype = BUS_USB; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + input_dev->id.version = 1; + input_dev->dev.parent = &dev->udev->dev; + + err = input_register_device(input_dev); + if (err) { + em28xx_errdev("input_register_device failed\n"); + input_free_device(input_dev); + return; + } + + dev->sbutton_input_dev = input_dev; + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); + return; + +} + +void em28xx_deregister_snapshot_button(struct em28xx *dev) +{ + if (dev->sbutton_input_dev != NULL) { + em28xx_info("Deregistering snapshot button\n"); + cancel_rearming_delayed_work(&dev->sbutton_query_work); + input_unregister_device(dev->sbutton_input_dev); + dev->sbutton_input_dev = NULL; + } + return; +} + /* ---------------------------------------------------------------------- * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 285bc62bbe4..2d9f14d2a00 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -683,7 +683,7 @@ static void get_scale(struct em28xx *dev, IOCTL vidioc handling ------------------------------------------------------------------*/ -static int vidioc_g_fmt_cap(struct file *file, void *priv, +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct em28xx_fh *fh = priv; @@ -706,7 +706,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv, return 0; } -static int vidioc_try_fmt_cap(struct file *file, void *priv, +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct em28xx_fh *fh = priv; @@ -766,7 +766,7 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, return 0; } -static int vidioc_s_fmt_cap(struct file *file, void *priv, +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct em28xx_fh *fh = priv; @@ -777,7 +777,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, if (rc < 0) return rc; - vidioc_try_fmt_cap(file, priv, f); + vidioc_try_fmt_vid_cap(file, priv, f); mutex_lock(&dev->lock); @@ -826,7 +826,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) /* Adjusts width/height, if needed */ f.fmt.pix.width = dev->width; f.fmt.pix.height = dev->height; - vidioc_try_fmt_cap(file, priv, &f); + vidioc_try_fmt_vid_cap(file, priv, &f); mutex_lock(&dev->lock); @@ -1277,7 +1277,7 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static int vidioc_enum_fmt_cap(struct file *file, void *priv, +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmtd) { if (fmtd->index != 0) @@ -1292,7 +1292,7 @@ static int vidioc_enum_fmt_cap(struct file *file, void *priv, } /* Sliced VBI ioctls */ -static int vidioc_g_fmt_vbi_capture(struct file *file, void *priv, +static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, struct v4l2_format *f) { struct em28xx_fh *fh = priv; @@ -1316,7 +1316,7 @@ static int vidioc_g_fmt_vbi_capture(struct file *file, void *priv, return rc; } -static int vidioc_try_set_vbi_capture(struct file *file, void *priv, +static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, struct v4l2_format *f) { struct em28xx_fh *fh = priv; @@ -1590,6 +1590,8 @@ static void em28xx_release_resources(struct em28xx *dev) dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN, dev->vbi_dev->minor-MINOR_VFL_TYPE_VBI_MIN); list_del(&dev->devlist); + if (dev->sbutton_input_dev) + em28xx_deregister_snapshot_button(dev); if (dev->radio_dev) { if (-1 != dev->radio_dev->minor) video_unregister_device(dev->radio_dev); @@ -1776,17 +1778,17 @@ static const struct video_device em28xx_video_template = { .minor = -1, .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, - .vidioc_g_fmt_cap = vidioc_g_fmt_cap, - .vidioc_try_fmt_cap = vidioc_try_fmt_cap, - .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .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, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_fmt_vbi_capture = vidioc_g_fmt_vbi_capture, - .vidioc_try_fmt_vbi_capture = vidioc_try_set_vbi_capture, - .vidioc_s_fmt_vbi_capture = vidioc_try_set_vbi_capture, + .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, + .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, + .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 002f170b211..89842c5d64a 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -55,6 +55,9 @@ #define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 #define EM2800_BOARD_VGEAR_POCKETTV 15 #define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 16 +#define EM2880_BOARD_PINNACLE_PCTV_HD_PRO 17 +#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 18 +#define EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA 19 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -247,6 +250,7 @@ struct em28xx_board { unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; unsigned int has_dvb:1; + unsigned int has_snapshot_button:1; enum em28xx_decoder decoder; @@ -326,6 +330,7 @@ struct em28xx { unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; unsigned int has_dvb:1; + unsigned int has_snapshot_button:1; /* Some older em28xx chips needs a waiting time after writing */ unsigned int wait_after_write; @@ -416,6 +421,11 @@ struct em28xx { /* Caches GPO and GPIO registers */ unsigned char reg_gpo, reg_gpio; + /* Snapshot button */ + char snapshot_button_path[30]; /* path of the input dev */ + struct input_dev *sbutton_input_dev; + struct delayed_work sbutton_query_work; + struct em28xx_dvb *dvb; }; @@ -481,6 +491,8 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); +void em28xx_register_snapshot_button(struct em28xx *dev); +void em28xx_deregister_snapshot_button(struct em28xx *dev); /* printk macros */ diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig new file mode 100644 index 00000000000..42b90742b40 --- /dev/null +++ b/drivers/media/video/gspca/Kconfig @@ -0,0 +1,13 @@ +config USB_GSPCA + tristate "USB GSPCA driver" + depends on VIDEO_V4L2 + ---help--- + Say Y here if you want support for various USB webcams. + + See <file:Documentation/video4linux/gspca.txt> for more info. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" to use this driver. + + To compile this driver as modules, choose M here: the + modules will be called gspca_xxxx. diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile new file mode 100644 index 00000000000..e68a8965297 --- /dev/null +++ b/drivers/media/video/gspca/Makefile @@ -0,0 +1,29 @@ +obj-$(CONFIG_USB_GSPCA) += gspca_main.o \ + gspca_conex.o gspca_etoms.o gspca_mars.o \ + gspca_ov519.o gspca_pac207.o gspca_pac7311.o \ + gspca_sonixb.o gspca_sonixj.o gspca_spca500.o gspca_spca501.o \ + gspca_spca505.o gspca_spca506.o gspca_spca508.o gspca_spca561.o \ + gspca_sunplus.o gspca_stk014.o gspca_t613.o gspca_tv8532.o \ + gspca_vc032x.o gspca_zc3xx.o + +gspca_main-objs := gspca.o +gspca_conex-objs := conex.o +gspca_etoms-objs := etoms.o +gspca_mars-objs := mars.o +gspca_ov519-objs := ov519.o +gspca_pac207-objs := pac207.o +gspca_pac7311-objs := pac7311.o +gspca_sonixb-objs := sonixb.o +gspca_sonixj-objs := sonixj.o +gspca_spca500-objs := spca500.o +gspca_spca501-objs := spca501.o +gspca_spca505-objs := spca505.o +gspca_spca506-objs := spca506.o +gspca_spca508-objs := spca508.o +gspca_spca561-objs := spca561.o +gspca_stk014-objs := stk014.o +gspca_sunplus-objs := sunplus.o +gspca_t613-objs := t613.o +gspca_tv8532-objs := tv8532.o +gspca_vc032x-objs := vc032x.o +gspca_zc3xx-objs := zc3xx.o diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c new file mode 100644 index 00000000000..013d593b0c6 --- /dev/null +++ b/drivers/media/video/gspca/conex.c @@ -0,0 +1,1051 @@ +/* + * Connexant Cx11646 library + * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "conex" + +#include "gspca.h" +#define CONEX_CAM 1 /* special JPEG header */ +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + + unsigned char qindex; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 0xd4 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0x0a, + .maximum = 0x1f, + .step = 1, +#define CONTRAST_DEF 0x0c + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 7, + .step = 1, +#define COLOR_DEF 3 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* the read bytes are found in gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 index, + __u16 len) +{ + struct usb_device *dev = gspca_dev->dev; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + if (len > sizeof gspca_dev->usb_buf) { + err("reg_r: buffer overflow"); + return; + } +#endif + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + index, gspca_dev->usb_buf, len, + 500); + PDEBUG(D_USBI, "reg read [%02x] -> %02x ..", + index, gspca_dev->usb_buf[0]); +} + +/* the bytes to write are in gspca_dev->usb_buf */ +static void reg_w_val(struct gspca_dev *gspca_dev, + __u16 index, + __u8 val) +{ + struct usb_device *dev = gspca_dev->dev; + + gspca_dev->usb_buf[0] = val; + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + index, gspca_dev->usb_buf, 1, 500); +} + +static void reg_w(struct gspca_dev *gspca_dev, + __u16 index, + const __u8 *buffer, + __u16 len) +{ + struct usb_device *dev = gspca_dev->dev; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + if (len > sizeof gspca_dev->usb_buf) { + err("reg_w: buffer overflow"); + return; + } + PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer); +#endif + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + index, gspca_dev->usb_buf, len, 500); +} + +static const __u8 cx_sensor_init[][4] = { + {0x88, 0x11, 0x01, 0x01}, + {0x88, 0x12, 0x70, 0x01}, + {0x88, 0x0f, 0x00, 0x01}, + {0x88, 0x05, 0x01, 0x01}, + {} +}; + +static const __u8 cx11646_fw1[][3] = { + {0x00, 0x02, 0x00}, + {0x01, 0x43, 0x00}, + {0x02, 0xA7, 0x00}, + {0x03, 0x8B, 0x01}, + {0x04, 0xE9, 0x02}, + {0x05, 0x08, 0x04}, + {0x06, 0x08, 0x05}, + {0x07, 0x07, 0x06}, + {0x08, 0xE7, 0x06}, + {0x09, 0xC6, 0x07}, + {0x0A, 0x86, 0x08}, + {0x0B, 0x46, 0x09}, + {0x0C, 0x05, 0x0A}, + {0x0D, 0xA5, 0x0A}, + {0x0E, 0x45, 0x0B}, + {0x0F, 0xE5, 0x0B}, + {0x10, 0x85, 0x0C}, + {0x11, 0x25, 0x0D}, + {0x12, 0xC4, 0x0D}, + {0x13, 0x45, 0x0E}, + {0x14, 0xE4, 0x0E}, + {0x15, 0x64, 0x0F}, + {0x16, 0xE4, 0x0F}, + {0x17, 0x64, 0x10}, + {0x18, 0xE4, 0x10}, + {0x19, 0x64, 0x11}, + {0x1A, 0xE4, 0x11}, + {0x1B, 0x64, 0x12}, + {0x1C, 0xE3, 0x12}, + {0x1D, 0x44, 0x13}, + {0x1E, 0xC3, 0x13}, + {0x1F, 0x24, 0x14}, + {0x20, 0xA3, 0x14}, + {0x21, 0x04, 0x15}, + {0x22, 0x83, 0x15}, + {0x23, 0xE3, 0x15}, + {0x24, 0x43, 0x16}, + {0x25, 0xA4, 0x16}, + {0x26, 0x23, 0x17}, + {0x27, 0x83, 0x17}, + {0x28, 0xE3, 0x17}, + {0x29, 0x43, 0x18}, + {0x2A, 0xA3, 0x18}, + {0x2B, 0x03, 0x19}, + {0x2C, 0x63, 0x19}, + {0x2D, 0xC3, 0x19}, + {0x2E, 0x22, 0x1A}, + {0x2F, 0x63, 0x1A}, + {0x30, 0xC3, 0x1A}, + {0x31, 0x23, 0x1B}, + {0x32, 0x83, 0x1B}, + {0x33, 0xE2, 0x1B}, + {0x34, 0x23, 0x1C}, + {0x35, 0x83, 0x1C}, + {0x36, 0xE2, 0x1C}, + {0x37, 0x23, 0x1D}, + {0x38, 0x83, 0x1D}, + {0x39, 0xE2, 0x1D}, + {0x3A, 0x23, 0x1E}, + {0x3B, 0x82, 0x1E}, + {0x3C, 0xC3, 0x1E}, + {0x3D, 0x22, 0x1F}, + {0x3E, 0x63, 0x1F}, + {0x3F, 0xC1, 0x1F}, + {} +}; +static void cx11646_fw(struct gspca_dev*gspca_dev) +{ + int i = 0; + + reg_w_val(gspca_dev, 0x006a, 0x02); + while (cx11646_fw1[i][1]) { + reg_w(gspca_dev, 0x006b, cx11646_fw1[i], 3); + i++; + } + reg_w_val(gspca_dev, 0x006a, 0x00); +} + +static const __u8 cxsensor[] = { + 0x88, 0x12, 0x70, 0x01, + 0x88, 0x0d, 0x02, 0x01, + 0x88, 0x0f, 0x00, 0x01, + 0x88, 0x03, 0x71, 0x01, 0x88, 0x04, 0x00, 0x01, /* 3 */ + 0x88, 0x02, 0x10, 0x01, + 0x88, 0x00, 0xD4, 0x01, 0x88, 0x01, 0x01, 0x01, /* 5 */ + 0x88, 0x0B, 0x00, 0x01, + 0x88, 0x0A, 0x0A, 0x01, + 0x88, 0x00, 0x08, 0x01, 0x88, 0x01, 0x00, 0x01, /* 8 */ + 0x88, 0x05, 0x01, 0x01, + 0xA1, 0x18, 0x00, 0x01, + 0x00 +}; + +static const __u8 reg20[] = { 0x10, 0x42, 0x81, 0x19, 0xd3, 0xff, 0xa7, 0xff }; +static const __u8 reg28[] = { 0x87, 0x00, 0x87, 0x00, 0x8f, 0xff, 0xea, 0xff }; +static const __u8 reg10[] = { 0xb1, 0xb1 }; +static const __u8 reg71a[] = { 0x08, 0x18, 0x0a, 0x1e }; /* 640 */ +static const __u8 reg71b[] = { 0x04, 0x0c, 0x05, 0x0f }; + /* 352{0x04,0x0a,0x06,0x12}; //352{0x05,0x0e,0x06,0x11}; //352 */ +static const __u8 reg71c[] = { 0x02, 0x07, 0x03, 0x09 }; + /* 320{0x04,0x0c,0x05,0x0f}; //320 */ +static const __u8 reg71d[] = { 0x02, 0x07, 0x03, 0x09 }; /* 176 */ +static const __u8 reg7b[] = { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }; + +static void cx_sensor(struct gspca_dev*gspca_dev) +{ + int i = 0; + int length; + const __u8 *ptsensor = cxsensor; + + reg_w(gspca_dev, 0x0020, reg20, 8); + reg_w(gspca_dev, 0x0028, reg28, 8); + reg_w(gspca_dev, 0x0010, reg10, 8); + reg_w_val(gspca_dev, 0x0092, 0x03); + + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + case 0: + reg_w(gspca_dev, 0x0071, reg71a, 4); + break; + case 1: + reg_w(gspca_dev, 0x0071, reg71b, 4); + break; + default: +/* case 2: */ + reg_w(gspca_dev, 0x0071, reg71c, 4); + break; + case 3: + reg_w(gspca_dev, 0x0071, reg71d, 4); + break; + } + reg_w(gspca_dev, 0x007b, reg7b, 6); + reg_w_val(gspca_dev, 0x00f8, 0x00); + reg_w(gspca_dev, 0x0010, reg10, 8); + reg_w_val(gspca_dev, 0x0098, 0x41); + for (i = 0; i < 11; i++) { + if (i == 3 || i == 5 || i == 8) + length = 8; + else + length = 4; + reg_w(gspca_dev, 0x00e5, ptsensor, length); + if (length == 4) + reg_r(gspca_dev, 0x00e8, 1); + else + reg_r(gspca_dev, 0x00e8, length); + ptsensor += length; + } + reg_r(gspca_dev, 0x00e7, 8); +} + +static const __u8 cx_inits_176[] = { + 0x33, 0x81, 0xB0, 0x00, 0x90, 0x00, 0x0A, 0x03, /* 176x144 */ + 0x00, 0x03, 0x03, 0x03, 0x1B, 0x05, 0x30, 0x03, + 0x65, 0x15, 0x18, 0x25, 0x03, 0x25, 0x08, 0x30, + 0x3B, 0x25, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xDC, 0xFF, 0xEE, 0xFF, 0xC5, 0xFF, 0xBF, 0xFF, + 0xF7, 0xFF, 0x88, 0xFF, 0x66, 0x02, 0x28, 0x02, + 0x1E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const __u8 cx_inits_320[] = { + 0x7f, 0x7f, 0x40, 0x01, 0xf0, 0x00, 0x02, 0x01, + 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x02, 0x01, + 0x65, 0x45, 0xfa, 0x4c, 0x2c, 0xdf, 0xb9, 0x81, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf5, 0xff, 0x6d, 0xff, 0xf6, 0x01, 0x43, 0x02, + 0xd3, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const __u8 cx_inits_352[] = { + 0x2e, 0x7c, 0x60, 0x01, 0x20, 0x01, 0x05, 0x03, + 0x00, 0x06, 0x03, 0x06, 0x1b, 0x10, 0x05, 0x3b, + 0x30, 0x25, 0x18, 0x25, 0x08, 0x30, 0x03, 0x25, + 0x3b, 0x30, 0x25, 0x1b, 0x10, 0x05, 0x00, 0x00, + 0xe3, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf5, 0xff, 0x6b, 0xff, 0xee, 0x01, 0x43, 0x02, + 0xe4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static const __u8 cx_inits_640[] = { + 0x7e, 0x7e, 0x80, 0x02, 0xe0, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x01, 0x02, 0x10, 0x30, 0x01, 0x01, + 0x65, 0x45, 0xf7, 0x52, 0x2c, 0xdf, 0xb9, 0x81, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf6, 0xff, 0x7b, 0xff, 0x01, 0x02, 0x43, 0x02, + 0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static void cx11646_initsize(struct gspca_dev *gspca_dev) +{ + const __u8 *cxinit; + static const __u8 reg12[] = { 0x08, 0x05, 0x07, 0x04, 0x24 }; + static const __u8 reg17[] = + { 0x0a, 0x00, 0xf2, 0x01, 0x0f, 0x00, 0x97, 0x02 }; + + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + case 0: + cxinit = cx_inits_640; + break; + case 1: + cxinit = cx_inits_352; + break; + default: +/* case 2: */ + cxinit = cx_inits_320; + break; + case 3: + cxinit = cx_inits_176; + break; + } + reg_w_val(gspca_dev, 0x009a, 0x01); + reg_w_val(gspca_dev, 0x0010, 0x10); + reg_w(gspca_dev, 0x0012, reg12, 5); + reg_w(gspca_dev, 0x0017, reg17, 8); + reg_w_val(gspca_dev, 0x00c0, 0x00); + reg_w_val(gspca_dev, 0x00c1, 0x04); + reg_w_val(gspca_dev, 0x00c2, 0x04); + + reg_w(gspca_dev, 0x0061, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev, 0x00ca, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev, 0x00d2, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev, 0x00da, cxinit, 6); + cxinit += 8; + reg_w(gspca_dev, 0x0041, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev, 0x0049, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev, 0x0051, cxinit, 2); + + reg_r(gspca_dev, 0x0010, 1); +} + +static const __u8 cx_jpeg_init[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x15}, /* 1 */ + {0x0f, 0x10, 0x12, 0x10, 0x0d, 0x15, 0x12, 0x11}, + {0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35, 0x22}, + {0x20, 0x1d, 0x1d, 0x20, 0x41, 0x2e, 0x31, 0x26}, + {0x35, 0x4d, 0x43, 0x51, 0x4f, 0x4b, 0x43, 0x4a}, + {0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A, 0x73}, + {0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73, 0x7D}, + {0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95, 0xA0}, + {0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83, 0x01}, + {0x15, 0x0F, 0x10, 0x12, 0x10, 0x0D, 0x15, 0x12}, + {0x11, 0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35}, + {0x22, 0x20, 0x1D, 0x1D, 0x20, 0x41, 0x2E, 0x31}, + {0x26, 0x35, 0x4D, 0x43, 0x51, 0x4F, 0x4B, 0x43}, + {0x4A, 0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A}, + {0x73, 0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73}, + {0x7D, 0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95}, + {0xA0, 0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83}, + {0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05}, + {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}, + {0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}, + {0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01}, + {0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, + {0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00}, + {0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05}, + {0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01}, + {0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21}, + {0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22}, + {0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23}, + {0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24}, + {0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17}, + {0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29}, + {0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A}, + {0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A}, + {0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A}, + {0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A}, + {0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A}, + {0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A}, + {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99}, + {0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8}, + {0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7}, + {0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6}, + {0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5}, + {0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3}, + {0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1}, + {0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9}, + {0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04}, + {0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01}, + {0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04}, + {0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07}, + {0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14}, + {0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33}, + {0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16}, + {0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19}, + {0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36}, + {0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46}, + {0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56}, + {0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66}, + {0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76}, + {0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85}, + {0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94}, + {0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3}, + {0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2}, + {0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA}, + {0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9}, + {0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8}, + {0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7}, + {0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6}, + {0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0x20, 0x00, 0x1F}, + {0x02, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x11, 0x00, 0x11, 0x22, 0x00, 0x22}, + {0x22, 0x11, 0x22, 0x22, 0x11, 0x33, 0x33, 0x11}, + {0x44, 0x66, 0x22, 0x55, 0x66, 0xFF, 0xDD, 0x00}, + {0x04, 0x00, 0x14, 0xFF, 0xC0, 0x00, 0x11, 0x08}, + {0x00, 0xF0, 0x01, 0x40, 0x03, 0x00, 0x21, 0x00}, + {0x01, 0x11, 0x01, 0x02, 0x11, 0x01, 0xFF, 0xDA}, + {0x00, 0x0C, 0x03, 0x00, 0x00, 0x01, 0x11, 0x02}, + {0x11, 0x00, 0x3F, 0x00, 0xFF, 0xD9, 0x00, 0x00} /* 79 */ +}; + + +static const __u8 cxjpeg_640[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x10}, /* 1 */ + {0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d}, + {0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a}, + {0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d}, + {0x28, 0x3a, 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38}, + {0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57}, + {0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F}, + {0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79}, + {0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63, 0x01}, + {0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E}, + {0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28}, + {0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25}, + {0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33}, + {0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44}, + {0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57}, + {0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71}, + {0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63}, + {0xFF, 0x20, 0x00, 0x1F, 0x00, 0x83, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x28, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x01, 0xE0, 0x02, 0x80}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */ +}; +static const __u8 cxjpeg_352[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d}, + {0x09, 0x09, 0x0b, 0x09, 0x08, 0x0D, 0x0b, 0x0a}, + {0x0b, 0x0e, 0x0d, 0x0d, 0x0f, 0x13, 0x1f, 0x14}, + {0x13, 0x11, 0x11, 0x13, 0x26, 0x1b, 0x1d, 0x17}, + {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, + {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, + {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, + {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, + {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, + {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, + {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, + {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, + {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, + {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, + {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, + {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, + {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, + {0xFF, 0x20, 0x00, 0x1F, 0x01, 0x83, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x16, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x01, 0x20, 0x01, 0x60}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; +static const __u8 cxjpeg_320[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x05}, + {0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04}, + {0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0c, 0x08}, + {0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, 0x0b, 0x09}, + {0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0f, 0x11}, + {0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A}, + {0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D}, + {0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24}, + {0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0x01}, + {0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04}, + {0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C}, + {0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B}, + {0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F}, + {0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14}, + {0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A}, + {0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22}, + {0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E}, + {0xFF, 0x20, 0x00, 0x1F, 0x02, 0x0C, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x14, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, 0x40}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */ +}; +static const __u8 cxjpeg_176[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d}, + {0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B, 0x0A}, + {0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F, 0x14}, + {0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D, 0x17}, + {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, + {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, + {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, + {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, + {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, + {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, + {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, + {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, + {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, + {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, + {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, + {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, + {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, + {0xFF, 0x20, 0x00, 0x1F, 0x03, 0xA1, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x0B, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x00, 0x90, 0x00, 0xB0}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; +/* 640 take with the zcx30x part */ +static const __u8 cxjpeg_qtable[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x08}, + {0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 0x07}, + {0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, 0x0a}, + {0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13, 0x0f}, + {0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, 0x1c}, + {0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22, 0x2c}, + {0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, 0x30}, + {0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39, 0x3d}, + {0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, 0x01}, + {0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0a}, + {0x0a, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 18 */ +}; + + +static void cx11646_jpegInit(struct gspca_dev*gspca_dev) +{ + int i; + int length; + + reg_w_val(gspca_dev, 0x00c0, 0x01); + reg_w_val(gspca_dev, 0x00c3, 0x00); + reg_w_val(gspca_dev, 0x00c0, 0x00); + reg_r(gspca_dev, 0x0001, 1); + length = 8; + for (i = 0; i < 79; i++) { + if (i == 78) + length = 6; + reg_w(gspca_dev, 0x0008, cx_jpeg_init[i], length); + } + reg_r(gspca_dev, 0x0002, 1); + reg_w_val(gspca_dev, 0x0055, 0x14); +} + +static const __u8 reg12[] = { 0x0a, 0x05, 0x07, 0x04, 0x19 }; +static const __u8 regE5_8[] = + { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; +static const __u8 regE5a[] = { 0x88, 0x0a, 0x0c, 0x01 }; +static const __u8 regE5b[] = { 0x88, 0x0b, 0x12, 0x01 }; +static const __u8 regE5c[] = { 0x88, 0x05, 0x01, 0x01 }; +static const __u8 reg51[] = { 0x77, 0x03 }; +#define reg70 0x03 + +static void cx11646_jpeg(struct gspca_dev*gspca_dev) +{ + int i; + int length; + __u8 Reg55; + int retry; + + reg_w_val(gspca_dev, 0x00c0, 0x01); + reg_w_val(gspca_dev, 0x00c3, 0x00); + reg_w_val(gspca_dev, 0x00c0, 0x00); + reg_r(gspca_dev, 0x0001, 1); + length = 8; + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + case 0: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev, 0x0008, cxjpeg_640[i], length); + } + Reg55 = 0x28; + break; + case 1: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev, 0x0008, cxjpeg_352[i], length); + } + Reg55 = 0x16; + break; + default: +/* case 2: */ + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev, 0x0008, cxjpeg_320[i], length); + } + Reg55 = 0x14; + break; + case 3: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev, 0x0008, cxjpeg_176[i], length); + } + Reg55 = 0x0B; + break; + } + + reg_r(gspca_dev, 0x0002, 1); + reg_w_val(gspca_dev, 0x0055, Reg55); + reg_r(gspca_dev, 0x0002, 1); + reg_w(gspca_dev, 0x0010, reg10, 2); + reg_w_val(gspca_dev, 0x0054, 0x02); + reg_w_val(gspca_dev, 0x0054, 0x01); + reg_w_val(gspca_dev, 0x0000, 0x94); + reg_w_val(gspca_dev, 0x0053, 0xc0); + reg_w_val(gspca_dev, 0x00fc, 0xe1); + reg_w_val(gspca_dev, 0x0000, 0x00); + /* wait for completion */ + retry = 50; + while (retry--) { + reg_r(gspca_dev, 0x0002, 1); + /* 0x07 until 0x00 */ + if (gspca_dev->usb_buf[0] == 0x00) + break; + reg_w_val(gspca_dev, 0x0053, 0x00); + } + if (retry == 0) + PDEBUG(D_ERR, "Damned Errors sending jpeg Table"); + /* send the qtable now */ + reg_r(gspca_dev, 0x0001, 1); /* -> 0x18 */ + length = 8; + for (i = 0; i < 18; i++) { + if (i == 17) + length = 2; + reg_w(gspca_dev, 0x0008, cxjpeg_qtable[i], length); + + } + reg_r(gspca_dev, 0x0002, 1); /* 0x00 */ + reg_r(gspca_dev, 0x0053, 1); /* 0x00 */ + reg_w_val(gspca_dev, 0x0054, 0x02); + reg_w_val(gspca_dev, 0x0054, 0x01); + reg_w_val(gspca_dev, 0x0000, 0x94); + reg_w_val(gspca_dev, 0x0053, 0xc0); + + reg_r(gspca_dev, 0x0038, 1); /* 0x40 */ + reg_r(gspca_dev, 0x0038, 1); /* 0x40 */ + reg_r(gspca_dev, 0x001f, 1); /* 0x38 */ + reg_w(gspca_dev, 0x0012, reg12, 5); + reg_w(gspca_dev, 0x00e5, regE5_8, 8); + reg_r(gspca_dev, 0x00e8, 8); + reg_w(gspca_dev, 0x00e5, regE5a, 4); + reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ + reg_w_val(gspca_dev, 0x009a, 0x01); + reg_w(gspca_dev, 0x00e5, regE5b, 4); + reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ + reg_w(gspca_dev, 0x00e5, regE5c, 4); + reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ + + reg_w(gspca_dev, 0x0051, reg51, 2); + reg_w(gspca_dev, 0x0010, reg10, 2); + reg_w_val(gspca_dev, 0x0070, reg70); +} + +static void cx11646_init1(struct gspca_dev *gspca_dev) +{ + int i = 0; + + reg_w_val(gspca_dev, 0x0010, 0x00); + reg_w_val(gspca_dev, 0x0053, 0x00); + reg_w_val(gspca_dev, 0x0052, 0x00); + reg_w_val(gspca_dev, 0x009b, 0x2f); + reg_w_val(gspca_dev, 0x009c, 0x10); + reg_r(gspca_dev, 0x0098, 1); + reg_w_val(gspca_dev, 0x0098, 0x40); + reg_r(gspca_dev, 0x0099, 1); + reg_w_val(gspca_dev, 0x0099, 0x07); + reg_w_val(gspca_dev, 0x0039, 0x40); + reg_w_val(gspca_dev, 0x003c, 0xff); + reg_w_val(gspca_dev, 0x003f, 0x1f); + reg_w_val(gspca_dev, 0x003d, 0x40); +/* reg_w_val(gspca_dev, 0x003d, 0x60); */ + reg_r(gspca_dev, 0x0099, 1); /* ->0x07 */ + + while (cx_sensor_init[i][0]) { + reg_w_val(gspca_dev, 0x00e5, cx_sensor_init[i][0]); + reg_r(gspca_dev, 0x00e8, 1); /* -> 0x00 */ + if (i == 1) { + reg_w_val(gspca_dev, 0x00ed, 0x01); + reg_r(gspca_dev, 0x00ed, 1); /* -> 0x01 */ + } + i++; + } + reg_w_val(gspca_dev, 0x00c3, 0x00); +} + +/* 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; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + + sd->qindex = 0; /* set the quantization */ + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + cx11646_init1(gspca_dev); + cx11646_initsize(gspca_dev); + cx11646_fw(gspca_dev); + cx_sensor(gspca_dev); + cx11646_jpegInit(gspca_dev); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + cx11646_initsize(gspca_dev); + cx11646_fw(gspca_dev); + cx_sensor(gspca_dev); + cx11646_jpeg(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + int retry = 50; + + reg_w_val(gspca_dev, 0x0000, 0x00); + reg_r(gspca_dev, 0x0002, 1); + reg_w_val(gspca_dev, 0x0053, 0x00); + + while (retry--) { +/* reg_r(gspca_dev, 0x0002, 1);*/ + reg_r(gspca_dev, 0x0053, 1); + if (gspca_dev->usb_buf[0] == 0) + break; + } + reg_w_val(gspca_dev, 0x0000, 0x00); + reg_r(gspca_dev, 0x0002, 1); + + reg_w_val(gspca_dev, 0x0010, 0x00); + reg_r(gspca_dev, 0x0033, 1); + reg_w_val(gspca_dev, 0x00fc, 0xe0); +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + if (data[0] == 0xff && data[1] == 0xd8) { + + /* start of frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + + /* put the JPEG header in the new frame */ + jpeg_put_header(gspca_dev, frame, + ((struct sd *) gspca_dev)->qindex, + 0x22); + data += 2; + len -= 2; + } + 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; + __u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; + __u8 reg51c[2]; + __u8 bright; + __u8 colors; + + bright = sd->brightness; + regE5cbx[2] = bright; + reg_w(gspca_dev, 0x00e5, regE5cbx, 8); + reg_r(gspca_dev, 0x00e8, 8); + reg_w(gspca_dev, 0x00e5, regE5c, 4); + reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ + + colors = sd->colors; + reg51c[0] = 0x77; + reg51c[1] = colors; + reg_w(gspca_dev, 0x0051, reg51c, 2); + reg_w(gspca_dev, 0x0010, reg10, 2); + reg_w_val(gspca_dev, 0x0070, reg70); +} + +static void setcontrast(struct gspca_dev*gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 }; /* seem MSB */ +/* __u8 regE5bcx[] = { 0x88, 0x0b, 0x12, 0x01}; * LSB */ + __u8 reg51c[2]; + + regE5acx[2] = sd->contrast; + reg_w(gspca_dev, 0x00e5, regE5acx, 4); + reg_r(gspca_dev, 0x00e8, 1); /* 0x00 */ + reg51c[0] = 0x77; + reg51c[1] = sd->colors; + reg_w(gspca_dev, 0x0051, reg51c, 2); + reg_w(gspca_dev, 0x0010, reg10, 2); + reg_w_val(gspca_dev, 0x0070, reg70); +} + +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_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) { + setbrightness(gspca_dev); + setcontrast(gspca_dev); + } + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0572, 0x0041), DVNM("Creative Notebook cx11646")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c new file mode 100644 index 00000000000..8ab4ea7201a --- /dev/null +++ b/drivers/media/video/gspca/etoms.c @@ -0,0 +1,956 @@ +/* + * Etoms Et61x151 GPL Linux driver by Michel Xhaard (09/09/2004) + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "etoms" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("Etoms USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + char sensor; +#define SENSOR_PAS106 0 +#define SENSOR_TAS5130CXX 1 + signed char ag_cnt; +#define AG_CNT_START 13 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 1, + .maximum = 127, + .step = 1, +#define BRIGHTNESS_DEF 63 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, +#define CONTRAST_DEF 127 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 15, + .step = 1, +#define COLOR_DEF 7 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, +/* {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, */ +}; + +static struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +#define ETOMS_ALT_SIZE_1000 12 + +#define ET_GPIO_DIR_CTRL 0x04 /* Control IO bit[0..5] (0 in 1 out) */ +#define ET_GPIO_OUT 0x05 /* Only IO data */ +#define ET_GPIO_IN 0x06 /* Read Only IO data */ +#define ET_RESET_ALL 0x03 +#define ET_ClCK 0x01 +#define ET_CTRL 0x02 /* enable i2c OutClck Powerdown mode */ + +#define ET_COMP 0x12 /* Compression register */ +#define ET_MAXQt 0x13 +#define ET_MINQt 0x14 +#define ET_COMP_VAL0 0x02 +#define ET_COMP_VAL1 0x03 + +#define ET_REG1d 0x1d +#define ET_REG1e 0x1e +#define ET_REG1f 0x1f +#define ET_REG20 0x20 +#define ET_REG21 0x21 +#define ET_REG22 0x22 +#define ET_REG23 0x23 +#define ET_REG24 0x24 +#define ET_REG25 0x25 +/* base registers for luma calculation */ +#define ET_LUMA_CENTER 0x39 + +#define ET_G_RED 0x4d +#define ET_G_GREEN1 0x4e +#define ET_G_BLUE 0x4f +#define ET_G_GREEN2 0x50 +#define ET_G_GR_H 0x51 +#define ET_G_GB_H 0x52 + +#define ET_O_RED 0x34 +#define ET_O_GREEN1 0x35 +#define ET_O_BLUE 0x36 +#define ET_O_GREEN2 0x37 + +#define ET_SYNCHRO 0x68 +#define ET_STARTX 0x69 +#define ET_STARTY 0x6a +#define ET_WIDTH_LOW 0x6b +#define ET_HEIGTH_LOW 0x6c +#define ET_W_H_HEIGTH 0x6d + +#define ET_REG6e 0x6e /* OBW */ +#define ET_REG6f 0x6f /* OBW */ +#define ET_REG70 0x70 /* OBW_AWB */ +#define ET_REG71 0x71 /* OBW_AWB */ +#define ET_REG72 0x72 /* OBW_AWB */ +#define ET_REG73 0x73 /* Clkdelay ns */ +#define ET_REG74 0x74 /* test pattern */ +#define ET_REG75 0x75 /* test pattern */ + +#define ET_I2C_CLK 0x8c +#define ET_PXL_CLK 0x60 + +#define ET_I2C_BASE 0x89 +#define ET_I2C_COUNT 0x8a +#define ET_I2C_PREFETCH 0x8b +#define ET_I2C_REG 0x88 +#define ET_I2C_DATA7 0x87 +#define ET_I2C_DATA6 0x86 +#define ET_I2C_DATA5 0x85 +#define ET_I2C_DATA4 0x84 +#define ET_I2C_DATA3 0x83 +#define ET_I2C_DATA2 0x82 +#define ET_I2C_DATA1 0x81 +#define ET_I2C_DATA0 0x80 + +#define PAS106_REG2 0x02 /* pxlClk = systemClk/(reg2) */ +#define PAS106_REG3 0x03 /* line/frame H [11..4] */ +#define PAS106_REG4 0x04 /* line/frame L [3..0] */ +#define PAS106_REG5 0x05 /* exposure time line offset(default 5) */ +#define PAS106_REG6 0x06 /* exposure time pixel offset(default 6) */ +#define PAS106_REG7 0x07 /* signbit Dac (default 0) */ +#define PAS106_REG9 0x09 +#define PAS106_REG0e 0x0e /* global gain [4..0](default 0x0e) */ +#define PAS106_REG13 0x13 /* end i2c write */ + +static const __u8 GainRGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; + +static const __u8 I2c2[] = { 0x08, 0x08, 0x08, 0x08, 0x0d }; + +static const __u8 I2c3[] = { 0x12, 0x05 }; + +static const __u8 I2c4[] = { 0x41, 0x08 }; + +/* read 'len' bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 index, + __u16 len) +{ + struct usb_device *dev = gspca_dev->dev; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + if (len > sizeof gspca_dev->usb_buf) { + err("reg_r: buffer overflow"); + return; + } +#endif + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0, + index, gspca_dev->usb_buf, len, 500); + PDEBUG(D_USBI, "reg read [%02x] -> %02x ..", + index, gspca_dev->usb_buf[0]); +} + +static void reg_w_val(struct gspca_dev *gspca_dev, + __u16 index, + __u8 val) +{ + struct usb_device *dev = gspca_dev->dev; + + gspca_dev->usb_buf[0] = val; + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0, + index, gspca_dev->usb_buf, 1, 500); +} + +static void reg_w(struct gspca_dev *gspca_dev, + __u16 index, + const __u8 *buffer, + __u16 len) +{ + struct usb_device *dev = gspca_dev->dev; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + if (len > sizeof gspca_dev->usb_buf) { + err("reg_w: buffer overflow"); + return; + } + PDEBUG(D_USBO, "reg write [%02x] = %02x..", index, *buffer); +#endif + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0, index, gspca_dev->usb_buf, len, 500); +} + +static int i2c_w(struct gspca_dev *gspca_dev, + __u8 reg, + const __u8 *buffer, + int len, __u8 mode) +{ + /* buffer should be [D0..D7] */ + __u8 ptchcount; + + /* set the base address */ + reg_w_val(gspca_dev, ET_I2C_BASE, 0x40); + /* sensor base for the pas106 */ + /* set count and prefetch */ + ptchcount = ((len & 0x07) << 4) | (mode & 0x03); + reg_w_val(gspca_dev, ET_I2C_COUNT, ptchcount); + /* set the register base */ + reg_w_val(gspca_dev, ET_I2C_REG, reg); + while (--len >= 0) + reg_w_val(gspca_dev, ET_I2C_DATA0 + len, buffer[len]); + return 0; +} + +static int i2c_r(struct gspca_dev *gspca_dev, + __u8 reg) +{ + /* set the base address */ + reg_w_val(gspca_dev, ET_I2C_BASE, 0x40); + /* sensor base for the pas106 */ + /* set count and prefetch (cnd: 4 bits - mode: 4 bits) */ + reg_w_val(gspca_dev, ET_I2C_COUNT, 0x11); + reg_w_val(gspca_dev, ET_I2C_REG, reg); /* set the register base */ + reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x02); /* prefetch */ + reg_w_val(gspca_dev, ET_I2C_PREFETCH, 0x00); + reg_r(gspca_dev, ET_I2C_DATA0, 1); /* read one byte */ + return 0; +} + +static int Et_WaitStatus(struct gspca_dev *gspca_dev) +{ + int retry = 10; + + while (retry--) { + reg_r(gspca_dev, ET_ClCK, 1); + if (gspca_dev->usb_buf[0] != 0) + return 1; + } + return 0; +} + +static int et_video(struct gspca_dev *gspca_dev, + int on) +{ + int ret; + + reg_w_val(gspca_dev, ET_GPIO_OUT, + on ? 0x10 /* startvideo - set Bit5 */ + : 0); /* stopvideo */ + ret = Et_WaitStatus(gspca_dev); + if (ret != 0) + PDEBUG(D_ERR, "timeout video on/off"); + return ret; +} + +static void Et_init2(struct gspca_dev *gspca_dev) +{ + __u8 value; + static const __u8 FormLine[] = { 0x84, 0x03, 0x14, 0xf4, 0x01, 0x05 }; + + PDEBUG(D_STREAM, "Open Init2 ET"); + reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 0x2f); + reg_w_val(gspca_dev, ET_GPIO_OUT, 0x10); + reg_r(gspca_dev, ET_GPIO_IN, 1); + reg_w_val(gspca_dev, ET_ClCK, 0x14); /* 0x14 // 0x16 enabled pattern */ + reg_w_val(gspca_dev, ET_CTRL, 0x1b); + + /* compression et subsampling */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) + value = ET_COMP_VAL1; /* 320 */ + else + value = ET_COMP_VAL0; /* 640 */ + reg_w_val(gspca_dev, ET_COMP, value); + reg_w_val(gspca_dev, ET_MAXQt, 0x1f); + reg_w_val(gspca_dev, ET_MINQt, 0x04); + /* undocumented registers */ + reg_w_val(gspca_dev, ET_REG1d, 0xff); + reg_w_val(gspca_dev, ET_REG1e, 0xff); + reg_w_val(gspca_dev, ET_REG1f, 0xff); + reg_w_val(gspca_dev, ET_REG20, 0x35); + reg_w_val(gspca_dev, ET_REG21, 0x01); + reg_w_val(gspca_dev, ET_REG22, 0x00); + reg_w_val(gspca_dev, ET_REG23, 0xff); + reg_w_val(gspca_dev, ET_REG24, 0xff); + reg_w_val(gspca_dev, ET_REG25, 0x0f); + /* colors setting */ + reg_w_val(gspca_dev, 0x30, 0x11); /* 0x30 */ + reg_w_val(gspca_dev, 0x31, 0x40); + reg_w_val(gspca_dev, 0x32, 0x00); + reg_w_val(gspca_dev, ET_O_RED, 0x00); /* 0x34 */ + reg_w_val(gspca_dev, ET_O_GREEN1, 0x00); + reg_w_val(gspca_dev, ET_O_BLUE, 0x00); + reg_w_val(gspca_dev, ET_O_GREEN2, 0x00); + /*************/ + reg_w_val(gspca_dev, ET_G_RED, 0x80); /* 0x4d */ + reg_w_val(gspca_dev, ET_G_GREEN1, 0x80); + reg_w_val(gspca_dev, ET_G_BLUE, 0x80); + reg_w_val(gspca_dev, ET_G_GREEN2, 0x80); + reg_w_val(gspca_dev, ET_G_GR_H, 0x00); + reg_w_val(gspca_dev, ET_G_GB_H, 0x00); /* 0x52 */ + /* Window control registers */ + reg_w_val(gspca_dev, 0x61, 0x80); /* use cmc_out */ + reg_w_val(gspca_dev, 0x62, 0x02); + reg_w_val(gspca_dev, 0x63, 0x03); + reg_w_val(gspca_dev, 0x64, 0x14); + reg_w_val(gspca_dev, 0x65, 0x0e); + reg_w_val(gspca_dev, 0x66, 0x02); + reg_w_val(gspca_dev, 0x67, 0x02); + + /**************************************/ + reg_w_val(gspca_dev, ET_SYNCHRO, 0x8f); /* 0x68 */ + reg_w_val(gspca_dev, ET_STARTX, 0x69); /* 0x6a //0x69 */ + reg_w_val(gspca_dev, ET_STARTY, 0x0d); /* 0x0d //0x0c */ + reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x80); + reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0xe0); + reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x60); /* 6d */ + reg_w_val(gspca_dev, ET_REG6e, 0x86); + reg_w_val(gspca_dev, ET_REG6f, 0x01); + reg_w_val(gspca_dev, ET_REG70, 0x26); + reg_w_val(gspca_dev, ET_REG71, 0x7a); + reg_w_val(gspca_dev, ET_REG72, 0x01); + /* Clock Pattern registers ***************** */ + reg_w_val(gspca_dev, ET_REG73, 0x00); + reg_w_val(gspca_dev, ET_REG74, 0x18); /* 0x28 */ + reg_w_val(gspca_dev, ET_REG75, 0x0f); /* 0x01 */ + /**********************************************/ + reg_w_val(gspca_dev, 0x8a, 0x20); + reg_w_val(gspca_dev, 0x8d, 0x0f); + reg_w_val(gspca_dev, 0x8e, 0x08); + /**************************************/ + reg_w_val(gspca_dev, 0x03, 0x08); + reg_w_val(gspca_dev, ET_PXL_CLK, 0x03); + reg_w_val(gspca_dev, 0x81, 0xff); + reg_w_val(gspca_dev, 0x80, 0x00); + reg_w_val(gspca_dev, 0x81, 0xff); + reg_w_val(gspca_dev, 0x80, 0x20); + reg_w_val(gspca_dev, 0x03, 0x01); + reg_w_val(gspca_dev, 0x03, 0x00); + reg_w_val(gspca_dev, 0x03, 0x08); + /********************************************/ + +/* reg_r(gspca_dev, ET_I2C_BASE, 1); + always 0x40 as the pas106 ??? */ + /* set the sensor */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) + value = 0x04; /* 320 */ + else /* 640 */ + value = 0x1e; /* 0x17 * setting PixelClock + * 0x03 mean 24/(3+1) = 6 Mhz + * 0x05 -> 24/(5+1) = 4 Mhz + * 0x0b -> 24/(11+1) = 2 Mhz + * 0x17 -> 24/(23+1) = 1 Mhz + */ + reg_w_val(gspca_dev, ET_PXL_CLK, value); + /* now set by fifo the FormatLine setting */ + reg_w(gspca_dev, 0x62, FormLine, 6); + + /* set exposure times [ 0..0x78] 0->longvalue 0x78->shortvalue */ + reg_w_val(gspca_dev, 0x81, 0x47); /* 0x47; */ + reg_w_val(gspca_dev, 0x80, 0x40); /* 0x40; */ + /* Pedro change */ + /* Brightness change Brith+ decrease value */ + /* Brigth- increase value */ + /* original value = 0x70; */ + reg_w_val(gspca_dev, 0x81, 0x30); /* 0x20; - set brightness */ + reg_w_val(gspca_dev, 0x80, 0x20); /* 0x20; */ +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d }; + __u8 i2cflags = 0x01; + /* __u8 green = 0; */ + __u8 colors = sd->colors; + + I2cc[3] = colors; /* red */ + I2cc[0] = 15 - colors; /* blue */ + /* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */ + /* I2cc[1] = I2cc[2] = green; */ + if (sd->sensor == SENSOR_PAS106) { + i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3); + i2c_w(gspca_dev, PAS106_REG9, I2cc, sizeof I2cc, 1); + } +/* PDEBUG(D_CONF , "Etoms red %d blue %d green %d", + I2cc[3], I2cc[0], green); */ +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAS106) { +/* i2c_r(gspca_dev, PAS106_REG9); * blue */ + i2c_r(gspca_dev, PAS106_REG9 + 3); /* red */ + sd->colors = gspca_dev->usb_buf[0] & 0x0f; + } +} + +static void Et_init1(struct gspca_dev *gspca_dev) +{ + __u8 value; +/* __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0x22, 0xac, 0x00, 0x01, 0x00}; */ + __u8 I2c0[] = { 0x0a, 0x12, 0x05, 0x6d, 0xcd, 0x00, 0x01, 0x00 }; + /* try 1/120 0x6d 0xcd 0x40 */ +/* __u8 I2c0 [] = {0x0a, 0x12, 0x05, 0xfe, 0xfe, 0xc0, 0x01, 0x00}; + * 1/60000 hmm ?? */ + + PDEBUG(D_STREAM, "Open Init1 ET"); + reg_w_val(gspca_dev, ET_GPIO_DIR_CTRL, 7); + reg_r(gspca_dev, ET_GPIO_IN, 1); + reg_w_val(gspca_dev, ET_RESET_ALL, 1); + reg_w_val(gspca_dev, ET_RESET_ALL, 0); + reg_w_val(gspca_dev, ET_ClCK, 0x10); + reg_w_val(gspca_dev, ET_CTRL, 0x19); + /* compression et subsampling */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) + value = ET_COMP_VAL1; + else + value = ET_COMP_VAL0; + PDEBUG(D_STREAM, "Open mode %d Compression %d", + gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv, + value); + reg_w_val(gspca_dev, ET_COMP, value); + reg_w_val(gspca_dev, ET_MAXQt, 0x1d); + reg_w_val(gspca_dev, ET_MINQt, 0x02); + /* undocumented registers */ + reg_w_val(gspca_dev, ET_REG1d, 0xff); + reg_w_val(gspca_dev, ET_REG1e, 0xff); + reg_w_val(gspca_dev, ET_REG1f, 0xff); + reg_w_val(gspca_dev, ET_REG20, 0x35); + reg_w_val(gspca_dev, ET_REG21, 0x01); + reg_w_val(gspca_dev, ET_REG22, 0x00); + reg_w_val(gspca_dev, ET_REG23, 0xf7); + reg_w_val(gspca_dev, ET_REG24, 0xff); + reg_w_val(gspca_dev, ET_REG25, 0x07); + /* colors setting */ + reg_w_val(gspca_dev, ET_G_RED, 0x80); + reg_w_val(gspca_dev, ET_G_GREEN1, 0x80); + reg_w_val(gspca_dev, ET_G_BLUE, 0x80); + reg_w_val(gspca_dev, ET_G_GREEN2, 0x80); + reg_w_val(gspca_dev, ET_G_GR_H, 0x00); + reg_w_val(gspca_dev, ET_G_GB_H, 0x00); + /* Window control registers */ + reg_w_val(gspca_dev, ET_SYNCHRO, 0xf0); + reg_w_val(gspca_dev, ET_STARTX, 0x56); /* 0x56 */ + reg_w_val(gspca_dev, ET_STARTY, 0x05); /* 0x04 */ + reg_w_val(gspca_dev, ET_WIDTH_LOW, 0x60); + reg_w_val(gspca_dev, ET_HEIGTH_LOW, 0x20); + reg_w_val(gspca_dev, ET_W_H_HEIGTH, 0x50); + reg_w_val(gspca_dev, ET_REG6e, 0x86); + reg_w_val(gspca_dev, ET_REG6f, 0x01); + reg_w_val(gspca_dev, ET_REG70, 0x86); + reg_w_val(gspca_dev, ET_REG71, 0x14); + reg_w_val(gspca_dev, ET_REG72, 0x00); + /* Clock Pattern registers */ + reg_w_val(gspca_dev, ET_REG73, 0x00); + reg_w_val(gspca_dev, ET_REG74, 0x00); + reg_w_val(gspca_dev, ET_REG75, 0x0a); + reg_w_val(gspca_dev, ET_I2C_CLK, 0x04); + reg_w_val(gspca_dev, ET_PXL_CLK, 0x01); + /* set the sensor */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + I2c0[0] = 0x06; + i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1); + i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1); + value = 0x06; + i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1); + i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1); + /* value = 0x1f; */ + value = 0x04; + i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1); + } else { + I2c0[0] = 0x0a; + + i2c_w(gspca_dev, PAS106_REG2, I2c0, sizeof I2c0, 1); + i2c_w(gspca_dev, PAS106_REG9, I2c2, sizeof I2c2, 1); + value = 0x0a; + i2c_w(gspca_dev, PAS106_REG2, &value, 1, 1); + i2c_w(gspca_dev, PAS106_REG3, I2c3, sizeof I2c3, 1); + value = 0x04; + /* value = 0x10; */ + i2c_w(gspca_dev, PAS106_REG0e, &value, 1, 1); + /* bit 2 enable bit 1:2 select 0 1 2 3 + value = 0x07; * curve 0 * + i2c_w(gspca_dev, PAS106_REG0f, &value, 1, 1); + */ + } + +/* value = 0x01; */ +/* value = 0x22; */ +/* i2c_w(gspca_dev, PAS106_REG5, &value, 1, 1); */ + /* magnetude and sign bit for DAC */ + i2c_w(gspca_dev, PAS106_REG7, I2c4, sizeof I2c4, 1); + /* now set by fifo the whole colors setting */ + reg_w(gspca_dev, ET_G_RED, GainRGBG, 6); + getcolors(gspca_dev); + setcolors(gspca_dev); +} + +/* 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; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; +/* switch (vendor) { */ +/* case 0x102c: * Etoms */ + switch (product) { + case 0x6151: + sd->sensor = SENSOR_PAS106; /* Etoms61x151 */ + break; + case 0x6251: + sd->sensor = SENSOR_TAS5130CXX; /* Etoms61x251 */ + break; +/* } */ +/* break; */ + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 1; + if (sd->sensor == SENSOR_PAS106) { + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + } else { + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + } + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + sd->autogain = AUTOGAIN_DEF; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAS106) + Et_init1(gspca_dev); + else + Et_init2(gspca_dev); + reg_w_val(gspca_dev, ET_RESET_ALL, 0x08); + et_video(gspca_dev, 0); /* video off */ + return 0; +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAS106) + Et_init1(gspca_dev); + else + Et_init2(gspca_dev); + + reg_w_val(gspca_dev, ET_RESET_ALL, 0x08); + et_video(gspca_dev, 1); /* video on */ +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + et_video(gspca_dev, 0); /* video off */ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + __u8 brightness = sd->brightness; + + for (i = 0; i < 4; i++) + reg_w_val(gspca_dev, ET_O_RED + i, brightness); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + int brightness = 0; + + for (i = 0; i < 4; i++) { + reg_r(gspca_dev, ET_O_RED + i, 1); + brightness += gspca_dev->usb_buf[0]; + } + sd->brightness = brightness >> 3; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; + __u8 contrast = sd->contrast; + + memset(RGBG, contrast, sizeof(RGBG) - 2); + reg_w(gspca_dev, ET_G_RED, RGBG, 6); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + int contrast = 0; + + for (i = 0; i < 4; i++) { + reg_r(gspca_dev, ET_G_RED + i, 1); + contrast += gspca_dev->usb_buf[0]; + } + sd->contrast = contrast >> 2; +} + +static __u8 Et_getgainG(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAS106) { + i2c_r(gspca_dev, PAS106_REG0e); + PDEBUG(D_CONF, "Etoms gain G %d", gspca_dev->usb_buf[0]); + return gspca_dev->usb_buf[0]; + } + return 0x1f; +} + +static void Et_setgainG(struct gspca_dev *gspca_dev, __u8 gain) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_PAS106) { + __u8 i2cflags = 0x01; + + i2c_w(gspca_dev, PAS106_REG13, &i2cflags, 1, 3); + i2c_w(gspca_dev, PAS106_REG0e, &gain, 1, 1); + } +} + +#define BLIMIT(bright) \ + (__u8)((bright > 0x1f)?0x1f:((bright < 4)?3:bright)) +#define LIMIT(color) \ + (unsigned char)((color > 0xff)?0xff:((color < 0)?0:color)) + +static void setautogain(struct gspca_dev *gspca_dev) +{ + __u8 luma = 0; + __u8 luma_mean = 128; + __u8 luma_delta = 20; + __u8 spring = 4; + int Gbright = 0; + __u8 r, g, b; + + Gbright = Et_getgainG(gspca_dev); + reg_r(gspca_dev, ET_LUMA_CENTER, 4); + g = (gspca_dev->usb_buf[0] + gspca_dev->usb_buf[3]) >> 1; + r = gspca_dev->usb_buf[1]; + b = gspca_dev->usb_buf[2]; + r = ((r << 8) - (r << 4) - (r << 3)) >> 10; + b = ((b << 7) >> 10); + g = ((g << 9) + (g << 7) + (g << 5)) >> 10; + luma = LIMIT(r + g + b); + PDEBUG(D_FRAM, "Etoms luma G %d", luma); + if (luma < luma_mean - luma_delta || luma > luma_mean + luma_delta) { + Gbright += (luma_mean - luma) >> spring; + Gbright = BLIMIT(Gbright); + PDEBUG(D_FRAM, "Etoms Gbright %d", Gbright); + Et_setgainG(gspca_dev, (__u8) Gbright); + } +} + +#undef BLIMIT +#undef LIMIT + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd; + int seqframe; + + seqframe = data[0] & 0x3f; + len = (int) (((data[0] & 0xc0) << 2) | data[1]); + if (seqframe == 0x3f) { + PDEBUG(D_FRAM, + "header packet found datalength %d !!", len); + PDEBUG(D_FRAM, "G %d R %d G %d B %d", + data[2], data[3], data[4], data[5]); + data += 30; + /* don't change datalength as the chips provided it */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + sd = (struct sd *) gspca_dev; + if (sd->ag_cnt >= 0) { + if (--sd->ag_cnt < 0) { + sd->ag_cnt = AG_CNT_START; + setautogain(gspca_dev); + } + } + return; + } + if (len) { + data += 8; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + } else { /* Drop Packet */ + gspca_dev->last_packet_type = DISCARD_PACKET; + } +} + +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; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { +#ifndef CONFIG_USB_ET61X251 + {USB_DEVICE(0x102c, 0x6151), DVNM("Qcam Sangha CIF")}, +#endif + {USB_DEVICE(0x102c, 0x6251), DVNM("Qcam xxxxxx VGA")}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c new file mode 100644 index 00000000000..16e367cec76 --- /dev/null +++ b/drivers/media/video/gspca/gspca.c @@ -0,0 +1,1905 @@ +/* + * Main USB camera driver + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define MODULE_NAME "gspca" + +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/vmalloc.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/pagemap.h> +#include <linux/io.h> +#include <asm/page.h> +#include <linux/uaccess.h> +#include <linux/jiffies.h> + +#include "gspca.h" + +/* global values */ +#define DEF_NURBS 2 /* default number of URBs */ + +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); +MODULE_DESCRIPTION("GSPCA USB Camera Driver"); +MODULE_LICENSE("GPL"); + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +static int video_nr = -1; + +#ifdef CONFIG_VIDEO_ADV_DEBUG +int gspca_debug = D_ERR | D_PROBE; +EXPORT_SYMBOL(gspca_debug); + +static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h) +{ + if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') { + PDEBUG(D_CONF|D_STREAM, "%s %c%c%c%c %dx%d", + txt, + pixfmt & 0xff, + (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, + pixfmt >> 24, + w, h); + } else { + PDEBUG(D_CONF|D_STREAM, "%s 0x%08x %dx%d", + txt, + pixfmt, + w, h); + } +} +#else +#define PDEBUG_MODE(txt, pixfmt, w, h) +#endif + +/* specific memory types - !! should different from V4L2_MEMORY_xxx */ +#define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ +#define GSPCA_MEMORY_READ 7 + +#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE) + +/* + * VMA operations. + */ +static void gspca_vm_open(struct vm_area_struct *vma) +{ + struct gspca_frame *frame = vma->vm_private_data; + + frame->vma_use_count++; + frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED; +} + +static void gspca_vm_close(struct vm_area_struct *vma) +{ + struct gspca_frame *frame = vma->vm_private_data; + + if (--frame->vma_use_count <= 0) + frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED; +} + +static struct vm_operations_struct gspca_vm_ops = { + .open = gspca_vm_open, + .close = gspca_vm_close, +}; + +/* + * fill a video frame from an URB and resubmit + */ +static void fill_frame(struct gspca_dev *gspca_dev, + struct urb *urb) +{ + struct gspca_frame *frame; + __u8 *data; /* address of data in the iso message */ + int i, j, len, st; + cam_pkt_op pkt_scan; + + if (urb->status != 0) { + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + return; /* disconnection ? */ + } + pkt_scan = gspca_dev->sd_desc->pkt_scan; + for (i = 0; i < urb->number_of_packets; i++) { + + /* check the availability of the frame buffer */ + j = gspca_dev->fr_i; + j = gspca_dev->fr_queue[j]; + frame = &gspca_dev->frame[j]; + if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) + != V4L2_BUF_FLAG_QUEUED) { + gspca_dev->last_packet_type = DISCARD_PACKET; + break; + } + + /* check the packet status and length */ + len = urb->iso_frame_desc[i].actual_length; + if (len == 0) + continue; + st = urb->iso_frame_desc[i].status; + if (st) { + PDEBUG(D_ERR, + "ISOC data error: [%d] len=%d, status=%d", + i, len, st); + gspca_dev->last_packet_type = DISCARD_PACKET; + continue; + } + + /* let the packet be analyzed by the subdriver */ + PDEBUG(D_PACK, "packet [%d] o:%d l:%d", + i, urb->iso_frame_desc[i].offset, len); + data = (__u8 *) urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + pkt_scan(gspca_dev, frame, data, len); + } + + /* resubmit the URB */ + urb->status = 0; + st = usb_submit_urb(urb, GFP_ATOMIC); + if (st < 0) + PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st); +} + +/* + * ISOC message interrupt from the USB device + * + * Analyse each packet and call the subdriver for copy to the frame buffer. + */ +static void isoc_irq(struct urb *urb +) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; + + PDEBUG(D_PACK, "isoc irq"); + if (!gspca_dev->streaming) + return; + fill_frame(gspca_dev, urb); +} + +/* + * add data to the current frame + * + * This function is called by the subdrivers at interrupt level. + * + * To build a frame, these ones must add + * - one FIRST_PACKET + * - 0 or many INTER_PACKETs + * - one LAST_PACKET + * DISCARD_PACKET invalidates the whole frame. + * On LAST_PACKET, a new frame is returned. + */ +struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, + int packet_type, + struct gspca_frame *frame, + const __u8 *data, + int len) +{ + int i, j; + + PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len); + + /* when start of a new frame, if the current frame buffer + * is not queued, discard the whole frame */ + if (packet_type == FIRST_PACKET) { + if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) + != V4L2_BUF_FLAG_QUEUED) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return frame; + } + frame->data_end = frame->data; + jiffies_to_timeval(get_jiffies_64(), + &frame->v4l2_buf.timestamp); + frame->v4l2_buf.sequence = ++gspca_dev->sequence; + } else if (gspca_dev->last_packet_type == DISCARD_PACKET) { + return frame; + } + + /* append the packet to the frame buffer */ + if (len > 0) { + if (frame->data_end - frame->data + len + > frame->v4l2_buf.length) { + PDEBUG(D_ERR|D_PACK, "frame overflow %zd > %d", + frame->data_end - frame->data + len, + frame->v4l2_buf.length); + packet_type = DISCARD_PACKET; + } else { + memcpy(frame->data_end, data, len); + frame->data_end += len; + } + } + gspca_dev->last_packet_type = packet_type; + + /* if last packet, wake the application and advance in the queue */ + if (packet_type == LAST_PACKET) { + frame->v4l2_buf.bytesused = frame->data_end - frame->data; + frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED; + frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; + atomic_inc(&gspca_dev->nevent); + wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ + i = (gspca_dev->fr_i + 1) % gspca_dev->nframes; + gspca_dev->fr_i = i; + PDEBUG(D_FRAM, "frame complete len:%d q:%d i:%d o:%d", + frame->v4l2_buf.bytesused, + gspca_dev->fr_q, + i, + gspca_dev->fr_o); + j = gspca_dev->fr_queue[i]; + frame = &gspca_dev->frame[j]; + } + return frame; +} +EXPORT_SYMBOL(gspca_frame_add); + +static int gspca_is_compressed(__u32 format) +{ + switch (format) { + case V4L2_PIX_FMT_MJPEG: + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_SPCA561: + case V4L2_PIX_FMT_PAC207: + return 1; + } + return 0; +} + +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + +/* size = PAGE_ALIGN(size); (already done) */ + mem = vmalloc_32(size); + if (mem != NULL) { + adr = (unsigned long) mem; + while ((long) size > 0) { + SetPageReserved(vmalloc_to_page((void *) adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void *mem, long size) +{ + unsigned long adr; + + adr = (unsigned long) mem; + while (size > 0) { + ClearPageReserved(vmalloc_to_page((void *) adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +static int frame_alloc(struct gspca_dev *gspca_dev, + unsigned int count) +{ + struct gspca_frame *frame; + unsigned int frsz; + int i; + + i = gspca_dev->curr_mode; + frsz = gspca_dev->cam.cam_mode[i].sizeimage; + PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz); + frsz = PAGE_ALIGN(frsz); + gspca_dev->frsz = frsz; + if (count > GSPCA_MAX_FRAMES) + count = GSPCA_MAX_FRAMES; + gspca_dev->frbuf = rvmalloc(frsz * count); + if (!gspca_dev->frbuf) { + err("frame alloc failed"); + return -ENOMEM; + } + gspca_dev->nframes = count; + for (i = 0; i < count; i++) { + frame = &gspca_dev->frame[i]; + frame->v4l2_buf.index = i; + frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + frame->v4l2_buf.flags = 0; + frame->v4l2_buf.field = V4L2_FIELD_NONE; + frame->v4l2_buf.length = frsz; + frame->v4l2_buf.memory = gspca_dev->memory; + frame->v4l2_buf.sequence = 0; + frame->data = frame->data_end = + gspca_dev->frbuf + i * frsz; + frame->v4l2_buf.m.offset = i * frsz; + } + gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; + gspca_dev->last_packet_type = DISCARD_PACKET; + gspca_dev->sequence = 0; + atomic_set(&gspca_dev->nevent, 0); + return 0; +} + +static void frame_free(struct gspca_dev *gspca_dev) +{ + int i; + + PDEBUG(D_STREAM, "frame free"); + if (gspca_dev->frbuf != NULL) { + rvfree(gspca_dev->frbuf, + gspca_dev->nframes * gspca_dev->frsz); + gspca_dev->frbuf = NULL; + for (i = 0; i < gspca_dev->nframes; i++) + gspca_dev->frame[i].data = NULL; + } + gspca_dev->nframes = 0; +} + +static void destroy_urbs(struct gspca_dev *gspca_dev) +{ + struct urb *urb; + unsigned int i; + + PDEBUG(D_STREAM, "kill transfer"); + for (i = 0; i < MAX_NURBS; ++i) { + urb = gspca_dev->urb[i]; + if (urb == NULL) + break; + + gspca_dev->urb[i] = NULL; + usb_kill_urb(urb); + if (urb->transfer_buffer != NULL) + usb_buffer_free(gspca_dev->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); + usb_free_urb(urb); + } +} + +/* + * search an input isochronous endpoint in an alternate setting + */ +static struct usb_host_endpoint *alt_isoc(struct usb_host_interface *alt, + __u8 epaddr) +{ + struct usb_host_endpoint *ep; + int i, attr; + + epaddr |= USB_DIR_IN; + for (i = 0; i < alt->desc.bNumEndpoints; i++) { + ep = &alt->endpoint[i]; + if (ep->desc.bEndpointAddress == epaddr) { + attr = ep->desc.bmAttributes + & USB_ENDPOINT_XFERTYPE_MASK; + if (attr == USB_ENDPOINT_XFER_ISOC) + return ep; + break; + } + } + return NULL; +} + +/* + * search an input isochronous endpoint + * + * The endpoint is defined by the subdriver. + * Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep). + * This routine may be called many times when the bandwidth is too small + * (the bandwidth is checked on urb submit). + */ +struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev) +{ + struct usb_interface *intf; + struct usb_host_endpoint *ep; + int i, ret; + + intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); + ep = NULL; + i = gspca_dev->alt; /* previous alt setting */ + while (--i > 0) { /* alt 0 is unusable */ + ep = alt_isoc(&intf->altsetting[i], gspca_dev->cam.epaddr); + if (ep) + break; + } + if (ep == NULL) { + err("no ISOC endpoint found"); + return NULL; + } + PDEBUG(D_STREAM, "use ISOC alt %d ep 0x%02x", + i, ep->desc.bEndpointAddress); + ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); + if (ret < 0) { + err("set interface err %d", ret); + return NULL; + } + gspca_dev->alt = i; /* memorize the current alt setting */ + return ep; +} + +/* + * create the isochronous URBs + */ +static int create_urbs(struct gspca_dev *gspca_dev, + struct usb_host_endpoint *ep) +{ + struct urb *urb; + int n, nurbs, i, psize, npkt, bsize; + + /* calculate the packet size and the number of packets */ + psize = le16_to_cpu(ep->desc.wMaxPacketSize); + + /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + npkt = ISO_MAX_SIZE / psize; + if (npkt > ISO_MAX_PKT) + npkt = ISO_MAX_PKT; + bsize = psize * npkt; + PDEBUG(D_STREAM, + "isoc %d pkts size %d (bsize:%d)", npkt, psize, bsize); + nurbs = DEF_NURBS; + gspca_dev->nurbs = nurbs; + for (n = 0; n < nurbs; n++) { + urb = usb_alloc_urb(npkt, GFP_KERNEL); + if (!urb) { + err("usb_alloc_urb failed"); + return -ENOMEM; + } + urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev, + bsize, + GFP_KERNEL, + &urb->transfer_dma); + + if (urb->transfer_buffer == NULL) { + usb_free_urb(urb); + destroy_urbs(gspca_dev); + err("usb_buffer_urb failed"); + return -ENOMEM; + } + gspca_dev->urb[n] = urb; + urb->dev = gspca_dev->dev; + urb->context = gspca_dev; + urb->pipe = usb_rcvisocpipe(gspca_dev->dev, + ep->desc.bEndpointAddress); + urb->transfer_flags = URB_ISO_ASAP + | URB_NO_TRANSFER_DMA_MAP; + urb->interval = ep->desc.bInterval; + urb->complete = isoc_irq; + urb->number_of_packets = npkt; + urb->transfer_buffer_length = bsize; + for (i = 0; i < npkt; i++) { + urb->iso_frame_desc[i].length = psize; + urb->iso_frame_desc[i].offset = psize * i; + } + } + return 0; +} + +/* + * start the USB transfer + */ +static int gspca_init_transfer(struct gspca_dev *gspca_dev) +{ + struct usb_host_endpoint *ep; + int n, ret; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + + /* set the higher alternate setting and + * loop until urb submit succeeds */ + gspca_dev->alt = gspca_dev->nbalt; + for (;;) { + PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt); + ep = get_isoc_ep(gspca_dev); + if (ep == NULL) { + ret = -EIO; + goto out; + } + ret = create_urbs(gspca_dev, ep); + if (ret < 0) + goto out; + + /* start the cam */ + gspca_dev->sd_desc->start(gspca_dev); + gspca_dev->streaming = 1; + atomic_set(&gspca_dev->nevent, 0); + + /* submit the URBs */ + for (n = 0; n < gspca_dev->nurbs; n++) { + ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL); + if (ret < 0) { + PDEBUG(D_ERR|D_STREAM, + "usb_submit_urb [%d] err %d", n, ret); + gspca_dev->streaming = 0; + destroy_urbs(gspca_dev); + if (ret == -ENOSPC) + break; /* try the previous alt */ + goto out; + } + } + if (ret >= 0) + break; + } +out: + mutex_unlock(&gspca_dev->usb_lock); + return ret; +} + +static int gspca_set_alt0(struct gspca_dev *gspca_dev) +{ + int ret; + + ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); + if (ret < 0) + PDEBUG(D_ERR|D_STREAM, "set interface 0 err %d", ret); + return ret; +} + +/* Note both the queue and the usb lock should be hold when calling this */ +static void gspca_stream_off(struct gspca_dev *gspca_dev) +{ + gspca_dev->streaming = 0; + atomic_set(&gspca_dev->nevent, 0); + if (gspca_dev->present) { + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_set_alt0(gspca_dev); + gspca_dev->sd_desc->stop0(gspca_dev); + PDEBUG(D_STREAM, "stream off OK"); + } +} + +static void gspca_set_default_mode(struct gspca_dev *gspca_dev) +{ + int i; + + i = gspca_dev->cam.nmodes - 1; /* take the highest mode */ + gspca_dev->curr_mode = i; + gspca_dev->width = gspca_dev->cam.cam_mode[i].width; + gspca_dev->height = gspca_dev->cam.cam_mode[i].height; + gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixelformat; +} + +static int wxh_to_mode(struct gspca_dev *gspca_dev, + int width, int height) +{ + int i; + + for (i = gspca_dev->cam.nmodes; --i > 0; ) { + if (width >= gspca_dev->cam.cam_mode[i].width + && height >= gspca_dev->cam.cam_mode[i].height) + break; + } + return i; +} + +/* + * search a mode with the right pixel format + */ +static int gspca_get_mode(struct gspca_dev *gspca_dev, + int mode, + int pixfmt) +{ + int modeU, modeD; + + modeU = modeD = mode; + while ((modeU < gspca_dev->cam.nmodes) || modeD >= 0) { + if (--modeD >= 0) { + if (gspca_dev->cam.cam_mode[modeD].pixelformat + == pixfmt) + return modeD; + } + if (++modeU < gspca_dev->cam.nmodes) { + if (gspca_dev->cam.cam_mode[modeU].pixelformat + == pixfmt) + return modeU; + } + } + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmtdesc) +{ + struct gspca_dev *gspca_dev = priv; + int i, j, index; + __u32 fmt_tb[8]; + + /* give an index to each format */ + index = 0; + j = 0; + for (i = gspca_dev->cam.nmodes; --i >= 0; ) { + fmt_tb[index] = gspca_dev->cam.cam_mode[i].pixelformat; + j = 0; + for (;;) { + if (fmt_tb[j] == fmt_tb[index]) + break; + j++; + } + if (j == index) { + if (fmtdesc->index == index) + break; /* new format */ + index++; + if (index >= sizeof fmt_tb / sizeof fmt_tb[0]) + return -EINVAL; + } + } + if (i < 0) + return -EINVAL; /* no more format */ + + fmtdesc->pixelformat = fmt_tb[index]; + if (gspca_is_compressed(fmt_tb[index])) + fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; + fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmtdesc->description[0] = fmtdesc->pixelformat & 0xff; + fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff; + fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff; + fmtdesc->description[3] = fmtdesc->pixelformat >> 24; + fmtdesc->description[4] = '\0'; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct gspca_dev *gspca_dev = priv; + int mode; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + mode = gspca_dev->curr_mode; + memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode], + sizeof fmt->fmt.pix); + return 0; +} + +static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, + struct v4l2_format *fmt) +{ + int w, h, mode, mode2; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + w = fmt->fmt.pix.width; + h = fmt->fmt.pix.height; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + if (gspca_debug & D_CONF) + PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h); +#endif + /* search the closest mode for width and height */ + mode = wxh_to_mode(gspca_dev, w, h); + + /* OK if right palette */ + if (gspca_dev->cam.cam_mode[mode].pixelformat + != fmt->fmt.pix.pixelformat) { + + /* else, search the closest mode with the same pixel format */ + mode2 = gspca_get_mode(gspca_dev, mode, + fmt->fmt.pix.pixelformat); + if (mode2 >= 0) + mode = mode2; +/* else + ; * no chance, return this mode */ + } + memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode], + sizeof fmt->fmt.pix); + return mode; /* used when s_fmt */ +} + +static int vidioc_try_fmt_vid_cap(struct file *file, + void *priv, + struct v4l2_format *fmt) +{ + struct gspca_dev *gspca_dev = priv; + int ret; + + ret = try_fmt_vid_cap(gspca_dev, fmt); + if (ret < 0) + return ret; + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct gspca_dev *gspca_dev = priv; + int ret; + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + + ret = try_fmt_vid_cap(gspca_dev, fmt); + if (ret < 0) + goto out; + + if (gspca_dev->nframes != 0 + && fmt->fmt.pix.sizeimage > gspca_dev->frsz) { + ret = -EINVAL; + goto out; + } + + if (ret == gspca_dev->curr_mode) { + ret = 0; + goto out; /* same mode */ + } + + if (gspca_dev->streaming) { + ret = -EBUSY; + goto out; + } + gspca_dev->width = fmt->fmt.pix.width; + gspca_dev->height = fmt->fmt.pix.height; + gspca_dev->pixfmt = fmt->fmt.pix.pixelformat; + gspca_dev->curr_mode = ret; + + ret = 0; +out: + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +static int dev_open(struct inode *inode, struct file *file) +{ + struct gspca_dev *gspca_dev; + int ret; + + PDEBUG(D_STREAM, "%s open", current->comm); + gspca_dev = (struct gspca_dev *) video_devdata(file); + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + if (!gspca_dev->present) { + ret = -ENODEV; + goto out; + } + + /* if not done yet, initialize the sensor */ + if (gspca_dev->users == 0) { + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { + ret = -ERESTARTSYS; + goto out; + } + ret = gspca_dev->sd_desc->open(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + if (ret != 0) { + PDEBUG(D_ERR|D_CONF, "init device failed %d", ret); + goto out; + } + } else if (gspca_dev->users > 4) { /* (arbitrary value) */ + ret = -EBUSY; + goto out; + } + gspca_dev->users++; + file->private_data = gspca_dev; +#ifdef CONFIG_VIDEO_ADV_DEBUG + /* activate the v4l2 debug */ + if (gspca_debug & D_V4L2) + gspca_dev->vdev.debug |= 3; + else + gspca_dev->vdev.debug &= ~3; +#endif +out: + mutex_unlock(&gspca_dev->queue_lock); + if (ret != 0) + PDEBUG(D_ERR|D_STREAM, "open failed err %d", ret); + else + PDEBUG(D_STREAM, "open done"); + return ret; +} + +static int dev_close(struct inode *inode, struct file *file) +{ + struct gspca_dev *gspca_dev = file->private_data; + + PDEBUG(D_STREAM, "%s close", current->comm); + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + gspca_dev->users--; + + /* if the file did the capture, free the streaming resources */ + if (gspca_dev->capt_file == file) { + mutex_lock(&gspca_dev->usb_lock); + if (gspca_dev->streaming) + gspca_stream_off(gspca_dev); + gspca_dev->sd_desc->close(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + frame_free(gspca_dev); + gspca_dev->capt_file = NULL; + gspca_dev->memory = GSPCA_MEMORY_NO; + } + file->private_data = NULL; + mutex_unlock(&gspca_dev->queue_lock); + PDEBUG(D_STREAM, "close done"); + return 0; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct gspca_dev *gspca_dev = priv; + + memset(cap, 0, sizeof *cap); + strncpy(cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver); + strncpy(cap->card, gspca_dev->cam.dev_name, sizeof cap->card); + strncpy(cap->bus_info, gspca_dev->dev->bus->bus_name, + sizeof cap->bus_info); + cap->version = DRIVER_VERSION_NUMBER; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_STREAMING + | V4L2_CAP_READWRITE; + return 0; +} + +/* the use of V4L2_CTRL_FLAG_NEXT_CTRL asks for the controls to be sorted */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *q_ctrl) +{ + struct gspca_dev *gspca_dev = priv; + int i; + u32 id; + + id = q_ctrl->id; + if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { + id &= V4L2_CTRL_ID_MASK; + id++; + for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { + if (id >= gspca_dev->sd_desc->ctrls[i].qctrl.id) { + memcpy(q_ctrl, + &gspca_dev->sd_desc->ctrls[i].qctrl, + sizeof *q_ctrl); + return 0; + } + } + return -EINVAL; + } + for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { + if (id == gspca_dev->sd_desc->ctrls[i].qctrl.id) { + memcpy(q_ctrl, + &gspca_dev->sd_desc->ctrls[i].qctrl, + sizeof *q_ctrl); + return 0; + } + } + if (id >= V4L2_CID_BASE + && id <= V4L2_CID_LASTP1) { + q_ctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + } + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct gspca_dev *gspca_dev = priv; + const struct ctrl *ctrls; + int i, ret; + + for (i = 0, ctrls = gspca_dev->sd_desc->ctrls; + i < gspca_dev->sd_desc->nctrls; + i++, ctrls++) { + if (ctrl->id != ctrls->qctrl.id) + continue; + if (ctrl->value < ctrls->qctrl.minimum + && ctrl->value > ctrls->qctrl.maximum) + return -ERANGE; + PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + ret = ctrls->set(gspca_dev, ctrl->value); + mutex_unlock(&gspca_dev->usb_lock); + return ret; + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct gspca_dev *gspca_dev = priv; + + const struct ctrl *ctrls; + int i, ret; + + for (i = 0, ctrls = gspca_dev->sd_desc->ctrls; + i < gspca_dev->sd_desc->nctrls; + i++, ctrls++) { + if (ctrl->id != ctrls->qctrl.id) + continue; + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + ret = ctrls->get(gspca_dev, &ctrl->value); + mutex_unlock(&gspca_dev->usb_lock); + return ret; + } + return -EINVAL; +} + +static int vidioc_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *qmenu) +{ + struct gspca_dev *gspca_dev = priv; + + if (!gspca_dev->sd_desc->querymenu) + return -EINVAL; + return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu); +} + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct gspca_dev *gspca_dev = priv; + + if (input->index != 0) + return -EINVAL; + memset(input, 0, sizeof *input); + input->type = V4L2_INPUT_TYPE_CAMERA; + strncpy(input->name, gspca_dev->sd_desc->name, + sizeof input->name); + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + return (0); +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct gspca_dev *gspca_dev = priv; + int i, ret = 0; + + if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + switch (rb->memory) { + case GSPCA_MEMORY_READ: /* (internal call) */ + case V4L2_MEMORY_MMAP: + case V4L2_MEMORY_USERPTR: + break; + default: + return -EINVAL; + } + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + + if (gspca_dev->memory != GSPCA_MEMORY_NO + && gspca_dev->memory != rb->memory) { + ret = -EBUSY; + goto out; + } + + /* only one file may do the capture */ + if (gspca_dev->capt_file != NULL + && gspca_dev->capt_file != file) { + ret = -EBUSY; + goto out; + } + + /* if allocated, the buffers must not be mapped */ + for (i = 0; i < gspca_dev->nframes; i++) { + if (gspca_dev->frame[i].vma_use_count) { + ret = -EBUSY; + goto out; + } + } + + /* stop streaming */ + if (gspca_dev->streaming) { + mutex_lock(&gspca_dev->usb_lock); + gspca_stream_off(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + } + + /* free the previous allocated buffers, if any */ + if (gspca_dev->nframes != 0) { + frame_free(gspca_dev); + gspca_dev->capt_file = NULL; + } + if (rb->count == 0) /* unrequest */ + goto out; + gspca_dev->memory = rb->memory; + ret = frame_alloc(gspca_dev, rb->count); + if (ret == 0) { + rb->count = gspca_dev->nframes; + gspca_dev->capt_file = file; + } +out: + mutex_unlock(&gspca_dev->queue_lock); + PDEBUG(D_STREAM, "reqbufs st:%d c:%d", ret, rb->count); + return ret; +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *v4l2_buf) +{ + struct gspca_dev *gspca_dev = priv; + struct gspca_frame *frame; + + if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE + || v4l2_buf->index < 0 + || v4l2_buf->index >= gspca_dev->nframes) + return -EINVAL; + + frame = &gspca_dev->frame[v4l2_buf->index]; + memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct gspca_dev *gspca_dev = priv; + int ret; + + if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + if (!gspca_dev->present) { + ret = -ENODEV; + goto out; + } + if (gspca_dev->nframes == 0) { + ret = -EINVAL; + goto out; + } + if (!gspca_dev->streaming) { + ret = gspca_init_transfer(gspca_dev); + if (ret < 0) + goto out; + } +#ifdef CONFIG_VIDEO_ADV_DEBUG + if (gspca_debug & D_STREAM) { + PDEBUG_MODE("stream on OK", + gspca_dev->pixfmt, + gspca_dev->width, + gspca_dev->height); + } +#endif + ret = 0; +out: + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct gspca_dev *gspca_dev = priv; + int i, ret; + + if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (!gspca_dev->streaming) + return 0; + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + + /* stop streaming */ + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { + ret = -ERESTARTSYS; + goto out; + } + gspca_stream_off(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + + /* empty the application queues */ + for (i = 0; i < gspca_dev->nframes; i++) + gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS; + gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; + gspca_dev->last_packet_type = DISCARD_PACKET; + gspca_dev->sequence = 0; + atomic_set(&gspca_dev->nevent, 0); + ret = 0; +out: + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +static int vidioc_g_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jpegcomp) +{ + struct gspca_dev *gspca_dev = priv; + int ret; + + if (!gspca_dev->sd_desc->get_jcomp) + return -EINVAL; + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); + mutex_unlock(&gspca_dev->usb_lock); + return ret; +} + +static int vidioc_s_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jpegcomp) +{ + struct gspca_dev *gspca_dev = priv; + int ret; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (!gspca_dev->sd_desc->set_jcomp) + return -EINVAL; + ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); + mutex_unlock(&gspca_dev->usb_lock); + return ret; +} + +static int vidioc_g_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct gspca_dev *gspca_dev = priv; + + memset(parm, 0, sizeof *parm); + parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + parm->parm.capture.readbuffers = gspca_dev->nbufread; + return 0; +} + +static int vidioc_s_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct gspca_dev *gspca_dev = priv; + int n; + + n = parm->parm.capture.readbuffers; + if (n == 0 || n > GSPCA_MAX_FRAMES) + parm->parm.capture.readbuffers = gspca_dev->nbufread; + else + gspca_dev->nbufread = n; + return 0; +} + +static int vidioc_s_std(struct file *filp, void *priv, + v4l2_std_id *parm) +{ + return 0; +} + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf(struct file *file, void *priv, + struct video_mbuf *mbuf) +{ + struct gspca_dev *gspca_dev = file->private_data; + int i; + + PDEBUG(D_STREAM, "cgmbuf"); + if (gspca_dev->nframes == 0) { + int ret; + + { + struct v4l2_format fmt; + + memset(&fmt, 0, sizeof fmt); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + i = gspca_dev->cam.nmodes - 1; /* highest mode */ + fmt.fmt.pix.width = gspca_dev->cam.cam_mode[i].width; + fmt.fmt.pix.height = gspca_dev->cam.cam_mode[i].height; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; + ret = vidioc_s_fmt_vid_cap(file, priv, &fmt); + if (ret != 0) + return ret; + } + { + struct v4l2_requestbuffers rb; + + memset(&rb, 0, sizeof rb); + rb.count = 4; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rb.memory = V4L2_MEMORY_MMAP; + ret = vidioc_reqbufs(file, priv, &rb); + if (ret != 0) + return ret; + } + } + mbuf->frames = gspca_dev->nframes; + mbuf->size = gspca_dev->frsz * gspca_dev->nframes; + for (i = 0; i < mbuf->frames; i++) + mbuf->offsets[i] = gspca_dev->frame[i].v4l2_buf.m.offset; + return 0; +} +#endif + +static int dev_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct gspca_dev *gspca_dev = file->private_data; + struct gspca_frame *frame; + struct page *page; + unsigned long addr, start, size; + int i, ret; + + start = vma->vm_start; + size = vma->vm_end - vma->vm_start; + PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size); + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + if (!gspca_dev->present) { + ret = -ENODEV; + goto out; + } + if (gspca_dev->capt_file != file) { + ret = -EINVAL; + goto out; + } + + frame = NULL; + for (i = 0; i < gspca_dev->nframes; ++i) { + if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) { + PDEBUG(D_STREAM, "mmap bad memory type"); + break; + } + if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT) + == vma->vm_pgoff) { + frame = &gspca_dev->frame[i]; + break; + } + } + if (frame == NULL) { + PDEBUG(D_STREAM, "mmap no frame buffer found"); + ret = -EINVAL; + goto out; + } +#ifdef CONFIG_VIDEO_V4L1_COMPAT + /* v4l1 maps all the buffers */ + if (i != 0 + || size != frame->v4l2_buf.length * gspca_dev->nframes) +#endif + if (size != frame->v4l2_buf.length) { + PDEBUG(D_STREAM, "mmap bad size"); + ret = -EINVAL; + goto out; + } + + /* + * - VM_IO marks the area as being a mmaped region for I/O to a + * device. It also prevents the region from being core dumped. + */ + vma->vm_flags |= VM_IO; + + addr = (unsigned long) frame->data; + while (size > 0) { + page = vmalloc_to_page((void *) addr); + ret = vm_insert_page(vma, start, page); + if (ret < 0) + goto out; + start += PAGE_SIZE; + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &gspca_vm_ops; + vma->vm_private_data = frame; + gspca_vm_open(vma); + ret = 0; +out: + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +/* + * wait for a video frame + * + * If a frame is ready, its index is returned. + */ +static int frame_wait(struct gspca_dev *gspca_dev, + int nonblock_ing) +{ + struct gspca_frame *frame; + int i, j, ret; + + /* check if a frame is ready */ + i = gspca_dev->fr_o; + j = gspca_dev->fr_queue[i]; + frame = &gspca_dev->frame[j]; + if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) { + atomic_dec(&gspca_dev->nevent); + goto ok; + } + if (nonblock_ing) /* no frame yet */ + return -EAGAIN; + + /* wait till a frame is ready */ + for (;;) { + ret = wait_event_interruptible_timeout(gspca_dev->wq, + atomic_read(&gspca_dev->nevent) > 0, + msecs_to_jiffies(3000)); + if (ret <= 0) { + if (ret < 0) + return ret; /* interrupt */ + return -EIO; /* timeout */ + } + atomic_dec(&gspca_dev->nevent); + if (!gspca_dev->streaming || !gspca_dev->present) + return -EIO; + i = gspca_dev->fr_o; + j = gspca_dev->fr_queue[i]; + frame = &gspca_dev->frame[j]; + if (frame->v4l2_buf.flags & V4L2_BUF_FLAG_DONE) + break; + } +ok: + gspca_dev->fr_o = (i + 1) % gspca_dev->nframes; + PDEBUG(D_FRAM, "frame wait q:%d i:%d o:%d", + gspca_dev->fr_q, + gspca_dev->fr_i, + gspca_dev->fr_o); + + if (gspca_dev->sd_desc->dq_callback) { + mutex_lock(&gspca_dev->usb_lock); + gspca_dev->sd_desc->dq_callback(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + } + return j; +} + +/* + * dequeue a video buffer + * + * If nonblock_ing is false, block until a buffer is available. + */ +static int vidioc_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *v4l2_buf) +{ + struct gspca_dev *gspca_dev = priv; + struct gspca_frame *frame; + int i, ret; + + PDEBUG(D_FRAM, "dqbuf"); + if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (v4l2_buf->memory != gspca_dev->memory) + return -EINVAL; + + /* if not streaming, be sure the application will not loop forever */ + if (!(file->f_flags & O_NONBLOCK) + && !gspca_dev->streaming && gspca_dev->users == 1) + return -EINVAL; + + /* only the capturing file may dequeue */ + if (gspca_dev->capt_file != file) + return -EINVAL; + + /* only one dequeue / read at a time */ + if (mutex_lock_interruptible(&gspca_dev->read_lock)) + return -ERESTARTSYS; + + ret = frame_wait(gspca_dev, file->f_flags & O_NONBLOCK); + if (ret < 0) + goto out; + i = ret; /* frame index */ + frame = &gspca_dev->frame[i]; + if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { + if (copy_to_user((__u8 *) frame->v4l2_buf.m.userptr, + frame->data, + frame->v4l2_buf.bytesused)) { + PDEBUG(D_ERR|D_STREAM, + "dqbuf cp to user failed"); + ret = -EFAULT; + goto out; + } + } + frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; + memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); + PDEBUG(D_FRAM, "dqbuf %d", i); + ret = 0; +out: + mutex_unlock(&gspca_dev->read_lock); + return ret; +} + +/* + * queue a video buffer + * + * Attempting to queue a buffer that has already been + * queued will return -EINVAL. + */ +static int vidioc_qbuf(struct file *file, void *priv, + struct v4l2_buffer *v4l2_buf) +{ + struct gspca_dev *gspca_dev = priv; + struct gspca_frame *frame; + int i, index, ret; + + PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index); + if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + return -ERESTARTSYS; + + index = v4l2_buf->index; + if ((unsigned) index >= gspca_dev->nframes) { + PDEBUG(D_FRAM, + "qbuf idx %d >= %d", index, gspca_dev->nframes); + ret = -EINVAL; + goto out; + } + if (v4l2_buf->memory != gspca_dev->memory) { + PDEBUG(D_FRAM, "qbuf bad memory type"); + ret = -EINVAL; + goto out; + } + + frame = &gspca_dev->frame[index]; + if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) { + PDEBUG(D_FRAM, "qbuf bad state"); + ret = -EINVAL; + goto out; + } + + frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED; +/* frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; */ + + if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) { + frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr; + frame->v4l2_buf.length = v4l2_buf->length; + } + + /* put the buffer in the 'queued' queue */ + i = gspca_dev->fr_q; + gspca_dev->fr_queue[i] = index; + gspca_dev->fr_q = (i + 1) % gspca_dev->nframes; + PDEBUG(D_FRAM, "qbuf q:%d i:%d o:%d", + gspca_dev->fr_q, + gspca_dev->fr_i, + gspca_dev->fr_o); + + v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; + v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE; + ret = 0; +out: + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +/* + * allocate the resources for read() + */ +static int read_alloc(struct gspca_dev *gspca_dev, + struct file *file) +{ + struct v4l2_buffer v4l2_buf; + int i, ret; + + PDEBUG(D_STREAM, "read alloc"); + if (gspca_dev->nframes == 0) { + struct v4l2_requestbuffers rb; + + memset(&rb, 0, sizeof rb); + rb.count = gspca_dev->nbufread; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rb.memory = GSPCA_MEMORY_READ; + ret = vidioc_reqbufs(file, gspca_dev, &rb); + if (ret != 0) { + PDEBUG(D_STREAM, "read reqbuf err %d", ret); + return ret; + } + memset(&v4l2_buf, 0, sizeof v4l2_buf); + v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_buf.memory = GSPCA_MEMORY_READ; + for (i = 0; i < gspca_dev->nbufread; i++) { + v4l2_buf.index = i; + ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); + if (ret != 0) { + PDEBUG(D_STREAM, "read qbuf err: %d", ret); + return ret; + } + } + gspca_dev->memory = GSPCA_MEMORY_READ; + } + + /* start streaming */ + ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (ret != 0) + PDEBUG(D_STREAM, "read streamon err %d", ret); + return ret; +} + +static unsigned int dev_poll(struct file *file, poll_table *wait) +{ + struct gspca_dev *gspca_dev = file->private_data; + int i, ret; + + PDEBUG(D_FRAM, "poll"); + + poll_wait(file, &gspca_dev->wq, wait); + if (!gspca_dev->present) + return POLLERR; + + /* if reqbufs is not done, the user would use read() */ + if (gspca_dev->nframes == 0) { + if (gspca_dev->memory != GSPCA_MEMORY_NO) + return POLLERR; /* not the 1st time */ + ret = read_alloc(gspca_dev, file); + if (ret != 0) + return POLLERR; + } + + if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) + return POLLERR; + if (!gspca_dev->present) { + ret = POLLERR; + goto out; + } + + /* check the next incoming buffer */ + i = gspca_dev->fr_o; + i = gspca_dev->fr_queue[i]; + if (gspca_dev->frame[i].v4l2_buf.flags & V4L2_BUF_FLAG_DONE) + ret = POLLIN | POLLRDNORM; /* something to read */ + else + ret = 0; +out: + mutex_unlock(&gspca_dev->queue_lock); + return ret; +} + +static ssize_t dev_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct gspca_dev *gspca_dev = file->private_data; + struct gspca_frame *frame; + struct v4l2_buffer v4l2_buf; + struct timeval timestamp; + int n, ret, ret2; + + PDEBUG(D_FRAM, "read (%zd)", count); + if (!gspca_dev->present) + return -ENODEV; + switch (gspca_dev->memory) { + case GSPCA_MEMORY_NO: /* first time */ + ret = read_alloc(gspca_dev, file); + if (ret != 0) + return ret; + break; + case GSPCA_MEMORY_READ: + if (gspca_dev->capt_file == file) + break; + /* fall thru */ + default: + return -EINVAL; + } + + /* get a frame */ + jiffies_to_timeval(get_jiffies_64(), ×tamp); + timestamp.tv_sec--; + n = 2; + for (;;) { + memset(&v4l2_buf, 0, sizeof v4l2_buf); + v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_buf.memory = GSPCA_MEMORY_READ; + ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf); + if (ret != 0) { + PDEBUG(D_STREAM, "read dqbuf err %d", ret); + return ret; + } + + /* if the process slept for more than 1 second, + * get anewer frame */ + frame = &gspca_dev->frame[v4l2_buf.index]; + if (--n < 0) + break; /* avoid infinite loop */ + if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec) + break; + ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); + if (ret != 0) { + PDEBUG(D_STREAM, "read qbuf err %d", ret); + return ret; + } + } + + /* copy the frame */ + if (count > frame->v4l2_buf.bytesused) + count = frame->v4l2_buf.bytesused; + ret = copy_to_user(data, frame->data, count); + if (ret != 0) { + PDEBUG(D_ERR|D_STREAM, + "read cp to user lack %d / %zd", ret, count); + ret = -EFAULT; + goto out; + } + ret = count; +out: + /* in each case, requeue the buffer */ + ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf); + if (ret2 != 0) + return ret2; + return ret; +} + +static void dev_release(struct video_device *vfd) +{ + /* nothing */ +} + +static struct file_operations dev_fops = { + .owner = THIS_MODULE, + .open = dev_open, + .release = dev_close, + .read = dev_read, + .mmap = dev_mmap, + .ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT + .compat_ioctl = v4l_compat_ioctl32, +#endif + .llseek = no_llseek, + .poll = dev_poll, +}; + +static struct video_device gspca_template = { + .name = "gspca main driver", + .type = VID_TYPE_CAPTURE, + .fops = &dev_fops, + .release = dev_release, /* mandatory */ + .minor = -1, + .vidioc_querycap = vidioc_querycap, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_streamon = vidioc_streamon, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_querymenu = vidioc_querymenu, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_jpegcomp = vidioc_g_jpegcomp, + .vidioc_s_jpegcomp = vidioc_s_jpegcomp, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_s_std = vidioc_s_std, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +}; + +/* + * probe and create a new gspca device + * + * This function must be called by the sub-driver when it is + * called for probing a new device. + */ +int gspca_dev_probe(struct usb_interface *intf, + const struct usb_device_id *id, + const struct sd_desc *sd_desc, + int dev_size, + struct module *module) +{ + struct usb_interface_descriptor *interface; + struct gspca_dev *gspca_dev; + struct usb_device *dev = interface_to_usbdev(intf); + int ret; + + PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct); + + /* we don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; + interface = &intf->cur_altsetting->desc; + if (interface->bInterfaceNumber > 0) + return -ENODEV; + + /* create the device */ + if (dev_size < sizeof *gspca_dev) + dev_size = sizeof *gspca_dev; + gspca_dev = kzalloc(dev_size, GFP_KERNEL); + if (gspca_dev == NULL) { + err("couldn't kzalloc gspca struct"); + return -EIO; + } + gspca_dev->dev = dev; + gspca_dev->iface = interface->bInterfaceNumber; + gspca_dev->nbalt = intf->num_altsetting; + gspca_dev->sd_desc = sd_desc; +/* gspca_dev->users = 0; (done by kzalloc) */ + gspca_dev->nbufread = 2; + + /* configure the subdriver */ + ret = gspca_dev->sd_desc->config(gspca_dev, id); + if (ret < 0) + goto out; + ret = gspca_set_alt0(gspca_dev); + if (ret < 0) + goto out; + gspca_set_default_mode(gspca_dev); + + mutex_init(&gspca_dev->usb_lock); + mutex_init(&gspca_dev->read_lock); + mutex_init(&gspca_dev->queue_lock); + init_waitqueue_head(&gspca_dev->wq); + + /* init video stuff */ + memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); + gspca_dev->vdev.dev = &dev->dev; + memcpy(&gspca_dev->fops, &dev_fops, sizeof gspca_dev->fops); + gspca_dev->vdev.fops = &gspca_dev->fops; + gspca_dev->fops.owner = module; /* module protection */ + ret = video_register_device(&gspca_dev->vdev, + VFL_TYPE_GRABBER, + video_nr); + if (ret < 0) { + err("video_register_device err %d", ret); + goto out; + } + + gspca_dev->present = 1; + usb_set_intfdata(intf, gspca_dev); + PDEBUG(D_PROBE, "probe ok"); + return 0; +out: + kfree(gspca_dev); + return ret; +} +EXPORT_SYMBOL(gspca_dev_probe); + +/* + * USB disconnection + * + * This function must be called by the sub-driver + * when the device disconnects, after the specific resources are freed. + */ +void gspca_disconnect(struct usb_interface *intf) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + + if (!gspca_dev) + return; + gspca_dev->present = 0; + mutex_lock(&gspca_dev->queue_lock); + mutex_lock(&gspca_dev->usb_lock); + gspca_dev->streaming = 0; + destroy_urbs(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->queue_lock); + while (gspca_dev->users != 0) { /* wait until fully closed */ + atomic_inc(&gspca_dev->nevent); + wake_up_interruptible(&gspca_dev->wq); /* wake processes */ + schedule(); + } +/* We don't want people trying to open up the device */ + video_unregister_device(&gspca_dev->vdev); +/* Free the memory */ + kfree(gspca_dev); + PDEBUG(D_PROBE, "disconnect complete"); +} +EXPORT_SYMBOL(gspca_disconnect); + +/* -- cam driver utility functions -- */ + +/* auto gain and exposure algorithm based on the knee algorithm described here: + http://ytse.tricolour.net/docs/LowLightOptimization.html + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, + int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee) +{ + int i, steps, gain, orig_gain, exposure, orig_exposure, autogain; + const struct ctrl *gain_ctrl = NULL; + const struct ctrl *exposure_ctrl = NULL; + const struct ctrl *autogain_ctrl = NULL; + int retval = 0; + + for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { + if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN) + gain_ctrl = &gspca_dev->sd_desc->ctrls[i]; + if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE) + exposure_ctrl = &gspca_dev->sd_desc->ctrls[i]; + if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_AUTOGAIN) + autogain_ctrl = &gspca_dev->sd_desc->ctrls[i]; + } + if (!gain_ctrl || !exposure_ctrl || !autogain_ctrl) { + PDEBUG(D_ERR, "Error: gspca_auto_gain_n_exposure called " + "on cam without (auto)gain/exposure"); + return 0; + } + + if (gain_ctrl->get(gspca_dev, &gain) || + exposure_ctrl->get(gspca_dev, &exposure) || + autogain_ctrl->get(gspca_dev, &autogain) || !autogain) + return 0; + + orig_gain = gain; + orig_exposure = exposure; + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = abs(desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n", + avg_lum, desired_avg_lum, steps); + + for (i = 0; i < steps; i++) { + if (avg_lum > desired_avg_lum) { + if (gain > gain_knee) + gain--; + else if (exposure > exposure_knee) + exposure--; + else if (gain > gain_ctrl->qctrl.default_value) + gain--; + else if (exposure > exposure_ctrl->qctrl.minimum) + exposure--; + else if (gain > gain_ctrl->qctrl.minimum) + gain--; + else + break; + } else { + if (gain < gain_ctrl->qctrl.default_value) + gain++; + else if (exposure < exposure_knee) + exposure++; + else if (gain < gain_knee) + gain++; + else if (exposure < exposure_ctrl->qctrl.maximum) + exposure++; + else if (gain < gain_ctrl->qctrl.maximum) + gain++; + else + break; + } + } + + if (gain != orig_gain) { + gain_ctrl->set(gspca_dev, gain); + retval = 1; + } + if (exposure != orig_exposure) { + exposure_ctrl->set(gspca_dev, exposure); + retval = 1; + } + + return retval; +} +EXPORT_SYMBOL(gspca_auto_gain_n_exposure); + +/* -- module insert / remove -- */ +static int __init gspca_init(void) +{ + info("main v%s registered", version); + return 0; +} +static void __exit gspca_exit(void) +{ + info("main deregistered"); +} + +module_init(gspca_init); +module_exit(gspca_exit); + +#ifdef CONFIG_VIDEO_ADV_DEBUG +module_param_named(debug, gspca_debug, int, 0644); +MODULE_PARM_DESC(debug, + "Debug (bit) 0x01:error 0x02:probe 0x04:config" + " 0x08:stream 0x10:frame 0x20:packet 0x40:USBin 0x80:USBout" + " 0x0100: v4l2"); +#endif diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h new file mode 100644 index 00000000000..3fd2c4eee20 --- /dev/null +++ b/drivers/media/video/gspca/gspca.h @@ -0,0 +1,176 @@ +#ifndef GSPCAV2_H +#define GSPCAV2_H + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <linux/mutex.h> + +#ifdef CONFIG_VIDEO_ADV_DEBUG +/* GSPCA our debug messages */ +extern int gspca_debug; +#define PDEBUG(level, fmt, args...) \ + do {\ + if (gspca_debug & (level)) \ + printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args); \ + } while (0) +#define D_ERR 0x01 +#define D_PROBE 0x02 +#define D_CONF 0x04 +#define D_STREAM 0x08 +#define D_FRAM 0x10 +#define D_PACK 0x20 +#define D_USBI 0x40 +#define D_USBO 0x80 +#define D_V4L2 0x0100 +#else +#define PDEBUG(level, fmt, args...) +#endif +#undef err +#define err(fmt, args...) \ + do {\ + printk(KERN_ERR MODULE_NAME ": " fmt "\n", ## args); \ + } while (0) +#undef info +#define info(fmt, args...) \ + do {\ + printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args); \ + } while (0) +#undef warn +#define warn(fmt, args...) \ + do {\ + printk(KERN_WARNING MODULE_NAME ": " fmt "\n", ## args); \ + } while (0) + +#define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */ +/* ISOC transfers */ +#define MAX_NURBS 16 /* max number of URBs */ +#define ISO_MAX_PKT 32 /* max number of packets in an ISOC transfer */ +#define ISO_MAX_SIZE 0x8000 /* max size of one URB buffer (32 Kb) */ + +/* device information - set at probe time */ +struct cam { + char *dev_name; + struct v4l2_pix_format *cam_mode; /* size nmodes */ + char nmodes; + __u8 epaddr; +}; + +struct gspca_dev; +struct gspca_frame; + +/* subdriver operations */ +typedef int (*cam_op) (struct gspca_dev *); +typedef void (*cam_v_op) (struct gspca_dev *); +typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *); +typedef int (*cam_jpg_op) (struct gspca_dev *, + struct v4l2_jpegcompression *); +typedef int (*cam_qmnu_op) (struct gspca_dev *, + struct v4l2_querymenu *); +typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, + struct gspca_frame *frame, + __u8 *data, + int len); + +struct ctrl { + struct v4l2_queryctrl qctrl; + int (*set)(struct gspca_dev *, __s32); + int (*get)(struct gspca_dev *, __s32 *); +}; + +/* subdriver description */ +struct sd_desc { +/* information */ + const char *name; /* sub-driver name */ +/* controls */ + const struct ctrl *ctrls; + int nctrls; +/* operations */ + cam_cf_op config; /* called on probe */ + cam_op open; /* called on open */ + cam_v_op start; /* called on stream on */ + cam_v_op stopN; /* called on stream off - main alt */ + cam_v_op stop0; /* called on stream off - alt 0 */ + cam_v_op close; /* called on close */ + cam_pkt_op pkt_scan; +/* optional operations */ + cam_v_op dq_callback; /* called when a frame has been dequeued */ + cam_jpg_op get_jcomp; + cam_jpg_op set_jcomp; + cam_qmnu_op querymenu; +}; + +/* packet types when moving from iso buf to frame buf */ +#define DISCARD_PACKET 0 +#define FIRST_PACKET 1 +#define INTER_PACKET 2 +#define LAST_PACKET 3 + +struct gspca_frame { + __u8 *data; /* frame buffer */ + __u8 *data_end; /* end of frame while filling */ + int vma_use_count; + struct v4l2_buffer v4l2_buf; +}; + +struct gspca_dev { + struct video_device vdev; /* !! must be the first item */ + struct file_operations fops; + struct usb_device *dev; + struct file *capt_file; /* file doing video capture */ + + struct cam cam; /* device information */ + const struct sd_desc *sd_desc; /* subdriver description */ + + __u8 usb_buf[8]; /* buffer for USB exchanges */ + struct urb *urb[MAX_NURBS]; + + __u8 *frbuf; /* buffer for nframes */ + struct gspca_frame frame[GSPCA_MAX_FRAMES]; + __u32 frsz; /* frame size */ + char nframes; /* number of frames */ + char fr_i; /* frame being filled */ + char fr_q; /* next frame to queue */ + char fr_o; /* next frame to dequeue */ + signed char fr_queue[GSPCA_MAX_FRAMES]; /* frame queue */ + char last_packet_type; + + __u8 iface; /* USB interface number */ + __u8 alt; /* USB alternate setting */ + __u8 curr_mode; /* current camera mode */ + __u32 pixfmt; /* current mode parameters */ + __u16 width; + __u16 height; + + atomic_t nevent; /* number of frames done */ + wait_queue_head_t wq; /* wait queue */ + struct mutex usb_lock; /* usb exchange protection */ + struct mutex read_lock; /* read protection */ + struct mutex queue_lock; /* ISOC queue protection */ + __u32 sequence; /* frame sequence number */ + char streaming; + char users; /* number of opens */ + char present; /* device connected */ + char nbufread; /* number of buffers for read() */ + char nurbs; /* number of allocated URBs */ + char memory; /* memory type (V4L2_MEMORY_xxx) */ + __u8 nbalt; /* number of USB alternate settings */ +}; + +int gspca_dev_probe(struct usb_interface *intf, + const struct usb_device_id *id, + const struct sd_desc *sd_desc, + int dev_size, + struct module *module); +void gspca_disconnect(struct usb_interface *intf); +struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, + int packet_type, + struct gspca_frame *frame, + const __u8 *data, + int len); +int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, + int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); +#endif /* GSPCAV2_H */ diff --git a/drivers/media/video/gspca/jpeg.h b/drivers/media/video/gspca/jpeg.h new file mode 100644 index 00000000000..d823b47bd4e --- /dev/null +++ b/drivers/media/video/gspca/jpeg.h @@ -0,0 +1,301 @@ +#ifndef JPEG_H +#define JPEG_H 1 +/* + * Insert a JPEG header at start of frame + * + * This module is used by the gspca subdrivers. + * A special case is done for Conexant webcams. + * + * Copyright (C) Jean-Francois Moine (http://moinejf.free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* start of jpeg frame + quantization table */ +static const unsigned char quant[][0x88] = { +/* index 0 - Q40*/ + { + 0xff, 0xd8, /* jpeg */ + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, /* quantization table part 1 */ + 20, 14, 15, 18, 15, 13, 20, 18, 16, 18, 23, 21, 20, 24, 30, 50, + 33, 30, 28, 28, 30, 61, 44, 46, 36, 50, 73, 64, 76, 75, 71, 64, + 70, 69, 80, 90, 115, 98, 80, 85, 109, 86, 69, 70, 100, 136, 101, + 109, + 119, 123, 129, 130, 129, 78, 96, 141, 151, 140, 125, 150, 115, + 126, 129, 124, +1, /* quantization table part 2 */ + 21, 23, 23, 30, 26, 30, 59, 33, 33, 59, 124, 83, 70, 83, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124, + 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, + 124, 124, 124}, +/* index 1 - Q50 */ + { + 0xff, 0xd8, + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, + 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, 40, + 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, 57, 51, + 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, 109, 81, 87, + 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, 100, 120, 92, 101, + 103, 99, +1, + 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}, +/* index 2 Q60 */ + { + 0xff, 0xd8, + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, + 13, 9, 10, 11, 10, 8, 13, 11, 10, 11, 14, 14, 13, 15, 19, 32, + 21, 19, 18, 18, 19, 39, 28, 30, 23, 32, 46, 41, 49, 48, 46, 41, + 45, 44, 51, 58, 74, 62, 51, 54, 70, 55, 44, 45, 64, 87, 65, 70, + 76, 78, 82, 83, 82, 50, 62, 90, 97, 90, 80, 96, 74, 81, 82, 79, +1, + 14, 14, 14, 19, 17, 19, 38, 21, 21, 38, 79, 53, 45, 53, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, + 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79}, +/* index 3 - Q70 */ + { + 0xff, 0xd8, + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, + 10, 7, 7, 8, 7, 6, 10, 8, 8, 8, 11, 10, 10, 11, 14, 24, + 16, 14, 13, 13, 14, 29, 21, 22, 17, 24, 35, 31, 37, 36, 34, 31, + 34, 33, 38, 43, 55, 47, 38, 41, 52, 41, 33, 34, 48, 65, 49, 52, + 57, 59, 62, 62, 62, 37, 46, 68, 73, 67, 60, 72, 55, 61, 62, 59, +1, + 10, 11, 11, 14, 13, 14, 28, 16, 16, 28, 59, 40, 34, 40, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59}, +/* index 4 - Q80 */ + { + 0xff, 0xd8, + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, + 6, 4, 5, 6, 5, 4, 6, 6, 5, 6, 7, 7, 6, 8, 10, 16, + 10, 10, 9, 9, 10, 20, 14, 15, 12, 16, 23, 20, 24, 24, 23, 20, + 22, 22, 26, 29, 37, 31, 26, 27, 35, 28, 22, 22, 32, 44, 32, 35, + 38, 39, 41, 42, 41, 25, 31, 45, 48, 45, 40, 48, 37, 40, 41, 40, +1, + 7, 7, 7, 10, 8, 10, 19, 10, 10, 19, 40, 26, 22, 26, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}, +/* index 5 - Q85 */ + { + 0xff, 0xd8, + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, + 5, 3, 4, 4, 4, 3, 5, 4, 4, 4, 5, 5, 5, 6, 7, 12, + 8, 7, 7, 7, 7, 15, 11, 11, 9, 12, 17, 15, 18, 18, 17, 15, + 17, 17, 19, 22, 28, 23, 19, 20, 26, 21, 17, 17, 24, 33, 24, 26, + 29, 29, 31, 31, 31, 19, 23, 34, 36, 34, 30, 36, 28, 30, 31, 30, +1, + 5, 5, 5, 7, 6, 7, 14, 8, 8, 14, 30, 20, 17, 20, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, +/* index 6 - 86 */ +{ + 0xff, 0xd8, + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, + 0x04, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x04, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x05, 0x07, 0x0B, + 0x07, 0x07, 0x06, 0x06, 0x07, 0x0E, 0x0A, 0x0A, + 0x08, 0x0B, 0x10, 0x0E, 0x11, 0x11, 0x10, 0x0E, + 0x10, 0x0F, 0x12, 0x14, 0x1A, 0x16, 0x12, 0x13, + 0x18, 0x13, 0x0F, 0x10, 0x16, 0x1F, 0x17, 0x18, + 0x1B, 0x1B, 0x1D, 0x1D, 0x1D, 0x11, 0x16, 0x20, + 0x22, 0x1F, 0x1C, 0x22, 0x1A, 0x1C, 0x1D, 0x1C, +1, + 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0D, 0x07, + 0x07, 0x0D, 0x1C, 0x12, 0x10, 0x12, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + }, +/* index 7 - 88 */ +{ + 0xff, 0xd8, + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, + 0x04, 0x03, 0x03, 0x03, 0x03, 0x02, 0x04, 0x03, + 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x06, 0x0A, + 0x06, 0x06, 0x05, 0x05, 0x06, 0x0C, 0x08, 0x09, + 0x07, 0x0A, 0x0E, 0x0C, 0x0F, 0x0E, 0x0E, 0x0C, + 0x0D, 0x0D, 0x0F, 0x11, 0x16, 0x13, 0x0F, 0x10, + 0x15, 0x11, 0x0D, 0x0D, 0x13, 0x1A, 0x13, 0x15, + 0x17, 0x18, 0x19, 0x19, 0x19, 0x0F, 0x12, 0x1B, + 0x1D, 0x1B, 0x18, 0x1D, 0x16, 0x18, 0x19, 0x18, +1, + 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0B, 0x06, + 0x06, 0x0B, 0x18, 0x10, 0x0D, 0x10, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +}, +/* index 8 - ?? */ +{ + 0xff, 0xd8, + 0xff, 0xdb, 0x00, 0x84, /* DQT */ +0, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x04, 0x05, + 0x04, 0x05, 0x07, 0x06, 0x08, 0x08, 0x07, 0x06, + 0x07, 0x07, 0x08, 0x09, 0x0C, 0x0A, 0x08, 0x09, + 0x0B, 0x09, 0x07, 0x07, 0x0A, 0x0E, 0x0A, 0x0B, + 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x08, 0x0A, 0x0E, + 0x0F, 0x0E, 0x0D, 0x0F, 0x0C, 0x0D, 0x0D, 0x0C, +1, + 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x06, 0x03, + 0x03, 0x06, 0x0C, 0x08, 0x07, 0x08, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C +} +}; + +/* huffman table + start of SOF0 */ +static unsigned char huffman[] = { + 0xff, 0xc4, 0x01, 0xa2, + 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x01, 0x00, 0x03, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, + 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, + 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, + 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, + 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, + 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, + 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, + 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, + 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, + 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, + 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, + 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, + 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, + 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x11, 0x00, 0x02, + 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, + 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, + 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, + 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, + 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, + 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, + 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, + 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, + 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, + 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, +#ifdef CONEX_CAM +/* the Conexant frames start with SOF0 */ +#else + 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */ + 0x08, /* data precision */ +#endif +}; + +#ifndef CONEX_CAM +/* variable part: + * 0x01, 0xe0, height + * 0x02, 0x80, width + * 0x03, component number + * 0x01, + * 0x21, samples Y + */ + +/* end of header */ +static unsigned char eoh[] = { + 0x00, /* quant Y */ + 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */ + 0x03, 0x11, 0x01, + + 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */ + 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +}; +#endif + +/* -- output the JPEG header -- */ +static void jpeg_put_header(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, + int qindex, + int samplesY) +{ +#ifndef CONEX_CAM + unsigned char tmpbuf[8]; +#endif + + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + (unsigned char *) quant[qindex], sizeof quant[0]); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + (unsigned char *) huffman, sizeof huffman); +#ifndef CONEX_CAM + tmpbuf[0] = gspca_dev->height >> 8; + tmpbuf[1] = gspca_dev->height & 0xff; + tmpbuf[2] = gspca_dev->width >> 8; + tmpbuf[3] = gspca_dev->width & 0xff; + tmpbuf[4] = 0x03; /* component number */ + tmpbuf[5] = 0x01; /* first component */ + tmpbuf[6] = samplesY; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + tmpbuf, 7); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + eoh, sizeof eoh); +#endif +} +#endif diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c new file mode 100644 index 00000000000..88c2b02f380 --- /dev/null +++ b/drivers/media/video/gspca/mars.c @@ -0,0 +1,464 @@ +/* + * Mars-Semi MR97311A library + * Copyright (C) 2005 <bradlch@hotmail.com> + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "mars" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + char qindex; +}; + +/* V4L2 controls supported by the driver */ +static struct ctrl sd_ctrls[] = { +}; + +static struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 589, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +}; + +/* MI Register table //elvis */ +enum { + REG_HW_MI_0, + REG_HW_MI_1, + REG_HW_MI_2, + REG_HW_MI_3, + REG_HW_MI_4, + REG_HW_MI_5, + REG_HW_MI_6, + REG_HW_MI_7, + REG_HW_MI_9 = 0x09, + REG_HW_MI_B = 0x0B, + REG_HW_MI_C, + REG_HW_MI_D, + REG_HW_MI_1E = 0x1E, + REG_HW_MI_20 = 0x20, + REG_HW_MI_2B = 0x2B, + REG_HW_MI_2C, + REG_HW_MI_2D, + REG_HW_MI_2E, + REG_HW_MI_35 = 0x35, + REG_HW_MI_5F = 0x5f, + REG_HW_MI_60, + REG_HW_MI_61, + REG_HW_MI_62, + REG_HW_MI_63, + REG_HW_MI_64, + REG_HW_MI_F1 = 0xf1, + ATTR_TOTAL_MI_REG = 0xf2 +}; + +/* the bytes to write are in gspca_dev->usb_buf */ +static int reg_w(struct gspca_dev *gspca_dev, + __u16 index, int len) +{ + int rc; + + rc = usb_control_msg(gspca_dev->dev, + usb_sndbulkpipe(gspca_dev->dev, 4), + 0x12, + 0xc8, /* ?? */ + 0, /* value */ + index, gspca_dev->usb_buf, len, 500); + if (rc < 0) + PDEBUG(D_ERR, "reg write [%02x] error %d", index, rc); + return rc; +} + +static int reg_w_buf(struct gspca_dev *gspca_dev, + __u16 index, __u8 *buf, int len) +{ + int rc; + + rc = usb_control_msg(gspca_dev->dev, + usb_sndbulkpipe(gspca_dev->dev, 4), + 0x12, + 0xc8, /* ?? */ + 0, /* value */ + index, buf, len, 500); + if (rc < 0) + PDEBUG(D_ERR, "reg write [%02x] error %d", index, rc); + return rc; +} + +static void bulk_w(struct gspca_dev *gspca_dev, + __u16 *pch, + __u16 Address) +{ + gspca_dev->usb_buf[0] = 0x1f; + gspca_dev->usb_buf[1] = 0; /* control byte */ + gspca_dev->usb_buf[2] = Address; + gspca_dev->usb_buf[3] = *pch >> 8; /* high byte */ + gspca_dev->usb_buf[4] = *pch; /* low byte */ + + reg_w(gspca_dev, Address, 5); +} + +/* 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; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + sd->qindex = 1; /* set the quantization table */ + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + int err_code; + __u8 *data; + __u16 *MI_buf; + int h_size, v_size; + int intpipe; + + PDEBUG(D_STREAM, "camera start, iface %d, alt 8", gspca_dev->iface); + if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 8) < 0) { + PDEBUG(D_ERR|D_STREAM, "Set packet size: set interface error"); + return; + } + + data = gspca_dev->usb_buf; + data[0] = 0x01; /* address */ + data[1] = 0x01; + + err_code = reg_w(gspca_dev, data[0], 2); + if (err_code < 0) + return; + + /* + Initialize the MR97113 chip register + */ + data = kmalloc(16, GFP_KERNEL); + data[0] = 0x00; /* address */ + data[1] = 0x0c | 0x01; /* reg 0 */ + data[2] = 0x01; /* reg 1 */ + h_size = gspca_dev->width; + v_size = gspca_dev->height; + data[3] = h_size / 8; /* h_size , reg 2 */ + data[4] = v_size / 8; /* v_size , reg 3 */ + data[5] = 0x30; /* reg 4, MI, PAS5101 : + * 0x30 for 24mhz , 0x28 for 12mhz */ + data[6] = 4; /* reg 5, H start */ + data[7] = 0xc0; /* reg 6, gamma 1.5 */ + data[8] = 3; /* reg 7, V start */ +/* if (h_size == 320 ) */ +/* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */ +/* else */ + data[9] = 0x52; /* reg 8, 24MHz, no scale down */ + data[10] = 0x5d; /* reg 9, I2C device address + * [for PAS5101 (0x40)] [for MI (0x5d)] */ + + err_code = reg_w_buf(gspca_dev, data[0], data, 11); + kfree(data); + if (err_code < 0) + return; + + data = gspca_dev->usb_buf; + data[0] = 0x23; /* address */ + data[1] = 0x09; /* reg 35, append frame header */ + + err_code = reg_w(gspca_dev, data[0], 2); + if (err_code < 0) + return; + + data[0] = 0x3c; /* address */ +/* if (gspca_dev->width == 1280) */ +/* data[1] = 200; * reg 60, pc-cam frame size + * (unit: 4KB) 800KB */ +/* else */ + data[1] = 50; /* 50 reg 60, pc-cam frame size + * (unit: 4KB) 200KB */ + err_code = reg_w(gspca_dev, data[0], 2); + if (err_code < 0) + return; + + if (0) { /* fixed dark-gain */ + data[1] = 0; /* reg 94, Y Gain (1.75) */ + data[2] = 0; /* reg 95, UV Gain (1.75) */ + data[3] = 0x3f; /* reg 96, Y Gain/UV Gain/disable + * auto dark-gain */ + data[4] = 0; /* reg 97, set fixed dark level */ + data[5] = 0; /* reg 98, don't care */ + } else { /* auto dark-gain */ + data[1] = 0; /* reg 94, Y Gain (auto) */ + data[2] = 0; /* reg 95, UV Gain (1.75) */ + data[3] = 0x78; /* reg 96, Y Gain/UV Gain/disable + * auto dark-gain */ + switch (gspca_dev->width) { +/* case 1280: */ +/* data[4] = 154; + * reg 97, %3 shadow point (unit: 256 pixel) */ +/* data[5] = 51; + * reg 98, %1 highlight point + * (uint: 256 pixel) */ +/* break; */ + default: +/* case 640: */ + data[4] = 36; /* reg 97, %3 shadow point + * (unit: 256 pixel) */ + data[5] = 12; /* reg 98, %1 highlight point + * (uint: 256 pixel) */ + break; + case 320: + data[4] = 9; /* reg 97, %3 shadow point + * (unit: 256 pixel) */ + data[5] = 3; /* reg 98, %1 highlight point + * (uint: 256 pixel) */ + break; + } + } + /* auto dark-gain */ + data[0] = 0x5e; /* address */ + + err_code = reg_w(gspca_dev, data[0], 6); + if (err_code < 0) + return; + + data[0] = 0x67; + data[1] = 0x13; /* reg 103, first pixel B, disable sharpness */ + err_code = reg_w(gspca_dev, data[0], 2); + if (err_code < 0) + return; + + /* + * initialize the value of MI sensor... + */ + MI_buf = kzalloc(ATTR_TOTAL_MI_REG * sizeof *MI_buf, GFP_KERNEL); + MI_buf[REG_HW_MI_1] = 0x000a; + MI_buf[REG_HW_MI_2] = 0x000c; + MI_buf[REG_HW_MI_3] = 0x0405; + MI_buf[REG_HW_MI_4] = 0x0507; + /* mi_Attr_Reg_[REG_HW_MI_5] = 0x01ff;//13 */ + MI_buf[REG_HW_MI_5] = 0x0013; /* 13 */ + MI_buf[REG_HW_MI_6] = 0x001f; /* vertical blanking */ + /* mi_Attr_Reg_[REG_HW_MI_6] = 0x0400; // vertical blanking */ + MI_buf[REG_HW_MI_7] = 0x0002; + /* mi_Attr_Reg_[REG_HW_MI_9] = 0x015f; */ + /* mi_Attr_Reg_[REG_HW_MI_9] = 0x030f; */ + MI_buf[REG_HW_MI_9] = 0x0374; + MI_buf[REG_HW_MI_B] = 0x0000; + MI_buf[REG_HW_MI_C] = 0x0000; + MI_buf[REG_HW_MI_D] = 0x0000; + MI_buf[REG_HW_MI_1E] = 0x8000; +/* mi_Attr_Reg_[REG_HW_MI_20] = 0x1104; */ + MI_buf[REG_HW_MI_20] = 0x1104; /* 0x111c; */ + MI_buf[REG_HW_MI_2B] = 0x0008; +/* mi_Attr_Reg_[REG_HW_MI_2C] = 0x000f; */ + MI_buf[REG_HW_MI_2C] = 0x001f; /* lita suggest */ + MI_buf[REG_HW_MI_2D] = 0x0008; + MI_buf[REG_HW_MI_2E] = 0x0008; + MI_buf[REG_HW_MI_35] = 0x0051; + MI_buf[REG_HW_MI_5F] = 0x0904; /* fail to write */ + MI_buf[REG_HW_MI_60] = 0x0000; + MI_buf[REG_HW_MI_61] = 0x0000; + MI_buf[REG_HW_MI_62] = 0x0498; + MI_buf[REG_HW_MI_63] = 0x0000; + MI_buf[REG_HW_MI_64] = 0x0000; + MI_buf[REG_HW_MI_F1] = 0x0001; + /* changing while setting up the different value of dx/dy */ + + if (gspca_dev->width != 1280) { + MI_buf[0x01] = 0x010a; + MI_buf[0x02] = 0x014c; + MI_buf[0x03] = 0x01e5; + MI_buf[0x04] = 0x0287; + } + MI_buf[0x20] = 0x1104; + + bulk_w(gspca_dev, MI_buf + 1, 1); + bulk_w(gspca_dev, MI_buf + 2, 2); + bulk_w(gspca_dev, MI_buf + 3, 3); + bulk_w(gspca_dev, MI_buf + 4, 4); + bulk_w(gspca_dev, MI_buf + 5, 5); + bulk_w(gspca_dev, MI_buf + 6, 6); + bulk_w(gspca_dev, MI_buf + 7, 7); + bulk_w(gspca_dev, MI_buf + 9, 9); + bulk_w(gspca_dev, MI_buf + 0x0b, 0x0b); + bulk_w(gspca_dev, MI_buf + 0x0c, 0x0c); + bulk_w(gspca_dev, MI_buf + 0x0d, 0x0d); + bulk_w(gspca_dev, MI_buf + 0x1e, 0x1e); + bulk_w(gspca_dev, MI_buf + 0x20, 0x20); + bulk_w(gspca_dev, MI_buf + 0x2b, 0x2b); + bulk_w(gspca_dev, MI_buf + 0x2c, 0x2c); + bulk_w(gspca_dev, MI_buf + 0x2d, 0x2d); + bulk_w(gspca_dev, MI_buf + 0x2e, 0x2e); + bulk_w(gspca_dev, MI_buf + 0x35, 0x35); + bulk_w(gspca_dev, MI_buf + 0x5f, 0x5f); + bulk_w(gspca_dev, MI_buf + 0x60, 0x60); + bulk_w(gspca_dev, MI_buf + 0x61, 0x61); + bulk_w(gspca_dev, MI_buf + 0x62, 0x62); + bulk_w(gspca_dev, MI_buf + 0x63, 0x63); + bulk_w(gspca_dev, MI_buf + 0x64, 0x64); + bulk_w(gspca_dev, MI_buf + 0xf1, 0xf1); + kfree(MI_buf); + + intpipe = usb_sndintpipe(gspca_dev->dev, 0); + err_code = usb_clear_halt(gspca_dev->dev, intpipe); + + data[0] = 0x00; + data[1] = 0x4d; /* ISOC transfering enable... */ + reg_w(gspca_dev, data[0], 2); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int result; + + gspca_dev->usb_buf[0] = 1; + gspca_dev->usb_buf[1] = 0; + result = reg_w(gspca_dev, gspca_dev->usb_buf[0], 2); + if (result < 0) + PDEBUG(D_ERR, "Camera Stop failed"); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int p; + + if (len < 6) { +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + for (p = 0; p < len - 6; p++) { + if (data[0 + p] == 0xff + && data[1 + p] == 0xff + && data[2 + p] == 0x00 + && data[3 + p] == 0xff + && data[4 + p] == 0x96) { + if (data[5 + p] == 0x64 + || data[5 + p] == 0x65 + || data[5 + p] == 0x66 + || data[5 + p] == 0x67) { + PDEBUG(D_PACK, "sof offset: %d leng: %d", + p, len); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, 0); + + /* put the JPEG header */ + jpeg_put_header(gspca_dev, frame, + sd->qindex, 0x21); + data += 16; + len -= 16; + break; + } + } + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x093a, 0x050f), DVNM("Mars-Semi Pc-Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c new file mode 100644 index 00000000000..08d99c3b78e --- /dev/null +++ b/drivers/media/video/gspca/ov519.c @@ -0,0 +1,2186 @@ +/** + * OV519 driver + * + * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) + * + * (This module is adapted from the ov51x-jpeg package) + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define MODULE_NAME "ov519" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); +MODULE_DESCRIPTION("OV519 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* global parameters */ +static int frame_rate; + +/* Number of times to retry a failed I2C transaction. Increase this if you + * are getting "Failed to read sensor ID..." */ +static int i2c_detect_tries = 10; + +/* ov519 device descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + /* Determined by sensor type */ + short maxwidth; + short maxheight; + + unsigned char primary_i2c_slave; /* I2C write id of sensor */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + + char compress; /* Should the next frame be compressed? */ + char compress_inited; /* Are compression params uploaded? */ + char stopped; /* Streaming is temporarily paused */ + + char frame_rate; /* current Framerate (OV519 only) */ + char clockdiv; /* clockdiv override for OV519 only */ + + char sensor; /* Type of image sensor chip (SEN_*) */ +#define SEN_UNKNOWN 0 +#define SEN_OV6620 1 +#define SEN_OV6630 2 +#define SEN_OV7610 3 +#define SEN_OV7620 4 +#define SEN_OV7630 5 +#define SEN_OV7640 6 +#define SEN_OV7670 7 +#define SEN_OV76BE 8 +#define SEN_OV8610 9 + +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(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 = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 589, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; +static struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 589, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 589, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* OV519 Camera interface register numbers */ +#define OV519_CAM_H_SIZE 0x10 +#define OV519_CAM_V_SIZE 0x11 +#define OV519_CAM_X_OFFSETL 0x12 +#define OV519_CAM_X_OFFSETH 0x13 +#define OV519_CAM_Y_OFFSETL 0x14 +#define OV519_CAM_Y_OFFSETH 0x15 +#define OV519_CAM_DIVIDER 0x16 +#define OV519_CAM_DFR 0x20 +#define OV519_CAM_FORMAT 0x25 + +/* OV519 System Controller register numbers */ +#define OV519_SYS_RESET1 0x51 +#define OV519_SYS_EN_CLK1 0x54 + +#define OV519_GPIO_DATA_OUT0 0x71 +#define OV519_GPIO_IO_CTRL0 0x72 + +#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ + +/* I2C registers */ +#define R51x_I2C_W_SID 0x41 +#define R51x_I2C_SADDR_3 0x42 +#define R51x_I2C_SADDR_2 0x43 +#define R51x_I2C_R_SID 0x44 +#define R51x_I2C_DATA 0x45 +#define R518_I2C_CTL 0x47 /* OV518(+) only */ + +/* I2C ADDRESSES */ +#define OV7xx0_SID 0x42 +#define OV8xx0_SID 0xa0 +#define OV6xx0_SID 0xc0 + +/* OV7610 registers */ +#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ +#define OV7610_REG_SAT 0x03 /* saturation */ +#define OV8610_REG_HUE 0x04 /* 04 reserved */ +#define OV7610_REG_CNT 0x05 /* Y contrast */ +#define OV7610_REG_BRT 0x06 /* Y brightness */ +#define OV7610_REG_COM_C 0x14 /* misc common regs */ +#define OV7610_REG_ID_HIGH 0x1c /* manufacturer ID MSB */ +#define OV7610_REG_ID_LOW 0x1d /* manufacturer ID LSB */ +#define OV7610_REG_COM_I 0x29 /* misc settings */ + +/* OV7670 registers */ +#define OV7670_REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define OV7670_REG_BLUE 0x01 /* blue gain */ +#define OV7670_REG_RED 0x02 /* red gain */ +#define OV7670_REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define OV7670_REG_COM1 0x04 /* Control 1 */ +#define OV7670_REG_AECHH 0x07 /* AEC MS 5 bits */ +#define OV7670_REG_COM3 0x0c /* Control 3 */ +#define OV7670_REG_COM4 0x0d /* Control 4 */ +#define OV7670_REG_COM5 0x0e /* All "reserved" */ +#define OV7670_REG_COM6 0x0f /* Control 6 */ +#define OV7670_REG_AECH 0x10 /* More bits of AEC value */ +#define OV7670_REG_CLKRC 0x11 /* Clock control */ +#define OV7670_REG_COM7 0x12 /* Control 7 */ +#define OV7670_COM7_FMT_VGA 0x00 +#define OV7670_COM7_YUV 0x00 /* YUV */ +#define OV7670_COM7_FMT_QVGA 0x10 /* QVGA format */ +#define OV7670_COM7_FMT_MASK 0x38 +#define OV7670_COM7_RESET 0x80 /* Register reset */ +#define OV7670_REG_COM8 0x13 /* Control 8 */ +#define OV7670_COM8_AEC 0x01 /* Auto exposure enable */ +#define OV7670_COM8_AWB 0x02 /* White balance enable */ +#define OV7670_COM8_AGC 0x04 /* Auto gain enable */ +#define OV7670_COM8_BFILT 0x20 /* Band filter enable */ +#define OV7670_COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define OV7670_COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define OV7670_REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define OV7670_REG_COM10 0x15 /* Control 10 */ +#define OV7670_REG_HSTART 0x17 /* Horiz start high bits */ +#define OV7670_REG_HSTOP 0x18 /* Horiz stop high bits */ +#define OV7670_REG_VSTART 0x19 /* Vert start high bits */ +#define OV7670_REG_VSTOP 0x1a /* Vert stop high bits */ +#define OV7670_REG_MVFP 0x1e /* Mirror / vflip */ +#define OV7670_MVFP_MIRROR 0x20 /* Mirror image */ +#define OV7670_REG_AEW 0x24 /* AGC upper limit */ +#define OV7670_REG_AEB 0x25 /* AGC lower limit */ +#define OV7670_REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define OV7670_REG_HREF 0x32 /* HREF pieces */ +#define OV7670_REG_TSLB 0x3a /* lots of stuff */ +#define OV7670_REG_COM11 0x3b /* Control 11 */ +#define OV7670_COM11_EXP 0x02 +#define OV7670_COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define OV7670_REG_COM12 0x3c /* Control 12 */ +#define OV7670_REG_COM13 0x3d /* Control 13 */ +#define OV7670_COM13_GAMMA 0x80 /* Gamma enable */ +#define OV7670_COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define OV7670_REG_COM14 0x3e /* Control 14 */ +#define OV7670_REG_EDGE 0x3f /* Edge enhancement factor */ +#define OV7670_REG_COM15 0x40 /* Control 15 */ +#define OV7670_COM15_R00FF 0xc0 /* 00 to FF */ +#define OV7670_REG_COM16 0x41 /* Control 16 */ +#define OV7670_COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define OV7670_REG_BRIGHT 0x55 /* Brightness */ +#define OV7670_REG_CONTRAS 0x56 /* Contrast control */ +#define OV7670_REG_GFIX 0x69 /* Fix gain control */ +#define OV7670_REG_RGB444 0x8c /* RGB 444 control */ +#define OV7670_REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define OV7670_REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ +#define OV7670_REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define OV7670_REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define OV7670_REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define OV7670_REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define OV7670_REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define OV7670_REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define OV7670_REG_BD60MAX 0xab /* 60hz banding step limit */ + +struct ovsensor_window { + short x; + short y; + short width; + short height; +/* int format; */ + short quarter; /* Scale width and height down 2x */ + short clockdiv; /* Clock divisor setting */ +}; + +static unsigned char ov7670_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + return (128 - v) | 0x80; +} + +/* Write a OV519 register */ +static int reg_w(struct sd *sd, __u16 index, __u8 value) +{ + int ret; + + sd->gspca_dev.usb_buf[0] = value; + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 1, /* REQ_IO (ov518/519) */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, + sd->gspca_dev.usb_buf, 1, 500); + if (ret < 0) + PDEBUG(D_ERR, "Write reg [%02x] %02x failed", index, value); + return ret; +} + +/* Read from a OV519 register */ +/* returns: negative is error, pos or zero is data */ +static int reg_r(struct sd *sd, __u16 index) +{ + int ret; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 1, /* REQ_IO */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, sd->gspca_dev.usb_buf, 1, 500); + + if (ret >= 0) + ret = sd->gspca_dev.usb_buf[0]; + else + PDEBUG(D_ERR, "Read reg [0x%02x] failed", index); + return ret; +} + +/* Read 8 values from a OV519 register */ +static int reg_r8(struct sd *sd, + __u16 index) +{ + int ret; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 1, /* REQ_IO */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, sd->gspca_dev.usb_buf, 8, 500); + + if (ret >= 0) + ret = sd->gspca_dev.usb_buf[0]; + else + PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index); + return ret; +} + +/* + * Writes bits at positions specified by mask to an OV51x reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static int reg_w_mask(struct sd *sd, + __u16 index, + __u8 value, + __u8 mask) +{ + int ret; + __u8 oldval; + + if (mask != 0xff) { + value &= mask; /* Enforce mask on value */ + ret = reg_r(sd, index); + if (ret < 0) + return ret; + + oldval = ret & ~mask; /* Clear the masked bits */ + value |= oldval; /* Set the desired bits */ + } + return reg_w(sd, index, value); +} + +/* + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_w(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int i2c_w(struct sd *sd, + __u8 reg, + __u8 value) +{ + int rc; + + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_3, reg); + if (rc < 0) + return rc; + + /* Write "value" to I2C data port of OV511 */ + rc = reg_w(sd, R51x_I2C_DATA, value); + if (rc < 0) + return rc; + + /* Initiate 3-byte write cycle */ + rc = reg_w(sd, R518_I2C_CTL, 0x01); + + /* wait for write complete */ + msleep(4); + if (rc < 0) + return rc; + return reg_r8(sd, R518_I2C_CTL); +} + +/* + * returns: negative is error, pos or zero is data + * + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_r(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int i2c_r(struct sd *sd, __u8 reg) +{ + int rc, value; + + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_2, reg); + if (rc < 0) + return rc; + + /* Initiate 2-byte write cycle */ + rc = reg_w(sd, R518_I2C_CTL, 0x03); + if (rc < 0) + return rc; + + /* Initiate 2-byte read cycle */ + rc = reg_w(sd, R518_I2C_CTL, 0x05); + if (rc < 0) + return rc; + value = reg_r(sd, R51x_I2C_DATA); + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); + return value; +} + +/* Writes bits at positions specified by mask to an I2C reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static int i2c_w_mask(struct sd *sd, + __u8 reg, + __u8 value, + __u8 mask) +{ + int rc; + __u8 oldval; + + value &= mask; /* Enforce mask on value */ + rc = i2c_r(sd, reg); + if (rc < 0) + return rc; + oldval = rc & ~mask; /* Clear the masked bits */ + value |= oldval; /* Set the desired bits */ + return i2c_w(sd, reg, value); +} + +/* Temporarily stops OV511 from functioning. Must do this before changing + * registers while the camera is streaming */ +static inline int ov51x_stop(struct sd *sd) +{ + PDEBUG(D_STREAM, "stopping"); + sd->stopped = 1; + return reg_w(sd, OV519_SYS_RESET1, 0x0f); +} + +/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not + * actually stopped (for performance). */ +static inline int ov51x_restart(struct sd *sd) +{ + PDEBUG(D_STREAM, "restarting"); + if (!sd->stopped) + return 0; + sd->stopped = 0; + + /* Reinitialize the stream */ + return reg_w(sd, OV519_SYS_RESET1, 0x00); +} + +/* This does an initial reset of an OmniVision sensor and ensures that I2C + * is synchronized. Returns <0 on failure. + */ +static int init_ov_sensor(struct sd *sd) +{ + int i, success; + + /* Reset the sensor */ + if (i2c_w(sd, 0x12, 0x80) < 0) + return -EIO; + + /* Wait for it to initialize */ + msleep(150); + + for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { + if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f && + i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) { + success = 1; + continue; + } + + /* Reset the sensor */ + if (i2c_w(sd, 0x12, 0x80) < 0) + return -EIO; + /* Wait for it to initialize */ + msleep(150); + /* Dummy read to sync I2C */ + if (i2c_r(sd, 0x00) < 0) + return -EIO; + } + if (!success) + return -EIO; + PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i); + return 0; +} + +/* Switch on standard JPEG compression. Returns 0 for success. */ +static int ov519_init_compression(struct sd *sd) +{ + if (!sd->compress_inited) { + if (reg_w_mask(sd, OV519_SYS_EN_CLK1, 1 << 2, 1 << 2) < 0) { + PDEBUG(D_ERR, "Error switching to compressed mode"); + return -EIO; + } + sd->compress_inited = 1; + } + return 0; +} + +/* Set the read and write slave IDs. The "slave" argument is the write slave, + * and the read slave will be set to (slave + 1). + * This should not be called from outside the i2c I/O functions. + * Sets I2C read and write slave IDs. Returns <0 for error + */ +static int ov51x_set_slave_ids(struct sd *sd, + __u8 slave) +{ + int rc; + + rc = reg_w(sd, R51x_I2C_W_SID, slave); + if (rc < 0) + return rc; + return reg_w(sd, R51x_I2C_R_SID, slave + 1); +} + +struct ov_regvals { + __u8 reg; + __u8 val; +}; +struct ov_i2c_regvals { + __u8 reg; + __u8 val; +}; + +static int write_regvals(struct sd *sd, + const struct ov_regvals *regvals, + int n) +{ + int rc; + + while (--n >= 0) { + rc = reg_w(sd, regvals->reg, regvals->val); + if (rc < 0) + return rc; + regvals++; + } + return 0; +} + +static int write_i2c_regvals(struct sd *sd, + const struct ov_i2c_regvals *regvals, + int n) +{ + int rc; + + while (--n >= 0) { + rc = i2c_w(sd, regvals->reg, regvals->val); + if (rc < 0) + return rc; + regvals++; + } + return 0; +} + +/**************************************************************************** + * + * OV511 and sensor configuration + * + ***************************************************************************/ + +/* This initializes the OV8110, OV8610 sensor. The OV8110 uses + * the same register settings as the OV8610, since they are very similar. + */ +static int ov8xx0_configure(struct sd *sd) +{ + int rc; + static const struct ov_i2c_regvals norm_8610[] = { + { 0x12, 0x80 }, + { 0x00, 0x00 }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, 0xc0 }, + { 0x04, 0x30 }, + { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */ + { 0x0a, 0x86 }, + { 0x0b, 0xb0 }, + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x11, 0x01 }, + { 0x12, 0x25 }, + { 0x13, 0x01 }, + { 0x14, 0x04 }, + { 0x15, 0x01 }, /* Lin and Win think different about UV order */ + { 0x16, 0x03 }, + { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */ + { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */ + { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */ + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */ + { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */ + { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */ + { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */ + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x80 }, + { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */ + { 0x2c, 0xac }, + { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */ + { 0x2e, 0x80 }, + { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */ + { 0x4c, 0x00 }, + { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */ + { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */ + { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */ + { 0x63, 0xff }, + { 0x64, 0x53 }, /* new windrv 090403 says 0x57, + * maybe thats wrong */ + { 0x65, 0x00 }, + { 0x66, 0x55 }, + { 0x67, 0xb0 }, + { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */ + { 0x69, 0x02 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but + deleting bit7 colors the first images red */ + { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6f, 0x01 }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */ + { 0x75, 0x0e }, + { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */ + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */ + { 0x7c, 0x00 }, + { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */ + { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */ + { 0x7f, 0xfb }, + { 0x80, 0x28 }, + { 0x81, 0x00 }, + { 0x82, 0x23 }, + { 0x83, 0x0b }, + { 0x84, 0x00 }, + { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */ + { 0x86, 0xc9 }, + { 0x87, 0x00 }, + { 0x88, 0x00 }, + { 0x89, 0x01 }, + { 0x12, 0x20 }, + { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */ + }; + + PDEBUG(D_PROBE, "starting ov8xx0 configuration"); + + if (init_ov_sensor(sd) < 0) + PDEBUG(D_ERR|D_PROBE, "Failed to read sensor ID"); + else + PDEBUG(D_PROBE, "OV86x0 initialized"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + if (rc < 0) { + PDEBUG(D_ERR, "Error detecting sensor type"); + return -1; + } + if ((rc & 3) == 1) { + PDEBUG(D_PROBE, "Sensor is an OV8610"); + sd->sensor = SEN_OV8610; + } else { + PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3); + return -1; + } + PDEBUG(D_PROBE, "Writing 8610 registers"); + if (write_i2c_regvals(sd, + norm_8610, + sizeof norm_8610 / sizeof norm_8610[0])) + return -1; + + /* Set sensor-specific vars */ + sd->maxwidth = 640; + sd->maxheight = 480; + return 0; +} + +/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses + * the same register settings as the OV7610, since they are very similar. + */ +static int ov7xx0_configure(struct sd *sd) +{ + int rc, high, low; + + /* Lawrence Glaister <lg@jfm.bc.ca> reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ + static const struct ov_i2c_regvals norm_7610[] = { + { 0x10, 0xff }, + { 0x16, 0x06 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + }; + + static const struct ov_i2c_regvals norm_7620[] = { + { 0x00, 0x00 }, /* gain */ + { 0x01, 0x80 }, /* blue gain */ + { 0x02, 0x80 }, /* red gain */ + { 0x03, 0xc0 }, /* OV7670_REG_VREF */ + { 0x06, 0x60 }, + { 0x07, 0x00 }, + { 0x0c, 0x24 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x11, 0x01 }, + { 0x12, 0x24 }, + { 0x13, 0x01 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x18 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x20 }, + { 0x29, 0x00 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x91 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x27 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x02 }, + { 0x6d, 0x44 }, + { 0x6e, 0x80 }, + { 0x6f, 0x1d }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe2 }, + { 0x7c, 0x00 }, + }; + + /* 7640 and 7648. The defaults should be OK for most registers. */ + static const struct ov_i2c_regvals norm_7640[] = { + { 0x12, 0x80 }, + { 0x12, 0x14 }, + }; + + /* 7670. Defaults taken from OmniVision provided data, + * as provided by Jonathan Corbet of OLPC */ + static const struct ov_i2c_regvals norm_7670[] = { + { OV7670_REG_COM7, OV7670_COM7_RESET }, + { OV7670_REG_TSLB, 0x04 }, /* OV */ + { OV7670_REG_COM7, OV7670_COM7_FMT_VGA }, /* VGA */ + { OV7670_REG_CLKRC, 0x1 }, + /* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { OV7670_REG_HSTART, 0x13 }, { OV7670_REG_HSTOP, 0x01 }, + { OV7670_REG_HREF, 0xb6 }, { OV7670_REG_VSTART, 0x02 }, + { OV7670_REG_VSTOP, 0x7a }, { OV7670_REG_VREF, 0x0a }, + + { OV7670_REG_COM3, 0 }, { OV7670_REG_COM14, 0 }, + /* Mystery scaling numbers */ + { 0x70, 0x3a }, { 0x71, 0x35 }, + { 0x72, 0x11 }, { 0x73, 0xf0 }, + { 0xa2, 0x02 }, +/* jfm */ +/* { OV7670_REG_COM10, 0x0 }, */ + + /* Gamma curve values */ + { 0x7a, 0x20 }, +/* jfm:win 7b=1c */ + { 0x7b, 0x10 }, +/* jfm:win 7c=28 */ + { 0x7c, 0x1e }, +/* jfm:win 7d=3c */ + { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, + + /* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT }, + { OV7670_REG_GAIN, 0 }, { OV7670_REG_AECH, 0 }, + { OV7670_REG_COM4, 0x40 }, /* magic reserved bit */ +/* jfm:win 14=38 */ + { OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { OV7670_REG_BD50MAX, 0x05 }, { OV7670_REG_BD60MAX, 0x07 }, + { OV7670_REG_AEW, 0x95 }, { OV7670_REG_AEB, 0x33 }, + { OV7670_REG_VPT, 0xe3 }, { OV7670_REG_HAECC1, 0x78 }, + { OV7670_REG_HAECC2, 0x68 }, +/* jfm:win a1=0b */ + { 0xa1, 0x03 }, /* magic */ + { OV7670_REG_HAECC3, 0xd8 }, { OV7670_REG_HAECC4, 0xd8 }, + { OV7670_REG_HAECC5, 0xf0 }, { OV7670_REG_HAECC6, 0x90 }, + { OV7670_REG_HAECC7, 0x94 }, + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC }, + + /* Almost all of these are magic "reserved" values. */ + { OV7670_REG_COM5, 0x61 }, { OV7670_REG_COM6, 0x4b }, + { 0x16, 0x02 }, +/* jfm */ +/* { OV7670_REG_MVFP, 0x07|OV7670_MVFP_MIRROR }, */ + { OV7670_REG_MVFP, 0x07 }, + { 0x21, 0x02 }, { 0x22, 0x91 }, + { 0x29, 0x07 }, { 0x33, 0x0b }, + { 0x35, 0x0b }, { 0x37, 0x1d }, + { 0x38, 0x71 }, { 0x39, 0x2a }, + { OV7670_REG_COM12, 0x78 }, { 0x4d, 0x40 }, + { 0x4e, 0x20 }, { OV7670_REG_GFIX, 0 }, + { 0x6b, 0x4a }, { 0x74, 0x10 }, + { 0x8d, 0x4f }, { 0x8e, 0 }, + { 0x8f, 0 }, { 0x90, 0 }, + { 0x91, 0 }, { 0x96, 0 }, + { 0x9a, 0 }, { 0xb0, 0x84 }, + { 0xb1, 0x0c }, { 0xb2, 0x0e }, + { 0xb3, 0x82 }, { 0xb8, 0x0a }, + + /* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, { 0x44, 0xf0 }, + { 0x45, 0x34 }, { 0x46, 0x58 }, + { 0x47, 0x28 }, { 0x48, 0x3a }, + { 0x59, 0x88 }, { 0x5a, 0x88 }, + { 0x5b, 0x44 }, { 0x5c, 0x67 }, + { 0x5d, 0x49 }, { 0x5e, 0x0e }, + { 0x6c, 0x0a }, { 0x6d, 0x55 }, + { 0x6e, 0x11 }, { 0x6f, 0x9f }, + /* "9e for advance AWB" */ + { 0x6a, 0x40 }, { OV7670_REG_BLUE, 0x40 }, + { OV7670_REG_RED, 0x60 }, + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC + | OV7670_COM8_AWB }, + + /* Matrix coefficients */ + { 0x4f, 0x80 }, { 0x50, 0x80 }, + { 0x51, 0 }, { 0x52, 0x22 }, + { 0x53, 0x5e }, { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { OV7670_REG_COM16, OV7670_COM16_AWBGAIN }, + { OV7670_REG_EDGE, 0 }, + { 0x75, 0x05 }, { 0x76, 0xe1 }, + { 0x4c, 0 }, { 0x77, 0x01 }, + { OV7670_REG_COM13, 0xc3 }, { 0x4b, 0x09 }, + { 0xc9, 0x60 }, { OV7670_REG_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, + { OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO }, + { 0xa4, 0x88 }, { 0x96, 0 }, + { 0x97, 0x30 }, { 0x98, 0x20 }, + { 0x99, 0x30 }, { 0x9a, 0x84 }, + { 0x9b, 0x29 }, { 0x9c, 0x03 }, + { 0x9d, 0x4c }, { 0x9e, 0x3f }, + { 0x78, 0x04 }, + + /* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, { 0xc8, 0xf0 }, + { 0x79, 0x0f }, { 0xc8, 0x00 }, + { 0x79, 0x10 }, { 0xc8, 0x7e }, + { 0x79, 0x0a }, { 0xc8, 0x80 }, + { 0x79, 0x0b }, { 0xc8, 0x01 }, + { 0x79, 0x0c }, { 0xc8, 0x0f }, + { 0x79, 0x0d }, { 0xc8, 0x20 }, + { 0x79, 0x09 }, { 0xc8, 0x80 }, + { 0x79, 0x02 }, { 0xc8, 0xc0 }, + { 0x79, 0x03 }, { 0xc8, 0x40 }, + { 0x79, 0x05 }, { 0xc8, 0x30 }, + { 0x79, 0x26 }, + + /* Format YUV422 */ + { OV7670_REG_COM7, OV7670_COM7_YUV }, /* Selects YUV mode */ + { OV7670_REG_RGB444, 0 }, /* No RGB444 please */ + { OV7670_REG_COM1, 0 }, + { OV7670_REG_COM15, OV7670_COM15_R00FF }, + { OV7670_REG_COM9, 0x18 }, + /* 4x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + { OV7670_REG_COM13, OV7670_COM13_GAMMA|OV7670_COM13_UVSAT }, +}; + + PDEBUG(D_PROBE, "starting OV7xx0 configuration"); + +/* jfm:already done? */ + if (init_ov_sensor(sd) < 0) + PDEBUG(D_ERR, "Failed to read sensor ID"); + else + PDEBUG(D_PROBE, "OV7xx0 initialized"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + + /* add OV7670 here + * it appears to be wrongly detected as a 7610 by default */ + if (rc < 0) { + PDEBUG(D_ERR, "Error detecting sensor type"); + return -1; + } + if ((rc & 3) == 3) { + /* quick hack to make OV7670s work */ + high = i2c_r(sd, 0x0a); + low = i2c_r(sd, 0x0b); + /* info("%x, %x", high, low); */ + if (high == 0x76 && low == 0x73) { + PDEBUG(D_PROBE, "Sensor is an OV7670"); + sd->sensor = SEN_OV7670; + } else { + PDEBUG(D_PROBE, "Sensor is an OV7610"); + sd->sensor = SEN_OV7610; + } + } else if ((rc & 3) == 1) { + /* I don't know what's different about the 76BE yet. */ + if (i2c_r(sd, 0x15) & 1) + PDEBUG(D_PROBE, "Sensor is an OV7620AE"); + else + PDEBUG(D_PROBE, "Sensor is an OV76BE"); + + /* OV511+ will return all zero isoc data unless we + * configure the sensor as a 7620. Someone needs to + * find the exact reg. setting that causes this. */ + sd->sensor = SEN_OV76BE; + } else if ((rc & 3) == 0) { + /* try to read product id registers */ + high = i2c_r(sd, 0x0a); + if (high < 0) { + PDEBUG(D_ERR, "Error detecting camera chip PID"); + return high; + } + low = i2c_r(sd, 0x0b); + if (low < 0) { + PDEBUG(D_ERR, "Error detecting camera chip VER"); + return low; + } + if (high == 0x76) { + if (low == 0x30) { + PDEBUG(D_PROBE, "Sensor is an OV7630/OV7635"); + sd->sensor = SEN_OV7630; + } else if (low == 0x40) { + PDEBUG(D_PROBE, "Sensor is an OV7645"); + sd->sensor = SEN_OV7640; /* FIXME */ + } else if (low == 0x45) { + PDEBUG(D_PROBE, "Sensor is an OV7645B"); + sd->sensor = SEN_OV7640; /* FIXME */ + } else if (low == 0x48) { + PDEBUG(D_PROBE, "Sensor is an OV7648"); + sd->sensor = SEN_OV7640; /* FIXME */ + } else { + PDEBUG(D_PROBE, "Unknown sensor: 0x76%X", low); + return -1; + } + } else { + PDEBUG(D_PROBE, "Sensor is an OV7620"); + sd->sensor = SEN_OV7620; + } + } else { + PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3); + return -1; + } + + if (sd->sensor == SEN_OV7620) { + PDEBUG(D_PROBE, "Writing 7620 registers"); + if (write_i2c_regvals(sd, norm_7620, + sizeof norm_7620 / sizeof norm_7620[0])) + return -1; + } else if (sd->sensor == SEN_OV7630) { + PDEBUG(D_ERR, "7630 is not supported by this driver version"); + return -1; + } else if (sd->sensor == SEN_OV7640) { + PDEBUG(D_PROBE, "Writing 7640 registers"); + if (write_i2c_regvals(sd, norm_7640, + sizeof norm_7640 / sizeof norm_7640[0])) + return -1; + } else if (sd->sensor == SEN_OV7670) { + PDEBUG(D_PROBE, "Writing 7670 registers"); + if (write_i2c_regvals(sd, norm_7670, + sizeof norm_7670 / sizeof norm_7670[0])) + return -1; + } else { + PDEBUG(D_PROBE, "Writing 7610 registers"); + if (write_i2c_regvals(sd, norm_7610, + sizeof norm_7610 / sizeof norm_7610[0])) + return -1; + } + + /* Set sensor-specific vars */ + sd->maxwidth = 640; + sd->maxheight = 480; + return 0; +} + +/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ +static int ov6xx0_configure(struct sd *sd) +{ + int rc; + static const struct ov_i2c_regvals norm_6x20[] = { + { 0x12, 0x80 }, /* reset */ + { 0x11, 0x01 }, + { 0x03, 0x60 }, + { 0x05, 0x7f }, /* For when autoadjust is off */ + { 0x07, 0xa8 }, + /* The ratio of 0x0c and 0x0d controls the white point */ + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x0f, 0x15 }, /* COMS */ + { 0x10, 0x75 }, /* AEC Exposure time */ + { 0x12, 0x24 }, /* Enable AGC */ + { 0x14, 0x04 }, + /* 0x16: 0x06 helps frame stability with moving objects */ + { 0x16, 0x06 }, +/* { 0x20, 0x30 }, * Aperture correction enable */ + { 0x26, 0xb2 }, /* BLC enable */ + /* 0x28: 0x05 Selects RGB format if RGB on */ + { 0x28, 0x05 }, + { 0x2a, 0x04 }, /* Disable framerate adjust */ +/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ + { 0x2d, 0x99 }, + { 0x33, 0xa0 }, /* Color Processing Parameter */ + { 0x34, 0xd2 }, /* Max A/D range */ + { 0x38, 0x8b }, + { 0x39, 0x40 }, + + { 0x3c, 0x39 }, /* Enable AEC mode changing */ + { 0x3c, 0x3c }, /* Change AEC mode */ + { 0x3c, 0x24 }, /* Disable AEC mode changing */ + + { 0x3d, 0x80 }, + /* These next two registers (0x4a, 0x4b) are undocumented. + * They control the color balance */ + { 0x4a, 0x80 }, + { 0x4b, 0x80 }, + { 0x4d, 0xd2 }, /* This reduces noise a bit */ + { 0x4e, 0xc1 }, + { 0x4f, 0x04 }, +/* Do 50-53 have any effect? */ +/* Toggle 0x12[2] off and on here? */ + }; + + static const struct ov_i2c_regvals norm_6x30[] = { + { 0x12, 0x80 }, /* Reset */ + { 0x00, 0x1f }, /* Gain */ + { 0x01, 0x99 }, /* Blue gain */ + { 0x02, 0x7c }, /* Red gain */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x0a }, /* Contrast */ + { 0x06, 0x95 }, /* Brightness */ + { 0x07, 0x2d }, /* Sharpness */ + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x0e, 0x20 }, + { 0x0f, 0x05 }, + { 0x10, 0x9a }, + { 0x11, 0x00 }, /* Pixel clock = fastest */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x21 }, + { 0x14, 0x80 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x38 }, + { 0x18, 0xea }, + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, + { 0x1e, 0xc4 }, + { 0x1f, 0x04 }, + { 0x20, 0x20 }, + { 0x21, 0x10 }, + { 0x22, 0x88 }, + { 0x23, 0xc0 }, /* Crystal circuit power level */ + { 0x25, 0x9a }, /* Increase AEC black ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x84 }, /* 60 Hz power */ + { 0x2b, 0xa8 }, /* 60 Hz power */ + { 0x2c, 0xa0 }, + { 0x2d, 0x95 }, /* Enable auto-brightness */ + { 0x2e, 0x88 }, + { 0x33, 0x26 }, + { 0x34, 0x03 }, + { 0x36, 0x8f }, + { 0x37, 0x80 }, + { 0x38, 0x83 }, + { 0x39, 0x80 }, + { 0x3a, 0x0f }, + { 0x3b, 0x3c }, + { 0x3c, 0x1a }, + { 0x3d, 0x80 }, + { 0x3e, 0x80 }, + { 0x3f, 0x0e }, + { 0x40, 0x00 }, /* White bal */ + { 0x41, 0x00 }, /* White bal */ + { 0x42, 0x80 }, + { 0x43, 0x3f }, /* White bal */ + { 0x44, 0x80 }, + { 0x45, 0x20 }, + { 0x46, 0x20 }, + { 0x47, 0x80 }, + { 0x48, 0x7f }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x80 }, + { 0x4c, 0xd0 }, + { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ + { 0x4e, 0x40 }, + { 0x4f, 0x07 }, /* UV avg., col. killer: max */ + { 0x50, 0xff }, + { 0x54, 0x23 }, /* Max AGC gain: 18dB */ + { 0x55, 0xff }, + { 0x56, 0x12 }, + { 0x57, 0x81 }, + { 0x58, 0x75 }, + { 0x59, 0x01 }, /* AGC dark current comp.: +1 */ + { 0x5a, 0x2c }, + { 0x5b, 0x0f }, /* AWB chrominance levels */ + { 0x5c, 0x10 }, + { 0x3d, 0x80 }, + { 0x27, 0xa6 }, + { 0x12, 0x20 }, /* Toggle AWB */ + { 0x12, 0x24 }, + }; + + PDEBUG(D_PROBE, "starting sensor configuration"); + + if (init_ov_sensor(sd) < 0) { + PDEBUG(D_ERR, "Failed to read sensor ID."); + return -1; + } + PDEBUG(D_PROBE, "OV6xx0 sensor detected"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + if (rc < 0) { + PDEBUG(D_ERR, "Error detecting sensor type"); + return -1; + } + + /* Ugh. The first two bits are the version bits, but + * the entire register value must be used. I guess OVT + * underestimated how many variants they would make. */ + if (rc == 0x00) { + sd->sensor = SEN_OV6630; + PDEBUG(D_ERR, + "WARNING: Sensor is an OV66308. Your camera may have"); + PDEBUG(D_ERR, "been misdetected in previous driver versions."); + } else if (rc == 0x01) { + sd->sensor = SEN_OV6620; + PDEBUG(D_PROBE, "Sensor is an OV6620"); + } else if (rc == 0x02) { + sd->sensor = SEN_OV6630; + PDEBUG(D_PROBE, "Sensor is an OV66308AE"); + } else if (rc == 0x03) { + sd->sensor = SEN_OV6630; + PDEBUG(D_PROBE, "Sensor is an OV66308AF"); + } else if (rc == 0x90) { + sd->sensor = SEN_OV6630; + PDEBUG(D_ERR, + "WARNING: Sensor is an OV66307. Your camera may have"); + PDEBUG(D_ERR, "been misdetected in previous driver versions."); + } else { + PDEBUG(D_ERR, "FATAL: Unknown sensor version: 0x%02x", rc); + return -1; + } + + /* Set sensor-specific vars */ + sd->maxwidth = 352; + sd->maxheight = 288; + + if (sd->sensor == SEN_OV6620) { + PDEBUG(D_PROBE, "Writing 6x20 registers"); + if (write_i2c_regvals(sd, norm_6x20, + sizeof norm_6x20 / sizeof norm_6x20[0])) + return -1; + } else { + PDEBUG(D_PROBE, "Writing 6x30 registers"); + if (write_i2c_regvals(sd, norm_6x30, + sizeof norm_6x30 / sizeof norm_6x30[0])) + return -1; + } + return 0; +} + +/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */ +static void ov51x_led_control(struct sd *sd, int on) +{ + PDEBUG(D_STREAM, "LED (%s)", on ? "on" : "off"); + +/* if (sd->bridge == BRG_OV511PLUS) */ +/* reg_w(sd, R511_SYS_LED_CTL, on ? 1 : 0); */ +/* else if (sd->bridge == BRG_OV519) */ + reg_w_mask(sd, OV519_GPIO_DATA_OUT0, !on, 1); /* 0 / 1 */ +/* else if (sd->bclass == BCL_OV518) */ +/* reg_w_mask(sd, R518_GPIO_OUT, on ? 0x02 : 0x00, 0x02); */ +} + +/* 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; + +/* (from ov519_configure) */ + static const struct ov_regvals init_519[] = { + { 0x5a, 0x6d }, /* EnableSystem */ +/* jfm trace usbsnoop3-1.txt */ +/* jfm 53 = fb */ + { 0x53, 0x9b }, + { 0x54, 0xff }, /* set bit2 to enable jpeg */ + { 0x5d, 0x03 }, + { 0x49, 0x01 }, + { 0x48, 0x00 }, + /* Set LED pin to output mode. Bit 4 must be cleared or sensor + * detection will fail. This deserves further investigation. */ + { OV519_GPIO_IO_CTRL0, 0xee }, + { 0x51, 0x0f }, /* SetUsbInit */ + { 0x51, 0x00 }, + { 0x22, 0x00 }, + /* windows reads 0x55 at this point*/ + }; + + if (write_regvals(sd, init_519, ARRAY_SIZE(init_519))) + goto error; +/* jfm: not seen in windows trace */ + if (ov519_init_compression(sd)) + goto error; + ov51x_led_control(sd, 0); /* turn LED off */ + + /* Test for 76xx */ + sd->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0) + goto error; + + /* The OV519 must be more aggressive about sensor detection since + * I2C write will never fail if the sensor is not present. We have + * to try to initialize the sensor to detect its presence */ + if (init_ov_sensor(sd) < 0) { + /* Test for 6xx0 */ + sd->primary_i2c_slave = OV6xx0_SID; + if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0) + goto error; + + if (init_ov_sensor(sd) < 0) { + /* Test for 8xx0 */ + sd->primary_i2c_slave = OV8xx0_SID; + if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0) + goto error; + + if (init_ov_sensor(sd) < 0) { + PDEBUG(D_ERR, + "Can't determine sensor slave IDs"); + goto error; + } else { + if (ov8xx0_configure(sd) < 0) { + PDEBUG(D_ERR, + "Failed to configure OV8xx0 sensor"); + goto error; + } + } + } else { + if (ov6xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV6xx0"); + goto error; + } + } + } else { + if (ov7xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV7xx0"); + goto error; + } + } + + cam = &gspca_dev->cam; + cam->epaddr = OV511_ENDPOINT_ADDRESS; + if (sd->maxwidth == 640) { + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + } else { + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + } + cam->dev_name = (char *) id->driver_info; + 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; + return 0; +error: + PDEBUG(D_ERR, "OV519 Config failed"); + return -EBUSY; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* Sets up the OV519 with the given image parameters + * + * OV519 needs a completely different approach, until we can figure out what + * the individual registers do. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int ov519_mode_init_regs(struct sd *sd, + int width, int height) +{ + static const struct ov_regvals mode_init_519_ov7670[] = { + { 0x5d, 0x03 }, /* Turn off suspend mode */ + { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */ + { 0x54, 0x0f }, /* bit2 (jpeg enable) */ + { 0xa2, 0x20 }, /* a2-a5 are undocumented */ + { 0xa3, 0x18 }, + { 0xa4, 0x04 }, + { 0xa5, 0x28 }, + { 0x37, 0x00 }, /* SetUsbInit */ + { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ + /* Enable both fields, YUV Input, disable defect comp (why?) */ + { 0x20, 0x0c }, + { 0x21, 0x38 }, + { 0x22, 0x1d }, + { 0x17, 0x50 }, /* undocumented */ + { 0x37, 0x00 }, /* undocumented */ + { 0x40, 0xff }, /* I2C timeout counter */ + { 0x46, 0x00 }, /* I2C clock prescaler */ + { 0x59, 0x04 }, /* new from windrv 090403 */ + { 0xff, 0x00 }, /* undocumented */ + /* windows reads 0x55 at this point, why? */ + }; + + static const struct ov_regvals mode_init_519[] = { + { 0x5d, 0x03 }, /* Turn off suspend mode */ + { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */ + { 0x54, 0x0f }, /* bit2 (jpeg enable) */ + { 0xa2, 0x20 }, /* a2-a5 are undocumented */ + { 0xa3, 0x18 }, + { 0xa4, 0x04 }, + { 0xa5, 0x28 }, + { 0x37, 0x00 }, /* SetUsbInit */ + { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ + /* Enable both fields, YUV Input, disable defect comp (why?) */ + { 0x22, 0x1d }, + { 0x17, 0x50 }, /* undocumented */ + { 0x37, 0x00 }, /* undocumented */ + { 0x40, 0xff }, /* I2C timeout counter */ + { 0x46, 0x00 }, /* I2C clock prescaler */ + { 0x59, 0x04 }, /* new from windrv 090403 */ + { 0xff, 0x00 }, /* undocumented */ + /* windows reads 0x55 at this point, why? */ + }; + +/* int hi_res; */ + + PDEBUG(D_CONF, "mode init %dx%d", width, height); + +/* if (width >= 800 && height >= 600) + hi_res = 1; + else + hi_res = 0; */ + +/* if (ov51x_stop(sd) < 0) + return -EIO; */ + + /******** Set the mode ********/ + if (sd->sensor != SEN_OV7670) { + if (write_regvals(sd, mode_init_519, + ARRAY_SIZE(mode_init_519))) + return -EIO; + } else { + if (write_regvals(sd, mode_init_519_ov7670, + ARRAY_SIZE(mode_init_519_ov7670))) + return -EIO; + } + + if (sd->sensor == SEN_OV7640) { + /* Select 8-bit input mode */ + reg_w_mask(sd, OV519_CAM_DFR, 0x10, 0x10); + } + + reg_w(sd, OV519_CAM_H_SIZE, width >> 4); + reg_w(sd, OV519_CAM_V_SIZE, height >> 3); + reg_w(sd, OV519_CAM_X_OFFSETL, 0x00); + reg_w(sd, OV519_CAM_X_OFFSETH, 0x00); + reg_w(sd, OV519_CAM_Y_OFFSETL, 0x00); + reg_w(sd, OV519_CAM_Y_OFFSETH, 0x00); + reg_w(sd, OV519_CAM_DIVIDER, 0x00); + reg_w(sd, OV519_CAM_FORMAT, 0x03); /* YUV422 */ + reg_w(sd, 0x26, 0x00); /* Undocumented */ + + /******** Set the framerate ********/ + if (frame_rate > 0) + sd->frame_rate = frame_rate; + +/* FIXME: These are only valid at the max resolution. */ + sd->clockdiv = 0; + if (sd->sensor == SEN_OV7640) { + switch (sd->frame_rate) { +/*jfm: default was 30 fps */ + case 30: + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0xff); + break; + case 25: + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0x1f); + break; + case 20: + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0x1b); + break; + default: +/* case 15: */ + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0xff); + sd->clockdiv = 1; + break; + case 10: + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0x1f); + sd->clockdiv = 1; + break; + case 5: + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0x1b); + sd->clockdiv = 1; + break; + } + } else if (sd->sensor == SEN_OV8610) { + switch (sd->frame_rate) { + default: /* 15 fps */ +/* case 15: */ + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0xff); + break; + case 10: + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0x1f); + break; + case 5: + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0x1b); + break; + } + sd->clockdiv = 0; + } else if (sd->sensor == SEN_OV7670) { /* guesses, based on 7640 */ + PDEBUG(D_STREAM, "Setting framerate to %d fps", + (sd->frame_rate == 0) ? 15 : sd->frame_rate); + switch (sd->frame_rate) { + case 30: + reg_w(sd, 0xa4, 0x10); + reg_w(sd, 0x23, 0xff); + break; + case 20: + reg_w(sd, 0xa4, 0x10); + reg_w(sd, 0x23, 0x1b); + break; + default: /* 15 fps */ +/* case 15: */ + reg_w(sd, 0xa4, 0x10); + reg_w(sd, 0x23, 0xff); + sd->clockdiv = 1; + break; + } + } + +/* if (ov51x_restart(sd) < 0) + return -EIO; */ + + /* Reset it just for good measure */ +/* if (ov51x_reset(sd, OV511_RESET_NOREGS) < 0) + return -EIO; */ + return 0; +} + +static int mode_init_ov_sensor_regs(struct sd *sd, + struct ovsensor_window *win) +{ + int qvga = win->quarter; + + /******** Mode (VGA/QVGA) and sensor specific regs ********/ + switch (sd->sensor) { + case SEN_OV8610: + /* For OV8610 qvga means qsvga */ + i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5); + break; + case SEN_OV7610: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV7620: +/* i2c_w(sd, 0x2b, 0x00); */ + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); + i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); + i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); + i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); + i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); + i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV76BE: +/* i2c_w(sd, 0x2b, 0x00); */ + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV7640: +/* i2c_w(sd, 0x2b, 0x00); */ + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); +/* i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); */ +/* i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); */ +/* i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); */ +/* i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); */ +/* i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); */ + break; + case SEN_OV7670: + /* set COM7_FMT_VGA or COM7_FMT_QVGA + * do we need to set anything else? + * HSTART etc are set in set_ov_sensor_window itself */ + i2c_w_mask(sd, OV7670_REG_COM7, + qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA, + OV7670_COM7_FMT_MASK); + break; + case SEN_OV6620: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV6630: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + default: + return -EINVAL; + } + + /******** Palette-specific regs ********/ +/* Need to do work here for the OV7670 */ + + if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { + /* not valid on the OV6620/OV7620/6630? */ + i2c_w_mask(sd, 0x0e, 0x00, 0x40); + } + + /* The OV518 needs special treatment. Although both the OV518 + * and the OV6630 support a 16-bit video bus, only the 8 bit Y + * bus is actually used. The UV bus is tied to ground. + * Therefore, the OV6630 needs to be in 8-bit multiplexed + * output mode */ + + /* OV7640 is 8-bit only */ + + if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV7640) + i2c_w_mask(sd, 0x13, 0x00, 0x20); +/* } */ + + /******** Clock programming ********/ + /* The OV6620 needs special handling. This prevents the + * severe banding that normally occurs */ + if (sd->sensor == SEN_OV6620) { + + /* Clock down */ + i2c_w(sd, 0x2a, 0x04); + i2c_w(sd, 0x11, win->clockdiv); + i2c_w(sd, 0x2a, 0x84); + /* This next setting is critical. It seems to improve + * the gain or the contrast. The "reserved" bits seem + * to have some effect in this case. */ + i2c_w(sd, 0x2d, 0x85); + } else if (win->clockdiv >= 0) { + i2c_w(sd, 0x11, win->clockdiv); + } + + /******** Special Features ********/ +/* no evidence this is possible with OV7670, either */ + /* Test Pattern */ + if (sd->sensor != SEN_OV7640 && sd->sensor != SEN_OV7670) + i2c_w_mask(sd, 0x12, 0x00, 0x02); + + /* Enable auto white balance */ + if (sd->sensor == SEN_OV7670) + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB, + OV7670_COM8_AWB); + else + i2c_w_mask(sd, 0x12, 0x04, 0x04); + + /* This will go away as soon as ov51x_mode_init_sensor_regs() */ + /* is fully tested. */ + /* 7620/6620/6630? don't have register 0x35, so play it safe */ + if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { + if (win->width == 640 /*&& win->height == 480*/) + i2c_w(sd, 0x35, 0x9e); + else + i2c_w(sd, 0x35, 0x1e); + } + return 0; +} + +static int set_ov_sensor_window(struct sd *sd, + struct ovsensor_window *win) +{ + int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale; + int ret, hstart, hstop, vstop, vstart; + __u8 v; + + /* The different sensor ICs handle setting up of window differently. + * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */ + switch (sd->sensor) { + case SEN_OV8610: + hwsbase = 0x1e; + hwebase = 0x1e; + vwsbase = 0x02; + vwebase = 0x02; + break; + case SEN_OV7610: + case SEN_OV76BE: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = vwebase = 0x05; + break; + case SEN_OV6620: + case SEN_OV6630: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = 0x05; + vwebase = 0x06; + break; + case SEN_OV7620: + hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ + hwebase = 0x2f; + vwsbase = vwebase = 0x05; + break; + case SEN_OV7640: + hwsbase = 0x1a; + hwebase = 0x1a; + vwsbase = vwebase = 0x03; + break; + case SEN_OV7670: + /*handling of OV7670 hardware sensor start and stop values + * is very odd, compared to the other OV sensors */ + vwsbase = vwebase = hwebase = hwsbase = 0x00; + break; + default: + return -EINVAL; + } + + switch (sd->sensor) { + case SEN_OV6620: + case SEN_OV6630: + if (win->quarter) { /* QCIF */ + hwscale = 0; + vwscale = 0; + } else { /* CIF */ + hwscale = 1; + vwscale = 1; /* The datasheet says 0; + * it's wrong */ + } + break; + case SEN_OV8610: + if (win->quarter) { /* QSVGA */ + hwscale = 1; + vwscale = 1; + } else { /* SVGA */ + hwscale = 2; + vwscale = 2; + } + break; + default: /* SEN_OV7xx0 */ + if (win->quarter) { /* QVGA */ + hwscale = 1; + vwscale = 0; + } else { /* VGA */ + hwscale = 2; + vwscale = 1; + } + } + + ret = mode_init_ov_sensor_regs(sd, win); + if (ret < 0) + return ret; + + if (sd->sensor == SEN_OV8610) { + i2c_w_mask(sd, 0x2d, 0x05, 0x40); + /* old 0x95, new 0x05 from windrv 090403 */ + /* bits 5-7: reserved */ + i2c_w_mask(sd, 0x28, 0x20, 0x20); + /* bit 5: progressive mode on */ + } + + /* The below is wrong for OV7670s because their window registers + * only store the high bits in 0x17 to 0x1a */ + + /* SRH Use sd->max values instead of requested win values */ + /* SCS Since we're sticking with only the max hardware widths + * for a given mode */ + /* I can hard code this for OV7670s */ + /* Yes, these numbers do look odd, but they're tested and work! */ + if (sd->sensor == SEN_OV7670) { + if (win->quarter) { /* QVGA from ov7670.c by + * Jonathan Corbet */ + hstart = 164; + hstop = 20; + vstart = 14; + vstop = 494; + } else { /* VGA */ + hstart = 158; + hstop = 14; + vstart = 10; + vstop = 490; + } + /* OV7670 hardware window registers are split across + * multiple locations */ + i2c_w(sd, OV7670_REG_HSTART, (hstart >> 3) & 0xff); + i2c_w(sd, OV7670_REG_HSTOP, (hstop >> 3) & 0xff); + v = i2c_r(sd, OV7670_REG_HREF); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x07); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_HREF, v); + + i2c_w(sd, OV7670_REG_VSTART, (vstart >> 2) & 0xff); + i2c_w(sd, OV7670_REG_VSTOP, (vstop >> 2) & 0xff); + v = i2c_r(sd, OV7670_REG_VREF); + v = (v & 0xc0) | ((vstop & 0x3) << 2) | (vstart & 0x03); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_VREF, v); + + } else { + i2c_w(sd, 0x17, hwsbase + (win->x >> hwscale)); + i2c_w(sd, 0x18, hwebase + ((win->x + win->width) >> hwscale)); + i2c_w(sd, 0x19, vwsbase + (win->y >> vwscale)); + i2c_w(sd, 0x1a, vwebase + ((win->y + win->height) >> vwscale)); + } + return 0; +} + +static int ov_sensor_mode_setup(struct sd *sd, + int width, int height) +{ + struct ovsensor_window win; + +/* win.format = mode; */ + + /* Unless subcapture is enabled, + * center the image window and downsample + * if possible to increase the field of view */ + /* NOTE: OV518(+) and OV519 does downsampling on its own */ + win.width = width; + win.height = height; + if (width == sd->maxwidth) + win.quarter = 0; + else + win.quarter = 1; + + /* Center it */ + win.x = (win.width - width) / 2; + win.y = (win.height - height) / 2; + + /* Clock is determined by OV519 frame rate code */ + win.clockdiv = sd->clockdiv; + + PDEBUG(D_CONF, "Setting clock divider to %d", win.clockdiv); + return set_ov_sensor_window(sd, &win); +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + + ret = ov519_mode_init_regs(sd, gspca_dev->width, gspca_dev->height); + if (ret < 0) + goto out; + ret = ov_sensor_mode_setup(sd, gspca_dev->width, gspca_dev->height); + if (ret < 0) + goto out; + + ret = ov51x_restart((struct sd *) gspca_dev); + if (ret < 0) + goto out; + PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt); + ov51x_led_control(sd, 1); + return; +out: + PDEBUG(D_ERR, "camera start error:%d", ret); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + ov51x_stop((struct sd *) gspca_dev); + ov51x_led_control((struct sd *) gspca_dev, 0); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + /* Header of ov519 is 16 bytes: + * Byte Value Description + * 0 0xff magic + * 1 0xff magic + * 2 0xff magic + * 3 0xXX 0x50 = SOF, 0x51 = EOF + * 9 0xXX 0x01 initial frame without data, + * 0x00 standard frame with image + * 14 Lo in EOF: length of image data / 8 + * 15 Hi + */ + + if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) { + switch (data[3]) { + case 0x50: /* start of frame */ +#define HDRSZ 16 + data += HDRSZ; + len -= HDRSZ; +#undef HDRSZ + if (data[0] == 0xff || data[1] == 0xd8) + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + else + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + case 0x51: /* end of frame */ + if (data[9] != 0) + gspca_dev->last_packet_type = DISCARD_PACKET; + gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + return; + } + } + + /* intermediate packet */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, len); +} + +/* -- management routines -- */ + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; +/* int was_streaming; */ + + val = sd->brightness; + PDEBUG(D_CONF, "brightness:%d", val); +/* was_streaming = gspca_dev->streaming; + * if (was_streaming) + * ov51x_stop(sd); */ + switch (sd->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7640: + i2c_w(sd, OV7610_REG_BRT, val); + break; + case SEN_OV7620: + /* 7620 doesn't like manual changes when in auto mode */ +/*fixme + * if (!sd->auto_brt) */ + i2c_w(sd, OV7610_REG_BRT, val); + break; + case SEN_OV7670: +/*jfm - from windblows + * i2c_w_mask(sd, OV7670_REG_COM8, 0, OV7670_COM8_AEC); */ + i2c_w(sd, OV7670_REG_BRIGHT, ov7670_abs_to_sm(val)); + break; + } +/* if (was_streaming) + * ov51x_restart(sd); */ +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; +/* int was_streaming; */ + + val = sd->contrast; + PDEBUG(D_CONF, "contrast:%d", val); +/* was_streaming = gspca_dev->streaming; + if (was_streaming) + ov51x_stop(sd); */ + switch (sd->sensor) { + case SEN_OV7610: + case SEN_OV6620: + i2c_w(sd, OV7610_REG_CNT, val); + break; + case SEN_OV6630: + i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f); + case SEN_OV8610: { + static const __u8 ctab[] = { + 0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f + }; + + /* Use Y gamma control instead. Bit 0 enables it. */ + i2c_w(sd, 0x64, ctab[val >> 5]); + break; + } + case SEN_OV7620: { + static const __u8 ctab[] = { + 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, + 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff + }; + + /* Use Y gamma control instead. Bit 0 enables it. */ + i2c_w(sd, 0x64, ctab[val >> 4]); + break; + } + case SEN_OV7640: + /* Use gain control instead. */ + i2c_w(sd, OV7610_REG_GAIN, val >> 2); + break; + case SEN_OV7670: + /* check that this isn't just the same as ov7610 */ + i2c_w(sd, OV7670_REG_CONTRAS, val >> 1); + break; + } +/* if (was_streaming) + ov51x_restart(sd); */ +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; +/* int was_streaming; */ + + val = sd->colors; + PDEBUG(D_CONF, "saturation:%d", val); +/* was_streaming = gspca_dev->streaming; + if (was_streaming) + ov51x_stop(sd); */ + switch (sd->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + i2c_w(sd, OV7610_REG_SAT, val); + break; + case SEN_OV7620: + /* Use UV gamma control instead. Bits 0 & 7 are reserved. */ +/* rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e); + if (rc < 0) + goto out; */ + i2c_w(sd, OV7610_REG_SAT, val); + break; + case SEN_OV7640: + i2c_w(sd, OV7610_REG_SAT, val & 0xf0); + break; + case SEN_OV7670: + /* supported later once I work out how to do it + * transparently fail now! */ + /* set REG_COM13 values for UV sat auto mode */ + break; + } +/* if (was_streaming) + ov51x_restart(sd); */ +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + 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_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4052), DVNM("Creative Live! VISTA IM")}, + {USB_DEVICE(0x041e, 0x405f), DVNM("Creative Live! VISTA VF0330")}, + {USB_DEVICE(0x041e, 0x4060), DVNM("Creative Live! VISTA VF0350")}, + {USB_DEVICE(0x041e, 0x4061), DVNM("Creative Live! VISTA VF0400")}, + {USB_DEVICE(0x041e, 0x4064), DVNM("Creative Live! VISTA VF0420")}, + {USB_DEVICE(0x041e, 0x4068), DVNM("Creative Live! VISTA VF0470")}, + {USB_DEVICE(0x045e, 0x028c), DVNM("Microsoft xbox cam")}, + {USB_DEVICE(0x054c, 0x0154), DVNM("Sonny toy4")}, + {USB_DEVICE(0x054c, 0x0155), DVNM("Sonny toy5")}, + {USB_DEVICE(0x05a9, 0x0519), DVNM("OmniVision")}, + {USB_DEVICE(0x05a9, 0x0530), DVNM("OmniVision")}, + {USB_DEVICE(0x05a9, 0x4519), DVNM("OmniVision")}, + {USB_DEVICE(0x05a9, 0x8519), DVNM("OmniVision")}, + {} +}; +#undef DVNAME +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); + +module_param(frame_rate, int, 0644); +MODULE_PARM_DESC(frame_rate, "Frame rate (5, 10, 15, 20 or 30 fps)"); + diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c new file mode 100644 index 00000000000..fa7abc41109 --- /dev/null +++ b/drivers/media/video/gspca/pac207.c @@ -0,0 +1,622 @@ +/* + * Pixart PAC207BCA library + * + * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MODULE_NAME "pac207" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>"); +MODULE_DESCRIPTION("Pixart PAC207"); +MODULE_LICENSE("GPL"); + +#define PAC207_CTRL_TIMEOUT 100 /* ms */ + +#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_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_GAIN_MIN 0 +#define PAC207_GAIN_MAX 31 +#define PAC207_GAIN_DEFAULT 9 /* power on default: 9 */ +#define PAC207_GAIN_KNEE 20 + +#define PAC207_AUTOGAIN_DEADZONE 30 +/* We calculating the autogain at the end of the transfer of a frame, at this + moment a frame with the old settings is being transmitted, and a frame is + being captured with the old settings. So if we adjust the autogain we must + ignore atleast the 2 next frames for the new settings to come into effect + before doing any other adjustments */ +#define PAC207_AUTOGAIN_IGNORE_FRAMES 3 + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + u8 mode; + + u8 brightness; + u8 exposure; + u8 autogain; + u8 gain; + + u8 sof_read; + u8 header_read; + u8 autogain_ignore_frames; + + atomic_t avg_lum; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(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 = PAC207_BRIGHTNESS_MIN, + .maximum = PAC207_BRIGHTNESS_MAX, + .step = 1, + .default_value = PAC207_BRIGHTNESS_DEFAULT, + .flags = 0, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_EXPOSURE 1 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = PAC207_EXPOSURE_MIN, + .maximum = PAC207_EXPOSURE_MAX, + .step = 1, + .default_value = PAC207_EXPOSURE_DEFAULT, + .flags = 0, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, +#define SD_AUTOGAIN 2 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define SD_GAIN 3 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = PAC207_GAIN_MIN, + .maximum = PAC207_GAIN_MAX, + .step = 1, + .default_value = PAC207_GAIN_DEFAULT, + .flags = 0, + }, + .set = sd_setgain, + .get = sd_getgain, + }, +}; + +static struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = (176 + 2) * 144, + /* uncompressed, add 2 bytes / line for line header */ + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, + .bytesperline = 352, + /* compressed, but only when needed (not compressed + when the framerate is low) */ + .sizeimage = (352 + 2) * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +static const __u8 pac207_sensor_init[][8] = { + {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0}, + {0x00, 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 const unsigned char pac207_sof_marker[5] = + { 0xff, 0xff, 0x00, 0xff, 0x96 }; + +static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, + const u8 *buffer, u16 length) +{ + struct usb_device *udev = gspca_dev->dev; + int err; + + memcpy(gspca_dev->usb_buf, buffer, length); + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, index, + gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT); + if (err < 0) + PDEBUG(D_ERR, + "Failed to write registers to index 0x%04X, error %d)", + index, err); + + return err; +} + + +int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) +{ + struct usb_device *udev = gspca_dev->dev; + int err; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, index, NULL, 0, PAC207_CTRL_TIMEOUT); + if (err) + PDEBUG(D_ERR, "Failed to write a register (index 0x%04X," + " value 0x%02X, error %d)", index, value, err); + + return err; +} + + +int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) +{ + struct usb_device *udev = gspca_dev->dev; + int res; + + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, index, + gspca_dev->usb_buf, 1, PAC207_CTRL_TIMEOUT); + if (res < 0) { + PDEBUG(D_ERR, + "Failed to read a register (index 0x%04X, error %d)", + index, res); + return res; + } + + 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 idreg[2]; + + idreg[0] = pac207_read_reg(gspca_dev, 0x0000); + idreg[1] = pac207_read_reg(gspca_dev, 0x0001); + idreg[0] = ((idreg[0] & 0x0F) << 4) | ((idreg[1] & 0xf0) >> 4); + idreg[1] = idreg[1] & 0x0f; + PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X", + idreg[0], idreg[1]); + + if (idreg[0] != 0x27) { + PDEBUG(D_PROBE, "Error invalid sensor ID!"); + return -ENODEV; + } + + pac207_write_reg(gspca_dev, 0x41, 0x00); + /* Bit_0=Image Format, + * Bit_1=LED, + * Bit_2=Compression test mode enable */ + pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ + pac207_write_reg(gspca_dev, 0x11, 0x30); /* Analog Bias */ + + PDEBUG(D_PROBE, + "Pixart PAC207BCA Image Processor and Control Chip detected" + " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x05; + cam->cam_mode = sif_mode; + cam->nmodes = ARRAY_SIZE(sif_mode); + sd->brightness = PAC207_BRIGHTNESS_DEFAULT; + sd->exposure = PAC207_EXPOSURE_DEFAULT; + sd->gain = PAC207_GAIN_DEFAULT; + + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = 1; + return 0; +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 mode; + + pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */ + 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); + + /* 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, 0x4b, 0x00); /* Sram test value */ + pac207_write_reg(gspca_dev, 0x08, sd->brightness); + + /* PGA global gain (Bit 4-0) */ + pac207_write_reg(gspca_dev, 0x0e, sd->gain); + pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */ + + mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ + if (gspca_dev->width == 176) { /* 176x144 */ + mode |= 0x01; + PDEBUG(D_STREAM, "pac207_start mode 176x144"); + } else { /* 352x288 */ + PDEBUG(D_STREAM, "pac207_start mode 352x288"); + } + pac207_write_reg(gspca_dev, 0x41, mode); + + pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ + pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ + msleep(10); + pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */ + + sd->sof_read = 0; + sd->autogain_ignore_frames = 0; + atomic_set(&sd->avg_lum, -1); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */ + pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */ + pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum = atomic_read(&sd->avg_lum); + + if (avg_lum == -1) + return; + + 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, + PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE)) + sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES; +} + +static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev, + unsigned char *m, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + + /* Search for the SOF marker (fixed part) in the header */ + for (i = 0; i < len; i++) { + if (m[i] == pac207_sof_marker[sd->sof_read]) { + sd->sof_read++; + if (sd->sof_read == sizeof(pac207_sof_marker)) { + PDEBUG(D_STREAM, + "SOF found, bytes to analyze: %u." + " Frame starts at byte #%u", + len, i + 1); + sd->sof_read = 0; + return m + i + 1; + } + } else { + sd->sof_read = 0; + } + } + + return NULL; +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, + __u8 *data, + int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned char *sof; + + sof = pac207_find_sof(gspca_dev, data, len); + if (sof) { + int n; + + /* finish decoding current frame */ + n = sof - data; + if (n > sizeof pac207_sof_marker) + n -= sizeof pac207_sof_marker; + else + n = 0; + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, n); + sd->header_read = 0; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); + len -= sof - data; + data = sof; + } + if (sd->header_read < 11) { + int needed; + + /* get average lumination from frame header (byte 5) */ + if (sd->header_read < 5) { + needed = 5 - sd->header_read; + if (len >= needed) + atomic_set(&sd->avg_lum, data[needed - 1]); + } + /* skip the rest of the header */ + needed = 11 - sd->header_read; + if (len <= needed) { + sd->header_read += len; + return; + } + data += needed; + len -= needed; + sd->header_read = 11; + } + + 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; + + pac207_write_reg(gspca_dev, 0x08, sd->brightness); + pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ + pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + pac207_write_reg(gspca_dev, 0x02, sd->exposure); + pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ + pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ +} + +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + pac207_write_reg(gspca_dev, 0x0e, sd->gain); + pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ + pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ +} + +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; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + if (sd->autogain) { + sd->exposure = PAC207_EXPOSURE_DEFAULT; + sd->gain = PAC207_GAIN_DEFAULT; + if (gspca_dev->streaming) { + sd->autogain_ignore_frames = + PAC207_AUTOGAIN_IGNORE_FRAMES; + setexposure(gspca_dev); + setgain(gspca_dev); + } + } + + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .dq_callback = pac207_do_auto_gain, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4028), DVNM("Creative Webcam Vista Plus")}, + {USB_DEVICE(0x093a, 0x2460), DVNM("Q-Tec Webcam 100")}, + {USB_DEVICE(0x093a, 0x2463), DVNM("Philips spc200nc pac207")}, + {USB_DEVICE(0x093a, 0x2464), DVNM("Labtec Webcam 1200")}, + {USB_DEVICE(0x093a, 0x2468), DVNM("PAC207")}, + {USB_DEVICE(0x093a, 0x2470), DVNM("Genius GF112")}, + {USB_DEVICE(0x093a, 0x2471), DVNM("Genius VideoCam GE111")}, + {USB_DEVICE(0x093a, 0x2472), DVNM("Genius VideoCam GE110")}, + {USB_DEVICE(0x2001, 0xf115), DVNM("D-Link DSB-C120")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c new file mode 100644 index 00000000000..5c052e31be4 --- /dev/null +++ b/drivers/media/video/gspca/pac7311.c @@ -0,0 +1,760 @@ +/* + * Pixart PAC7311 library + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "pac7311" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); +MODULE_DESCRIPTION("Pixart PAC7311"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int avg_lum; + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + char ffseq; + signed char ag_cnt; +#define AG_CNT_START 13 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, +#define BRIGHTNESS_MAX 0x20 + .maximum = BRIGHTNESS_MAX, + .step = 1, +#define BRIGHTNESS_DEF 0x10 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, +#define CONTRAST_DEF 127 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 255, + .step = 1, +#define COLOR_DEF 127 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +#define PAC7311_JPEG_HEADER_SIZE (sizeof pac7311_jpeg_header) /* (594) */ + +static const __u8 pac7311_jpeg_header[] = { + 0xff, 0xd8, + 0xff, 0xe0, 0x00, 0x03, 0x20, + 0xff, 0xc0, 0x00, 0x11, 0x08, + 0x01, 0xe0, /* 12: height */ + 0x02, 0x80, /* 14: width */ + 0x03, /* 16 */ + 0x01, 0x21, 0x00, + 0x02, 0x11, 0x01, + 0x03, 0x11, 0x01, + 0xff, 0xdb, 0x00, 0x84, + 0x00, 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d, + 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18, 0x16, + 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d, 0x28, 0x3a, 0x33, 0x3d, + 0x3c, 0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, + 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, 0x5f, + 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, 0x79, 0x70, 0x64, + 0x78, 0x5c, 0x65, 0x67, 0x63, 0x01, 0x11, 0x12, 0x12, 0x18, + 0x15, 0x18, 0x2f, 0x1a, 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, + 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, + 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, + 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, + 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, + 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, + 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, + 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, + 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, + 0x11, 0x00, 0x3f, 0x00 +}; + +static void reg_w_buf(struct gspca_dev *gspca_dev, + __u16 index, + const char *buffer, __u16 len) +{ + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 1, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, len, + 500); +} + +static __u8 reg_r(struct gspca_dev *gspca_dev, + __u16 index) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, 1, + 500); + return gspca_dev->usb_buf[0]; +} + +static void reg_w(struct gspca_dev *gspca_dev, + __u16 index, + __u8 value) +{ + gspca_dev->usb_buf[0] = value; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, gspca_dev->usb_buf, 1, + 500); +} + +/* 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; + + PDEBUG(D_CONF, "Find Sensor PAC7311"); + reg_w(gspca_dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */ + reg_w(gspca_dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + reg_w(gspca_dev, 0xff, 0x04); + reg_w(gspca_dev, 0x27, 0x80); + reg_w(gspca_dev, 0x28, 0xca); + reg_w(gspca_dev, 0x29, 0x53); + reg_w(gspca_dev, 0x2a, 0x0e); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x3e, 0x20); + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x05; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + sd->autogain = AUTOGAIN_DEF; + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int brightness; + +/*jfm: inverted?*/ + brightness = BRIGHTNESS_MAX - sd->brightness; + reg_w(gspca_dev, 0xff, 0x04); +/* reg_w(gspca_dev, 0x0e, 0x00); */ + reg_w(gspca_dev, 0x0f, brightness); + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); + PDEBUG(D_CONF|D_STREAM, "brightness: %i", brightness); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x80, sd->contrast); + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); + PDEBUG(D_CONF|D_STREAM, "contrast: %i", sd->contrast); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x10, sd->colors); + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); + PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0x78, 0x00); /* Turn on LED */ + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0xff, 0x01); + reg_w_buf(gspca_dev, 0x0002, "\x48\x0a\x40\x08\x00\x00\x08\x00", 8); + reg_w_buf(gspca_dev, 0x000a, "\x06\xff\x11\xff\x5a\x30\x90\x4c", 8); + reg_w_buf(gspca_dev, 0x0012, "\x00\x07\x00\x0a\x10\x00\xa0\x10", 8); + reg_w_buf(gspca_dev, 0x001a, "\x02\x00\x00\x00\x00\x0b\x01\x00", 8); + reg_w_buf(gspca_dev, 0x0022, "\x00\x00\x00\x00\x00\x00\x00\x00", 8); + reg_w_buf(gspca_dev, 0x002a, "\x00\x00\x00", 3); + reg_w_buf(gspca_dev, 0x003e, "\x00\x00\x78\x52\x4a\x52\x78\x6e", 8); + reg_w_buf(gspca_dev, 0x0046, "\x48\x46\x48\x6e\x5f\x49\x42\x49", 8); + reg_w_buf(gspca_dev, 0x004e, "\x5f\x5f\x49\x42\x49\x5f\x6e\x48", 8); + reg_w_buf(gspca_dev, 0x0056, "\x46\x48\x6e\x78\x52\x4a\x52\x78", 8); + reg_w_buf(gspca_dev, 0x005e, "\x00\x00\x09\x1b\x34\x49\x5c\x9b", 8); + reg_w_buf(gspca_dev, 0x0066, "\xd0\xff", 2); + reg_w_buf(gspca_dev, 0x0078, "\x44\x00\xf2\x01\x01\x80", 6); + reg_w_buf(gspca_dev, 0x007f, "\x2a\x1c\x00\xc8\x02\x58\x03\x84", 8); + reg_w_buf(gspca_dev, 0x0087, "\x12\x00\x1a\x04\x08\x0c\x10\x14", 8); + reg_w_buf(gspca_dev, 0x008f, "\x18\x20", 2); + reg_w_buf(gspca_dev, 0x0096, "\x01\x08\x04", 3); + reg_w_buf(gspca_dev, 0x00a0, "\x44\x44\x44\x04", 4); + reg_w_buf(gspca_dev, 0x00f0, "\x01\x00\x00\x00\x22\x00\x20\x00", 8); + reg_w_buf(gspca_dev, 0x00f8, "\x3f\x00\x0a\x01\x00", 5); + + reg_w(gspca_dev, 0xff, 0x04); + reg_w(gspca_dev, 0x02, 0x04); + reg_w(gspca_dev, 0x03, 0x54); + reg_w(gspca_dev, 0x04, 0x07); + reg_w(gspca_dev, 0x05, 0x2b); + reg_w(gspca_dev, 0x06, 0x09); + reg_w(gspca_dev, 0x07, 0x0f); + reg_w(gspca_dev, 0x08, 0x09); + reg_w(gspca_dev, 0x09, 0x00); + reg_w(gspca_dev, 0x0c, 0x07); + reg_w(gspca_dev, 0x0d, 0x00); + reg_w(gspca_dev, 0x0e, 0x00); + reg_w(gspca_dev, 0x0f, 0x62); + reg_w(gspca_dev, 0x10, 0x08); + reg_w(gspca_dev, 0x12, 0x07); + reg_w(gspca_dev, 0x13, 0x00); + reg_w(gspca_dev, 0x14, 0x00); + reg_w(gspca_dev, 0x15, 0x00); + reg_w(gspca_dev, 0x16, 0x00); + reg_w(gspca_dev, 0x17, 0x00); + reg_w(gspca_dev, 0x18, 0x00); + reg_w(gspca_dev, 0x19, 0x00); + reg_w(gspca_dev, 0x1a, 0x00); + reg_w(gspca_dev, 0x1b, 0x03); + reg_w(gspca_dev, 0x1c, 0xa0); + reg_w(gspca_dev, 0x1d, 0x01); + reg_w(gspca_dev, 0x1e, 0xf4); + reg_w(gspca_dev, 0x21, 0x00); + reg_w(gspca_dev, 0x22, 0x08); + reg_w(gspca_dev, 0x24, 0x03); + reg_w(gspca_dev, 0x26, 0x00); + reg_w(gspca_dev, 0x27, 0x01); + reg_w(gspca_dev, 0x28, 0xca); + reg_w(gspca_dev, 0x29, 0x10); + reg_w(gspca_dev, 0x2a, 0x06); + reg_w(gspca_dev, 0x2b, 0x78); + reg_w(gspca_dev, 0x2c, 0x00); + reg_w(gspca_dev, 0x2d, 0x00); + reg_w(gspca_dev, 0x2e, 0x00); + reg_w(gspca_dev, 0x2f, 0x00); + reg_w(gspca_dev, 0x30, 0x23); + reg_w(gspca_dev, 0x31, 0x28); + reg_w(gspca_dev, 0x32, 0x04); + reg_w(gspca_dev, 0x33, 0x11); + reg_w(gspca_dev, 0x34, 0x00); + reg_w(gspca_dev, 0x35, 0x00); + reg_w(gspca_dev, 0x11, 0x01); + setcontrast(gspca_dev); + setbrightness(gspca_dev); + setcolors(gspca_dev); + + /* set correct resolution */ + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + case 2: /* 160x120 */ + reg_w(gspca_dev, 0xff, 0x04); + reg_w(gspca_dev, 0x02, 0x03); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x08, 0x09); + reg_w(gspca_dev, 0x17, 0x20); + reg_w(gspca_dev, 0x1b, 0x00); +/* reg_w(gspca_dev, 0x80, 0x69); */ + reg_w(gspca_dev, 0x87, 0x10); + break; + case 1: /* 320x240 */ + reg_w(gspca_dev, 0xff, 0x04); + reg_w(gspca_dev, 0x02, 0x03); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x08, 0x09); + reg_w(gspca_dev, 0x17, 0x30); +/* reg_w(gspca_dev, 0x80, 0x3f); */ + reg_w(gspca_dev, 0x87, 0x11); + break; + case 0: /* 640x480 */ + reg_w(gspca_dev, 0xff, 0x04); + reg_w(gspca_dev, 0x02, 0x03); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x08, 0x08); + reg_w(gspca_dev, 0x17, 0x00); +/* reg_w(gspca_dev, 0x80, 0x1c); */ + reg_w(gspca_dev, 0x87, 0x12); + break; + } + + /* start stream */ + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x04); + reg_w(gspca_dev, 0x78, 0x05); + + if (sd->autogain) { + sd->ag_cnt = AG_CNT_START; + sd->avg_lum = 0; + } else { + sd->ag_cnt = -1; + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0xff, 0x04); + reg_w(gspca_dev, 0x27, 0x80); + reg_w(gspca_dev, 0x28, 0xca); + reg_w(gspca_dev, 0x29, 0x53); + reg_w(gspca_dev, 0x2a, 0x0e); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x3e, 0x20); + reg_w(gspca_dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0xff, 0x04); + reg_w(gspca_dev, 0x27, 0x80); + reg_w(gspca_dev, 0x28, 0xca); + reg_w(gspca_dev, 0x29, 0x53); + reg_w(gspca_dev, 0x2a, 0x0e); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x3e, 0x20); + reg_w(gspca_dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ +} + +static void setautogain(struct gspca_dev *gspca_dev, int luma) +{ + int luma_mean = 128; + int luma_delta = 20; + __u8 spring = 5; + int Gbright; + + Gbright = reg_r(gspca_dev, 0x02); + PDEBUG(D_FRAM, "luma mean %d", luma); + if (luma < luma_mean - luma_delta || + luma > luma_mean + luma_delta) { + Gbright += (luma_mean - luma) >> spring; + if (Gbright > 0x1a) + Gbright = 0x1a; + else if (Gbright < 4) + Gbright = 4; + PDEBUG(D_FRAM, "gbright %d", Gbright); + reg_w(gspca_dev, 0xff, 0x04); + reg_w(gspca_dev, 0x0f, Gbright); + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned char tmpbuf[4]; + int i, p, ffseq; + +/* if (len < 5) { */ + if (len < 6) { +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + + ffseq = sd->ffseq; + + for (p = 0; p < len - 6; p++) { + if ((data[0 + p] == 0xff) + && (data[1 + p] == 0xff) + && (data[2 + p] == 0x00) + && (data[3 + p] == 0xff) + && (data[4 + p] == 0x96)) { + + /* start of frame */ + if (sd->ag_cnt >= 0 && p > 28) { + sd->avg_lum += data[p - 23]; + if (--sd->ag_cnt < 0) { + sd->ag_cnt = AG_CNT_START; + setautogain(gspca_dev, + sd->avg_lum / AG_CNT_START); + sd->avg_lum = 0; + } + } + + /* copy the end of data to the current frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, p); + + /* put the JPEG header in the new frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + (unsigned char *) pac7311_jpeg_header, + 12); + tmpbuf[0] = gspca_dev->height >> 8; + tmpbuf[1] = gspca_dev->height & 0xff; + tmpbuf[2] = gspca_dev->width >> 8; + tmpbuf[3] = gspca_dev->width & 0xff; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + tmpbuf, 4); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + (unsigned char *) &pac7311_jpeg_header[16], + PAC7311_JPEG_HEADER_SIZE - 16); + + data += p + 7; + len -= p + 7; + ffseq = 0; + break; + } + } + + /* remove the 'ff ff ff xx' sequences */ + switch (ffseq) { + case 3: + data += 1; + len -= 1; + break; + case 2: + if (data[0] == 0xff) { + data += 2; + len -= 2; + frame->data_end -= 2; + } + break; + case 1: + if (data[0] == 0xff + && data[1] == 0xff) { + data += 3; + len -= 3; + frame->data_end -= 1; + } + break; + } + for (i = 0; i < len - 4; i++) { + if (data[i] == 0xff + && data[i + 1] == 0xff + && data[i + 2] == 0xff) { + memmove(&data[i], &data[i + 4], len - i - 4); + len -= 4; + } + } + ffseq = 0; + if (data[len - 4] == 0xff) { + if (data[len - 3] == 0xff + && data[len - 2] == 0xff) { + len -= 4; + } + } else if (data[len - 3] == 0xff) { + if (data[len - 2] == 0xff + && data[len - 1] == 0xff) + ffseq = 3; + } else if (data[len - 2] == 0xff) { + if (data[len - 1] == 0xff) + ffseq = 2; + } else if (data[len - 1] == 0xff) + ffseq = 1; + sd->ffseq = ffseq; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ +/* sd->brightness = reg_r(gspca_dev, 0x08); + return sd->brightness; */ +/* PDEBUG(D_CONF, "Called pac7311_getbrightness: Not implemented yet"); */ +} + + + +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; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + +/* getcontrast(gspca_dev); */ + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + +/* getcolors(gspca_dev); */ + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) { + sd->ag_cnt = AG_CNT_START; + sd->avg_lum = 0; + } else { + sd->ag_cnt = -1; + } + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x093a, 0x2600), DVNM("Typhoon")}, + {USB_DEVICE(0x093a, 0x2601), DVNM("Philips SPC610NC")}, + {USB_DEVICE(0x093a, 0x2603), DVNM("PAC7312")}, + {USB_DEVICE(0x093a, 0x2608), DVNM("Trust WB-3300p")}, + {USB_DEVICE(0x093a, 0x260e), DVNM("Gigaware VGA PC Camera")}, + /* and also ', Trust WB-3350p, SIGMA cam 2350' */ + {USB_DEVICE(0x093a, 0x260f), DVNM("SnakeCam")}, + {USB_DEVICE(0x093a, 0x2621), DVNM("PAC731x")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c new file mode 100644 index 00000000000..dbeebe8625c --- /dev/null +++ b/drivers/media/video/gspca/sonixb.c @@ -0,0 +1,1477 @@ +/* + * sonix sn9c102 (bayer) library + * Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr + * Add Pas106 Stefano Mozzi (C) 2004 + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "sonixb" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 8) +static const char version[] = "2.1.8"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + struct sd_desc sd_desc; /* our nctrls differ dependend upon the + sensor, so we use a per cam copy */ + atomic_t avg_lum; + + unsigned char gain; + unsigned char exposure; + unsigned char brightness; + unsigned char autogain; + unsigned char autogain_ignore_frames; + unsigned char freq; /* light freq filter setting */ + unsigned char saturation; + unsigned char hue; + unsigned char contrast; + + unsigned char fr_h_sz; /* size of frame header */ + char sensor; /* Type of image sensor chip */ +#define SENSOR_HV7131R 0 +#define SENSOR_OV6650 1 +#define SENSOR_OV7630 2 +#define SENSOR_OV7630_3 3 +#define SENSOR_PAS106 4 +#define SENSOR_PAS202 5 +#define SENSOR_TAS5110 6 +#define SENSOR_TAS5130CXX 7 + char sensor_has_gain; + __u8 sensor_addr; +}; + +#define COMP2 0x8f +#define COMP 0xc7 /* 0x87 //0x07 */ +#define COMP1 0xc9 /* 0x89 //0x09 */ + +#define MCK_INIT 0x63 +#define MCK_INIT1 0x20 /*fixme: Bayer - 0x50 for JPEG ??*/ + +#define SYS_CLK 0x04 + +/* We calculate the autogain at the end of the transfer of a frame, at this + moment a frame with the old settings is being transmitted, and a frame is + being captured with the old settings. So if we adjust the autogain we must + ignore atleast the 2 next frames for the new settings to come into effect + before doing any other adjustments */ +#define AUTOGAIN_IGNORE_FRAMES 3 +#define AUTOGAIN_DEADZONE 1000 +#define DESIRED_AVG_LUM 7000 + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 127 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 255, + .step = 1, +#define GAIN_DEF 127 +#define GAIN_KNEE 200 + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", +#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ +#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = EXPOSURE_DEF, + .flags = 0, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Gain (and Exposure)", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, + .flags = 0, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 1 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, +#define SATURATION_DEF 127 + .default_value = SATURATION_DEF, + }, + .set = sd_setsaturation, + .get = sd_getsaturation, + }, + { + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 255, + .step = 1, +#define HUE_DEF 127 + .default_value = HUE_DEF, + }, + .set = sd_sethue, + .get = sd_gethue, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, +#define CONTRAST_DEF 127 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_SN9C10X, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +static const __u8 probe_ov7630[] = {0x08, 0x44}; + +static const __u8 initHv7131[] = { + 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, /* shift from 0x02 0x01 0x00 */ + 0x28, 0x1e, 0x60, 0x8a, 0x20, + 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c +}; +static const __u8 hv7131_sensor_init[][8] = { + {0xc0, 0x11, 0x31, 0x38, 0x2a, 0x2e, 0x00, 0x10}, + {0xa0, 0x11, 0x01, 0x08, 0x2a, 0x2e, 0x00, 0x10}, + {0xb0, 0x11, 0x20, 0x00, 0xd0, 0x2e, 0x00, 0x10}, + {0xc0, 0x11, 0x25, 0x03, 0x0e, 0x28, 0x00, 0x16}, + {0xa0, 0x11, 0x30, 0x10, 0x0e, 0x28, 0x00, 0x15}, +}; +static const __u8 initOv6650[] = { + 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x0b, + 0x10, 0x1d, 0x10, 0x00, 0x06, 0x1f, 0x00 +}; +static const __u8 ov6650_sensor_init[][8] = +{ + /* Bright, contrast, etc are set througth SCBB interface. + * AVCAP on win2 do not send any data on this controls. */ + /* Anyway, some registers appears to alter bright and constrat */ + + /* Reset sensor */ + {0xa0, 0x60, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, + /* Set clock register 0x11 low nibble is clock divider */ + {0xd0, 0x60, 0x11, 0xc0, 0x1b, 0x18, 0xc1, 0x10}, + /* Next some unknown stuff */ + {0xb0, 0x60, 0x15, 0x00, 0x02, 0x18, 0xc1, 0x10}, +/* {0xa0, 0x60, 0x1b, 0x01, 0x02, 0x18, 0xc1, 0x10}, + * THIS SET GREEN SCREEN + * (pixels could be innverted in decode kind of "brg", + * but blue wont be there. Avoid this data ... */ + {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, /* format out? */ + {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, + {0xa0, 0x60, 0x30, 0x3d, 0x0A, 0xd8, 0xa4, 0x10}, + /* Enable rgb brightness control */ + {0xa0, 0x60, 0x61, 0x08, 0x00, 0x00, 0x00, 0x10}, + /* HDG: Note windows uses the line below, which sets both register 0x60 + and 0x61 I believe these registers of the ov6650 are identical as + those of the ov7630, because if this is true the windows settings + add a bit additional red gain and a lot additional blue gain, which + matches my findings that the windows settings make blue much too + blue and red a little too red. + {0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10}, */ + /* Some more unknown stuff */ + {0xa0, 0x60, 0x68, 0x04, 0x68, 0xd8, 0xa4, 0x10}, + {0xd0, 0x60, 0x17, 0x24, 0xd6, 0x04, 0x94, 0x10}, /* Clipreg */ +}; + +static const __u8 initOv7630[] = { + 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ + 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ + 0x28, 0x1e, /* H & V sizes r15 .. r16 */ + 0x68, COMP1, MCK_INIT1, /* r17 .. r19 */ + 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c /* r1a .. r1f */ +}; +static const __u8 initOv7630_3[] = { + 0x44, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0x80, /* r01 .. r08 */ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, /* r09 .. r10 */ + 0x00, 0x01, 0x01, 0x0a, /* r11 .. r14 */ + 0x28, 0x1e, /* H & V sizes r15 .. r16 */ + 0x68, 0x8f, MCK_INIT1, /* r17 .. r19 */ + 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c, 0x00, /* r1a .. r20 */ + 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, /* r21 .. r28 */ + 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, 0xff /* r29 .. r30 */ +}; +static const __u8 ov7630_sensor_init_com[][8] = { + {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10}, +/* {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10}, jfm */ + {0xd0, 0x21, 0x12, 0x1c, 0x00, 0x80, 0x34, 0x10}, /* jfm */ + {0xa0, 0x21, 0x1b, 0x04, 0x00, 0x80, 0x34, 0x10}, + {0xa0, 0x21, 0x20, 0x44, 0x00, 0x80, 0x34, 0x10}, + {0xa0, 0x21, 0x23, 0xee, 0x00, 0x80, 0x34, 0x10}, + {0xd0, 0x21, 0x26, 0xa0, 0x9a, 0xa0, 0x30, 0x10}, + {0xb0, 0x21, 0x2a, 0x80, 0x00, 0xa0, 0x30, 0x10}, + {0xb0, 0x21, 0x2f, 0x3d, 0x24, 0xa0, 0x30, 0x10}, + {0xa0, 0x21, 0x32, 0x86, 0x24, 0xa0, 0x30, 0x10}, + {0xb0, 0x21, 0x60, 0xa9, 0x4a, 0xa0, 0x30, 0x10}, +/* {0xb0, 0x21, 0x60, 0xa9, 0x42, 0xa0, 0x30, 0x10}, * jfm */ + {0xa0, 0x21, 0x65, 0x00, 0x42, 0xa0, 0x30, 0x10}, + {0xa0, 0x21, 0x69, 0x38, 0x42, 0xa0, 0x30, 0x10}, + {0xc0, 0x21, 0x6f, 0x88, 0x0b, 0x00, 0x30, 0x10}, + {0xc0, 0x21, 0x74, 0x21, 0x8e, 0x00, 0x30, 0x10}, + {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10}, + {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, +}; +static const __u8 ov7630_sensor_init[][8] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 200ms */ + {0xa0, 0x21, 0x11, 0x01, 0xbd, 0x06, 0xf6, 0x10}, /* jfm */ + {0xa0, 0x21, 0x10, 0x57, 0xbd, 0x06, 0xf6, 0x16}, + {0xa0, 0x21, 0x76, 0x02, 0xbd, 0x06, 0xf6, 0x16}, + {0xa0, 0x21, 0x00, 0x10, 0xbd, 0x06, 0xf6, 0x15}, /* gain */ +}; +static const __u8 ov7630_sensor_init_3[][8] = { + {0xa0, 0x21, 0x2a, 0xa0, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x21, 0x2a, 0x80, 0x00, 0x00, 0x00, 0x10}, +}; + +static const __u8 initPas106[] = { + 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, + 0x16, 0x12, 0x28, COMP1, MCK_INIT1, + 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c +}; +/* compression 0x86 mckinit1 0x2b */ +static const __u8 pas106_data[][2] = { + {0x02, 0x04}, /* Pixel Clock Divider 6 */ + {0x03, 0x13}, /* Frame Time MSB */ +/* {0x03, 0x12}, * Frame Time MSB */ + {0x04, 0x06}, /* Frame Time LSB */ +/* {0x04, 0x05}, * Frame Time LSB */ + {0x05, 0x65}, /* Shutter Time Line Offset */ +/* {0x05, 0x6d}, * Shutter Time Line Offset */ +/* {0x06, 0xb1}, * Shutter Time Pixel Offset */ + {0x06, 0xcd}, /* Shutter Time Pixel Offset */ + {0x07, 0xc1}, /* Black Level Subtract Sign */ +/* {0x07, 0x00}, * Black Level Subtract Sign */ + {0x08, 0x06}, /* Black Level Subtract Level */ + {0x08, 0x06}, /* Black Level Subtract Level */ +/* {0x08, 0x01}, * Black Level Subtract Level */ + {0x09, 0x05}, /* Color Gain B Pixel 5 a */ + {0x0a, 0x04}, /* Color Gain G1 Pixel 1 5 */ + {0x0b, 0x04}, /* Color Gain G2 Pixel 1 0 5 */ + {0x0c, 0x05}, /* Color Gain R Pixel 3 1 */ + {0x0d, 0x00}, /* Color GainH Pixel */ + {0x0e, 0x0e}, /* Global Gain */ + {0x0f, 0x00}, /* Contrast */ + {0x10, 0x06}, /* H&V synchro polarity */ + {0x11, 0x06}, /* ?default */ + {0x12, 0x06}, /* DAC scale */ + {0x14, 0x02}, /* ?default */ + {0x13, 0x01}, /* Validate Settings */ +}; +static const __u8 initPas202[] = { + 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x03, 0x0a, /* 6 */ + 0x28, 0x1e, 0x28, 0x89, 0x30, + 0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c +}; +static const __u8 pas202_sensor_init[][8] = { + {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10}, + {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10}, + {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x00, 0x32, 0x10}, + {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, + {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x10}, + {0xb0, 0x40, 0x0e, 0x00, 0x3d, 0x00, 0x63, 0x10}, + + {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x10, 0x08, 0x3d, 0x00, 0x63, 0x15}, + {0xa0, 0x40, 0x02, 0x04, 0x3d, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, + {0xb0, 0x40, 0x0e, 0x00, 0x31, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}, + {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, +}; + +static const __u8 initTas5110[] = { + 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x01, 0x00, 0x46, 0x09, 0x0a, /* shift from 0x45 0x09 0x0a */ + 0x16, 0x12, 0x60, 0x86, 0x2b, + 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 +}; +static const __u8 tas5110_sensor_init[][8] = { + {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10}, + {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10}, + {0xa0, 0x61, 0x9a, 0xca, 0x00, 0x00, 0x00, 0x17}, +}; + +static const __u8 initTas5130[] = { + 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x01, 0x00, 0x69, 0x0c, 0x0a, + 0x28, 0x1e, 0x60, COMP, MCK_INIT, + 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c +}; +static const __u8 tas5130_sensor_init[][8] = { +/* {0x30, 0x11, 0x00, 0x40, 0x47, 0x00, 0x00, 0x10}, + * shutter 0x47 short exposure? */ + {0x30, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x10}, + /* shutter 0x01 long exposure */ + {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}, +}; + +/* get one byte in gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 value) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, + 0, /* index */ + gspca_dev->usb_buf, 1, + 500); +} + +static void reg_w(struct gspca_dev *gspca_dev, + __u16 value, + const __u8 *buffer, + int len) +{ +#ifdef CONFIG_VIDEO_ADV_DEBUG + if (len > sizeof gspca_dev->usb_buf) { + PDEBUG(D_ERR|D_PACK, "reg_w: buffer overflow"); + return; + } +#endif + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, + 0, /* index */ + gspca_dev->usb_buf, len, + 500); +} + +static void reg_w_big(struct gspca_dev *gspca_dev, + __u16 value, + const __u8 *buffer, + int len) +{ + __u8 *tmpbuf; + + tmpbuf = kmalloc(len, GFP_KERNEL); + memcpy(tmpbuf, buffer, len); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, + 0, /* index */ + tmpbuf, len, + 500); + kfree(tmpbuf); +} + +static int i2c_w(struct gspca_dev *gspca_dev, const __u8 *buffer) +{ + int retry = 60; + + /* is i2c ready */ + reg_w(gspca_dev, 0x08, buffer, 8); + while (retry--) { + msleep(10); + reg_r(gspca_dev, 0x08); + if (gspca_dev->usb_buf[0] & 0x04) { + if (gspca_dev->usb_buf[0] & 0x08) + return -1; + return 0; + } + } + return -1; +} + +static void i2c_w_vector(struct gspca_dev *gspca_dev, + const __u8 buffer[][8], int len) +{ + for (;;) { + reg_w(gspca_dev, 0x08, *buffer, 8); + len -= 8; + if (len <= 0) + break; + buffer++; + } +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value; + + switch (sd->sensor) { + case SENSOR_OV6650: + case SENSOR_OV7630_3: + case SENSOR_OV7630: { + __u8 i2cOV[] = + {0xa0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}; + + /* change reg 0x06 */ + i2cOV[1] = sd->sensor_addr; + i2cOV[3] = sd->brightness; + if (i2c_w(gspca_dev, i2cOV) < 0) + goto err; + break; + } + case SENSOR_PAS106: { + __u8 i2c1[] = + {0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14}; + + i2c1[3] = sd->brightness >> 3; + i2c1[2] = 0x0e; + if (i2c_w(gspca_dev, i2c1) < 0) + goto err; + i2c1[3] = 0x01; + i2c1[2] = 0x13; + if (i2c_w(gspca_dev, i2c1) < 0) + goto err; + break; + } + case SENSOR_PAS202: { + /* __u8 i2cpexpo1[] = + {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x16}; */ + __u8 i2cpexpo[] = + {0xb0, 0x40, 0x0e, 0x01, 0xab, 0x00, 0x63, 0x16}; + __u8 i2cp202[] = + {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}; + static __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}; + + /* change reg 0x10 */ + i2cpexpo[4] = 0xff - sd->brightness; +/* if(i2c_w(gspca_dev,i2cpexpo1) < 0) + goto err; */ +/* if(i2c_w(gspca_dev,i2cpdoit) < 0) + goto err; */ + if (i2c_w(gspca_dev, i2cpexpo) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpdoit) < 0) + goto err; + i2cp202[3] = sd->brightness >> 3; + if (i2c_w(gspca_dev, i2cp202) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpdoit) < 0) + goto err; + break; + } + case SENSOR_TAS5130CXX: { + __u8 i2c[] = + {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; + + value = 0xff - sd->brightness; + i2c[4] = value; + PDEBUG(D_CONF, "brightness %d : %d", value, i2c[4]); + if (i2c_w(gspca_dev, i2c) < 0) + goto err; + break; + } + case SENSOR_TAS5110: + /* FIXME figure out howto control brightness on TAS5110 */ + break; + } + return; +err: + PDEBUG(D_ERR, "i2c error brightness"); +} + +static void setsensorgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned char gain = sd->gain; + + switch (sd->sensor) { + + case SENSOR_TAS5110: { + __u8 i2c[] = + {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; + + i2c[4] = 255 - gain; + if (i2c_w(gspca_dev, i2c) < 0) + goto err; + break; + } + + case SENSOR_OV6650: + gain >>= 1; + /* fall thru */ + case SENSOR_OV7630_3: { + __u8 i2c[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; + + i2c[1] = sd->sensor_addr; + i2c[3] = gain >> 2; + if (i2c_w(gspca_dev, i2c) < 0) + goto err; + break; + } + } + return; +err: + PDEBUG(D_ERR, "i2c error gain"); +} + +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 gain; + __u8 rgb_value; + + gain = sd->gain >> 4; + + /* red and blue gain */ + rgb_value = gain << 4 | gain; + reg_w(gspca_dev, 0x10, &rgb_value, 1); + /* green gain */ + rgb_value = gain; + reg_w(gspca_dev, 0x11, &rgb_value, 1); + + if (sd->sensor_has_gain) + setsensorgain(gspca_dev); +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_TAS5110: { + __u8 reg; + + /* register 19's high nibble contains the sn9c10x clock divider + The high nibble configures the no fps according to the + formula: 60 / high_nibble. With a maximum of 30 fps */ + reg = 120 * sd->exposure / 1000; + if (reg < 2) + reg = 2; + else if (reg > 15) + reg = 15; + reg = (reg << 4) | 0x0b; + reg_w(gspca_dev, 0x19, ®, 1); + break; + } + case SENSOR_OV6650: + case SENSOR_OV7630_3: { + /* The ov6650 / ov7630 have 2 registers which both influence + exposure, register 11, whose low nibble sets the nr off fps + according to: fps = 30 / (low_nibble + 1) + + The fps configures the maximum exposure setting, but it is + possible to use less exposure then what the fps maximum + allows by setting register 10. register 10 configures the + actual exposure as quotient of the full exposure, with 0 + being no exposure at all (not very usefull) and reg10_max + being max exposure possible at that framerate. + + The code maps our 0 - 510 ms exposure ctrl to these 2 + registers, trying to keep fps as high as possible. + */ + __u8 i2c[] = {0xb0, 0x00, 0x10, 0x00, 0xc0, 0x00, 0x00, 0x10}; + int reg10, reg11; + /* ov6645 datasheet says reg10_max is 9a, but that uses + tline * 2 * reg10 as formula for calculating texpo, the + ov6650 probably uses the same formula as the 7730 which uses + tline * 4 * reg10, which explains why the reg10max we've + found experimentally for the ov6650 is exactly half that of + the ov6645. The ov7630 datasheet says the max is 0x41. */ + const int reg10_max = (sd->sensor == SENSOR_OV6650) + ? 0x4d : 0x41; + + reg11 = (60 * sd->exposure + 999) / 1000; + if (reg11 < 1) + reg11 = 1; + else if (reg11 > 16) + reg11 = 16; + + /* frame exposure time in ms = 1000 * reg11 / 30 -> + reg10 = sd->exposure * 2 * reg10_max / (1000 * reg11 / 30) */ + reg10 = (sd->exposure * 60 * reg10_max) / (1000 * reg11); + + /* Don't allow this to get below 10 when using autogain, the + steps become very large (relatively) when below 10 causing + the image to oscilate from much too dark, to much too bright + and back again. */ + if (sd->autogain && reg10 < 10) + reg10 = 10; + else if (reg10 > reg10_max) + reg10 = reg10_max; + + /* Write reg 10 and reg11 low nibble */ + i2c[1] = sd->sensor_addr; + i2c[3] = reg10; + i2c[4] |= reg11 - 1; + if (sd->sensor == SENSOR_OV7630_3) { + __u8 reg76 = reg10 & 0x03; + __u8 i2c_reg76[] = {0xa0, 0x21, 0x76, 0x00, + 0x00, 0x00, 0x00, 0x10}; + reg10 >>= 2; + i2c_reg76[3] = reg76; + if (i2c_w(gspca_dev, i2c_reg76) < 0) + PDEBUG(D_ERR, "i2c error exposure"); + } + if (i2c_w(gspca_dev, i2c) < 0) + PDEBUG(D_ERR, "i2c error exposure"); + break; + } + } +} + +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_OV6650: + case SENSOR_OV7630_3: { + /* Framerate adjust register for artificial light 50 hz flicker + compensation, identical to ov6630 0x2b register, see ov6630 + datasheet. + 0x4f -> (30 fps -> 25 fps), 0x00 -> no adjustment */ + __u8 i2c[] = {0xa0, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}; + switch (sd->freq) { + default: +/* case 0: * no filter*/ +/* case 2: * 60 hz */ + i2c[3] = 0; + break; + case 1: /* 50 hz */ + i2c[3] = (sd->sensor == SENSOR_OV6650) + ? 0x4f : 0x8a; + break; + } + i2c[1] = sd->sensor_addr; + if (i2c_w(gspca_dev, i2c) < 0) + PDEBUG(D_ERR, "i2c error setfreq"); + break; + } + } +} + +static void setsaturation(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { +/* case SENSOR_OV6650: */ + case SENSOR_OV7630_3: + case SENSOR_OV7630: { + __u8 i2c[] = {0xa0, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}; + i2c[1] = sd->sensor_addr; + i2c[3] = sd->saturation & 0xf0; + if (i2c_w(gspca_dev, i2c) < 0) + PDEBUG(D_ERR, "i2c error setsaturation"); + else + PDEBUG(D_CONF, "saturation set to: %d", + (int)sd->saturation); + break; + } + } +} + +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { +/* case SENSOR_OV6650: */ + case SENSOR_OV7630_3: + case SENSOR_OV7630: { + __u8 i2c[] = {0xa0, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10}; + i2c[1] = sd->sensor_addr; + i2c[3] = 0x20 | (sd->hue >> 3); + if (i2c_w(gspca_dev, i2c) < 0) + PDEBUG(D_ERR, "i2c error setsaturation"); + else + PDEBUG(D_CONF, "hue set to: %d", (int)sd->hue); + break; + } + } +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { +/* case SENSOR_OV6650: */ + case SENSOR_OV7630_3: + case SENSOR_OV7630: { + __u8 i2c[] = {0xa0, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}; + i2c[1] = sd->sensor_addr; + i2c[3] = 0x20 | (sd->contrast >> 3); + if (i2c_w(gspca_dev, i2c) < 0) + PDEBUG(D_ERR, "i2c error setcontrast"); + else + PDEBUG(D_CONF, "contrast set to: %d", + (int)sd->contrast); + break; + } + } +} + + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum = atomic_read(&sd->avg_lum); + + if (avg_lum == -1) + return; + + if (sd->autogain_ignore_frames > 0) + sd->autogain_ignore_frames--; + else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, + sd->brightness * DESIRED_AVG_LUM / 127, + AUTOGAIN_DEADZONE, GAIN_KNEE, EXPOSURE_KNEE)) { + PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d\n", + (int)sd->gain, (int)sd->exposure); + sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; + } +} + +/* 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; + __u16 product; + int sif = 0; + + /* nctrls depends upon the sensor, so we use a per cam copy */ + memcpy(&sd->sd_desc, gspca_dev->sd_desc, sizeof(struct sd_desc)); + gspca_dev->sd_desc = &sd->sd_desc; + + sd->fr_h_sz = 12; /* default size of the frame header */ + sd->sd_desc.nctrls = 2; /* default nb of ctrls */ + sd->autogain = AUTOGAIN_DEF; /* default is autogain active */ + + product = id->idProduct; +/* switch (id->idVendor) { */ +/* case 0x0c45: * Sonix */ + switch (product) { + case 0x6001: /* SN9C102 */ + case 0x6005: /* SN9C101 */ + case 0x6007: /* SN9C101 */ + sd->sensor = SENSOR_TAS5110; + sd->sensor_has_gain = 1; + sd->sd_desc.nctrls = 4; + sd->sd_desc.dq_callback = do_autogain; + sif = 1; + break; + case 0x6009: /* SN9C101 */ + case 0x600d: /* SN9C101 */ + case 0x6029: /* SN9C101 */ + sd->sensor = SENSOR_PAS106; + sif = 1; + break; + case 0x6011: /* SN9C101 - SN9C101G */ + sd->sensor = SENSOR_OV6650; + sd->sensor_has_gain = 1; + sd->sensor_addr = 0x60; + sd->sd_desc.nctrls = 5; + sd->sd_desc.dq_callback = do_autogain; + sif = 1; + break; + case 0x6019: /* SN9C101 */ + case 0x602c: /* SN9C102 */ + case 0x602e: /* SN9C102 */ + sd->sensor = SENSOR_OV7630; + sd->sensor_addr = 0x21; + break; + case 0x60b0: /* SN9C103 */ + sd->sensor = SENSOR_OV7630_3; + sd->sensor_addr = 0x21; + sd->fr_h_sz = 18; /* size of frame header */ + sd->sensor_has_gain = 1; + sd->sd_desc.nctrls = 8; + sd->sd_desc.dq_callback = do_autogain; + sd->autogain = 0; + break; + case 0x6024: /* SN9C102 */ + case 0x6025: /* SN9C102 */ + sd->sensor = SENSOR_TAS5130CXX; + break; + case 0x6028: /* SN9C102 */ + sd->sensor = SENSOR_PAS202; + break; + case 0x602d: /* SN9C102 */ + sd->sensor = SENSOR_HV7131R; + break; + case 0x60af: /* SN9C103 */ + sd->sensor = SENSOR_PAS202; + sd->fr_h_sz = 18; /* size of frame header (?) */ + break; + } +/* break; */ +/* } */ + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + if (!sif) { + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + if (sd->sensor == SENSOR_OV7630_3) { + /* We only have 320x240 & 640x480 */ + cam->cam_mode++; + cam->nmodes--; + } + } else { + cam->cam_mode = sif_mode; + cam->nmodes = ARRAY_SIZE(sif_mode); + } + sd->brightness = BRIGHTNESS_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPOSURE_DEF; + sd->freq = FREQ_DEF; + sd->contrast = CONTRAST_DEF; + sd->saturation = SATURATION_DEF; + sd->hue = HUE_DEF; + if (sd->sensor == SENSOR_OV7630_3) /* jfm: from win trace */ + reg_w(gspca_dev, 0x01, probe_ov7630, sizeof probe_ov7630); + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + reg_r(gspca_dev, 0x00); + if (gspca_dev->usb_buf[0] != 0x10) + return -ENODEV; + return 0; +} + +static void pas106_i2cinit(struct gspca_dev *gspca_dev) +{ + int i; + const __u8 *data; + __u8 i2c1[] = { 0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 }; + + i = ARRAY_SIZE(pas106_data); + data = pas106_data[0]; + while (--i >= 0) { + memcpy(&i2c1[2], data, 2); + /* copy 2 bytes from the template */ + if (i2c_w(gspca_dev, i2c1) < 0) + PDEBUG(D_ERR, "i2c error pas106"); + data += 2; + } +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int mode, l; + const __u8 *sn9c10x; + __u8 reg01, reg17; + __u8 reg17_19[3]; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + switch (sd->sensor) { + case SENSOR_HV7131R: + sn9c10x = initHv7131; + reg17_19[0] = 0x60; + reg17_19[1] = (mode << 4) | 0x8a; + reg17_19[2] = 0x20; + break; + case SENSOR_OV6650: + sn9c10x = initOv6650; + reg17_19[0] = 0x68; + reg17_19[1] = (mode << 4) | 0x8b; + reg17_19[2] = 0x20; + break; + case SENSOR_OV7630: + sn9c10x = initOv7630; + reg17_19[0] = 0x68; + reg17_19[1] = (mode << 4) | COMP2; + reg17_19[2] = MCK_INIT1; + break; + case SENSOR_OV7630_3: + sn9c10x = initOv7630_3; + reg17_19[0] = 0x68; + reg17_19[1] = (mode << 4) | COMP2; + reg17_19[2] = MCK_INIT1; + break; + case SENSOR_PAS106: + sn9c10x = initPas106; + reg17_19[0] = 0x24; /* 0x28 */ + reg17_19[1] = (mode << 4) | COMP1; + reg17_19[2] = MCK_INIT1; + break; + case SENSOR_PAS202: + sn9c10x = initPas202; + reg17_19[0] = mode ? 0x24 : 0x20; + reg17_19[1] = (mode << 4) | 0x89; + reg17_19[2] = 0x20; + break; + case SENSOR_TAS5110: + sn9c10x = initTas5110; + reg17_19[0] = 0x60; + reg17_19[1] = (mode << 4) | 0x86; + reg17_19[2] = 0x2b; /* 0xf3; */ + break; + default: +/* case SENSOR_TAS5130CXX: */ + sn9c10x = initTas5130; + reg17_19[0] = 0x60; + reg17_19[1] = (mode << 4) | COMP; + reg17_19[2] = mode ? 0x23 : 0x43; + break; + } + switch (sd->sensor) { + case SENSOR_OV7630: + reg01 = 0x06; + reg17 = 0x29; + l = sizeof initOv7630; + break; + case SENSOR_OV7630_3: + reg01 = 0x44; + reg17 = 0x68; + l = sizeof initOv7630_3; + break; + default: + reg01 = sn9c10x[0]; + reg17 = sn9c10x[0x17 - 1]; + l = 0x1f; + break; + } + + /* reg 0x01 bit 2 video transfert on */ + reg_w(gspca_dev, 0x01, ®01, 1); + /* reg 0x17 SensorClk enable inv Clk 0x60 */ + reg_w(gspca_dev, 0x17, ®17, 1); +/*fixme: for ov7630 102 + reg_w(gspca_dev, 0x01, {0x06, sn9c10x[1]}, 2); */ + /* Set the registers from the template */ + reg_w_big(gspca_dev, 0x01, sn9c10x, l); + switch (sd->sensor) { + case SENSOR_HV7131R: + i2c_w_vector(gspca_dev, hv7131_sensor_init, + sizeof hv7131_sensor_init); + break; + case SENSOR_OV6650: + i2c_w_vector(gspca_dev, ov6650_sensor_init, + sizeof ov6650_sensor_init); + break; + case SENSOR_OV7630: + i2c_w_vector(gspca_dev, ov7630_sensor_init_com, + sizeof ov7630_sensor_init_com); + msleep(200); + i2c_w_vector(gspca_dev, ov7630_sensor_init, + sizeof ov7630_sensor_init); + break; + case SENSOR_OV7630_3: + i2c_w_vector(gspca_dev, ov7630_sensor_init_com, + sizeof ov7630_sensor_init_com); + msleep(200); + i2c_w(gspca_dev, ov7630_sensor_init_3[mode]); + break; + case SENSOR_PAS106: + pas106_i2cinit(gspca_dev); + break; + case SENSOR_PAS202: + i2c_w_vector(gspca_dev, pas202_sensor_init, + sizeof pas202_sensor_init); + break; + case SENSOR_TAS5110: + i2c_w_vector(gspca_dev, tas5110_sensor_init, + sizeof tas5110_sensor_init); + break; + default: +/* case SENSOR_TAS5130CXX: */ + i2c_w_vector(gspca_dev, tas5130_sensor_init, + sizeof tas5130_sensor_init); + break; + } + /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */ + reg_w(gspca_dev, 0x15, &sn9c10x[0x15 - 1], 2); + /* compression register */ + reg_w(gspca_dev, 0x18, ®17_19[1], 1); + /* H_start */ + reg_w(gspca_dev, 0x12, &sn9c10x[0x12 - 1], 1); + /* V_START */ + reg_w(gspca_dev, 0x13, &sn9c10x[0x13 - 1], 1); + /* reset 0x17 SensorClk enable inv Clk 0x60 */ + /*fixme: ov7630 [17]=68 8f (+20 if 102)*/ + reg_w(gspca_dev, 0x17, ®17_19[0], 1); + /*MCKSIZE ->3 */ /*fixme: not ov7630*/ + reg_w(gspca_dev, 0x19, ®17_19[2], 1); + /* AE_STRX AE_STRY AE_ENDX AE_ENDY */ + reg_w(gspca_dev, 0x1c, &sn9c10x[0x1c - 1], 4); + /* Enable video transfert */ + reg_w(gspca_dev, 0x01, &sn9c10x[0], 1); + /* Compression */ + reg_w(gspca_dev, 0x18, ®17_19[1], 2); + msleep(20); + + setgain(gspca_dev); + setbrightness(gspca_dev); + setexposure(gspca_dev); + setfreq(gspca_dev); + setsaturation(gspca_dev); + sethue(gspca_dev); + setcontrast(gspca_dev); + + sd->autogain_ignore_frames = 0; + atomic_set(&sd->avg_lum, -1); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + __u8 ByteSend; + + ByteSend = 0x09; /* 0X00 */ + reg_w(gspca_dev, 0x01, &ByteSend, 1); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + int i; + struct sd *sd = (struct sd *) gspca_dev; + + /* frames start with: + * ff ff 00 c4 c4 96 synchro + * 00 (unknown) + * xx (frame sequence / size / compression) + * (xx) (idem - extra byte for sn9c103) + * ll mm brightness sum inside auto exposure + * ll mm brightness sum outside auto exposure + * (xx xx xx xx xx) audio values for snc103 + */ + if (len > 6 && len < 24) { + for (i = 0; i < len - 6; i++) { + if (data[0 + i] == 0xff + && data[1 + i] == 0xff + && data[2 + i] == 0x00 + && data[3 + i] == 0xc4 + && data[4 + i] == 0xc4 + && data[5 + i] == 0x96) { /* start of frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, 0); + if (len - i < sd->fr_h_sz) { + atomic_set(&sd->avg_lum, -1); + PDEBUG(D_STREAM, "packet too short to" + " get avg brightness"); + } else if (sd->fr_h_sz == 12) { + atomic_set(&sd->avg_lum, + data[i + 8] + + (data[i + 9] << 8)); + } else { + atomic_set(&sd->avg_lum, + data[i + 9] + + (data[i + 10] << 8)); + } + data += i + sd->fr_h_sz; + len -= i + sd->fr_h_sz; + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, len); + return; + } + } + } + gspca_frame_add(gspca_dev, INTER_PACKET, + frame, data, len); +} + +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_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + if (sd->autogain) { + sd->exposure = EXPOSURE_DEF; + sd->gain = GAIN_DEF; + if (gspca_dev->streaming) { + sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; + setexposure(gspca_dev); + setgain(gspca_dev); + } + } + + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + +static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->saturation = val; + if (gspca_dev->streaming) + setsaturation(gspca_dev); + return 0; +} + +static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->saturation; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + sethue(gspca_dev); + return 0; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hue; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { +#ifndef CONFIG_USB_SN9C102 + {USB_DEVICE(0x0c45, 0x6001), DVNM("Genius VideoCAM NB")}, + {USB_DEVICE(0x0c45, 0x6005), DVNM("Sweex Tas5110")}, + {USB_DEVICE(0x0c45, 0x6007), DVNM("Sonix sn9c101 + Tas5110D")}, + {USB_DEVICE(0x0c45, 0x6009), DVNM("spcaCam@120")}, + {USB_DEVICE(0x0c45, 0x600d), DVNM("spcaCam@120")}, +#endif + {USB_DEVICE(0x0c45, 0x6011), DVNM("MAX Webcam Microdia")}, +#ifndef CONFIG_USB_SN9C102 + {USB_DEVICE(0x0c45, 0x6019), DVNM("Generic Sonix OV7630")}, + {USB_DEVICE(0x0c45, 0x6024), DVNM("Generic Sonix Tas5130c")}, + {USB_DEVICE(0x0c45, 0x6025), DVNM("Xcam Shanga")}, + {USB_DEVICE(0x0c45, 0x6028), DVNM("Sonix Btc Pc380")}, + {USB_DEVICE(0x0c45, 0x6029), DVNM("spcaCam@150")}, + {USB_DEVICE(0x0c45, 0x602c), DVNM("Generic Sonix OV7630")}, + {USB_DEVICE(0x0c45, 0x602d), DVNM("LIC-200 LG")}, + {USB_DEVICE(0x0c45, 0x602e), DVNM("Genius VideoCam Messenger")}, + {USB_DEVICE(0x0c45, 0x60af), DVNM("Trust WB3100P")}, + {USB_DEVICE(0x0c45, 0x60b0), DVNM("Genius VideoCam Look")}, +#endif + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c new file mode 100644 index 00000000000..3e68b992695 --- /dev/null +++ b/drivers/media/video/gspca/sonixj.c @@ -0,0 +1,1671 @@ +/* + * Sonix sn9c102p sn9c105 sn9c120 (jpeg) library + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "sonixj" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int avg_lum; + unsigned int exposure; + + unsigned short brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + signed char ag_cnt; +#define AG_CNT_START 13 + + char qindex; + unsigned char bridge; +#define BRIDGE_SN9C102P 0 +#define BRIDGE_SN9C105 1 +#define BRIDGE_SN9C110 2 +#define BRIDGE_SN9C120 3 +#define BRIDGE_SN9C325 4 + char sensor; /* Type of image sensor chip */ +#define SENSOR_HV7131R 0 +#define SENSOR_MI0360 1 +#define SENSOR_MO4000 2 +#define SENSOR_OV7648 3 +#define SENSOR_OV7660 4 + unsigned char i2c_base; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xffff, + .step = 1, +#define BRIGHTNESS_DEF 0x7fff + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, +#define CONTRAST_DEF 63 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 255, + .step = 1, +#define COLOR_DEF 127 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/*Data from sn9c102p+hv71331r */ +static const __u8 sn_hv7131[] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 */ + 0x00, 0x03, 0x64, 0x00, 0x1A, 0x20, 0x20, 0x20, 0xA1, 0x11, +/* rega regb regc regd rege regf reg10 reg11 */ + 0x02, 0x09, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, /* 00 */ +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b */ + 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, 0x0a, 0x00, 0x00, 0x00, +/* reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const __u8 sn_mi0360[] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 */ + 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0xb1, 0x5d, +/* rega regb regc regd rege regf reg10 reg11 */ + 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b */ + 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, 0x06, 0x00, 0x00, 0x00, +/* reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const __u8 sn_mo4000[] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 */ + 0x12, 0x23, 0x60, 0x00, 0x1A, 0x00, 0x20, 0x18, 0x81, +/* reg9 rega regb regc regd rege regf reg10 reg11*/ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a*/ + 0x0b, 0x0f, 0x14, 0x28, 0x1e, 0x40, 0x08, 0x00, 0x00, +/* reg1b reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x25, 0x39, 0x4b, + 0x5c, 0x6b, 0x79, 0x87, 0x95, 0xa2, 0xaf, 0xbb, 0xc7, + 0xd3, 0xdf, 0xea, 0xf5 +}; + +static const __u8 sn_ov7648[] = { + 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, 0xA1, 0x6E, 0x18, 0x65, + 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1E, 0x82, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const __u8 sn_ov7660[] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 */ + 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x81, +/* reg9 rega regb regc regd rege regf reg10 reg11*/ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a*/ + 0x01, 0x01, 0x14, 0x28, 0x1e, 0x00, 0x07, 0x00, 0x00, +/* reg1b reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* sequence specific to the sensors - !! index = SENSOR_xxx */ +static const __u8 *sn_tb[] = { + sn_hv7131, + sn_mi0360, + sn_mo4000, + sn_ov7648, + sn_ov7660 +}; + +static const __u8 regsn20[] = { + 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99, + 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff +}; +static const __u8 regsn20_sn9c120[] = { + 0x00, 0x25, 0x3c, 0x50, 0x62, 0x72, 0x81, 0x90, + 0x9e, 0xab, 0xb8, 0xc5, 0xd1, 0xdd, 0xe9, 0xf4, 0xff +}; +static const __u8 regsn20_sn9c325[] = { + 0x0a, 0x3a, 0x56, 0x6c, 0x7e, 0x8d, 0x9a, 0xa4, + 0xaf, 0xbb, 0xc5, 0xcd, 0xd5, 0xde, 0xe8, 0xed, 0xf5 +}; + +static const __u8 reg84[] = { + 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe5, 0x0f, + 0xe4, 0x0f, 0x38, 0x00, 0x3e, 0x00, 0xc3, 0x0f, +/* 0x00, 0x00, 0x00, 0x00, 0x00 */ + 0xf7, 0x0f, 0x0a, 0x00, 0x00 +}; +static const __u8 reg84_sn9c120_1[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x00 +}; +static const __u8 reg84_sn9c120_2[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x02, 0x3b +}; +static const __u8 reg84_sn9c120_3[] = { + 0x14, 0x00, 0x27, 0x00, 0x08, 0x00, 0xeb, 0x0f, + 0xd5, 0x0f, 0x42, 0x00, 0x41, 0x00, 0xca, 0x0f, + 0xf5, 0x0f, 0x0c, 0x02, 0x3b +}; +static const __u8 reg84_sn9c325[] = { + 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe4, 0x0f, + 0xd3, 0x0f, 0x4b, 0x00, 0x48, 0x00, 0xc0, 0x0f, + 0xf8, 0x0f, 0x00, 0x00, 0x00 +}; + +static const __u8 hv7131r_sensor_init[][8] = { + {0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x11, 0x34, 0x17, 0x7F, 0x00, 0x00, 0x10}, + {0xD1, 0x11, 0x40, 0xFF, 0x7F, 0x7F, 0x7F, 0x10}, + {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x11, 0x14, 0x01, 0xE2, 0x02, 0x82, 0x10}, + {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, + + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x11, 0x25, 0x00, 0x61, 0xA8, 0x00, 0x10}, + {0xA1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x11, 0x31, 0x20, 0x2E, 0x20, 0x00, 0x10}, + {0xC1, 0x11, 0x25, 0x00, 0xC3, 0x50, 0x00, 0x10}, + {0xA1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */ + {0xC1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */ + + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10}, + + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const __u8 mi0360_sensor_init[][8] = { + {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x0D, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10}, + {0xD1, 0x5D, 0x03, 0x01, 0xE2, 0x02, 0x82, 0x10}, + {0xD1, 0x5D, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10}, + {0xB1, 0x5D, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10}, + {0xD1, 0x5D, 0x2F, 0xF7, 0xB0, 0x00, 0x04, 0x10}, + {0xD1, 0x5D, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10}, + {0xB1, 0x5D, 0x3D, 0x06, 0x8F, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x40, 0x01, 0xE0, 0x00, 0xD1, 0x10}, + {0xB1, 0x5D, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10}, + {0xD1, 0x5D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x5E, 0x00, 0x00, 0xA3, 0x1D, 0x10}, + {0xB1, 0x5D, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10}, + + {0xB1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x2B, 0x00, 0xA0, 0x00, 0xB0, 0x10}, + {0xD1, 0x5D, 0x2D, 0x00, 0xA0, 0x00, 0xA0, 0x10}, + + {0xB1, 0x5D, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */ + {0xB1, 0x5D, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */ + + {0xD1, 0x5D, 0x2B, 0x00, 0xB9, 0x00, 0xE3, 0x10}, + {0xD1, 0x5D, 0x2D, 0x00, 0x5f, 0x00, 0xB9, 0x10}, /* 42 */ +/* {0xB1, 0x5D, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */ +/* {0xB1, 0x5D, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */ + {0xB1, 0x5D, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */ + {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */ + {} +}; +static const __u8 mo4000_sensor_init[][8] = { + {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x06, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x06, 0x81, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x30, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const __u8 ov7660_sensor_init[][8] = { + {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ + {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, + /* Outformat ?? rawRGB */ + {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ + {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10}, +/* {0xd1, 0x21, 0x00, 0x01, 0x74, 0x74, 0x00, 0x10}, */ + /* GAIN BLUE RED VREF */ + {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10}, + /* COM 1 BAVE GEAVE AECHH */ + {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */ + {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */ + {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xf8, 0x10}, +/* {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10}, */ + /* AECH CLKRC COM7 COM8 */ + {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */ + {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10}, + /* HSTART HSTOP VSTRT VSTOP */ + {0xa1, 0x21, 0x1b, 0x02, 0x00, 0x00, 0x00, 0x10}, /* PSHFT */ + {0xb1, 0x21, 0x1e, 0x01, 0x0e, 0x00, 0x00, 0x10}, /* MVFP LAEC */ + {0xd1, 0x21, 0x20, 0x07, 0x07, 0x07, 0x07, 0x10}, + /* BOS GBOS GROS ROS (BGGR offset) */ + {0xd1, 0x21, 0x24, 0x68, 0x58, 0xd4, 0x80, 0x10}, +/* {0xd1, 0x21, 0x24, 0x78, 0x68, 0xd4, 0x80, 0x10}, */ + /* AEW AEB VPT BBIAS */ + {0xd1, 0x21, 0x28, 0x80, 0x30, 0x00, 0x00, 0x10}, + /* GbBIAS RSVD EXHCH EXHCL */ + {0xd1, 0x21, 0x2c, 0x80, 0x00, 0x00, 0x62, 0x10}, + /* RBIAS ADVFL ASDVFH YAVE */ + {0xc1, 0x21, 0x30, 0x08, 0x30, 0xb4, 0x00, 0x10}, + /* HSYST HSYEN HREF */ + {0xd1, 0x21, 0x33, 0x00, 0x07, 0x84, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x37, 0x0c, 0x02, 0x43, 0x00, 0x10}, + /* ADC ACOM OFON TSLB */ + {0xd1, 0x21, 0x3b, 0x02, 0x6c, 0x19, 0x0e, 0x10}, + /* COM11 COM12 COM13 COM14 */ + {0xd1, 0x21, 0x3f, 0x41, 0xc1, 0x22, 0x08, 0x10}, + /* EDGE COM15 COM16 COM17 */ + {0xd1, 0x21, 0x43, 0xf0, 0x10, 0x78, 0xa8, 0x10}, /* reserved */ + {0xd1, 0x21, 0x47, 0x60, 0x80, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x4f, 0x46, 0x36, 0x0f, 0x17, 0x10}, /* MTX 1 2 3 4 */ + {0xd1, 0x21, 0x53, 0x7f, 0x96, 0x40, 0x40, 0x10}, /* MTX 5 6 7 8 */ + {0xb1, 0x21, 0x57, 0x40, 0x0f, 0x00, 0x00, 0x10}, /* MTX9 MTXS */ + {0xd1, 0x21, 0x59, 0xba, 0x9a, 0x22, 0xb9, 0x10}, /* reserved */ + {0xd1, 0x21, 0x5d, 0x9b, 0x10, 0xf0, 0x05, 0x10}, /* reserved */ + {0xa1, 0x21, 0x61, 0x60, 0x00, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x62, 0x00, 0x00, 0x50, 0x30, 0x10}, + /* LCC1 LCC2 LCC3 LCC4 */ + {0xa1, 0x21, 0x66, 0x00, 0x00, 0x00, 0x00, 0x10}, /* LCC5 */ + {0xd1, 0x21, 0x67, 0x80, 0x7a, 0x90, 0x80, 0x10}, + {0xa1, 0x21, 0x6b, 0x0a, 0x00, 0x00, 0x00, 0x10}, + /* band gap reference [0..3] DBLV */ + {0xd1, 0x21, 0x6c, 0x30, 0x48, 0x80, 0x74, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x70, 0x64, 0x60, 0x5c, 0x58, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x74, 0x54, 0x4c, 0x40, 0x38, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x78, 0x34, 0x30, 0x2f, 0x2b, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x7c, 0x03, 0x07, 0x17, 0x34, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x80, 0x41, 0x4d, 0x58, 0x63, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x84, 0x6e, 0x77, 0x87, 0x95, 0x10}, /* gamma curve */ + {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */ + {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */ + {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, +/****** (some exchanges in the win trace) ******/ + {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, + /* bits[3..0]reserved */ + {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + /* VREF vertical frame ctrl */ + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, /* 0x20 */ + {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, +/* {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, */ + {0xa1, 0x21, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, +/****** (some exchanges in the win trace) ******/ + {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */ + {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10},/* dummy line low */ + {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, +/****** (some exchanges in the win trace) ******/ +/**********startsensor KO if changed !!****/ + {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10}, +/* here may start the isoc exchanges */ + {} +}; +/* reg0x04 reg0x07 reg 0x10 */ +/* expo = (COM1 & 0x02) | (AECHH & 0x2f <<10) [ (AECh << 2) */ + +static const __u8 ov7648_sensor_init[][8] = { + {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xA1, 0x6E, 0x3F, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x04, 0x02, 0xB1, 0x02, 0x39, 0x10}, + {0xD1, 0x6E, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x0C, 0x02, 0x7F, 0x01, 0xE0, 0x10}, + {0xD1, 0x6E, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10}, + {0xD1, 0x6E, 0x16, 0x85, 0x40, 0x4A, 0x40, 0x10}, + {0xC1, 0x6E, 0x1A, 0x00, 0x80, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x1D, 0x08, 0x03, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x23, 0x00, 0xB0, 0x00, 0x94, 0x10}, + {0xD1, 0x6E, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x2D, 0x14, 0x35, 0x61, 0x84, 0x10}, + {0xD1, 0x6E, 0x31, 0xA2, 0xBD, 0xD8, 0xFF, 0x10}, + {0xD1, 0x6E, 0x35, 0x06, 0x1E, 0x12, 0x02, 0x10}, + {0xD1, 0x6E, 0x39, 0xAA, 0x53, 0x37, 0xD5, 0x10}, + {0xA1, 0x6E, 0x3D, 0xF2, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x3E, 0x00, 0x00, 0x80, 0x03, 0x10}, + {0xD1, 0x6E, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x6E, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10}, + {0xD1, 0x6E, 0x4B, 0x02, 0xEF, 0x08, 0xCD, 0x10}, + {0xD1, 0x6E, 0x4F, 0x00, 0xD0, 0x00, 0xA0, 0x10}, + {0xD1, 0x6E, 0x53, 0x01, 0xAA, 0x01, 0x40, 0x10}, + {0xD1, 0x6E, 0x5A, 0x50, 0x04, 0x30, 0x03, 0x10}, + {0xA1, 0x6E, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x5F, 0x10, 0x40, 0xFF, 0x00, 0x10}, + /* {0xD1, 0x6E, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10}, + * This is currently setting a + * blue tint, and some things more , i leave it here for future test if + * somene is having problems with color on this sensor + {0xD1, 0x6E, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x6E, 0x73, 0x10, 0x80, 0xEB, 0x00, 0x10}, + {0xA1, 0x6E, 0x1E, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x15, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x6E, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, + {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x07, 0xB5, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x18, 0x6B, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x07, 0xB8, 0x00, 0x00, 0x00, 0x10}, */ + {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xA1, 0x6E, 0x06, 0x03, 0x00, 0x00, 0x00, 0x10}, /* Bright... */ + {0xA1, 0x6E, 0x07, 0x66, 0x00, 0x00, 0x00, 0x10}, /* B.. */ + {0xC1, 0x6E, 0x1A, 0x03, 0x65, 0x90, 0x00, 0x10}, /* Bright/Witen....*/ +/* {0xC1, 0x6E, 0x16, 0x45, 0x40, 0x60, 0x00, 0x10}, * Bright/Witene */ + {} +}; + +static const __u8 qtable4[] = { + 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06, + 0x06, 0x08, 0x0A, 0x11, + 0x0A, 0x0A, 0x08, 0x08, 0x0A, 0x15, 0x0F, 0x0F, 0x0C, 0x11, 0x19, 0x15, + 0x19, 0x19, 0x17, 0x15, + 0x17, 0x17, 0x1B, 0x1D, 0x25, 0x21, 0x1B, 0x1D, 0x23, 0x1D, 0x17, 0x17, + 0x21, 0x2E, 0x21, 0x23, + 0x27, 0x29, 0x2C, 0x2C, 0x2C, 0x19, 0x1F, 0x30, 0x32, 0x2E, 0x29, 0x32, + 0x25, 0x29, 0x2C, 0x29, + 0x06, 0x08, 0x08, 0x0A, 0x08, 0x0A, 0x13, 0x0A, 0x0A, 0x13, 0x29, 0x1B, + 0x17, 0x1B, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29 +}; + +/* read <len> bytes (len < sizeof gspca_dev->usb_buf) to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 value, int len) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, 0, + gspca_dev->usb_buf, len, + 500); +} + +static void reg_w(struct gspca_dev *gspca_dev, + __u16 value, + const __u8 *buffer, + int len) +{ + if (len <= sizeof gspca_dev->usb_buf) { + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, 0, + gspca_dev->usb_buf, len, + 500); + } else { + __u8 *tmpbuf; + + tmpbuf = kmalloc(len, GFP_KERNEL); + memcpy(tmpbuf, buffer, len); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, 0, + tmpbuf, len, + 500); + kfree(tmpbuf); + } +} + +/* I2C write 2 bytes */ +static void i2c_w2(struct gspca_dev *gspca_dev, + const __u8 *buffer) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 mode[8]; + + /* is i2c ready */ + mode[0] = 0x81 | (2 << 4); + mode[1] = sd->i2c_base; + mode[2] = buffer[0]; + mode[3] = buffer[1]; + mode[4] = 0; + mode[5] = 0; + mode[6] = 0; + mode[7] = 0x10; + reg_w(gspca_dev, 0x08, mode, 8); +} + +/* I2C write 8 bytes */ +static void i2c_w8(struct gspca_dev *gspca_dev, + const __u8 *buffer) +{ + reg_w(gspca_dev, 0x08, buffer, 8); + msleep(1); +} + +/* read 5 bytes in gspca_dev->usb_buf */ +static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 mode[8]; + + mode[0] = 0x81 | 0x10; + mode[1] = sd->i2c_base; + mode[2] = reg; + mode[3] = 0; + mode[4] = 0; + mode[5] = 0; + mode[6] = 0; + mode[7] = 0x10; + i2c_w8(gspca_dev, mode); + mode[0] = 0x81 | (5 << 4) | 0x02; + mode[2] = 0; + i2c_w8(gspca_dev, mode); + reg_r(gspca_dev, 0x0a, 5); +} + +static int probesensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 reg02; + static const __u8 datasend[] = { 2, 0 }; + /* reg val1 val2 val3 val4 */ + + i2c_w2(gspca_dev, datasend); +/* should write 0xa1 0x11 0x02 0x00 0x00 0x00 0x00 the 0x10 is add by i2cw */ + msleep(10); + reg02 = 0x66; + reg_w(gspca_dev, 0x02, ®02, 1); /* Gpio on */ + msleep(10); + i2c_r5(gspca_dev, 0); /* read sensor id */ + if (gspca_dev->usb_buf[0] == 0x02 + && gspca_dev->usb_buf[1] == 0x09 + && gspca_dev->usb_buf[2] == 0x01 + && gspca_dev->usb_buf[3] == 0x00 + && gspca_dev->usb_buf[4] == 0x00) { + PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R"); + sd->sensor = SENSOR_HV7131R; + return SENSOR_HV7131R; + } + PDEBUG(D_PROBE, "Find Sensor %d %d %d", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1], + gspca_dev->usb_buf[2]); + PDEBUG(D_PROBE, "Sensor sn9c102P Not found"); + return -ENODEV; +} + +static int configure_gpio(struct gspca_dev *gspca_dev, + const __u8 *sn9c1xx) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 data; + __u8 regF1; + const __u8 *reg9a; + static const __u8 reg9a_def[] = + {0x08, 0x40, 0x20, 0x10, 0x00, 0x04}; + static const __u8 reg9a_sn9c120[] = /* from win trace */ + {0x00, 0x40, 0x38, 0x30, 0x00, 0x20}; + static const __u8 reg9a_sn9c325[] = + {0x0a, 0x40, 0x38, 0x30, 0x00, 0x20}; + + + regF1 = 0x00; + reg_w(gspca_dev, 0xf1, ®F1, 1); + reg_w(gspca_dev, 0x01, &sn9c1xx[0], 1); /*fixme:jfm was [1] en v1*/ + + /* configure gpio */ + reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2); + reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2); + reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); /* jfm was 3 */ + switch (sd->bridge) { + case BRIDGE_SN9C325: + reg9a = reg9a_sn9c325; + break; + case BRIDGE_SN9C120: + reg9a = reg9a_sn9c120; + break; + default: + reg9a = reg9a_def; + break; + } + reg_w(gspca_dev, 0x9a, reg9a, 6); + + data = 0x60; /*fixme:jfm 60 00 00 (3) */ + reg_w(gspca_dev, 0xd4, &data, 1); + + reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f); + + switch (sd->bridge) { + case BRIDGE_SN9C120: /* from win trace */ + data = 0x61; + reg_w(gspca_dev, 0x01, &data, 1); + data = 0x20; + reg_w(gspca_dev, 0x17, &data, 1); + data = 0x60; + reg_w(gspca_dev, 0x01, &data, 1); + break; + case BRIDGE_SN9C325: + data = 0x43; + reg_w(gspca_dev, 0x01, &data, 1); + data = 0xae; + reg_w(gspca_dev, 0x17, &data, 1); + data = 0x42; + reg_w(gspca_dev, 0x01, &data, 1); + break; + default: + data = 0x43; + reg_w(gspca_dev, 0x01, &data, 1); + data = 0x61; + reg_w(gspca_dev, 0x17, &data, 1); + data = 0x42; + reg_w(gspca_dev, 0x01, &data, 1); + } + + if (sd->sensor == SENSOR_HV7131R) { + if (probesensor(gspca_dev) < 0) + return -ENODEV; + } + return 0; +} + +static void hv7131R_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + static const __u8 SetSensorClk[] = /* 0x08 Mclk */ + { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 }; + + while (hv7131r_sensor_init[i][0]) { + i2c_w8(gspca_dev, hv7131r_sensor_init[i]); + i++; + } + i2c_w8(gspca_dev, SetSensorClk); +} + +static void mi0360_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + + while (mi0360_sensor_init[i][0]) { + i2c_w8(gspca_dev, mi0360_sensor_init[i]); + i++; + } +} + +static void mo4000_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + + while (mo4000_sensor_init[i][0]) { + i2c_w8(gspca_dev, mo4000_sensor_init[i]); + i++; + } +} + +static void ov7648_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + + while (ov7648_sensor_init[i][0]) { + i2c_w8(gspca_dev, ov7648_sensor_init[i]); + i++; + } +} + +static void ov7660_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + + while (ov7660_sensor_init[i][0]) { + i2c_w8(gspca_dev, ov7660_sensor_init[i]); + i++; + } +} + +/* 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; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + sd->sensor = -1; + switch (vendor) { + case 0x0458: /* Genius */ +/* switch (product) { + case 0x7025: */ + sd->bridge = BRIDGE_SN9C120; + sd->sensor = SENSOR_MI0360; + sd->i2c_base = 0x5d; +/* break; + } */ + break; + case 0x045e: +/* switch (product) { + case 0x00f5: + case 0x00f7: */ + sd->bridge = BRIDGE_SN9C105; + sd->sensor = SENSOR_OV7660; + sd->i2c_base = 0x21; +/* break; + } */ + break; + case 0x0471: /* Philips */ +/* switch (product) { + case 0x0327: + case 0x0328: + case 0x0330: */ + sd->bridge = BRIDGE_SN9C105; + sd->sensor = SENSOR_MI0360; + sd->i2c_base = 0x5d; +/* break; + } */ + break; + case 0x0c45: /* Sonix */ + switch (product) { + case 0x6040: + sd->bridge = BRIDGE_SN9C102P; +/* sd->sensor = SENSOR_MI0360; * from BW600.inf */ +/*fixme: MI0360 base=5d ? */ + sd->sensor = SENSOR_HV7131R; /* gspcav1 value */ + sd->i2c_base = 0x11; + break; +/* case 0x607a: * from BW600.inf + sd->bridge = BRIDGE_SN9C102P; + sd->sensor = SENSOR_OV7648; + sd->i2c_base = 0x??; + break; */ + case 0x607c: + sd->bridge = BRIDGE_SN9C102P; + sd->sensor = SENSOR_HV7131R; + sd->i2c_base = 0x11; + break; +/* case 0x607e: * from BW600.inf + sd->bridge = BRIDGE_SN9C102P; + sd->sensor = SENSOR_OV7630; + sd->i2c_base = 0x??; + break; */ + case 0x60c0: + sd->bridge = BRIDGE_SN9C105; + sd->sensor = SENSOR_MI0360; + sd->i2c_base = 0x5d; + break; +/* case 0x60c8: * from BW600.inf + sd->bridge = BRIDGE_SN9C105; + sd->sensor = SENSOR_OM6801; + sd->i2c_base = 0x??; + break; */ +/* case 0x60cc: * from BW600.inf + sd->bridge = BRIDGE_SN9C105; + sd->sensor = SENSOR_HV7131GP; + sd->i2c_base = 0x??; + break; */ + case 0x60ec: + sd->bridge = BRIDGE_SN9C105; + sd->sensor = SENSOR_MO4000; + sd->i2c_base = 0x21; + break; +/* case 0x60ef: * from BW600.inf + sd->bridge = BRIDGE_SN9C105; + sd->sensor = SENSOR_ICM105C; + sd->i2c_base = 0x??; + break; */ +/* case 0x60fa: * from BW600.inf + sd->bridge = BRIDGE_SN9C105; + sd->sensor = SENSOR_OV7648; + sd->i2c_base = 0x??; + break; */ + case 0x60fb: + sd->bridge = BRIDGE_SN9C105; + sd->sensor = SENSOR_OV7660; + sd->i2c_base = 0x21; + break; + case 0x60fc: + sd->bridge = BRIDGE_SN9C105; + sd->sensor = SENSOR_HV7131R; + sd->i2c_base = 0x11; + break; +/* case 0x60fe: * from BW600.inf + sd->bridge = BRIDGE_SN9C105; + sd->sensor = SENSOR_OV7630; + sd->i2c_base = 0x??; + break; */ +/* case 0x6108: * from BW600.inf + sd->bridge = BRIDGE_SN9C120; + sd->sensor = SENSOR_OM6801; + sd->i2c_base = 0x??; + break; */ +/* case 0x6122: * from BW600.inf + sd->bridge = BRIDGE_SN9C110; + sd->sensor = SENSOR_ICM105C; + sd->i2c_base = 0x??; + break; */ + case 0x612a: +/* sd->bridge = BRIDGE_SN9C110; * in BW600.inf */ + sd->bridge = BRIDGE_SN9C325; + sd->sensor = SENSOR_OV7648; + sd->i2c_base = 0x21; +/*fixme: sensor_init has base = 00 et 6e!*/ + break; +/* case 0x6123: * from BW600.inf + sd->bridge = BRIDGE_SN9C110; + sd->sensor = SENSOR_SanyoCCD; + sd->i2c_base = 0x??; + break; */ + case 0x612c: + sd->bridge = BRIDGE_SN9C110; + sd->sensor = SENSOR_MO4000; + sd->i2c_base = 0x21; + break; +/* case 0x612e: * from BW600.inf + sd->bridge = BRIDGE_SN9C110; + sd->sensor = SENSOR_OV7630; + sd->i2c_base = 0x??; + break; */ +/* case 0x612f: * from BW600.inf + sd->bridge = BRIDGE_SN9C110; + sd->sensor = SENSOR_ICM105C; + sd->i2c_base = 0x??; + break; */ + case 0x6130: + sd->bridge = BRIDGE_SN9C120; + sd->sensor = SENSOR_MI0360; + sd->i2c_base = 0x5d; + break; + case 0x6138: + sd->bridge = BRIDGE_SN9C120; + sd->sensor = SENSOR_MO4000; + sd->i2c_base = 0x21; + break; +/* case 0x613a: * from BW600.inf + sd->bridge = BRIDGE_SN9C120; + sd->sensor = SENSOR_OV7648; + sd->i2c_base = 0x??; + break; */ + case 0x613b: + sd->bridge = BRIDGE_SN9C120; + sd->sensor = SENSOR_OV7660; + sd->i2c_base = 0x21; + break; + case 0x613c: + sd->bridge = BRIDGE_SN9C120; + sd->sensor = SENSOR_HV7131R; + sd->i2c_base = 0x11; + break; +/* case 0x613e: * from BW600.inf + sd->bridge = BRIDGE_SN9C120; + sd->sensor = SENSOR_OV7630; + sd->i2c_base = 0x??; + break; */ + } + break; + } + if (sd->sensor < 0) { + PDEBUG(D_ERR, "Invalid vendor/product %04x:%04x", + vendor, product); + return -EINVAL; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + + sd->qindex = 4; /* set the quantization table */ + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + sd->autogain = AUTOGAIN_DEF; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; +/* const __u8 *sn9c1xx; */ + __u8 regF1; + __u8 regGpio[] = { 0x29, 0x74 }; + + /* setup a selector by bridge */ + regF1 = 0x01; + reg_w(gspca_dev, 0xf1, ®F1, 1); + reg_r(gspca_dev, 0x00, 1); /* -> regF1 = 0x00 */ + regF1 = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0xf1, ®F1, 1); + reg_r(gspca_dev, 0x00, 1); + regF1 = gspca_dev->usb_buf[0]; + switch (sd->bridge) { + case BRIDGE_SN9C102P: + if (regF1 != 0x11) + return -ENODEV; + reg_w(gspca_dev, 0x02, ®Gpio[1], 1); + break; + case BRIDGE_SN9C105: + if (regF1 != 0x11) + return -ENODEV; + reg_w(gspca_dev, 0x02, regGpio, 2); + break; + case BRIDGE_SN9C110: + if (regF1 != 0x12) + return -ENODEV; + regGpio[1] = 0x62; + reg_w(gspca_dev, 0x02, ®Gpio[1], 1); + break; + case BRIDGE_SN9C120: + if (regF1 != 0x12) + return -ENODEV; + regGpio[1] = 0x70; + reg_w(gspca_dev, 0x02, regGpio, 2); + break; + default: +/* case BRIDGE_SN9C325: */ + if (regF1 != 0x12) + return -ENODEV; + regGpio[1] = 0x62; + reg_w(gspca_dev, 0x02, ®Gpio[1], 1); + break; + } + + regF1 = 0x01; + reg_w(gspca_dev, 0xf1, ®F1, 1); + + return 0; +} + +static unsigned int setexposure(struct gspca_dev *gspca_dev, + unsigned int expo) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const __u8 doit[] = /* update sensor */ + { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; + static const __u8 sensorgo[] = /* sensor on */ + { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 }; + static const __u8 gainMo[] = + { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d }; + + switch (sd->sensor) { + case SENSOR_HV7131R: { + __u8 Expodoit[] = + { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 }; + + Expodoit[3] = expo >> 16; + Expodoit[4] = expo >> 8; + Expodoit[5] = expo; + i2c_w8(gspca_dev, Expodoit); + break; + } + case SENSOR_MI0360: { + __u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */ + { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 }; + + if (expo > 0x0635) + expo = 0x0635; + else if (expo < 0x0001) + expo = 0x0001; + expoMi[3] = expo >> 8; + expoMi[4] = expo; + i2c_w8(gspca_dev, expoMi); + i2c_w8(gspca_dev, doit); + i2c_w8(gspca_dev, sensorgo); + break; + } + case SENSOR_MO4000: { + __u8 expoMof[] = + { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 }; + __u8 expoMo10[] = + { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 }; + + if (expo > 0x1fff) + expo = 0x1fff; + else if (expo < 0x0001) + expo = 0x0001; + expoMof[3] = (expo & 0x03fc) >> 2; + i2c_w8(gspca_dev, expoMof); + expoMo10[3] = ((expo & 0x1c00) >> 10) + | ((expo & 0x0003) << 4); + i2c_w8(gspca_dev, expoMo10); + i2c_w8(gspca_dev, gainMo); + PDEBUG(D_CONF, "set exposure %d", + ((expoMo10[3] & 0x07) << 10) + | (expoMof[3] << 2) + | ((expoMo10[3] & 0x30) >> 4)); + break; + } + } + return expo; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned int expo; + __u8 k2; + + switch (sd->sensor) { + case SENSOR_HV7131R: + expo = sd->brightness << 4; + if (expo > 0x002dc6c0) + expo = 0x002dc6c0; + else if (expo < 0x02a0) + expo = 0x02a0; + sd->exposure = setexposure(gspca_dev, expo); + break; + case SENSOR_MI0360: + expo = sd->brightness >> 4; + sd->exposure = setexposure(gspca_dev, expo); + break; + case SENSOR_MO4000: + expo = sd->brightness >> 4; + sd->exposure = setexposure(gspca_dev, expo); + break; + case SENSOR_OV7660: + return; /*jfm??*/ + } + + k2 = sd->brightness >> 10; + reg_w(gspca_dev, 0x96, &k2, 1); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 k2; + __u8 contrast[] = { 0x00, 0x00, 0x28, 0x00, 0x07, 0x00 }; + + if (sd->sensor == SENSOR_OV7660) + return; /*jfm??*/ + k2 = sd->contrast; + contrast[2] = k2; + contrast[0] = (k2 + 1) >> 1; + contrast[4] = (k2 + 1) / 5; + reg_w(gspca_dev, 0x84, contrast, 6); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 data; + int colour; + + colour = sd->colors - 128; + if (colour > 0) + data = (colour + 32) & 0x7f; /* blue */ + else + data = (-colour + 32) & 0x7f; /* red */ + reg_w(gspca_dev, 0x05, &data, 1); +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + __u8 data; + __u8 reg1; + __u8 reg17; + const __u8 *sn9c1xx; + int mode; + static const __u8 DC29[] = { 0x6a, 0x50, 0x00, 0x00, 0x50, 0x3c }; + static const __u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; + static const __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; + static const __u8 CA_sn9c120[] = + { 0x14, 0xec, 0x0a, 0xf6 }; /* SN9C120 */ + static const __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ + static const __u8 CE_sn9c325[] = + { 0x32, 0xdd, 0x32, 0xdd }; /* OV7648 - SN9C325 */ + + sn9c1xx = sn_tb[(int) sd->sensor]; + configure_gpio(gspca_dev, sn9c1xx); + +/*fixme:jfm this sequence should appear at end of sd_start */ +/* with + data = 0x44; + reg_w(gspca_dev, 0x01, &data, 1); */ + reg_w(gspca_dev, 0x15, &sn9c1xx[0x15], 1); + reg_w(gspca_dev, 0x16, &sn9c1xx[0x16], 1); + reg_w(gspca_dev, 0x12, &sn9c1xx[0x12], 1); + reg_w(gspca_dev, 0x13, &sn9c1xx[0x13], 1); + reg_w(gspca_dev, 0x18, &sn9c1xx[0x18], 1); + reg_w(gspca_dev, 0xd2, &DC29[0], 1); + reg_w(gspca_dev, 0xd3, &DC29[1], 1); + reg_w(gspca_dev, 0xc6, &DC29[2], 1); + reg_w(gspca_dev, 0xc7, &DC29[3], 1); + reg_w(gspca_dev, 0xc8, &DC29[4], 1); + reg_w(gspca_dev, 0xc9, &DC29[5], 1); +/*fixme:jfm end of ending sequence */ + reg_w(gspca_dev, 0x18, &sn9c1xx[0x18], 1); + switch (sd->bridge) { + case BRIDGE_SN9C325: + data = 0xae; + break; + case BRIDGE_SN9C120: + data = 0xa0; + break; + default: + data = 0x60; + break; + } + reg_w(gspca_dev, 0x17, &data, 1); + reg_w(gspca_dev, 0x05, &sn9c1xx[5], 1); + reg_w(gspca_dev, 0x07, &sn9c1xx[7], 1); + reg_w(gspca_dev, 0x06, &sn9c1xx[6], 1); + reg_w(gspca_dev, 0x14, &sn9c1xx[0x14], 1); + switch (sd->bridge) { + case BRIDGE_SN9C325: + reg_w(gspca_dev, 0x20, regsn20_sn9c325, + sizeof regsn20_sn9c325); + for (i = 0; i < 8; i++) + reg_w(gspca_dev, 0x84, reg84_sn9c325, + sizeof reg84_sn9c325); + data = 0x0a; + reg_w(gspca_dev, 0x9a, &data, 1); + data = 0x60; + reg_w(gspca_dev, 0x99, &data, 1); + break; + case BRIDGE_SN9C120: + reg_w(gspca_dev, 0x20, regsn20_sn9c120, + sizeof regsn20_sn9c120); + for (i = 0; i < 2; i++) + reg_w(gspca_dev, 0x84, reg84_sn9c120_1, + sizeof reg84_sn9c120_1); + for (i = 0; i < 6; i++) + reg_w(gspca_dev, 0x84, reg84_sn9c120_2, + sizeof reg84_sn9c120_2); + reg_w(gspca_dev, 0x84, reg84_sn9c120_3, + sizeof reg84_sn9c120_3); + data = 0x05; + reg_w(gspca_dev, 0x9a, &data, 1); + data = 0x5b; + reg_w(gspca_dev, 0x99, &data, 1); + break; + default: + reg_w(gspca_dev, 0x20, regsn20, sizeof regsn20); + for (i = 0; i < 8; i++) + reg_w(gspca_dev, 0x84, reg84, sizeof reg84); + data = 0x08; + reg_w(gspca_dev, 0x9a, &data, 1); + data = 0x59; + reg_w(gspca_dev, 0x99, &data, 1); + break; + } + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + reg1 = 0x02; + reg17 = 0x61; + switch (sd->sensor) { + case SENSOR_HV7131R: + hv7131R_InitSensor(gspca_dev); + if (mode) + reg1 = 0x46; /* 320 clk 48Mhz */ + else + reg1 = 0x06; /* 640 clk 24Mz */ + break; + case SENSOR_MI0360: + mi0360_InitSensor(gspca_dev); + if (mode) + reg1 = 0x46; /* 320 clk 48Mhz */ + else + reg1 = 0x06; /* 640 clk 24Mz */ + break; + case SENSOR_MO4000: + mo4000_InitSensor(gspca_dev); + if (mode) { +/* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */ + reg1 = 0x06; /* clk 24Mz */ + } else { + reg17 = 0x22; /* 640 MCKSIZE */ + reg1 = 0x06; /* 640 clk 24Mz */ + } + break; + case SENSOR_OV7648: + reg17 = 0xa2; + reg1 = 0x44; + ov7648_InitSensor(gspca_dev); +/* if (mode) + ; * 320x2... + else + ; * 640x... */ + break; + default: +/* case SENSOR_OV7660: */ + ov7660_InitSensor(gspca_dev); + if (mode) { +/* reg17 = 0x21; * 320 */ +/* reg1 = 0x44; */ + reg1 = 0x46; + } else { + reg17 = 0xa2; /* 640 */ + reg1 = 0x40; + } + break; + } + reg_w(gspca_dev, 0xc0, C0, 6); + switch (sd->bridge) { + case BRIDGE_SN9C120: /*jfm ?? */ + reg_w(gspca_dev, 0xca, CA_sn9c120, 4); + break; + default: + reg_w(gspca_dev, 0xca, CA, 4); + break; + } + switch (sd->bridge) { + case BRIDGE_SN9C120: /*jfm ?? */ + case BRIDGE_SN9C325: + reg_w(gspca_dev, 0xce, CE_sn9c325, 4); + break; + default: + reg_w(gspca_dev, 0xce, CE, 4); + /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */ + break; + } + + /* here change size mode 0 -> VGA; 1 -> CIF */ + data = 0x40 | sn9c1xx[0x18] | (mode << 4); + reg_w(gspca_dev, 0x18, &data, 1); + + reg_w(gspca_dev, 0x100, qtable4, 0x40); + reg_w(gspca_dev, 0x140, qtable4 + 0x40, 0x40); + + data = sn9c1xx[0x18] | (mode << 4); + reg_w(gspca_dev, 0x18, &data, 1); + + reg_w(gspca_dev, 0x17, ®17, 1); + reg_w(gspca_dev, 0x01, ®1, 1); + setbrightness(gspca_dev); + setcontrast(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const __u8 stophv7131[] = + { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 }; + static const __u8 stopmi0360[] = + { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; + __u8 regF1; + __u8 data; + const __u8 *sn9c1xx; + + data = 0x0b; + switch (sd->sensor) { + case SENSOR_HV7131R: + i2c_w8(gspca_dev, stophv7131); + data = 0x2b; + break; + case SENSOR_MI0360: + i2c_w8(gspca_dev, stopmi0360); + data = 0x29; + break; + case SENSOR_MO4000: + break; + case SENSOR_OV7648: + data = 0x29; + break; + default: +/* case SENSOR_OV7660: */ + break; + } + sn9c1xx = sn_tb[(int) sd->sensor]; + reg_w(gspca_dev, 0x01, &sn9c1xx[1], 1); + reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 1); + reg_w(gspca_dev, 0x01, &sn9c1xx[1], 1); + reg_w(gspca_dev, 0x01, &data, 1); + regF1 = 0x01; + reg_w(gspca_dev, 0xf1, ®F1, 1); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + /* Thanks S., without your advice, autobright should not work :) */ + int delta; + int expotimes = 0; + __u8 luma_mean = 130; + __u8 luma_delta = 20; + + delta = sd->avg_lum; + if (delta < luma_mean - luma_delta || + delta > luma_mean + luma_delta) { + switch (sd->sensor) { + case SENSOR_HV7131R: + expotimes = sd->exposure >> 8; + expotimes += (luma_mean - delta) >> 4; + if (expotimes < 0) + expotimes = 0; + sd->exposure = setexposure(gspca_dev, + (unsigned int) (expotimes << 8)); + break; + case SENSOR_MO4000: + case SENSOR_MI0360: + expotimes = sd->exposure; + expotimes += (luma_mean - delta) >> 6; + if (expotimes < 0) + expotimes = 0; + sd->exposure = setexposure(gspca_dev, + (unsigned int) expotimes); + setcolors(gspca_dev); + break; + } + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int sof, avg_lum; + + sof = len - 64; + if (sof >= 0 && data[sof] == 0xff && data[sof + 1] == 0xd9) { + + /* end of frame */ + gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, sof + 2); + if (sd->ag_cnt < 0) + return; + if (--sd->ag_cnt >= 0) + return; + sd->ag_cnt = AG_CNT_START; +/* w1 w2 w3 */ +/* w4 w5 w6 */ +/* w7 w8 */ +/* w4 */ + avg_lum = ((data[sof + 29] << 8) | data[sof + 30]) >> 6; +/* w6 */ + avg_lum += ((data[sof + 33] << 8) | data[sof + 34]) >> 6; +/* w2 */ + avg_lum += ((data[sof + 25] << 8) | data[sof + 26]) >> 6; +/* w8 */ + avg_lum += ((data[sof + 37] << 8) | data[sof + 38]) >> 6; +/* w5 */ + avg_lum += ((data[sof + 31] << 8) | data[sof + 32]) >> 4; + avg_lum >>= 4; + sd->avg_lum = avg_lum; + PDEBUG(D_PACK, "mean lum %d", avg_lum); + setautogain(gspca_dev); + return; + } + if (gspca_dev->last_packet_type == LAST_PACKET) { + + /* put the JPEG 422 header */ + jpeg_put_header(gspca_dev, frame, sd->qindex, 0x21); + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static unsigned int getexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 hexpo, mexpo, lexpo; + + switch (sd->sensor) { + case SENSOR_HV7131R: + /* read sensor exposure */ + i2c_r5(gspca_dev, 0x25); + return (gspca_dev->usb_buf[0] << 16) + | (gspca_dev->usb_buf[1] << 8) + | gspca_dev->usb_buf[2]; + case SENSOR_MI0360: + /* read sensor exposure */ + i2c_r5(gspca_dev, 0x09); + return (gspca_dev->usb_buf[0] << 8) + | gspca_dev->usb_buf[1]; + case SENSOR_MO4000: + i2c_r5(gspca_dev, 0x0e); + hexpo = 0; /* gspca_dev->usb_buf[1] & 0x07; */ + mexpo = 0x40; /* gspca_dev->usb_buf[2] & 0xff; */ + lexpo = (gspca_dev->usb_buf[1] & 0x30) >> 4; + PDEBUG(D_CONF, "exposure %d", + (hexpo << 10) | (mexpo << 2) | lexpo); + return (hexpo << 10) | (mexpo << 2) | lexpo; + default: +/* case SENSOR_OV7660: */ + /* read sensor exposure */ + i2c_r5(gspca_dev, 0x04); + hexpo = gspca_dev->usb_buf[3] & 0x2f; + lexpo = gspca_dev->usb_buf[0] & 0x02; + i2c_r5(gspca_dev, 0x08); + mexpo = gspca_dev->usb_buf[2]; + return (hexpo << 10) | (mexpo << 2) | lexpo; + } +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* hardcoded registers seem not readable */ + switch (sd->sensor) { + case SENSOR_HV7131R: +/* sd->brightness = 0x7fff; */ + sd->brightness = getexposure(gspca_dev) >> 4; + break; + case SENSOR_MI0360: + sd->brightness = getexposure(gspca_dev) << 4; + break; + case SENSOR_MO4000: +/* sd->brightness = 0x1fff; */ + sd->brightness = getexposure(gspca_dev) << 4; + break; + } +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { +#ifndef CONFIG_USB_SN9C102 + {USB_DEVICE(0x0458, 0x7025), DVNM("Genius Eye 311Q")}, + {USB_DEVICE(0x045e, 0x00f5), DVNM("MicroSoft VX3000")}, + {USB_DEVICE(0x045e, 0x00f7), DVNM("MicroSoft VX1000")}, + {USB_DEVICE(0x0471, 0x0327), DVNM("Philips SPC 600 NC")}, + {USB_DEVICE(0x0471, 0x0328), DVNM("Philips SPC 700 NC")}, +#endif + {USB_DEVICE(0x0471, 0x0330), DVNM("Philips SPC 710NC")}, + {USB_DEVICE(0x0c45, 0x6040), DVNM("Speed NVC 350K")}, + {USB_DEVICE(0x0c45, 0x607c), DVNM("Sonix sn9c102p Hv7131R")}, + {USB_DEVICE(0x0c45, 0x60c0), DVNM("Sangha Sn535")}, + {USB_DEVICE(0x0c45, 0x60ec), DVNM("SN9C105+MO4000")}, + {USB_DEVICE(0x0c45, 0x60fb), DVNM("Surfer NoName")}, + {USB_DEVICE(0x0c45, 0x60fc), DVNM("LG-LIC300")}, + {USB_DEVICE(0x0c45, 0x612a), DVNM("Avant Camera")}, + {USB_DEVICE(0x0c45, 0x612c), DVNM("Typhoon Rasy Cam 1.3MPix")}, +#ifndef CONFIG_USB_SN9C102 + {USB_DEVICE(0x0c45, 0x6130), DVNM("Sonix Pccam")}, + {USB_DEVICE(0x0c45, 0x6138), DVNM("Sn9c120 Mo4000")}, + {USB_DEVICE(0x0c45, 0x613b), DVNM("Surfer SN-206")}, + {USB_DEVICE(0x0c45, 0x613c), DVNM("Sonix Pccam168")}, +#endif + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + info("v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c new file mode 100644 index 00000000000..15620611879 --- /dev/null +++ b/drivers/media/video/gspca/spca500.c @@ -0,0 +1,1216 @@ +/* + * SPCA500 chip based cameras initialization data + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MODULE_NAME "spca500" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + __u8 packet[ISO_MAX_SIZE + 128]; + /* !! no more than 128 ff in an ISO packet */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + + char qindex; + char subtype; +#define AgfaCl20 0 +#define AiptekPocketDV 1 +#define BenqDC1016 2 +#define CreativePCCam300 3 +#define DLinkDSC350 4 +#define Gsmartmini 5 +#define IntelPocketPCCamera 6 +#define KodakEZ200 7 +#define LogitechClickSmart310 8 +#define LogitechClickSmart510 9 +#define LogitechTraveler 10 +#define MustekGsmart300 11 +#define Optimedia 12 +#define PalmPixDC85 13 +#define ToptroIndus 14 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 127 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 63, + .step = 1, +#define CONTRAST_DEF 31 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 63, + .step = 1, +#define COLOR_DEF 31 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* Frame packet header offsets for the spca500 */ +#define SPCA500_OFFSET_PADDINGLB 2 +#define SPCA500_OFFSET_PADDINGHB 3 +#define SPCA500_OFFSET_MODE 4 +#define SPCA500_OFFSET_IMGWIDTH 5 +#define SPCA500_OFFSET_IMGHEIGHT 6 +#define SPCA500_OFFSET_IMGMODE 7 +#define SPCA500_OFFSET_QTBLINDEX 8 +#define SPCA500_OFFSET_FRAMSEQ 9 +#define SPCA500_OFFSET_CDSPINFO 10 +#define SPCA500_OFFSET_GPIO 11 +#define SPCA500_OFFSET_AUGPIO 12 +#define SPCA500_OFFSET_DATA 16 + + +static const __u16 spca500_visual_defaults[][3] = { + {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, + * hue (H byte) = 0, + * saturation/hue enable, + * brightness/contrast enable. + */ + {0x00, 0x0000, 0x8167}, /* brightness = 0 */ + {0x00, 0x0020, 0x8168}, /* contrast = 0 */ + {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, + * hue (H byte) = 0, saturation/hue enable, + * brightness/contrast enable. + * was 0x0003, now 0x0000. + */ + {0x00, 0x0000, 0x816a}, /* hue (L byte) = 0 */ + {0x00, 0x0020, 0x8169}, /* saturation = 0x20 */ + {0x00, 0x0050, 0x8157}, /* edge gain high threshold */ + {0x00, 0x0030, 0x8158}, /* edge gain low threshold */ + {0x00, 0x0028, 0x8159}, /* edge bandwidth high threshold */ + {0x00, 0x000a, 0x815a}, /* edge bandwidth low threshold */ + {0x00, 0x0001, 0x8202}, /* clock rate compensation = 1/25 sec/frame */ + {0x0c, 0x0004, 0x0000}, + /* set interface */ + {} +}; +static const __u16 Clicksmart510_defaults[][3] = { + {0x00, 0x00, 0x8211}, + {0x00, 0x01, 0x82c0}, + {0x00, 0x10, 0x82cb}, + {0x00, 0x0f, 0x800d}, + {0x00, 0x82, 0x8225}, + {0x00, 0x21, 0x8228}, + {0x00, 0x00, 0x8203}, + {0x00, 0x00, 0x8204}, + {0x00, 0x08, 0x8205}, + {0x00, 0xf8, 0x8206}, + {0x00, 0x28, 0x8207}, + {0x00, 0xa0, 0x8208}, + {0x00, 0x08, 0x824a}, + {0x00, 0x08, 0x8214}, + {0x00, 0x80, 0x82c1}, + {0x00, 0x00, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0x80, 0x82c1}, + {0x00, 0x04, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0xfc, 0x8100}, + {0x00, 0xfc, 0x8105}, + {0x00, 0x30, 0x8101}, + {0x00, 0x00, 0x8102}, + {0x00, 0x00, 0x8103}, + {0x00, 0x66, 0x8107}, + {0x00, 0x00, 0x816b}, + {0x00, 0x00, 0x8155}, + {0x00, 0x01, 0x8156}, + {0x00, 0x60, 0x8157}, + {0x00, 0x40, 0x8158}, + {0x00, 0x0a, 0x8159}, + {0x00, 0x06, 0x815a}, + {0x00, 0x00, 0x813f}, + {0x00, 0x00, 0x8200}, + {0x00, 0x19, 0x8201}, + {0x00, 0x00, 0x82c1}, + {0x00, 0xa0, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0x00, 0x8117}, + {0x00, 0x00, 0x8118}, + {0x00, 0x65, 0x8119}, + {0x00, 0x00, 0x811a}, + {0x00, 0x00, 0x811b}, + {0x00, 0x55, 0x811c}, + {0x00, 0x65, 0x811d}, + {0x00, 0x55, 0x811e}, + {0x00, 0x16, 0x811f}, + {0x00, 0x19, 0x8120}, + {0x00, 0x80, 0x8103}, + {0x00, 0x83, 0x816b}, + {0x00, 0x25, 0x8168}, + {0x00, 0x01, 0x820f}, + {0x00, 0xff, 0x8115}, + {0x00, 0x48, 0x8116}, + {0x00, 0x50, 0x8151}, + {0x00, 0x40, 0x8152}, + {0x00, 0x78, 0x8153}, + {0x00, 0x40, 0x8154}, + {0x00, 0x00, 0x8167}, + {0x00, 0x20, 0x8168}, + {0x00, 0x00, 0x816a}, + {0x00, 0x03, 0x816b}, + {0x00, 0x20, 0x8169}, + {0x00, 0x60, 0x8157}, + {0x00, 0x00, 0x8190}, + {0x00, 0x00, 0x81a1}, + {0x00, 0x00, 0x81b2}, + {0x00, 0x27, 0x8191}, + {0x00, 0x27, 0x81a2}, + {0x00, 0x27, 0x81b3}, + {0x00, 0x4b, 0x8192}, + {0x00, 0x4b, 0x81a3}, + {0x00, 0x4b, 0x81b4}, + {0x00, 0x66, 0x8193}, + {0x00, 0x66, 0x81a4}, + {0x00, 0x66, 0x81b5}, + {0x00, 0x79, 0x8194}, + {0x00, 0x79, 0x81a5}, + {0x00, 0x79, 0x81b6}, + {0x00, 0x8a, 0x8195}, + {0x00, 0x8a, 0x81a6}, + {0x00, 0x8a, 0x81b7}, + {0x00, 0x9b, 0x8196}, + {0x00, 0x9b, 0x81a7}, + {0x00, 0x9b, 0x81b8}, + {0x00, 0xa6, 0x8197}, + {0x00, 0xa6, 0x81a8}, + {0x00, 0xa6, 0x81b9}, + {0x00, 0xb2, 0x8198}, + {0x00, 0xb2, 0x81a9}, + {0x00, 0xb2, 0x81ba}, + {0x00, 0xbe, 0x8199}, + {0x00, 0xbe, 0x81aa}, + {0x00, 0xbe, 0x81bb}, + {0x00, 0xc8, 0x819a}, + {0x00, 0xc8, 0x81ab}, + {0x00, 0xc8, 0x81bc}, + {0x00, 0xd2, 0x819b}, + {0x00, 0xd2, 0x81ac}, + {0x00, 0xd2, 0x81bd}, + {0x00, 0xdb, 0x819c}, + {0x00, 0xdb, 0x81ad}, + {0x00, 0xdb, 0x81be}, + {0x00, 0xe4, 0x819d}, + {0x00, 0xe4, 0x81ae}, + {0x00, 0xe4, 0x81bf}, + {0x00, 0xed, 0x819e}, + {0x00, 0xed, 0x81af}, + {0x00, 0xed, 0x81c0}, + {0x00, 0xf7, 0x819f}, + {0x00, 0xf7, 0x81b0}, + {0x00, 0xf7, 0x81c1}, + {0x00, 0xff, 0x81a0}, + {0x00, 0xff, 0x81b1}, + {0x00, 0xff, 0x81c2}, + {0x00, 0x03, 0x8156}, + {0x00, 0x00, 0x8211}, + {0x00, 0x20, 0x8168}, + {0x00, 0x01, 0x8202}, + {0x00, 0x30, 0x8101}, + {0x00, 0x00, 0x8111}, + {0x00, 0x00, 0x8112}, + {0x00, 0x00, 0x8113}, + {0x00, 0x00, 0x8114}, + {} +}; + +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, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +static const __u8 qtable_kodak_ez200[2][64] = { + { /* Q-table Y-components */ + 0x02, 0x01, 0x01, 0x02, 0x02, 0x04, 0x05, 0x06, + 0x01, 0x01, 0x01, 0x02, 0x03, 0x06, 0x06, 0x06, + 0x01, 0x01, 0x02, 0x02, 0x04, 0x06, 0x07, 0x06, + 0x01, 0x02, 0x02, 0x03, 0x05, 0x09, 0x08, 0x06, + 0x02, 0x02, 0x04, 0x06, 0x07, 0x0b, 0x0a, 0x08, + 0x02, 0x04, 0x06, 0x06, 0x08, 0x0a, 0x0b, 0x09, + 0x05, 0x06, 0x08, 0x09, 0x0a, 0x0c, 0x0c, 0x0a, + 0x07, 0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a}, + { /* Q-table C-components */ + 0x02, 0x02, 0x02, 0x05, 0x0a, 0x0a, 0x0a, 0x0a, + 0x02, 0x02, 0x03, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, + 0x02, 0x03, 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x05, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a} +}; + +static const __u8 qtable_pocketdv[2][64] = { + { /* Q-table Y-components start registers 0x8800 */ + 0x06, 0x04, 0x04, 0x06, 0x0a, 0x10, 0x14, 0x18, + 0x05, 0x05, 0x06, 0x08, 0x0a, 0x17, 0x18, 0x16, + 0x06, 0x05, 0x06, 0x0a, 0x10, 0x17, 0x1c, 0x16, + 0x06, 0x07, 0x09, 0x0c, 0x14, 0x23, 0x20, 0x19, + 0x07, 0x09, 0x0f, 0x16, 0x1b, 0x2c, 0x29, 0x1f, + 0x0a, 0x0e, 0x16, 0x1a, 0x20, 0x2a, 0x2d, 0x25, + 0x14, 0x1a, 0x1f, 0x23, 0x29, 0x30, 0x30, 0x28, + 0x1d, 0x25, 0x26, 0x27, 0x2d, 0x28, 0x29, 0x28, + }, + { /* Q-table C-components start registers 0x8840 */ + 0x07, 0x07, 0x0a, 0x13, 0x28, 0x28, 0x28, 0x28, + 0x07, 0x08, 0x0a, 0x1a, 0x28, 0x28, 0x28, 0x28, + 0x0a, 0x0a, 0x16, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x13, 0x1a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28} +}; + +/* read 'len' bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 index, + __u16 length) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, length, 500); +} + +static int reg_w(struct gspca_dev *gspca_dev, + __u16 req, __u16 index, __u16 value) +{ + int ret; + + PDEBUG(D_USBO, "reg write: [0x%02x] = 0x%02x", index, value); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_r_12(struct gspca_dev *gspca_dev, + __u16 req, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + + gspca_dev->usb_buf[1] = 0; + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + gspca_dev->usb_buf, length, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_r_12 err %d", ret); + return -1; + } + return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; +} + +/* + * Simple function to wait for a given 8-bit value to be returned from + * a reg_read call. + * Returns: negative is error or timeout, zero is success. + */ +static int reg_r_wait(struct gspca_dev *gspca_dev, + __u16 reg, __u16 index, __u16 value) +{ + int ret, cnt = 20; + + while (--cnt > 0) { + ret = reg_r_12(gspca_dev, reg, index, 1); + if (ret == value) + return 0; + msleep(50); + } + return -EIO; +} + +static int write_vector(struct gspca_dev *gspca_dev, + const __u16 data[][3]) +{ + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_w(gspca_dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) + return ret; + i++; + } + 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]) +{ + int i, err; + + /* loop over y components */ + for (i = 0; i < 64; i++) { + err = reg_w(gspca_dev, request, ybase + i, qtable[0][i]); + if (err < 0) + return err; + } + + /* loop over c components */ + for (i = 0; i < 64; i++) { + err = reg_w(gspca_dev, request, cbase + i, qtable[1][i]); + if (err < 0) + return err; + } + return 0; +} + +static void spca500_ping310(struct gspca_dev *gspca_dev) +{ + reg_r(gspca_dev, 0x0d04, 2); + PDEBUG(D_STREAM, "ClickSmart310 ping 0x0d04 0x%02x 0x%02x", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); +} + +static void spca500_clksmart310_init(struct gspca_dev *gspca_dev) +{ + reg_r(gspca_dev, 0x0d05, 2); + PDEBUG(D_STREAM, "ClickSmart310 init 0x0d05 0x%02x 0x%02x", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); + reg_w(gspca_dev, 0x00, 0x8167, 0x5a); + spca500_ping310(gspca_dev); + + reg_w(gspca_dev, 0x00, 0x8168, 0x22); + reg_w(gspca_dev, 0x00, 0x816a, 0xc0); + reg_w(gspca_dev, 0x00, 0x816b, 0x0b); + reg_w(gspca_dev, 0x00, 0x8169, 0x25); + reg_w(gspca_dev, 0x00, 0x8157, 0x5b); + reg_w(gspca_dev, 0x00, 0x8158, 0x5b); + reg_w(gspca_dev, 0x00, 0x813f, 0x03); + reg_w(gspca_dev, 0x00, 0x8151, 0x4a); + reg_w(gspca_dev, 0x00, 0x8153, 0x78); + reg_w(gspca_dev, 0x00, 0x0d01, 0x04); + /* 00 for adjust shutter */ + reg_w(gspca_dev, 0x00, 0x0d02, 0x01); + reg_w(gspca_dev, 0x00, 0x8169, 0x25); + reg_w(gspca_dev, 0x00, 0x0d01, 0x02); +} + +static void spca500_setmode(struct gspca_dev *gspca_dev, + __u8 xmult, __u8 ymult) +{ + int mode; + + /* set x multiplier */ + reg_w(gspca_dev, 0, 0x8001, xmult); + + /* set y multiplier */ + reg_w(gspca_dev, 0, 0x8002, ymult); + + /* use compressed mode, VGA, with mode specific subsample */ + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + reg_w(gspca_dev, 0, 0x8003, mode << 4); +} + +static int spca500_full_reset(struct gspca_dev *gspca_dev) +{ + int err; + + /* send the reset command */ + err = reg_w(gspca_dev, 0xe0, 0x0001, 0x0000); + if (err < 0) + return err; + + /* wait for the reset to complete */ + err = reg_r_wait(gspca_dev, 0x06, 0x0000, 0x0000); + if (err < 0) + return err; + err = reg_w(gspca_dev, 0xe0, 0x0000, 0x0000); + if (err < 0) + return err; + err = reg_r_wait(gspca_dev, 0x06, 0, 0); + if (err < 0) { + PDEBUG(D_ERR, "reg_r_wait() failed"); + return err; + } + /* all ok */ + return 0; +} + +/* Synchro the Bridge with sensor */ +/* Maybe that will work on all spca500 chip */ +/* because i only own a clicksmart310 try for that chip */ +/* using spca50x_set_packet_size() cause an Ooops here */ +/* usb_set_interface from kernel 2.6.x clear all the urb stuff */ +/* up-port the same feature as in 2.4.x kernel */ +static int spca500_synch310(struct gspca_dev *gspca_dev) +{ + if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0) < 0) { + PDEBUG(D_ERR, "Set packet size: set interface error"); + goto error; + } + spca500_ping310(gspca_dev); + + reg_r(gspca_dev, 0x0d00, 1); + + /* need alt setting here */ + PDEBUG(D_PACK, "ClickSmart310 sync alt: %d", gspca_dev->alt); + + /* Windoze use pipe with altsetting 6 why 7 here */ + if (usb_set_interface(gspca_dev->dev, + gspca_dev->iface, + gspca_dev->alt) < 0) { + PDEBUG(D_ERR, "Set packet size: set interface error"); + goto error; + } + return 0; +error: + return -EBUSY; +} + +static void spca500_reinit(struct gspca_dev *gspca_dev) +{ + int err; + __u8 Data; + + /* some unknow command from Aiptek pocket dv and family300 */ + + reg_w(gspca_dev, 0x00, 0x0d01, 0x01); + reg_w(gspca_dev, 0x00, 0x0d03, 0x00); + reg_w(gspca_dev, 0x00, 0x0d02, 0x01); + + /* enable drop packet */ + reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840, + qtable_pocketdv); + if (err < 0) + PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed on init"); + + /* set qtable index */ + reg_w(gspca_dev, 0x00, 0x8880, 2); + /* family cam Quicksmart stuff */ + reg_w(gspca_dev, 0x00, 0x800a, 0x00); + /* Set agc transfer: synced inbetween frames */ + reg_w(gspca_dev, 0x00, 0x820f, 0x01); + /* Init SDRAM - needed for SDRAM access */ + reg_w(gspca_dev, 0x00, 0x870a, 0x04); + /*Start init sequence or stream */ + reg_w(gspca_dev, 0, 0x8003, 0x00); + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + msleep(2000); + if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) { + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + } +} + +/* 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; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x040a: /* Kodak cameras */ +/* switch (product) { */ +/* case 0x0300: */ + sd->subtype = KodakEZ200; +/* break; */ +/* } */ + break; + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x400a: */ + sd->subtype = CreativePCCam300; +/* break; */ +/* } */ + break; + case 0x046d: /* Logitech Labtec */ + switch (product) { + case 0x0890: + sd->subtype = LogitechTraveler; + break; + case 0x0900: + sd->subtype = LogitechClickSmart310; + break; + case 0x0901: + sd->subtype = LogitechClickSmart510; + break; + } + break; + case 0x04a5: /* Benq */ +/* switch (product) { */ +/* case 0x300c: */ + sd->subtype = BenqDC1016; +/* break; */ +/* } */ + break; + case 0x04fc: /* SunPlus */ +/* switch (product) { */ +/* case 0x7333: */ + sd->subtype = PalmPixDC85; +/* break; */ +/* } */ + break; + case 0x055f: /* Mustek cameras */ + switch (product) { + case 0xc200: + sd->subtype = MustekGsmart300; + break; + case 0xc220: + sd->subtype = Gsmartmini; + break; + } + break; + case 0x06bd: /* Agfa Cl20 */ +/* switch (product) { */ +/* case 0x0404: */ + sd->subtype = AgfaCl20; +/* break; */ +/* } */ + break; + case 0x06be: /* Optimedia */ +/* switch (product) { */ +/* case 0x0800: */ + sd->subtype = Optimedia; +/* break; */ +/* } */ + break; + case 0x084d: /* D-Link / Minton */ +/* switch (product) { */ +/* case 0x0003: * DSC-350 / S-Cam F5 */ + sd->subtype = DLinkDSC350; +/* break; */ +/* } */ + break; + case 0x08ca: /* Aiptek */ +/* switch (product) { */ +/* case 0x0103: */ + sd->subtype = AiptekPocketDV; +/* break; */ +/* } */ + break; + case 0x2899: /* ToptroIndustrial */ +/* switch (product) { */ +/* case 0x012c: */ + sd->subtype = ToptroIndus; +/* break; */ +/* } */ + break; + case 0x8086: /* Intel */ +/* switch (product) { */ +/* case 0x0630: * Pocket PC Camera */ + sd->subtype = IntelPocketPCCamera; +/* break; */ +/* } */ + break; + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + if (sd->subtype != LogitechClickSmart310) { + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + } else { + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + } + sd->qindex = 5; + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* initialisation of spca500 based cameras is deferred */ + PDEBUG(D_STREAM, "SPCA500 init"); + if (sd->subtype == LogitechClickSmart310) + spca500_clksmart310_init(gspca_dev); +/* else + spca500_initialise(gspca_dev); */ + PDEBUG(D_STREAM, "SPCA500 init done"); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err; + __u8 Data; + __u8 xmult, ymult; + + if (sd->subtype == LogitechClickSmart310) { + xmult = 0x16; + ymult = 0x12; + } else { + xmult = 0x28; + ymult = 0x1e; + } + + /* is there a sensor here ? */ + reg_r(gspca_dev, 0x8a04, 1); + PDEBUG(D_STREAM, "Spca500 Sensor Address 0x%02x", + gspca_dev->usb_buf[0]); + PDEBUG(D_STREAM, "Spca500 curr_mode: %d Xmult: 0x%02x, Ymult: 0x%02x", + gspca_dev->curr_mode, xmult, ymult); + + /* setup qtable */ + switch (sd->subtype) { + case LogitechClickSmart310: + spca500_setmode(gspca_dev, xmult, ymult); + + /* enable drop packet */ + reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + reg_w(gspca_dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + /* Init SDRAM - needed for SDRAM access */ + reg_w(gspca_dev, 0x00, 0x870a, 0x04); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + msleep(500); + if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_r_wait() failed"); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + + spca500_synch310(gspca_dev); + + write_vector(gspca_dev, spca500_visual_defaults); + spca500_setmode(gspca_dev, xmult, ymult); + /* enable drop packet */ + reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + PDEBUG(D_ERR, "failed to enable drop packet"); + reg_w(gspca_dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + + /* Init SDRAM - needed for SDRAM access */ + reg_w(gspca_dev, 0x00, 0x870a, 0x04); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + + if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_r_wait() failed"); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + break; + case CreativePCCam300: /* Creative PC-CAM 300 640x480 CCD */ + case IntelPocketPCCamera: /* FIXME: Temporary fix for + * Intel Pocket PC Camera + * - NWG (Sat 29th March 2003) */ + + /* do a full reset */ + err = spca500_full_reset(gspca_dev); + if (err < 0) + PDEBUG(D_ERR, "spca500_full_reset failed"); + + /* enable drop packet */ + err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + if (err < 0) + PDEBUG(D_ERR, "failed to enable drop packet"); + reg_w(gspca_dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + + spca500_setmode(gspca_dev, xmult, ymult); + reg_w(gspca_dev, 0x20, 0x0001, 0x0004); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + + if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_r_wait() failed"); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + +/* write_vector(gspca_dev, spca500_visual_defaults); */ + break; + case KodakEZ200: /* Kodak EZ200 */ + + /* do a full reset */ + err = spca500_full_reset(gspca_dev); + if (err < 0) + PDEBUG(D_ERR, "spca500_full_reset failed"); + /* enable drop packet */ + reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + reg_w(gspca_dev, 0x00, 0x8880, 0); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_kodak_ez200); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + spca500_setmode(gspca_dev, xmult, ymult); + + reg_w(gspca_dev, 0x20, 0x0001, 0x0004); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + + if (reg_r_wait(gspca_dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_r_wait() failed"); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + +/* write_vector(gspca_dev, spca500_visual_defaults); */ + break; + + case BenqDC1016: + case DLinkDSC350: /* FamilyCam 300 */ + case AiptekPocketDV: /* Aiptek PocketDV */ + case Gsmartmini: /*Mustek Gsmart Mini */ + case MustekGsmart300: /* Mustek Gsmart 300 */ + case PalmPixDC85: + case Optimedia: + case ToptroIndus: + case AgfaCl20: + spca500_reinit(gspca_dev); + reg_w(gspca_dev, 0x00, 0x0d01, 0x01); + /* enable drop packet */ + reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, qtable_pocketdv); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + reg_w(gspca_dev, 0x00, 0x8880, 2); + + /* familycam Quicksmart pocketDV stuff */ + reg_w(gspca_dev, 0x00, 0x800a, 0x00); + /* Set agc transfer: synced inbetween frames */ + reg_w(gspca_dev, 0x00, 0x820f, 0x01); + /* Init SDRAM - needed for SDRAM access */ + reg_w(gspca_dev, 0x00, 0x870a, 0x04); + + spca500_setmode(gspca_dev, xmult, ymult); + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + + reg_r_wait(gspca_dev, 0, 0x8000, 0x44); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + break; + case LogitechTraveler: + case LogitechClickSmart510: + reg_w(gspca_dev, 0x02, 0x00, 0x00); + /* enable drop packet */ + reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, + 0x8840, qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + reg_w(gspca_dev, 0x00, 0x8880, 3); + reg_w(gspca_dev, 0x00, 0x800a, 0x00); + /* Init SDRAM - needed for SDRAM access */ + reg_w(gspca_dev, 0x00, 0x870a, 0x04); + + spca500_setmode(gspca_dev, xmult, ymult); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + reg_r_wait(gspca_dev, 0, 0x8000, 0x44); + + reg_r(gspca_dev, 0x816b, 1); + Data = gspca_dev->usb_buf[0]; + reg_w(gspca_dev, 0x00, 0x816b, Data); + write_vector(gspca_dev, Clicksmart510_defaults); + break; + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0, 0x8003, 0x00); + + /* switch to video camera mode */ + reg_w(gspca_dev, 0x00, 0x8000, 0x0004); + reg_r(gspca_dev, 0x8000, 1); + PDEBUG(D_STREAM, "stop SPCA500 done reg8000: 0x%2x", + gspca_dev->usb_buf[0]); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + __u8 *s, *d; + static __u8 ffd9[] = {0xff, 0xd9}; + +/* frames are jpeg 4.1.1 without 0xff escape */ + if (data[0] == 0xff) { + if (data[1] != 0x01) { /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + ffd9, 2); + + /* put the JPEG header in the new frame */ + jpeg_put_header(gspca_dev, frame, sd->qindex, 0x22); + + data += SPCA500_OFFSET_DATA; + len -= SPCA500_OFFSET_DATA; + } else { + data += 1; + len -= 1; + } + + /* add 0x00 after 0xff */ + for (i = len; --i >= 0; ) + if (data[i] == 0xff) + break; + if (i < 0) { /* no 0xff */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + return; + } + s = data; + d = sd->packet; + for (i = 0; i < len; i++) { + *d++ = *s++; + if (s[-1] == 0xff) + *d++ = 0x00; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + sd->packet, d - sd->packet); +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0x00, 0x8167, + (__u8) (sd->brightness - 128)); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_r_12(gspca_dev, 0x00, 0x8167, 1); + if (ret >= 0) + sd->brightness = ret + 128; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0x00, 0x8168, sd->contrast); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_r_12(gspca_dev, 0x0, 0x8168, 1); + if (ret >= 0) + sd->contrast = ret; +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0x00, 0x8169, sd->colors); +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_r_12(gspca_dev, 0x0, 0x8169, 1); + if (ret >= 0) + sd->colors = ret; +} + +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; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x040a, 0x0300), DVNM("Kodak EZ200")}, + {USB_DEVICE(0x041e, 0x400a), DVNM("Creative PC-CAM 300")}, + {USB_DEVICE(0x046d, 0x0890), DVNM("Logitech QuickCam traveler")}, + {USB_DEVICE(0x046d, 0x0900), DVNM("Logitech Inc. ClickSmart 310")}, + {USB_DEVICE(0x046d, 0x0901), DVNM("Logitech Inc. ClickSmart 510")}, + {USB_DEVICE(0x04a5, 0x300c), DVNM("Benq DC1016")}, + {USB_DEVICE(0x04fc, 0x7333), DVNM("PalmPixDC85")}, + {USB_DEVICE(0x055f, 0xc200), DVNM("Mustek Gsmart 300")}, + {USB_DEVICE(0x055f, 0xc220), DVNM("Gsmart Mini")}, + {USB_DEVICE(0x06bd, 0x0404), DVNM("Agfa CL20")}, + {USB_DEVICE(0x06be, 0x0800), DVNM("Optimedia")}, + {USB_DEVICE(0x084d, 0x0003), DVNM("D-Link DSC-350")}, + {USB_DEVICE(0x08ca, 0x0103), DVNM("Aiptek PocketDV")}, + {USB_DEVICE(0x2899, 0x012c), DVNM("Toptro Industrial")}, + {USB_DEVICE(0x8086, 0x0630), DVNM("Intel Pocket PC Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c new file mode 100644 index 00000000000..50e929de020 --- /dev/null +++ b/drivers/media/video/gspca/spca501.c @@ -0,0 +1,2229 @@ +/* + * SPCA501 chip based cameras initialization data + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MODULE_NAME "spca501" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA/SPCA501 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned short contrast; + __u8 brightness; + __u8 colors; + + char subtype; +#define Arowana300KCMOSCamera 0 +#define IntelCreateAndShare 1 +#define KodakDVC325 2 +#define MystFromOriUnknownCamera 3 +#define SmileIntlCamera 4 +#define ThreeComHomeConnectLite 5 +#define ViewQuestM318B 6 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define MY_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 63, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define MY_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xffff, + .step = 1, + .default_value = 0xaa00, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define MY_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 63, + .step = 1, + .default_value = 31, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SPCA501, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +#define SPCA50X_REG_USB 0x2 /* spca505 501 */ +/* + * Data to initialize a SPCA501. From a capture file provided by Bill Roehl + * With SPCA501 chip description + */ +#define CCDSP_SET /* set CCDSP parameters */ +#define TG_SET /* set time generator set */ +#undef DSPWIN_SET /* set DSP windows parameters */ +#undef ALTER_GAMA /* Set alternate set to YUV transform coeffs. */ +#define SPCA501_SNAPBIT 0x80 +#define SPCA501_SNAPCTRL 0x10 +/* Frame packet header offsets for the spca501 */ +#define SPCA501_OFFSET_GPIO 1 +#define SPCA501_OFFSET_TYPE 2 +#define SPCA501_OFFSET_TURN3A 3 +#define SPCA501_OFFSET_FRAMSEQ 4 +#define SPCA501_OFFSET_COMPRESS 5 +#define SPCA501_OFFSET_QUANT 6 +#define SPCA501_OFFSET_QUANT2 7 +#define SPCA501_OFFSET_DATA 8 + +#define SPCA501_PROP_COMP_ENABLE(d) ((d) & 1) +#define SPCA501_PROP_SNAP(d) ((d) & 0x40) +#define SPCA501_PROP_SNAP_CTRL(d) ((d) & 0x10) +#define SPCA501_PROP_COMP_THRESH(d) (((d) & 0x0e) >> 1) +#define SPCA501_PROP_COMP_QUANT(d) (((d) & 0x70) >> 4) + +/* SPCA501 CCDSP control */ +#define SPCA501_REG_CCDSP 0x01 +/* SPCA501 control/status registers */ +#define SPCA501_REG_CTLRL 0x02 + +/* registers for color correction and YUV transformation */ +#define SPCA501_A11 0x08 +#define SPCA501_A12 0x09 +#define SPCA501_A13 0x0A +#define SPCA501_A21 0x0B +#define SPCA501_A22 0x0C +#define SPCA501_A23 0x0D +#define SPCA501_A31 0x0E +#define SPCA501_A32 0x0F +#define SPCA501_A33 0x10 + +/* Data for video camera initialization before capturing */ +static const __u16 spca501_open_data[][3] = { + /* bmRequest,value,index */ + + {0x2, 0x50, 0x00}, /* C/S enable soft reset */ + {0x2, 0x40, 0x00}, /* C/S disable soft reset */ + {0x2, 0x02, 0x05}, /* C/S general purpose I/O data */ + {0x2, 0x03, 0x05}, /* C/S general purpose I/O data */ + +#ifdef CCDSP_SET + {0x1, 0x38, 0x01}, /* CCDSP options */ + {0x1, 0x05, 0x02}, /* CCDSP Optical black level for user settings */ + {0x1, 0xC0, 0x03}, /* CCDSP Optical black settings */ + + {0x1, 0x67, 0x07}, + {0x1, 0x63, 0x3f}, /* CCDSP CCD gamma enable */ + {0x1, 0x03, 0x56}, /* Add gamma correction */ + + {0x1, 0xFF, 0x15}, /* CCDSP High luminance for white balance */ + {0x1, 0x01, 0x16}, /* CCDSP Low luminance for white balance */ + +/* Color correction and RGB-to-YUV transformation coefficients changing */ +#ifdef ALTER_GAMA + {0x0, 0x00, 0x08}, /* A11 */ + {0x0, 0x00, 0x09}, /* A12 */ + {0x0, 0x90, 0x0A}, /* A13 */ + {0x0, 0x12, 0x0B}, /* A21 */ + {0x0, 0x00, 0x0C}, /* A22 */ + {0x0, 0x00, 0x0D}, /* A23 */ + {0x0, 0x00, 0x0E}, /* A31 */ + {0x0, 0x02, 0x0F}, /* A32 */ + {0x0, 0x00, 0x10}, /* A33 */ +#else + {0x1, 0x2a, 0x08}, /* A11 0x31 */ + {0x1, 0xf8, 0x09}, /* A12 f8 */ + {0x1, 0xf8, 0x0A}, /* A13 f8 */ + {0x1, 0xf8, 0x0B}, /* A21 f8 */ + {0x1, 0x14, 0x0C}, /* A22 0x14 */ + {0x1, 0xf8, 0x0D}, /* A23 f8 */ + {0x1, 0xf8, 0x0E}, /* A31 f8 */ + {0x1, 0xf8, 0x0F}, /* A32 f8 */ + {0x1, 0x20, 0x10}, /* A33 0x20 */ +#endif + {0x1, 0x00, 0x11}, /* R offset */ + {0x1, 0x00, 0x12}, /* G offset */ + {0x1, 0x00, 0x13}, /* B offset */ + {0x1, 0x00, 0x14}, /* GB offset */ + +#endif + +#ifdef TG_SET + /* Time generator manipulations */ + {0x0, 0xfc, 0x0}, /* Set up high bits of shutter speed */ + {0x0, 0x01, 0x1}, /* Set up low bits of shutter speed */ + + {0x0, 0xe4, 0x04}, /* DCLK*2 clock phase adjustment */ + {0x0, 0x08, 0x05}, /* ADCK phase adjustment, inv. ext. VB */ + {0x0, 0x03, 0x06}, /* FR phase adjustment */ + {0x0, 0x01, 0x07}, /* FCDS phase adjustment */ + {0x0, 0x39, 0x08}, /* FS phase adjustment */ + {0x0, 0x88, 0x0a}, /* FH1 phase and delay adjustment */ + {0x0, 0x03, 0x0f}, /* pixel identification */ + {0x0, 0x00, 0x11}, /* clock source selection (default) */ + + /*VERY strange manipulations with + * select DMCLP or OBPX to be ADCLP output (0x0C) + * OPB always toggle or not (0x0D) but they allow + * us to set up brightness + */ + {0x0, 0x01, 0x0c}, + {0x0, 0xe0, 0x0d}, + /* Done */ +#endif + +#ifdef DSPWIN_SET + {0x1, 0xa0, 0x01}, /* Setting image processing parameters */ + {0x1, 0x1c, 0x17}, /* Changing Windows positions X1 */ + {0x1, 0xe2, 0x19}, /* X2 */ + {0x1, 0x1c, 0x1b}, /* X3 */ + {0x1, 0xe2, 0x1d}, /* X4 */ + {0x1, 0x5f, 0x1f}, /* X5 */ + {0x1, 0x32, 0x20}, /* Y5 */ + {0x1, 0x01, 0x10}, /* Changing A33 */ +#endif + + {0x2, 0x204a, 0x07},/* Setting video compression & resolution 160x120 */ + {0x2, 0x94, 0x06}, /* Setting video no compression */ + {} +}; + +/* + The SPCAxxx docs from Sunplus document these values + in tables, one table per register number. In the data + below, dmRequest is the register number, index is the Addr, + and value is a combination of Bit values. + Bit Value (hex) + 0 01 + 1 02 + 2 04 + 3 08 + 4 10 + 5 20 + 6 40 + 7 80 + */ + +/* Data for chip initialization (set default values) */ +static const __u16 spca501_init_data[][3] = { + /* Set all the values to powerup defaults */ + /* bmRequest,value,index */ + {0x0, 0xAA, 0x00}, + {0x0, 0x02, 0x01}, + {0x0, 0x01, 0x02}, + {0x0, 0x02, 0x03}, + {0x0, 0xCE, 0x04}, + {0x0, 0x00, 0x05}, + {0x0, 0x00, 0x06}, + {0x0, 0x00, 0x07}, + {0x0, 0x00, 0x08}, + {0x0, 0x00, 0x09}, + {0x0, 0x90, 0x0A}, + {0x0, 0x12, 0x0B}, + {0x0, 0x00, 0x0C}, + {0x0, 0x00, 0x0D}, + {0x0, 0x00, 0x0E}, + {0x0, 0x02, 0x0F}, + {0x0, 0x00, 0x10}, + {0x0, 0x00, 0x11}, + {0x0, 0x00, 0x12}, + {0x0, 0x00, 0x13}, + {0x0, 0x00, 0x14}, + {0x0, 0x00, 0x15}, + {0x0, 0x00, 0x16}, + {0x0, 0x00, 0x17}, + {0x0, 0x00, 0x18}, + {0x0, 0x00, 0x19}, + {0x0, 0x00, 0x1A}, + {0x0, 0x00, 0x1B}, + {0x0, 0x00, 0x1C}, + {0x0, 0x00, 0x1D}, + {0x0, 0x00, 0x1E}, + {0x0, 0x00, 0x1F}, + {0x0, 0x00, 0x20}, + {0x0, 0x00, 0x21}, + {0x0, 0x00, 0x22}, + {0x0, 0x00, 0x23}, + {0x0, 0x00, 0x24}, + {0x0, 0x00, 0x25}, + {0x0, 0x00, 0x26}, + {0x0, 0x00, 0x27}, + {0x0, 0x00, 0x28}, + {0x0, 0x00, 0x29}, + {0x0, 0x00, 0x2A}, + {0x0, 0x00, 0x2B}, + {0x0, 0x00, 0x2C}, + {0x0, 0x00, 0x2D}, + {0x0, 0x00, 0x2E}, + {0x0, 0x00, 0x2F}, + {0x0, 0x00, 0x30}, + {0x0, 0x00, 0x31}, + {0x0, 0x00, 0x32}, + {0x0, 0x00, 0x33}, + {0x0, 0x00, 0x34}, + {0x0, 0x00, 0x35}, + {0x0, 0x00, 0x36}, + {0x0, 0x00, 0x37}, + {0x0, 0x00, 0x38}, + {0x0, 0x00, 0x39}, + {0x0, 0x00, 0x3A}, + {0x0, 0x00, 0x3B}, + {0x0, 0x00, 0x3C}, + {0x0, 0x00, 0x3D}, + {0x0, 0x00, 0x3E}, + {0x0, 0x00, 0x3F}, + {0x0, 0x00, 0x40}, + {0x0, 0x00, 0x41}, + {0x0, 0x00, 0x42}, + {0x0, 0x00, 0x43}, + {0x0, 0x00, 0x44}, + {0x0, 0x00, 0x45}, + {0x0, 0x00, 0x46}, + {0x0, 0x00, 0x47}, + {0x0, 0x00, 0x48}, + {0x0, 0x00, 0x49}, + {0x0, 0x00, 0x4A}, + {0x0, 0x00, 0x4B}, + {0x0, 0x00, 0x4C}, + {0x0, 0x00, 0x4D}, + {0x0, 0x00, 0x4E}, + {0x0, 0x00, 0x4F}, + {0x0, 0x00, 0x50}, + {0x0, 0x00, 0x51}, + {0x0, 0x00, 0x52}, + {0x0, 0x00, 0x53}, + {0x0, 0x00, 0x54}, + {0x0, 0x00, 0x55}, + {0x0, 0x00, 0x56}, + {0x0, 0x00, 0x57}, + {0x0, 0x00, 0x58}, + {0x0, 0x00, 0x59}, + {0x0, 0x00, 0x5A}, + {0x0, 0x00, 0x5B}, + {0x0, 0x00, 0x5C}, + {0x0, 0x00, 0x5D}, + {0x0, 0x00, 0x5E}, + {0x0, 0x00, 0x5F}, + {0x0, 0x00, 0x60}, + {0x0, 0x00, 0x61}, + {0x0, 0x00, 0x62}, + {0x0, 0x00, 0x63}, + {0x0, 0x00, 0x64}, + {0x0, 0x00, 0x65}, + {0x0, 0x00, 0x66}, + {0x0, 0x00, 0x67}, + {0x0, 0x00, 0x68}, + {0x0, 0x00, 0x69}, + {0x0, 0x00, 0x6A}, + {0x0, 0x00, 0x6B}, + {0x0, 0x00, 0x6C}, + {0x0, 0x00, 0x6D}, + {0x0, 0x00, 0x6E}, + {0x0, 0x00, 0x6F}, + {0x0, 0x00, 0x70}, + {0x0, 0x00, 0x71}, + {0x0, 0x00, 0x72}, + {0x0, 0x00, 0x73}, + {0x0, 0x00, 0x74}, + {0x0, 0x00, 0x75}, + {0x0, 0x00, 0x76}, + {0x0, 0x00, 0x77}, + {0x0, 0x00, 0x78}, + {0x0, 0x00, 0x79}, + {0x0, 0x00, 0x7A}, + {0x0, 0x00, 0x7B}, + {0x0, 0x00, 0x7C}, + {0x0, 0x00, 0x7D}, + {0x0, 0x00, 0x7E}, + {0x0, 0x00, 0x7F}, + {0x0, 0x00, 0x80}, + {0x0, 0x00, 0x81}, + {0x0, 0x00, 0x82}, + {0x0, 0x00, 0x83}, + {0x0, 0x00, 0x84}, + {0x0, 0x00, 0x85}, + {0x0, 0x00, 0x86}, + {0x0, 0x00, 0x87}, + {0x0, 0x00, 0x88}, + {0x0, 0x00, 0x89}, + {0x0, 0x00, 0x8A}, + {0x0, 0x00, 0x8B}, + {0x0, 0x00, 0x8C}, + {0x0, 0x00, 0x8D}, + {0x0, 0x00, 0x8E}, + {0x0, 0x00, 0x8F}, + {0x0, 0x00, 0x90}, + {0x0, 0x00, 0x91}, + {0x0, 0x00, 0x92}, + {0x0, 0x00, 0x93}, + {0x0, 0x00, 0x94}, + {0x0, 0x00, 0x95}, + {0x0, 0x00, 0x96}, + {0x0, 0x00, 0x97}, + {0x0, 0x00, 0x98}, + {0x0, 0x00, 0x99}, + {0x0, 0x00, 0x9A}, + {0x0, 0x00, 0x9B}, + {0x0, 0x00, 0x9C}, + {0x0, 0x00, 0x9D}, + {0x0, 0x00, 0x9E}, + {0x0, 0x00, 0x9F}, + {0x0, 0x00, 0xA0}, + {0x0, 0x00, 0xA1}, + {0x0, 0x00, 0xA2}, + {0x0, 0x00, 0xA3}, + {0x0, 0x00, 0xA4}, + {0x0, 0x00, 0xA5}, + {0x0, 0x00, 0xA6}, + {0x0, 0x00, 0xA7}, + {0x0, 0x00, 0xA8}, + {0x0, 0x00, 0xA9}, + {0x0, 0x00, 0xAA}, + {0x0, 0x00, 0xAB}, + {0x0, 0x00, 0xAC}, + {0x0, 0x00, 0xAD}, + {0x0, 0x00, 0xAE}, + {0x0, 0x00, 0xAF}, + {0x0, 0x00, 0xB0}, + {0x0, 0x00, 0xB1}, + {0x0, 0x00, 0xB2}, + {0x0, 0x00, 0xB3}, + {0x0, 0x00, 0xB4}, + {0x0, 0x00, 0xB5}, + {0x0, 0x00, 0xB6}, + {0x0, 0x00, 0xB7}, + {0x0, 0x00, 0xB8}, + {0x0, 0x00, 0xB9}, + {0x0, 0x00, 0xBA}, + {0x0, 0x00, 0xBB}, + {0x0, 0x00, 0xBC}, + {0x0, 0x00, 0xBD}, + {0x0, 0x00, 0xBE}, + {0x0, 0x00, 0xBF}, + {0x0, 0x00, 0xC0}, + {0x0, 0x00, 0xC1}, + {0x0, 0x00, 0xC2}, + {0x0, 0x00, 0xC3}, + {0x0, 0x00, 0xC4}, + {0x0, 0x00, 0xC5}, + {0x0, 0x00, 0xC6}, + {0x0, 0x00, 0xC7}, + {0x0, 0x00, 0xC8}, + {0x0, 0x00, 0xC9}, + {0x0, 0x00, 0xCA}, + {0x0, 0x00, 0xCB}, + {0x0, 0x00, 0xCC}, + {0x1, 0xF4, 0x00}, + {0x1, 0x38, 0x01}, + {0x1, 0x40, 0x02}, + {0x1, 0x0A, 0x03}, + {0x1, 0x40, 0x04}, + {0x1, 0x40, 0x05}, + {0x1, 0x40, 0x06}, + {0x1, 0x67, 0x07}, + {0x1, 0x31, 0x08}, + {0x1, 0x00, 0x09}, + {0x1, 0x00, 0x0A}, + {0x1, 0x00, 0x0B}, + {0x1, 0x14, 0x0C}, + {0x1, 0x00, 0x0D}, + {0x1, 0x00, 0x0E}, + {0x1, 0x00, 0x0F}, + {0x1, 0x1E, 0x10}, + {0x1, 0x00, 0x11}, + {0x1, 0x00, 0x12}, + {0x1, 0x00, 0x13}, + {0x1, 0x00, 0x14}, + {0x1, 0xFF, 0x15}, + {0x1, 0x01, 0x16}, + {0x1, 0x32, 0x17}, + {0x1, 0x23, 0x18}, + {0x1, 0xCE, 0x19}, + {0x1, 0x23, 0x1A}, + {0x1, 0x32, 0x1B}, + {0x1, 0x8D, 0x1C}, + {0x1, 0xCE, 0x1D}, + {0x1, 0x8D, 0x1E}, + {0x1, 0x00, 0x1F}, + {0x1, 0x00, 0x20}, + {0x1, 0xFF, 0x3E}, + {0x1, 0x02, 0x3F}, + {0x1, 0x00, 0x40}, + {0x1, 0x00, 0x41}, + {0x1, 0x00, 0x42}, + {0x1, 0x00, 0x43}, + {0x1, 0x00, 0x44}, + {0x1, 0x00, 0x45}, + {0x1, 0x00, 0x46}, + {0x1, 0x00, 0x47}, + {0x1, 0x00, 0x48}, + {0x1, 0x00, 0x49}, + {0x1, 0x00, 0x4A}, + {0x1, 0x00, 0x4B}, + {0x1, 0x00, 0x4C}, + {0x1, 0x00, 0x4D}, + {0x1, 0x00, 0x4E}, + {0x1, 0x00, 0x4F}, + {0x1, 0x00, 0x50}, + {0x1, 0x00, 0x51}, + {0x1, 0x00, 0x52}, + {0x1, 0x00, 0x53}, + {0x1, 0x00, 0x54}, + {0x1, 0x00, 0x55}, + {0x1, 0x00, 0x56}, + {0x1, 0x00, 0x57}, + {0x1, 0x00, 0x58}, + {0x1, 0x00, 0x59}, + {0x1, 0x00, 0x5A}, + {0x2, 0x03, 0x00}, + {0x2, 0x00, 0x01}, + {0x2, 0x00, 0x05}, + {0x2, 0x00, 0x06}, + {0x2, 0x00, 0x07}, + {0x2, 0x00, 0x10}, + {0x2, 0x00, 0x11}, + /* Strange - looks like the 501 driver doesn't do anything + * at insert time except read the EEPROM + */ + {} +}; + +/* Data for video camera init before capture. + * Capture and decoding by Colin Peart. + * This is is for the 3com HomeConnect Lite which is spca501a based. + */ +static const __u16 spca501_3com_open_data[][3] = { + /* bmRequest,value,index */ + {0x2, 0x0050, 0x0000}, /* C/S Enable TG soft reset, timing mode=010 */ + {0x2, 0x0043, 0x0000}, /* C/S Disable TG soft reset, timing mode=010 */ + {0x2, 0x0002, 0x0005}, /* C/S GPIO */ + {0x2, 0x0003, 0x0005}, /* C/S GPIO */ + +#ifdef CCDSP_SET + {0x1, 0x0020, 0x0001}, /* CCDSP Options */ + + {0x1, 0x0020, 0x0002}, /* CCDSP Black Level */ + {0x1, 0x006e, 0x0007}, /* CCDSP Gamma options */ + {0x1, 0x0090, 0x0015}, /* CCDSP Luminance Low */ + {0x1, 0x00ff, 0x0016}, /* CCDSP Luminance High */ + {0x1, 0x0003, 0x003F}, /* CCDSP Gamma correction toggle */ + +#ifdef ALTER_GAMMA + {0x1, 0x0010, 0x0008}, /* CCDSP YUV A11 */ + {0x1, 0x0000, 0x0009}, /* CCDSP YUV A12 */ + {0x1, 0x0000, 0x000a}, /* CCDSP YUV A13 */ + {0x1, 0x0000, 0x000b}, /* CCDSP YUV A21 */ + {0x1, 0x0010, 0x000c}, /* CCDSP YUV A22 */ + {0x1, 0x0000, 0x000d}, /* CCDSP YUV A23 */ + {0x1, 0x0000, 0x000e}, /* CCDSP YUV A31 */ + {0x1, 0x0000, 0x000f}, /* CCDSP YUV A32 */ + {0x1, 0x0010, 0x0010}, /* CCDSP YUV A33 */ + {0x1, 0x0000, 0x0011}, /* CCDSP R Offset */ + {0x1, 0x0000, 0x0012}, /* CCDSP G Offset */ + {0x1, 0x0001, 0x0013}, /* CCDSP B Offset */ + {0x1, 0x0001, 0x0014}, /* CCDSP BG Offset */ + {0x1, 0x003f, 0x00C1}, /* CCDSP Gamma Correction Enable */ +#endif +#endif + +#ifdef TG_SET + {0x0, 0x00fc, 0x0000}, /* TG Shutter Speed High Bits */ + {0x0, 0x0000, 0x0001}, /* TG Shutter Speed Low Bits */ + {0x0, 0x00e4, 0x0004}, /* TG DCLK*2 Adjust */ + {0x0, 0x0008, 0x0005}, /* TG ADCK Adjust */ + {0x0, 0x0003, 0x0006}, /* TG FR Phase Adjust */ + {0x0, 0x0001, 0x0007}, /* TG FCDS Phase Adjust */ + {0x0, 0x0039, 0x0008}, /* TG FS Phase Adjust */ + {0x0, 0x0088, 0x000a}, /* TG MH1 */ + {0x0, 0x0003, 0x000f}, /* TG Pixel ID */ + + /* Like below, unexplained toglleing */ + {0x0, 0x0080, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0004, 0x000d}, + {0x0, 0x0000, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0040, 0x000c}, + {0x0, 0x0017, 0x000d}, + {0x0, 0x00c0, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0006, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0004, 0x000d}, + {0x0, 0x0002, 0x0003}, +#endif + +#ifdef DSPWIN_SET + {0x1, 0x001c, 0x0017}, /* CCDSP W1 Start X */ + {0x1, 0x00e2, 0x0019}, /* CCDSP W2 Start X */ + {0x1, 0x001c, 0x001b}, /* CCDSP W3 Start X */ + {0x1, 0x00e2, 0x001d}, /* CCDSP W4 Start X */ + {0x1, 0x00aa, 0x001f}, /* CCDSP W5 Start X */ + {0x1, 0x0070, 0x0020}, /* CCDSP W5 Start Y */ +#endif + {0x0, 0x0001, 0x0010}, /* TG Start Clock */ + +/* {0x2, 0x006a, 0x0001}, * C/S Enable ISOSYNCH Packet Engine */ + {0x2, 0x0068, 0x0001}, /* C/S Diable ISOSYNCH Packet Engine */ + {0x2, 0x0000, 0x0005}, + {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */ + {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */ + {0x2, 0x0002, 0x0005}, /* C/S GPIO */ + {0x2, 0x0003, 0x0005}, /* C/S GPIO */ + + {0x2, 0x006a, 0x0001}, /* C/S Enable ISOSYNCH Packet Engine */ + {} +}; + +/* + * Data used to initialize a SPCA501C with HV7131B sensor. + * From a capture file taken with USBSnoop v 1.5 + * I have a "SPCA501C pc camera chipset" manual by sunplus, but some + * of the value meanings are obscure or simply "reserved". + * to do list: + * 1) Understand what every value means + * 2) Understand why some values seem to appear more than once + * 3) Write a small comment for each line of the following arrays. + */ +static const __u16 spca501c_arowana_open_data[][3] = { + /* bmRequest,value,index */ + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x01, 0x0006, 0x0011}, + {0x01, 0x00ff, 0x0012}, + {0x01, 0x0014, 0x0013}, + {0x01, 0x0000, 0x0014}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x01, 0x0000, 0x0055}, + {0x00, 0x0025, 0x0000}, + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {} +}; + +static const __u16 spca501c_arowana_init_data[][3] = { + /* bmRequest,value,index */ + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x01, 0x0006, 0x0011}, + {0x01, 0x00ff, 0x0012}, + {0x01, 0x0014, 0x0013}, + {0x01, 0x0000, 0x0014}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x01, 0x0000, 0x0055}, + {0x00, 0x0025, 0x0000}, + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x00c8, 0x0015}, + {0x01, 0x0032, 0x0016}, + {0x01, 0x0000, 0x0011}, + {0x01, 0x0000, 0x0012}, + {0x01, 0x0000, 0x0013}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x000f, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x001e, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x002d, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {} +}; + +/* Unknow camera from Ori Usbid 0x0000:0x0000 */ +/* Based on snoops from Ori Cohen */ +static const __u16 spca501c_mysterious_open_data[][3] = { + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, +/* DSP Registers */ + {0x01, 0x0016, 0x0011}, /* RGB offset */ + {0x01, 0x0000, 0x0012}, + {0x01, 0x0006, 0x0013}, + {0x01, 0x0078, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0046, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x00, 0x0025, 0x0000}, +/* {0x00, 0x0000, 0x0000 }, */ +/* Part 2 */ +/* TG Registers */ + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {} +}; + +/* Based on snoops from Ori Cohen */ +static const __u16 spca501c_mysterious_init_data[][3] = { +/* Part 3 */ +/* TG registers */ +/* {0x00, 0x0000, 0x0000}, */ + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x0006, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0001, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, /* 640 */ + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, /* 480 */ + {0x00, 0x0000, 0x0024}, /* Offset H hight */ + {0x00, 0x00d3, 0x0025}, /* low */ + {0x00, 0x0000, 0x0026}, /* Offset V */ + {0x00, 0x000d, 0x0027}, /* low */ + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, +/* DSP Registers */ + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, /* Level Calc bit7 ->1 Auto */ + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x000f, 0x0008}, /* A11 Color correction coeff */ + {0x01, 0x002d, 0x0009}, /* A12 */ + {0x01, 0x0005, 0x000a}, /* A13 */ + {0x01, 0x0023, 0x000b}, /* A21 */ + {0x01, 0x00e0, 0x000c}, /* A22 */ + {0x01, 0x00fd, 0x000d}, /* A23 */ + {0x01, 0x00f4, 0x000e}, /* A31 */ + {0x01, 0x00e4, 0x000f}, /* A32 */ + {0x01, 0x0028, 0x0010}, /* A33 */ + {0x01, 0x00ff, 0x0015}, /* Reserved */ + {0x01, 0x0001, 0x0016}, /* Reserved */ + {0x01, 0x0032, 0x0017}, /* Win1 Start begin */ + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, /* Win1 Start end */ + {0x01, 0x00ff, 0x003e}, /* Reserved begin */ + {0x01, 0x0002, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0003, 0x0056}, /* Reserved end */ + {0x01, 0x0060, 0x0057}, /* Edge Gain */ + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, /* Edge Bandwidth */ + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x200a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, +/* Part 4 */ + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0000, 0x0005}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x004e, 0x0000}, +/* Part 5 */ + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x000f, 0x0008}, + {0x01, 0x002d, 0x0009}, + {0x01, 0x0005, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffe0, 0x000c}, + {0x01, 0xfffd, 0x000d}, + {0x01, 0xfff4, 0x000e}, + {0x01, 0xffe4, 0x000f}, + {0x01, 0x0028, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x00c8, 0x0015}, /* c8 Poids fort Luma */ + {0x01, 0x0032, 0x0016}, /* 32 */ + {0x01, 0x0016, 0x0011}, /* R 00 */ + {0x01, 0x0016, 0x0012}, /* G 00 */ + {0x01, 0x0016, 0x0013}, /* B 00 */ + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {} +}; + +static int reg_write(struct usb_device *dev, + __u16 req, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x 0x%02x 0x%02x", + req, index, value); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct gspca_dev *gspca_dev, + __u16 req, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + + gspca_dev->usb_buf[1] = 0; + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + gspca_dev->usb_buf, length, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_read err %d", ret); + return -1; + } + return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; +} + +static int write_vector(struct gspca_dev *gspca_dev, + const __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) { + PDEBUG(D_ERR, + "Reg write failed for 0x%02x,0x%02x,0x%02x", + data[i][0], data[i][1], data[i][2]); + return ret; + } + i++; + } + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->brightness); + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness); + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->brightness); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u16 brightness; + + brightness = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x11, 2); + sd->brightness = brightness << 1; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, 0x00, 0x00, + (sd->contrast >> 8) & 0xff); + reg_write(gspca_dev->dev, 0x00, 0x01, + sd->contrast & 0xff); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ +/* spca50x->contrast = 0xaa01; */ +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, sd->colors); +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x0c, 2); +/* sd->hue = (reg_read(gspca_dev, SPCA501_REG_CCDSP, 0x13, */ +/* 2) & 0xFF) << 8; */ +} + +/* 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; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x0000: /* Unknow Camera */ +/* switch (product) { */ +/* case 0x0000: */ + sd->subtype = MystFromOriUnknownCamera; +/* break; */ +/* } */ + break; + case 0x040a: /* Kodak cameras */ +/* switch (product) { */ +/* case 0x0002: */ + sd->subtype = KodakDVC325; +/* break; */ +/* } */ + break; + case 0x0497: /* Smile International */ +/* switch (product) { */ +/* case 0xc001: */ + sd->subtype = SmileIntlCamera; +/* break; */ +/* } */ + break; + case 0x0506: /* 3COM cameras */ +/* switch (product) { */ +/* case 0x00df: */ + sd->subtype = ThreeComHomeConnectLite; +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ + switch (product) { + case 0x0401: + sd->subtype = IntelCreateAndShare; + break; + case 0x0402: + sd->subtype = ViewQuestM318B; + break; + } + break; + case 0x1776: /* Arowana */ +/* switch (product) { */ +/* case 0x501c: */ + sd->subtype = Arowana300KCMOSCamera; +/* break; */ +/* } */ + break; + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value; + + switch (sd->subtype) { + case Arowana300KCMOSCamera: + case SmileIntlCamera: + /* Arowana 300k CMOS Camera data */ + if (write_vector(gspca_dev, spca501c_arowana_init_data)) + goto error; + break; + case MystFromOriUnknownCamera: + /* UnKnow Ori CMOS Camera data */ + if (write_vector(gspca_dev, spca501c_mysterious_open_data)) + goto error; + break; + default: + /* generic spca501 init data */ + if (write_vector(gspca_dev, spca501_init_data)) + goto error; + break; + } + return 0; +error: + return -EINVAL; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->subtype) { + case ThreeComHomeConnectLite: + /* Special handling for 3com data */ + write_vector(gspca_dev, spca501_3com_open_data); + break; + case Arowana300KCMOSCamera: + case SmileIntlCamera: + /* Arowana 300k CMOS Camera data */ + write_vector(gspca_dev, spca501c_arowana_open_data); + break; + case MystFromOriUnknownCamera: + /* UnKnow CMOS Camera data */ + write_vector(gspca_dev, spca501c_mysterious_init_data); + break; + default: + /* Generic 501 open data */ + write_vector(gspca_dev, spca501_open_data); + } + PDEBUG(D_STREAM, "Initializing SPCA501 finished"); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int mode; + + /* memorize the wanted pixel format */ + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + + /* Enable ISO packet machine CTRL reg=2, + * index=1 bitmask=0x2 (bit ordinal 1) */ + reg_write(dev, SPCA50X_REG_USB, 0x6, 0x94); + switch (mode) { + case 0: /* 640x480 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x004a); + break; + case 1: /* 320x240 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x104a); + break; + default: +/* case 2: * 160x120 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x204a); + break; + } + reg_write(dev, SPCA501_REG_CTLRL, 0x01, 0x02); + + /* HDG atleast the Intel CreateAndShare needs to have one of its + * brightness / contrast / color set otherwise it assumes what seems + * max contrast. Note that strange enough setting any of these is + * enough to fix the max contrast problem, to be sure we set all 3 */ + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setcolors(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Disable ISO packet + * machine CTRL reg=2, index=1 bitmask=0x0 (bit ordinal 1) */ + reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x01, 0x00); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x05, 0x00); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + switch (data[0]) { + case 0: /* start of frame */ + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + data, 0); + data += SPCA501_OFFSET_DATA; + len -= SPCA501_OFFSET_DATA; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data++; + len--; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, len); +} + +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; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x040a, 0x0002), DVNM("Kodak DVC-325")}, + {USB_DEVICE(0x0497, 0xc001), DVNM("Smile International")}, + {USB_DEVICE(0x0506, 0x00df), DVNM("3Com HomeConnect Lite")}, + {USB_DEVICE(0x0733, 0x0401), DVNM("Intel Create and Share")}, + {USB_DEVICE(0x0733, 0x0402), DVNM("ViewQuest M318B")}, + {USB_DEVICE(0x1776, 0x501c), DVNM("Arowana 300K CMOS Camera")}, + {USB_DEVICE(0x0000, 0x0000), DVNM("MystFromOri Unknow Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c new file mode 100644 index 00000000000..ddea6e140aa --- /dev/null +++ b/drivers/media/video/gspca/spca505.c @@ -0,0 +1,951 @@ +/* + * SPCA505 chip based cameras initialization data + * + * V4L2 by Jean-Francis Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MODULE_NAME "spca505" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA/SPCA505 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; + unsigned char tmpbuf[640 * 480 * 3 / 2]; /* YYUV per line */ + unsigned char tmpbuf2[640 * 480 * 2]; /* YUYV */ + + unsigned char brightness; + + char subtype; +#define IntelPCCameraPro 0 +#define Nxultra 1 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 160 * 2, + .sizeimage = 160 * 120 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 5}, + {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 176 * 2, + .sizeimage = 176 * 144 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 4}, + {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 352 * 2, + .sizeimage = 352 * 288 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +#define SPCA50X_OFFSET_DATA 10 + +#define SPCA50X_REG_USB 0x02 /* spca505 501 */ + +#define SPCA50X_USB_CTRL 0x00 /* spca505 */ +#define SPCA50X_CUSB_ENABLE 0x01 /* spca505 */ +#define SPCA50X_REG_GLOBAL 0x03 /* spca505 */ +#define SPCA50X_GMISC0_IDSEL 0x01 /* Global control device ID select spca505 */ +#define SPCA50X_GLOBAL_MISC0 0x00 /* Global control miscellaneous 0 spca505 */ + +#define SPCA50X_GLOBAL_MISC1 0x01 /* 505 */ +#define SPCA50X_GLOBAL_MISC3 0x03 /* 505 */ +#define SPCA50X_GMISC3_SAA7113RST 0x20 /* Not sure about this one spca505 */ + +/* + * Data to initialize a SPCA505. Common to the CCD and external modes + */ +static const __u16 spca505_init_data[][3] = { + /* line bmRequest,value,index */ + /* 1819 */ + {SPCA50X_REG_GLOBAL, SPCA50X_GMISC3_SAA7113RST, SPCA50X_GLOBAL_MISC3}, + /* Sensor reset */ + /* 1822 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3}, + /* 1825 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1}, + /* Block USB reset */ + /* 1828 */ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL, + SPCA50X_GLOBAL_MISC0}, + + /* 1831 */ {0x5, 0x01, 0x10}, + /* Maybe power down some stuff */ + /* 1834 */ {0x5, 0x0f, 0x11}, + + /* Setup internal CCD ? */ + /* 1837 */ {0x6, 0x10, 0x08}, + /* 1840 */ {0x6, 0x00, 0x09}, + /* 1843 */ {0x6, 0x00, 0x0a}, + /* 1846 */ {0x6, 0x00, 0x0b}, + /* 1849 */ {0x6, 0x10, 0x0c}, + /* 1852 */ {0x6, 0x00, 0x0d}, + /* 1855 */ {0x6, 0x00, 0x0e}, + /* 1858 */ {0x6, 0x00, 0x0f}, + /* 1861 */ {0x6, 0x10, 0x10}, + /* 1864 */ {0x6, 0x02, 0x11}, + /* 1867 */ {0x6, 0x00, 0x12}, + /* 1870 */ {0x6, 0x04, 0x13}, + /* 1873 */ {0x6, 0x02, 0x14}, + /* 1876 */ {0x6, 0x8a, 0x51}, + /* 1879 */ {0x6, 0x40, 0x52}, + /* 1882 */ {0x6, 0xb6, 0x53}, + /* 1885 */ {0x6, 0x3d, 0x54}, + {} +}; + +/* + * Data to initialize the camera using the internal CCD + */ +static const __u16 spca505_open_data_ccd[][3] = { + /* line bmRequest,value,index */ + /* Internal CCD data set */ + /* 1891 */ {0x3, 0x04, 0x01}, + /* This could be a reset */ + /* 1894 */ {0x3, 0x00, 0x01}, + + /* Setup compression and image registers. 0x6 and 0x7 seem to be + related to H&V hold, and are resolution mode specific */ + /* 1897 */ {0x4, 0x10, 0x01}, + /* DIFF(0x50), was (0x10) */ + /* 1900 */ {0x4, 0x00, 0x04}, + /* 1903 */ {0x4, 0x00, 0x05}, + /* 1906 */ {0x4, 0x20, 0x06}, + /* 1909 */ {0x4, 0x20, 0x07}, + + /* 1912 */ {0x8, 0x0a, 0x00}, + /* DIFF (0x4a), was (0xa) */ + + /* 1915 */ {0x5, 0x00, 0x10}, + /* 1918 */ {0x5, 0x00, 0x11}, + /* 1921 */ {0x5, 0x00, 0x00}, + /* DIFF not written */ + /* 1924 */ {0x5, 0x00, 0x01}, + /* DIFF not written */ + /* 1927 */ {0x5, 0x00, 0x02}, + /* DIFF not written */ + /* 1930 */ {0x5, 0x00, 0x03}, + /* DIFF not written */ + /* 1933 */ {0x5, 0x00, 0x04}, + /* DIFF not written */ + /* 1936 */ {0x5, 0x80, 0x05}, + /* DIFF not written */ + /* 1939 */ {0x5, 0xe0, 0x06}, + /* DIFF not written */ + /* 1942 */ {0x5, 0x20, 0x07}, + /* DIFF not written */ + /* 1945 */ {0x5, 0xa0, 0x08}, + /* DIFF not written */ + /* 1948 */ {0x5, 0x0, 0x12}, + /* DIFF not written */ + /* 1951 */ {0x5, 0x02, 0x0f}, + /* DIFF not written */ + /* 1954 */ {0x5, 0x10, 0x46}, + /* DIFF not written */ + /* 1957 */ {0x5, 0x8, 0x4a}, + /* DIFF not written */ + + /* 1960 */ {0x3, 0x08, 0x03}, + /* DIFF (0x3,0x28,0x3) */ + /* 1963 */ {0x3, 0x08, 0x01}, + /* 1966 */ {0x3, 0x0c, 0x03}, + /* DIFF not written */ + /* 1969 */ {0x3, 0x21, 0x00}, + /* DIFF (0x39) */ + +/* Extra block copied from init to hopefully ensure CCD is in a sane state */ + /* 1837 */ {0x6, 0x10, 0x08}, + /* 1840 */ {0x6, 0x00, 0x09}, + /* 1843 */ {0x6, 0x00, 0x0a}, + /* 1846 */ {0x6, 0x00, 0x0b}, + /* 1849 */ {0x6, 0x10, 0x0c}, + /* 1852 */ {0x6, 0x00, 0x0d}, + /* 1855 */ {0x6, 0x00, 0x0e}, + /* 1858 */ {0x6, 0x00, 0x0f}, + /* 1861 */ {0x6, 0x10, 0x10}, + /* 1864 */ {0x6, 0x02, 0x11}, + /* 1867 */ {0x6, 0x00, 0x12}, + /* 1870 */ {0x6, 0x04, 0x13}, + /* 1873 */ {0x6, 0x02, 0x14}, + /* 1876 */ {0x6, 0x8a, 0x51}, + /* 1879 */ {0x6, 0x40, 0x52}, + /* 1882 */ {0x6, 0xb6, 0x53}, + /* 1885 */ {0x6, 0x3d, 0x54}, + /* End of extra block */ + + /* 1972 */ {0x6, 0x3f, 0x1}, + /* Block skipped */ + /* 1975 */ {0x6, 0x10, 0x02}, + /* 1978 */ {0x6, 0x64, 0x07}, + /* 1981 */ {0x6, 0x10, 0x08}, + /* 1984 */ {0x6, 0x00, 0x09}, + /* 1987 */ {0x6, 0x00, 0x0a}, + /* 1990 */ {0x6, 0x00, 0x0b}, + /* 1993 */ {0x6, 0x10, 0x0c}, + /* 1996 */ {0x6, 0x00, 0x0d}, + /* 1999 */ {0x6, 0x00, 0x0e}, + /* 2002 */ {0x6, 0x00, 0x0f}, + /* 2005 */ {0x6, 0x10, 0x10}, + /* 2008 */ {0x6, 0x02, 0x11}, + /* 2011 */ {0x6, 0x00, 0x12}, + /* 2014 */ {0x6, 0x04, 0x13}, + /* 2017 */ {0x6, 0x02, 0x14}, + /* 2020 */ {0x6, 0x8a, 0x51}, + /* 2023 */ {0x6, 0x40, 0x52}, + /* 2026 */ {0x6, 0xb6, 0x53}, + /* 2029 */ {0x6, 0x3d, 0x54}, + /* 2032 */ {0x6, 0x60, 0x57}, + /* 2035 */ {0x6, 0x20, 0x58}, + /* 2038 */ {0x6, 0x15, 0x59}, + /* 2041 */ {0x6, 0x05, 0x5a}, + + /* 2044 */ {0x5, 0x01, 0xc0}, + /* 2047 */ {0x5, 0x10, 0xcb}, + /* 2050 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2053 */ {0x5, 0x0, 0xc2}, + /* 4 was 0 */ + /* 2056 */ {0x5, 0x00, 0xca}, + /* 2059 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2062 */ {0x5, 0x04, 0xc2}, + /* 2065 */ {0x5, 0x00, 0xca}, + /* 2068 */ {0x5, 0x0, 0xc1}, + /* */ + /* 2071 */ {0x5, 0x00, 0xc2}, + /* 2074 */ {0x5, 0x00, 0xca}, + /* 2077 */ {0x5, 0x40, 0xc1}, + /* */ + /* 2080 */ {0x5, 0x17, 0xc2}, + /* 2083 */ {0x5, 0x00, 0xca}, + /* 2086 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2089 */ {0x5, 0x06, 0xc2}, + /* 2092 */ {0x5, 0x00, 0xca}, + /* 2095 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2098 */ {0x5, 0x04, 0xc2}, + /* 2101 */ {0x5, 0x00, 0xca}, + + /* 2104 */ {0x3, 0x4c, 0x3}, + /* 2107 */ {0x3, 0x18, 0x1}, + + /* 2110 */ {0x6, 0x70, 0x51}, + /* 2113 */ {0x6, 0xbe, 0x53}, + /* 2116 */ {0x6, 0x71, 0x57}, + /* 2119 */ {0x6, 0x20, 0x58}, + /* 2122 */ {0x6, 0x05, 0x59}, + /* 2125 */ {0x6, 0x15, 0x5a}, + + /* 2128 */ {0x4, 0x00, 0x08}, + /* Compress = OFF (0x1 to turn on) */ + /* 2131 */ {0x4, 0x12, 0x09}, + /* 2134 */ {0x4, 0x21, 0x0a}, + /* 2137 */ {0x4, 0x10, 0x0b}, + /* 2140 */ {0x4, 0x21, 0x0c}, + /* 2143 */ {0x4, 0x05, 0x00}, + /* was 5 (Image Type ? ) */ + /* 2146 */ {0x4, 0x00, 0x01}, + + /* 2149 */ {0x6, 0x3f, 0x01}, + + /* 2152 */ {0x4, 0x00, 0x04}, + /* 2155 */ {0x4, 0x00, 0x05}, + /* 2158 */ {0x4, 0x40, 0x06}, + /* 2161 */ {0x4, 0x40, 0x07}, + + /* 2164 */ {0x6, 0x1c, 0x17}, + /* 2167 */ {0x6, 0xe2, 0x19}, + /* 2170 */ {0x6, 0x1c, 0x1b}, + /* 2173 */ {0x6, 0xe2, 0x1d}, + /* 2176 */ {0x6, 0xaa, 0x1f}, + /* 2179 */ {0x6, 0x70, 0x20}, + + /* 2182 */ {0x5, 0x01, 0x10}, + /* 2185 */ {0x5, 0x00, 0x11}, + /* 2188 */ {0x5, 0x01, 0x00}, + /* 2191 */ {0x5, 0x05, 0x01}, + /* 2194 */ {0x5, 0x00, 0xc1}, + /* */ + /* 2197 */ {0x5, 0x00, 0xc2}, + /* 2200 */ {0x5, 0x00, 0xca}, + + /* 2203 */ {0x6, 0x70, 0x51}, + /* 2206 */ {0x6, 0xbe, 0x53}, + {} +}; + +/* + Made by Tomasz Zablocki (skalamandra@poczta.onet.pl) + * SPCA505b chip based cameras initialization data + * + */ +/* jfm */ +#define initial_brightness 0x7f /* 0x0(white)-0xff(black) */ +/* #define initial_brightness 0x0 //0x0(white)-0xff(black) */ +/* + * Data to initialize a SPCA505. Common to the CCD and external modes + */ +static const __u16 spca505b_init_data[][3] = { +/* start */ + {0x02, 0x00, 0x00}, /* init */ + {0x02, 0x00, 0x01}, + {0x02, 0x00, 0x02}, + {0x02, 0x00, 0x03}, + {0x02, 0x00, 0x04}, + {0x02, 0x00, 0x05}, + {0x02, 0x00, 0x06}, + {0x02, 0x00, 0x07}, + {0x02, 0x00, 0x08}, + {0x02, 0x00, 0x09}, + {0x03, 0x00, 0x00}, + {0x03, 0x00, 0x01}, + {0x03, 0x00, 0x02}, + {0x03, 0x00, 0x03}, + {0x03, 0x00, 0x04}, + {0x03, 0x00, 0x05}, + {0x03, 0x00, 0x06}, + {0x04, 0x00, 0x00}, + {0x04, 0x00, 0x02}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x00, 0x06}, + {0x04, 0x00, 0x07}, + {0x04, 0x00, 0x08}, + {0x04, 0x00, 0x09}, + {0x04, 0x00, 0x0a}, + {0x04, 0x00, 0x0b}, + {0x04, 0x00, 0x0c}, + {0x07, 0x00, 0x00}, + {0x07, 0x00, 0x03}, + {0x08, 0x00, 0x00}, + {0x08, 0x00, 0x01}, + {0x08, 0x00, 0x02}, + {0x00, 0x01, 0x00}, + {0x00, 0x01, 0x01}, + {0x00, 0x01, 0x34}, + {0x00, 0x01, 0x35}, + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x06, 0x18, 0x0c}, + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x18, 0x10}, + {0x06, 0xfe, 0x12}, + {0x06, 0x00, 0x11}, + {0x06, 0x00, 0x14}, + {0x06, 0x00, 0x13}, + {0x06, 0x28, 0x51}, + {0x06, 0xff, 0x53}, + {0x02, 0x00, 0x08}, + + {0x03, 0x00, 0x03}, + {0x03, 0x10, 0x03}, + {} +}; + +/* + * Data to initialize the camera using the internal CCD + */ +static const __u16 spca505b_open_data_ccd[][3] = { + +/* {0x02,0x00,0x00}, */ + {0x03, 0x04, 0x01}, /* rst */ + {0x03, 0x00, 0x01}, + {0x03, 0x00, 0x00}, + {0x03, 0x21, 0x00}, + {0x03, 0x00, 0x04}, + {0x03, 0x00, 0x03}, + {0x03, 0x18, 0x03}, + {0x03, 0x08, 0x01}, + {0x03, 0x1c, 0x03}, + {0x03, 0x5c, 0x03}, + {0x03, 0x5c, 0x03}, + {0x03, 0x18, 0x01}, + +/* same as 505 */ + {0x04, 0x10, 0x01}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x20, 0x06}, + {0x04, 0x20, 0x07}, + + {0x08, 0x0a, 0x00}, + + {0x05, 0x00, 0x10}, + {0x05, 0x00, 0x11}, + {0x05, 0x00, 0x12}, + {0x05, 0x6f, 0x00}, + {0x05, initial_brightness >> 6, 0x00}, + {0x05, initial_brightness << 2, 0x01}, + {0x05, 0x00, 0x02}, + {0x05, 0x01, 0x03}, + {0x05, 0x00, 0x04}, + {0x05, 0x03, 0x05}, + {0x05, 0xe0, 0x06}, + {0x05, 0x20, 0x07}, + {0x05, 0xa0, 0x08}, + {0x05, 0x00, 0x12}, + {0x05, 0x02, 0x0f}, + {0x05, 128, 0x14}, /* max exposure off (0=on) */ + {0x05, 0x01, 0xb0}, + {0x05, 0x01, 0xbf}, + {0x03, 0x02, 0x06}, + {0x05, 0x10, 0x46}, + {0x05, 0x08, 0x4a}, + + {0x06, 0x00, 0x01}, + {0x06, 0x10, 0x02}, + {0x06, 0x64, 0x07}, + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x04, 0x00, 0x01}, + {0x06, 0x18, 0x0c}, + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x11, 0x10}, /* contrast */ + {0x06, 0x00, 0x11}, + {0x06, 0xfe, 0x12}, + {0x06, 0x00, 0x13}, + {0x06, 0x00, 0x14}, + {0x06, 0x9d, 0x51}, + {0x06, 0x40, 0x52}, + {0x06, 0x7c, 0x53}, + {0x06, 0x40, 0x54}, + {0x06, 0x02, 0x57}, + {0x06, 0x03, 0x58}, + {0x06, 0x15, 0x59}, + {0x06, 0x05, 0x5a}, + {0x06, 0x03, 0x56}, + {0x06, 0x02, 0x3f}, + {0x06, 0x00, 0x40}, + {0x06, 0x39, 0x41}, + {0x06, 0x69, 0x42}, + {0x06, 0x87, 0x43}, + {0x06, 0x9e, 0x44}, + {0x06, 0xb1, 0x45}, + {0x06, 0xbf, 0x46}, + {0x06, 0xcc, 0x47}, + {0x06, 0xd5, 0x48}, + {0x06, 0xdd, 0x49}, + {0x06, 0xe3, 0x4a}, + {0x06, 0xe8, 0x4b}, + {0x06, 0xed, 0x4c}, + {0x06, 0xf2, 0x4d}, + {0x06, 0xf7, 0x4e}, + {0x06, 0xfc, 0x4f}, + {0x06, 0xff, 0x50}, + + {0x05, 0x01, 0xc0}, + {0x05, 0x10, 0xcb}, + {0x05, 0x40, 0xc1}, + {0x05, 0x04, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x40, 0xc1}, + {0x05, 0x09, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0xc0, 0xc1}, + {0x05, 0x09, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x40, 0xc1}, + {0x05, 0x59, 0xc2}, + {0x05, 0x00, 0xca}, + {0x04, 0x00, 0x01}, + {0x05, 0x80, 0xc1}, + {0x05, 0xec, 0xc2}, + {0x05, 0x0, 0xca}, + + {0x06, 0x02, 0x57}, + {0x06, 0x01, 0x58}, + {0x06, 0x15, 0x59}, + {0x06, 0x0a, 0x5a}, + {0x06, 0x01, 0x57}, + {0x06, 0x8a, 0x03}, + {0x06, 0x0a, 0x6c}, + {0x06, 0x30, 0x01}, + {0x06, 0x20, 0x02}, + {0x06, 0x00, 0x03}, + + {0x05, 0x8c, 0x25}, + + {0x06, 0x4d, 0x51}, /* maybe saturation (4d) */ + {0x06, 0x84, 0x53}, /* making green (84) */ + {0x06, 0x00, 0x57}, /* sharpness (1) */ + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x06, 0x18, 0x0c}, /* maybe hue (18) */ + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x18, 0x10}, /* maybe contrast (18) */ + + {0x05, 0x01, 0x02}, + + {0x04, 0x00, 0x08}, /* compression */ + {0x04, 0x12, 0x09}, + {0x04, 0x21, 0x0a}, + {0x04, 0x10, 0x0b}, + {0x04, 0x21, 0x0c}, + {0x04, 0x1d, 0x00}, /* imagetype (1d) */ + {0x04, 0x41, 0x01}, /* hardware snapcontrol */ + + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x10, 0x06}, + {0x04, 0x10, 0x07}, + {0x04, 0x40, 0x06}, + {0x04, 0x40, 0x07}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + + {0x06, 0x1c, 0x17}, + {0x06, 0xe2, 0x19}, + {0x06, 0x1c, 0x1b}, + {0x06, 0xe2, 0x1d}, + {0x06, 0x5f, 0x1f}, + {0x06, 0x32, 0x20}, + + {0x05, initial_brightness >> 6, 0x00}, + {0x05, initial_brightness << 2, 0x01}, + {0x05, 0x06, 0xc1}, + {0x05, 0x58, 0xc2}, + {0x05, 0x0, 0xca}, + {0x05, 0x0, 0x11}, + {} +}; + +static int reg_write(struct usb_device *dev, + __u16 reg, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + reg, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_PACK, "reg write: 0x%02x,0x%02x:0x%02x, 0x%x", + reg, index, value, ret); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct gspca_dev *gspca_dev, + __u16 reg, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + + gspca_dev->usb_buf[1] = 0; + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + reg, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (__u16) 0, /* value */ + (__u16) index, + gspca_dev->usb_buf, length, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_read err %d", ret); + return -1; + } + return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; +} + +static int write_vector(struct gspca_dev *gspca_dev, + const __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + 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]); + return ret; + } + i++; + } + return 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; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x401d: * here505b */ + sd->subtype = Nxultra; +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ +/* switch (product) { */ +/* case 0x0430: */ +/* fixme: may be UsbGrabberPV321 BRIDGE_SPCA506 SENSOR_SAA7113 */ + sd->subtype = IntelPCCameraPro; +/* break; */ +/* } */ + break; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + if (sd->subtype != IntelPCCameraPro) + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + else /* no 640x480 for IntelPCCameraPro */ + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0] - 1; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + + if (sd->subtype == Nxultra) { + if (write_vector(gspca_dev, spca505b_init_data)) + return -EIO; + } else { + if (write_vector(gspca_dev, spca505_init_data)) + return -EIO; + } + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + PDEBUG(D_STREAM, "Initializing SPCA505"); + if (sd->subtype == Nxultra) + write_vector(gspca_dev, spca505b_open_data_ccd); + else + write_vector(gspca_dev, spca505_open_data_ccd); + ret = reg_read(gspca_dev, 6, 0x16, 2); + + if (ret < 0) { + PDEBUG(D_ERR|D_STREAM, + "register read failed for after vector read err = %d", + ret); + return -EIO; + } + PDEBUG(D_STREAM, + "After vector read returns : 0x%x should be 0x0101", + ret & 0xffff); + + ret = reg_write(gspca_dev->dev, 6, 0x16, 0x0a); + if (ret < 0) { + PDEBUG(D_ERR, "register write failed for (6,0xa,0x16) err=%d", + ret); + return -EIO; + } + reg_write(gspca_dev->dev, 5, 0xc2, 18); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + /* necessary because without it we can see stream + * only once after loading module */ + /* stopping usb registers Tomasz change */ + reg_write(dev, 0x02, 0x0, 0x0); + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + case 0: + reg_write(dev, 0x04, 0x00, 0x00); + reg_write(dev, 0x04, 0x06, 0x10); + reg_write(dev, 0x04, 0x07, 0x10); + break; + case 1: + reg_write(dev, 0x04, 0x00, 0x01); + reg_write(dev, 0x04, 0x06, 0x1a); + reg_write(dev, 0x04, 0x07, 0x1a); + break; + case 2: + reg_write(dev, 0x04, 0x00, 0x02); + reg_write(dev, 0x04, 0x06, 0x1c); + reg_write(dev, 0x04, 0x07, 0x1d); + break; + case 4: + reg_write(dev, 0x04, 0x00, 0x04); + reg_write(dev, 0x04, 0x06, 0x34); + reg_write(dev, 0x04, 0x07, 0x34); + break; + default: +/* case 5: */ + reg_write(dev, 0x04, 0x00, 0x05); + reg_write(dev, 0x04, 0x06, 0x40); + reg_write(dev, 0x04, 0x07, 0x40); + break; + } +/* Enable ISO packet machine - should we do this here or in ISOC init ? */ + ret = reg_write(dev, SPCA50X_REG_USB, + SPCA50X_USB_CTRL, + SPCA50X_CUSB_ENABLE); + +/* reg_write(dev, 0x5, 0x0, 0x0); */ +/* reg_write(dev, 0x5, 0x0, 0x1); */ +/* reg_write(dev, 0x5, 0x11, 0x2); */ +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Disable ISO packet machine */ + reg_write(gspca_dev->dev, 0x02, 0x00, 0x00); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + /* This maybe reset or power control */ + reg_write(gspca_dev->dev, 0x03, 0x03, 0x20); + reg_write(gspca_dev->dev, 0x03, 0x01, 0x0); + reg_write(gspca_dev->dev, 0x03, 0x00, 0x1); + reg_write(gspca_dev->dev, 0x05, 0x10, 0x1); + reg_write(gspca_dev->dev, 0x05, 0x11, 0xf); +} + +/* convert YYUV per line to YUYV (YUV 4:2:2) */ +static void yyuv_decode(unsigned char *out, + unsigned char *in, + int width, + int height) +{ + unsigned char *Ui, *Vi, *yi, *yi1; + unsigned char *out1; + int i, j; + + yi = in; + for (i = height / 2; --i >= 0; ) { + out1 = out + width * 2; /* next line */ + yi1 = yi + width; + Ui = yi1 + width; + Vi = Ui + width / 2; + for (j = width / 2; --j >= 0; ) { + *out++ = 128 + *yi++; + *out++ = 128 + *Ui; + *out++ = 128 + *yi++; + *out++ = 128 + *Vi; + + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Ui++; + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Vi++; + } + yi += width * 2; + out = out1; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + yyuv_decode(sd->tmpbuf2, sd->tmpbuf, + gspca_dev->width, + gspca_dev->height); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + sd->tmpbuf2, + gspca_dev->width + * gspca_dev->height + * 2); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, 0); + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + if (len > 0) + memcpy(sd->tmpbuf, data, len); + else + len = 0; + sd->buflen = len; + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data += 1; + len -= 1; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + __u8 brightness = sd->brightness; + reg_write(gspca_dev->dev, 5, 0x00, (255 - brightness) >> 6); + reg_write(gspca_dev->dev, 5, 0x01, (255 - brightness) << 2); + +} +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = 255 + - ((reg_read(gspca_dev, 5, 0x01, 1) >> 2) + + (reg_read(gspca_dev, 5, 0x0, 1) << 6)); +} + +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; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x401d), DVNM("Creative Webcam NX ULTRA")}, + {USB_DEVICE(0x0733, 0x0430), DVNM("Intel PC Camera Pro")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c new file mode 100644 index 00000000000..143203c1fd9 --- /dev/null +++ b/drivers/media/video/gspca/spca506.c @@ -0,0 +1,847 @@ +/* + * SPCA506 chip based cameras function + * M Xhaard 15/04/2004 based on different work Mark Taylor and others + * and my own snoopy file on a pv-321c donate by a german compagny + * "Firma Frank Gmbh" from Saarbruecken + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "spca506" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; + __u8 tmpbuf[640 * 480 * 3]; /* YYUV per line */ + __u8 tmpbuf2[640 * 480 * 2]; /* YUYV */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char hue; + char norme; + char channel; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethue(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, + .step = 1, + .default_value = 0x80, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x47, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x40, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_HUE 3 + { + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0, + }, + .set = sd_sethue, + .get = sd_gethue, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 160 * 2, + .sizeimage = 160 * 120 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 5}, + {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 176 * 2, + .sizeimage = 176 * 144 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 4}, + {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 352 * 2, + .sizeimage = 352 * 288 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +#define SPCA50X_OFFSET_DATA 10 + +#define SAA7113_bright 0x0a /* defaults 0x80 */ +#define SAA7113_contrast 0x0b /* defaults 0x47 */ +#define SAA7113_saturation 0x0c /* defaults 0x40 */ +#define SAA7113_hue 0x0d /* defaults 0x00 */ +#define SAA7113_I2C_BASE_WRITE 0x4a + +/* read 'len' bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 req, + __u16 index, + __u16 length) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, length, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + NULL, 0, 500); +} + +static void spca506_Initi2c(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004); +} + +static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur, + __u16 reg) +{ + int retry = 60; + + reg_w(gspca_dev->dev, 0x07, reg, 0x0001); + reg_w(gspca_dev->dev, 0x07, valeur, 0x0000); + while (retry--) { + reg_r(gspca_dev, 0x07, 0x0003, 2); + if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00) + break; + } +} + +static int spca506_ReadI2c(struct gspca_dev *gspca_dev, __u16 reg) +{ + int retry = 60; + + reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004); + reg_w(gspca_dev->dev, 0x07, reg, 0x0001); + reg_w(gspca_dev->dev, 0x07, 0x01, 0x0002); + while (--retry) { + reg_r(gspca_dev, 0x07, 0x0003, 2); + if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00) + break; + } + if (retry == 0) + return -1; + reg_r(gspca_dev, 0x07, 0x0000, 1); + return gspca_dev->usb_buf[0]; +} + +static void spca506_SetNormeInput(struct gspca_dev *gspca_dev, + __u16 norme, + __u16 channel) +{ + struct sd *sd = (struct sd *) gspca_dev; +/* fixme: check if channel == 0..3 and 6..9 (8 values) */ + __u8 setbit0 = 0x00; + __u8 setbit1 = 0x00; + __u8 videomask = 0x00; + + PDEBUG(D_STREAM, "** Open Set Norme **"); + spca506_Initi2c(gspca_dev); + /* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */ + /* Composite channel bit1 -> 1 S-video bit 1 -> 0 */ + /* and exclude SAA7113 reserved channel set default 0 otherwise */ + if (norme & V4L2_STD_NTSC) + setbit0 = 0x01; + if (channel == 4 || channel == 5 || channel > 9) + channel = 0; + if (channel < 4) + setbit1 = 0x02; + videomask = (0x48 | setbit0 | setbit1); + reg_w(gspca_dev->dev, 0x08, videomask, 0x0000); + spca506_WriteI2c(gspca_dev, (0xc0 | (channel & 0x0F)), 0x02); + + if (norme & V4L2_STD_NTSC) + spca506_WriteI2c(gspca_dev, 0x33, 0x0e); + /* Chrominance Control NTSC N */ + else if (norme & V4L2_STD_SECAM) + spca506_WriteI2c(gspca_dev, 0x53, 0x0e); + /* Chrominance Control SECAM */ + else + spca506_WriteI2c(gspca_dev, 0x03, 0x0e); + /* Chrominance Control PAL BGHIV */ + + sd->norme = norme; + sd->channel = channel; + PDEBUG(D_STREAM, "Set Video Byte to 0x%2x", videomask); + PDEBUG(D_STREAM, "Set Norme: %08x Channel %d", norme, channel); +} + +static void spca506_GetNormeInput(struct gspca_dev *gspca_dev, + __u16 *norme, __u16 *channel) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Read the register is not so good value change so + we use your own copy in spca50x struct */ + *norme = sd->norme; + *channel = sd->channel; + PDEBUG(D_STREAM, "Get Norme: %d Channel %d", *norme, *channel); +} + +static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code, + __u16 xmult, __u16 ymult) +{ + struct usb_device *dev = gspca_dev->dev; + + PDEBUG(D_STREAM, "** SetSize **"); + reg_w(dev, 0x04, (0x18 | (code & 0x07)), 0x0000); + /* Soft snap 0x40 Hard 0x41 */ + reg_w(dev, 0x04, 0x41, 0x0001); + reg_w(dev, 0x04, 0x00, 0x0002); + /* reserved */ + reg_w(dev, 0x04, 0x00, 0x0003); + + /* reserved */ + reg_w(dev, 0x04, 0x00, 0x0004); + /* reserved */ + reg_w(dev, 0x04, 0x01, 0x0005); + /* reserced */ + reg_w(dev, 0x04, xmult, 0x0006); + /* reserved */ + reg_w(dev, 0x04, ymult, 0x0007); + /* compression 1 */ + reg_w(dev, 0x04, 0x00, 0x0008); + /* T=64 -> 2 */ + reg_w(dev, 0x04, 0x00, 0x0009); + /* threshold2D */ + reg_w(dev, 0x04, 0x21, 0x000a); + /* quantization */ + reg_w(dev, 0x04, 0x00, 0x000b); +} + +/* 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; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + 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->hue = sd_ctrls[SD_HUE].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0000); + reg_w(dev, 0x03, 0x1c, 0x0001); + reg_w(dev, 0x03, 0x18, 0x0001); + /* Init on PAL and composite input0 */ + spca506_SetNormeInput(gspca_dev, 0, 0); + reg_w(dev, 0x03, 0x1c, 0x0001); + reg_w(dev, 0x03, 0x18, 0x0001); + reg_w(dev, 0x05, 0x00, 0x0000); + reg_w(dev, 0x05, 0xef, 0x0001); + reg_w(dev, 0x05, 0x00, 0x00c1); + reg_w(dev, 0x05, 0x00, 0x00c2); + reg_w(dev, 0x06, 0x18, 0x0002); + reg_w(dev, 0x06, 0xf5, 0x0011); + reg_w(dev, 0x06, 0x02, 0x0012); + reg_w(dev, 0x06, 0xfb, 0x0013); + reg_w(dev, 0x06, 0x00, 0x0014); + reg_w(dev, 0x06, 0xa4, 0x0051); + reg_w(dev, 0x06, 0x40, 0x0052); + reg_w(dev, 0x06, 0x71, 0x0053); + reg_w(dev, 0x06, 0x40, 0x0054); + /************************************************/ + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x60, 0x0000); + reg_w(dev, 0x03, 0x18, 0x0001); + /* for a better reading mx :) */ + /*sdca506_WriteI2c(value,register) */ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, 0x08, 0x01); + spca506_WriteI2c(gspca_dev, 0xc0, 0x02); + /* input composite video */ + spca506_WriteI2c(gspca_dev, 0x33, 0x03); + spca506_WriteI2c(gspca_dev, 0x00, 0x04); + spca506_WriteI2c(gspca_dev, 0x00, 0x05); + spca506_WriteI2c(gspca_dev, 0x0d, 0x06); + spca506_WriteI2c(gspca_dev, 0xf0, 0x07); + spca506_WriteI2c(gspca_dev, 0x98, 0x08); + spca506_WriteI2c(gspca_dev, 0x03, 0x09); + spca506_WriteI2c(gspca_dev, 0x80, 0x0a); + spca506_WriteI2c(gspca_dev, 0x47, 0x0b); + spca506_WriteI2c(gspca_dev, 0x48, 0x0c); + spca506_WriteI2c(gspca_dev, 0x00, 0x0d); + spca506_WriteI2c(gspca_dev, 0x03, 0x0e); /* Chroma Pal adjust */ + spca506_WriteI2c(gspca_dev, 0x2a, 0x0f); + spca506_WriteI2c(gspca_dev, 0x00, 0x10); + spca506_WriteI2c(gspca_dev, 0x0c, 0x11); + spca506_WriteI2c(gspca_dev, 0xb8, 0x12); + spca506_WriteI2c(gspca_dev, 0x01, 0x13); + spca506_WriteI2c(gspca_dev, 0x00, 0x14); + spca506_WriteI2c(gspca_dev, 0x00, 0x15); + spca506_WriteI2c(gspca_dev, 0x00, 0x16); + spca506_WriteI2c(gspca_dev, 0x00, 0x17); + spca506_WriteI2c(gspca_dev, 0x00, 0x18); + spca506_WriteI2c(gspca_dev, 0x00, 0x19); + spca506_WriteI2c(gspca_dev, 0x00, 0x1a); + spca506_WriteI2c(gspca_dev, 0x00, 0x1b); + spca506_WriteI2c(gspca_dev, 0x00, 0x1c); + spca506_WriteI2c(gspca_dev, 0x00, 0x1d); + spca506_WriteI2c(gspca_dev, 0x00, 0x1e); + spca506_WriteI2c(gspca_dev, 0xa1, 0x1f); + spca506_WriteI2c(gspca_dev, 0x02, 0x40); + spca506_WriteI2c(gspca_dev, 0xff, 0x41); + spca506_WriteI2c(gspca_dev, 0xff, 0x42); + spca506_WriteI2c(gspca_dev, 0xff, 0x43); + spca506_WriteI2c(gspca_dev, 0xff, 0x44); + spca506_WriteI2c(gspca_dev, 0xff, 0x45); + spca506_WriteI2c(gspca_dev, 0xff, 0x46); + spca506_WriteI2c(gspca_dev, 0xff, 0x47); + spca506_WriteI2c(gspca_dev, 0xff, 0x48); + spca506_WriteI2c(gspca_dev, 0xff, 0x49); + spca506_WriteI2c(gspca_dev, 0xff, 0x4a); + spca506_WriteI2c(gspca_dev, 0xff, 0x4b); + spca506_WriteI2c(gspca_dev, 0xff, 0x4c); + spca506_WriteI2c(gspca_dev, 0xff, 0x4d); + spca506_WriteI2c(gspca_dev, 0xff, 0x4e); + spca506_WriteI2c(gspca_dev, 0xff, 0x4f); + spca506_WriteI2c(gspca_dev, 0xff, 0x50); + spca506_WriteI2c(gspca_dev, 0xff, 0x51); + spca506_WriteI2c(gspca_dev, 0xff, 0x52); + spca506_WriteI2c(gspca_dev, 0xff, 0x53); + spca506_WriteI2c(gspca_dev, 0xff, 0x54); + spca506_WriteI2c(gspca_dev, 0xff, 0x55); + spca506_WriteI2c(gspca_dev, 0xff, 0x56); + spca506_WriteI2c(gspca_dev, 0xff, 0x57); + spca506_WriteI2c(gspca_dev, 0x00, 0x58); + spca506_WriteI2c(gspca_dev, 0x54, 0x59); + spca506_WriteI2c(gspca_dev, 0x07, 0x5a); + spca506_WriteI2c(gspca_dev, 0x83, 0x5b); + spca506_WriteI2c(gspca_dev, 0x00, 0x5c); + spca506_WriteI2c(gspca_dev, 0x00, 0x5d); + spca506_WriteI2c(gspca_dev, 0x00, 0x5e); + spca506_WriteI2c(gspca_dev, 0x00, 0x5f); + spca506_WriteI2c(gspca_dev, 0x00, 0x60); + spca506_WriteI2c(gspca_dev, 0x05, 0x61); + spca506_WriteI2c(gspca_dev, 0x9f, 0x62); + PDEBUG(D_STREAM, "** Close Init *"); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u16 norme; + __u16 channel; + + /**************************************/ + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x60, 0x0000); + reg_w(dev, 0x03, 0x18, 0x0001); + + /*sdca506_WriteI2c(value,register) */ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, 0x08, 0x01); /* Increment Delay */ +/* spca506_WriteI2c(gspca_dev, 0xc0, 0x02); * Analog Input Control 1 */ + spca506_WriteI2c(gspca_dev, 0x33, 0x03); + /* Analog Input Control 2 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x04); + /* Analog Input Control 3 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x05); + /* Analog Input Control 4 */ + spca506_WriteI2c(gspca_dev, 0x0d, 0x06); + /* Horizontal Sync Start 0xe9-0x0d */ + spca506_WriteI2c(gspca_dev, 0xf0, 0x07); + /* Horizontal Sync Stop 0x0d-0xf0 */ + + spca506_WriteI2c(gspca_dev, 0x98, 0x08); /* Sync Control */ +/* Defaults value */ + spca506_WriteI2c(gspca_dev, 0x03, 0x09); /* Luminance Control */ + spca506_WriteI2c(gspca_dev, 0x80, 0x0a); + /* Luminance Brightness */ + spca506_WriteI2c(gspca_dev, 0x47, 0x0b); /* Luminance Contrast */ + spca506_WriteI2c(gspca_dev, 0x48, 0x0c); + /* Chrominance Saturation */ + spca506_WriteI2c(gspca_dev, 0x00, 0x0d); + /* Chrominance Hue Control */ + spca506_WriteI2c(gspca_dev, 0x2a, 0x0f); + /* Chrominance Gain Control */ + /**************************************/ + spca506_WriteI2c(gspca_dev, 0x00, 0x10); + /* Format/Delay Control */ + spca506_WriteI2c(gspca_dev, 0x0c, 0x11); /* Output Control 1 */ + spca506_WriteI2c(gspca_dev, 0xb8, 0x12); /* Output Control 2 */ + spca506_WriteI2c(gspca_dev, 0x01, 0x13); /* Output Control 3 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x14); /* reserved */ + spca506_WriteI2c(gspca_dev, 0x00, 0x15); /* VGATE START */ + spca506_WriteI2c(gspca_dev, 0x00, 0x16); /* VGATE STOP */ + spca506_WriteI2c(gspca_dev, 0x00, 0x17); /* VGATE Control (MSB) */ + spca506_WriteI2c(gspca_dev, 0x00, 0x18); + spca506_WriteI2c(gspca_dev, 0x00, 0x19); + spca506_WriteI2c(gspca_dev, 0x00, 0x1a); + spca506_WriteI2c(gspca_dev, 0x00, 0x1b); + spca506_WriteI2c(gspca_dev, 0x00, 0x1c); + spca506_WriteI2c(gspca_dev, 0x00, 0x1d); + spca506_WriteI2c(gspca_dev, 0x00, 0x1e); + spca506_WriteI2c(gspca_dev, 0xa1, 0x1f); + spca506_WriteI2c(gspca_dev, 0x02, 0x40); + spca506_WriteI2c(gspca_dev, 0xff, 0x41); + spca506_WriteI2c(gspca_dev, 0xff, 0x42); + spca506_WriteI2c(gspca_dev, 0xff, 0x43); + spca506_WriteI2c(gspca_dev, 0xff, 0x44); + spca506_WriteI2c(gspca_dev, 0xff, 0x45); + spca506_WriteI2c(gspca_dev, 0xff, 0x46); + spca506_WriteI2c(gspca_dev, 0xff, 0x47); + spca506_WriteI2c(gspca_dev, 0xff, 0x48); + spca506_WriteI2c(gspca_dev, 0xff, 0x49); + spca506_WriteI2c(gspca_dev, 0xff, 0x4a); + spca506_WriteI2c(gspca_dev, 0xff, 0x4b); + spca506_WriteI2c(gspca_dev, 0xff, 0x4c); + spca506_WriteI2c(gspca_dev, 0xff, 0x4d); + spca506_WriteI2c(gspca_dev, 0xff, 0x4e); + spca506_WriteI2c(gspca_dev, 0xff, 0x4f); + spca506_WriteI2c(gspca_dev, 0xff, 0x50); + spca506_WriteI2c(gspca_dev, 0xff, 0x51); + spca506_WriteI2c(gspca_dev, 0xff, 0x52); + spca506_WriteI2c(gspca_dev, 0xff, 0x53); + spca506_WriteI2c(gspca_dev, 0xff, 0x54); + spca506_WriteI2c(gspca_dev, 0xff, 0x55); + spca506_WriteI2c(gspca_dev, 0xff, 0x56); + spca506_WriteI2c(gspca_dev, 0xff, 0x57); + spca506_WriteI2c(gspca_dev, 0x00, 0x58); + spca506_WriteI2c(gspca_dev, 0x54, 0x59); + spca506_WriteI2c(gspca_dev, 0x07, 0x5a); + spca506_WriteI2c(gspca_dev, 0x83, 0x5b); + spca506_WriteI2c(gspca_dev, 0x00, 0x5c); + spca506_WriteI2c(gspca_dev, 0x00, 0x5d); + spca506_WriteI2c(gspca_dev, 0x00, 0x5e); + spca506_WriteI2c(gspca_dev, 0x00, 0x5f); + spca506_WriteI2c(gspca_dev, 0x00, 0x60); + spca506_WriteI2c(gspca_dev, 0x05, 0x61); + spca506_WriteI2c(gspca_dev, 0x9f, 0x62); + /**************************************/ + reg_w(dev, 0x05, 0x00, 0x0003); + reg_w(dev, 0x05, 0x00, 0x0004); + reg_w(dev, 0x03, 0x10, 0x0001); + reg_w(dev, 0x03, 0x78, 0x0000); + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + case 0: + spca506_Setsize(gspca_dev, 0, 0x10, 0x10); + break; + case 1: + spca506_Setsize(gspca_dev, 1, 0x1a, 0x1a); + break; + case 2: + spca506_Setsize(gspca_dev, 2, 0x1c, 0x1c); + break; + case 4: + spca506_Setsize(gspca_dev, 4, 0x34, 0x34); + break; + default: +/* case 5: */ + spca506_Setsize(gspca_dev, 5, 0x40, 0x40); + break; + } + + /* compress setting and size */ + /* set i2c luma */ + reg_w(dev, 0x02, 0x01, 0x0000); + reg_w(dev, 0x03, 0x12, 0x0000); + reg_r(gspca_dev, 0x04, 0x0001, 2); + PDEBUG(D_STREAM, "webcam started"); + spca506_GetNormeInput(gspca_dev, &norme, &channel); + spca506_SetNormeInput(gspca_dev, norme, channel); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +/* convert YYUV per line to YUYV (YUV 4:2:2) */ +static void yyuv_decode(unsigned char *out, + unsigned char *in, + int width, + int height) +{ + unsigned char *Ui, *Vi, *yi, *yi1; + unsigned char *out1; + int i, j; + + yi = in; + for (i = height / 2; --i >= 0; ) { + out1 = out + width * 2; /* next line */ + yi1 = yi + width; + Ui = yi1 + width; + Vi = Ui + width / 2; + for (j = width / 2; --j >= 0; ) { + *out++ = 128 + *yi++; + *out++ = 128 + *Ui; + *out++ = 128 + *yi++; + *out++ = 128 + *Vi; + + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Ui++; + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Vi++; + } + yi += width * 2; + out = out1; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + yyuv_decode(sd->tmpbuf2, sd->tmpbuf, + gspca_dev->width, + gspca_dev->height); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + sd->tmpbuf2, + gspca_dev->width + * gspca_dev->height + * 2); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, 0); + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + if (len > 0) + memcpy(sd->tmpbuf, data, len); + else + len = 0; + sd->buflen = len; + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data += 1; + len -= 1; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->brightness, SAA7113_bright); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = spca506_ReadI2c(gspca_dev, SAA7113_bright); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->contrast, SAA7113_contrast); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = spca506_ReadI2c(gspca_dev, SAA7113_contrast); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->colors, SAA7113_saturation); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = spca506_ReadI2c(gspca_dev, SAA7113_saturation); +} + +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->hue, SAA7113_hue); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void gethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = spca506_ReadI2c(gspca_dev, SAA7113_hue); +} + +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; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + sethue(gspca_dev); + return 0; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + gethue(gspca_dev); + *val = sd->hue; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x06e1, 0xa190), DVNM("ADS Instant VCD")}, +/* {USB_DEVICE(0x0733, 0x0430), DVNM("UsbGrabber PV321c")}, */ + {USB_DEVICE(0x0734, 0x043b), DVNM("3DeMon USB Capture aka")}, + {USB_DEVICE(0x99fa, 0x8988), DVNM("Grandtec V.cap")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c new file mode 100644 index 00000000000..d8cd93866a4 --- /dev/null +++ b/drivers/media/video/gspca/spca508.c @@ -0,0 +1,1791 @@ +/* + * SPCA508 chip based cameras subdriver + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "spca508" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA/SPCA508 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; + unsigned char tmpbuf[352 * 288 * 3 / 2]; /* YUVY per line */ + unsigned char tmpbuf2[352 * 288 * 2]; /* YUYV */ + + unsigned char brightness; + + char subtype; +#define CreativeVista 0 +#define HamaUSBSightcam 1 +#define HamaUSBSightcam2 2 +#define IntelEasyPCCamera 3 +#define MicroInnovationIC200 4 +#define ViewQuestVQ110 5 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 128 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +}; + +static struct v4l2_pix_format sif_mode[] = { + {160, 120, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 160 * 2, + .sizeimage = 160 * 120 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 176 * 2, + .sizeimage = 176 * 144 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 352 * 2, + .sizeimage = 352 * 288 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* Frame packet header offsets for the spca508 */ +#define SPCA508_OFFSET_TYPE 1 +#define SPCA508_OFFSET_COMPRESS 2 +#define SPCA508_OFFSET_FRAMSEQ 8 +#define SPCA508_OFFSET_WIN1LUM 11 +#define SPCA508_OFFSET_DATA 37 + +#define SPCA508_SNAPBIT 0x20 +#define SPCA508_SNAPCTRL 0x40 +/*************** I2c ****************/ +#define SPCA508_INDEX_I2C_BASE 0x8800 + +/* + * Initialization data: this is the first set-up data written to the + * device (before the open data). + */ +static const __u16 spca508_init_data[][3] = +#define IGN(x) /* nothing */ +{ + /* line URB value, index */ + /* 44274 1804 */ {0x0000, 0x870b}, + + /* 44299 1805 */ {0x0020, 0x8112}, + /* Video drop enable, ISO streaming disable */ + /* 44324 1806 */ {0x0003, 0x8111}, + /* Reset compression & memory */ + /* 44349 1807 */ {0x0000, 0x8110}, + /* Disable all outputs */ + /* 44372 1808 */ /* READ {0x0000, 0x8114} -> 0000: 00 */ + /* 44398 1809 */ {0x0000, 0x8114}, + /* SW GPIO data */ + /* 44423 1810 */ {0x0008, 0x8110}, + /* Enable charge pump output */ + /* 44527 1811 */ {0x0002, 0x8116}, + /* 200 kHz pump clock */ + /* 44555 1812 */ + /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE:) */ + /* 44590 1813 */ {0x0003, 0x8111}, + /* Reset compression & memory */ + /* 44615 1814 */ {0x0000, 0x8111}, + /* Normal mode (not reset) */ + /* 44640 1815 */ {0x0098, 0x8110}, + /* Enable charge pump output, sync.serial,external 2x clock */ + /* 44665 1816 */ {0x000d, 0x8114}, + /* SW GPIO data */ + /* 44690 1817 */ {0x0002, 0x8116}, + /* 200 kHz pump clock */ + /* 44715 1818 */ {0x0020, 0x8112}, + /* Video drop enable, ISO streaming disable */ +/* --------------------------------------- */ + /* 44740 1819 */ {0x000f, 0x8402}, + /* memory bank */ + /* 44765 1820 */ {0x0000, 0x8403}, + /* ... address */ +/* --------------------------------------- */ +/* 0x88__ is Synchronous Serial Interface. */ +/* TBD: This table could be expressed more compactly */ +/* using spca508_write_i2c_vector(). */ +/* TBD: Should see if the values in spca50x_i2c_data */ +/* would work with the VQ110 instead of the values */ +/* below. */ + /* 44790 1821 */ {0x00c0, 0x8804}, + /* SSI slave addr */ + /* 44815 1822 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 44838 1823 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 44862 1824 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 44888 1825 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 44913 1826 */ {0x0012, 0x8801}, + /* SSI reg addr */ + /* 44938 1827 */ {0x0080, 0x8800}, + /* SSI data to write */ + /* 44961 1828 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 44985 1829 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45009 1830 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45035 1831 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 45060 1832 */ {0x0012, 0x8801}, + /* SSI reg addr */ + /* 45085 1833 */ {0x0000, 0x8800}, + /* SSI data to write */ + /* 45108 1834 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45132 1835 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45156 1836 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45182 1837 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 45207 1838 */ {0x0011, 0x8801}, + /* SSI reg addr */ + /* 45232 1839 */ {0x0040, 0x8800}, + /* SSI data to write */ + /* 45255 1840 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45279 1841 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45303 1842 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45329 1843 */ {0x0008, 0x8802}, + /* 45354 1844 */ {0x0013, 0x8801}, + /* 45379 1845 */ {0x0000, 0x8800}, + /* 45402 1846 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45426 1847 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45450 1848 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45476 1849 */ {0x0008, 0x8802}, + /* 45501 1850 */ {0x0014, 0x8801}, + /* 45526 1851 */ {0x0000, 0x8800}, + /* 45549 1852 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45573 1853 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45597 1854 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45623 1855 */ {0x0008, 0x8802}, + /* 45648 1856 */ {0x0015, 0x8801}, + /* 45673 1857 */ {0x0001, 0x8800}, + /* 45696 1858 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45720 1859 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45744 1860 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45770 1861 */ {0x0008, 0x8802}, + /* 45795 1862 */ {0x0016, 0x8801}, + /* 45820 1863 */ {0x0003, 0x8800}, + /* 45843 1864 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45867 1865 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45891 1866 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45917 1867 */ {0x0008, 0x8802}, + /* 45942 1868 */ {0x0017, 0x8801}, + /* 45967 1869 */ {0x0036, 0x8800}, + /* 45990 1870 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46014 1871 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46038 1872 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46064 1873 */ {0x0008, 0x8802}, + /* 46089 1874 */ {0x0018, 0x8801}, + /* 46114 1875 */ {0x00ec, 0x8800}, + /* 46137 1876 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46161 1877 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46185 1878 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46211 1879 */ {0x0008, 0x8802}, + /* 46236 1880 */ {0x001a, 0x8801}, + /* 46261 1881 */ {0x0094, 0x8800}, + /* 46284 1882 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46308 1883 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46332 1884 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46358 1885 */ {0x0008, 0x8802}, + /* 46383 1886 */ {0x001b, 0x8801}, + /* 46408 1887 */ {0x0000, 0x8800}, + /* 46431 1888 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46455 1889 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46479 1890 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46505 1891 */ {0x0008, 0x8802}, + /* 46530 1892 */ {0x0027, 0x8801}, + /* 46555 1893 */ {0x00a2, 0x8800}, + /* 46578 1894 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46602 1895 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46626 1896 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46652 1897 */ {0x0008, 0x8802}, + /* 46677 1898 */ {0x0028, 0x8801}, + /* 46702 1899 */ {0x0040, 0x8800}, + /* 46725 1900 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46749 1901 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46773 1902 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46799 1903 */ {0x0008, 0x8802}, + /* 46824 1904 */ {0x002a, 0x8801}, + /* 46849 1905 */ {0x0084, 0x8800}, + /* 46872 1906 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46896 1907 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46920 1908 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46946 1909 */ {0x0008, 0x8802}, + /* 46971 1910 */ {0x002b, 0x8801}, + /* 46996 1911 */ {0x00a8, 0x8800}, + /* 47019 1912 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47043 1913 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47067 1914 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47093 1915 */ {0x0008, 0x8802}, + /* 47118 1916 */ {0x002c, 0x8801}, + /* 47143 1917 */ {0x00fe, 0x8800}, + /* 47166 1918 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47190 1919 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47214 1920 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47240 1921 */ {0x0008, 0x8802}, + /* 47265 1922 */ {0x002d, 0x8801}, + /* 47290 1923 */ {0x0003, 0x8800}, + /* 47313 1924 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47337 1925 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47361 1926 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47387 1927 */ {0x0008, 0x8802}, + /* 47412 1928 */ {0x0038, 0x8801}, + /* 47437 1929 */ {0x0083, 0x8800}, + /* 47460 1930 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47484 1931 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47508 1932 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47534 1933 */ {0x0008, 0x8802}, + /* 47559 1934 */ {0x0033, 0x8801}, + /* 47584 1935 */ {0x0081, 0x8800}, + /* 47607 1936 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47631 1937 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47655 1938 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47681 1939 */ {0x0008, 0x8802}, + /* 47706 1940 */ {0x0034, 0x8801}, + /* 47731 1941 */ {0x004a, 0x8800}, + /* 47754 1942 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47778 1943 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47802 1944 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47828 1945 */ {0x0008, 0x8802}, + /* 47853 1946 */ {0x0039, 0x8801}, + /* 47878 1947 */ {0x0000, 0x8800}, + /* 47901 1948 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47925 1949 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47949 1950 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47975 1951 */ {0x0008, 0x8802}, + /* 48000 1952 */ {0x0010, 0x8801}, + /* 48025 1953 */ {0x00a8, 0x8800}, + /* 48048 1954 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48072 1955 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48096 1956 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48122 1957 */ {0x0008, 0x8802}, + /* 48147 1958 */ {0x0006, 0x8801}, + /* 48172 1959 */ {0x0058, 0x8800}, + /* 48195 1960 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48219 1961 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48243 1962 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48269 1963 */ {0x0008, 0x8802}, + /* 48294 1964 */ {0x0000, 0x8801}, + /* 48319 1965 */ {0x0004, 0x8800}, + /* 48342 1966 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48366 1967 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48390 1968 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48416 1969 */ {0x0008, 0x8802}, + /* 48441 1970 */ {0x0040, 0x8801}, + /* 48466 1971 */ {0x0080, 0x8800}, + /* 48489 1972 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48513 1973 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48537 1974 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48563 1975 */ {0x0008, 0x8802}, + /* 48588 1976 */ {0x0041, 0x8801}, + /* 48613 1977 */ {0x000c, 0x8800}, + /* 48636 1978 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48660 1979 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48684 1980 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48710 1981 */ {0x0008, 0x8802}, + /* 48735 1982 */ {0x0042, 0x8801}, + /* 48760 1983 */ {0x000c, 0x8800}, + /* 48783 1984 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48807 1985 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48831 1986 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48857 1987 */ {0x0008, 0x8802}, + /* 48882 1988 */ {0x0043, 0x8801}, + /* 48907 1989 */ {0x0028, 0x8800}, + /* 48930 1990 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48954 1991 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48978 1992 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49004 1993 */ {0x0008, 0x8802}, + /* 49029 1994 */ {0x0044, 0x8801}, + /* 49054 1995 */ {0x0080, 0x8800}, + /* 49077 1996 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49101 1997 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49125 1998 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49151 1999 */ {0x0008, 0x8802}, + /* 49176 2000 */ {0x0045, 0x8801}, + /* 49201 2001 */ {0x0020, 0x8800}, + /* 49224 2002 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49248 2003 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49272 2004 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49298 2005 */ {0x0008, 0x8802}, + /* 49323 2006 */ {0x0046, 0x8801}, + /* 49348 2007 */ {0x0020, 0x8800}, + /* 49371 2008 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49395 2009 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49419 2010 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49445 2011 */ {0x0008, 0x8802}, + /* 49470 2012 */ {0x0047, 0x8801}, + /* 49495 2013 */ {0x0080, 0x8800}, + /* 49518 2014 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49542 2015 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49566 2016 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49592 2017 */ {0x0008, 0x8802}, + /* 49617 2018 */ {0x0048, 0x8801}, + /* 49642 2019 */ {0x004c, 0x8800}, + /* 49665 2020 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49689 2021 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49713 2022 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49739 2023 */ {0x0008, 0x8802}, + /* 49764 2024 */ {0x0049, 0x8801}, + /* 49789 2025 */ {0x0084, 0x8800}, + /* 49812 2026 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49836 2027 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49860 2028 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49886 2029 */ {0x0008, 0x8802}, + /* 49911 2030 */ {0x004a, 0x8801}, + /* 49936 2031 */ {0x0084, 0x8800}, + /* 49959 2032 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49983 2033 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 50007 2034 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 50033 2035 */ {0x0008, 0x8802}, + /* 50058 2036 */ {0x004b, 0x8801}, + /* 50083 2037 */ {0x0084, 0x8800}, + /* 50106 2038 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* --------------------------------------- */ + /* 50132 2039 */ {0x0012, 0x8700}, + /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + /* 50157 2040 */ {0x0000, 0x8701}, + /* CKx1 clock delay adj */ + /* 50182 2041 */ {0x0000, 0x8701}, + /* CKx1 clock delay adj */ + /* 50207 2042 */ {0x0001, 0x870c}, + /* CKOx2 output */ + /* --------------------------------------- */ + /* 50232 2043 */ {0x0080, 0x8600}, + /* Line memory read counter (L) */ + /* 50257 2044 */ {0x0001, 0x8606}, + /* reserved */ + /* 50282 2045 */ {0x0064, 0x8607}, + /* Line memory read counter (H) 0x6480=25,728 */ + /* 50307 2046 */ {0x002a, 0x8601}, + /* CDSP sharp interpolation mode, + * line sel for color sep, edge enhance enab */ + /* 50332 2047 */ {0x0000, 0x8602}, + /* optical black level for user settng = 0 */ + /* 50357 2048 */ {0x0080, 0x8600}, + /* Line memory read counter (L) */ + /* 50382 2049 */ {0x000a, 0x8603}, + /* optical black level calc mode: auto; optical black offset = 10 */ + /* 50407 2050 */ {0x00df, 0x865b}, + /* Horiz offset for valid pixels (L)=0xdf */ + /* 50432 2051 */ {0x0012, 0x865c}, + /* Vert offset for valid lines (L)=0x12 */ + +/* The following two lines seem to be the "wrong" resolution. */ +/* But perhaps these indicate the actual size of the sensor */ +/* rather than the size of the current video mode. */ + /* 50457 2052 */ {0x0058, 0x865d}, + /* Horiz valid pixels (*4) (L) = 352 */ + /* 50482 2053 */ {0x0048, 0x865e}, + /* Vert valid lines (*4) (L) = 288 */ + + /* 50507 2054 */ {0x0015, 0x8608}, + /* A11 Coef ... */ + /* 50532 2055 */ {0x0030, 0x8609}, + /* 50557 2056 */ {0x00fb, 0x860a}, + /* 50582 2057 */ {0x003e, 0x860b}, + /* 50607 2058 */ {0x00ce, 0x860c}, + /* 50632 2059 */ {0x00f4, 0x860d}, + /* 50657 2060 */ {0x00eb, 0x860e}, + /* 50682 2061 */ {0x00dc, 0x860f}, + /* 50707 2062 */ {0x0039, 0x8610}, + /* 50732 2063 */ {0x0001, 0x8611}, + /* R offset for white balance ... */ + /* 50757 2064 */ {0x0000, 0x8612}, + /* 50782 2065 */ {0x0001, 0x8613}, + /* 50807 2066 */ {0x0000, 0x8614}, + /* 50832 2067 */ {0x005b, 0x8651}, + /* R gain for white balance ... */ + /* 50857 2068 */ {0x0040, 0x8652}, + /* 50882 2069 */ {0x0060, 0x8653}, + /* 50907 2070 */ {0x0040, 0x8654}, + /* 50932 2071 */ {0x0000, 0x8655}, + /* 50957 2072 */ {0x0001, 0x863f}, + /* Fixed gamma correction enable, USB control, + * lum filter disable, lum noise clip disable */ + /* 50982 2073 */ {0x00a1, 0x8656}, + /* Window1 size 256x256, Windows2 size 64x64, + * gamma look-up disable, new edge enhancement enable */ + /* 51007 2074 */ {0x0018, 0x8657}, + /* Edge gain high thresh */ + /* 51032 2075 */ {0x0020, 0x8658}, + /* Edge gain low thresh */ + /* 51057 2076 */ {0x000a, 0x8659}, + /* Edge bandwidth high threshold */ + /* 51082 2077 */ {0x0005, 0x865a}, + /* Edge bandwidth low threshold */ + /* -------------------------------- */ + /* 51107 2078 */ {0x0030, 0x8112}, + /* Video drop enable, ISO streaming enable */ + /* 51130 2079 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51154 2080 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 51180 2081 */ {0xa908, 0x8802}, + /* 51205 2082 */ {0x0034, 0x8801}, + /* SSI reg addr */ + /* 51230 2083 */ {0x00ca, 0x8800}, + /* SSI data to write */ + /* 51253 2084 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51277 2085 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51301 2086 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 51327 2087 */ {0x1f08, 0x8802}, + /* 51352 2088 */ {0x0006, 0x8801}, + /* 51377 2089 */ {0x0080, 0x8800}, + /* 51400 2090 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + +/* ----- Read back coefs we wrote earlier. */ + /* 51424 2091 */ /* READ { 0, 0x0000, 0x8608 } -> 0000: 15 */ + /* 51448 2092 */ /* READ { 0, 0x0000, 0x8609 } -> 0000: 30 */ + /* 51472 2093 */ /* READ { 0, 0x0000, 0x860a } -> 0000: fb */ + /* 51496 2094 */ /* READ { 0, 0x0000, 0x860b } -> 0000: 3e */ + /* 51520 2095 */ /* READ { 0, 0x0000, 0x860c } -> 0000: ce */ + /* 51544 2096 */ /* READ { 0, 0x0000, 0x860d } -> 0000: f4 */ + /* 51568 2097 */ /* READ { 0, 0x0000, 0x860e } -> 0000: eb */ + /* 51592 2098 */ /* READ { 0, 0x0000, 0x860f } -> 0000: dc */ + /* 51616 2099 */ /* READ { 0, 0x0000, 0x8610 } -> 0000: 39 */ + /* 51640 2100 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51664 2101 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 51690 2102 */ {0xb008, 0x8802}, + /* 51715 2103 */ {0x0006, 0x8801}, + /* 51740 2104 */ {0x007d, 0x8800}, + /* 51763 2105 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + + + /* This chunk is seemingly redundant with */ + /* earlier commands (A11 Coef...), but if I disable it, */ + /* the image appears too dark. Maybe there was some kind of */ + /* reset since the earlier commands, so this is necessary again. */ + /* 51789 2106 */ {0x0015, 0x8608}, + /* 51814 2107 */ {0x0030, 0x8609}, + /* 51839 2108 */ {0xfffb, 0x860a}, + /* 51864 2109 */ {0x003e, 0x860b}, + /* 51889 2110 */ {0xffce, 0x860c}, + /* 51914 2111 */ {0xfff4, 0x860d}, + /* 51939 2112 */ {0xffeb, 0x860e}, + /* 51964 2113 */ {0xffdc, 0x860f}, + /* 51989 2114 */ {0x0039, 0x8610}, + /* 52014 2115 */ {0x0018, 0x8657}, + + /* 52039 2116 */ {0x0000, 0x8508}, + /* Disable compression. */ + /* Previous line was: + * 52039 2116 * { 0, 0x0021, 0x8508 }, * Enable compression. */ + /* 52064 2117 */ {0x0032, 0x850b}, + /* compression stuff */ + /* 52089 2118 */ {0x0003, 0x8509}, + /* compression stuff */ + /* 52114 2119 */ {0x0011, 0x850a}, + /* compression stuff */ + /* 52139 2120 */ {0x0021, 0x850d}, + /* compression stuff */ + /* 52164 2121 */ {0x0010, 0x850c}, + /* compression stuff */ + /* 52189 2122 */ {0x0003, 0x8500}, + /* *** Video mode: 160x120 */ + /* 52214 2123 */ {0x0001, 0x8501}, + /* Hardware-dominated snap control */ + /* 52239 2124 */ {0x0061, 0x8656}, + /* Window1 size 128x128, Windows2 size 128x128, + * gamma look-up disable, new edge enhancement enable */ + /* 52264 2125 */ {0x0018, 0x8617}, + /* Window1 start X (*2) */ + /* 52289 2126 */ {0x0008, 0x8618}, + /* Window1 start Y (*2) */ + /* 52314 2127 */ {0x0061, 0x8656}, + /* Window1 size 128x128, Windows2 size 128x128, + * gamma look-up disable, new edge enhancement enable */ + /* 52339 2128 */ {0x0058, 0x8619}, + /* Window2 start X (*2) */ + /* 52364 2129 */ {0x0008, 0x861a}, + /* Window2 start Y (*2) */ + /* 52389 2130 */ {0x00ff, 0x8615}, + /* High lum thresh for white balance */ + /* 52414 2131 */ {0x0000, 0x8616}, + /* Low lum thresh for white balance */ + /* 52439 2132 */ {0x0012, 0x8700}, + /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + /* 52464 2133 */ {0x0012, 0x8700}, + /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + /* 52487 2134 */ /* READ { 0, 0x0000, 0x8656 } -> 0000: 61 */ + /* 52513 2135 */ {0x0028, 0x8802}, + /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ + /* 52536 2136 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52560 2137 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ + /* 52586 2138 */ {0x1f28, 0x8802}, + /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ + /* 52611 2139 */ {0x0010, 0x8801}, + /* SSI reg addr */ + /* 52636 2140 */ {0x003e, 0x8800}, + /* SSI data to write */ + /* 52659 2141 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52685 2142 */ {0x0028, 0x8802}, + /* 52708 2143 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52732 2144 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ + /* 52758 2145 */ {0x1f28, 0x8802}, + /* 52783 2146 */ {0x0000, 0x8801}, + /* 52808 2147 */ {0x001f, 0x8800}, + /* 52831 2148 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52857 2149 */ {0x0001, 0x8602}, + /* optical black level for user settning = 1 */ + + /* Original: */ + /* 52882 2150 */ {0x0023, 0x8700}, + /* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */ + /* 52907 2151 */ {0x000f, 0x8602}, + /* optical black level for user settning = 15 */ + + /* 52932 2152 */ {0x0028, 0x8802}, + /* 52955 2153 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52979 2154 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ + /* 53005 2155 */ {0x1f28, 0x8802}, + /* 53030 2156 */ {0x0010, 0x8801}, + /* 53055 2157 */ {0x007b, 0x8800}, + /* 53078 2158 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 53104 2159 */ {0x002f, 0x8651}, + /* R gain for white balance ... */ + /* 53129 2160 */ {0x0080, 0x8653}, + /* 53152 2161 */ /* READ { 0, 0x0000, 0x8655 } -> 0000: 00 */ + /* 53178 2162 */ {0x0000, 0x8655}, + + /* 53203 2163 */ {0x0030, 0x8112}, + /* Video drop enable, ISO streaming enable */ + /* 53228 2164 */ {0x0020, 0x8112}, + /* Video drop enable, ISO streaming disable */ + /* 53252 2165 */ + /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE: (ALT=0) ) */ + {} +}; + + +/* + * Initialization data for Intel EasyPC Camera CS110 + */ +static const __u16 spca508cs110_init_data[][3] = { + {0x0000, 0x870b}, /* Reset CTL3 */ + {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ + {0x0000, 0x8111}, /* Normal operation on reset */ + {0x0090, 0x8110}, + /* External Clock 2x & Synchronous Serial Interface Output */ + {0x0020, 0x8112}, /* Video Drop packet enable */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + + /* Initial sequence Synchronous Serial Interface */ + {0x000f, 0x8402}, /* Memory bank Address */ + {0x0000, 0x8403}, /* Memory bank Address */ + {0x00ba, 0x8804}, /* SSI Slave address */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock two DataByte */ + + {0x0001, 0x8801}, + {0x000a, 0x8805},/* a - NWG: Dunno what this is about */ + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0002, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0003, 0x8801}, + {0x0027, 0x8805}, + {0x0001, 0x8800}, + {0x0010, 0x8802}, + + {0x0004, 0x8801}, + {0x0065, 0x8805}, + {0x0001, 0x8800}, + {0x0010, 0x8802}, + + {0x0005, 0x8801}, + {0x0003, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0006, 0x8801}, + {0x001c, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0007, 0x8801}, + {0x002a, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0002, 0x8704}, /* External input CKIx1 */ + {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ + {0x009a, 0x8600}, /* Line memory Read Counter (L) */ + {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ + {0x0003, 0x865c}, /* 3 Vertical Offset for Valid Lines(L) */ + {0x0058, 0x865d}, /* 58 Horizontal Valid Pixel Window(L) */ + + {0x0006, 0x8660}, /* Nibble data + input order */ + + {0x000a, 0x8602}, /* Optical black level set to 0x0a */ +/* 1945 */ {0x0000, 0x8603}, /* Optical black level Offset */ + +/* 1962 * {0, 0x0000, 0x8611}, * 0 R Offset for white Balance */ +/* 1963 * {0, 0x0000, 0x8612}, * 1 Gr Offset for white Balance */ +/* 1964 * {0, 0x0000, 0x8613}, * 1f B Offset for white Balance */ +/* 1965 * {0, 0x0000, 0x8614}, * f0 Gb Offset for white Balance */ + + {0x0040, 0x8651}, /* 2b BLUE gain for white balance good at all 60 */ + {0x0030, 0x8652}, /* 41 Gr Gain for white Balance (L) */ + {0x0035, 0x8653}, /* 26 RED gain for white balance */ + {0x0035, 0x8654}, /* 40Gb Gain for white Balance (L) */ + {0x0041, 0x863f}, + /* Fixed Gamma correction enabled (makes colours look better) */ + +/* 2422 */ {0x0000, 0x8655}, + /* High bits for white balance*****brightness control*** */ + {} +}; + +static const __u16 spca508_sightcam_init_data[][3] = { +/* This line seems to setup the frame/canvas */ + /*368 */ {0x000f, 0x8402}, + +/* Theese 6 lines are needed to startup the webcam */ + /*398 */ {0x0090, 0x8110}, + /*399 */ {0x0001, 0x8114}, + /*400 */ {0x0001, 0x8114}, + /*401 */ {0x0001, 0x8114}, + /*402 */ {0x0003, 0x8114}, + /*403 */ {0x0080, 0x8804}, + +/* This part seems to make the pictures darker? (autobrightness?) */ + /*436 */ {0x0001, 0x8801}, + /*437 */ {0x0004, 0x8800}, + /*439 */ {0x0003, 0x8801}, + /*440 */ {0x00e0, 0x8800}, + /*442 */ {0x0004, 0x8801}, + /*443 */ {0x00b4, 0x8800}, + /*445 */ {0x0005, 0x8801}, + /*446 */ {0x0000, 0x8800}, + + /*448 */ {0x0006, 0x8801}, + /*449 */ {0x00e0, 0x8800}, + /*451 */ {0x0007, 0x8801}, + /*452 */ {0x000c, 0x8800}, + +/* This section is just needed, it probably + * does something like the previous section, + * but the cam won't start if it's not included. + */ + /*484 */ {0x0014, 0x8801}, + /*485 */ {0x0008, 0x8800}, + /*487 */ {0x0015, 0x8801}, + /*488 */ {0x0067, 0x8800}, + /*490 */ {0x0016, 0x8801}, + /*491 */ {0x0000, 0x8800}, + /*493 */ {0x0017, 0x8801}, + /*494 */ {0x0020, 0x8800}, + /*496 */ {0x0018, 0x8801}, + /*497 */ {0x0044, 0x8800}, + +/* Makes the picture darker - and the + * cam won't start if not included + */ + /*505 */ {0x001e, 0x8801}, + /*506 */ {0x00ea, 0x8800}, + /*508 */ {0x001f, 0x8801}, + /*509 */ {0x0001, 0x8800}, + /*511 */ {0x0003, 0x8801}, + /*512 */ {0x00e0, 0x8800}, + +/* seems to place the colors ontop of each other #1 */ + /*517 */ {0x0006, 0x8704}, + /*518 */ {0x0001, 0x870c}, + /*519 */ {0x0016, 0x8600}, + /*520 */ {0x0002, 0x8606}, + +/* if not included the pictures becomes _very_ dark */ + /*521 */ {0x0064, 0x8607}, + /*522 */ {0x003a, 0x8601}, + /*523 */ {0x0000, 0x8602}, + +/* seems to place the colors ontop of each other #2 */ + /*524 */ {0x0016, 0x8600}, + /*525 */ {0x0018, 0x8617}, + /*526 */ {0x0008, 0x8618}, + /*527 */ {0x00a1, 0x8656}, + +/* webcam won't start if not included */ + /*528 */ {0x0007, 0x865b}, + /*529 */ {0x0001, 0x865c}, + /*530 */ {0x0058, 0x865d}, + /*531 */ {0x0048, 0x865e}, + +/* adjusts the colors */ + /*541 */ {0x0049, 0x8651}, + /*542 */ {0x0040, 0x8652}, + /*543 */ {0x004c, 0x8653}, + /*544 */ {0x0040, 0x8654}, + {} +}; + +static const __u16 spca508_sightcam2_init_data[][3] = { +/* 35 */ {0x0020, 0x8112}, + +/* 36 */ {0x000f, 0x8402}, +/* 37 */ {0x0000, 0x8403}, + +/* 38 */ {0x0008, 0x8201}, +/* 39 */ {0x0008, 0x8200}, +/* 40 */ {0x0001, 0x8200}, +/* 43 */ {0x0009, 0x8201}, +/* 44 */ {0x0008, 0x8200}, +/* 45 */ {0x0001, 0x8200}, +/* 48 */ {0x000a, 0x8201}, +/* 49 */ {0x0008, 0x8200}, +/* 50 */ {0x0001, 0x8200}, +/* 53 */ {0x000b, 0x8201}, +/* 54 */ {0x0008, 0x8200}, +/* 55 */ {0x0001, 0x8200}, +/* 58 */ {0x000c, 0x8201}, +/* 59 */ {0x0008, 0x8200}, +/* 60 */ {0x0001, 0x8200}, +/* 63 */ {0x000d, 0x8201}, +/* 64 */ {0x0008, 0x8200}, +/* 65 */ {0x0001, 0x8200}, +/* 68 */ {0x000e, 0x8201}, +/* 69 */ {0x0008, 0x8200}, +/* 70 */ {0x0001, 0x8200}, +/* 73 */ {0x0007, 0x8201}, +/* 74 */ {0x0008, 0x8200}, +/* 75 */ {0x0001, 0x8200}, +/* 78 */ {0x000f, 0x8201}, +/* 79 */ {0x0008, 0x8200}, +/* 80 */ {0x0001, 0x8200}, + +/* 84 */ {0x0018, 0x8660}, +/* 85 */ {0x0010, 0x8201}, + +/* 86 */ {0x0008, 0x8200}, +/* 87 */ {0x0001, 0x8200}, +/* 90 */ {0x0011, 0x8201}, +/* 91 */ {0x0008, 0x8200}, +/* 92 */ {0x0001, 0x8200}, + +/* 95 */ {0x0000, 0x86b0}, +/* 96 */ {0x0034, 0x86b1}, +/* 97 */ {0x0000, 0x86b2}, +/* 98 */ {0x0049, 0x86b3}, +/* 99 */ {0x0000, 0x86b4}, +/* 100 */ {0x0000, 0x86b4}, + +/* 101 */ {0x0012, 0x8201}, +/* 102 */ {0x0008, 0x8200}, +/* 103 */ {0x0001, 0x8200}, +/* 106 */ {0x0013, 0x8201}, +/* 107 */ {0x0008, 0x8200}, +/* 108 */ {0x0001, 0x8200}, + +/* 111 */ {0x0001, 0x86b0}, +/* 112 */ {0x00aa, 0x86b1}, +/* 113 */ {0x0000, 0x86b2}, +/* 114 */ {0x00e4, 0x86b3}, +/* 115 */ {0x0000, 0x86b4}, +/* 116 */ {0x0000, 0x86b4}, + +/* 118 */ {0x0018, 0x8660}, + +/* 119 */ {0x0090, 0x8110}, +/* 120 */ {0x0001, 0x8114}, +/* 121 */ {0x0001, 0x8114}, +/* 122 */ {0x0001, 0x8114}, +/* 123 */ {0x0003, 0x8114}, + +/* 124 */ {0x0080, 0x8804}, +/* 157 */ {0x0003, 0x8801}, +/* 158 */ {0x0012, 0x8800}, +/* 160 */ {0x0004, 0x8801}, +/* 161 */ {0x0005, 0x8800}, +/* 163 */ {0x0005, 0x8801}, +/* 164 */ {0x0000, 0x8800}, +/* 166 */ {0x0006, 0x8801}, +/* 167 */ {0x0000, 0x8800}, +/* 169 */ {0x0007, 0x8801}, +/* 170 */ {0x0000, 0x8800}, +/* 172 */ {0x0008, 0x8801}, +/* 173 */ {0x0005, 0x8800}, +/* 175 */ {0x000a, 0x8700}, +/* 176 */ {0x000e, 0x8801}, +/* 177 */ {0x0004, 0x8800}, +/* 179 */ {0x0005, 0x8801}, +/* 180 */ {0x0047, 0x8800}, +/* 182 */ {0x0006, 0x8801}, +/* 183 */ {0x0000, 0x8800}, +/* 185 */ {0x0007, 0x8801}, +/* 186 */ {0x00c0, 0x8800}, +/* 188 */ {0x0008, 0x8801}, +/* 189 */ {0x0003, 0x8800}, +/* 191 */ {0x0013, 0x8801}, +/* 192 */ {0x0001, 0x8800}, +/* 194 */ {0x0009, 0x8801}, +/* 195 */ {0x0000, 0x8800}, +/* 197 */ {0x000a, 0x8801}, +/* 198 */ {0x0000, 0x8800}, +/* 200 */ {0x000b, 0x8801}, +/* 201 */ {0x0000, 0x8800}, +/* 203 */ {0x000c, 0x8801}, +/* 204 */ {0x0000, 0x8800}, +/* 206 */ {0x000e, 0x8801}, +/* 207 */ {0x0004, 0x8800}, +/* 209 */ {0x000f, 0x8801}, +/* 210 */ {0x0000, 0x8800}, +/* 212 */ {0x0010, 0x8801}, +/* 213 */ {0x0006, 0x8800}, +/* 215 */ {0x0011, 0x8801}, +/* 216 */ {0x0006, 0x8800}, +/* 218 */ {0x0012, 0x8801}, +/* 219 */ {0x0000, 0x8800}, +/* 221 */ {0x0013, 0x8801}, +/* 222 */ {0x0001, 0x8800}, + +/* 224 */ {0x000a, 0x8700}, +/* 225 */ {0x0000, 0x8702}, +/* 226 */ {0x0000, 0x8703}, +/* 227 */ {0x00c2, 0x8704}, +/* 228 */ {0x0001, 0x870c}, + +/* 229 */ {0x0044, 0x8600}, +/* 230 */ {0x0002, 0x8606}, +/* 231 */ {0x0064, 0x8607}, +/* 232 */ {0x003a, 0x8601}, +/* 233 */ {0x0008, 0x8602}, +/* 234 */ {0x0044, 0x8600}, +/* 235 */ {0x0018, 0x8617}, +/* 236 */ {0x0008, 0x8618}, +/* 237 */ {0x00a1, 0x8656}, +/* 238 */ {0x0004, 0x865b}, +/* 239 */ {0x0002, 0x865c}, +/* 240 */ {0x0058, 0x865d}, +/* 241 */ {0x0048, 0x865e}, +/* 242 */ {0x0012, 0x8608}, +/* 243 */ {0x002c, 0x8609}, +/* 244 */ {0x0002, 0x860a}, +/* 245 */ {0x002c, 0x860b}, +/* 246 */ {0x00db, 0x860c}, +/* 247 */ {0x00f9, 0x860d}, +/* 248 */ {0x00f1, 0x860e}, +/* 249 */ {0x00e3, 0x860f}, +/* 250 */ {0x002c, 0x8610}, +/* 251 */ {0x006c, 0x8651}, +/* 252 */ {0x0041, 0x8652}, +/* 253 */ {0x0059, 0x8653}, +/* 254 */ {0x0040, 0x8654}, +/* 255 */ {0x00fa, 0x8611}, +/* 256 */ {0x00ff, 0x8612}, +/* 257 */ {0x00f8, 0x8613}, +/* 258 */ {0x0000, 0x8614}, +/* 259 */ {0x0001, 0x863f}, +/* 260 */ {0x0000, 0x8640}, +/* 261 */ {0x0026, 0x8641}, +/* 262 */ {0x0045, 0x8642}, +/* 263 */ {0x0060, 0x8643}, +/* 264 */ {0x0075, 0x8644}, +/* 265 */ {0x0088, 0x8645}, +/* 266 */ {0x009b, 0x8646}, +/* 267 */ {0x00b0, 0x8647}, +/* 268 */ {0x00c5, 0x8648}, +/* 269 */ {0x00d2, 0x8649}, +/* 270 */ {0x00dc, 0x864a}, +/* 271 */ {0x00e5, 0x864b}, +/* 272 */ {0x00eb, 0x864c}, +/* 273 */ {0x00f0, 0x864d}, +/* 274 */ {0x00f6, 0x864e}, +/* 275 */ {0x00fa, 0x864f}, +/* 276 */ {0x00ff, 0x8650}, +/* 277 */ {0x0060, 0x8657}, +/* 278 */ {0x0010, 0x8658}, +/* 279 */ {0x0018, 0x8659}, +/* 280 */ {0x0005, 0x865a}, +/* 281 */ {0x0018, 0x8660}, +/* 282 */ {0x0003, 0x8509}, +/* 283 */ {0x0011, 0x850a}, +/* 284 */ {0x0032, 0x850b}, +/* 285 */ {0x0010, 0x850c}, +/* 286 */ {0x0021, 0x850d}, +/* 287 */ {0x0001, 0x8500}, +/* 288 */ {0x0000, 0x8508}, +/* 289 */ {0x0012, 0x8608}, +/* 290 */ {0x002c, 0x8609}, +/* 291 */ {0x0002, 0x860a}, +/* 292 */ {0x0039, 0x860b}, +/* 293 */ {0x00d0, 0x860c}, +/* 294 */ {0x00f7, 0x860d}, +/* 295 */ {0x00ed, 0x860e}, +/* 296 */ {0x00db, 0x860f}, +/* 297 */ {0x0039, 0x8610}, +/* 298 */ {0x0012, 0x8657}, +/* 299 */ {0x000c, 0x8619}, +/* 300 */ {0x0004, 0x861a}, +/* 301 */ {0x00a1, 0x8656}, +/* 302 */ {0x00c8, 0x8615}, +/* 303 */ {0x0032, 0x8616}, + +/* 306 */ {0x0030, 0x8112}, +/* 313 */ {0x0020, 0x8112}, +/* 314 */ {0x0020, 0x8112}, +/* 315 */ {0x000f, 0x8402}, +/* 316 */ {0x0000, 0x8403}, + +/* 317 */ {0x0090, 0x8110}, +/* 318 */ {0x0001, 0x8114}, +/* 319 */ {0x0001, 0x8114}, +/* 320 */ {0x0001, 0x8114}, +/* 321 */ {0x0003, 0x8114}, +/* 322 */ {0x0080, 0x8804}, + +/* 355 */ {0x0003, 0x8801}, +/* 356 */ {0x0012, 0x8800}, +/* 358 */ {0x0004, 0x8801}, +/* 359 */ {0x0005, 0x8800}, +/* 361 */ {0x0005, 0x8801}, +/* 362 */ {0x0047, 0x8800}, +/* 364 */ {0x0006, 0x8801}, +/* 365 */ {0x0000, 0x8800}, +/* 367 */ {0x0007, 0x8801}, +/* 368 */ {0x00c0, 0x8800}, +/* 370 */ {0x0008, 0x8801}, +/* 371 */ {0x0003, 0x8800}, +/* 373 */ {0x000a, 0x8700}, +/* 374 */ {0x000e, 0x8801}, +/* 375 */ {0x0004, 0x8800}, +/* 377 */ {0x0005, 0x8801}, +/* 378 */ {0x0047, 0x8800}, +/* 380 */ {0x0006, 0x8801}, +/* 381 */ {0x0000, 0x8800}, +/* 383 */ {0x0007, 0x8801}, +/* 384 */ {0x00c0, 0x8800}, +/* 386 */ {0x0008, 0x8801}, +/* 387 */ {0x0003, 0x8800}, +/* 389 */ {0x0013, 0x8801}, +/* 390 */ {0x0001, 0x8800}, +/* 392 */ {0x0009, 0x8801}, +/* 393 */ {0x0000, 0x8800}, +/* 395 */ {0x000a, 0x8801}, +/* 396 */ {0x0000, 0x8800}, +/* 398 */ {0x000b, 0x8801}, +/* 399 */ {0x0000, 0x8800}, +/* 401 */ {0x000c, 0x8801}, +/* 402 */ {0x0000, 0x8800}, +/* 404 */ {0x000e, 0x8801}, +/* 405 */ {0x0004, 0x8800}, +/* 407 */ {0x000f, 0x8801}, +/* 408 */ {0x0000, 0x8800}, +/* 410 */ {0x0010, 0x8801}, +/* 411 */ {0x0006, 0x8800}, +/* 413 */ {0x0011, 0x8801}, +/* 414 */ {0x0006, 0x8800}, +/* 416 */ {0x0012, 0x8801}, +/* 417 */ {0x0000, 0x8800}, +/* 419 */ {0x0013, 0x8801}, +/* 420 */ {0x0001, 0x8800}, +/* 422 */ {0x000a, 0x8700}, +/* 423 */ {0x0000, 0x8702}, +/* 424 */ {0x0000, 0x8703}, +/* 425 */ {0x00c2, 0x8704}, +/* 426 */ {0x0001, 0x870c}, +/* 427 */ {0x0044, 0x8600}, +/* 428 */ {0x0002, 0x8606}, +/* 429 */ {0x0064, 0x8607}, +/* 430 */ {0x003a, 0x8601}, +/* 431 */ {0x0008, 0x8602}, +/* 432 */ {0x0044, 0x8600}, +/* 433 */ {0x0018, 0x8617}, +/* 434 */ {0x0008, 0x8618}, +/* 435 */ {0x00a1, 0x8656}, +/* 436 */ {0x0004, 0x865b}, +/* 437 */ {0x0002, 0x865c}, +/* 438 */ {0x0058, 0x865d}, +/* 439 */ {0x0048, 0x865e}, +/* 440 */ {0x0012, 0x8608}, +/* 441 */ {0x002c, 0x8609}, +/* 442 */ {0x0002, 0x860a}, +/* 443 */ {0x002c, 0x860b}, +/* 444 */ {0x00db, 0x860c}, +/* 445 */ {0x00f9, 0x860d}, +/* 446 */ {0x00f1, 0x860e}, +/* 447 */ {0x00e3, 0x860f}, +/* 448 */ {0x002c, 0x8610}, +/* 449 */ {0x006c, 0x8651}, +/* 450 */ {0x0041, 0x8652}, +/* 451 */ {0x0059, 0x8653}, +/* 452 */ {0x0040, 0x8654}, +/* 453 */ {0x00fa, 0x8611}, +/* 454 */ {0x00ff, 0x8612}, +/* 455 */ {0x00f8, 0x8613}, +/* 456 */ {0x0000, 0x8614}, +/* 457 */ {0x0001, 0x863f}, +/* 458 */ {0x0000, 0x8640}, +/* 459 */ {0x0026, 0x8641}, +/* 460 */ {0x0045, 0x8642}, +/* 461 */ {0x0060, 0x8643}, +/* 462 */ {0x0075, 0x8644}, +/* 463 */ {0x0088, 0x8645}, +/* 464 */ {0x009b, 0x8646}, +/* 465 */ {0x00b0, 0x8647}, +/* 466 */ {0x00c5, 0x8648}, +/* 467 */ {0x00d2, 0x8649}, +/* 468 */ {0x00dc, 0x864a}, +/* 469 */ {0x00e5, 0x864b}, +/* 470 */ {0x00eb, 0x864c}, +/* 471 */ {0x00f0, 0x864d}, +/* 472 */ {0x00f6, 0x864e}, +/* 473 */ {0x00fa, 0x864f}, +/* 474 */ {0x00ff, 0x8650}, +/* 475 */ {0x0060, 0x8657}, +/* 476 */ {0x0010, 0x8658}, +/* 477 */ {0x0018, 0x8659}, +/* 478 */ {0x0005, 0x865a}, +/* 479 */ {0x0018, 0x8660}, +/* 480 */ {0x0003, 0x8509}, +/* 481 */ {0x0011, 0x850a}, +/* 482 */ {0x0032, 0x850b}, +/* 483 */ {0x0010, 0x850c}, +/* 484 */ {0x0021, 0x850d}, +/* 485 */ {0x0001, 0x8500}, +/* 486 */ {0x0000, 0x8508}, + +/* 487 */ {0x0012, 0x8608}, +/* 488 */ {0x002c, 0x8609}, +/* 489 */ {0x0002, 0x860a}, +/* 490 */ {0x0039, 0x860b}, +/* 491 */ {0x00d0, 0x860c}, +/* 492 */ {0x00f7, 0x860d}, +/* 493 */ {0x00ed, 0x860e}, +/* 494 */ {0x00db, 0x860f}, +/* 495 */ {0x0039, 0x8610}, +/* 496 */ {0x0012, 0x8657}, +/* 497 */ {0x0064, 0x8619}, + +/* This line starts it all, it is not needed here */ +/* since it has been build into the driver */ +/* jfm: don't start now */ +/* 590 * {0x0030, 0x8112}, */ + {} +}; + +/* + * Initialization data for Creative Webcam Vista + */ +static const __u16 spca508_vista_init_data[][3] = { + {0x0008, 0x8200}, /* Clear register */ + {0x0000, 0x870b}, /* Reset CTL3 */ + {0x0020, 0x8112}, /* Video Drop packet enable */ + {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ + {0x0000, 0x8110}, /* Disable everything */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8114}, + + {0x0003, 0x8111}, + {0x0000, 0x8111}, + {0x0090, 0x8110}, /* Enable: SSI output, External 2X clock output */ + {0x0020, 0x8112}, + {0x0000, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + + {0x000f, 0x8402}, /* Memory bank Address */ + {0x0000, 0x8403}, /* Memory bank Address */ + {0x00ba, 0x8804}, /* SSI Slave address */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, /* Will write 2 bytes (DATA1+DATA2) */ + {0x0020, 0x8801}, /* Register address for SSI read/write */ + {0x0044, 0x8805}, /* DATA2 */ + {0x0004, 0x8800}, /* DATA1 -> write triggered */ + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0009, 0x8801}, + {0x0042, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x003c, 0x8801}, + {0x0001, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0001, 0x8801}, + {0x000a, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0002, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0003, 0x8801}, + {0x0027, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0004, 0x8801}, + {0x0065, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0005, 0x8801}, + {0x0003, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0006, 0x8801}, + {0x001c, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0007, 0x8801}, + {0x002a, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x000e, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0028, 0x8801}, + {0x002e, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0039, 0x8801}, + {0x0013, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x003b, 0x8801}, + {0x000c, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0035, 0x8801}, + {0x0028, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0009, 0x8801}, + {0x0042, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + {0x0050, 0x8703}, + {0x0002, 0x8704}, /* External input CKIx1 */ + {0x0001, 0x870C}, /* Select CKOx2 output */ + {0x009A, 0x8600}, /* Line memory Read Counter (L) */ + {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ + {0x0023, 0x8601}, + {0x0010, 0x8602}, + {0x000A, 0x8603}, + {0x009A, 0x8600}, + {0x0001, 0x865B}, /* 1 Horizontal Offset for Valid Pixel(L) */ + {0x0003, 0x865C}, /* Vertical offset for valid lines (L) */ + {0x0058, 0x865D}, /* Horizontal valid pixels window (L) */ + {0x0048, 0x865E}, /* Vertical valid lines window (L) */ + {0x0000, 0x865F}, + + {0x0006, 0x8660}, + /* Enable nibble data input, select nibble input order */ + + {0x0013, 0x8608}, /* A11 Coeficients for color correction */ + {0x0028, 0x8609}, + /* Note: these values are confirmed at the end of array */ + {0x0005, 0x860A}, /* ... */ + {0x0025, 0x860B}, + {0x00E1, 0x860C}, + {0x00FA, 0x860D}, + {0x00F4, 0x860E}, + {0x00E8, 0x860F}, + {0x0025, 0x8610}, /* A33 Coef. */ + {0x00FC, 0x8611}, /* White balance offset: R */ + {0x0001, 0x8612}, /* White balance offset: Gr */ + {0x00FE, 0x8613}, /* White balance offset: B */ + {0x0000, 0x8614}, /* White balance offset: Gb */ + + {0x0064, 0x8651}, /* R gain for white balance (L) */ + {0x0040, 0x8652}, /* Gr gain for white balance (L) */ + {0x0066, 0x8653}, /* B gain for white balance (L) */ + {0x0040, 0x8654}, /* Gb gain for white balance (L) */ + {0x0001, 0x863F}, /* Enable fixed gamma correction */ + + {0x00A1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128 */ + /* UV division: UV no change, Enable New edge enhancement */ + {0x0018, 0x8657}, /* Edge gain high threshold */ + {0x0020, 0x8658}, /* Edge gain low threshold */ + {0x000A, 0x8659}, /* Edge bandwidth high threshold */ + {0x0005, 0x865A}, /* Edge bandwidth low threshold */ + {0x0064, 0x8607}, /* UV filter enable */ + + {0x0016, 0x8660}, + {0x0000, 0x86B0}, /* Bad pixels compensation address */ + {0x00DC, 0x86B1}, /* X coord for bad pixels compensation (L) */ + {0x0000, 0x86B2}, + {0x0009, 0x86B3}, /* Y coord for bad pixels compensation (L) */ + {0x0000, 0x86B4}, + + {0x0001, 0x86B0}, + {0x00F5, 0x86B1}, + {0x0000, 0x86B2}, + {0x00C6, 0x86B3}, + {0x0000, 0x86B4}, + + {0x0002, 0x86B0}, + {0x001C, 0x86B1}, + {0x0001, 0x86B2}, + {0x00D7, 0x86B3}, + {0x0000, 0x86B4}, + + {0x0003, 0x86B0}, + {0x001C, 0x86B1}, + {0x0001, 0x86B2}, + {0x00D8, 0x86B3}, + {0x0000, 0x86B4}, + + {0x0004, 0x86B0}, + {0x001D, 0x86B1}, + {0x0001, 0x86B2}, + {0x00D8, 0x86B3}, + {0x0000, 0x86B4}, + {0x001E, 0x8660}, + + /* READ { 0, 0x0000, 0x8608 } -> + 0000: 13 */ + /* READ { 0, 0x0000, 0x8609 } -> + 0000: 28 */ + /* READ { 0, 0x0000, 0x8610 } -> + 0000: 05 */ + /* READ { 0, 0x0000, 0x8611 } -> + 0000: 25 */ + /* READ { 0, 0x0000, 0x8612 } -> + 0000: e1 */ + /* READ { 0, 0x0000, 0x8613 } -> + 0000: fa */ + /* READ { 0, 0x0000, 0x8614 } -> + 0000: f4 */ + /* READ { 0, 0x0000, 0x8615 } -> + 0000: e8 */ + /* READ { 0, 0x0000, 0x8616 } -> + 0000: 25 */ + {} +}; + +static int reg_write(struct usb_device *dev, + __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write i:0x%04x = 0x%02x", + index, value); + if (ret < 0) + PDEBUG(D_ERR|D_USBO, "reg write: error %d", ret); + return ret; +} + +/* read 1 byte */ +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct gspca_dev *gspca_dev, + __u16 index) /* wIndex */ +{ + int ret; + + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, /* register */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + gspca_dev->usb_buf, 1, + 500); /* timeout */ + PDEBUG(D_USBI, "reg read i:%04x --> %02x", + index, gspca_dev->usb_buf[0]); + if (ret < 0) { + PDEBUG(D_ERR|D_USBI, "reg_read err %d", ret); + return ret; + } + return gspca_dev->usb_buf[0]; +} + +static int write_vector(struct gspca_dev *gspca_dev, + const __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][1] != 0) { + ret = reg_write(dev, data[i][1], data[i][0]); + if (ret < 0) + return ret; + i++; + } + return 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; + __u16 product; + int data1, data2; + + product = id->idProduct; + switch (id->idVendor) { + case 0x0130: /* Clone webcam */ +/* switch (product) { */ +/* case 0x0130: */ + sd->subtype = HamaUSBSightcam; /* same as Hama 0010 */ +/* break; */ +/* } */ + break; + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x4018: */ + sd->subtype = CreativeVista; +/* break; */ +/* } */ + break; + case 0x0461: /* MicroInnovation */ +/* switch (product) { */ +/* case 0x0815: */ + sd->subtype = MicroInnovationIC200; +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ +/* switch (product) { */ +/* case 0x110: */ + sd->subtype = ViewQuestVQ110; +/* break; */ +/* } */ + break; + case 0x0af9: /* Hama cameras */ + switch (product) { + case 0x0010: + sd->subtype = HamaUSBSightcam; + break; + case 0x0011: + sd->subtype = HamaUSBSightcam2; + break; + } + break; + case 0x8086: /* Intel */ +/* switch (product) { */ +/* case 0x0110: */ + sd->subtype = IntelEasyPCCamera; +/* break; */ +/* } */ + break; + } + + /* Read from global register the USB product and vendor IDs, just to + * prove that we can communicate with the device. This works, which + * confirms at we are communicating properly and that the device + * is a 508. */ + data1 = reg_read(gspca_dev, 0x8104); + data2 = reg_read(gspca_dev, 0x8105); + PDEBUG(D_PROBE, "Webcam Vendor ID: 0x%02x%02x", data2, data1); + + data1 = reg_read(gspca_dev, 0x8106); + data2 = reg_read(gspca_dev, 0x8107); + PDEBUG(D_PROBE, "Webcam Product ID: 0x%02x%02x", data2, data1); + + data1 = reg_read(gspca_dev, 0x8621); + PDEBUG(D_PROBE, "Window 1 average luminance: %d", data1); + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = sif_mode; + cam->nmodes = ARRAY_SIZE(sif_mode); + sd->brightness = BRIGHTNESS_DEF; + + switch (sd->subtype) { + case ViewQuestVQ110: + if (write_vector(gspca_dev, spca508_init_data)) + return -1; + break; + default: +/* case MicroInnovationIC200: */ +/* case IntelEasyPCCamera: */ + if (write_vector(gspca_dev, spca508cs110_init_data)) + return -1; + break; + case HamaUSBSightcam: + if (write_vector(gspca_dev, spca508_sightcam_init_data)) + return -1; + break; + case HamaUSBSightcam2: + if (write_vector(gspca_dev, spca508_sightcam2_init_data)) + return -1; + break; + case CreativeVista: + if (write_vector(gspca_dev, spca508_vista_init_data)) + return -1; + break; + } + return 0; /* success */ +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ +/* write_vector(gspca_dev, spca508_open_data); */ + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + int mode; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + reg_write(gspca_dev->dev, 0x8500, mode); + switch (mode) { + case 0: + case 1: + reg_write(gspca_dev->dev, 0x8700, 0x28); /* clock */ + break; + default: +/* case 2: */ +/* case 3: */ + reg_write(gspca_dev->dev, 0x8700, 0x23); /* clock */ + break; + } + reg_write(gspca_dev->dev, 0x8112, 0x10 | 0x20); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Video ISO disable, Video Drop Packet enable: */ + reg_write(gspca_dev->dev, 0x8112, 0x20); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +/* convert YUVY per line to YUYV (YUV 4:2:2) */ +static void yuvy_decode(unsigned char *out, + unsigned char *in, + int width, + int height) +{ + unsigned char *Ui, *Vi, *yi, *yi1; + unsigned char *out1; + int i, j; + + yi = in; + for (i = height / 2; --i >= 0; ) { + out1 = out + width * 2; /* next line */ + Ui = yi + width; + Vi = Ui + width / 2; + yi1 = Vi + width / 2; + for (j = width / 2; --j >= 0; ) { + *out++ = 128 + *yi++; + *out++ = 128 + *Ui; + *out++ = 128 + *yi++; + *out++ = 128 + *Vi; + + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Ui++; + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Vi++; + } + yi += width * 2; + out = out1; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + yuvy_decode(sd->tmpbuf2, sd->tmpbuf, + gspca_dev->width, + gspca_dev->height); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + sd->tmpbuf2, + gspca_dev->width + * gspca_dev->height + * 2); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, 0); + data += SPCA508_OFFSET_DATA; + len -= SPCA508_OFFSET_DATA; + if (len > 0) + memcpy(sd->tmpbuf, data, len); + else + len = 0; + sd->buflen = len; + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data += 1; + len -= 1; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 brightness = sd->brightness; + + /* MX seem contrast */ + reg_write(gspca_dev->dev, 0x8651, brightness); + reg_write(gspca_dev->dev, 0x8652, brightness); + reg_write(gspca_dev->dev, 0x8653, brightness); + reg_write(gspca_dev->dev, 0x8654, brightness); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = reg_read(gspca_dev, 0x8651); +} + +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; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0130, 0x0130), DVNM("Clone Digital Webcam 11043")}, + {USB_DEVICE(0x041e, 0x4018), DVNM("Creative Webcam Vista (PD1100)")}, + {USB_DEVICE(0x0461, 0x0815), DVNM("Micro Innovation IC200")}, + {USB_DEVICE(0x0733, 0x0110), DVNM("ViewQuest VQ110")}, + {USB_DEVICE(0x0af9, 0x0010), DVNM("Hama USB Sightcam 100")}, + {USB_DEVICE(0x0af9, 0x0011), DVNM("Hama USB Sightcam 100")}, + {USB_DEVICE(0x8086, 0x0110), DVNM("Intel Easy PC Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c new file mode 100644 index 00000000000..b659bd0f788 --- /dev/null +++ b/drivers/media/video/gspca/spca561.c @@ -0,0 +1,1052 @@ +/* + * Sunplus spca561 subdriver + * + * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "spca561" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned short contrast; + __u8 brightness; + __u8 autogain; + + __u8 chip_revision; + signed char ag_cnt; +#define AG_CNT_START 13 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 63, + .step = 1, + .default_value = 32, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0x3fff, + .step = 1, + .default_value = 0x2000, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_AUTOGAIN 2 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct v4l2_pix_format sif_mode[] = { + {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 4 / 8, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 4 / 8, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* + * Initialization data + * I'm not very sure how to split initialization from open data + * chunks. For now, we'll consider everything as initialization + */ +/* Frame packet header offsets for the spca561 */ +#define SPCA561_OFFSET_SNAP 1 +#define SPCA561_OFFSET_TYPE 2 +#define SPCA561_OFFSET_COMPRESS 3 +#define SPCA561_OFFSET_FRAMSEQ 4 +#define SPCA561_OFFSET_GPIO 5 +#define SPCA561_OFFSET_USBBUFF 6 +#define SPCA561_OFFSET_WIN2GRAVE 7 +#define SPCA561_OFFSET_WIN2RAVE 8 +#define SPCA561_OFFSET_WIN2BAVE 9 +#define SPCA561_OFFSET_WIN2GBAVE 10 +#define SPCA561_OFFSET_WIN1GRAVE 11 +#define SPCA561_OFFSET_WIN1RAVE 12 +#define SPCA561_OFFSET_WIN1BAVE 13 +#define SPCA561_OFFSET_WIN1GBAVE 14 +#define SPCA561_OFFSET_FREQ 15 +#define SPCA561_OFFSET_VSYNC 16 +#define SPCA561_OFFSET_DATA 1 +#define SPCA561_INDEX_I2C_BASE 0x8800 +#define SPCA561_SNAPBIT 0x20 +#define SPCA561_SNAPCTRL 0x40 +enum { + Rev072A = 0, + Rev012A, +}; + +static void reg_w_val(struct usb_device *dev, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); +} + +static void write_vector(struct gspca_dev *gspca_dev, + const __u16 data[][2]) +{ + struct usb_device *dev = gspca_dev->dev; + int i; + + i = 0; + while (data[i][1] != 0) { + reg_w_val(dev, data[i][1], data[i][0]); + i++; + } +} + +/* read 'len' bytes to gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 index, __u16 length) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, length, 500); +} + +static void reg_w_buf(struct gspca_dev *gspca_dev, + __u16 index, const __u8 *buffer, __u16 len) +{ + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, len, 500); +} + +static void i2c_init(struct gspca_dev *gspca_dev, __u8 mode) +{ + reg_w_val(gspca_dev->dev, 0x92, 0x8804); + reg_w_val(gspca_dev->dev, mode, 0x8802); +} + +static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg) +{ + int retry = 60; + __u8 DataLow; + __u8 DataHight; + + DataLow = valeur; + DataHight = valeur >> 8; + reg_w_val(gspca_dev->dev, reg, 0x8801); + reg_w_val(gspca_dev->dev, DataLow, 0x8805); + reg_w_val(gspca_dev->dev, DataHight, 0x8800); + while (retry--) { + reg_r(gspca_dev, 0x8803, 1); + if (!gspca_dev->usb_buf[0]) + break; + } +} + +static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) +{ + int retry = 60; + __u8 value; + __u8 vallsb; + + reg_w_val(gspca_dev->dev, 0x92, 0x8804); + reg_w_val(gspca_dev->dev, reg, 0x8801); + reg_w_val(gspca_dev->dev, (mode | 0x01), 0x8802); + while (retry--) { + reg_r(gspca_dev, 0x8803, 1); + if (!gspca_dev->usb_buf) + break; + } + if (retry == 0) + return -1; + reg_r(gspca_dev, 0x8800, 1); + value = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8805, 1); + vallsb = gspca_dev->usb_buf[0]; + return ((int) value << 8) | vallsb; +} + +static const __u16 spca561_init_data[][2] = { + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0001, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8112}, /* Some kind of reset */ + {0x0003, 0x8701}, /* PCLK clock delay adjustment */ + {0x0001, 0x8703}, /* HSYNC from cmos inverted */ + {0x0011, 0x8118}, /* Enable and conf sensor */ + {0x0001, 0x8118}, /* Conf sensor */ + {0x0092, 0x8804}, /* I know nothing about these */ + {0x0010, 0x8802}, /* 0x88xx registers, so I won't */ + /***************/ + {0x000d, 0x8805}, /* sensor default setting */ + {0x0001, 0x8801}, /* 1 <- 0x0d */ + {0x0000, 0x8800}, + {0x0018, 0x8805}, + {0x0002, 0x8801}, /* 2 <- 0x18 */ + {0x0000, 0x8800}, + {0x0065, 0x8805}, + {0x0004, 0x8801}, /* 4 <- 0x01 0x65 */ + {0x0001, 0x8800}, + {0x0021, 0x8805}, + {0x0005, 0x8801}, /* 5 <- 0x21 */ + {0x0000, 0x8800}, + {0x00aa, 0x8805}, + {0x0007, 0x8801}, /* 7 <- 0xaa */ + {0x0000, 0x8800}, + {0x0004, 0x8805}, + {0x0020, 0x8801}, /* 0x20 <- 0x15 0x04 */ + {0x0015, 0x8800}, + {0x0002, 0x8805}, + {0x0039, 0x8801}, /* 0x39 <- 0x02 */ + {0x0000, 0x8800}, + {0x0010, 0x8805}, + {0x0035, 0x8801}, /* 0x35 <- 0x10 */ + {0x0000, 0x8800}, + {0x0049, 0x8805}, + {0x0009, 0x8801}, /* 0x09 <- 0x10 0x49 */ + {0x0010, 0x8800}, + {0x000b, 0x8805}, + {0x0028, 0x8801}, /* 0x28 <- 0x0b */ + {0x0000, 0x8800}, + {0x000f, 0x8805}, + {0x003b, 0x8801}, /* 0x3b <- 0x0f */ + {0x0000, 0x8800}, + {0x0000, 0x8805}, + {0x003c, 0x8801}, /* 0x3c <- 0x00 */ + {0x0000, 0x8800}, + /***************/ + {0x0018, 0x8601}, /* Pixel/line selection for color separation */ + {0x0000, 0x8602}, /* Optical black level for user setting */ + {0x0060, 0x8604}, /* Optical black horizontal offset */ + {0x0002, 0x8605}, /* Optical black vertical offset */ + {0x0000, 0x8603}, /* Non-automatic optical black level */ + {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ + {0x0000, 0x865f}, /* Vertical valid pixels window (x2) */ + {0x00b0, 0x865d}, /* Horizontal valid pixels window (x2) */ + {0x0090, 0x865e}, /* Vertical valid lines window (x2) */ + {0x00e0, 0x8406}, /* Memory buffer threshold */ + {0x0000, 0x8660}, /* Compensation memory stuff */ + {0x0002, 0x8201}, /* Output address for r/w serial EEPROM */ + {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ + {0x0001, 0x8200}, /* OprMode to be executed by hardware */ + {0x0007, 0x8201}, /* Output address for r/w serial EEPROM */ + {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ + {0x0001, 0x8200}, /* OprMode to be executed by hardware */ + {0x0010, 0x8660}, /* Compensation memory stuff */ + {0x0018, 0x8660}, /* Compensation memory stuff */ + + {0x0004, 0x8611}, /* R offset for white balance */ + {0x0004, 0x8612}, /* Gr offset for white balance */ + {0x0007, 0x8613}, /* B offset for white balance */ + {0x0000, 0x8614}, /* Gb offset for white balance */ + {0x008c, 0x8651}, /* R gain for white balance */ + {0x008c, 0x8652}, /* Gr gain for white balance */ + {0x00b5, 0x8653}, /* B gain for white balance */ + {0x008c, 0x8654}, /* Gb gain for white balance */ + {0x0002, 0x8502}, /* Maximum average bit rate stuff */ + + {0x0011, 0x8802}, + {0x0087, 0x8700}, /* Set master clock (96Mhz????) */ + {0x0081, 0x8702}, /* Master clock output enable */ + + {0x0000, 0x8500}, /* Set image type (352x288 no compression) */ + /* Originally was 0x0010 (352x288 compression) */ + + {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ + {0x0003, 0x865c}, /* Vertical offset for valid lines */ + /***************//* sensor active */ + {0x0003, 0x8801}, /* 0x03 <- 0x01 0x21 //289 */ + {0x0021, 0x8805}, + {0x0001, 0x8800}, + {0x0004, 0x8801}, /* 0x04 <- 0x01 0x65 //357 */ + {0x0065, 0x8805}, + {0x0001, 0x8800}, + {0x0005, 0x8801}, /* 0x05 <- 0x2f */ + {0x002f, 0x8805}, + {0x0000, 0x8800}, + {0x0006, 0x8801}, /* 0x06 <- 0 */ + {0x0000, 0x8805}, + {0x0000, 0x8800}, + {0x000a, 0x8801}, /* 0x0a <- 2 */ + {0x0002, 0x8805}, + {0x0000, 0x8800}, + {0x0009, 0x8801}, /* 0x09 <- 0x1061 */ + {0x0061, 0x8805}, + {0x0010, 0x8800}, + {0x0035, 0x8801}, /* 0x35 <-0x14 */ + {0x0014, 0x8805}, + {0x0000, 0x8800}, + {0x0030, 0x8112}, /* ISO and drop packet enable */ + {0x0000, 0x8112}, /* Some kind of reset ???? */ + {0x0009, 0x8118}, /* Enable sensor and set standby */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0001, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8112}, /* Some kind of reset ??? */ + {0x0003, 0x8701}, + {0x0001, 0x8703}, + {0x0011, 0x8118}, + {0x0001, 0x8118}, + /***************/ + {0x0092, 0x8804}, + {0x0010, 0x8802}, + {0x000d, 0x8805}, + {0x0001, 0x8801}, + {0x0000, 0x8800}, + {0x0018, 0x8805}, + {0x0002, 0x8801}, + {0x0000, 0x8800}, + {0x0065, 0x8805}, + {0x0004, 0x8801}, + {0x0001, 0x8800}, + {0x0021, 0x8805}, + {0x0005, 0x8801}, + {0x0000, 0x8800}, + {0x00aa, 0x8805}, + {0x0007, 0x8801}, /* mode 0xaa */ + {0x0000, 0x8800}, + {0x0004, 0x8805}, + {0x0020, 0x8801}, + {0x0015, 0x8800}, /* mode 0x0415 */ + {0x0002, 0x8805}, + {0x0039, 0x8801}, + {0x0000, 0x8800}, + {0x0010, 0x8805}, + {0x0035, 0x8801}, + {0x0000, 0x8800}, + {0x0049, 0x8805}, + {0x0009, 0x8801}, + {0x0010, 0x8800}, + {0x000b, 0x8805}, + {0x0028, 0x8801}, + {0x0000, 0x8800}, + {0x000f, 0x8805}, + {0x003b, 0x8801}, + {0x0000, 0x8800}, + {0x0000, 0x8805}, + {0x003c, 0x8801}, + {0x0000, 0x8800}, + {0x0002, 0x8502}, + {0x0039, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + + {0x0087, 0x8700}, /* overwrite by start */ + {0x0081, 0x8702}, + {0x0000, 0x8500}, +/* {0x0010, 0x8500}, -- Previous line was this */ + {0x0002, 0x865b}, + {0x0003, 0x865c}, + /***************/ + {0x0003, 0x8801}, /* 0x121-> 289 */ + {0x0021, 0x8805}, + {0x0001, 0x8800}, + {0x0004, 0x8801}, /* 0x165 -> 357 */ + {0x0065, 0x8805}, + {0x0001, 0x8800}, + {0x0005, 0x8801}, /* 0x2f //blanking control colonne */ + {0x002f, 0x8805}, + {0x0000, 0x8800}, + {0x0006, 0x8801}, /* 0x00 //blanking mode row */ + {0x0000, 0x8805}, + {0x0000, 0x8800}, + {0x000a, 0x8801}, /* 0x01 //0x02 */ + {0x0001, 0x8805}, + {0x0000, 0x8800}, + {0x0009, 0x8801}, /* 0x1061 - setexposure times && pixel clock + * 0001 0 | 000 0110 0001 */ + {0x0061, 0x8805}, /* 61 31 */ + {0x0008, 0x8800}, /* 08 */ + {0x0035, 0x8801}, /* 0x14 - set gain general */ + {0x001f, 0x8805}, /* 0x14 */ + {0x0000, 0x8800}, + {0x0030, 0x8112}, + {} +}; + +static void sensor_reset(struct gspca_dev *gspca_dev) +{ + reg_w_val(gspca_dev->dev, 0x8631, 0xc8); + reg_w_val(gspca_dev->dev, 0x8634, 0xc8); + reg_w_val(gspca_dev->dev, 0x8112, 0x00); + reg_w_val(gspca_dev->dev, 0x8114, 0x00); + reg_w_val(gspca_dev->dev, 0x8118, 0x21); + i2c_init(gspca_dev, 0x14); + i2c_write(gspca_dev, 1, 0x0d); + i2c_write(gspca_dev, 0, 0x0d); +} + +/******************** QC Express etch2 stuff ********************/ +static const __u16 Pb100_1map8300[][2] = { + /* reg, value */ + {0x8320, 0x3304}, + + {0x8303, 0x0125}, /* image area */ + {0x8304, 0x0169}, + {0x8328, 0x000b}, + {0x833c, 0x0001}, + + {0x832f, 0x0419}, + {0x8307, 0x00aa}, + {0x8301, 0x0003}, + {0x8302, 0x000e}, + {} +}; +static const __u16 Pb100_2map8300[][2] = { + /* reg, value */ + {0x8339, 0x0000}, + {0x8307, 0x00aa}, + {} +}; + +static const __u16 spca561_161rev12A_data1[][2] = { + {0x21, 0x8118}, + {0x01, 0x8114}, + {0x00, 0x8112}, + {0x92, 0x8804}, + {0x04, 0x8802}, /* windows uses 08 */ + {} +}; +static const __u16 spca561_161rev12A_data2[][2] = { + {0x21, 0x8118}, + {0x10, 0x8500}, + {0x07, 0x8601}, + {0x07, 0x8602}, + {0x04, 0x8501}, + {0x21, 0x8118}, + + {0x07, 0x8201}, /* windows uses 02 */ + {0x08, 0x8200}, + {0x01, 0x8200}, + + {0x00, 0x8114}, + {0x01, 0x8114}, /* windows uses 00 */ + + {0x90, 0x8604}, + {0x00, 0x8605}, + {0xb0, 0x8603}, + + /* sensor gains */ + {0x00, 0x8610}, /* *red */ + {0x00, 0x8611}, /* 3f *green */ + {0x00, 0x8612}, /* green *blue */ + {0x00, 0x8613}, /* blue *green */ + {0x35, 0x8614}, /* green *red */ + {0x35, 0x8615}, /* 40 *green */ + {0x35, 0x8616}, /* 7a *blue */ + {0x35, 0x8617}, /* 40 *green */ + + {0x0c, 0x8620}, /* 0c */ + {0xc8, 0x8631}, /* c8 */ + {0xc8, 0x8634}, /* c8 */ + {0x23, 0x8635}, /* 23 */ + {0x1f, 0x8636}, /* 1f */ + {0xdd, 0x8637}, /* dd */ + {0xe1, 0x8638}, /* e1 */ + {0x1d, 0x8639}, /* 1d */ + {0x21, 0x863a}, /* 21 */ + {0xe3, 0x863b}, /* e3 */ + {0xdf, 0x863c}, /* df */ + {0xf0, 0x8505}, + {0x32, 0x850a}, + {} +}; + +static void sensor_mapwrite(struct gspca_dev *gspca_dev, + const __u16 sensormap[][2]) +{ + int i = 0; + __u8 usbval[2]; + + while (sensormap[i][0]) { + usbval[0] = sensormap[i][1]; + usbval[1] = sensormap[i][1] >> 8; + reg_w_buf(gspca_dev, sensormap[i][0], usbval, 2); + i++; + } +} +static void init_161rev12A(struct gspca_dev *gspca_dev) +{ + sensor_reset(gspca_dev); + write_vector(gspca_dev, spca561_161rev12A_data1); + sensor_mapwrite(gspca_dev, Pb100_1map8300); + write_vector(gspca_dev, spca561_161rev12A_data2); + sensor_mapwrite(gspca_dev, Pb100_2map8300); +} + +/* 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; + __u16 vendor, product; + __u8 data1, data2; + + /* Read frm global register the USB product and vendor IDs, just to + * prove that we can communicate with the device. This works, which + * confirms at we are communicating properly and that the device + * is a 561. */ + reg_r(gspca_dev, 0x8104, 1); + data1 = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8105, 1); + data2 = gspca_dev->usb_buf[0]; + vendor = (data2 << 8) | data1; + reg_r(gspca_dev, 0x8106, 1); + data1 = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8107, 1); + data2 = gspca_dev->usb_buf[0]; + product = (data2 << 8) | data1; + if (vendor != id->idVendor || product != id->idProduct) { + PDEBUG(D_PROBE, "Bad vendor / product from device"); + return -EINVAL; + } + switch (product) { + case 0x0928: + case 0x0929: + case 0x092a: + case 0x092b: + case 0x092c: + case 0x092d: + case 0x092e: + case 0x092f: + case 0x403b: + sd->chip_revision = Rev012A; + break; + default: +/* case 0x0561: + case 0x0815: * ?? in spca508.c + case 0x401a: + case 0x7004: + case 0x7e50: + case 0xa001: + case 0xcdee: */ + sd->chip_revision = Rev072A; + break; + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + gspca_dev->nbalt = 7 + 1; /* choose alternate 7 first */ + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->chip_revision) { + case Rev072A: + PDEBUG(D_STREAM, "Chip revision id: 072a"); + write_vector(gspca_dev, spca561_init_data); + break; + default: +/* case Rev012A: */ + PDEBUG(D_STREAM, "Chip revision id: 012a"); + init_161rev12A(gspca_dev); + break; + } + return 0; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 lowb; + int expotimes; + + switch (sd->chip_revision) { + case Rev072A: + lowb = sd->contrast >> 8; + reg_w_val(dev, lowb, 0x8651); + reg_w_val(dev, lowb, 0x8652); + reg_w_val(dev, lowb, 0x8653); + reg_w_val(dev, lowb, 0x8654); + break; + case Rev012A: { + __u8 Reg8391[] = + { 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00 }; + + /* Write camera sensor settings */ + expotimes = (sd->contrast >> 5) & 0x07ff; + Reg8391[0] = expotimes & 0xff; /* exposure */ + Reg8391[1] = 0x18 | (expotimes >> 8); + Reg8391[2] = sd->brightness; /* gain */ + reg_w_buf(gspca_dev, 0x8391, Reg8391, 8); + reg_w_buf(gspca_dev, 0x8390, Reg8391, 8); + break; + } + } +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int Clck; + __u8 Reg8307[] = { 0xaa, 0x00 }; + int mode; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + switch (sd->chip_revision) { + case Rev072A: + switch (mode) { + default: +/* case 0: + case 1: */ + Clck = 0x25; + break; + case 2: + Clck = 0x22; + break; + case 3: + Clck = 0x21; + break; + } + reg_w_val(dev, 0x8500, mode); /* mode */ + reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ + reg_w_val(dev, 0x8112, 0x10 | 0x20); + break; + default: +/* case Rev012A: */ + switch (mode) { + case 0: + case 1: + Clck = 0x8a; + break; + case 2: + Clck = 0x85; + break; + default: + Clck = 0x83; + break; + } + if (mode <= 1) { + /* Use compression on 320x240 and above */ + reg_w_val(dev, 0x8500, 0x10 | mode); + } else { + /* I couldn't get the compression to work below 320x240 + * Fortunately at these resolutions the bandwidth + * is sufficient to push raw frames at ~20fps */ + reg_w_val(dev, 0x8500, mode); + } /* -- qq@kuku.eu.org */ + reg_w_buf(gspca_dev, 0x8307, Reg8307, 2); + reg_w_val(gspca_dev->dev, 0x8700, Clck); + /* 0x8f 0x85 0x27 clock */ + reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20); + reg_w_val(gspca_dev->dev, 0x850b, 0x03); + setcontrast(gspca_dev); + break; + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w_val(gspca_dev->dev, 0x8112, 0x20); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + reg_w_val(gspca_dev->dev, 0x8114, 0); +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int expotimes = 0; + int pixelclk = 0; + int gainG = 0; + __u8 R, Gr, Gb, B; + int y; + __u8 luma_mean = 110; + __u8 luma_delta = 20; + __u8 spring = 4; + + switch (sd->chip_revision) { + case Rev072A: + reg_r(gspca_dev, 0x8621, 1); + Gr = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8622, 1); + R = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8623, 1); + B = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8624, 1); + Gb = gspca_dev->usb_buf[0]; + y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8; + /* u= (128*B-(43*(Gr+Gb+R))) >> 8; */ + /* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */ + /* PDEBUG(D_CONF,"reading Y %d U %d V %d ",y,u,v); */ + + if (y < luma_mean - luma_delta || + y > luma_mean + luma_delta) { + expotimes = i2c_read(gspca_dev, 0x09, 0x10); + pixelclk = 0x0800; + expotimes = expotimes & 0x07ff; + /* PDEBUG(D_PACK, + "Exposition Times 0x%03X Clock 0x%04X ", + expotimes,pixelclk); */ + gainG = i2c_read(gspca_dev, 0x35, 0x10); + /* PDEBUG(D_PACK, + "reading Gain register %d", gainG); */ + + expotimes += (luma_mean - y) >> spring; + gainG += (luma_mean - y) / 50; + /* PDEBUG(D_PACK, + "compute expotimes %d gain %d", + expotimes,gainG); */ + + if (gainG > 0x3f) + gainG = 0x3f; + else if (gainG < 4) + gainG = 3; + i2c_write(gspca_dev, gainG, 0x35); + + if (expotimes >= 0x0256) + expotimes = 0x0256; + else if (expotimes < 4) + expotimes = 3; + i2c_write(gspca_dev, expotimes | pixelclk, 0x09); + } + break; + case Rev012A: + /* sensor registers is access and memory mapped to 0x8300 */ + /* readind all 0x83xx block the sensor */ + /* + * The data from the header seem wrong where is the luma + * and chroma mean value + * at the moment set exposure in contrast set + */ + break; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + if (sd->ag_cnt >= 0) { + if (--sd->ag_cnt < 0) { + sd->ag_cnt = AG_CNT_START; + setautogain(gspca_dev); + } + } + data += SPCA561_OFFSET_DATA; + len -= SPCA561_OFFSET_DATA; + if (data[1] & 0x10) { + /* compressed bayer */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, len); + } else { + /* raw bayer (with a header, which we skip) */ + data += 20; + len -= 20; + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, len); + } + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data++; + len--; + 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; + __u8 value; + + switch (sd->chip_revision) { + case Rev072A: + value = sd->brightness; + reg_w_val(gspca_dev->dev, value, 0x8611); + reg_w_val(gspca_dev->dev, value, 0x8612); + reg_w_val(gspca_dev->dev, value, 0x8613); + reg_w_val(gspca_dev->dev, value, 0x8614); + break; + default: +/* case Rev012A: */ + setcontrast(gspca_dev); + break; + } +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u16 tot; + + switch (sd->chip_revision) { + case Rev072A: + tot = 0; + reg_r(gspca_dev, 0x8611, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8612, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8613, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8614, 1); + tot += gspca_dev->usb_buf[0]; + sd->brightness = tot >> 2; + break; + default: +/* case Rev012A: */ + /* no way to read sensor settings */ + break; + } +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u16 tot; + + switch (sd->chip_revision) { + case Rev072A: + tot = 0; + reg_r(gspca_dev, 0x8651, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8652, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8653, 1); + tot += gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0x8654, 1); + tot += gspca_dev->usb_buf[0]; + sd->contrast = tot << 6; + break; + default: +/* case Rev012A: */ + /* no way to read sensor settings */ + break; + } + PDEBUG(D_CONF, "get contrast %d", sd->contrast); +} + +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; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x401a), DVNM("Creative Webcam Vista (PD1100)")}, + {USB_DEVICE(0x041e, 0x403b), DVNM("Creative Webcam Vista (VF0010)")}, + {USB_DEVICE(0x0458, 0x7004), DVNM("Genius VideoCAM Express V2")}, + {USB_DEVICE(0x046d, 0x0928), DVNM("Logitech QC Express Etch2")}, + {USB_DEVICE(0x046d, 0x0929), DVNM("Labtec Webcam Elch2")}, + {USB_DEVICE(0x046d, 0x092a), DVNM("Logitech QC for Notebook")}, + {USB_DEVICE(0x046d, 0x092b), DVNM("Labtec Webcam Plus")}, + {USB_DEVICE(0x046d, 0x092c), DVNM("Logitech QC chat Elch2")}, + {USB_DEVICE(0x046d, 0x092d), DVNM("Logitech QC Elch2")}, + {USB_DEVICE(0x046d, 0x092e), DVNM("Logitech QC Elch2")}, + {USB_DEVICE(0x046d, 0x092f), DVNM("Logitech QC Elch2")}, + {USB_DEVICE(0x04fc, 0x0561), DVNM("Flexcam 100")}, + {USB_DEVICE(0x060b, 0xa001), DVNM("Maxell Compact Pc PM3")}, + {USB_DEVICE(0x10fd, 0x7e50), DVNM("FlyCam Usb 100")}, + {USB_DEVICE(0xabcd, 0xcdee), DVNM("Petcam")}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c new file mode 100644 index 00000000000..c78ee0d3e59 --- /dev/null +++ b/drivers/media/video/gspca/stk014.c @@ -0,0 +1,592 @@ +/* + * Syntek DV4000 (STK014) subdriver + * + * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "stk014" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); +MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char lightfreq; +}; + +/* global parameters */ +static int sd_quant = 7; /* <= 4 KO - 7: good (enough!) */ + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 127 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, +#define CONTRAST_DEF 127 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 255, + .step = 1, +#define COLOR_DEF 127 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 1, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 1 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* -- read a register -- */ +static int reg_r(struct gspca_dev *gspca_dev, + __u16 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, + index, + gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_r err %d", ret); + return ret; + } + return gspca_dev->usb_buf[0]; +} + +/* -- write a register -- */ +static int reg_w(struct gspca_dev *gspca_dev, + __u16 index, __u16 value) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + NULL, + 0, + 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w err %d", ret); + return ret; +} + +/* -- get a bulk value (4 bytes) -- */ +static int rcv_val(struct gspca_dev *gspca_dev, + int ads) +{ + struct usb_device *dev = gspca_dev->dev; + int alen, ret; + + reg_w(gspca_dev, 0x634, (ads >> 16) & 0xff); + reg_w(gspca_dev, 0x635, (ads >> 8) & 0xff); + reg_w(gspca_dev, 0x636, ads & 0xff); + reg_w(gspca_dev, 0x637, 0); + reg_w(gspca_dev, 0x638, 4); /* len & 0xff */ + reg_w(gspca_dev, 0x639, 0); /* len >> 8 */ + reg_w(gspca_dev, 0x63a, 0); + reg_w(gspca_dev, 0x63b, 0); + reg_w(gspca_dev, 0x630, 5); + ret = usb_bulk_msg(dev, + usb_rcvbulkpipe(dev, 5), + gspca_dev->usb_buf, + 4, /* length */ + &alen, + 500); /* timeout in milliseconds */ + return ret; +} + +/* -- send a bulk value -- */ +static int snd_val(struct gspca_dev *gspca_dev, + int ads, + unsigned int val) +{ + struct usb_device *dev = gspca_dev->dev; + int alen, ret; + __u8 seq = 0; + + if (ads == 0x003f08) { + ret = reg_r(gspca_dev, 0x0704); + if (ret < 0) + goto ko; + ret = reg_r(gspca_dev, 0x0705); + if (ret < 0) + goto ko; + seq = ret; /* keep the sequence number */ + ret = reg_r(gspca_dev, 0x0650); + if (ret < 0) + goto ko; + reg_w(gspca_dev, 0x654, seq); + } else { + reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff); + } + reg_w(gspca_dev, 0x655, (ads >> 8) & 0xff); + reg_w(gspca_dev, 0x656, ads & 0xff); + reg_w(gspca_dev, 0x657, 0); + reg_w(gspca_dev, 0x658, 0x04); /* size */ + reg_w(gspca_dev, 0x659, 0); + reg_w(gspca_dev, 0x65a, 0); + reg_w(gspca_dev, 0x65b, 0); + reg_w(gspca_dev, 0x650, 5); + gspca_dev->usb_buf[0] = val >> 24; + gspca_dev->usb_buf[1] = val >> 16; + gspca_dev->usb_buf[2] = val >> 8; + gspca_dev->usb_buf[3] = val; + ret = usb_bulk_msg(dev, + usb_sndbulkpipe(dev, 6), + gspca_dev->usb_buf, + 4, + &alen, + 500); /* timeout in milliseconds */ + if (ret < 0) + goto ko; + if (ads == 0x003f08) { + seq += 4; + seq &= 0x3f; + reg_w(gspca_dev, 0x705, seq); + } + return ret; +ko: + PDEBUG(D_ERR, "snd_val err %d", ret); + return ret; +} + +/* set a camera parameter */ +static int set_par(struct gspca_dev *gspca_dev, + int parval) +{ + return snd_val(gspca_dev, 0x003f08, parval); +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int parval; + + parval = 0x06000000 /* whiteness */ + + (sd->brightness << 16); + set_par(gspca_dev, parval); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int parval; + + parval = 0x07000000 /* contrast */ + + (sd->contrast << 16); + set_par(gspca_dev, parval); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int parval; + + parval = 0x08000000 /* saturation */ + + (sd->colors << 16); + set_par(gspca_dev, parval); +} + +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + set_par(gspca_dev, sd->lightfreq == 1 + ? 0x33640000 /* 50 Hz */ + : 0x33780000); /* 60 Hz */ +} + +/* 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 = &gspca_dev->cam; + + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x02; + gspca_dev->cam.cam_mode = vga_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + sd->lightfreq = FREQ_DEF; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + int ret; + + /* check if the device responds */ + usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); + ret = reg_r(gspca_dev, 0x0740); + if (ret < 0) + return ret; + if (ret != 0xff) { + PDEBUG(D_ERR|D_STREAM, "init reg: 0x%02x", ret); + return -1; + } + return 0; +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + int ret, value; + + /* work on alternate 1 */ + usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); + + set_par(gspca_dev, 0x10000000); + set_par(gspca_dev, 0x00000000); + set_par(gspca_dev, 0x8002e001); + set_par(gspca_dev, 0x14000000); + if (gspca_dev->width > 320) + value = 0x8002e001; /* 640x480 */ + else + value = 0x4001f000; /* 320x240 */ + set_par(gspca_dev, value); + ret = usb_set_interface(gspca_dev->dev, + gspca_dev->iface, + gspca_dev->alt); + if (ret < 0) { + PDEBUG(D_ERR|D_STREAM, "set intf %d %d failed", + gspca_dev->iface, gspca_dev->alt); + goto out; + } + ret = reg_r(gspca_dev, 0x0630); + if (ret < 0) + goto out; + rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */ + ret = reg_r(gspca_dev, 0x0650); + if (ret < 0) + goto out; + snd_val(gspca_dev, 0x000020, 0xffffffff); + reg_w(gspca_dev, 0x0620, 0); + reg_w(gspca_dev, 0x0630, 0); + reg_w(gspca_dev, 0x0640, 0); + reg_w(gspca_dev, 0x0650, 0); + reg_w(gspca_dev, 0x0660, 0); + setbrightness(gspca_dev); /* whiteness */ + setcontrast(gspca_dev); /* contrast */ + setcolors(gspca_dev); /* saturation */ + set_par(gspca_dev, 0x09800000); /* Red ? */ + set_par(gspca_dev, 0x0a800000); /* Green ? */ + set_par(gspca_dev, 0x0b800000); /* Blue ? */ + set_par(gspca_dev, 0x0d030000); /* Gamma ? */ + setfreq(gspca_dev); /* light frequency */ + + /* start the video flow */ + set_par(gspca_dev, 0x01000000); + set_par(gspca_dev, 0x01000000); + PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt); + return; +out: + PDEBUG(D_ERR|D_STREAM, "camera start err %d", ret); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + set_par(gspca_dev, 0x02000000); + set_par(gspca_dev, 0x02000000); + usb_set_interface(dev, gspca_dev->iface, 1); + reg_r(gspca_dev, 0x0630); + rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */ + reg_r(gspca_dev, 0x0650); + snd_val(gspca_dev, 0x000020, 0xffffffff); + reg_w(gspca_dev, 0x0620, 0); + reg_w(gspca_dev, 0x0630, 0); + reg_w(gspca_dev, 0x0640, 0); + reg_w(gspca_dev, 0x0650, 0); + reg_w(gspca_dev, 0x0660, 0); + PDEBUG(D_STREAM, "camera stopped"); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + static unsigned char ffd9[] = {0xff, 0xd9}; + + /* a frame starts with: + * - 0xff 0xfe + * - 0x08 0x00 - length (little endian ?!) + * - 4 bytes = size of whole frame (BE - including header) + * - 0x00 0x0c + * - 0xff 0xd8 + * - .. JPEG image with escape sequences (ff 00) + * (without ending - ff d9) + */ + if (data[0] == 0xff && data[1] == 0xfe) { + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + ffd9, 2); + + /* put the JPEG 411 header */ + jpeg_put_header(gspca_dev, frame, sd_quant, 0x22); + + /* beginning of the frame */ +#define STKHDRSZ 12 + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data + STKHDRSZ, len - STKHDRSZ); +#undef STKHDRSZ + return; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +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_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x05e1, 0x0893), DVNM("Syntek DV4000")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + info("v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); + +module_param_named(quant, sd_quant, int, 0644); +MODULE_PARM_DESC(quant, "Quantization index (0..8)"); diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c new file mode 100644 index 00000000000..abd7bef9b3d --- /dev/null +++ b/drivers/media/video/gspca/sunplus.c @@ -0,0 +1,1677 @@ +/* + * Sunplus spca504(abc) spca533 spca536 library + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "sunplus" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 8) +static const char version[] = "2.1.8"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + __u8 packet[ISO_MAX_SIZE + 128]; + /* !! no more than 128 ff in an ISO packet */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + char qindex; + char bridge; +#define BRIDGE_SPCA504 0 +#define BRIDGE_SPCA504B 1 +#define BRIDGE_SPCA504C 2 +#define BRIDGE_SPCA533 3 +#define BRIDGE_SPCA536 4 + char subtype; +#define AiptekMiniPenCam13 1 +#define LogitechClickSmart420 2 +#define LogitechClickSmart820 3 +#define MegapixV4 4 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x20, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x1a, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_AUTOGAIN 3 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +}; + +static struct v4l2_pix_format custom_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {464, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 464, + .sizeimage = 464 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +}; + +static struct v4l2_pix_format vga_mode2[] = { + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 4}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +}; + +#define SPCA50X_OFFSET_DATA 10 +#define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3 +#define SPCA504_PCCAM600_OFFSET_COMPRESS 4 +#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_FRAMSEQ 15 +/* Frame packet header offsets for the spca536 */ +#define SPCA536_OFFSET_DATA 4 +#define SPCA536_OFFSET_FRAMSEQ 1 + +/* Initialisation data for the Creative PC-CAM 600 */ +static const __u16 spca504_pccam600_init_data[][3] = { +/* {0xa0, 0x0000, 0x0503}, * capture mode */ + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0001, 0x21ac}, + {0x00, 0x0001, 0x21a6}, + {0x00, 0x0000, 0x21a7}, /* brightness */ + {0x00, 0x0020, 0x21a8}, /* contrast */ + {0x00, 0x0001, 0x21ac}, /* sat/hue */ + {0x00, 0x0000, 0x21ad}, /* hue */ + {0x00, 0x001a, 0x21ae}, /* saturation */ + {0x00, 0x0002, 0x21a3}, /* gamma */ + {0x30, 0x0154, 0x0008}, + {0x30, 0x0004, 0x0006}, + {0x30, 0x0258, 0x0009}, + {0x30, 0x0004, 0x0000}, + {0x30, 0x0093, 0x0004}, + {0x30, 0x0066, 0x0005}, + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {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] = { + {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] = { +/* {0xa0, 0x0000, 0x0503}, * capture mode */ + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0001, 0x21ac}, + {0x00, 0x0001, 0x21a6}, + {0x00, 0x0000, 0x21a7}, /* brightness */ + {0x00, 0x0020, 0x21a8}, /* contrast */ + {0x00, 0x0001, 0x21ac}, /* sat/hue */ + {0x00, 0x0000, 0x21ad}, /* hue */ + {0x00, 0x001a, 0x21ae}, /* saturation */ + {0x00, 0x0002, 0x21a3}, /* gamma */ + {0x30, 0x0004, 0x000a}, + {0xb0, 0x0001, 0x0000}, + + + {0x0a1, 0x0080, 0x0001}, + {0x30, 0x0049, 0x0000}, + {0x30, 0x0060, 0x0005}, + {0x0c, 0x0004, 0x0000}, + {0x00, 0x0000, 0x0000}, + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0000, 0x2000}, + + {} +}; + +/* clicksmart 420 open data ? */ +static const __u16 spca504A_clicksmart420_open_data[][3] = { + {0x00, 0x0001, 0x2501}, + {0x20, 0x0502, 0x0000}, + {0x06, 0x0000, 0x0000}, + {0x00, 0x0004, 0x2880}, + {0x00, 0x0001, 0x2881}, +/* look like setting a qTable */ + {0x00, 0x0006, 0x2800}, + {0x00, 0x0004, 0x2801}, + {0x00, 0x0004, 0x2802}, + {0x00, 0x0006, 0x2803}, + {0x00, 0x000a, 0x2804}, + {0x00, 0x0010, 0x2805}, + {0x00, 0x0014, 0x2806}, + {0x00, 0x0018, 0x2807}, + {0x00, 0x0005, 0x2808}, + {0x00, 0x0005, 0x2809}, + {0x00, 0x0006, 0x280a}, + {0x00, 0x0008, 0x280b}, + {0x00, 0x000a, 0x280c}, + {0x00, 0x0017, 0x280d}, + {0x00, 0x0018, 0x280e}, + {0x00, 0x0016, 0x280f}, + + {0x00, 0x0006, 0x2810}, + {0x00, 0x0005, 0x2811}, + {0x00, 0x0006, 0x2812}, + {0x00, 0x000a, 0x2813}, + {0x00, 0x0010, 0x2814}, + {0x00, 0x0017, 0x2815}, + {0x00, 0x001c, 0x2816}, + {0x00, 0x0016, 0x2817}, + {0x00, 0x0006, 0x2818}, + {0x00, 0x0007, 0x2819}, + {0x00, 0x0009, 0x281a}, + {0x00, 0x000c, 0x281b}, + {0x00, 0x0014, 0x281c}, + {0x00, 0x0023, 0x281d}, + {0x00, 0x0020, 0x281e}, + {0x00, 0x0019, 0x281f}, + + {0x00, 0x0007, 0x2820}, + {0x00, 0x0009, 0x2821}, + {0x00, 0x000f, 0x2822}, + {0x00, 0x0016, 0x2823}, + {0x00, 0x001b, 0x2824}, + {0x00, 0x002c, 0x2825}, + {0x00, 0x0029, 0x2826}, + {0x00, 0x001f, 0x2827}, + {0x00, 0x000a, 0x2828}, + {0x00, 0x000e, 0x2829}, + {0x00, 0x0016, 0x282a}, + {0x00, 0x001a, 0x282b}, + {0x00, 0x0020, 0x282c}, + {0x00, 0x002a, 0x282d}, + {0x00, 0x002d, 0x282e}, + {0x00, 0x0025, 0x282f}, + + {0x00, 0x0014, 0x2830}, + {0x00, 0x001a, 0x2831}, + {0x00, 0x001f, 0x2832}, + {0x00, 0x0023, 0x2833}, + {0x00, 0x0029, 0x2834}, + {0x00, 0x0030, 0x2835}, + {0x00, 0x0030, 0x2836}, + {0x00, 0x0028, 0x2837}, + {0x00, 0x001d, 0x2838}, + {0x00, 0x0025, 0x2839}, + {0x00, 0x0026, 0x283a}, + {0x00, 0x0027, 0x283b}, + {0x00, 0x002d, 0x283c}, + {0x00, 0x0028, 0x283d}, + {0x00, 0x0029, 0x283e}, + {0x00, 0x0028, 0x283f}, + + {0x00, 0x0007, 0x2840}, + {0x00, 0x0007, 0x2841}, + {0x00, 0x000a, 0x2842}, + {0x00, 0x0013, 0x2843}, + {0x00, 0x0028, 0x2844}, + {0x00, 0x0028, 0x2845}, + {0x00, 0x0028, 0x2846}, + {0x00, 0x0028, 0x2847}, + {0x00, 0x0007, 0x2848}, + {0x00, 0x0008, 0x2849}, + {0x00, 0x000a, 0x284a}, + {0x00, 0x001a, 0x284b}, + {0x00, 0x0028, 0x284c}, + {0x00, 0x0028, 0x284d}, + {0x00, 0x0028, 0x284e}, + {0x00, 0x0028, 0x284f}, + + {0x00, 0x000a, 0x2850}, + {0x00, 0x000a, 0x2851}, + {0x00, 0x0016, 0x2852}, + {0x00, 0x0028, 0x2853}, + {0x00, 0x0028, 0x2854}, + {0x00, 0x0028, 0x2855}, + {0x00, 0x0028, 0x2856}, + {0x00, 0x0028, 0x2857}, + {0x00, 0x0013, 0x2858}, + {0x00, 0x001a, 0x2859}, + {0x00, 0x0028, 0x285a}, + {0x00, 0x0028, 0x285b}, + {0x00, 0x0028, 0x285c}, + {0x00, 0x0028, 0x285d}, + {0x00, 0x0028, 0x285e}, + {0x00, 0x0028, 0x285f}, + + {0x00, 0x0028, 0x2860}, + {0x00, 0x0028, 0x2861}, + {0x00, 0x0028, 0x2862}, + {0x00, 0x0028, 0x2863}, + {0x00, 0x0028, 0x2864}, + {0x00, 0x0028, 0x2865}, + {0x00, 0x0028, 0x2866}, + {0x00, 0x0028, 0x2867}, + {0x00, 0x0028, 0x2868}, + {0x00, 0x0028, 0x2869}, + {0x00, 0x0028, 0x286a}, + {0x00, 0x0028, 0x286b}, + {0x00, 0x0028, 0x286c}, + {0x00, 0x0028, 0x286d}, + {0x00, 0x0028, 0x286e}, + {0x00, 0x0028, 0x286f}, + + {0x00, 0x0028, 0x2870}, + {0x00, 0x0028, 0x2871}, + {0x00, 0x0028, 0x2872}, + {0x00, 0x0028, 0x2873}, + {0x00, 0x0028, 0x2874}, + {0x00, 0x0028, 0x2875}, + {0x00, 0x0028, 0x2876}, + {0x00, 0x0028, 0x2877}, + {0x00, 0x0028, 0x2878}, + {0x00, 0x0028, 0x2879}, + {0x00, 0x0028, 0x287a}, + {0x00, 0x0028, 0x287b}, + {0x00, 0x0028, 0x287c}, + {0x00, 0x0028, 0x287d}, + {0x00, 0x0028, 0x287e}, + {0x00, 0x0028, 0x287f}, + + {0xa0, 0x0000, 0x0503}, + {} +}; + +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, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +/* FIXME: This Q-table is identical to the Creative PC-CAM one, + * except for one byte. Possibly a typo? + * NWG: 18/05/2003. + */ +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, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e, + }, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +static void reg_r(struct usb_device *dev, + __u16 req, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, buffer, length, + 500); +} + +/* write req / index / value */ +static int reg_w_riv(struct usb_device *dev, + __u16 req, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d", + req, index, value, ret); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +/* read 1 byte */ +static int reg_r_1(struct gspca_dev *gspca_dev, + __u16 value) /* wValue */ +{ + int ret; + + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0x20, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + 0, /* index */ + gspca_dev->usb_buf, 1, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_r_1 err %d", ret); + return 0; + } + return gspca_dev->usb_buf[0]; +} + +/* 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) */ +{ + int ret; + + gspca_dev->usb_buf[1] = 0; + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + gspca_dev->usb_buf, length, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_read err %d", ret); + return -1; + } + return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; +} + +static int write_vector(struct gspca_dev *gspca_dev, + const __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + 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]); + 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]); + return ret; + } + i++; + } + 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]) +{ + 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]); + 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]); + if (err < 0) + return err; + } + return 0; +} + +static void spca504_acknowledged_command(struct gspca_dev *gspca_dev, + __u16 req, __u16 idx, __u16 val) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 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); + + msleep(200); + notdone = reg_r_12(gspca_dev, 0x01, 0x0001, 1); + PDEBUG(D_FRAM, "after wait 0x%x", notdone); +} + +static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, + __u16 req, + __u16 idx, __u16 val, __u8 stat, __u8 count) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 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); + if (!count) + return; + count = 200; + while (--count > 0) { + msleep(10); + /* gsmart mini2 write a each wait setting 1 ms is enought */ +/* 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", + status, 200 - count); + break; + } + } +} + +static int spca504B_PollingDataReady(struct gspca_dev *gspca_dev) +{ + int count = 10; + + while (--count > 0) { + reg_r(gspca_dev->dev, 0x21, 0, gspca_dev->usb_buf, 1); + if ((gspca_dev->usb_buf[0] & 0x01) == 0) + break; + msleep(10); + } + return gspca_dev->usb_buf[0]; +} + +static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int count = 50; + + while (--count > 0) { + reg_r(dev, 0x21, 1, gspca_dev->usb_buf, 1); + if (gspca_dev->usb_buf[0] != 0) { + gspca_dev->usb_buf[0] = 0; + reg_w(dev, 0x21, 0, 1, gspca_dev->usb_buf, 1); + reg_r(dev, 0x21, 1, gspca_dev->usb_buf, 1); + spca504B_PollingDataReady(gspca_dev); + break; + } + msleep(10); + } +} + +static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 *data; + + data = kmalloc(64, GFP_KERNEL); + reg_r(dev, 0x20, 0, data, 5); + PDEBUG(D_STREAM, "FirmWare : %d %d %d %d %d ", + data[0], data[1], data[2], data[3], data[4]); + reg_r(dev, 0x23, 0, data, 64); + reg_r(dev, 0x23, 1, data, 64); + kfree(data); +} + +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; + int rc; + + Size = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + Type = 0; + switch (sd->bridge) { + case BRIDGE_SPCA533: + reg_w(dev, 0x31, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + rc = spca504B_PollingDataReady(gspca_dev); + spca50x_GetFirmware(gspca_dev); + gspca_dev->usb_buf[0] = 2; /* type */ + reg_w(dev, 0x24, 0, 8, gspca_dev->usb_buf, 1); + reg_r(dev, 0x24, 8, gspca_dev->usb_buf, 1); + + gspca_dev->usb_buf[0] = Size; + reg_w(dev, 0x25, 0, 4, gspca_dev->usb_buf, 1); + reg_r(dev, 0x25, 4, gspca_dev->usb_buf, 1); /* size */ + rc = spca504B_PollingDataReady(gspca_dev); + + /* Init the cam width height with some values get on init ? */ + reg_w(dev, 0x31, 0, 4, NULL, 0); + 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(dev, 0x25, 0, 4, gspca_dev->usb_buf, 1); + reg_r(dev, 0x25, 4, gspca_dev->usb_buf, 1); /* size */ + Type = 6; + gspca_dev->usb_buf[0] = Type; + reg_w(dev, 0x27, 0, 0, gspca_dev->usb_buf, 1); + reg_r(dev, 0x27, 0, gspca_dev->usb_buf, 1); /* type */ + rc = spca504B_PollingDataReady(gspca_dev); + break; + case BRIDGE_SPCA504: + Size += 3; + if (sd->subtype == AiptekMiniPenCam13) { + /* spca504a aiptek */ + spca504A_acknowledged_command(gspca_dev, + 0x08, Size, 0, + 0x80 | (Size & 0x0f), 1); + spca504A_acknowledged_command(gspca_dev, + 1, 3, 0, 0x9f, 0); + } else { + spca504_acknowledged_command(gspca_dev, 0x08, Size, 0); + } + break; + case BRIDGE_SPCA504C: + /* capture mode */ + reg_w_riv(dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00); + reg_w_riv(dev, 0x20, 0x01, 0x0500 | (Size & 0x0f)); + break; + } +} + +static void spca504_wait_status(struct gspca_dev *gspca_dev) +{ + int cnt; + + cnt = 256; + while (--cnt > 0) { + /* With this we get the status, when return 0 it's all ok */ + if (reg_r_12(gspca_dev, 0x06, 0x00, 1) == 0) + return; + msleep(10); + } +} + +static void spca504B_setQtable(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + gspca_dev->usb_buf[0] = 3; + reg_w(dev, 0x26, 0, 0, gspca_dev->usb_buf, 1); + reg_r(dev, 0x26, 0, gspca_dev->usb_buf, 1); + spca504B_PollingDataReady(gspca_dev); +} + +static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int pollreg = 1; + + switch (sd->bridge) { + case BRIDGE_SPCA504: + case BRIDGE_SPCA504C: + pollreg = 0; + /* fall thru */ + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ + reg_w(dev, 0, 0, 0x21a7, NULL, 0); /* brightness */ + reg_w(dev, 0, 0x20, 0x21a8, NULL, 0); /* contrast */ + reg_w(dev, 0, 0, 0x21ad, NULL, 0); /* hue */ + reg_w(dev, 0, 1, 0x21ac, NULL, 0); /* sat/hue */ + reg_w(dev, 0, 0x20, 0x21ae, NULL, 0); /* saturation */ + reg_w(dev, 0, 0, 0x21a3, NULL, 0); /* gamma */ + break; + case BRIDGE_SPCA536: + reg_w(dev, 0, 0, 0x20f0, NULL, 0); + reg_w(dev, 0, 0x21, 0x20f1, NULL, 0); + reg_w(dev, 0, 0x40, 0x20f5, NULL, 0); + reg_w(dev, 0, 1, 0x20f4, NULL, 0); + reg_w(dev, 0, 0x40, 0x20f6, NULL, 0); + reg_w(dev, 0, 0, 0x2089, NULL, 0); + break; + } + if (pollreg) + spca504B_PollingDataReady(gspca_dev); +} + +/* 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 usb_device *dev = gspca_dev->dev; + struct cam *cam; + __u16 vendor; + __u16 product; + __u8 fw; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x400b: */ +/* case 0x4012: */ +/* case 0x4013: */ +/* sd->bridge = BRIDGE_SPCA504C; */ +/* break; */ +/* } */ + break; + case 0x0458: /* Genius KYE cameras */ +/* switch (product) { */ +/* case 0x7006: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x0461: /* MicroInnovation */ +/* switch (product) { */ +/* case 0x0821: */ + sd->bridge = BRIDGE_SPCA533; +/* break; */ +/* } */ + break; + case 0x046d: /* Logitech Labtec */ + switch (product) { + case 0x0905: + sd->subtype = LogitechClickSmart820; + sd->bridge = BRIDGE_SPCA533; + break; + case 0x0960: + sd->subtype = LogitechClickSmart420; + sd->bridge = BRIDGE_SPCA504C; + break; + } + break; + case 0x0471: /* Philips */ +/* switch (product) { */ +/* case 0x0322: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x04a5: /* Benq */ + switch (product) { + case 0x3003: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x3008: + case 0x300a: + sd->bridge = BRIDGE_SPCA533; + break; + } + break; + case 0x04f1: /* JVC */ +/* switch (product) { */ +/* case 0x1001: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x04fc: /* SunPlus */ + switch (product) { + case 0x500c: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x504a: +/* try to get the firmware as some cam answer 2.0.1.2.2 + * and should be a spca504b then overwrite that setting */ + reg_r(dev, 0x20, 0, gspca_dev->usb_buf, 1); + fw = gspca_dev->usb_buf[0]; + if (fw == 1) { + sd->subtype = AiptekMiniPenCam13; + sd->bridge = BRIDGE_SPCA504; + } else if (fw == 2) { + sd->bridge = BRIDGE_SPCA504B; + } else + return -ENODEV; + break; + case 0x504b: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x5330: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x5360: + sd->bridge = BRIDGE_SPCA536; + break; + case 0xffff: + sd->bridge = BRIDGE_SPCA504B; + break; + } + break; + case 0x052b: /* ?? Megapix */ +/* switch (product) { */ +/* case 0x1513: */ + sd->subtype = MegapixV4; + sd->bridge = BRIDGE_SPCA533; +/* break; */ +/* } */ + break; + case 0x0546: /* Polaroid */ + switch (product) { + case 0x3155: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x3191: + case 0x3273: + sd->bridge = BRIDGE_SPCA504B; + break; + } + break; + case 0x055f: /* Mustek cameras */ + switch (product) { + case 0xc211: + sd->bridge = BRIDGE_SPCA536; + break; + case 0xc230: + case 0xc232: + sd->bridge = BRIDGE_SPCA533; + break; + case 0xc360: + sd->bridge = BRIDGE_SPCA536; + break; + case 0xc420: + sd->bridge = BRIDGE_SPCA504; + break; + case 0xc430: + case 0xc440: + sd->bridge = BRIDGE_SPCA533; + break; + case 0xc520: + sd->bridge = BRIDGE_SPCA504; + break; + case 0xc530: + case 0xc540: + case 0xc630: + case 0xc650: + sd->bridge = BRIDGE_SPCA533; + break; + } + break; + case 0x05da: /* Digital Dream cameras */ +/* switch (product) { */ +/* case 0x1018: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x06d6: /* Trust */ +/* switch (product) { */ +/* case 0x0031: */ + sd->bridge = BRIDGE_SPCA533; /* SPCA533A */ +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ + switch (product) { + case 0x1311: + case 0x1314: + case 0x2211: + case 0x2221: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x3261: + case 0x3281: + sd->bridge = BRIDGE_SPCA536; + break; + } + break; + case 0x08ca: /* Aiptek */ + switch (product) { + case 0x0104: + case 0x0106: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2008: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x2010: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2016: + case 0x2018: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x2020: + case 0x2022: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2024: + sd->bridge = BRIDGE_SPCA536; + break; + case 0x2028: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2040: + case 0x2042: + case 0x2050: + case 0x2060: + sd->bridge = BRIDGE_SPCA536; + break; + } + break; + case 0x0d64: /* SunPlus */ +/* switch (product) { */ +/* case 0x0303: */ + sd->bridge = BRIDGE_SPCA536; +/* break; */ +/* } */ + break; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA536: */ + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + break; + case BRIDGE_SPCA533: + cam->cam_mode = custom_mode; + cam->nmodes = sizeof custom_mode / sizeof custom_mode[0]; + break; + case BRIDGE_SPCA504C: + cam->cam_mode = vga_mode2; + cam->nmodes = sizeof vga_mode2 / sizeof vga_mode2[0]; + break; + } + sd->qindex = 5; /* set the quantization table */ + 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; + return 0; +} + +/* this function is called at open time */ +static int sd_open(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; + + switch (sd->bridge) { + case BRIDGE_SPCA504B: + reg_w(dev, 0x1d, 0, 0, NULL, 0); + reg_w(dev, 0, 1, 0x2306, NULL, 0); + reg_w(dev, 0, 0, 0x0d04, NULL, 0); + reg_w(dev, 0, 0, 0x2000, NULL, 0); + reg_w(dev, 0, 0x13, 0x2301, NULL, 0); + reg_w(dev, 0, 0, 0x2306, NULL, 0); + /* fall thru */ + case BRIDGE_SPCA533: + rc = spca504B_PollingDataReady(gspca_dev); + spca50x_GetFirmware(gspca_dev); + break; + case BRIDGE_SPCA536: + spca50x_GetFirmware(gspca_dev); + reg_r(dev, 0x00, 0x5002, gspca_dev->usb_buf, 1); + gspca_dev->usb_buf[0] = 0; + reg_w(dev, 0x24, 0, 0, gspca_dev->usb_buf, 1); + reg_r(dev, 0x24, 0, gspca_dev->usb_buf, 1); + rc = spca504B_PollingDataReady(gspca_dev); + reg_w(dev, 0x34, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + break; + case BRIDGE_SPCA504C: /* pccam600 */ + PDEBUG(D_STREAM, "Opening SPCA504 (PC-CAM 600)"); + reg_w_riv(dev, 0xe0, 0x0000, 0x0000); + reg_w_riv(dev, 0xe0, 0x0000, 0x0001); /* reset */ + spca504_wait_status(gspca_dev); + if (sd->subtype == LogitechClickSmart420) + write_vector(gspca_dev, + spca504A_clicksmart420_open_data); + else + write_vector(gspca_dev, spca504_pccam600_open_data); + err_code = spca50x_setup_qtable(gspca_dev, + 0x00, 0x2800, + 0x2840, qtable_creative_pccam); + if (err_code < 0) { + PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed"); + return err_code; + } + break; + default: +/* case BRIDGE_SPCA504: */ + PDEBUG(D_STREAM, "Opening SPCA504"); + if (sd->subtype == AiptekMiniPenCam13) { + /*****************************/ + for (i = 0; i < 6; i++) + info[i] = reg_r_1(gspca_dev, i); + PDEBUG(D_STREAM, + "Read info: %d %d %d %d %d %d." + " Should be 1,0,2,2,0,0", + info[0], info[1], info[2], + info[3], info[4], info[5]); + /* spca504a aiptek */ + /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 1); + /* Twice sequencial need status 0xff->0x9e->0x9d */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 0); + + spca504A_acknowledged_command(gspca_dev, 0x24, + 0, 0, 0x9d, 1); + /******************************/ + /* spca504a aiptek */ + spca504A_acknowledged_command(gspca_dev, 0x08, + 6, 0, 0x86, 1); +/* reg_write (dev, 0, 0x2000, 0); */ +/* reg_write (dev, 0, 0x2883, 1); */ +/* spca504A_acknowledged_command (gspca_dev, 0x08, + 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); + spca504A_acknowledged_command(gspca_dev, 0x01, + 0x0f, 0, 0xff, 0); + } + /* setup qtable */ + 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"); + return err_code; + } + break; + } + return 0; +} + +static void 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]; + + if (sd->bridge == BRIDGE_SPCA504B) + spca504B_setQtable(gspca_dev); + spca504B_SetSizeType(gspca_dev); + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA536: */ + if (sd->subtype == MegapixV4 || + sd->subtype == LogitechClickSmart820) { + reg_w(dev, 0xf0, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + reg_r(dev, 0xf0, 4, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + } else { + reg_w(dev, 0x31, 0, 4, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + rc = spca504B_PollingDataReady(gspca_dev); + } + break; + case BRIDGE_SPCA504: + if (sd->subtype == AiptekMiniPenCam13) { + for (i = 0; i < 6; i++) + info[i] = reg_r_1(gspca_dev, i); + PDEBUG(D_STREAM, + "Read info: %d %d %d %d %d %d." + " Should be 1,0,2,2,0,0", + info[0], info[1], info[2], + info[3], info[4], info[5]); + /* spca504a aiptek */ + /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 1); + /* Twice sequencial need status 0xff->0x9e->0x9d */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 0); + spca504A_acknowledged_command(gspca_dev, 0x24, + 0, 0, 0x9d, 1); + } else { + spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); + for (i = 0; i < 6; i++) + info[i] = reg_r_1(gspca_dev, i); + PDEBUG(D_STREAM, + "Read info: %d %d %d %d %d %d." + " Should be 1,0,2,2,0,0", + info[0], info[1], info[2], + info[3], info[4], info[5]); + spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); + 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); + break; + case BRIDGE_SPCA504C: + if (sd->subtype == LogitechClickSmart420) { + write_vector(gspca_dev, + spca504A_clicksmart420_init_data); + } else { + write_vector(gspca_dev, spca504_pccam600_init_data); + } + enable = (sd->autogain ? 0x04 : 0x01); + reg_w_riv(dev, 0x0c, 0x0000, enable); /* auto exposure */ + reg_w_riv(dev, 0xb0, 0x0000, enable); /* auto whiteness */ + + /* set default exposure compensation and whiteness balance */ + reg_w_riv(dev, 0x30, 0x0001, 800); /* ~ 20 fps */ + reg_w_riv(dev, 0x30, 0x0002, 1600); + spca504B_SetSizeType(gspca_dev); + break; + } + sp5xx_initContBrigHueRegisters(gspca_dev); +} + +static void sd_stopN(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_SPCA536: */ +/* case BRIDGE_SPCA504B: */ + reg_w(dev, 0x31, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + spca504B_PollingDataReady(gspca_dev); + break; + case BRIDGE_SPCA504: + case BRIDGE_SPCA504C: + reg_w_riv(dev, 0x00, 0x2000, 0x0000); + + if (sd->subtype == AiptekMiniPenCam13) { + /* spca504a aiptek */ +/* spca504A_acknowledged_command(gspca_dev, 0x08, + 6, 0, 0x86, 1); */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 0x00, 0x00, 0x9d, 1); + spca504A_acknowledged_command(gspca_dev, 0x01, + 0x0f, 0x00, 0xff, 1); + } else { + spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); + reg_w_riv(dev, 0x01, 0x000f, 0x00); + } + break; + } +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, sof = 0; + unsigned char *s, *d; + static unsigned char ffd9[] = {0xff, 0xd9}; + +/* frames are jpeg 4.1.1 without 0xff escape */ + switch (sd->bridge) { + case BRIDGE_SPCA533: + if (data[0] == 0xff) { + if (data[1] != 0x01) { /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + sof = 1; + data += SPCA533_OFFSET_DATA; + len -= SPCA533_OFFSET_DATA; + } else { + data += 1; + len -= 1; + } + break; + case BRIDGE_SPCA536: + if (data[0] == 0xff) { + sof = 1; + data += SPCA536_OFFSET_DATA; + len -= SPCA536_OFFSET_DATA; + } else { + data += 2; + len -= 2; + } + break; + default: +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504B: */ + switch (data[0]) { + case 0xfe: /* start of frame */ + sof = 1; + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + break; + case 0xff: /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + default: + data += 1; + len -= 1; + break; + } + break; + case BRIDGE_SPCA504C: + switch (data[0]) { + case 0xfe: /* start of frame */ + sof = 1; + data += SPCA504_PCCAM600_OFFSET_DATA; + len -= SPCA504_PCCAM600_OFFSET_DATA; + break; + case 0xff: /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + default: + data += 1; + len -= 1; + break; + } + break; + } + if (sof) { /* start of frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + ffd9, 2); + + /* put the JPEG header in the new frame */ + jpeg_put_header(gspca_dev, frame, + ((struct sd *) gspca_dev)->qindex, + 0x22); + } + + /* add 0x00 after 0xff */ + for (i = len; --i >= 0; ) + if (data[i] == 0xff) + break; + if (i < 0) { /* no 0xff */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + return; + } + s = data; + d = sd->packet; + for (i = 0; i < len; i++) { + *d++ = *s++; + if (s[-1] == 0xff) + *d++ = 0x00; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + sd->packet, d - sd->packet); +} + +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 getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u16 brightness = 0; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + brightness = reg_r_12(gspca_dev, 0x00, 0x21a7, 2); + break; + case BRIDGE_SPCA536: + brightness = reg_r_12(gspca_dev, 0x00, 0x20f0, 2); + break; + } + sd->brightness = ((brightness & 0xff) - 128) % 255; +} + +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 getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + sd->contrast = reg_r_12(gspca_dev, 0x00, 0x21a8, 2); + break; + case BRIDGE_SPCA536: + sd->contrast = reg_r_12(gspca_dev, 0x00, 0x20f1, 2); + 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 void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + sd->colors = reg_r_12(gspca_dev, 0x00, 0x21ae, 2) >> 1; + break; + case BRIDGE_SPCA536: + sd->colors = reg_r_12(gspca_dev, 0x00, 0x20f6, 2) >> 1; + break; + } +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x400b), DVNM("Creative PC-CAM 600")}, + {USB_DEVICE(0x041e, 0x4012), DVNM("PC-Cam350")}, + {USB_DEVICE(0x041e, 0x4013), DVNM("Creative Pccam750")}, + {USB_DEVICE(0x0458, 0x7006), DVNM("Genius Dsc 1.3 Smart")}, + {USB_DEVICE(0x0461, 0x0821), DVNM("Fujifilm MV-1")}, + {USB_DEVICE(0x046d, 0x0905), DVNM("Logitech ClickSmart 820")}, + {USB_DEVICE(0x046d, 0x0960), DVNM("Logitech ClickSmart 420")}, + {USB_DEVICE(0x0471, 0x0322), DVNM("Philips DMVC1300K")}, + {USB_DEVICE(0x04a5, 0x3003), DVNM("Benq DC 1300")}, + {USB_DEVICE(0x04a5, 0x3008), DVNM("Benq DC 1500")}, + {USB_DEVICE(0x04a5, 0x300a), DVNM("Benq DC3410")}, + {USB_DEVICE(0x04f1, 0x1001), DVNM("JVC GC A50")}, + {USB_DEVICE(0x04fc, 0x500c), DVNM("Sunplus CA500C")}, + {USB_DEVICE(0x04fc, 0x504a), DVNM("Aiptek Mini PenCam 1.3")}, + {USB_DEVICE(0x04fc, 0x504b), DVNM("Maxell MaxPocket LE 1.3")}, + {USB_DEVICE(0x04fc, 0x5330), DVNM("Digitrex 2110")}, + {USB_DEVICE(0x04fc, 0x5360), DVNM("Sunplus Generic")}, + {USB_DEVICE(0x04fc, 0xffff), DVNM("Pure DigitalDakota")}, + {USB_DEVICE(0x052b, 0x1513), DVNM("Megapix V4")}, + {USB_DEVICE(0x0546, 0x3155), DVNM("Polaroid PDC3070")}, + {USB_DEVICE(0x0546, 0x3191), DVNM("Polaroid Ion 80")}, + {USB_DEVICE(0x0546, 0x3273), DVNM("Polaroid PDC2030")}, + {USB_DEVICE(0x055f, 0xc211), DVNM("Kowa Bs888e Microcamera")}, + {USB_DEVICE(0x055f, 0xc230), DVNM("Mustek Digicam 330K")}, + {USB_DEVICE(0x055f, 0xc232), DVNM("Mustek MDC3500")}, + {USB_DEVICE(0x055f, 0xc360), DVNM("Mustek DV4000 Mpeg4 ")}, + {USB_DEVICE(0x055f, 0xc420), DVNM("Mustek gSmart Mini 2")}, + {USB_DEVICE(0x055f, 0xc430), DVNM("Mustek Gsmart LCD 2")}, + {USB_DEVICE(0x055f, 0xc440), DVNM("Mustek DV 3000")}, + {USB_DEVICE(0x055f, 0xc520), DVNM("Mustek gSmart Mini 3")}, + {USB_DEVICE(0x055f, 0xc530), DVNM("Mustek Gsmart LCD 3")}, + {USB_DEVICE(0x055f, 0xc540), DVNM("Gsmart D30")}, + {USB_DEVICE(0x055f, 0xc630), DVNM("Mustek MDC4000")}, + {USB_DEVICE(0x055f, 0xc650), DVNM("Mustek MDC5500Z")}, + {USB_DEVICE(0x05da, 0x1018), DVNM("Digital Dream Enigma 1.3")}, + {USB_DEVICE(0x06d6, 0x0031), DVNM("Trust 610 LCD PowerC@m Zoom")}, + {USB_DEVICE(0x0733, 0x1311), DVNM("Digital Dream Epsilon 1.3")}, + {USB_DEVICE(0x0733, 0x1314), DVNM("Mercury 2.1MEG Deluxe Classic Cam")}, + {USB_DEVICE(0x0733, 0x2211), DVNM("Jenoptik jdc 21 LCD")}, + {USB_DEVICE(0x0733, 0x2221), DVNM("Mercury Digital Pro 3.1p")}, + {USB_DEVICE(0x0733, 0x3261), DVNM("Concord 3045 spca536a")}, + {USB_DEVICE(0x0733, 0x3281), DVNM("Cyberpix S550V")}, + {USB_DEVICE(0x08ca, 0x0104), DVNM("Aiptek PocketDVII 1.3")}, + {USB_DEVICE(0x08ca, 0x0106), DVNM("Aiptek Pocket DV3100+")}, + {USB_DEVICE(0x08ca, 0x2008), DVNM("Aiptek Mini PenCam 2 M")}, + {USB_DEVICE(0x08ca, 0x2010), DVNM("Aiptek PocketCam 3M")}, + {USB_DEVICE(0x08ca, 0x2016), DVNM("Aiptek PocketCam 2 Mega")}, + {USB_DEVICE(0x08ca, 0x2018), DVNM("Aiptek Pencam SD 2M")}, + {USB_DEVICE(0x08ca, 0x2020), DVNM("Aiptek Slim 3000F")}, + {USB_DEVICE(0x08ca, 0x2022), DVNM("Aiptek Slim 3200")}, + {USB_DEVICE(0x08ca, 0x2024), DVNM("Aiptek DV3500 Mpeg4 ")}, + {USB_DEVICE(0x08ca, 0x2028), DVNM("Aiptek PocketCam4M")}, + {USB_DEVICE(0x08ca, 0x2040), DVNM("Aiptek PocketDV4100M")}, + {USB_DEVICE(0x08ca, 0x2042), DVNM("Aiptek PocketDV5100")}, + {USB_DEVICE(0x08ca, 0x2050), DVNM("Medion MD 41437")}, + {USB_DEVICE(0x08ca, 0x2060), DVNM("Aiptek PocketDV5300")}, + {USB_DEVICE(0x0d64, 0x0303), DVNM("Sunplus FashionCam DXG")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c new file mode 100644 index 00000000000..00f47e463a0 --- /dev/null +++ b/drivers/media/video/gspca/t613.c @@ -0,0 +1,1038 @@ +/* + *Notes: * t613 + tas5130A + * * Focus to light do not balance well as in win. + * Quality in win is not good, but its kinda better. + * * Fix some "extraneous bytes", most of apps will show the image anyway + * * Gamma table, is there, but its really doing something? + * * 7~8 Fps, its ok, max on win its 10. + * Costantino Leandro + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "t613" +#include "gspca.h" +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +#define MAX_GAMMA 0x10 /* 0 to 15 */ + +/* From LUVCVIEW */ +#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 3) + +MODULE_AUTHOR("Leandro Costantino <le_costantino@pixartargentina.com.ar>"); +MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver"); +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; + unsigned char gamma; + unsigned char sharpness; + unsigned char freq; + unsigned char whitebalance; + unsigned char mirror; + unsigned char effect; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val); +static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0x0f, + .step = 1, + .default_value = 0x09, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0x0d, + .step = 1, + .default_value = 0x07, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 0x0f, + .step = 1, + .default_value = 0x05, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_GAMMA 3 + { + { + .id = V4L2_CID_GAMMA, /* (gamma on win) */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma (Untested)", + .minimum = 0, + .maximum = MAX_GAMMA, + .step = 1, + .default_value = 0x09, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, +#define SD_AUTOGAIN 4 + { + { + .id = V4L2_CID_GAIN, /* here, i activate only the lowlight, + * some apps dont bring up the + * backligth_compensation control) */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Low Light", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0x01, + }, + .set = sd_setlowlight, + .get = sd_getlowlight, + }, +#define SD_MIRROR 5 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror Image", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = sd_setflip, + .get = sd_getflip + }, +#define SD_LIGHTFREQ 6 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light Frequency Filter", + .minimum = 1, /* 1 -> 0x50, 2->0x60 */ + .maximum = 2, + .step = 1, + .default_value = 1, + }, + .set = sd_setfreq, + .get = sd_getfreq}, + +#define SD_WHITE_BALANCE 7 + { + { + .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setwhitebalance, + .get = sd_getwhitebalance + }, +#define SD_SHARPNESS 8 /* (aka definition on win) */ + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = MAX_GAMMA, /* 0 to 16 */ + .step = 1, + .default_value = 0x06, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, +#define SD_EFFECTS 9 + { + { + .id = V4L2_CID_EFFECTS, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Webcam Effects", + .minimum = 0, + .maximum = 4, + .step = 1, + .default_value = 0, + }, + .set = sd_seteffect, + .get = sd_geteffect + }, +}; + +static char *effects_control[] = { + "Normal", + "Emboss", /* disabled */ + "Monochrome", + "Sepia", + "Sketch", + "Sun Effect", /* disabled */ + "Negative", +}; + +static struct v4l2_pix_format vga_mode_t16[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 4}, + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +#define T16_OFFSET_DATA 631 +#define MAX_EFFECTS 7 +/* easily done by soft, this table could be removed, + * i keep it here just in case */ +static const __u8 effects_table[MAX_EFFECTS][6] = { + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */ + {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */ + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x20}, /* Monochrome */ + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x80}, /* Sepia */ + {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x02}, /* Croquis */ + {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x10}, /* Sun Effect */ + {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40}, /* Negative */ +}; + +static const __u8 gamma_table[MAX_GAMMA][34] = { + {0x90, 0x00, 0x91, 0x3e, 0x92, 0x69, 0x93, 0x85, + 0x94, 0x95, 0x95, 0xa1, 0x96, 0xae, 0x97, 0xb9, + 0x98, 0xc2, 0x99, 0xcb, 0x9a, 0xd4, 0x9b, 0xdb, + 0x9c, 0xe3, 0x9d, 0xea, 0x9e, 0xf1, 0x9f, 0xf8, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x33, 0x92, 0x5A, 0x93, 0x75, + 0x94, 0x85, 0x95, 0x93, 0x96, 0xA1, 0x97, 0xAD, + 0x98, 0xB7, 0x99, 0xC2, 0x9A, 0xCB, 0x9B, 0xD4, + 0x9C, 0xDE, 0x9D, 0xE7, 0x9E, 0xF0, 0x9F, 0xF7, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x2F, 0x92, 0x51, 0x93, 0x6B, + 0x94, 0x7C, 0x95, 0x8A, 0x96, 0x99, 0x97, 0xA6, + 0x98, 0xB1, 0x99, 0xBC, 0x9A, 0xC6, 0x9B, 0xD0, + 0x9C, 0xDB, 0x9D, 0xE4, 0x9E, 0xED, 0x9F, 0xF6, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x29, 0x92, 0x48, 0x93, 0x60, + 0x94, 0x72, 0x95, 0x81, 0x96, 0x90, 0x97, 0x9E, + 0x98, 0xAA, 0x99, 0xB5, 0x9A, 0xBF, 0x9B, 0xCB, + 0x9C, 0xD6, 0x9D, 0xE1, 0x9E, 0xEB, 0x9F, 0xF5, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x23, 0x92, 0x3F, 0x93, 0x55, + 0x94, 0x68, 0x95, 0x77, 0x96, 0x86, 0x97, 0x95, + 0x98, 0xA2, 0x99, 0xAD, 0x9A, 0xB9, 0x9B, 0xC6, + 0x9C, 0xD2, 0x9D, 0xDE, 0x9E, 0xE9, 0x9F, 0xF4, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x1B, 0x92, 0x33, 0x93, 0x48, + 0x94, 0x59, 0x95, 0x69, 0x96, 0x79, 0x97, 0x87, + 0x98, 0x96, 0x99, 0xA3, 0x9A, 0xB1, 0x9B, 0xBE, + 0x9C, 0xCC, 0x9D, 0xDA, 0x9E, 0xE7, 0x9F, 0xF3, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x02, 0x92, 0x10, 0x93, 0x20, + 0x94, 0x32, 0x95, 0x40, 0x96, 0x57, 0x97, 0x67, + 0x98, 0x77, 0x99, 0x88, 0x9a, 0x99, 0x9b, 0xaa, + 0x9c, 0xbb, 0x9d, 0xcc, 0x9e, 0xdd, 0x9f, 0xee, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x02, 0x92, 0x14, 0x93, 0x26, + 0x94, 0x38, 0x95, 0x4A, 0x96, 0x60, 0x97, 0x70, + 0x98, 0x80, 0x99, 0x90, 0x9A, 0xA0, 0x9B, 0xB0, + 0x9C, 0xC0, 0x9D, 0xD0, 0x9E, 0xE0, 0x9F, 0xF0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x10, 0x92, 0x22, 0x93, 0x35, + 0x94, 0x47, 0x95, 0x5A, 0x96, 0x69, 0x97, 0x79, + 0x98, 0x88, 0x99, 0x97, 0x9A, 0xA7, 0x9B, 0xB6, + 0x9C, 0xC4, 0x9D, 0xD3, 0x9E, 0xE0, 0x9F, 0xF0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x10, 0x92, 0x26, 0x93, 0x40, + 0x94, 0x54, 0x95, 0x65, 0x96, 0x75, 0x97, 0x84, + 0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd, + 0x9c, 0xca, 0x9d, 0xd6, 0x9e, 0xe0, 0x9f, 0xf0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x18, 0x92, 0x2B, 0x93, 0x44, + 0x94, 0x60, 0x95, 0x70, 0x96, 0x80, 0x97, 0x8E, + 0x98, 0x9C, 0x99, 0xAA, 0x9A, 0xB7, 0x9B, 0xC4, + 0x9C, 0xD0, 0x9D, 0xD8, 0x9E, 0xE2, 0x9F, 0xF0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x1A, 0x92, 0x34, 0x93, 0x52, + 0x94, 0x66, 0x95, 0x7E, 0x96, 0x8D, 0x97, 0x9B, + 0x98, 0xA8, 0x99, 0xB4, 0x9A, 0xC0, 0x9B, 0xCB, + 0x9C, 0xD6, 0x9D, 0xE1, 0x9E, 0xEB, 0x9F, 0xF5, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x3F, 0x92, 0x5A, 0x93, 0x6E, + 0x94, 0x7F, 0x95, 0x8E, 0x96, 0x9C, 0x97, 0xA8, + 0x98, 0xB4, 0x99, 0xBF, 0x9A, 0xC9, 0x9B, 0xD3, + 0x9C, 0xDC, 0x9D, 0xE5, 0x9E, 0xEE, 0x9F, 0xF6, + 0xA0, 0xFF}, + {0x90, 0x00, 0x91, 0x54, 0x92, 0x6F, 0x93, 0x83, + 0x94, 0x93, 0x95, 0xA0, 0x96, 0xAD, 0x97, 0xB7, + 0x98, 0xC2, 0x99, 0xCB, 0x9A, 0xD4, 0x9B, 0xDC, + 0x9C, 0xE4, 0x9D, 0xEB, 0x9E, 0xF2, 0x9F, 0xF9, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x6E, 0x92, 0x88, 0x93, 0x9A, + 0x94, 0xA8, 0x95, 0xB3, 0x96, 0xBD, 0x97, 0xC6, + 0x98, 0xCF, 0x99, 0xD6, 0x9A, 0xDD, 0x9B, 0xE3, + 0x9C, 0xE9, 0x9D, 0xEF, 0x9E, 0xF4, 0x9F, 0xFA, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x93, 0x92, 0xA8, 0x93, 0xB7, + 0x94, 0xC1, 0x95, 0xCA, 0x96, 0xD2, 0x97, 0xD8, + 0x98, 0xDE, 0x99, 0xE3, 0x9A, 0xE8, 0x9B, 0xED, + 0x9C, 0xF1, 0x9D, 0xF5, 0x9E, 0xF8, 0x9F, 0xFC, + 0xA0, 0xFF} +}; + +static const __u8 tas5130a_sensor_init[][8] = { + {0x62, 0x08, 0x63, 0x70, 0x64, 0x1d, 0x60, 0x09}, + {0x62, 0x20, 0x63, 0x01, 0x64, 0x02, 0x60, 0x09}, + {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, + {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, + {}, +}; + +/* read 1 byte */ +static int reg_r_1(struct gspca_dev *gspca_dev, + __u16 index) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + gspca_dev->usb_buf, 1, 500); + return gspca_dev->usb_buf[0]; +} + +static void reg_w(struct gspca_dev *gspca_dev, + __u16 value, + __u16 index, + const __u8 *buffer, __u16 len) +{ + if (buffer == NULL) { + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, index, + NULL, 0, 500); + return; + } + if (len <= sizeof gspca_dev->usb_buf) { + memcpy(gspca_dev->usb_buf, buffer, len); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, index, + gspca_dev->usb_buf, len, 500); + } else { + __u8 *tmpbuf; + + tmpbuf = kmalloc(len, GFP_KERNEL); + memcpy(tmpbuf, buffer, len); + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, index, + tmpbuf, len, 500); + kfree(tmpbuf); + } +} + +/* 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; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + + cam->cam_mode = vga_mode_t16; + cam->nmodes = ARRAY_SIZE(vga_mode_t16); + + 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->gamma = sd_ctrls[SD_GAMMA].qctrl.default_value; + sd->mirror = sd_ctrls[SD_MIRROR].qctrl.default_value; + sd->freq = sd_ctrls[SD_LIGHTFREQ].qctrl.default_value; + sd->whitebalance = sd_ctrls[SD_WHITE_BALANCE].qctrl.default_value; + sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value; + sd->effect = sd_ctrls[SD_EFFECTS].qctrl.default_value; + return 0; +} + +static int init_default_parameters(struct gspca_dev *gspca_dev) +{ + /* some of this registers are not really neded, because + * they are overriden by setbrigthness, setcontrast, etc, + * but wont hurt anyway, and can help someone with similar webcam + * to see the initial parameters.*/ + int i = 0; + __u8 test_byte; + + static const __u8 read_indexs[] = + { 0x06, 0x07, 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5, + 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00, 0x00 }; + static const __u8 n1[6] = + {0x08, 0x03, 0x09, 0x03, 0x12, 0x04}; + static const __u8 n2[2] = + {0x08, 0x00}; + static const __u8 nset[6] = + { 0x61, 0x68, 0x62, 0xff, 0x60, 0x07 }; + static const __u8 n3[6] = + {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}; + static const __u8 n4[0x46] = + {0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c, + 0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68, + 0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1, + 0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8, + 0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48, + 0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0, + 0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68, + 0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40, + 0xac, 0x84, 0xad, 0x86, 0xaf, 0x46}; + static const __u8 nset4[18] = { + 0xe0, 0x60, 0xe1, 0xa8, 0xe2, 0xe0, 0xe3, 0x60, 0xe4, 0xa8, + 0xe5, 0xe0, 0xe6, 0x60, 0xe7, 0xa8, + 0xe8, 0xe0 + }; + /* ojo puede ser 0xe6 en vez de 0xe9 */ + static const __u8 nset2[20] = { + 0xd0, 0xbb, 0xd1, 0x28, 0xd2, 0x10, 0xd3, 0x10, 0xd4, 0xbb, + 0xd5, 0x28, 0xd6, 0x1e, 0xd7, 0x27, + 0xd8, 0xc8, 0xd9, 0xfc + }; + static const __u8 missing[8] = + { 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38 }; + static const __u8 nset3[18] = { + 0xc7, 0x60, 0xc8, 0xa8, 0xc9, 0xe0, 0xca, 0x60, 0xcb, 0xa8, + 0xcc, 0xe0, 0xcd, 0x60, 0xce, 0xa8, + 0xcf, 0xe0 + }; + static const __u8 nset5[4] = + { 0x8f, 0x24, 0xc3, 0x00 }; /* bright */ + static const __u8 nset6[34] = { + 0x90, 0x00, 0x91, 0x1c, 0x92, 0x30, 0x93, 0x43, 0x94, 0x54, + 0x95, 0x65, 0x96, 0x75, 0x97, 0x84, + 0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd, 0x9c, 0xca, + 0x9d, 0xd8, 0x9e, 0xe5, 0x9f, 0xf2, + 0xa0, 0xff + }; /* Gamma */ + static const __u8 nset7[4] = + { 0x66, 0xca, 0xa8, 0xf8 }; /* 50/60 Hz */ + static const __u8 nset9[4] = + { 0x0b, 0x04, 0x0a, 0x78 }; + static const __u8 nset8[6] = + { 0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00 }; + static const __u8 nset10[6] = + { 0x0c, 0x03, 0xab, 0x10, 0x81, 0x20 }; + + reg_w(gspca_dev, 0x01, 0x0000, n1, 0x06); + reg_w(gspca_dev, 0x01, 0x0000, nset, 0x06); + reg_r_1(gspca_dev, 0x0063); + reg_w(gspca_dev, 0x01, 0x0000, n2, 0x02); + + while (read_indexs[i] != 0x00) { + test_byte = reg_r_1(gspca_dev, read_indexs[i]); + PDEBUG(D_CONF, "Reg 0x%02x => 0x%02x", read_indexs[i], + test_byte); + i++; + } + + reg_w(gspca_dev, 0x01, 0x0000, n3, 0x06); + reg_w(gspca_dev, 0x01, 0x0000, n4, 0x46); + reg_r_1(gspca_dev, 0x0080); + reg_w(gspca_dev, 0x00, 0x2c80, NULL, 0); + reg_w(gspca_dev, 0x01, 0x0000, nset2, 0x14); + reg_w(gspca_dev, 0x01, 0x0000, nset3, 0x12); + reg_w(gspca_dev, 0x01, 0x0000, nset4, 0x12); + reg_w(gspca_dev, 0x00, 0x3880, NULL, 0); + reg_w(gspca_dev, 0x00, 0x3880, NULL, 0); + reg_w(gspca_dev, 0x00, 0x338e, NULL, 0); + reg_w(gspca_dev, 0x01, 0x0000, nset5, 0x04); + reg_w(gspca_dev, 0x00, 0x00a9, NULL, 0); + reg_w(gspca_dev, 0x01, 0x0000, nset6, 0x22); + reg_w(gspca_dev, 0x00, 0x86bb, NULL, 0); + reg_w(gspca_dev, 0x00, 0x4aa6, NULL, 0); + + reg_w(gspca_dev, 0x01, 0x0000, missing, 0x08); + + reg_w(gspca_dev, 0x00, 0x2087, NULL, 0); + reg_w(gspca_dev, 0x00, 0x2088, NULL, 0); + reg_w(gspca_dev, 0x00, 0x2089, NULL, 0); + + reg_w(gspca_dev, 0x01, 0x0000, nset7, 0x04); + reg_w(gspca_dev, 0x01, 0x0000, nset10, 0x06); + reg_w(gspca_dev, 0x01, 0x0000, nset8, 0x06); + reg_w(gspca_dev, 0x01, 0x0000, nset9, 0x04); + + reg_w(gspca_dev, 0x00, 0x2880, NULL, 0); + reg_w(gspca_dev, 0x01, 0x0000, nset2, 0x14); + reg_w(gspca_dev, 0x01, 0x0000, nset3, 0x12); + reg_w(gspca_dev, 0x01, 0x0000, nset4, 0x12); + + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned int brightness; + __u8 set6[4] = { 0x8f, 0x26, 0xc3, 0x80 }; + brightness = sd->brightness; + + if (brightness < 7) { + set6[3] = 0x70 - (brightness * 0xa); + } else { + set6[1] = 0x24; + set6[3] = 0x00 + ((brightness - 7) * 0xa); + } + + reg_w(gspca_dev, 0x01, 0x0000, set6, 4); +} + +static void setflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + __u8 flipcmd[8] = + { 0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09 }; + + if (sd->mirror == 1) + flipcmd[3] = 0x01; + + reg_w(gspca_dev, 0x01, 0x0000, flipcmd, 8); +} + +static void seteffect(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_w(gspca_dev, 0x01, 0x0000, effects_table[sd->effect], 0x06); + if (sd->effect == 1 || sd->effect == 5) { + PDEBUG(D_CONF, + "This effect have been disabled for webcam \"safety\""); + return; + } + + if (sd->effect == 1 || sd->effect == 4) + reg_w(gspca_dev, 0x00, 0x4aa6, NULL, 0); + else + reg_w(gspca_dev, 0x00, 0xfaa6, NULL, 0); +} + +static void setwhitebalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + __u8 white_balance[8] = + { 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38 }; + + if (sd->whitebalance == 1) + white_balance[7] = 0x3c; + + reg_w(gspca_dev, 0x01, 0x0000, white_balance, 8); +} + +static void setlightfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 freq[4] = { 0x66, 0x40, 0xa8, 0xe8 }; + + if (sd->freq == 2) /* 60hz */ + freq[1] = 0x00; + + reg_w(gspca_dev, 0x1, 0x0000, freq, 0x4); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned int contrast = sd->contrast; + __u16 reg_to_write = 0x00; + + if (contrast < 7) + reg_to_write = 0x8ea9 - (0x200 * contrast); + else + reg_to_write = (0x00a9 + ((contrast - 7) * 0x200)); + + reg_w(gspca_dev, 0x00, reg_to_write, NULL, 0); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u16 reg_to_write; + + reg_to_write = 0xc0bb + sd->colors * 0x100; + reg_w(gspca_dev, 0x00, reg_to_write, NULL, 0); +} + +static void setgamma(struct gspca_dev *gspca_dev) +{ +} + +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u16 reg_to_write; + + reg_to_write = 0x0aa6 + 0x1000 * sd->sharpness; + + reg_w(gspca_dev, 0x00, reg_to_write, NULL, 0); +} + +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 *val; +} + +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->whitebalance = val; + if (gspca_dev->streaming) + setwhitebalance(gspca_dev); + return 0; +} + +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->whitebalance; + return *val; +} + +static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->mirror = val; + if (gspca_dev->streaming) + setflip(gspca_dev); + return 0; +} + +static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->mirror; + return *val; +} + +static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->effect = val; + if (gspca_dev->streaming) + seteffect(gspca_dev); + return 0; +} + +static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->effect; + return *val; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return *val; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) + setgamma(gspca_dev); + return 0; +} + +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->gamma; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freq = val; + if (gspca_dev->streaming) + setlightfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) + setsharpness(gspca_dev); + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +/* Low Light set here......*/ +static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val != 0) + reg_w(gspca_dev, 0x00, 0xf48e, NULL, 0); + else + reg_w(gspca_dev, 0x00, 0xb48e, NULL, 0); + return 0; +} + +static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + int mode; + + static const __u8 t1[] = { 0x66, 0x00, 0xa8, 0xe8 }; + __u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 }; + static const __u8 t3[] = + { 0xb3, 0x07, 0xb4, 0x00, 0xb5, 0x88, 0xb6, 0x02, 0xb7, 0x06, + 0xb8, 0x00, 0xb9, 0xe7, 0xba, 0x01 }; + static const __u8 t4[] = { 0x0b, 0x04, 0x0a, 0x40 }; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode]. priv; + switch (mode) { + case 1: /* 352x288 */ + t2[1] = 0x40; + break; + case 2: /* 320x240 */ + t2[1] = 0x10; + break; + case 3: /* 176x144 */ + t2[1] = 0x50; + break; + case 4: /* 160x120 */ + t2[1] = 0x20; + break; + default: /* 640x480 (0x00) */ + break; + } + + reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[0], 0x8); + reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[1], 0x8); + reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[2], 0x8); + reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[3], 0x8); + reg_w(gspca_dev, 0x00, 0x3c80, NULL, 0); + /* just in case and to keep sync with logs (for mine) */ + reg_w(gspca_dev, 0x01, 0x0000, tas5130a_sensor_init[3], 0x8); + reg_w(gspca_dev, 0x00, 0x3c80, NULL, 0); + /* just in case and to keep sync with logs (for mine) */ + reg_w(gspca_dev, 0x01, 0x0000, t1, 4); + reg_w(gspca_dev, 0x01, 0x0000, t2, 6); + reg_r_1(gspca_dev, 0x0012); + reg_w(gspca_dev, 0x01, 0x0000, t3, 0x10); + reg_w(gspca_dev, 0x00, 0x0013, NULL, 0); + reg_w(gspca_dev, 0x01, 0x0000, t4, 0x4); + /* restart on each start, just in case, sometimes regs goes wrong + * when using controls from app */ + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setcolors(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + int sof = 0; + static __u8 ffd9[] = { 0xff, 0xd9 }; + + if (data[0] == 0x5a) { + /* Control Packet, after this came the header again, + * but extra bytes came in the packet before this, + * sometimes an EOF arrives, sometimes not... */ + return; + } + + if (data[len - 1] == 0xff && data[len] == 0xd9) { + /* Just in case, i have seen packets with the marker, + * other's do not include it... */ + data += 2; + len -= 4; + } else if (data[2] == 0xff && data[3] == 0xd8) { + sof = 1; + data += 2; + len -= 2; + } else { + data += 2; + len -= 2; + } + + if (sof) { + /* extra bytes....., could be processed too but would be + * a waste of time, right now leave the application and + * libjpeg do it for ourserlves.. */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + ffd9, 2); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + return; + } + + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + case V4L2_CID_EFFECTS: + if ((unsigned) menu->index < ARRAY_SIZE(effects_control)) { + strncpy((char *) menu->name, + effects_control[menu->index], 32); + return 0; + } + break; + } + return -EINVAL; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + init_default_parameters(gspca_dev); + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x17a1, 0x0128), DVNM("XPX Webcam")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c new file mode 100644 index 00000000000..0b793899095 --- /dev/null +++ b/drivers/media/video/gspca/tv8532.c @@ -0,0 +1,670 @@ +/* + * Quickcam cameras initialization data + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define MODULE_NAME "tv8532" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("TV8532 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; /* current length of tmpbuf */ + __u8 tmpbuf[352 * 288 + 10 * 288]; /* no protection... */ + __u8 tmpbuf2[352 * 288]; /* no protection... */ + + unsigned short brightness; + unsigned short contrast; + + char packet; + char synchro; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 1, + .maximum = 0x2ff, + .step = 1, + .default_value = 0x18f, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xffff, + .step = 1, + .default_value = 0x7fff, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +}; + +static struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* + * Initialization data: this is the first set-up data written to the + * device (before the open data). + */ +#define TESTCLK 0x10 /* reg 0x2c -> 0x12 //10 */ +#define TESTCOMP 0x90 /* reg 0x28 -> 0x80 */ +#define TESTLINE 0x81 /* reg 0x29 -> 0x81 */ +#define QCIFLINE 0x41 /* reg 0x29 -> 0x81 */ +#define TESTPTL 0x14 /* reg 0x2D -> 0x14 */ +#define TESTPTH 0x01 /* reg 0x2E -> 0x01 */ +#define TESTPTBL 0x12 /* reg 0x2F -> 0x0a */ +#define TESTPTBH 0x01 /* reg 0x30 -> 0x01 */ +#define ADWIDTHL 0xe8 /* reg 0x0c -> 0xe8 */ +#define ADWIDTHH 0x03 /* reg 0x0d -> 0x03 */ +#define ADHEIGHL 0x90 /* reg 0x0e -> 0x91 //93 */ +#define ADHEIGHH 0x01 /* reg 0x0f -> 0x01 */ +#define EXPOL 0x8f /* reg 0x1c -> 0x8f */ +#define EXPOH 0x01 /* reg 0x1d -> 0x01 */ +#define ADCBEGINL 0x44 /* reg 0x10 -> 0x46 //47 */ +#define ADCBEGINH 0x00 /* reg 0x11 -> 0x00 */ +#define ADRBEGINL 0x0a /* reg 0x14 -> 0x0b //0x0c */ +#define ADRBEGINH 0x00 /* reg 0x15 -> 0x00 */ +#define TV8532_CMD_UPDATE 0x84 + +#define TV8532_EEprom_Add 0x03 +#define TV8532_EEprom_DataL 0x04 +#define TV8532_EEprom_DataM 0x05 +#define TV8532_EEprom_DataH 0x06 +#define TV8532_EEprom_TableLength 0x07 +#define TV8532_EEprom_Write 0x08 +#define TV8532_PART_CTRL 0x00 +#define TV8532_CTRL 0x01 +#define TV8532_CMD_EEprom_Open 0x30 +#define TV8532_CMD_EEprom_Close 0x29 +#define TV8532_UDP_UPDATE 0x31 +#define TV8532_GPIO 0x39 +#define TV8532_GPIO_OE 0x3B +#define TV8532_REQ_RegWrite 0x02 +#define TV8532_REQ_RegRead 0x03 + +#define TV8532_ADWIDTH_L 0x0C +#define TV8532_ADWIDTH_H 0x0D +#define TV8532_ADHEIGHT_L 0x0E +#define TV8532_ADHEIGHT_H 0x0F +#define TV8532_EXPOSURE 0x1C +#define TV8532_QUANT_COMP 0x28 +#define TV8532_MODE_PACKET 0x29 +#define TV8532_SETCLK 0x2C +#define TV8532_POINT_L 0x2D +#define TV8532_POINT_H 0x2E +#define TV8532_POINTB_L 0x2F +#define TV8532_POINTB_H 0x30 +#define TV8532_BUDGET_L 0x2A +#define TV8532_BUDGET_H 0x2B +#define TV8532_VID_L 0x34 +#define TV8532_VID_H 0x35 +#define TV8532_PID_L 0x36 +#define TV8532_PID_H 0x37 +#define TV8532_DeviceID 0x83 +#define TV8532_AD_SLOPE 0x91 +#define TV8532_AD_BITCTRL 0x94 +#define TV8532_AD_COLBEGIN_L 0x10 +#define TV8532_AD_COLBEGIN_H 0x11 +#define TV8532_AD_ROWBEGIN_L 0x14 +#define TV8532_AD_ROWBEGIN_H 0x15 + +static const __u32 tv_8532_eeprom_data[] = { +/* add dataL dataM dataH */ + 0x00010001, 0x01018011, 0x02050014, 0x0305001c, + 0x040d001e, 0x0505001f, 0x06050519, 0x0705011b, + 0x0805091e, 0x090d892e, 0x0a05892f, 0x0b050dd9, + 0x0c0509f1, 0 +}; + +static int reg_r(struct gspca_dev *gspca_dev, + __u16 index) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + TV8532_REQ_RegRead, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, 1, + 500); + return gspca_dev->usb_buf[0]; +} + +/* write 1 byte */ +static void reg_w_1(struct gspca_dev *gspca_dev, + __u16 index, __u8 value) +{ + gspca_dev->usb_buf[0] = value; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + TV8532_REQ_RegWrite, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, 1, 500); +} + +/* write 2 bytes */ +static void reg_w_2(struct gspca_dev *gspca_dev, + __u16 index, __u8 val1, __u8 val2) +{ + gspca_dev->usb_buf[0] = val1; + gspca_dev->usb_buf[1] = val2; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + TV8532_REQ_RegWrite, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, 2, 500); +} + +static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev) +{ + int i = 0; + __u8 reg, data0, data1, data2; + + reg_w_1(gspca_dev, TV8532_GPIO, 0xb0); + reg_w_1(gspca_dev, TV8532_CTRL, TV8532_CMD_EEprom_Open); +/* msleep(1); */ + while (tv_8532_eeprom_data[i]) { + reg = (tv_8532_eeprom_data[i] & 0xff000000) >> 24; + reg_w_1(gspca_dev, TV8532_EEprom_Add, reg); + /* msleep(1); */ + data0 = (tv_8532_eeprom_data[i] & 0x000000ff); + reg_w_1(gspca_dev, TV8532_EEprom_DataL, data0); + /* msleep(1); */ + data1 = (tv_8532_eeprom_data[i] & 0x0000ff00) >> 8; + reg_w_1(gspca_dev, TV8532_EEprom_DataM, data1); + /* msleep(1); */ + data2 = (tv_8532_eeprom_data[i] & 0x00ff0000) >> 16; + reg_w_1(gspca_dev, TV8532_EEprom_DataH, data2); + /* msleep(1); */ + reg_w_1(gspca_dev, TV8532_EEprom_Write, 0); + /* msleep(10); */ + i++; + } + reg_w_1(gspca_dev, TV8532_EEprom_TableLength, i); +/* msleep(1); */ + reg_w_1(gspca_dev, TV8532_CTRL, TV8532_CMD_EEprom_Close); + msleep(10); +} + +/* 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; + + tv_8532WriteEEprom(gspca_dev); + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 1; + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + return 0; +} + +static void tv_8532ReadRegisters(struct gspca_dev *gspca_dev) +{ + __u8 data; + + data = reg_r(gspca_dev, 0x0001); + PDEBUG(D_USBI, "register 0x01-> %x", data); + data = reg_r(gspca_dev, 0x0002); + PDEBUG(D_USBI, "register 0x02-> %x", data); + reg_r(gspca_dev, TV8532_ADWIDTH_L); + reg_r(gspca_dev, TV8532_ADWIDTH_H); + reg_r(gspca_dev, TV8532_QUANT_COMP); + reg_r(gspca_dev, TV8532_MODE_PACKET); + reg_r(gspca_dev, TV8532_SETCLK); + reg_r(gspca_dev, TV8532_POINT_L); + reg_r(gspca_dev, TV8532_POINT_H); + reg_r(gspca_dev, TV8532_POINTB_L); + reg_r(gspca_dev, TV8532_POINTB_H); + reg_r(gspca_dev, TV8532_BUDGET_L); + reg_r(gspca_dev, TV8532_BUDGET_H); + reg_r(gspca_dev, TV8532_VID_L); + reg_r(gspca_dev, TV8532_VID_H); + reg_r(gspca_dev, TV8532_PID_L); + reg_r(gspca_dev, TV8532_PID_H); + reg_r(gspca_dev, TV8532_DeviceID); + reg_r(gspca_dev, TV8532_AD_COLBEGIN_L); + reg_r(gspca_dev, TV8532_AD_COLBEGIN_H); + reg_r(gspca_dev, TV8532_AD_ROWBEGIN_L); + reg_r(gspca_dev, TV8532_AD_ROWBEGIN_H); +} + +static void tv_8532_setReg(struct gspca_dev *gspca_dev) +{ + reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_L, + ADCBEGINL); /* 0x10 */ + reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_H, + ADCBEGINH); /* also digital gain */ + reg_w_1(gspca_dev, TV8532_PART_CTRL, + TV8532_CMD_UPDATE); /* 0x00<-0x84 */ + + reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0a); + /******************************************************/ + reg_w_1(gspca_dev, TV8532_ADHEIGHT_L, ADHEIGHL); /* 0e */ + reg_w_1(gspca_dev, TV8532_ADHEIGHT_H, ADHEIGHH); /* 0f */ + reg_w_2(gspca_dev, TV8532_EXPOSURE, + EXPOL, EXPOH); /* 350d 0x014c; 1c */ + reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_L, + ADCBEGINL); /* 0x10 */ + reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_H, + ADCBEGINH); /* also digital gain */ + reg_w_1(gspca_dev, TV8532_AD_ROWBEGIN_L, + ADRBEGINL); /* 0x14 */ + + reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x00); /* 0x91 */ + reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x02); /* 0x94 */ + + reg_w_1(gspca_dev, TV8532_CTRL, + TV8532_CMD_EEprom_Close); /* 0x01 */ + + reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x00); /* 0x91 */ + reg_w_1(gspca_dev, TV8532_PART_CTRL, + TV8532_CMD_UPDATE); /* 0x00<-0x84 */ +} + +static void tv_8532_PollReg(struct gspca_dev *gspca_dev) +{ + int i; + + /* strange polling from tgc */ + for (i = 0; i < 10; i++) { + reg_w_1(gspca_dev, TV8532_SETCLK, + TESTCLK); /* 0x48; //0x08; 0x2c */ + reg_w_1(gspca_dev, TV8532_PART_CTRL, TV8532_CMD_UPDATE); + reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01); /* 0x31 */ + } +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32); + reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00); + tv_8532ReadRegisters(gspca_dev); + reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); + reg_w_2(gspca_dev, TV8532_ADHEIGHT_L, ADHEIGHL, + ADHEIGHH); /* 401d 0x0169; 0e */ + reg_w_2(gspca_dev, TV8532_EXPOSURE, EXPOL, + EXPOH); /* 350d 0x014c; 1c */ + reg_w_1(gspca_dev, TV8532_ADWIDTH_L, ADWIDTHL); /* 0x20; 0x0c */ + reg_w_1(gspca_dev, TV8532_ADWIDTH_H, ADWIDTHH); /* 0x0d */ + + /*******************************************************************/ + reg_w_1(gspca_dev, TV8532_QUANT_COMP, + TESTCOMP); /* 0x72 compressed mode 0x28 */ + reg_w_1(gspca_dev, TV8532_MODE_PACKET, + TESTLINE); /* 0x84; // CIF | 4 packet 0x29 */ + + /************************************************/ + reg_w_1(gspca_dev, TV8532_SETCLK, + TESTCLK); /* 0x48; //0x08; 0x2c */ + reg_w_1(gspca_dev, TV8532_POINT_L, + TESTPTL); /* 0x38; 0x2d */ + reg_w_1(gspca_dev, TV8532_POINT_H, + TESTPTH); /* 0x04; 0x2e */ + reg_w_1(gspca_dev, TV8532_POINTB_L, + TESTPTBL); /* 0x04; 0x2f */ + reg_w_1(gspca_dev, TV8532_POINTB_H, + TESTPTBH); /* 0x04; 0x30 */ + reg_w_1(gspca_dev, TV8532_PART_CTRL, + TV8532_CMD_UPDATE); /* 0x00<-0x84 */ + /*************************************************/ + reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01); /* 0x31 */ + msleep(200); + reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */ + /*************************************************/ + tv_8532_setReg(gspca_dev); + /*************************************************/ + reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); + /*************************************************/ + tv_8532_setReg(gspca_dev); + /*************************************************/ + tv_8532_PollReg(gspca_dev); + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int brightness = sd->brightness; + + reg_w_2(gspca_dev, TV8532_EXPOSURE, + brightness >> 8, brightness); /* 1c */ + reg_w_1(gspca_dev, TV8532_PART_CTRL, TV8532_CMD_UPDATE); +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32); + reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00); + tv_8532ReadRegisters(gspca_dev); + reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); + reg_w_2(gspca_dev, TV8532_ADHEIGHT_L, + ADHEIGHL, ADHEIGHH); /* 401d 0x0169; 0e */ +/* reg_w_2(gspca_dev, TV8532_EXPOSURE, + EXPOL, EXPOH); * 350d 0x014c; 1c */ + setbrightness(gspca_dev); + + reg_w_1(gspca_dev, TV8532_ADWIDTH_L, ADWIDTHL); /* 0x20; 0x0c */ + reg_w_1(gspca_dev, TV8532_ADWIDTH_H, ADWIDTHH); /* 0x0d */ + + /************************************************/ + reg_w_1(gspca_dev, TV8532_QUANT_COMP, + TESTCOMP); /* 0x72 compressed mode 0x28 */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { + /* 176x144 */ + reg_w_1(gspca_dev, TV8532_MODE_PACKET, + QCIFLINE); /* 0x84; // CIF | 4 packet 0x29 */ + } else { + /* 352x288 */ + reg_w_1(gspca_dev, TV8532_MODE_PACKET, + TESTLINE); /* 0x84; // CIF | 4 packet 0x29 */ + } + /************************************************/ + reg_w_1(gspca_dev, TV8532_SETCLK, + TESTCLK); /* 0x48; //0x08; 0x2c */ + reg_w_1(gspca_dev, TV8532_POINT_L, + TESTPTL); /* 0x38; 0x2d */ + reg_w_1(gspca_dev, TV8532_POINT_H, + TESTPTH); /* 0x04; 0x2e */ + reg_w_1(gspca_dev, TV8532_POINTB_L, + TESTPTBL); /* 0x04; 0x2f */ + reg_w_1(gspca_dev, TV8532_POINTB_H, + TESTPTBH); /* 0x04; 0x30 */ + reg_w_1(gspca_dev, TV8532_PART_CTRL, + TV8532_CMD_UPDATE); /* 0x00<-0x84 */ + /************************************************/ + reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01); /* 0x31 */ + msleep(200); + reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */ + /************************************************/ + tv_8532_setReg(gspca_dev); + /************************************************/ + reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); + /************************************************/ + tv_8532_setReg(gspca_dev); + /************************************************/ + tv_8532_PollReg(gspca_dev); + reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */ +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void tv8532_preprocess(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; +/* we should received a whole frame with header and EOL marker + * in gspca_dev->tmpbuf and return a GBRG pattern in gspca_dev->tmpbuf2 + * sequence 2bytes header the Alternate pixels bayer GB 4 bytes + * Alternate pixels bayer RG 4 bytes EOL */ + int width = gspca_dev->width; + int height = gspca_dev->height; + unsigned char *dst = sd->tmpbuf2; + unsigned char *data = sd->tmpbuf; + int i; + + /* precompute where is the good bayer line */ + if (((data[3] + data[width + 7]) >> 1) + + (data[4] >> 2) + + (data[width + 6] >> 1) >= ((data[2] + data[width + 6]) >> 1) + + (data[3] >> 2) + + (data[width + 5] >> 1)) + data += 3; + else + data += 2; + for (i = 0; i < height / 2; i++) { + memcpy(dst, data, width); + data += width + 3; + dst += width; + memcpy(dst, data, width); + data += width + 7; + dst += width; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (data[0] != 0x80) { + sd->packet++; + if (sd->buflen + len > sizeof sd->tmpbuf) { + if (gspca_dev->last_packet_type != DISCARD_PACKET) { + PDEBUG(D_PACK, "buffer overflow"); + gspca_dev->last_packet_type = DISCARD_PACKET; + } + return; + } + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; + return; + } + + /* here we detect 0x80 */ + /* counter is limited so we need few header for a frame :) */ + + /* header 0x80 0x80 0x80 0x80 0x80 */ + /* packet 00 63 127 145 00 */ + /* sof 0 1 1 0 0 */ + + /* update sequence */ + if (sd->packet == 63 || sd->packet == 127) + sd->synchro = 1; + + /* is there a frame start ? */ + if (sd->packet >= (gspca_dev->height >> 1) - 1) { + PDEBUG(D_PACK, "SOF > %d packet %d", sd->synchro, + sd->packet); + if (!sd->synchro) { /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + tv8532_preprocess(gspca_dev); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, sd->tmpbuf2, + gspca_dev->width * + gspca_dev->width); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, 0); + memcpy(sd->tmpbuf, data, len); + sd->buflen = len; + sd->packet = 0; + return; + } + if (gspca_dev->last_packet_type != DISCARD_PACKET) { + PDEBUG(D_PACK, + "Warning wrong TV8532 frame detection %d", + sd->packet); + gspca_dev->last_packet_type = DISCARD_PACKET; + } + return; + } + + if (!sd->synchro) { + /* Drop packet frame corrupt */ + PDEBUG(D_PACK, "DROP SOF %d packet %d", + sd->synchro, sd->packet); + sd->packet = 0; + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + sd->synchro = 1; + sd->packet++; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ +} + +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_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x046d, 0x0920), DVNM("QC Express")}, + {USB_DEVICE(0x046d, 0x0921), DVNM("Labtec Webcam")}, + {USB_DEVICE(0x0545, 0x808b), DVNM("Veo Stingray")}, + {USB_DEVICE(0x0545, 0x8333), DVNM("Veo Stingray")}, + {USB_DEVICE(0x0923, 0x010f), DVNM("ICM532 cams")}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c new file mode 100644 index 00000000000..fcf2c9e3257 --- /dev/null +++ b/drivers/media/video/gspca/vc032x.c @@ -0,0 +1,1818 @@ +/* + * Z-star vc0321 library + * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl + * Copyright (C) 2006 Michel Xhaard + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "vc032x" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); +MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char autogain; + unsigned char lightfreq; + + char qindex; + char bridge; +#define BRIDGE_VC0321 0 +#define BRIDGE_VC0323 1 + char sensor; +#define SENSOR_HV7131R 0 +#define SENSOR_MI1320 1 +#define SENSOR_MI1310_SOC 2 +#define SENSOR_OV7660 3 +#define SENSOR_OV7670 4 +#define SENSOR_PO3130NC 5 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: No, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 1 + .default_value = FREQ_DEF, + .default_value = 1, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, +}; + +static struct v4l2_pix_format vc0321_mode[] = { + {320, 240, V4L2_PIX_FMT_YUV420, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_YUV420, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static struct v4l2_pix_format vc0323_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static const __u8 mi1310_socinitVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, + {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, + {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, + {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, + {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, + {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, + {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, + {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, + {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, + {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, + {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, + {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, + {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x00, 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}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, + {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, + {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, + {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, + {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, + {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, + {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, + {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, + {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, + {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, + {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, + {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, + {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, + {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, + {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, + {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, + {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, + {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x34, 0x0c, 0x50, 0xbb}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {}, +}; +static const __u8 mi1310_socinitQVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb}, + {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {}, +}; + +static const __u8 mi1320_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static const __u8 mi1320_matrix[9] = { + 0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52 +}; +static const __u8 mi1320_initVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, {0xb3, 0x23, 0xc0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, {0xbc, 0x00, 0xd0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, + {0x0d, 0x00, 0x09, 0xbb}, {0x00, 0x01, 0x00, 0xdd}, + {0x0d, 0x00, 0x08, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0xa1, 0x05, 0x00, 0xbb}, {0xa4, 0x03, 0xc0, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x9f, 0x0b, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0x20, 0x01, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xf0, 0x00, 0x01, 0xbb}, {0x9d, 0x3c, 0xa0, 0xbb}, + {0x47, 0x30, 0x30, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x0a, 0x80, 0x11, 0xbb}, {0x35, 0x00, 0x22, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x9d, 0xc5, 0x05, 0xbb}, + {0xdc, 0x0f, 0xfc, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0x74, 0x0e, 0xbb}, {0x80, 0x00, 0x06, 0xbb}, + {0x81, 0x04, 0x00, 0xbb}, {0x82, 0x01, 0x02, 0xbb}, + {0x83, 0x03, 0x02, 0xbb}, {0x84, 0x05, 0x00, 0xbb}, + {0x85, 0x01, 0x00, 0xbb}, {0x86, 0x03, 0x02, 0xbb}, + {0x87, 0x05, 0x00, 0xbb}, {0x88, 0x01, 0x00, 0xbb}, + {0x89, 0x02, 0x02, 0xbb}, {0x8a, 0xfd, 0x04, 0xbb}, + {0x8b, 0xfc, 0xfd, 0xbb}, {0x8c, 0xff, 0xfd, 0xbb}, + {0x8d, 0x00, 0x00, 0xbb}, {0x8e, 0xfe, 0x05, 0xbb}, + {0x8f, 0xfc, 0xfd, 0xbb}, {0x90, 0xfe, 0xfd, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, {0x92, 0xfe, 0x03, 0xbb}, + {0x93, 0xfd, 0xfe, 0xbb}, {0x94, 0xff, 0xfd, 0xbb}, + {0x95, 0x00, 0x00, 0xbb}, {0xb6, 0x07, 0x05, 0xbb}, + {0xb7, 0x13, 0x06, 0xbb}, {0xb8, 0x08, 0x06, 0xbb}, + {0xb9, 0x14, 0x08, 0xbb}, {0xba, 0x06, 0x05, 0xbb}, + {0xbb, 0x13, 0x06, 0xbb}, {0xbc, 0x03, 0x01, 0xbb}, + {0xbd, 0x03, 0x04, 0xbb}, {0xbe, 0x00, 0x02, 0xbb}, + {0xbf, 0x03, 0x01, 0xbb}, {0xc0, 0x02, 0x04, 0xbb}, + {0xc1, 0x00, 0x04, 0xbb}, {0xc2, 0x02, 0x01, 0xbb}, + {0xc3, 0x01, 0x03, 0xbb}, {0xc4, 0x00, 0x04, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x05, 0x01, 0x13, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb}, + {0x08, 0x00, 0x27, 0xbb}, {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x39, 0x03, 0x0d, 0xbb}, + {0x3a, 0x06, 0x1b, 0xbb}, {0x3b, 0x00, 0x95, 0xbb}, + {0x3c, 0x04, 0xdb, 0xbb}, {0x57, 0x02, 0x00, 0xbb}, + {0x58, 0x02, 0x66, 0xbb}, {0x59, 0x00, 0xff, 0xbb}, + {0x5a, 0x01, 0x33, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, {0x64, 0x5e, 0x1c, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0x36, 0x68, 0x10, 0xbb}, {0x00, 0x00, 0x30, 0xdd}, + {0x37, 0x82, 0x00, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x05, 0xcc}, {0xb6, 0x02, 0x00, 0xcc}, + {0xb6, 0x05, 0x04, 0xcc}, {0xb6, 0x04, 0x00, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x29, 0xcc}, + {0xb6, 0x18, 0x0a, 0xcc}, {0xb6, 0x17, 0x00, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 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}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; +static const __u8 mi1320_initQVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 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, 0x17, 0x7f, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd0, 0xcc}, {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, {0x0d, 0x00, 0x09, 0xbb}, + {0x00, 0x01, 0x00, 0xdd}, {0x0d, 0x00, 0x08, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x02, 0x00, 0x64, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x00, 0xbb}, {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x9d, 0x3c, 0xa0, 0xbb}, {0x47, 0x30, 0x30, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x0a, 0x80, 0x11, 0xbb}, + {0x35, 0x00, 0x22, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0x9d, 0xc5, 0x05, 0xbb}, {0xdc, 0x0f, 0xfc, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0x74, 0x0e, 0xbb}, + {0x80, 0x00, 0x06, 0xbb}, {0x81, 0x04, 0x00, 0xbb}, + {0x82, 0x01, 0x02, 0xbb}, {0x83, 0x03, 0x02, 0xbb}, + {0x84, 0x05, 0x00, 0xbb}, {0x85, 0x01, 0x00, 0xbb}, + {0x86, 0x03, 0x02, 0xbb}, {0x87, 0x05, 0x00, 0xbb}, + {0x88, 0x01, 0x00, 0xbb}, {0x89, 0x02, 0x02, 0xbb}, + {0x8a, 0xfd, 0x04, 0xbb}, {0x8b, 0xfc, 0xfd, 0xbb}, + {0x8c, 0xff, 0xfd, 0xbb}, {0x8d, 0x00, 0x00, 0xbb}, + {0x8e, 0xfe, 0x05, 0xbb}, {0x8f, 0xfc, 0xfd, 0xbb}, + {0x90, 0xfe, 0xfd, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xfe, 0x03, 0xbb}, {0x93, 0xfd, 0xfe, 0xbb}, + {0x94, 0xff, 0xfd, 0xbb}, {0x95, 0x00, 0x00, 0xbb}, + {0xb6, 0x07, 0x05, 0xbb}, {0xb7, 0x13, 0x06, 0xbb}, + {0xb8, 0x08, 0x06, 0xbb}, {0xb9, 0x14, 0x08, 0xbb}, + {0xba, 0x06, 0x05, 0xbb}, {0xbb, 0x13, 0x06, 0xbb}, + {0xbc, 0x03, 0x01, 0xbb}, {0xbd, 0x03, 0x04, 0xbb}, + {0xbe, 0x00, 0x02, 0xbb}, {0xbf, 0x03, 0x01, 0xbb}, + {0xc0, 0x02, 0x04, 0xbb}, {0xc1, 0x00, 0x04, 0xbb}, + {0xc2, 0x02, 0x01, 0xbb}, {0xc3, 0x01, 0x03, 0xbb}, + {0xc4, 0x00, 0x04, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x00, 0x00, 0xbb}, {0x2e, 0x00, 0x00, 0xbb}, + {0x2e, 0x0c, 0x5b, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x68, 0x10, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, {0x37, 0x81, 0x00, 0xbb}, + {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}, + {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {} +}; + +static const __u8 po3130_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static const __u8 po3130_matrix[9] = { + 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 +}; + +static const __u8 po3130_initVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x04, 0xcc}, {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, 0x1a, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe8, 0xcc}, {0xb8, 0x08, 0xe8, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x71, 0xcc}, + {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 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, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa}, + {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa}, + {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa}, + {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa}, + {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa}, + {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa}, + {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa}, + {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x5a, 0x04, 0xaa}, + {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa}, + {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa}, + {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa}, + {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa}, + {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa}, + {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa}, + {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa}, + {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa}, + {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa}, + {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa}, + {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa}, + {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa}, + {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa}, + {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa}, + {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa}, + {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa}, + {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa}, + {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa}, + {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0x4c, 0x07, 0xaa}, + {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa}, + {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa}, +/* {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, */ + {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 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}, {0x00, 0x05, 0x00, 0xaa}, + {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; +static const __u8 po3130_rundata[][4] = { + {0x00, 0x47, 0x45, 0xaa}, {0x00, 0x48, 0x9b, 0xaa}, + {0x00, 0x49, 0x3a, 0xaa}, {0x00, 0x4a, 0x01, 0xaa}, + {0x00, 0x44, 0x40, 0xaa}, +/* {0x00, 0xd5, 0x7c, 0xaa}, */ + {0x00, 0xad, 0x04, 0xaa}, {0x00, 0xae, 0x00, 0xaa}, + {0x00, 0xb0, 0x78, 0xaa}, {0x00, 0x98, 0x02, 0xaa}, + {0x00, 0x94, 0x25, 0xaa}, {0x00, 0x95, 0x25, 0xaa}, + {0x00, 0x59, 0x68, 0xaa}, {0x00, 0x44, 0x20, 0xaa}, + {0x00, 0x17, 0x50, 0xaa}, {0x00, 0x19, 0x50, 0xaa}, + {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0xd1, 0x3c, 0xaa}, + {0x00, 0x1e, 0x06, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {} +}; + +static const __u8 po3130_initQVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x09, 0xcc}, + {0xb3, 0x00, 0x04, 0xcc}, {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, 0x1a, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb8, 0x08, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, + {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 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, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa}, + {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa}, + {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa}, + {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa}, + {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa}, + {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa}, + {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa}, + {0x00, 0x59, 0x6f, 0xaa}, {0x00, 0x5a, 0x04, 0xaa}, + {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa}, + {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa}, + {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa}, + {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa}, + {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa}, + {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa}, + {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa}, + {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa}, + {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa}, + {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa}, + {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa}, + {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa}, + {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa}, + {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa}, + {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa}, + {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa}, + {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa}, + {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa}, + {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0x4c, 0x07, 0xaa}, + {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa}, + {0x00, 0x59, 0x66, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa}, + {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 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}, {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}, {0x00, 0x05, 0x00, 0xaa}, + {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; + +static const __u8 hv7131r_gamma[17] = { +/* 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + * 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff */ + 0x04, 0x1a, 0x36, 0x55, 0x6f, 0x87, 0x9d, 0xb0, 0xc1, + 0xcf, 0xda, 0xe4, 0xec, 0xf3, 0xf8, 0xfd, 0xff +}; +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, 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, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 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, 0x30, 0x50, 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, 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}, + {0x00, 0x30, 0x18, 0xaa}, + {} +}; + +static const __u8 hv7131r_initQVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {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, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 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}, + {0xb8, 0x30, 0x50, 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, 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}, {0x00, 0x30, 0x18, 0xaa}, + {} +}; + +static const __u8 ov7660_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static const __u8 ov7660_matrix[9] = { + 0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62 +}; +static const __u8 ov7660_initVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, + {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */ + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc}, + {0xb3, 0x1f, 0x02, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb8, 0x00, 0x33, 0xcc}, /* 13 */ + {0xb8, 0x01, 0x7d, 0xcc}, + {0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, + {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x12, 0x05, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, + {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ + {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ + {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa}, + {0x00, 0x36, 0x00, 0xaa}, + {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa}, + {0x00, 0x39, 0x43, 0xaa}, + {0x00, 0x8d, 0xcf, 0xaa}, + {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa}, + {0x00, 0x0f, 0x62, 0xaa}, + {0x00, 0x35, 0x84, 0xaa}, + {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */ + {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/ + {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */ + {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, + {0x00, 0x02, 0x80, 0xaa}, + {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}, + + {0x00, 0x29, 0x3c, 0xaa}, {0xb3, 0x01, 0x45, 0xcc}, + {} +}; +static const __u8 ov7660_initQVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */ + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc}, + {0xb3, 0x1f, 0x02, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb8, 0x00, 0x33, 0xcc}, /* 13 */ + {0xb8, 0x01, 0x7d, 0xcc}, +/* sizer */ + {0xbc, 0x00, 0xd3, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, + {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, + {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ + {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ + {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa}, + {0x00, 0x36, 0x00, 0xaa}, + {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa}, + {0x00, 0x39, 0x43, 0xaa}, {0x00, 0x8d, 0xcf, 0xaa}, + {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa}, + {0x00, 0x0f, 0x62, 0xaa}, {0x00, 0x35, 0x84, 0xaa}, + {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */ + {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/ + {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */ + {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, + {0x00, 0x02, 0x80, 0xaa}, +/* sizer filters */ + {0xbc, 0x02, 0x08, 0xcc}, + {0xbc, 0x03, 0x70, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 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, 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}, /* ff */ + {0x00, 0x29, 0x3c, 0xaa}, + {0xb3, 0x01, 0x45, 0xcc}, /* 45 */ + {} +}; + +static const __u8 ov7660_50HZ[][4] = { + {0x00, 0x3b, 0x08, 0xaa}, + {0x00, 0x9d, 0x40, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {} +}; + +static const __u8 ov7660_60HZ[][4] = { + {0x00, 0x3b, 0x00, 0xaa}, + {0x00, 0x9e, 0x40, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {} +}; + +static const __u8 ov7660_NoFliker[][4] = { + {0x00, 0x13, 0x87, 0xaa}, + {} +}; + +static const __u8 ov7670_initVGA_JPG[][4] = { + {0xb3, 0x01, 0x05, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xbc, 0x00, 0x41, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x13, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x77, 0x05, 0xaa}, + {}, +}; + +static const __u8 ov7670_initQVGA_JPG[][4] = { + {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x30, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x21, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 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}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x77, 0x05, 0xaa }, + {}, +}; + +struct sensor_info { + int sensorId; + __u8 I2cAdd; + __u8 IdAdd; + __u16 VpId; + __u8 m1; + __u8 m2; + __u8 op; + }; + +static const struct sensor_info sensor_info_data[] = { +/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ + {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, + {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, + {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, + {SENSOR_MI1320, 0x80 | 0xc8, 0x00, 0x148c, 0x64, 0x65, 0x01}, + {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, + {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, +}; + +/* read 'len' bytes in gspca_dev->usb_buf */ +static void reg_r(struct gspca_dev *gspca_dev, + __u16 req, + __u16 index, + __u16 len) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, /* value */ + index, gspca_dev->usb_buf, len, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, + 500); +} + +static void read_sensor_register(struct gspca_dev *gspca_dev, + __u16 address, __u16 *value) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 ldata, mdata, hdata; + int retry = 50; + + *value = 0; + + reg_r(gspca_dev, 0xa1, 0xb33f, 1); + /*PDEBUG(D_PROBE, " I2c Bus Busy Wait 0x%02X ", tmpvalue); */ + if (!(gspca_dev->usb_buf[0] & 0x02)) { + PDEBUG(D_ERR, "I2c Bus Busy Wait %d", + gspca_dev->usb_buf[0] & 0x02); + return; + } + reg_w(dev, 0xa0, address, 0xb33a); + reg_w(dev, 0xa0, 0x02, 0xb339); + + reg_r(gspca_dev, 0xa1, 0xb33b, 1); + while (retry-- && gspca_dev->usb_buf[0]) { + reg_r(gspca_dev, 0xa1, 0xb33b, 1); +/* PDEBUG(D_PROBE, "Read again 0xb33b %d", tmpvalue); */ + msleep(1); + } + reg_r(gspca_dev, 0xa1, 0xb33e, 1); + hdata = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0xa1, 0xb33d, 1); + mdata = gspca_dev->usb_buf[0]; + reg_r(gspca_dev, 0xa1, 0xb33c, 1); + ldata = gspca_dev->usb_buf[0]; + PDEBUG(D_PROBE, "Read Sensor h (0x%02X) m (0x%02X) l (0x%02X)", + hdata, mdata, ldata); + reg_r(gspca_dev, 0xa1, 0xb334, 1); + if (gspca_dev->usb_buf[0] == 0x02) + *value = (ldata << 8) + mdata; + else + *value = ldata; +} + +static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int i; + __u16 value; + const struct sensor_info *ptsensor_info; + + reg_r(gspca_dev, 0xa1, 0xbfcf, 1); + PDEBUG(D_PROBE, "check sensor header %d", gspca_dev->usb_buf[0]); + for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) { + ptsensor_info = &sensor_info_data[i]; + reg_w(dev, 0xa0, 0x02, 0xb334); + reg_w(dev, 0xa0, ptsensor_info->m1, 0xb300); + reg_w(dev, 0xa0, ptsensor_info->m2, 0xb300); + reg_w(dev, 0xa0, 0x01, 0xb308); + reg_w(dev, 0xa0, 0x0c, 0xb309); + reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); +/* PDEBUG(D_PROBE, + "check sensor VC032X -> %d Add -> ox%02X!", + i, ptsensor_info->I2cAdd); */ + reg_w(dev, 0xa0, ptsensor_info->op, 0xb301); + read_sensor_register(gspca_dev, ptsensor_info->IdAdd, &value); + if (value == ptsensor_info->VpId) { +/* PDEBUG(D_PROBE, "find sensor VC032X -> ox%04X!", + ptsensor_info->VpId); */ + return ptsensor_info->sensorId; + } + } + return -1; +} + +static __u8 i2c_write(struct gspca_dev *gspca_dev, + __u8 reg, const __u8 *val, __u8 size) +{ + struct usb_device *dev = gspca_dev->dev; + + if (size > 3 || size < 1) + return -EINVAL; + reg_r(gspca_dev, 0xa1, 0xb33f, 1); + reg_w(dev, 0xa0, size, 0xb334); + reg_w(dev, 0xa0, reg, 0xb33a); + switch (size) { + case 1: + reg_w(dev, 0xa0, val[0], 0xb336); + break; + case 2: + reg_w(dev, 0xa0, val[0], 0xb336); + reg_w(dev, 0xa0, val[1], 0xb337); + break; + case 3: + reg_w(dev, 0xa0, val[0], 0xb336); + reg_w(dev, 0xa0, val[1], 0xb337); + reg_w(dev, 0xa0, val[2], 0xb338); + break; + default: + reg_w(dev, 0xa0, 0x01, 0xb334); + return -EINVAL; + } + reg_w(dev, 0xa0, 0x01, 0xb339); + reg_r(gspca_dev, 0xa1, 0xb33b, 1); + return gspca_dev->usb_buf[0] == 0; +} + +static void put_tab_to_reg(struct gspca_dev *gspca_dev, + const __u8 *tab, __u8 tabsize, __u16 addr) +{ + int j; + __u16 ad = addr; + + for (j = 0; j < tabsize; j++) + reg_w(gspca_dev->dev, 0xa0, tab[j], ad++); +} + +static void usb_exchange(struct gspca_dev *gspca_dev, + const __u8 data[][4]) +{ + struct usb_device *dev = gspca_dev->dev; + int i = 0; + + for (;;) { + switch (data[i][3]) { + default: + return; + case 0xcc: /* normal write */ + reg_w(dev, 0xa0, data[i][2], + ((data[i][0])<<8) | data[i][1]); + break; + case 0xaa: /* i2c op */ + i2c_write(gspca_dev, data[i][1], &data[i][2], 1); + break; + case 0xbb: /* i2c op */ + i2c_write(gspca_dev, data[i][0], &data[i][1], 2); + break; + case 0xdd: + msleep(data[i][2] + 10); + break; + } + i++; + } + /*not reached*/ +} + +/* + "GammaT"=hex:04,17,31,4f,6a,83,99,ad,bf,ce,da,e5,ee,f5,fb,ff,ff + "MatrixT"=hex:60,f9,e5,e7,50,05,f3,e6,66 + */ + +static void vc0321_reset(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev->dev, 0xa0, 0x00, 0xb04d); + reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb301); + msleep(100); + reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb003); + msleep(100); +} + +/* 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 usb_device *dev = gspca_dev->dev; + struct cam *cam; + int sensor; + __u16 product; + + product = id->idProduct; + sd->bridge = BRIDGE_VC0321; + switch (id->idVendor) { + case 0x0ac8: /* Vimicro z-star */ + switch (product) { + case 0x0323: + sd->bridge = BRIDGE_VC0323; + break; + } + break; + case 0x17ef: /* Lenovo */ +/* switch (product) { */ +/* case 0x4802: * Lenovo MI1310_SOC */ + sd->bridge = BRIDGE_VC0323; +/* break; */ +/* } */ + break; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x02; + if (sd->bridge == BRIDGE_VC0321) { + cam->cam_mode = vc0321_mode; + cam->nmodes = ARRAY_SIZE(vc0321_mode); + } else { + cam->cam_mode = vc0323_mode; + cam->nmodes = ARRAY_SIZE(vc0323_mode); + } + + vc0321_reset(gspca_dev); + sensor = vc032x_probe_sensor(gspca_dev); + switch (sensor) { + case -1: + PDEBUG(D_PROBE, "Unknown sensor..."); + return -EINVAL; + case SENSOR_HV7131R: + PDEBUG(D_PROBE, "Find Sensor HV7131R"); + sd->sensor = SENSOR_HV7131R; + break; + case SENSOR_MI1310_SOC: + PDEBUG(D_PROBE, "Find Sensor MI1310_SOC"); + sd->sensor = SENSOR_MI1310_SOC; + break; + case SENSOR_MI1320: + PDEBUG(D_PROBE, "Find Sensor MI1320"); + sd->sensor = SENSOR_MI1320; + break; + case SENSOR_OV7660: + PDEBUG(D_PROBE, "Find Sensor OV7660"); + sd->sensor = SENSOR_OV7660; + break; + case SENSOR_OV7670: + PDEBUG(D_PROBE, "Find Sensor OV7670"); + sd->sensor = SENSOR_OV7670; + break; + case SENSOR_PO3130NC: + PDEBUG(D_PROBE, "Find Sensor PO3130NC"); + sd->sensor = SENSOR_PO3130NC; + break; + } + + sd->qindex = 7; + sd->autogain = AUTOGAIN_DEF; + sd->lightfreq = FREQ_DEF; + + if (sd->bridge == BRIDGE_VC0321) { + reg_r(gspca_dev, 0x8a, 0, 3); + reg_w(dev, 0x87, 0x00, 0x0f0f); + + reg_r(gspca_dev, 0x8b, 0, 3); + reg_w(dev, 0x88, 0x00, 0x0202); + } + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static void setquality(struct gspca_dev *gspca_dev) +{ +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ +} + +static void setlightfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static const __u8 (*ov7660_freq_tb[3])[4] = + {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ}; + + if (sd->sensor != SENSOR_OV7660) + return; + usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]); +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + const __u8 *GammaT = NULL; + const __u8 *MatrixT = NULL; + int mode; + + /* Assume start use the good resolution from gspca_dev->mode */ + if (sd->bridge == BRIDGE_VC0321) { + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfec); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfed); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfee); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfef); + } + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + switch (sd->sensor) { + case SENSOR_HV7131R: + GammaT = hv7131r_gamma; + MatrixT = hv7131r_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, hv7131r_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, hv7131r_initVGA_data); + } + break; + case SENSOR_OV7660: + GammaT = ov7660_gamma; + MatrixT = ov7660_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, ov7660_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, ov7660_initVGA_data); + } + break; + case SENSOR_OV7670: + /*GammaT = ov7660_gamma; */ + /*MatrixT = ov7660_matrix; */ + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, ov7670_initQVGA_JPG); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, ov7670_initVGA_JPG); + } + break; + case SENSOR_MI1310_SOC: + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, mi1310_socinitQVGA_JPG); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, mi1310_socinitVGA_JPG); + } + break; + case SENSOR_MI1320: + GammaT = mi1320_gamma; + MatrixT = mi1320_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, mi1320_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, mi1320_initVGA_data); + } + break; + case SENSOR_PO3130NC: + GammaT = po3130_gamma; + MatrixT = po3130_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, po3130_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, po3130_initVGA_data); + } + usb_exchange(gspca_dev, po3130_rundata); + break; + default: + PDEBUG(D_PROBE, "Damned !! no sensor found Bye"); + return; + } + if (GammaT && MatrixT) { + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a); + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb85b); + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c); + put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c); + + /* Seem SHARPNESS */ + /* + reg_w(gspca_dev->dev, 0xa0, 0x80, 0xb80a); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80b); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80e); + */ + /* all 0x40 ??? do nothing + reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb822); + reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb823); + reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb824); + */ + /* Only works for HV7131R ?? + reg_r (gspca_dev, 0xa1, 0xb881, 1); + reg_w(gspca_dev->dev, 0xa0, 0xfe01, 0xb881); + reg_w(gspca_dev->dev, 0xa0, 0x79, 0xb801); + */ + /* only hv7131r et ov7660 + reg_w(gspca_dev->dev, 0xa0, 0x20, 0xb827); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb826); * ISP_GAIN 80 + reg_w(gspca_dev->dev, 0xa0, 0x23, 0xb800); * ISP CTRL_BAS + */ + /* set the led on 0x0892 0x0896 */ + reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + msleep(100); + setquality(gspca_dev); + setautogain(gspca_dev); + setlightfreq(gspca_dev); + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x89, 0xffff, 0xffff); + reg_w(dev, 0xa0, 0x01, 0xb301); + reg_w(dev, 0xa0, 0x09, 0xb003); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x89, 0xffff, 0xffff); +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ +/* struct usb_device *dev = gspca_dev->dev; + __u8 buffread; + + reg_w(dev, 0x89, 0xffff, 0xffff); + reg_w(dev, 0xa0, 0x01, 0xb301); + reg_w(dev, 0xa0, 0x09, 0xb303); + reg_w(dev, 0x89, 0xffff, 0xffff); +*/ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso pkt length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (data[0] == 0xff && data[1] == 0xd8) { + PDEBUG(D_PACK, + "vc032x header packet found len %d", len); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + if (sd->bridge == BRIDGE_VC0321) { +#define VCHDRSZ 46 + data += VCHDRSZ; + len -= VCHDRSZ; +#undef VCHDRSZ + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + return; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (gspca_dev->streaming) + setautogain(gspca_dev); + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setlightfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x046d, 0x0892), DVNM("Logitech Orbicam")}, + {USB_DEVICE(0x046d, 0x0896), DVNM("Logitech Orbicam")}, + {USB_DEVICE(0x0ac8, 0x0321), DVNM("Vimicro generic vc0321")}, + {USB_DEVICE(0x0ac8, 0x0323), DVNM("Vimicro Vc0323")}, + {USB_DEVICE(0x0ac8, 0x0328), DVNM("A4Tech PK-130MG")}, + {USB_DEVICE(0x0ac8, 0xc001), DVNM("Sony embedded vimicro")}, + {USB_DEVICE(0x0ac8, 0xc002), DVNM("Sony embedded vimicro")}, + {USB_DEVICE(0x17ef, 0x4802), DVNM("Lenovo Vc0323+MI1310_SOC")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/zc3xx-reg.h b/drivers/media/video/gspca/zc3xx-reg.h new file mode 100644 index 00000000000..f52e09c2cc1 --- /dev/null +++ b/drivers/media/video/gspca/zc3xx-reg.h @@ -0,0 +1,261 @@ +/* + * zc030x registers + * + * Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@infradead.org> + * + * The register aliases used here came from this driver: + * http://zc0302.sourceforge.net/zc0302.php + * + * This code is placed under the terms of the GNU General Public License v2 + */ + +/* Define the register map */ +#define ZC3XX_R000_SYSTEMCONTROL 0x0000 +#define ZC3XX_R001_SYSTEMOPERATING 0x0001 + +/* Picture size */ +#define ZC3XX_R002_CLOCKSELECT 0x0002 +#define ZC3XX_R003_FRAMEWIDTHHIGH 0x0003 +#define ZC3XX_R004_FRAMEWIDTHLOW 0x0004 +#define ZC3XX_R005_FRAMEHEIGHTHIGH 0x0005 +#define ZC3XX_R006_FRAMEHEIGHTLOW 0x0006 + +/* JPEG control */ +#define ZC3XX_R008_CLOCKSETTING 0x0008 + +/* Test mode */ +#define ZC3XX_R00B_TESTMODECONTROL 0x000b + +/* Frame retreiving */ +#define ZC3XX_R00C_LASTACQTIME 0x000c +#define ZC3XX_R00D_MONITORRES 0x000d +#define ZC3XX_R00E_TIMESTAMPHIGH 0x000e +#define ZC3XX_R00F_TIMESTAMPLOW 0x000f +#define ZC3XX_R018_FRAMELOST 0x0018 +#define ZC3XX_R019_AUTOADJUSTFPS 0x0019 +#define ZC3XX_R01A_LASTFRAMESTATE 0x001a +#define ZC3XX_R025_DATACOUNTER 0x0025 + +/* Stream and sensor specific */ +#define ZC3XX_R010_CMOSSENSORSELECT 0x0010 +#define ZC3XX_R011_VIDEOSTATUS 0x0011 +#define ZC3XX_R012_VIDEOCONTROLFUNC 0x0012 + +/* Horizontal and vertical synchros */ +#define ZC3XX_R01D_HSYNC_0 0x001d +#define ZC3XX_R01E_HSYNC_1 0x001e +#define ZC3XX_R01F_HSYNC_2 0x001f +#define ZC3XX_R020_HSYNC_3 0x0020 + +/* Target picture size in byte */ +#define ZC3XX_R022_TARGETPICTSIZE_0 0x0022 +#define ZC3XX_R023_TARGETPICTSIZE_1 0x0023 +#define ZC3XX_R024_TARGETPICTSIZE_2 0x0024 + +/* Audio registers */ +#define ZC3XX_R030_AUDIOADC 0x0030 +#define ZC3XX_R031_AUDIOSTREAMSTATUS 0x0031 +#define ZC3XX_R032_AUDIOSTATUS 0x0032 + +/* Sensor interface */ +#define ZC3XX_R080_HBLANKHIGH 0x0080 +#define ZC3XX_R081_HBLANKLOW 0x0081 +#define ZC3XX_R082_RESETLEVELADDR 0x0082 +#define ZC3XX_R083_RGAINADDR 0x0083 +#define ZC3XX_R084_GGAINADDR 0x0084 +#define ZC3XX_R085_BGAINADDR 0x0085 +#define ZC3XX_R086_EXPTIMEHIGH 0x0086 +#define ZC3XX_R087_EXPTIMEMID 0x0087 +#define ZC3XX_R088_EXPTIMELOW 0x0088 +#define ZC3XX_R089_RESETBLACKHIGH 0x0089 +#define ZC3XX_R08A_RESETWHITEHIGH 0x008a +#define ZC3XX_R08B_I2CDEVICEADDR 0x008b +#define ZC3XX_R08C_I2CIDLEANDNACK 0x008c +#define ZC3XX_R08D_COMPABILITYMODE 0x008d +#define ZC3XX_R08E_COMPABILITYMODE2 0x008e + +/* I2C control */ +#define ZC3XX_R090_I2CCOMMAND 0x0090 +#define ZC3XX_R091_I2CSTATUS 0x0091 +#define ZC3XX_R092_I2CADDRESSSELECT 0x0092 +#define ZC3XX_R093_I2CSETVALUE 0x0093 +#define ZC3XX_R094_I2CWRITEACK 0x0094 +#define ZC3XX_R095_I2CREAD 0x0095 +#define ZC3XX_R096_I2CREADACK 0x0096 + +/* Window inside the sensor array */ +#define ZC3XX_R097_WINYSTARTHIGH 0x0097 +#define ZC3XX_R098_WINYSTARTLOW 0x0098 +#define ZC3XX_R099_WINXSTARTHIGH 0x0099 +#define ZC3XX_R09A_WINXSTARTLOW 0x009a +#define ZC3XX_R09B_WINHEIGHTHIGH 0x009b +#define ZC3XX_R09C_WINHEIGHTLOW 0x009c +#define ZC3XX_R09D_WINWIDTHHIGH 0x009d +#define ZC3XX_R09E_WINWIDTHLOW 0x009e +#define ZC3XX_R119_FIRSTYHIGH 0x0119 +#define ZC3XX_R11A_FIRSTYLOW 0x011a +#define ZC3XX_R11B_FIRSTXHIGH 0x011b +#define ZC3XX_R11C_FIRSTXLOW 0x011c + +/* Max sensor array size */ +#define ZC3XX_R09F_MAXXHIGH 0x009f +#define ZC3XX_R0A0_MAXXLOW 0x00a0 +#define ZC3XX_R0A1_MAXYHIGH 0x00a1 +#define ZC3XX_R0A2_MAXYLOW 0x00a2 +#define ZC3XX_R0A3_EXPOSURETIMEHIGH 0x00a3 +#define ZC3XX_R0A4_EXPOSURETIMELOW 0x00a4 +#define ZC3XX_R0A5_EXPOSUREGAIN 0x00a5 +#define ZC3XX_R0A6_EXPOSUREBLACKLVL 0x00a6 + +/* Other registers */ +#define ZC3XX_R100_OPERATIONMODE 0x0100 +#define ZC3XX_R101_SENSORCORRECTION 0x0101 + +/* Gains */ +#define ZC3XX_R116_RGAIN 0x0116 +#define ZC3XX_R117_GGAIN 0x0117 +#define ZC3XX_R118_BGAIN 0x0118 +#define ZC3XX_R11D_GLOBALGAIN 0x011d +#define ZC3XX_R1A8_DIGITALGAIN 0x01a8 +#define ZC3XX_R1A9_DIGITALLIMITDIFF 0x01a9 +#define ZC3XX_R1AA_DIGITALGAINSTEP 0x01aa + +/* Auto correction */ +#define ZC3XX_R180_AUTOCORRECTENABLE 0x0180 +#define ZC3XX_R181_WINXSTART 0x0181 +#define ZC3XX_R182_WINXWIDTH 0x0182 +#define ZC3XX_R183_WINXCENTER 0x0183 +#define ZC3XX_R184_WINYSTART 0x0184 +#define ZC3XX_R185_WINYWIDTH 0x0185 +#define ZC3XX_R186_WINYCENTER 0x0186 + +/* Gain range */ +#define ZC3XX_R187_MAXGAIN 0x0187 +#define ZC3XX_R188_MINGAIN 0x0188 + +/* Auto exposure and white balance */ +#define ZC3XX_R189_AWBSTATUS 0x0189 +#define ZC3XX_R18A_AWBFREEZE 0x018a +#define ZC3XX_R18B_AESTATUS 0x018b +#define ZC3XX_R18C_AEFREEZE 0x018c +#define ZC3XX_R18F_AEUNFREEZE 0x018f +#define ZC3XX_R190_EXPOSURELIMITHIGH 0x0190 +#define ZC3XX_R191_EXPOSURELIMITMID 0x0191 +#define ZC3XX_R192_EXPOSURELIMITLOW 0x0192 +#define ZC3XX_R195_ANTIFLICKERHIGH 0x0195 +#define ZC3XX_R196_ANTIFLICKERMID 0x0196 +#define ZC3XX_R197_ANTIFLICKERLOW 0x0197 + +/* What is this ? */ +#define ZC3XX_R18D_YTARGET 0x018d +#define ZC3XX_R18E_RESETLVL 0x018e + +/* Color */ +#define ZC3XX_R1A0_REDMEANAFTERAGC 0x01a0 +#define ZC3XX_R1A1_GREENMEANAFTERAGC 0x01a1 +#define ZC3XX_R1A2_BLUEMEANAFTERAGC 0x01a2 +#define ZC3XX_R1A3_REDMEANAFTERAWB 0x01a3 +#define ZC3XX_R1A4_GREENMEANAFTERAWB 0x01a4 +#define ZC3XX_R1A5_BLUEMEANAFTERAWB 0x01a5 +#define ZC3XX_R1A6_YMEANAFTERAE 0x01a6 +#define ZC3XX_R1A7_CALCGLOBALMEAN 0x01a7 + +#define ZC3XX_R1A2_BLUEMEANAFTERAGC 0x01a2 + +/* Matrixes */ + +/* Color matrix is like : + R' = R * RGB00 + G * RGB01 + B * RGB02 + RGB03 + G' = R * RGB10 + G * RGB11 + B * RGB22 + RGB13 + B' = R * RGB20 + G * RGB21 + B * RGB12 + RGB23 + */ +#define ZC3XX_R10A_RGB00 0x010a +#define ZC3XX_R10B_RGB01 0x010b +#define ZC3XX_R10C_RGB02 0x010c +#define ZC3XX_R113_RGB03 0x0113 +#define ZC3XX_R10D_RGB10 0x010d +#define ZC3XX_R10E_RGB11 0x010e +#define ZC3XX_R10F_RGB12 0x010f +#define ZC3XX_R114_RGB13 0x0114 +#define ZC3XX_R110_RGB20 0x0110 +#define ZC3XX_R111_RGB21 0x0111 +#define ZC3XX_R112_RGB22 0x0112 +#define ZC3XX_R115_RGB23 0x0115 + +/* Gamma matrix */ +#define ZC3XX_R120_GAMMA00 0x0120 +#define ZC3XX_R121_GAMMA01 0x0121 +#define ZC3XX_R122_GAMMA02 0x0122 +#define ZC3XX_R123_GAMMA03 0x0123 +#define ZC3XX_R124_GAMMA04 0x0124 +#define ZC3XX_R125_GAMMA05 0x0125 +#define ZC3XX_R126_GAMMA06 0x0126 +#define ZC3XX_R127_GAMMA07 0x0127 +#define ZC3XX_R128_GAMMA08 0x0128 +#define ZC3XX_R129_GAMMA09 0x0129 +#define ZC3XX_R12A_GAMMA0A 0x012a +#define ZC3XX_R12B_GAMMA0B 0x012b +#define ZC3XX_R12C_GAMMA0C 0x012c +#define ZC3XX_R12D_GAMMA0D 0x012d +#define ZC3XX_R12E_GAMMA0E 0x012e +#define ZC3XX_R12F_GAMMA0F 0x012f +#define ZC3XX_R130_GAMMA10 0x0130 +#define ZC3XX_R131_GAMMA11 0x0131 +#define ZC3XX_R132_GAMMA12 0x0132 +#define ZC3XX_R133_GAMMA13 0x0133 +#define ZC3XX_R134_GAMMA14 0x0134 +#define ZC3XX_R135_GAMMA15 0x0135 +#define ZC3XX_R136_GAMMA16 0x0136 +#define ZC3XX_R137_GAMMA17 0x0137 +#define ZC3XX_R138_GAMMA18 0x0138 +#define ZC3XX_R139_GAMMA19 0x0139 +#define ZC3XX_R13A_GAMMA1A 0x013a +#define ZC3XX_R13B_GAMMA1B 0x013b +#define ZC3XX_R13C_GAMMA1C 0x013c +#define ZC3XX_R13D_GAMMA1D 0x013d +#define ZC3XX_R13E_GAMMA1E 0x013e +#define ZC3XX_R13F_GAMMA1F 0x013f + +/* Luminance gamma */ +#define ZC3XX_R140_YGAMMA00 0x0140 +#define ZC3XX_R141_YGAMMA01 0x0141 +#define ZC3XX_R142_YGAMMA02 0x0142 +#define ZC3XX_R143_YGAMMA03 0x0143 +#define ZC3XX_R144_YGAMMA04 0x0144 +#define ZC3XX_R145_YGAMMA05 0x0145 +#define ZC3XX_R146_YGAMMA06 0x0146 +#define ZC3XX_R147_YGAMMA07 0x0147 +#define ZC3XX_R148_YGAMMA08 0x0148 +#define ZC3XX_R149_YGAMMA09 0x0149 +#define ZC3XX_R14A_YGAMMA0A 0x014a +#define ZC3XX_R14B_YGAMMA0B 0x014b +#define ZC3XX_R14C_YGAMMA0C 0x014c +#define ZC3XX_R14D_YGAMMA0D 0x014d +#define ZC3XX_R14E_YGAMMA0E 0x014e +#define ZC3XX_R14F_YGAMMA0F 0x014f +#define ZC3XX_R150_YGAMMA10 0x0150 +#define ZC3XX_R151_YGAMMA11 0x0151 + +#define ZC3XX_R1C5_SHARPNESSMODE 0x01c5 +#define ZC3XX_R1C6_SHARPNESS00 0x01c6 +#define ZC3XX_R1C7_SHARPNESS01 0x01c7 +#define ZC3XX_R1C8_SHARPNESS02 0x01c8 +#define ZC3XX_R1C9_SHARPNESS03 0x01c9 +#define ZC3XX_R1CA_SHARPNESS04 0x01ca +#define ZC3XX_R1CB_SHARPNESS05 0x01cb + +/* Synchronization */ +#define ZC3XX_R190_SYNC00LOW 0x0190 +#define ZC3XX_R191_SYNC00MID 0x0191 +#define ZC3XX_R192_SYNC00HIGH 0x0192 +#define ZC3XX_R195_SYNC01LOW 0x0195 +#define ZC3XX_R196_SYNC01MID 0x0196 +#define ZC3XX_R197_SYNC01HIGH 0x0197 + +/* Dead pixels */ +#define ZC3XX_R250_DEADPIXELSMODE 0x0250 + +/* EEPROM */ +#define ZC3XX_R300_EEPROMCONFIG 0x0300 +#define ZC3XX_R301_EEPROMACCESS 0x0301 +#define ZC3XX_R302_EEPROMSTATUS 0x0302 diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c new file mode 100644 index 00000000000..b761b11c5c6 --- /dev/null +++ b/drivers/media/video/gspca/zc3xx.c @@ -0,0 +1,7623 @@ +/* + * Z-Star/Vimicro zc301/zc302p/vc30x library + * Copyright (C) 2004 2005 2006 Michel Xhaard + * mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define MODULE_NAME "zc3xx" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 7) +static const char version[] = "2.1.7"; + +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>, " + "Serge A. Suchkov <Serge.A.S@tochka.ru>"); +MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver"); +MODULE_LICENSE("GPL"); + +static int force_sensor = -1; + +#include "jpeg.h" +#include "zc3xx-reg.h" + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + __u8 brightness; + __u8 contrast; + __u8 gamma; + __u8 autogain; + __u8 lightfreq; + __u8 sharpness; + + char qindex; + char sensor; /* Type of image sensor chip */ +/* !! values used in different tables */ +#define SENSOR_CS2102 0 +#define SENSOR_CS2102K 1 +#define SENSOR_GC0305 2 +#define SENSOR_HDCS2020 3 +#define SENSOR_HDCS2020b 4 +#define SENSOR_HV7131B 5 +#define SENSOR_HV7131C 6 +#define SENSOR_ICM105A 7 +#define SENSOR_MC501CB 8 +#define SENSOR_OV7620 9 +/*#define SENSOR_OV7648 9 - same values */ +#define SENSOR_OV7630C 10 +#define SENSOR_PAS106 11 +#define SENSOR_PB0330 12 +#define SENSOR_PO2030 13 +#define SENSOR_TAS5130CK 14 +#define SENSOR_TAS5130CXX 15 +#define SENSOR_TAS5130C_VF0250 16 +#define SENSOR_MAX 17 + unsigned short chip_revision; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 128, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 128, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_GAMMA 2 + { + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 1, + .maximum = 6, + .step = 1, + .default_value = 4, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, +#define SD_AUTOGAIN 3 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define SD_FREQ 4 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, + .default_value = 1, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, +#define SD_SHARPNESS 5 + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 3, + .step = 1, + .default_value = 2, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, +}; + +static struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static struct v4l2_pix_format sif_mode[] = { + {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* usb exchanges */ +struct usb_action { + __u8 req; + __u8 val; + __u16 idx; +}; + +static const struct usb_action cs2102_Initial[] = { + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH}, + {0xa0, 0x21, ZC3XX_R081_HBLANKLOW}, + {0xa0, 0x30, ZC3XX_R083_RGAINADDR}, + {0xa0, 0x31, ZC3XX_R084_GGAINADDR}, + {0xa0, 0x32, ZC3XX_R085_BGAINADDR}, + {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH}, + {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xaa, 0x02, 0x0008}, + {0xaa, 0x03, 0x0000}, + {0xaa, 0x11, 0x0000}, + {0xaa, 0x12, 0x0089}, + {0xaa, 0x13, 0x0000}, + {0xaa, 0x14, 0x00e9}, + {0xaa, 0x20, 0x0000}, + {0xaa, 0x22, 0x0000}, + {0xaa, 0x0b, 0x0004}, + {0xaa, 0x30, 0x0030}, + {0xaa, 0x31, 0x0030}, + {0xaa, 0x32, 0x0030}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x10, 0x01ae}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x68, ZC3XX_R18D_YTARGET}, + {0xa0, 0x00, 0x01ad}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */ + {0xa0, 0x44, ZC3XX_R121_GAMMA01}, + {0xa0, 0x64, ZC3XX_R122_GAMMA02}, + {0xa0, 0x84, ZC3XX_R123_GAMMA03}, + {0xa0, 0x9d, ZC3XX_R124_GAMMA04}, + {0xa0, 0xb2, ZC3XX_R125_GAMMA05}, + {0xa0, 0xc4, ZC3XX_R126_GAMMA06}, + {0xa0, 0xd3, ZC3XX_R127_GAMMA07}, + {0xa0, 0xe0, ZC3XX_R128_GAMMA08}, + {0xa0, 0xeb, ZC3XX_R129_GAMMA09}, + {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x18, ZC3XX_R130_GAMMA10}, + {0xa0, 0x20, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0e, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf4, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf4, ZC3XX_R10D_RGB10}, + {0xa0, 0x58, ZC3XX_R10E_RGB11}, + {0xa0, 0xf4, ZC3XX_R10F_RGB12}, + {0xa0, 0xf4, ZC3XX_R110_RGB20}, + {0xa0, 0xf4, ZC3XX_R111_RGB21}, + {0xa0, 0x58, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x0055}, + {0xaa, 0x25, 0x00cc}, + {0xaa, 0x21, 0x003f}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x39, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x70, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {} +}; + +static const struct usb_action cs2102_InitialScale[] = { + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x20, ZC3XX_R080_HBLANKHIGH}, + {0xa0, 0x21, ZC3XX_R081_HBLANKLOW}, + {0xa0, 0x30, ZC3XX_R083_RGAINADDR}, + {0xa0, 0x31, ZC3XX_R084_GGAINADDR}, + {0xa0, 0x32, ZC3XX_R085_BGAINADDR}, + {0xa0, 0x23, ZC3XX_R086_EXPTIMEHIGH}, + {0xa0, 0x24, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x25, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0xb3, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xaa, 0x02, 0x0008}, + {0xaa, 0x03, 0x0000}, + {0xaa, 0x11, 0x0001}, + {0xaa, 0x12, 0x0087}, + {0xaa, 0x13, 0x0001}, + {0xaa, 0x14, 0x00e7}, + {0xaa, 0x20, 0x0000}, + {0xaa, 0x22, 0x0000}, + {0xaa, 0x0b, 0x0004}, + {0xaa, 0x30, 0x0030}, + {0xaa, 0x31, 0x0030}, + {0xaa, 0x32, 0x0030}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x68, ZC3XX_R18D_YTARGET}, + {0xa0, 0x00, 0x01ad}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */ + {0xa0, 0x44, ZC3XX_R121_GAMMA01}, + {0xa0, 0x64, ZC3XX_R122_GAMMA02}, + {0xa0, 0x84, ZC3XX_R123_GAMMA03}, + {0xa0, 0x9d, ZC3XX_R124_GAMMA04}, + {0xa0, 0xb2, ZC3XX_R125_GAMMA05}, + {0xa0, 0xc4, ZC3XX_R126_GAMMA06}, + {0xa0, 0xd3, ZC3XX_R127_GAMMA07}, + {0xa0, 0xe0, ZC3XX_R128_GAMMA08}, + {0xa0, 0xeb, ZC3XX_R129_GAMMA09}, + {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x18, ZC3XX_R130_GAMMA10}, + {0xa0, 0x20, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0e, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf4, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf4, ZC3XX_R10D_RGB10}, + {0xa0, 0x58, ZC3XX_R10E_RGB11}, + {0xa0, 0xf4, ZC3XX_R10F_RGB12}, + {0xa0, 0xf4, ZC3XX_R110_RGB20}, + {0xa0, 0xf4, ZC3XX_R111_RGB21}, + {0xa0, 0x58, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0000}, + {0xaa, 0x24, 0x00aa}, + {0xaa, 0x25, 0x00e6}, + {0xaa, 0x21, 0x003f}, + {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x55, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xcc, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x18, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x6a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x3f, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action cs2102_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0f, 0x008c}, /* 00,0f,8c,aa */ + {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ + {0xaa, 0x04, 0x00ac}, /* 00,04,ac,aa */ + {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ + {0xaa, 0x11, 0x00ac}, /* 00,11,ac,aa */ + {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ + {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ + {0xaa, 0x1d, 0x00ac}, /* 00,1d,ac,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x42, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,42,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x8c, ZC3XX_R01D_HSYNC_0}, /* 00,1d,8c,cc */ + {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */ + {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */ + {} +}; +static const struct usb_action cs2102_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0f, 0x0093}, /* 00,0f,93,aa */ + {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ + {0xaa, 0x04, 0x00a1}, /* 00,04,a1,aa */ + {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ + {0xaa, 0x11, 0x00a1}, /* 00,11,a1,aa */ + {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ + {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ + {0xaa, 0x1d, 0x00a1}, /* 00,1d,a1,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ + {0xa0, 0xf7, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f7,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x93, ZC3XX_R01D_HSYNC_0}, /* 00,1d,93,cc */ + {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */ + {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */ + {} +}; +static const struct usb_action cs2102_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0f, 0x005d}, /* 00,0f,5d,aa */ + {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ + {0xaa, 0x04, 0x00aa}, /* 00,04,aa,aa */ + {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ + {0xaa, 0x11, 0x00aa}, /* 00,11,aa,aa */ + {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ + {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ + {0xaa, 0x1d, 0x00aa}, /* 00,1d,aa,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ + {0xa0, 0xe4, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e4,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3a,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x5d, ZC3XX_R01D_HSYNC_0}, /* 00,1d,5d,cc */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ + {0xa0, 0xd0, 0x00c8}, /* 00,c8,d0,cc */ + {} +}; +static const struct usb_action cs2102_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0f, 0x00b7}, /* 00,0f,b7,aa */ + {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ + {0xaa, 0x04, 0x00be}, /* 00,04,be,aa */ + {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ + {0xaa, 0x11, 0x00be}, /* 00,11,be,aa */ + {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ + {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ + {0xaa, 0x1d, 0x00be}, /* 00,1d,be,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ + {0xa0, 0xfc, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,fc,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x69, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,69,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0xb7, ZC3XX_R01D_HSYNC_0}, /* 00,1d,b7,cc */ + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ + {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ + {} +}; +static const struct usb_action cs2102_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */ + {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ + {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */ + {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ + {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ + {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ + {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ + {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {} +}; +static const struct usb_action cs2102_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */ + {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ + {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */ + {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ + {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ + {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ + {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ + {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {} +}; + +/* CS2102_KOCOM */ +static const struct usb_action cs2102K_Initial[] = { + {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x01, 0x01b1}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x4c, ZC3XX_R118_BGAIN}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ + {0xa0, 0x38, ZC3XX_R121_GAMMA01}, + {0xa0, 0x59, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x92, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, + {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf4, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf4, ZC3XX_R10D_RGB10}, + {0xa0, 0x58, ZC3XX_R10E_RGB11}, + {0xa0, 0xf4, ZC3XX_R10F_RGB12}, + {0xa0, 0xf4, ZC3XX_R110_RGB20}, + {0xa0, 0xf4, ZC3XX_R111_RGB21}, + {0xa0, 0x58, ZC3XX_R112_RGB22}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, + {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x19, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x1f, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x4c, ZC3XX_R118_BGAIN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {} +}; + +static const struct usb_action cs2102K_InitialScale[] = { + {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0a, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0b, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0c, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7b, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0d, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xa3, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0e, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x01, 0x01b1}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x4c, ZC3XX_R118_BGAIN}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ + {0xa0, 0x38, ZC3XX_R121_GAMMA01}, + {0xa0, 0x59, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x92, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, + {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf4, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf4, ZC3XX_R10D_RGB10}, + {0xa0, 0x58, ZC3XX_R10E_RGB11}, + {0xa0, 0xf4, ZC3XX_R10F_RGB12}, + {0xa0, 0xf4, ZC3XX_R110_RGB20}, + {0xa0, 0xf4, ZC3XX_R111_RGB21}, + {0xa0, 0x58, ZC3XX_R112_RGB22}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, + {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x19, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x1f, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x4c, ZC3XX_R118_BGAIN}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0A, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0B, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0C, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7b, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0D, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xA3, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0E, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0C, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x01, 0x01b1}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x4c, ZC3XX_R118_BGAIN}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ + {0xa0, 0x38, ZC3XX_R121_GAMMA01}, + {0xa0, 0x59, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x92, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, + {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf4, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf4, ZC3XX_R10D_RGB10}, + {0xa0, 0x58, ZC3XX_R10E_RGB11}, + {0xa0, 0xf4, ZC3XX_R10F_RGB12}, + {0xa0, 0xf4, ZC3XX_R110_RGB20}, + {0xa0, 0xf4, ZC3XX_R111_RGB21}, + {0xa0, 0x58, ZC3XX_R112_RGB22}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, + {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x19, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x1f, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x4c, ZC3XX_R118_BGAIN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x5c, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x96, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xd0, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x0a, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x44, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7e, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {} +}; + +static const struct usb_action gc0305_Initial[] = { /* 640x480 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */ + {0xaa, 0x13, 0x0002}, /* 00,13,02,aa */ + {0xaa, 0x15, 0x0003}, /* 00,15,03,aa */ + {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ + {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */ + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ + {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */ + {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ + {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */ + {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */ + {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */ + {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */ + {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */ + {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ + {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */ + {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */ + {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */ + {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */ + {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */ + {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */ + {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */ + {0xaa, 0x17, 0x00e6}, /* 00,17,e6,aa */ + {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */ + {0xaa, 0x19, 0x0086}, /* 00,19,86,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */ + {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ + {0xa0, 0x85, ZC3XX_R18D_YTARGET}, /* 01,8d,85,cc */ + {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */ + {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */ + {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */ + {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ + {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */ + {} +}; +static const struct usb_action gc0305_InitialScale[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc */ + {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */ + {0xaa, 0x15, 0x0001}, /* 00,15,01,aa */ + {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ + {0xaa, 0x02, 0x0000}, /* 00,02,00,aa */ + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ + {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa */ + {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ + {0xaa, 0x1f, 0x0008}, /* 00,1f,08,aa */ + {0xaa, 0x21, 0x0012}, /* 00,21,12,aa */ + {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc */ + {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc */ + {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc */ + {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ + {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa */ + {0xaa, 0x0b, 0x00b0}, /* 00,0b,b0,aa */ + {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa */ + {0xaa, 0x0d, 0x00b0}, /* 00,0d,b0,aa */ + {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa */ + {0xaa, 0x0f, 0x00b0}, /* 00,0f,b0,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x11, 0x00b0}, /* 00,11,b0,aa */ + {0xaa, 0x16, 0x0001}, /* 00,16,01,aa */ + {0xaa, 0x17, 0x00e8}, /* 00,17,e8,aa */ + {0xaa, 0x18, 0x0002}, /* 00,18,02,aa */ + {0xaa, 0x19, 0x0088}, /* 00,19,88,aa */ + {0xaa, 0x20, 0x0000}, /* 00,20,00,aa */ + {0xaa, 0x1b, 0x0020}, /* 00,1b,20,aa */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc */ + {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ + {0xa0, 0x00, 0x011e}, /* 01,1e,00,cc */ + {0xa0, 0x52, ZC3XX_R116_RGAIN}, /* 01,16,52,cc */ + {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* 01,17,40,cc */ + {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ + {0xa0, 0x03, ZC3XX_R113_RGB03}, /* 01,13,03,cc */ + {} +}; +static const struct usb_action gc0305_50HZ[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0002}, /* 00,83,02,aa */ + {0xaa, 0x84, 0x0038}, /* 00,84,38,aa */ /* win: 00,84,ec */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */ + {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ + /* win: 01,92,10 */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc */ + /* win: 01,97,ec */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ +/* {0xa0, 0x85, ZC3XX_R18D_YTARGET}, * 01,8d,85,cc * + * if 640x480 */ + {} +}; +static const struct usb_action gc0305_60HZ[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ + {0xaa, 0x84, 0x00ec}, /* 00,84,ec,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc */ + {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0xec, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,ec,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */ + {} +}; + +static const struct usb_action gc0305_NoFliker[] = { + {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc */ + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ + {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,00,cc */ + {0xa0, 0x48, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,48,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,60,cc */ + {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */ + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* 01,8d,80,cc */ + {} +}; + +/* play poker with registers at your own risk !! */ +static const struct usb_action hdcs2020xx_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, + /* D0 ?? E0 did not start */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, + {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, + {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xaa, 0x02, 0x0002}, + {0xaa, 0x07, 0x0006}, + {0xaa, 0x08, 0x0002}, + {0xaa, 0x09, 0x0006}, + {0xaa, 0x0a, 0x0001}, + {0xaa, 0x0b, 0x0001}, + {0xaa, 0x0c, 0x0008}, + {0xaa, 0x0d, 0x0000}, + {0xaa, 0x10, 0x0000}, + {0xaa, 0x12, 0x0005}, + {0xaa, 0x13, 0x0063}, + {0xaa, 0x15, 0x0070}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4 */ + {0xa0, 0x37, ZC3XX_R121_GAMMA01}, + {0xa0, 0x58, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x91, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa6, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb8, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc7, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd3, ZC3XX_R128_GAMMA08}, + {0xa0, 0xde, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xed, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x23, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + + {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf5, ZC3XX_R10B_RGB01}, + {0xa0, 0xff, ZC3XX_R10C_RGB02}, + {0xa0, 0xf9, ZC3XX_R10D_RGB10}, + {0xa0, 0x51, ZC3XX_R10E_RGB11}, + {0xa0, 0xf5, ZC3XX_R10F_RGB12}, + {0xa0, 0xfb, ZC3XX_R110_RGB20}, + {0xa0, 0xed, ZC3XX_R111_RGB21}, + {0xa0, 0x5f, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, + {0xaa, 0x20, 0x0004}, + {0xaa, 0x21, 0x003d}, + {0xaa, 0x03, 0x0041}, + {0xaa, 0x04, 0x0010}, + {0xaa, 0x05, 0x003d}, + {0xaa, 0x0e, 0x0001}, + {0xaa, 0x0f, 0x0000}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0195}, + {0xa1, 0x01, 0x0196}, + {0xa1, 0x01, 0x0197}, + {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x1d, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x85, ZC3XX_R118_BGAIN}, + {0xa1, 0x01, 0x0116}, + {0xa1, 0x01, 0x0118}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x1d, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x85, ZC3XX_R118_BGAIN}, + {0xa1, 0x01, 0x0116}, + {0xa1, 0x01, 0x0118}, +/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */ + {0xa0, 0x00, 0x0007}, + {} +}; + +static const struct usb_action hdcs2020xx_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, + {0xaa, 0x02, 0x0002}, + {0xaa, 0x07, 0x0006}, + {0xaa, 0x08, 0x0002}, + {0xaa, 0x09, 0x0006}, + {0xaa, 0x0a, 0x0001}, + {0xaa, 0x0b, 0x0001}, + {0xaa, 0x0c, 0x0008}, + {0xaa, 0x0d, 0x0000}, + {0xaa, 0x10, 0x0000}, + {0xaa, 0x12, 0x0005}, + {0xaa, 0x13, 0x0063}, + {0xaa, 0x15, 0x0070}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4*/ + {0xa0, 0x37, ZC3XX_R121_GAMMA01}, + {0xa0, 0x58, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x91, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa6, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb8, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc7, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd3, ZC3XX_R128_GAMMA08}, + {0xa0, 0xde, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xed, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x23, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xff, ZC3XX_R10B_RGB01}, + {0xa0, 0xff, ZC3XX_R10C_RGB02}, + {0xa0, 0xff, ZC3XX_R10D_RGB10}, + {0xa0, 0x60, ZC3XX_R10E_RGB11}, + {0xa0, 0xff, ZC3XX_R10F_RGB12}, + {0xa0, 0xff, ZC3XX_R110_RGB20}, + {0xa0, 0xff, ZC3XX_R111_RGB21}, + {0xa0, 0x60, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, + {0xaa, 0x20, 0x0002}, + {0xaa, 0x21, 0x001b}, + {0xaa, 0x03, 0x0044}, + {0xaa, 0x04, 0x0008}, + {0xaa, 0x05, 0x001b}, + {0xaa, 0x0e, 0x0001}, + {0xaa, 0x0f, 0x0000}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0195}, + {0xa1, 0x01, 0x0196}, + {0xa1, 0x01, 0x0197}, + {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x1d, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x99, ZC3XX_R118_BGAIN}, + {0xa1, 0x01, 0x0116}, + {0xa1, 0x01, 0x0118}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x1d, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x99, ZC3XX_R118_BGAIN}, +/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */ + {0xa0, 0x00, 0x0007}, +/* {0xa0, 0x18, 0x00fe}, */ + {} +}; +static const struct usb_action hdcs2020xb_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* qtable 0x05 */ + {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xaa, 0x1c, 0x0000}, + {0xaa, 0x0a, 0x0001}, + {0xaa, 0x0b, 0x0006}, + {0xaa, 0x0c, 0x007b}, + {0xaa, 0x0d, 0x00a7}, + {0xaa, 0x03, 0x00fb}, + {0xaa, 0x05, 0x0000}, + {0xaa, 0x06, 0x0003}, + {0xaa, 0x09, 0x0008}, + + {0xaa, 0x0f, 0x0018}, /* set sensor gain */ + {0xaa, 0x10, 0x0018}, + {0xaa, 0x11, 0x0018}, + {0xaa, 0x12, 0x0018}, + + {0xaa, 0x15, 0x004e}, + {0xaa, 0x1c, 0x0004}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ + {0xa0, 0x38, ZC3XX_R121_GAMMA01}, + {0xa0, 0x59, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x92, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, + {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + + {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xed, ZC3XX_R10B_RGB01}, + {0xa0, 0xed, ZC3XX_R10C_RGB02}, + {0xa0, 0xed, ZC3XX_R10D_RGB10}, + {0xa0, 0x66, ZC3XX_R10E_RGB11}, + {0xa0, 0xed, ZC3XX_R10F_RGB12}, + {0xa0, 0xed, ZC3XX_R110_RGB20}, + {0xa0, 0xed, ZC3XX_R111_RGB21}, + {0xa0, 0x66, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x13, 0x0031}, + {0xaa, 0x14, 0x0001}, + {0xaa, 0x0e, 0x0004}, + {0xaa, 0x19, 0x00cd}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 0x14 */ + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x41, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action hdcs2020xb_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xaa, 0x1c, 0x0000}, + {0xaa, 0x0a, 0x0001}, + {0xaa, 0x0b, 0x0006}, + {0xaa, 0x0c, 0x007a}, + {0xaa, 0x0d, 0x00a7}, + {0xaa, 0x03, 0x00fb}, + {0xaa, 0x05, 0x0000}, + {0xaa, 0x06, 0x0003}, + {0xaa, 0x09, 0x0008}, + {0xaa, 0x0f, 0x0018}, /* original setting */ + {0xaa, 0x10, 0x0018}, + {0xaa, 0x11, 0x0018}, + {0xaa, 0x12, 0x0018}, + {0xaa, 0x15, 0x004e}, + {0xaa, 0x1c, 0x0004}, + {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ + {0xa0, 0x38, ZC3XX_R121_GAMMA01}, + {0xa0, 0x59, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x92, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, + {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x66, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xed, ZC3XX_R10B_RGB01}, + {0xa0, 0xed, ZC3XX_R10C_RGB02}, + {0xa0, 0xed, ZC3XX_R10D_RGB10}, + {0xa0, 0x66, ZC3XX_R10E_RGB11}, + {0xa0, 0xed, ZC3XX_R10F_RGB12}, + {0xa0, 0xed, ZC3XX_R110_RGB20}, + {0xa0, 0xed, ZC3XX_R111_RGB21}, + {0xa0, 0x66, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + /**** set exposure ***/ + {0xaa, 0x13, 0x0031}, + {0xaa, 0x14, 0x0001}, + {0xaa, 0x0e, 0x0004}, + {0xaa, 0x19, 0x00cd}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x41, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action hdcs2020b_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x13, 0x0018}, /* 00,13,18,aa */ + {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ + {0xaa, 0x0e, 0x0005}, /* 00,0e,05,aa */ + {0xaa, 0x19, 0x001f}, /* 00,19,1f,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ + {0xa0, 0x76, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,76,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */ + {0xa0, 0x05, ZC3XX_R01D_HSYNC_0}, /* 00,1d,05,cc */ + {0xa0, 0x1a, ZC3XX_R01E_HSYNC_1}, /* 00,1e,1a,cc */ + {0xa0, 0x2f, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2f,cc */ + {} +}; +static const struct usb_action hdcs2020b_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x13, 0x0031}, /* 00,13,31,aa */ + {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ + {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */ + {0xaa, 0x19, 0x00cd}, /* 00,19,cd,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ + {0xa0, 0x62, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,62,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,28,cc */ + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */ + {0xa0, 0x18, ZC3XX_R01E_HSYNC_1}, /* 00,1e,18,cc */ + {0xa0, 0x2c, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2c,cc */ + {} +}; +static const struct usb_action hdcs2020b_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x13, 0x0010}, /* 00,13,10,aa */ + {0xaa, 0x14, 0x0001}, /* 00,14,01,aa */ + {0xaa, 0x0e, 0x0004}, /* 00,0e,04,aa */ + {0xaa, 0x19, 0x0000}, /* 00,19,00,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ + {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, /* 00,1d,04,cc */ + {0xa0, 0x17, ZC3XX_R01E_HSYNC_1}, /* 00,1e,17,cc */ + {0xa0, 0x2a, ZC3XX_R01F_HSYNC_2}, /* 00,1f,2a,cc */ + {} +}; + +static const struct usb_action hv7131bxx_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xaa, 0x30, 0x002d}, + {0xaa, 0x01, 0x0005}, + {0xaa, 0x11, 0x0000}, + {0xaa, 0x13, 0x0001}, /* {0xaa, 0x13, 0x0000}, */ + {0xaa, 0x14, 0x0001}, + {0xaa, 0x15, 0x00e8}, + {0xaa, 0x16, 0x0002}, + {0xaa, 0x17, 0x0086}, + {0xaa, 0x31, 0x0038}, + {0xaa, 0x32, 0x0038}, + {0xaa, 0x33, 0x0038}, + {0xaa, 0x5b, 0x0001}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x68, ZC3XX_R18D_YTARGET}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0xc0, 0x019b}, + {0xa0, 0xa0, 0x019c}, + {0xa0, 0x02, ZC3XX_R188_MINGAIN}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xaa, 0x02, 0x0080}, /* {0xaa, 0x02, 0x0090}; */ + {0xa1, 0x01, 0x0002}, + {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, + {0xa1, 0x01, 0x0091}, + {0xa1, 0x01, 0x0095}, + {0xa1, 0x01, 0x0096}, + + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x25, 0x0007}, + {0xaa, 0x26, 0x00a1}, + {0xaa, 0x27, 0x0020}, + {0xaa, 0x20, 0x0000}, + {0xaa, 0x21, 0x00a0}, + {0xaa, 0x22, 0x0016}, + {0xaa, 0x23, 0x0040}, + + {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2F */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 4d */ + {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xa0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x16, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x40, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa1, 0x01, 0x001d}, + {0xa1, 0x01, 0x001e}, + {0xa1, 0x01, 0x001f}, + {0xa1, 0x01, 0x0020}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, +/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */ + {} +}; + +static const struct usb_action hv7131bxx_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xaa, 0x30, 0x002d}, + {0xaa, 0x01, 0x0005}, + {0xaa, 0x11, 0x0001}, + {0xaa, 0x13, 0x0000}, /* {0xaa, 0x13, 0x0001}; */ + {0xaa, 0x14, 0x0001}, + {0xaa, 0x15, 0x00e6}, + {0xaa, 0x16, 0x0002}, + {0xaa, 0x17, 0x0086}, + {0xaa, 0x31, 0x0038}, + {0xaa, 0x32, 0x0038}, + {0xaa, 0x33, 0x0038}, + {0xaa, 0x5b, 0x0001}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x70, ZC3XX_R18D_YTARGET}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0xc0, 0x019b}, + {0xa0, 0xa0, 0x019c}, + {0xa0, 0x02, ZC3XX_R188_MINGAIN}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xaa, 0x02, 0x0090}, /* {0xaa, 0x02, 0x0080}, */ + {0xa1, 0x01, 0x0002}, + {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, + {0xa1, 0x01, 0x0091}, + {0xa1, 0x01, 0x0095}, + {0xa1, 0x01, 0x0096}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x25, 0x0007}, + {0xaa, 0x26, 0x00a1}, + {0xaa, 0x27, 0x0020}, + {0xaa, 0x20, 0x0000}, + {0xaa, 0x21, 0x0040}, + {0xaa, 0x22, 0x0013}, + {0xaa, 0x23, 0x004c}, + {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 4d */ + {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 60 */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x00, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x13, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0x4c, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa1, 0x01, 0x001d}, + {0xa1, 0x01, 0x001e}, + {0xa1, 0x01, 0x001f}, + {0xa1, 0x01, 0x0020}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, +/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */ + {} +}; + +static const struct usb_action hv7131cxx_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x000c}, + {0xaa, 0x11, 0x0000}, + {0xaa, 0x13, 0x0000}, + {0xaa, 0x14, 0x0001}, + {0xaa, 0x15, 0x00e8}, + {0xaa, 0x16, 0x0002}, + {0xaa, 0x17, 0x0088}, + + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x89, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0xc0, 0x019b}, + {0xa0, 0xa0, 0x019c}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa1, 0x01, 0x0002}, + {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, + {0xa1, 0x01, 0x0091}, + {0xa1, 0x01, 0x0095}, + {0xa1, 0x01, 0x0096}, + + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf0, ZC3XX_R10B_RGB01}, + {0xa0, 0xf0, ZC3XX_R10C_RGB02}, + {0xa0, 0xf0, ZC3XX_R10D_RGB10}, + {0xa0, 0x60, ZC3XX_R10E_RGB11}, + {0xa0, 0xf0, ZC3XX_R10F_RGB12}, + {0xa0, 0xf0, ZC3XX_R110_RGB20}, + {0xa0, 0xf0, ZC3XX_R111_RGB21}, + {0xa0, 0x60, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x25, 0x0007}, + {0xaa, 0x26, 0x0053}, + {0xaa, 0x27, 0x0000}, + + {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 9b */ + {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 80 */ + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa1, 0x01, 0x001d}, + {0xa1, 0x01, 0x001e}, + {0xa1, 0x01, 0x001f}, + {0xa1, 0x01, 0x0020}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action hv7131cxx_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* diff */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 1e0 */ + + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x000c}, + {0xaa, 0x11, 0x0000}, + {0xaa, 0x13, 0x0000}, + {0xaa, 0x14, 0x0001}, + {0xaa, 0x15, 0x00e8}, + {0xaa, 0x16, 0x0002}, + {0xaa, 0x17, 0x0088}, + + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00 */ + + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x89, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0xc0, 0x019b}, + {0xa0, 0xa0, 0x019c}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa1, 0x01, 0x0002}, + {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, + /* read the i2c chips ident */ + {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, + {0xa1, 0x01, 0x0091}, + {0xa1, 0x01, 0x0095}, + {0xa1, 0x01, 0x0096}, + + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x60, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf0, ZC3XX_R10B_RGB01}, + {0xa0, 0xf0, ZC3XX_R10C_RGB02}, + {0xa0, 0xf0, ZC3XX_R10D_RGB10}, + {0xa0, 0x60, ZC3XX_R10E_RGB11}, + {0xa0, 0xf0, ZC3XX_R10F_RGB12}, + {0xa0, 0xf0, ZC3XX_R110_RGB20}, + {0xa0, 0xf0, ZC3XX_R111_RGB21}, + {0xa0, 0x60, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x25, 0x0007}, + {0xaa, 0x26, 0x0053}, + {0xaa, 0x27, 0x0000}, + + {0xa0, 0x10, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 2f */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 9b */ + {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 80 */ + + {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, + + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x13, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa1, 0x01, 0x001d}, + {0xa1, 0x01, 0x001e}, + {0xa1, 0x01, 0x001f}, + {0xa1, 0x01, 0x0020}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action icm105axx_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH}, + {0xa0, 0x01, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH}, + {0xa0, 0x01, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x01, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x01, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xaa, 0x01, 0x0010}, + {0xaa, 0x03, 0x0000}, + {0xaa, 0x04, 0x0001}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0001}, + {0xaa, 0x04, 0x0011}, + {0xaa, 0x05, 0x00a0}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0002}, + {0xaa, 0x04, 0x0013}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0003}, + {0xaa, 0x04, 0x0015}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0004}, + {0xaa, 0x04, 0x0017}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x000d}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0005}, + {0xaa, 0x04, 0x0019}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0006}, + {0xaa, 0x04, 0x0017}, + {0xaa, 0x05, 0x0026}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0007}, + {0xaa, 0x04, 0x0019}, + {0xaa, 0x05, 0x0022}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0008}, + {0xaa, 0x04, 0x0021}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0009}, + {0xaa, 0x04, 0x0023}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x000d}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000a}, + {0xaa, 0x04, 0x0025}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000b}, + {0xaa, 0x04, 0x00ec}, + {0xaa, 0x05, 0x002e}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000c}, + {0xaa, 0x04, 0x00fa}, + {0xaa, 0x05, 0x002a}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x07, 0x000d}, + {0xaa, 0x01, 0x0005}, + {0xaa, 0x94, 0x0002}, + {0xaa, 0x90, 0x0000}, + {0xaa, 0x91, 0x001f}, + {0xaa, 0x10, 0x0064}, + {0xaa, 0x9b, 0x00f0}, + {0xaa, 0x9c, 0x0002}, + {0xaa, 0x14, 0x001a}, + {0xaa, 0x20, 0x0080}, + {0xaa, 0x22, 0x0080}, + {0xaa, 0x24, 0x0080}, + {0xaa, 0x26, 0x0080}, + {0xaa, 0x00, 0x0084}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xaa, 0xa8, 0x00c0}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {0xa1, 0x01, 0x0008}, + + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf7, ZC3XX_R10B_RGB01}, + {0xa0, 0xf7, ZC3XX_R10C_RGB02}, + {0xa0, 0xf7, ZC3XX_R10D_RGB10}, + {0xa0, 0x52, ZC3XX_R10E_RGB11}, + {0xa0, 0xf7, ZC3XX_R10F_RGB12}, + {0xa0, 0xf7, ZC3XX_R110_RGB20}, + {0xa0, 0xf7, ZC3XX_R111_RGB21}, + {0xa0, 0x52, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x0d, 0x0003}, + {0xaa, 0x0c, 0x008c}, + {0xaa, 0x0e, 0x0095}, + {0xaa, 0x0f, 0x0002}, + {0xaa, 0x1c, 0x0094}, + {0xaa, 0x1d, 0x0002}, + {0xaa, 0x20, 0x0080}, + {0xaa, 0x22, 0x0080}, + {0xaa, 0x24, 0x0080}, + {0xaa, 0x26, 0x0080}, + {0xaa, 0x00, 0x0084}, + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, + {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xec, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0xc0, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {} +}; + +static const struct usb_action icm105axx_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0c, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x00, ZC3XX_R097_WINYSTARTHIGH}, + {0xa0, 0x02, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R099_WINXSTARTHIGH}, + {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x02, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xaa, 0x01, 0x0010}, + {0xaa, 0x03, 0x0000}, + {0xaa, 0x04, 0x0001}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0001}, + {0xaa, 0x04, 0x0011}, + {0xaa, 0x05, 0x00a0}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0002}, + {0xaa, 0x04, 0x0013}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0001}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0003}, + {0xaa, 0x04, 0x0015}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0004}, + {0xaa, 0x04, 0x0017}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x000d}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0005}, + {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x19, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa1, 0x01, 0x0091}, + {0xaa, 0x05, 0x0020}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0006}, + {0xaa, 0x04, 0x0017}, + {0xaa, 0x05, 0x0026}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0007}, + {0xaa, 0x04, 0x0019}, + {0xaa, 0x05, 0x0022}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0008}, + {0xaa, 0x04, 0x0021}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x0009}, + {0xaa, 0x04, 0x0023}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x000d}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000a}, + {0xaa, 0x04, 0x0025}, + {0xaa, 0x05, 0x00aa}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000b}, + {0xaa, 0x04, 0x00ec}, + {0xaa, 0x05, 0x002e}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x03, 0x000c}, + {0xaa, 0x04, 0x00fa}, + {0xaa, 0x05, 0x002a}, + {0xaa, 0x06, 0x0005}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x07, 0x000d}, + {0xaa, 0x01, 0x0005}, + {0xaa, 0x94, 0x0002}, + {0xaa, 0x90, 0x0000}, + {0xaa, 0x91, 0x0010}, + {0xaa, 0x10, 0x0064}, + {0xaa, 0x9b, 0x00f0}, + {0xaa, 0x9c, 0x0002}, + {0xaa, 0x14, 0x001a}, + {0xaa, 0x20, 0x0080}, + {0xaa, 0x22, 0x0080}, + {0xaa, 0x24, 0x0080}, + {0xaa, 0x26, 0x0080}, + {0xaa, 0x00, 0x0084}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xaa, 0xa8, 0x0080}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {0xa1, 0x01, 0x0008}, + + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x52, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf7, ZC3XX_R10B_RGB01}, + {0xa0, 0xf7, ZC3XX_R10C_RGB02}, + {0xa0, 0xf7, ZC3XX_R10D_RGB10}, + {0xa0, 0x52, ZC3XX_R10E_RGB11}, + {0xa0, 0xf7, ZC3XX_R10F_RGB12}, + {0xa0, 0xf7, ZC3XX_R110_RGB20}, + {0xa0, 0xf7, ZC3XX_R111_RGB21}, + {0xa0, 0x52, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x0d, 0x0003}, + {0xaa, 0x0c, 0x0020}, + {0xaa, 0x0e, 0x000e}, + {0xaa, 0x0f, 0x0002}, + {0xaa, 0x1c, 0x000d}, + {0xaa, 0x1d, 0x0002}, + {0xaa, 0x20, 0x0080}, + {0xaa, 0x22, 0x0080}, + {0xaa, 0x24, 0x0080}, + {0xaa, 0x26, 0x0080}, + {0xaa, 0x00, 0x0084}, + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, + {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R116_RGAIN}, + {0xa0, 0x40, ZC3XX_R117_GGAIN}, + {0xa0, 0x40, ZC3XX_R118_BGAIN}, + {} +}; +static const struct usb_action icm105a_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x0020}, /* 00,0c,20,aa */ + {0xaa, 0x0e, 0x000e}, /* 00,0e,0e,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x000d}, /* 00,1c,0d,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x0d, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,0d,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x1a, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,1a,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x4b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4b,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ + {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */ + {0xa0, 0xd8, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d8,cc */ + {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {} +}; +static const struct usb_action icm105a_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x008c}, /* 00,0c,8c,aa */ + {0xaa, 0x0e, 0x0095}, /* 00,0e,95,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x0094}, /* 00,1c,94,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x94, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,94,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x84, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,84,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ + {0xa0, 0xe3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e3,cc */ + {0xa0, 0xec, ZC3XX_R01E_HSYNC_1}, /* 00,1e,ec,cc */ + {0xa0, 0xf5, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f5,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */ + {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ + {} +}; +static const struct usb_action icm105a_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ + {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x0008}, /* 00,1c,08,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x08, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,08,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x41, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,41,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ + {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */ + {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */ + {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {} +}; +static const struct usb_action icm105a_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */ + {0xaa, 0x0e, 0x0086}, /* 00,0e,86,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x0085}, /* 00,1c,85,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x85, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,85,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,08,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x12, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,12,cc */ + {0xa0, 0xc2, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c2,cc */ + {0xa0, 0xd6, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d6,cc */ + {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */ + {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ + {} +}; +static const struct usb_action icm105a_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ + {0xaa, 0x0e, 0x000d}, /* 00,0e,0d,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x0000}, /* 00,1c,00,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x00, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,00,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */ + {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */ + {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {} +}; +static const struct usb_action icm105a_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ + {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ + {0xaa, 0x0e, 0x0081}, /* 00,0e,81,aa */ + {0xaa, 0x0f, 0x0002}, /* 00,0f,02,aa */ + {0xaa, 0x1c, 0x0080}, /* 00,1c,80,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x20, 0x0080}, /* 00,20,80,aa */ + {0xaa, 0x22, 0x0080}, /* 00,22,80,aa */ + {0xaa, 0x24, 0x0080}, /* 00,24,80,aa */ + {0xaa, 0x26, 0x0080}, /* 00,26,80,aa */ + {0xaa, 0x00, 0x0084}, /* 00,00,84,aa */ + {0xa0, 0x02, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,02,cc */ + {0xa0, 0x80, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,80,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,20,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0xc1, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c1,cc */ + {0xa0, 0xd4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d4,cc */ + {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, /* 01,a7,00,cc */ + {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ + {} +}; + +static const struct usb_action MC501CB_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ + {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */ + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */ + {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */ + {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */ + {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ + {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */ + {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ + {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ + {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */ + {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */ + {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ + {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */ + {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */ + {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */ + {0xaa, 0x18, 0x00de}, /* 00,18,de,aa */ + {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ + {0xaa, 0x1a, 0x0086}, /* 00,1a,86,aa */ + {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */ + {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */ + {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */ + {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */ + {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */ + {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */ + {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */ + {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */ + {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */ + {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */ + {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */ + {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */ + {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */ + {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */ + {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */ + {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */ + {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */ + {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */ + {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */ + {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */ + {0xaa, 0xa0, ZC3XX_R01A_LASTFRAMESTATE}, /* 00,a0,1a,aa */ + {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */ + {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */ + {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */ + {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */ + {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */ + {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */ + {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */ + {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */ + {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */ + {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */ + {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */ + {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */ + {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */ + {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */ + {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */ + {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */ + {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */ + {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */ + {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */ + {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x1c, 0x0050}, /* 00,1C,50,aa */ + {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */ + {0xaa, 0x3b, 0x001d}, /* 00,3b,1D,aa */ + {0xaa, 0x3c, 0x004c}, /* 00,3c,4C,aa */ + {0xaa, 0x3d, 0x0018}, /* 00,3d,18,aa */ + {0xaa, 0x3e, 0x006a}, /* 00,3e,6A,aa */ + {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ + {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */ + {0xaa, 0x51, 0x0027}, /* 00,51,27,aa */ + {0xaa, 0x52, 0x0020}, /* 00,52,20,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */ + {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */ + {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */ + {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */ + {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */ + {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */ + + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ + {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ + {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ + {} +}; + +static const struct usb_action MC501CB_Initial[] = { /* 320x240 */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ + {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */ + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xa0, 0x33, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,33,cc */ + {0xa0, 0x34, ZC3XX_R087_EXPTIMEMID}, /* 00,87,34,cc */ + {0xa0, 0x35, ZC3XX_R088_EXPTIMELOW}, /* 00,88,35,cc */ + {0xa0, 0xb0, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,b0,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ + {0xaa, 0x01, 0x0003}, /* 00,01,03,aa */ + {0xaa, 0x01, 0x0001}, /* 00,01,01,aa */ + {0xaa, 0x03, 0x0000}, /* 00,03,00,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ + {0xaa, 0x12, 0x0000}, /* 00,12,00,aa */ + {0xaa, 0x13, 0x0000}, /* 00,13,00,aa */ + {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ + {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */ + {0xaa, 0x16, 0x0000}, /* 00,16,00,aa */ + {0xaa, 0x17, 0x0001}, /* 00,17,01,aa */ + {0xaa, 0x18, 0x00d8}, /* 00,18,d8,aa */ + {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ + {0xaa, 0x1a, 0x0088}, /* 00,1a,88,aa */ + {0xaa, 0x20, 0x00a8}, /* 00,20,a8,aa */ + {0xaa, 0x22, 0x0000}, /* 00,22,00,aa */ + {0xaa, 0x23, 0x0000}, /* 00,23,00,aa */ + {0xaa, 0x24, 0x0000}, /* 00,24,00,aa */ + {0xaa, 0x40, 0x0033}, /* 00,40,33,aa */ + {0xaa, 0x41, 0x0077}, /* 00,41,77,aa */ + {0xaa, 0x42, 0x0053}, /* 00,42,53,aa */ + {0xaa, 0x43, 0x00b0}, /* 00,43,b0,aa */ + {0xaa, 0x4b, 0x0001}, /* 00,4b,01,aa */ + {0xaa, 0x72, 0x0020}, /* 00,72,20,aa */ + {0xaa, 0x73, 0x0000}, /* 00,73,00,aa */ + {0xaa, 0x80, 0x0000}, /* 00,80,00,aa */ + {0xaa, 0x85, 0x0050}, /* 00,85,50,aa */ + {0xaa, 0x91, 0x0070}, /* 00,91,70,aa */ + {0xaa, 0x92, 0x0072}, /* 00,92,72,aa */ + {0xaa, 0x03, 0x0001}, /* 00,03,01,aa */ + {0xaa, 0x10, 0x00a0}, /* 00,10,a0,aa */ + {0xaa, 0x11, 0x0001}, /* 00,11,01,aa */ + {0xaa, 0x30, 0x0000}, /* 00,30,00,aa */ + {0xaa, 0x60, 0x0000}, /* 00,60,00,aa */ + {0xaa, 0xa0, ZC3XX_R01A_LASTFRAMESTATE}, /* 00,a0,1a,aa */ + {0xaa, 0xa1, 0x0000}, /* 00,a1,00,aa */ + {0xaa, 0xa2, 0x003f}, /* 00,a2,3f,aa */ + {0xaa, 0xa3, 0x0028}, /* 00,a3,28,aa */ + {0xaa, 0xa4, 0x0010}, /* 00,a4,10,aa */ + {0xaa, 0xa5, 0x0020}, /* 00,a5,20,aa */ + {0xaa, 0xb1, 0x0044}, /* 00,b1,44,aa */ + {0xaa, 0xd0, 0x0001}, /* 00,d0,01,aa */ + {0xaa, 0xd1, 0x0085}, /* 00,d1,85,aa */ + {0xaa, 0xd2, 0x0080}, /* 00,d2,80,aa */ + {0xaa, 0xd3, 0x0080}, /* 00,d3,80,aa */ + {0xaa, 0xd4, 0x0080}, /* 00,d4,80,aa */ + {0xaa, 0xd5, 0x0080}, /* 00,d5,80,aa */ + {0xaa, 0xc0, 0x00c3}, /* 00,c0,c3,aa */ + {0xaa, 0xc2, 0x0044}, /* 00,c2,44,aa */ + {0xaa, 0xc4, 0x0040}, /* 00,c4,40,aa */ + {0xaa, 0xc5, 0x0020}, /* 00,c5,20,aa */ + {0xaa, 0xc6, 0x0008}, /* 00,c6,08,aa */ + {0xaa, 0x03, 0x0004}, /* 00,03,04,aa */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ + {0xaa, 0x40, 0x0030}, /* 00,40,30,aa */ + {0xaa, 0x41, 0x0020}, /* 00,41,20,aa */ + {0xaa, 0x42, 0x002d}, /* 00,42,2d,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x1c, 0x0050}, /* 00,1c,50,aa */ + {0xaa, 0x11, 0x0081}, /* 00,11,81,aa */ + {0xaa, 0x3b, 0x003a}, /* 00,3b,3A,aa */ + {0xaa, 0x3c, 0x0098}, /* 00,3c,98,aa */ + {0xaa, 0x3d, 0x0030}, /* 00,3d,30,aa */ + {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ + {0xaa, 0x01, 0x0000}, /* 00,01,00,aa */ + {0xaa, 0x52, 0x00ff}, /* 00,52,FF,aa */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xaa, 0x03, 0x0002}, /* 00,03,02,aa */ + {0xaa, 0x51, 0x004e}, /* 00,51,4E,aa */ + {0xaa, 0x52, 0x0041}, /* 00,52,41,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x50, 0x0010}, /* 00,50,10,aa */ + {0xaa, 0x51, 0x0010}, /* 00,51,10,aa */ + {0xaa, 0x54, 0x0010}, /* 00,54,10,aa */ + {0xaa, 0x55, 0x0010}, /* 00,55,10,aa */ + {0xa0, 0xf0, 0x0199}, /* 01,99,F0,cc */ + {0xa0, 0x80, 0x019a}, /* 01,9A,80,cc */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ + {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ + {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ + {} +}; + +static const struct usb_action MC501CB_50HZ[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ + {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ + {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ + {0xaa, 0x3c, 0x004c}, /* 00,3C,4C,aa */ + {0xaa, 0x3d, 0x001d}, /* 00,3D,1D,aa */ + {0xaa, 0x3e, 0x004c}, /* 00,3E,4C,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */ + {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */ + {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */ + {} +}; + +static const struct usb_action MC501CB_50HZScale[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */ + {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */ + {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */ + {0xaa, 0x3c, 0x0098}, /* 00,3C,98,aa */ + {0xaa, 0x3d, 0x003a}, /* 00,3D,3A,aa */ + {0xaa, 0x3e, 0x0098}, /* 00,3E,98,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ + {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ + {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ + {} +}; + +static const struct usb_action MC501CB_60HZ[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ + {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ + {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ + {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */ + {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */ + {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ + {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ + {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ + {} +}; + +static const struct usb_action MC501CB_60HZScale[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ + {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ + {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ + {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ + {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */ + {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ + {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ + {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ + {} +}; + +static const struct usb_action MC501CB_NoFliker[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ + {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ + {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ + {0xaa, 0x3e, 0x006a}, /* 00,3E,6A,aa */ + {0xaa, 0x3b, 0x0018}, /* 00,3B,18,aa */ + {0xaa, 0x3c, 0x006a}, /* 00,3C,6A,aa */ + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ + {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ + {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ + {} +}; + +static const struct usb_action MC501CB_NoFlikerScale[] = { + {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ + {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ + {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ + {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ + {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ + {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ + {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */ + {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */ + {} +}; + +/* from zs211.inf - HKR,%OV7620%,Initial - 640x480 */ +static const struct usb_action OV7620_mode0[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, /* 00,02,40,cc */ + {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */ + {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */ + {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */ + {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */ + {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */ + {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */ + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */ + {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */ + {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ + {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ + {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */ + {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ + {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ + {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */ + {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */ + {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */ + {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ + {0xaa, 0x1a, 0x00f1}, /* 00,1a,f1,aa */ + {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */ + {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */ + {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */ + {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */ + {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */ + {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ + {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */ + {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ + {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ + {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */ + {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */ + {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */ + {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */ + {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */ + {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */ + {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */ + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */ + {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ + {0xa0, 0x40, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,40,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */ + {} +}; + +/* from zs211.inf - HKR,%OV7620%,InitialScale - 320x240 */ +static const struct usb_action OV7620_mode1[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, /* 00,02,50,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ + /* mx change? */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */ + {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */ + {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, /* 00,85,01,cc */ + {0xa0, 0x80, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,80,cc */ + {0xa0, 0x81, ZC3XX_R087_EXPTIMEMID}, /* 00,87,81,cc */ + {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, /* 00,88,10,cc */ + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,a1,cc */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xd6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d6,cc */ + /* OV7648 00,9c,d8,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xaa, 0x12, 0x0088}, /* 00,12,88,aa */ + {0xaa, 0x12, 0x0048}, /* 00,12,48,aa */ + {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ + {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ + {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */ + {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ + {0xaa, 0x14, 0x0000}, /* 00,14,00,aa */ + {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */ + {0xaa, 0x24, 0x0088}, /* 00,24,88,aa */ + {0xaa, 0x25, 0x0078}, /* 00,25,78,aa */ + {0xaa, 0x17, 0x0018}, /* 00,17,18,aa */ + {0xaa, 0x18, 0x00ba}, /* 00,18,ba,aa */ + {0xaa, 0x19, 0x0002}, /* 00,19,02,aa */ + {0xaa, 0x1a, 0x00f2}, /* 00,1a,f2,aa */ + {0xaa, 0x20, 0x0040}, /* 00,20,40,aa */ + {0xaa, 0x27, 0x00f6}, /* 00,27,f6,aa */ + {0xaa, 0x28, 0x00a0}, /* 00,28,a0,aa */ + {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ + {0xaa, 0x2a, 0x0083}, /* 00,2a,83,aa */ + {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ + {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ + {0xaa, 0x74, 0x0020}, /* 00,74,20,aa */ + {0xaa, 0x61, 0x0068}, /* 00,61,68,aa */ + {0xaa, 0x64, 0x0088}, /* 00,64,88,aa */ + {0xaa, 0x00, 0x0000}, /* 00,00,00,aa */ + {0xaa, 0x06, 0x0080}, /* 00,06,80,aa */ + {0xaa, 0x01, 0x0090}, /* 00,01,90,aa */ + {0xaa, 0x02, 0x0030}, /* 00,02,30,aa */ + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,77,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x68, ZC3XX_R116_RGAIN}, /* 01,16,68,cc */ + {0xa0, 0x52, ZC3XX_R118_BGAIN}, /* 01,18,52,cc */ + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,50,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */ + {} +}; + +/* from zs211.inf - HKR,%OV7620%\AE,50HZ */ +static const struct usb_action OV7620_50HZ[] = { + {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ + {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ + {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ + {0xaa, 0x10, 0x0082}, /* 00,10,82,aa */ + {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */ +/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc + if mode0 (640x480) */ + {} +}; + +/* from zs211.inf - HKR,%OV7620%\AE,60HZ */ +static const struct usb_action OV7620_60HZ[] = { + {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ + /* (bug in zs211.inf) */ + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */ + {0xaa, 0x75, 0x008a}, /* 00,75,8a,aa */ + {0xaa, 0x2d, 0x0005}, /* 00,2d,05,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ + {0xaa, 0x10, 0x0020}, /* 00,10,20,aa */ + {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */ +/* {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, * 00,02,40,cc + * if mode0 (640x480) */ +/* ?? in gspca v1, it was + {0xa0, 0x00, 0x0039}, * 00,00,00,dd * + {0xa1, 0x01, 0x0037}, */ + {} +}; + +/* from zs211.inf - HKR,%OV7620%\AE,NoFliker */ +static const struct usb_action OV7620_NoFliker[] = { + {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ + /* (bug in zs211.inf) */ + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xaa, 0x2b, 0x0000}, /* 00,2b,00,aa */ + {0xaa, 0x75, 0x008e}, /* 00,75,8e,aa */ + {0xaa, 0x2d, 0x0001}, /* 00,2d,01,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,04,cc */ + {0xa0, 0x18, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,18,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,01,cc */ +/* {0xa0, 0x44, ZC3XX_R002_CLOCKSELECT}, * 00,02,44,cc + - if mode1 (320x240) */ +/* ?? was + {0xa0, 0x00, 0x0039}, * 00,00,00,dd * + {0xa1, 0x01, 0x0037}, */ + {} +}; + +static const struct usb_action ov7630c_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x12, 0x0080}, + {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, + {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, + {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH}, + {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xaa, 0x12, 0x0069}, + {0xaa, 0x04, 0x0020}, + {0xaa, 0x06, 0x0050}, + {0xaa, 0x13, 0x0083}, + {0xaa, 0x14, 0x0000}, + {0xaa, 0x15, 0x0024}, + {0xaa, 0x17, 0x0018}, + {0xaa, 0x18, 0x00ba}, + {0xaa, 0x19, 0x0002}, + {0xaa, 0x1a, 0x00f6}, + {0xaa, 0x1b, 0x0002}, + {0xaa, 0x20, 0x00c2}, + {0xaa, 0x24, 0x0060}, + {0xaa, 0x25, 0x0040}, + {0xaa, 0x26, 0x0030}, + {0xaa, 0x27, 0x00ea}, + {0xaa, 0x28, 0x00a0}, + {0xaa, 0x21, 0x0000}, + {0xaa, 0x2a, 0x0081}, + {0xaa, 0x2b, 0x0096}, + {0xaa, 0x2d, 0x0094}, + {0xaa, 0x2f, 0x003d}, + {0xaa, 0x30, 0x0024}, + {0xaa, 0x60, 0x0000}, + {0xaa, 0x61, 0x0040}, + {0xaa, 0x68, 0x007c}, + {0xaa, 0x6f, 0x0015}, + {0xaa, 0x75, 0x0088}, + {0xaa, 0x77, 0x00b5}, + {0xaa, 0x01, 0x0060}, + {0xaa, 0x02, 0x0060}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x46, ZC3XX_R118_BGAIN}, + {0xa0, 0x04, ZC3XX_R113_RGB03}, +/* 0x10, */ + {0xa1, 0x01, 0x0002}, + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, +/* 0x03, */ + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x01, ZC3XX_R120_GAMMA00}, /* gamma 2 ?*/ + {0xa0, 0x0c, ZC3XX_R121_GAMMA01}, + {0xa0, 0x1f, ZC3XX_R122_GAMMA02}, + {0xa0, 0x3a, ZC3XX_R123_GAMMA03}, + {0xa0, 0x53, ZC3XX_R124_GAMMA04}, + {0xa0, 0x6d, ZC3XX_R125_GAMMA05}, + {0xa0, 0x85, ZC3XX_R126_GAMMA06}, + {0xa0, 0x9c, ZC3XX_R127_GAMMA07}, + {0xa0, 0xb0, ZC3XX_R128_GAMMA08}, + {0xa0, 0xc2, ZC3XX_R129_GAMMA09}, + {0xa0, 0xd1, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xde, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xe9, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf2, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xf9, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x05, ZC3XX_R130_GAMMA10}, + {0xa0, 0x0f, ZC3XX_R131_GAMMA11}, + {0xa0, 0x16, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1a, ZC3XX_R133_GAMMA13}, + {0xa0, 0x19, ZC3XX_R134_GAMMA14}, + {0xa0, 0x19, ZC3XX_R135_GAMMA15}, + {0xa0, 0x17, ZC3XX_R136_GAMMA16}, + {0xa0, 0x15, ZC3XX_R137_GAMMA17}, + {0xa0, 0x12, ZC3XX_R138_GAMMA18}, + {0xa0, 0x10, ZC3XX_R139_GAMMA19}, + {0xa0, 0x0e, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x0b, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x09, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x08, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x06, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x03, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xaa, 0x10, 0x001b}, + {0xaa, 0x76, 0x0002}, + {0xaa, 0x2a, 0x0081}, + {0xaa, 0x2b, 0x0000}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xb8, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x37, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xaa, 0x13, 0x0083}, /* 40 */ + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action ov7630c_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0xa1, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + + {0xaa, 0x12, 0x0080}, + {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, + {0xa0, 0x01, ZC3XX_R085_BGAINADDR}, + {0xa0, 0x90, ZC3XX_R086_EXPTIMEHIGH}, + {0xa0, 0x91, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x10, ZC3XX_R088_EXPTIMELOW}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, + {0xaa, 0x12, 0x0069}, /* i2c */ + {0xaa, 0x04, 0x0020}, + {0xaa, 0x06, 0x0050}, + {0xaa, 0x13, 0x00c3}, + {0xaa, 0x14, 0x0000}, + {0xaa, 0x15, 0x0024}, + {0xaa, 0x19, 0x0003}, + {0xaa, 0x1a, 0x00f6}, + {0xaa, 0x1b, 0x0002}, + {0xaa, 0x20, 0x00c2}, + {0xaa, 0x24, 0x0060}, + {0xaa, 0x25, 0x0040}, + {0xaa, 0x26, 0x0030}, + {0xaa, 0x27, 0x00ea}, + {0xaa, 0x28, 0x00a0}, + {0xaa, 0x21, 0x0000}, + {0xaa, 0x2a, 0x0081}, + {0xaa, 0x2b, 0x0096}, + {0xaa, 0x2d, 0x0084}, + {0xaa, 0x2f, 0x003d}, + {0xaa, 0x30, 0x0024}, + {0xaa, 0x60, 0x0000}, + {0xaa, 0x61, 0x0040}, + {0xaa, 0x68, 0x007c}, + {0xaa, 0x6f, 0x0015}, + {0xaa, 0x75, 0x0088}, + {0xaa, 0x77, 0x00b5}, + {0xaa, 0x01, 0x0060}, + {0xaa, 0x02, 0x0060}, + {0xaa, 0x17, 0x0018}, + {0xaa, 0x18, 0x00ba}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x77, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R116_RGAIN}, + {0xa0, 0x46, ZC3XX_R118_BGAIN}, + {0xa0, 0x04, ZC3XX_R113_RGB03}, + + {0xa1, 0x01, 0x0002}, + {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xfe, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf7, ZC3XX_R10D_RGB10}, + {0xa0, 0x4d, ZC3XX_R10E_RGB11}, + {0xa0, 0xfc, ZC3XX_R10F_RGB12}, + {0xa0, 0x00, ZC3XX_R110_RGB20}, + {0xa0, 0xf6, ZC3XX_R111_RGB21}, + {0xa0, 0x4a, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x16, ZC3XX_R120_GAMMA00}, /* gamma ~4 */ + {0xa0, 0x3a, ZC3XX_R121_GAMMA01}, + {0xa0, 0x5b, ZC3XX_R122_GAMMA02}, + {0xa0, 0x7c, ZC3XX_R123_GAMMA03}, + {0xa0, 0x94, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa9, ZC3XX_R125_GAMMA05}, + {0xa0, 0xbb, ZC3XX_R126_GAMMA06}, + {0xa0, 0xca, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd7, ZC3XX_R128_GAMMA08}, + {0xa0, 0xe1, ZC3XX_R129_GAMMA09}, + {0xa0, 0xea, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xf1, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf7, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xfc, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x20, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x4e, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xfe, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf7, ZC3XX_R10D_RGB10}, + {0xa0, 0x4d, ZC3XX_R10E_RGB11}, + {0xa0, 0xfc, ZC3XX_R10F_RGB12}, + {0xa0, 0x00, ZC3XX_R110_RGB20}, + {0xa0, 0xf6, ZC3XX_R111_RGB21}, + {0xa0, 0x4a, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xaa, 0x10, 0x000d}, + {0xaa, 0x76, 0x0002}, + {0xaa, 0x2a, 0x0081}, + {0xaa, 0x2b, 0x0000}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x00, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd8, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x1b, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xaa, 0x13, 0x00c3}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action pas106b_Initial_com[] = { +/* Sream and Sensor specific */ + {0xa1, 0x01, 0x0010}, /* CMOSSensorSelect */ +/* System */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */ + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* SystemControl */ +/* Picture size */ + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* ClockSelect */ + {0xa0, 0x03, 0x003a}, + {0xa0, 0x0c, 0x003b}, + {0xa0, 0x04, 0x0038}, + {} +}; + +static const struct usb_action pas106b_Initial[] = { /* 176x144 */ +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ +/* Sream and Sensor specific */ + {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT}, /* CMOSSensorSelect */ +/* Picture size */ + {0xa0, 0x00, ZC3XX_R003_FRAMEWIDTHHIGH}, /* FrameWidthHigh 00 */ + {0xa0, 0xb0, ZC3XX_R004_FRAMEWIDTHLOW}, /* FrameWidthLow B0 */ + {0xa0, 0x00, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* FrameHeightHigh 00 */ + {0xa0, 0x90, ZC3XX_R006_FRAMEHEIGHTLOW}, /* FrameHightLow 90 */ +/* System */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* SystemOperating */ +/* Sream and Sensor specific */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */ +/* Sensor Interface */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* Compatibily Mode */ +/* Window inside sensor array */ + {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* WinXStartLow */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* FirstYLow */ + {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* FirstxLow */ + {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW}, /* WinHeightLow */ + {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW}, /* WinWidthLow */ +/* Init the sensor */ + {0xaa, 0x02, 0x0004}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x09, 0x0005}, + {0xaa, 0x0a, 0x0002}, + {0xaa, 0x0b, 0x0002}, + {0xaa, 0x0c, 0x0005}, + {0xaa, 0x0d, 0x0000}, + {0xaa, 0x0e, 0x0002}, + {0xaa, 0x14, 0x0081}, + +/* Other registors */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* SensorCorrection */ +/* Frame retreiving */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* AutoAdjustFPS */ +/* Gains */ + {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN}, /* DigitalGain */ +/* Unknown */ + {0xa0, 0x00, 0x01ad}, +/* Sharpness */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* SharpnessMode */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* Sharpness05 */ +/* Other registors */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */ +/* Auto exposure and white balance */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* AWBStatus */ +/*Dead pixels */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */ +/* EEPROM */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* EEPROMAccess */ +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ +/* Other registers */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */ +/* Auto exposure and white balance */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* AWBStatus */ +/*Dead pixels */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */ +/* EEPROM */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* EEPROMAccess */ +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf4, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf4, ZC3XX_R10D_RGB10}, + {0xa0, 0x58, ZC3XX_R10E_RGB11}, + {0xa0, 0xf4, ZC3XX_R10F_RGB12}, + {0xa0, 0xf4, ZC3XX_R110_RGB20}, + {0xa0, 0xf4, ZC3XX_R111_RGB21}, + {0xa0, 0x58, ZC3XX_R112_RGB22}, +/* Auto correction */ + {0xa0, 0x03, ZC3XX_R181_WINXSTART}, /* WinXstart */ + {0xa0, 0x08, ZC3XX_R182_WINXWIDTH}, /* WinXWidth */ + {0xa0, 0x16, ZC3XX_R183_WINXCENTER}, /* WinXCenter */ + {0xa0, 0x03, ZC3XX_R184_WINYSTART}, /* WinYStart */ + {0xa0, 0x05, ZC3XX_R185_WINYWIDTH}, /* WinYWidth */ + {0xa0, 0x14, ZC3XX_R186_WINYCENTER}, /* WinYCenter */ + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ + +/* Auto exposure and white balance */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* ExposureLimitHigh */ + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* ExposureLimitMid */ + {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW}, /* ExposureLimitLow */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* AntiFlickerHigh */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* AntiFlickerLow */ + {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, /* AntiFlickerLow */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* AEBFreeze */ + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* AEBUnfreeze */ +/* sensor on */ + {0xaa, 0x07, 0x00b1}, + {0xaa, 0x05, 0x0003}, + {0xaa, 0x04, 0x0001}, + {0xaa, 0x03, 0x003b}, +/* Gains */ + {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* DigitalLimitDiff */ + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* DigitalGainStep */ + {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN}, /* GlobalGain */ + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* GlobalGain */ +/* Auto correction */ + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ + {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ +/* Gains */ + {0xa0, 0x40, ZC3XX_R116_RGAIN}, /* RGain */ + {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* GGain */ + {0xa0, 0x40, ZC3XX_R118_BGAIN}, /* BGain */ + {} +}; + +static const struct usb_action pas106b_InitialScale[] = { /* 352x288 */ +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ +/* Sream and Sensor specific */ + {0xa0, 0x0f, ZC3XX_R010_CMOSSENSORSELECT}, /* CMOSSensorSelect */ +/* Picture size */ + {0xa0, 0x01, ZC3XX_R003_FRAMEWIDTHHIGH}, /* FrameWidthHigh */ + {0xa0, 0x60, ZC3XX_R004_FRAMEWIDTHLOW}, /* FrameWidthLow */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* FrameHeightHigh */ + {0xa0, 0x20, ZC3XX_R006_FRAMEHEIGHTLOW}, /* FrameHightLow */ +/* System */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* SystemOperating */ +/* Sream and Sensor specific */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* VideoControlFunction */ +/* Sensor Interface */ + {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* Compatibily Mode */ +/* Window inside sensor array */ + {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* WinXStartLow */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* FirstYLow */ + {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* FirstxLow */ + {0xa0, 0x28, ZC3XX_R09C_WINHEIGHTLOW}, /* WinHeightLow */ + {0xa0, 0x68, ZC3XX_R09E_WINWIDTHLOW}, /* WinWidthLow */ +/* Init the sensor */ + {0xaa, 0x02, 0x0004}, + {0xaa, 0x08, 0x0000}, + {0xaa, 0x09, 0x0005}, + {0xaa, 0x0a, 0x0002}, + {0xaa, 0x0b, 0x0002}, + {0xaa, 0x0c, 0x0005}, + {0xaa, 0x0d, 0x0000}, + {0xaa, 0x0e, 0x0002}, + {0xaa, 0x14, 0x0081}, + +/* Other registors */ + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, /* SensorCorrection */ +/* Frame retreiving */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* AutoAdjustFPS */ +/* Gains */ + {0xa0, 0xa0, ZC3XX_R1A8_DIGITALGAIN}, /* DigitalGain */ +/* Unknown */ + {0xa0, 0x00, 0x01ad}, +/* Sharpness */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* SharpnessMode */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* Sharpness05 */ +/* Other registors */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */ +/* Auto exposure and white balance */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* AWBStatus */ + {0xa0, 0x80, ZC3XX_R18D_YTARGET}, /* ????????? */ +/*Dead pixels */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */ +/* EEPROM */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* EEPROMAccess */ +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ +/* Other registers */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* OperationMode */ +/* Auto exposure and white balance */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* AWBStatus */ +/*Dead pixels */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* DeadPixelsMode */ +/* EEPROM */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* EEPROMAccess */ +/* JPEG control */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* ClockSetting */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf4, ZC3XX_R10B_RGB01}, + {0xa0, 0xf4, ZC3XX_R10C_RGB02}, + {0xa0, 0xf4, ZC3XX_R10D_RGB10}, + {0xa0, 0x58, ZC3XX_R10E_RGB11}, + {0xa0, 0xf4, ZC3XX_R10F_RGB12}, + {0xa0, 0xf4, ZC3XX_R110_RGB20}, + {0xa0, 0xf4, ZC3XX_R111_RGB21}, + {0xa0, 0x58, ZC3XX_R112_RGB22}, +/* Auto correction */ + {0xa0, 0x03, ZC3XX_R181_WINXSTART}, /* WinXstart */ + {0xa0, 0x08, ZC3XX_R182_WINXWIDTH}, /* WinXWidth */ + {0xa0, 0x16, ZC3XX_R183_WINXCENTER}, /* WinXCenter */ + {0xa0, 0x03, ZC3XX_R184_WINYSTART}, /* WinYStart */ + {0xa0, 0x05, ZC3XX_R185_WINYWIDTH}, /* WinYWidth */ + {0xa0, 0x14, ZC3XX_R186_WINYCENTER}, /* WinYCenter */ + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ + +/* Auto exposure and white balance */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* ExposureLimitHigh 0 */ + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* ExposureLimitMid */ + {0xa0, 0xb1, ZC3XX_R192_EXPOSURELIMITLOW}, /* ExposureLimitLow 0xb1 */ + + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* AntiFlickerHigh 0x00 */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* AntiFlickerLow 0x00 */ + {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, /* AntiFlickerLow 0x87 */ + + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* AEBFreeze 0x10 0x0c */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* AEBUnfreeze 0x30 0x18 */ +/* sensor on */ + {0xaa, 0x07, 0x00b1}, + {0xaa, 0x05, 0x0003}, + {0xaa, 0x04, 0x0001}, + {0xaa, 0x03, 0x003b}, +/* Gains */ + {0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* DigitalLimitDiff */ + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* DigitalGainStep */ + {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN}, /* GlobalGain */ + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, /* GlobalGain */ +/* Auto correction */ + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ + {0xa1, 0x01, 0x0180}, /* AutoCorrectEnable */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* AutoCorrectEnable */ +/* Gains */ + {0xa0, 0x40, ZC3XX_R116_RGAIN}, /* RGain */ + {0xa0, 0x40, ZC3XX_R117_GGAIN}, /* GGain */ + {0xa0, 0x40, ZC3XX_R118_BGAIN}, /* BGain */ + + {0xa0, 0x00, 0x0007}, /* AutoCorrectEnable */ + {0xa0, 0xff, ZC3XX_R018_FRAMELOST}, /* Frame adjust */ + {} +}; +static const struct usb_action pas106b_50HZ[] = { + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */ + {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,54,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x87, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,87,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */ + {0xaa, 0x03, 0x0021}, /* 00,03,21,aa */ + {0xaa, 0x04, 0x000c}, /* 00,04,0c,aa */ + {0xaa, 0x05, 0x0002}, /* 00,05,02,aa */ + {0xaa, 0x07, 0x001c}, /* 00,07,1c,aa */ + {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */ + {} +}; +static const struct usb_action pas106b_60HZ[] = { + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */ + {0xa0, 0x2e, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,2e,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x71, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,71,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x30, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,30,cc */ + {0xaa, 0x03, 0x001c}, /* 00,03,1c,aa */ + {0xaa, 0x04, 0x0004}, /* 00,04,04,aa */ + {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */ + {0xaa, 0x07, 0x00c4}, /* 00,07,c4,aa */ + {0xa0, 0x04, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,04,cc */ + {} +}; +static const struct usb_action pas106b_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,06,cc */ + {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xaa, 0x03, 0x0013}, /* 00,03,13,aa */ + {0xaa, 0x04, 0x0000}, /* 00,04,00,aa */ + {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */ + {0xaa, 0x07, 0x0030}, /* 00,07,30,aa */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {} +}; + +static const struct usb_action pb03303x_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xaa, 0x01, 0x0001}, + {0xaa, 0x06, 0x0000}, + {0xaa, 0x08, 0x0483}, + {0xaa, 0x01, 0x0004}, + {0xaa, 0x08, 0x0006}, + {0xaa, 0x02, 0x0011}, + {0xaa, 0x03, 0x01e7}, + {0xaa, 0x04, 0x0287}, + {0xaa, 0x07, 0x3002}, + {0xaa, 0x20, 0x1100}, + {0xaa, 0x35, 0x0050}, + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xaa, 0x2b, 0x0028}, + {0xaa, 0x2c, 0x0030}, + {0xaa, 0x2d, 0x0030}, + {0xaa, 0x2e, 0x0028}, + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, + {0xa0, 0x65, ZC3XX_R118_BGAIN}, + + {0xa1, 0x01, 0x0002}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x0d, 0x003a}, + {0xa0, 0x02, 0x003b}, + {0xa0, 0x00, 0x0038}, + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ + {0xa0, 0x38, ZC3XX_R121_GAMMA01}, + {0xa0, 0x59, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x92, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, + {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0009}, + {0xaa, 0x09, 0x0134}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action pb03303x_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xaa, 0x01, 0x0001}, + {0xaa, 0x06, 0x0000}, + {0xaa, 0x08, 0x0483}, + {0xaa, 0x01, 0x0004}, + {0xaa, 0x08, 0x0006}, + {0xaa, 0x02, 0x0011}, + {0xaa, 0x03, 0x01e7}, + {0xaa, 0x04, 0x0287}, + {0xaa, 0x07, 0x3002}, + {0xaa, 0x20, 0x1100}, + {0xaa, 0x35, 0x0050}, + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xaa, 0x2b, 0x0028}, + {0xaa, 0x2c, 0x0030}, + {0xaa, 0x2d, 0x0030}, + {0xaa, 0x2e, 0x0028}, + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, + {0xa0, 0x65, ZC3XX_R118_BGAIN}, + + {0xa1, 0x01, 0x0002}, + + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + + {0xa0, 0x0d, 0x003a}, + {0xa0, 0x02, 0x003b}, + {0xa0, 0x00, 0x0038}, + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ + {0xa0, 0x38, ZC3XX_R121_GAMMA01}, + {0xa0, 0x59, ZC3XX_R122_GAMMA02}, + {0xa0, 0x79, ZC3XX_R123_GAMMA03}, + {0xa0, 0x92, ZC3XX_R124_GAMMA04}, + {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, + {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, + {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, + {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, + {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, + {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x26, ZC3XX_R130_GAMMA10}, + {0xa0, 0x22, ZC3XX_R131_GAMMA11}, + {0xa0, 0x20, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, + {0xa0, 0x16, ZC3XX_R134_GAMMA14}, + {0xa0, 0x13, ZC3XX_R135_GAMMA15}, + {0xa0, 0x10, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0009}, + {0xaa, 0x09, 0x0134}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action pb0330xx_Initial[] = { + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x0006}, + {0xaa, 0x02, 0x0011}, + {0xaa, 0x03, 0x01e7}, + {0xaa, 0x04, 0x0287}, + {0xaa, 0x06, 0x0003}, + {0xaa, 0x07, 0x3002}, + {0xaa, 0x20, 0x1100}, + {0xaa, 0x2f, 0xf7b0}, + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x34, 0x0100}, + {0xaa, 0x35, 0x0060}, + {0xaa, 0x3d, 0x068f}, + {0xaa, 0x40, 0x01e0}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, + {0xa1, 0x01, 0x0002}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, + {0xa1, 0x01, 0x0091}, + {0xa1, 0x01, 0x0095}, + {0xa1, 0x01, 0x0096}, + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0066}, + {0xaa, 0x09, 0x02b2}, + {0xaa, 0x10, 0x0002}, + + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0007}, +/* {0xa0, 0x30, 0x0007}, */ +/* {0xa0, 0x00, 0x0007}, */ + {} +}; + +static const struct usb_action pb0330xx_InitialScale[] = { + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 10 */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xaa, 0x01, 0x0006}, + {0xaa, 0x02, 0x0011}, + {0xaa, 0x03, 0x01e7}, + {0xaa, 0x04, 0x0287}, + {0xaa, 0x06, 0x0003}, + {0xaa, 0x07, 0x3002}, + {0xaa, 0x20, 0x1100}, + {0xaa, 0x2f, 0xf7b0}, + {0xaa, 0x30, 0x0005}, + {0xaa, 0x31, 0x0000}, + {0xaa, 0x34, 0x0100}, + {0xaa, 0x35, 0x0060}, + {0xaa, 0x3d, 0x068f}, + {0xaa, 0x40, 0x01e0}, + {0xaa, 0x58, 0x0078}, + {0xaa, 0x62, 0x0411}, + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, + {0xa1, 0x01, 0x0002}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, + {0xa1, 0x01, 0x0091}, + {0xa1, 0x01, 0x0095}, + {0xa1, 0x01, 0x0096}, + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf8, ZC3XX_R10B_RGB01}, + {0xa0, 0xf8, ZC3XX_R10C_RGB02}, + {0xa0, 0xf8, ZC3XX_R10D_RGB10}, + {0xa0, 0x50, ZC3XX_R10E_RGB11}, + {0xa0, 0xf8, ZC3XX_R10F_RGB12}, + {0xa0, 0xf8, ZC3XX_R110_RGB20}, + {0xa0, 0xf8, ZC3XX_R111_RGB21}, + {0xa0, 0x50, ZC3XX_R112_RGB22}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x05, 0x0066}, + {0xaa, 0x09, 0x02b2}, + {0xaa, 0x10, 0x0002}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0008}, + {0xa1, 0x01, 0x0007}, +/* {0xa0, 0x30, 0x0007}, */ +/* {0xa0, 0x00, 0x0007}, */ + {} +}; +static const struct usb_action pb0330_50HZ[] = { + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,ee,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, /* 00,1d,68,cc */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {} +}; +static const struct usb_action pb0330_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0xe5, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e5,cc */ + {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f0,cc */ + {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */ + {} +}; +static const struct usb_action pb0330_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,dd,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x43, ZC3XX_R01D_HSYNC_0}, /* 00,1d,43,cc */ + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {} +}; +static const struct usb_action pb0330_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */ + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {} +}; +static const struct usb_action pb0330_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */ + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */ + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {} +}; +static const struct usb_action pb0330_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */ + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */ + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {} +}; + +/* from oem9.inf - HKR,%PO2030%,Initial - 640x480 - (close to CS2102) */ +static const struct usb_action PO2030_mode0[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */ + {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */ + {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */ + {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */ + {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */ + {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */ + {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */ + {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ + {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xaa, 0x09, 0x00ce}, /* 00,09,ce,aa */ + {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */ + {0xaa, 0x0d, 0x0054}, /* 00,0d,54,aa */ + {0xaa, 0x0f, 0x00eb}, /* 00,0f,eb,aa */ + {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */ + {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */ + {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */ + {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */ + {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */ + {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */ + {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */ + {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */ + {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */ + {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */ + {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */ + {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */ + {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */ + {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */ + {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */ + {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */ + {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ + {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */ + {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */ + {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */ + {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */ + {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */ + {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */ + {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */ + {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */ + {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */ + {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */ + {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */ + {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */ + {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */ + {} +}; + +/* from oem9.inf - HKR,%PO2030%,InitialScale - 320x240 */ +static const struct usb_action PO2030_mode1[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x04, ZC3XX_R080_HBLANKHIGH}, /* 00,80,04,cc */ + {0xa0, 0x05, ZC3XX_R081_HBLANKLOW}, /* 00,81,05,cc */ + {0xa0, 0x16, ZC3XX_R083_RGAINADDR}, /* 00,83,16,cc */ + {0xa0, 0x18, ZC3XX_R085_BGAINADDR}, /* 00,85,18,cc */ + {0xa0, 0x1a, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,1a,cc */ + {0xa0, 0x1b, ZC3XX_R087_EXPTIMEMID}, /* 00,87,1b,cc */ + {0xa0, 0x1c, ZC3XX_R088_EXPTIMELOW}, /* 00,88,1c,cc */ + {0xa0, 0xee, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,ee,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ + {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xaa, 0x09, 0x00cc}, /* 00,09,cc,aa */ + {0xaa, 0x0b, 0x0005}, /* 00,0b,05,aa */ + {0xaa, 0x0d, 0x0058}, /* 00,0d,58,aa */ + {0xaa, 0x0f, 0x00ed}, /* 00,0f,ed,aa */ + {0xaa, 0x87, 0x0000}, /* 00,87,00,aa */ + {0xaa, 0x88, 0x0004}, /* 00,88,04,aa */ + {0xaa, 0x89, 0x0000}, /* 00,89,00,aa */ + {0xaa, 0x8a, 0x0005}, /* 00,8a,05,aa */ + {0xaa, 0x13, 0x0003}, /* 00,13,03,aa */ + {0xaa, 0x16, 0x0040}, /* 00,16,40,aa */ + {0xaa, 0x18, 0x0040}, /* 00,18,40,aa */ + {0xaa, 0x1d, 0x0002}, /* 00,1d,02,aa */ + {0xaa, 0x29, 0x00e8}, /* 00,29,e8,aa */ + {0xaa, 0x45, 0x0045}, /* 00,45,45,aa */ + {0xaa, 0x50, 0x00ed}, /* 00,50,ed,aa */ + {0xaa, 0x51, 0x0025}, /* 00,51,25,aa */ + {0xaa, 0x52, 0x0042}, /* 00,52,42,aa */ + {0xaa, 0x53, 0x002f}, /* 00,53,2f,aa */ + {0xaa, 0x79, 0x0025}, /* 00,79,25,aa */ + {0xaa, 0x7b, 0x0000}, /* 00,7b,00,aa */ + {0xaa, 0x7e, 0x0025}, /* 00,7e,25,aa */ + {0xaa, 0x7f, 0x0025}, /* 00,7f,25,aa */ + {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */ + {0xaa, 0x33, 0x0036}, /* 00,33,36,aa */ + {0xaa, 0x36, 0x0060}, /* 00,36,60,aa */ + {0xaa, 0x37, 0x0008}, /* 00,37,08,aa */ + {0xaa, 0x3b, 0x0031}, /* 00,3b,31,aa */ + {0xaa, 0x44, 0x000f}, /* 00,44,0f,aa */ + {0xaa, 0x58, 0x0002}, /* 00,58,02,aa */ + {0xaa, 0x66, 0x00c0}, /* 00,66,c0,aa */ + {0xaa, 0x67, 0x0044}, /* 00,67,44,aa */ + {0xaa, 0x6b, 0x00a0}, /* 00,6b,a0,aa */ + {0xaa, 0x6c, 0x0054}, /* 00,6c,54,aa */ + {0xaa, 0xd6, 0x0007}, /* 00,d6,07,aa */ + {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,f7,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x00, 0x01ad}, /* 01,ad,00,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x7a, ZC3XX_R116_RGAIN}, /* 01,16,7a,cc */ + {0xa0, 0x4a, ZC3XX_R118_BGAIN}, /* 01,18,4a,cc */ + {} +}; + +static const struct usb_action PO2030_50HZ[] = { + {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ + {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */ + {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */ + {0xaa, 0x1c, 0x00b0}, /* 00,1c,b0,aa */ + {0xa0, 0x05, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,05,cc */ + {0xa0, 0x35, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,35,cc */ + {0xa0, 0x70, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,70,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x85, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,85,cc */ + {0xa0, 0x58, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,58,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */ + {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ + {} +}; + +static const struct usb_action PO2030_60HZ[] = { + {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ + {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */ + {0xaa, 0x1c, 0x0040}, /* 00,1c,40,aa */ + {0xa0, 0x08, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,08,cc */ + {0xa0, 0xae, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,ae,cc */ + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x6f, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,6f,cc */ + {0xa0, 0x20, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,20,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */ + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */ + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ + {0xa0, 0x22, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,22,cc */ + {0xa0, 0x88, ZC3XX_R18D_YTARGET}, /* 01,8d,88,cc */ + /* win: 01,8d,80 */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc */ + {} +}; + +static const struct usb_action PO2030_NoFliker[] = { + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */ + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ + {0xaa, 0x1b, 0x0002}, /* 00,1b,02,aa */ + {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */ + {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */ + {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */ + {} +}; + +/* TEST */ +static const struct usb_action tas5130CK_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x01, 0x003b}, + {0xa0, 0x0e, 0x003a}, + {0xa0, 0x01, 0x0038}, + {0xa0, 0x0b, 0x0039}, + {0xa0, 0x00, 0x0038}, + {0xa0, 0x0b, 0x0039}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xE7, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x87, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x2c, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, + {0xa0, 0x65, ZC3XX_R118_BGAIN}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf1, ZC3XX_R10B_RGB01}, + {0xa0, 0x03, ZC3XX_R10C_RGB02}, + {0xa0, 0xfe, ZC3XX_R10D_RGB10}, + {0xa0, 0x51, ZC3XX_R10E_RGB11}, + {0xa0, 0xf1, ZC3XX_R10F_RGB12}, + {0xa0, 0xec, ZC3XX_R110_RGB20}, + {0xa0, 0x03, ZC3XX_R111_RGB21}, + {0xa0, 0x51, ZC3XX_R112_RGB22}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x38, ZC3XX_R120_GAMMA00}, /* gamma > 5 */ + {0xa0, 0x51, ZC3XX_R121_GAMMA01}, + {0xa0, 0x6e, ZC3XX_R122_GAMMA02}, + {0xa0, 0x8c, ZC3XX_R123_GAMMA03}, + {0xa0, 0xa2, ZC3XX_R124_GAMMA04}, + {0xa0, 0xb6, ZC3XX_R125_GAMMA05}, + {0xa0, 0xc8, ZC3XX_R126_GAMMA06}, + {0xa0, 0xd6, ZC3XX_R127_GAMMA07}, + {0xa0, 0xe2, ZC3XX_R128_GAMMA08}, + {0xa0, 0xed, ZC3XX_R129_GAMMA09}, + {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x12, ZC3XX_R130_GAMMA10}, + {0xa0, 0x1b, ZC3XX_R131_GAMMA11}, + {0xa0, 0x1d, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1a, ZC3XX_R133_GAMMA13}, + {0xa0, 0x15, ZC3XX_R134_GAMMA14}, + {0xa0, 0x12, ZC3XX_R135_GAMMA15}, + {0xa0, 0x0f, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x05, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf1, ZC3XX_R10B_RGB01}, + {0xa0, 0x03, ZC3XX_R10C_RGB02}, + {0xa0, 0xfe, ZC3XX_R10D_RGB10}, + {0xa0, 0x51, ZC3XX_R10E_RGB11}, + {0xa0, 0xf1, ZC3XX_R10F_RGB12}, + {0xa0, 0xec, ZC3XX_R110_RGB20}, + {0xa0, 0x03, ZC3XX_R111_RGB21}, + {0xa0, 0x51, ZC3XX_R112_RGB22}, + {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x09, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x34, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action tas5130CK_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x01, 0x003b}, + {0xa0, 0x0e, 0x003a}, + {0xa0, 0x01, 0x0038}, + {0xa0, 0x0b, 0x0039}, + {0xa0, 0x00, 0x0038}, + {0xa0, 0x0b, 0x0039}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x83, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x01, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x08, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x06, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x02, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xe5, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x04, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x85, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x02, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x07, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x30, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x51, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x35, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x50, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x30, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x05, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x31, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x58, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x78, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x62, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x11, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x04, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x2B, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x2C, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7F, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x2D, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x2e, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x7f, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, + {0xa0, 0x61, ZC3XX_R116_RGAIN}, + {0xa0, 0x65, ZC3XX_R118_BGAIN}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf1, ZC3XX_R10B_RGB01}, + {0xa0, 0x03, ZC3XX_R10C_RGB02}, + {0xa0, 0xfe, ZC3XX_R10D_RGB10}, + {0xa0, 0x51, ZC3XX_R10E_RGB11}, + {0xa0, 0xf1, ZC3XX_R10F_RGB12}, + {0xa0, 0xec, ZC3XX_R110_RGB20}, + {0xa0, 0x03, ZC3XX_R111_RGB21}, + {0xa0, 0x51, ZC3XX_R112_RGB22}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + {0xa0, 0x38, ZC3XX_R120_GAMMA00}, /* gamma > 5 */ + {0xa0, 0x51, ZC3XX_R121_GAMMA01}, + {0xa0, 0x6e, ZC3XX_R122_GAMMA02}, + {0xa0, 0x8c, ZC3XX_R123_GAMMA03}, + {0xa0, 0xa2, ZC3XX_R124_GAMMA04}, + {0xa0, 0xb6, ZC3XX_R125_GAMMA05}, + {0xa0, 0xc8, ZC3XX_R126_GAMMA06}, + {0xa0, 0xd6, ZC3XX_R127_GAMMA07}, + {0xa0, 0xe2, ZC3XX_R128_GAMMA08}, + {0xa0, 0xed, ZC3XX_R129_GAMMA09}, + {0xa0, 0xf5, ZC3XX_R12A_GAMMA0A}, + {0xa0, 0xfc, ZC3XX_R12B_GAMMA0B}, + {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, + {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, + {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, + {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, + {0xa0, 0x12, ZC3XX_R130_GAMMA10}, + {0xa0, 0x1b, ZC3XX_R131_GAMMA11}, + {0xa0, 0x1d, ZC3XX_R132_GAMMA12}, + {0xa0, 0x1a, ZC3XX_R133_GAMMA13}, + {0xa0, 0x15, ZC3XX_R134_GAMMA14}, + {0xa0, 0x12, ZC3XX_R135_GAMMA15}, + {0xa0, 0x0f, ZC3XX_R136_GAMMA16}, + {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, + {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, + {0xa0, 0x09, ZC3XX_R139_GAMMA19}, + {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, + {0xa0, 0x05, ZC3XX_R13B_GAMMA1B}, + {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, + {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, + {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, + {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, + {0xa0, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xf1, ZC3XX_R10B_RGB01}, + {0xa0, 0x03, ZC3XX_R10C_RGB02}, + {0xa0, 0xfe, ZC3XX_R10D_RGB10}, + {0xa0, 0x51, ZC3XX_R10E_RGB11}, + {0xa0, 0xf1, ZC3XX_R10F_RGB12}, + {0xa0, 0xec, ZC3XX_R110_RGB20}, + {0xa0, 0x03, ZC3XX_R111_RGB21}, + {0xa0, 0x51, ZC3XX_R112_RGB22}, + {0xa0, 0x10, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0x62, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, + {0xa0, 0xaa, ZC3XX_R093_I2CSETVALUE}, + {0xa0, 0x01, ZC3XX_R094_I2CWRITEACK}, + {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x30, 0x0007}, + {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x00, 0x0007}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {} +}; + +static const struct usb_action tas5130cxx_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN}, + {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL}, + + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + + {0xa0, 0x04, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x04, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE}, + {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x68, ZC3XX_R18D_YTARGET}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xec, ZC3XX_R10B_RGB01}, + {0xa0, 0xec, ZC3XX_R10C_RGB02}, + {0xa0, 0xec, ZC3XX_R10D_RGB10}, + {0xa0, 0x68, ZC3XX_R10E_RGB11}, + {0xa0, 0xec, ZC3XX_R10F_RGB12}, + {0xa0, 0xec, ZC3XX_R110_RGB20}, + {0xa0, 0xec, ZC3XX_R111_RGB21}, + {0xa0, 0x68, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x018d}, + {0xa0, 0x90, ZC3XX_R18D_YTARGET}, /* 90 */ + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + + {0xaa, 0xa3, 0x0001}, + {0xaa, 0xa4, 0x0077}, + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, + {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, + + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 00 */ + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 03 */ + {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* e8 */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 0 */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 0 */ + {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 7d */ + + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 08 */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 24 */ + {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, + {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 50 */ + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action tas5130cxx_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, + {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, + + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa1, 0x01, 0x0008}, + + {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN}, + {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL}, + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, + {0xa0, 0x05, ZC3XX_R098_WINYSTARTLOW}, + {0xa0, 0x0f, ZC3XX_R09A_WINXSTARTLOW}, + {0xa0, 0x05, ZC3XX_R11A_FIRSTYLOW}, + {0xa0, 0x0f, ZC3XX_R11C_FIRSTXLOW}, + {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, + {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, + {0xa0, 0x06, ZC3XX_R08D_COMPABILITYMODE}, + {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x68, ZC3XX_R18D_YTARGET}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x00, 0x01ad}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa1, 0x01, 0x0002}, + {0xa1, 0x01, 0x0008}, + + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, + {0xa1, 0x01, 0x0008}, /* clock ? */ + {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ + {0xa1, 0x01, 0x01c8}, + {0xa1, 0x01, 0x01c9}, + {0xa1, 0x01, 0x01ca}, + {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ + + {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */ + {0xa0, 0xec, ZC3XX_R10B_RGB01}, + {0xa0, 0xec, ZC3XX_R10C_RGB02}, + {0xa0, 0xec, ZC3XX_R10D_RGB10}, + {0xa0, 0x68, ZC3XX_R10E_RGB11}, + {0xa0, 0xec, ZC3XX_R10F_RGB12}, + {0xa0, 0xec, ZC3XX_R110_RGB20}, + {0xa0, 0xec, ZC3XX_R111_RGB21}, + {0xa0, 0x68, ZC3XX_R112_RGB22}, + + {0xa1, 0x01, 0x018d}, + {0xa0, 0x90, ZC3XX_R18D_YTARGET}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0xa3, 0x0001}, + {0xaa, 0xa4, 0x0063}, + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, + {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, + {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa1, 0x01, 0x0180}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action tas5130cxx_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0063}, /* 00,a4,63,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ + {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,38,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */ + {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */ + {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {} +}; +static const struct usb_action tas5130cxx_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ + {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ + {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */ + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */ + {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {} +}; +static const struct usb_action tas5130cxx_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0036}, /* 00,a4,36,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */ + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ + {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {} +}; +static const struct usb_action tas5130cxx_60HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0077}, /* 00,a4,77,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ + {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ + {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ + {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */ + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ + {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {} +}; +static const struct usb_action tas5130cxx_NoFliker[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0040}, /* 00,a4,40,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ + {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ + {} +}; + +static const struct usb_action tas5130cxx_NoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ + {0xaa, 0xa3, 0x0001}, /* 00,a3,01,aa */ + {0xaa, 0xa4, 0x0090}, /* 00,a4,90,aa */ + {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ + {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ + {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ + {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ + {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ + {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ + {} +}; + +static const struct usb_action tas5130c_vf0250_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */ + {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc, + * 0<->10 */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */ + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc, + * 6<->8 */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc, + * 6<->8 */ + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ + {0xaa, 0x1b, 0x0024}, /* 00,1b,24,aa, */ + {0xdd, 0x00, 0x0080}, /* 00,00,80,dd, */ + {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa, */ + {0xaa, 0x13, 0x0002}, /* 00,13,02,aa, */ + {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */ + {0xaa, 0x01, 0x0000}, + {0xaa, 0x01, 0x0000}, + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */ + {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */ + {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */ + {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */ + {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */ + {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */ + {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa, */ + {0xaa, 0x0b, 0x00a0}, /* 00,0b,a0,aa, */ + {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa, */ + {0xaa, 0x0d, 0x00a0}, /* 00,0d,a0,aa, */ + {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa, */ + {0xaa, 0x0f, 0x00a0}, /* 00,0f,a0,aa, */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa, */ + {0xaa, 0x11, 0x00a0}, /* 00,11,a0,aa, */ + {0xa0, 0x00, 0x0039}, + {0xa1, 0x01, 0x0037}, + {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */ + {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa, (e6 -> e8) */ + {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */ + {0xaa, 0x19, 0x0088}, /* 00,19,86,aa, */ + {0xaa, 0x20, 0x0020}, /* 00,20,20,aa, */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */ + {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */ + {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */ + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc, */ + {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */ + {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */ + {} +}; + +static const struct usb_action tas5130c_vf0250_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */ + {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */ + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc, */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc, */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc, */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc, */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,cc, */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc, */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc, */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc, */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc, */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc, */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc, */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc, */ + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e8,cc, + * 8<->6 */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc, + * 8<->6 */ + {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, /* 00,87,10,cc, */ + {0xa0, 0x98, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,98,cc, */ + {0xaa, 0x1b, 0x0024}, /* 00,1b,24,aa, */ + {0xdd, 0x00, 0x0080}, /* 00,00,80,dd, */ + {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa, */ + {0xaa, 0x13, 0x0002}, /* 00,13,02,aa, */ + {0xaa, 0x15, 0x0004}, /* 00,15,04,aa */ + {0xaa, 0x01, 0x0000}, + {0xaa, 0x01, 0x0000}, + {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */ + {0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */ + {0xa0, 0x82, ZC3XX_R086_EXPTIMEHIGH}, /* 00,86,82,cc, */ + {0xa0, 0x83, ZC3XX_R087_EXPTIMEMID}, /* 00,87,83,cc, */ + {0xa0, 0x84, ZC3XX_R088_EXPTIMELOW}, /* 00,88,84,cc, */ + {0xaa, 0x05, 0x0010}, /* 00,05,10,aa, */ + {0xaa, 0x0a, 0x0000}, /* 00,0a,00,aa, */ + {0xaa, 0x0b, 0x00a0}, /* 00,0b,a0,aa, */ + {0xaa, 0x0c, 0x0000}, /* 00,0c,00,aa, */ + {0xaa, 0x0d, 0x00a0}, /* 00,0d,a0,aa, */ + {0xaa, 0x0e, 0x0000}, /* 00,0e,00,aa, */ + {0xaa, 0x0f, 0x00a0}, /* 00,0f,a0,aa, */ + {0xaa, 0x10, 0x0000}, /* 00,10,00,aa, */ + {0xaa, 0x11, 0x00a0}, /* 00,11,a0,aa, */ + {0xa0, 0x00, 0x0039}, + {0xa1, 0x01, 0x0037}, + {0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */ + {0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa (e6 -> e8) */ + {0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */ + {0xaa, 0x19, 0x0088}, /* 00,19,88,aa, */ + {0xaa, 0x20, 0x0020}, /* 00,20,20,aa, */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc, */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc, */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc, */ + {0xa0, 0x76, ZC3XX_R189_AWBSTATUS}, /* 01,89,76,cc, */ + {0xa0, 0x09, 0x01ad}, /* 01,ad,09,cc, */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc, */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc, */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc, */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc, */ + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,60,cc, */ + {0xa0, 0x61, ZC3XX_R116_RGAIN}, /* 01,16,61,cc, */ + {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */ + {} +}; +/* "50HZ" light frequency banding filter */ +static const struct usb_action tas5130c_vf0250_50HZ[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0001}, /* 00,83,01,aa */ + {0xaa, 0x84, 0x00aa}, /* 00,84,aa,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ + {0xa0, 0x06, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */ + {0xa0, 0xa8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ + {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /* 01,8d,78,cc */ + {} +}; + +/* "50HZScale" light frequency banding filter */ +static const struct usb_action tas5130c_vf0250_50HZScale[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0003}, /* 00,83,03,aa */ + {0xaa, 0x84, 0x0054}, /* 00,84,54,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ + {0xa0, 0x0d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0d,cc, */ + {0xa0, 0x50, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,50,cc, */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ + {0xa0, 0x8e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,8e,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /* 01,8d,78,cc */ + {} +}; + +/* "60HZ" light frequency banding filter */ +static const struct usb_action tas5130c_vf0250_60HZ[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0001}, /* 00,83,01,aa */ + {0xaa, 0x84, 0x0062}, /* 00,84,62,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ + {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,05,cc, */ + {0xa0, 0x88, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,88,cc, */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ + {0xa0, 0x3b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3b,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc, */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc, */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /* 01,8d,78,cc */ + {} +}; + +/* "60HZScale" light frequency banding ilter */ +static const struct usb_action tas5130c_vf0250_60HZScale[] = { + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0002}, /* 00,83,02,aa */ + {0xaa, 0x84, 0x00c4}, /* 00,84,c4,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ + {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,1,0b,cc, */ + {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,2,10,cc, */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,5,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,6,00,cc, */ + {0xa0, 0x76, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,7,76,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,f,15,cc, */ + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,9,10,cc, */ + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,a,24,cc, */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,0,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,d,58,cc, */ + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,42,cc, */ + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /* 01,d,78,cc */ + {} +}; + +/* "NoFliker" light frequency banding flter */ +static const struct usb_action tas5130c_vf0250_NoFliker[] = { + {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */ + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ + {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,0,00,cc, */ + {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,05,cc, */ + {0xa0, 0x88, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,88,cc, */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ + {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */ + {} +}; + +/* "NoFlikerScale" light frequency banding filter */ +static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = { + {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */ + {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ + {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ + {0xaa, 0x84, 0x0020}, /* 00,84,20,aa */ + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc, */ + {0xa0, 0x0b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0b,cc, */ + {0xa0, 0x10, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,10,cc, */ + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc, */ + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc, */ + {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc, */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0e,cc, */ + {0xa0, 0x15, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,15,cc, */ + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, /* 00,1d,62,cc, */ + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc, */ + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc, */ + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc, */ + {0xa0, 0x58, ZC3XX_R11D_GLOBALGAIN}, /* 01,1d,58,cc, */ + {0xa0, 0x03, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,03,cc */ + {} +}; + +static int reg_r_i(struct gspca_dev *gspca_dev, + __u16 index) +{ + usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + 0xa1, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x01, /* value */ + index, gspca_dev->usb_buf, 1, + 500); + return gspca_dev->usb_buf[0]; +} + +static int reg_r(struct gspca_dev *gspca_dev, + __u16 index) +{ + int ret; + + ret = reg_r_i(gspca_dev, index); + PDEBUG(D_USBI, "reg r [%04x] -> %02x", index, ret); + return ret; +} + +static void reg_w_i(struct usb_device *dev, + __u8 value, + __u16 index) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0xa0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, + 500); +} + +static void reg_w(struct usb_device *dev, + __u8 value, + __u16 index) +{ + PDEBUG(D_USBO, "reg w %02x -> [%04x]", value, index); + reg_w_i(dev, value, index); +} + +static __u16 i2c_read(struct gspca_dev *gspca_dev, + __u8 reg) +{ + __u8 retbyte; + __u8 retval[2]; + + reg_w_i(gspca_dev->dev, reg, 0x92); + reg_w_i(gspca_dev->dev, 0x02, 0x90); /* <- read command */ + msleep(25); + retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + retval[0] = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */ + retval[1] = reg_r_i(gspca_dev, 0x0096); /* read Hightbyte */ + PDEBUG(D_USBO, "i2c r [%02x] -> (%02x) %02x%02x", + reg, retbyte, retval[1], retval[0]); + return (retval[1] << 8) | retval[0]; +} + +static __u8 i2c_write(struct gspca_dev *gspca_dev, + __u8 reg, + __u8 valL, + __u8 valH) +{ + __u8 retbyte; + + reg_w_i(gspca_dev->dev, reg, 0x92); + reg_w_i(gspca_dev->dev, valL, 0x93); + reg_w_i(gspca_dev->dev, valH, 0x94); + reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */ + msleep(5); + retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + PDEBUG(D_USBO, "i2c w [%02x] %02x%02x (%02x)", + reg, valH, valL, retbyte); + return retbyte; +} + +static void usb_exchange(struct gspca_dev *gspca_dev, + const struct usb_action *action) +{ + while (action->req) { + switch (action->req) { + case 0xa0: /* write register */ + reg_w(gspca_dev->dev, action->val, action->idx); + break; + case 0xa1: /* read status */ + reg_r(gspca_dev, action->idx); + break; + case 0xaa: + i2c_write(gspca_dev, + action->val, /* reg */ + action->idx & 0xff, /* valL */ + action->idx >> 8); /* valH */ + break; + default: +/* case 0xdd: * delay */ + msleep(action->val / 64 + 10); + break; + } + action++; +/* msleep(1); */ + } +} + +static void setmatrix(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + const __u8 *matrix; + static const __u8 gc0305_matrix[9] = + {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50}; + static const __u8 ov7620_matrix[9] = + {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58}; + static const __u8 po2030_matrix[9] = + {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60}; + + switch (sd->sensor) { + case SENSOR_GC0305: + matrix = gc0305_matrix; + break; + case SENSOR_MC501CB: + return; /* no matrix? */ + case SENSOR_OV7620: +/* case SENSOR_OV7648: */ + matrix = ov7620_matrix; + break; + case SENSOR_PO2030: + matrix = po2030_matrix; + break; + case SENSOR_TAS5130C_VF0250: /* no matrix? */ + return; + default: /* matrix already loaded */ + return; + } + for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++) + reg_w(gspca_dev->dev, matrix[i], 0x010a + i); +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 brightness; + + switch (sd->sensor) { + case SENSOR_GC0305: + case SENSOR_OV7620: + case SENSOR_PO2030: + return; + } +/*fixme: is it really write to 011d and 018d for all other sensors? */ + brightness = sd->brightness; + reg_w(gspca_dev->dev, brightness, 0x011d); + if (brightness < 0x70) + brightness += 0x10; + else + brightness = 0x80; + reg_w(gspca_dev->dev, brightness, 0x018d); +} + +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int sharpness; + static const __u8 sharpness_tb[][2] = { + {0x02, 0x03}, + {0x04, 0x07}, + {0x08, 0x0f}, + {0x10, 0x1e} + }; + + sharpness = sd->sharpness; + reg_w(dev, sharpness_tb[sharpness][0], 0x01c6); + reg_r(gspca_dev, 0x01c8); + reg_r(gspca_dev, 0x01c9); + reg_r(gspca_dev, 0x01ca); + reg_w(dev, sharpness_tb[sharpness][1], 0x01cb); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + const __u8 *Tgamma, *Tgradient; + int g, i, k; + static const __u8 kgamma_tb[16] = /* delta for contrast */ + {0x15, 0x0d, 0x0a, 0x09, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; + static const __u8 kgrad_tb[16] = + {0x1b, 0x06, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x04}; + static const __u8 Tgamma_1[16] = + {0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f, + 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff}; + static const __u8 Tgradient_1[16] = + {0x00, 0x01, 0x05, 0x0b, 0x10, 0x15, 0x18, 0x1a, + 0x1a, 0x18, 0x16, 0x14, 0x12, 0x0f, 0x0d, 0x06}; + static const __u8 Tgamma_2[16] = + {0x01, 0x0c, 0x1f, 0x3a, 0x53, 0x6d, 0x85, 0x9c, + 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff}; + static const __u8 Tgradient_2[16] = + {0x05, 0x0f, 0x16, 0x1a, 0x19, 0x19, 0x17, 0x15, + 0x12, 0x10, 0x0e, 0x0b, 0x09, 0x08, 0x06, 0x03}; + static const __u8 Tgamma_3[16] = + {0x04, 0x16, 0x30, 0x4e, 0x68, 0x81, 0x98, 0xac, + 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff}; + static const __u8 Tgradient_3[16] = + {0x0c, 0x16, 0x1b, 0x1c, 0x19, 0x18, 0x15, 0x12, + 0x10, 0x0d, 0x0b, 0x09, 0x08, 0x06, 0x05, 0x03}; + static const __u8 Tgamma_4[16] = + {0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff}; + static const __u8 Tgradient_4[16] = + {0x26, 0x22, 0x20, 0x1c, 0x16, 0x13, 0x10, 0x0d, + 0x0b, 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02}; + static const __u8 Tgamma_5[16] = + {0x20, 0x4b, 0x6e, 0x8d, 0xa3, 0xb5, 0xc5, 0xd2, + 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff}; + static const __u8 Tgradient_5[16] = + {0x37, 0x26, 0x20, 0x1a, 0x14, 0x10, 0x0e, 0x0b, + 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02}; + static const __u8 Tgamma_6[16] = /* ?? was gamma 5 */ + {0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3, + 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff}; + static const __u8 Tgradient_6[16] = + {0x18, 0x20, 0x20, 0x1c, 0x16, 0x13, 0x10, 0x0e, + 0x0b, 0x09, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01}; + static const __u8 *gamma_tb[] = { + NULL, Tgamma_1, Tgamma_2, + Tgamma_3, Tgamma_4, Tgamma_5, Tgamma_6 + }; + static const __u8 *gradient_tb[] = { + NULL, Tgradient_1, Tgradient_2, + Tgradient_3, Tgradient_4, Tgradient_5, Tgradient_6 + }; +#ifdef CONFIG_VIDEO_ADV_DEBUG + __u8 v[16]; +#endif + + Tgamma = gamma_tb[sd->gamma]; + Tgradient = gradient_tb[sd->gamma]; + + k = (sd->contrast - 128) /* -128 / 128 */ + * Tgamma[0]; + PDEBUG(D_CONF, "gamma:%d contrast:%d gamma coeff: %d/128", + sd->gamma, sd->contrast, k); + for (i = 0; i < 16; i++) { + g = Tgamma[i] + kgamma_tb[i] * k / 128; + if (g > 0xff) + g = 0xff; + else if (g <= 0) + g = 1; + reg_w(dev, g, 0x0120 + i); /* gamma */ +#ifdef CONFIG_VIDEO_ADV_DEBUG + if (gspca_debug & D_CONF) + v[i] = g; +#endif + } + PDEBUG(D_CONF, "tb: %02x %02x %02x %02x %02x %02x %02x %02x", + v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); + PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x", + v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]); + for (i = 0; i < 16; i++) { + g = Tgradient[i] - kgrad_tb[i] * k / 128; + if (g > 0xff) + g = 0xff; + else if (g <= 0) { + if (i != 15) + g = 0; + else + g = 1; + } + reg_w(dev, g, 0x0130 + i); /* gradient */ +#ifdef CONFIG_VIDEO_ADV_DEBUG + if (gspca_debug & D_CONF) + v[i] = g; +#endif + } + PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x", + v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); + PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x", + v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]); +} + +static void setquality(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 quality; + __u8 frxt; + + switch (sd->sensor) { + case SENSOR_GC0305: + case SENSOR_OV7620: + case SENSOR_PO2030: + return; + } +/*fixme: is it really 0008 0007 0018 for all other sensors? */ + quality = sd->qindex; + reg_w(dev, quality, 0x0008); + frxt = 0x30; + reg_w(dev, frxt, 0x0007); + switch (quality) { + case 0: + case 1: + case 2: + frxt = 0xff; + break; + case 3: + frxt = 0xf0; + break; + case 4: + frxt = 0xe0; + break; + case 5: + frxt = 0x20; + break; + } + reg_w(dev, frxt, 0x0018); +} + +/* Matches the sensor's internal frame rate to the lighting frequency. + * Valid frequencies are: + * 50Hz, for European and Asian lighting (default) + * 60Hz, for American lighting + * 0 = No Fliker (for outdoore usage) + * Returns: 0 for success + */ +static int setlightfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, mode; + const struct usb_action *zc3_freq; + static const struct usb_action *freq_tb[SENSOR_MAX][6] = { +/* SENSOR_CS2102 0 */ + {cs2102_NoFliker, cs2102_NoFlikerScale, + cs2102_50HZ, cs2102_50HZScale, + cs2102_60HZ, cs2102_60HZScale}, +/* SENSOR_CS2102K 1 */ + {cs2102_NoFliker, cs2102_NoFlikerScale, + cs2102_50HZ, cs2102_50HZScale, + cs2102_60HZ, cs2102_60HZScale}, +/* SENSOR_GC0305 2 */ + {gc0305_NoFliker, gc0305_NoFliker, + gc0305_50HZ, gc0305_50HZ, + gc0305_60HZ, gc0305_60HZ}, +/* SENSOR_HDCS2020 3 */ + {NULL, NULL, + NULL, NULL, + NULL, NULL}, +/* SENSOR_HDCS2020b 4 */ + {hdcs2020b_NoFliker, hdcs2020b_NoFliker, + hdcs2020b_50HZ, hdcs2020b_50HZ, + hdcs2020b_60HZ, hdcs2020b_60HZ}, +/* SENSOR_HV7131B 5 */ + {NULL, NULL, + NULL, NULL, + NULL, NULL}, +/* SENSOR_HV7131C 6 */ + {NULL, NULL, + NULL, NULL, + NULL, NULL}, +/* SENSOR_ICM105A 7 */ + {icm105a_NoFliker, icm105a_NoFlikerScale, + icm105a_50HZ, icm105a_50HZScale, + icm105a_60HZ, icm105a_60HZScale}, +/* SENSOR_MC501CB 8 */ + {MC501CB_NoFliker, MC501CB_NoFlikerScale, + MC501CB_50HZ, MC501CB_50HZScale, + MC501CB_60HZ, MC501CB_60HZScale}, +/* SENSOR_OV7620 9 */ + {OV7620_NoFliker, OV7620_NoFliker, + OV7620_50HZ, OV7620_50HZ, + OV7620_60HZ, OV7620_60HZ}, +/* SENSOR_OV7630C 10 */ + {NULL, NULL, + NULL, NULL, + NULL, NULL}, +/* SENSOR_PAS106 11 */ + {pas106b_NoFliker, pas106b_NoFliker, + pas106b_50HZ, pas106b_50HZ, + pas106b_60HZ, pas106b_60HZ}, +/* SENSOR_PB0330 12 */ + {pb0330_NoFliker, pb0330_NoFlikerScale, + pb0330_50HZ, pb0330_50HZScale, + pb0330_60HZ, pb0330_60HZScale}, +/* SENSOR_PO2030 13 */ + {PO2030_NoFliker, PO2030_NoFliker, + PO2030_50HZ, PO2030_50HZ, + PO2030_60HZ, PO2030_60HZ}, +/* SENSOR_TAS5130CK 14 */ + {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, + tas5130cxx_50HZ, tas5130cxx_50HZScale, + tas5130cxx_60HZ, tas5130cxx_60HZScale}, +/* SENSOR_TAS5130CXX 15 */ + {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, + tas5130cxx_50HZ, tas5130cxx_50HZScale, + tas5130cxx_60HZ, tas5130cxx_60HZScale}, +/* SENSOR_TAS5130C_VF0250 16 */ + {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale, + tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale, + tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale}, + }; + + i = sd->lightfreq * 2; + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + if (!mode) + i++; /* 640x480 */ + zc3_freq = freq_tb[(int) sd->sensor][i]; + if (zc3_freq != NULL) { + usb_exchange(gspca_dev, zc3_freq); + switch (sd->sensor) { + case SENSOR_GC0305: + if (mode /* if 320x240 */ + && sd->lightfreq == 1) /* and 50Hz */ + reg_w(gspca_dev->dev, 0x85, 0x018d); + /* win: 0x80, 0x018d */ + break; + case SENSOR_OV7620: + if (!mode) { /* if 640x480 */ + if (sd->lightfreq != 0) /* and 50 or 60 Hz */ + reg_w(gspca_dev->dev, 0x40, 0x0002); + else + reg_w(gspca_dev->dev, 0x44, 0x0002); + } + break; + } + } + return 0; +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 autoval; + + if (sd->autogain) + autoval = 0x42; + else + autoval = 0x02; + reg_w(gspca_dev->dev, autoval, 0x0180); +} + +static void send_unknown(struct usb_device *dev, int sensor) +{ + reg_w(dev, 0x01, 0x0000); /* led off */ + switch (sensor) { + case SENSOR_PAS106: + reg_w(dev, 0x03, 0x003a); + reg_w(dev, 0x0c, 0x003b); + reg_w(dev, 0x08, 0x0038); + break; + case SENSOR_GC0305: + case SENSOR_OV7620: + case SENSOR_PB0330: + case SENSOR_PO2030: + reg_w(dev, 0x0d, 0x003a); + reg_w(dev, 0x02, 0x003b); + reg_w(dev, 0x00, 0x0038); + break; + } +} + +/* start probe 2 wires */ +static void start_2wr_probe(struct usb_device *dev, int sensor) +{ + reg_w(dev, 0x01, 0x0000); + reg_w(dev, sensor, 0x0010); + reg_w(dev, 0x01, 0x0001); + reg_w(dev, 0x03, 0x0012); + reg_w(dev, 0x01, 0x0012); +/* msleep(2); */ +} + +static int sif_probe(struct gspca_dev *gspca_dev) +{ + __u16 checkword; + + start_2wr_probe(gspca_dev->dev, 0x0f); /* PAS106 */ + reg_w(gspca_dev->dev, 0x08, 0x008d); + msleep(150); + checkword = ((i2c_read(gspca_dev, 0x00) & 0x0f) << 4) + | ((i2c_read(gspca_dev, 0x01) & 0xf0) >> 4); + PDEBUG(D_PROBE, "probe sif 0x%04x", checkword); + if (checkword == 0x0007) { + send_unknown(gspca_dev->dev, SENSOR_PAS106); + return 0x0f; /* PAS106 */ + } + return -1; +} + +static int vga_2wr_probe(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 retbyte; + __u16 checkword; + + start_2wr_probe(dev, 0x00); /* HV7131B */ + i2c_write(gspca_dev, 0x01, 0xaa, 0x00); + retbyte = i2c_read(gspca_dev, 0x01); + if (retbyte != 0) + return 0x00; /* HV7131B */ + + start_2wr_probe(dev, 0x04); /* CS2102 */ + i2c_write(gspca_dev, 0x01, 0xaa, 0x00); + retbyte = i2c_read(gspca_dev, 0x01); + if (retbyte != 0) + return 0x04; /* CS2102 */ + + start_2wr_probe(dev, 0x06); /* OmniVision */ + reg_w(dev, 0x08, 0x8d); + i2c_write(gspca_dev, 0x11, 0xaa, 0x00); + retbyte = i2c_read(gspca_dev, 0x11); + if (retbyte != 0) { + /* (should have returned 0xaa) --> Omnivision? */ + /* reg_r 0x10 -> 0x06 --> */ + goto ov_check; + } + + start_2wr_probe(dev, 0x08); /* HDCS2020 */ + i2c_write(gspca_dev, 0x15, 0xaa, 0x00); + retbyte = i2c_read(gspca_dev, 0x15); + if (retbyte != 0) + return 0x08; /* HDCS2020 */ + + start_2wr_probe(dev, 0x0a); /* PB0330 */ + i2c_write(gspca_dev, 0x07, 0xaa, 0xaa); + retbyte = i2c_read(gspca_dev, 0x07); + if (retbyte != 0) + return 0x0a; /* PB0330 */ + retbyte = i2c_read(gspca_dev, 0x03); + if (retbyte != 0) + return 0x0a; /* PB0330 ?? */ + retbyte = i2c_read(gspca_dev, 0x04); + if (retbyte != 0) + return 0x0a; /* PB0330 ?? */ + + start_2wr_probe(dev, 0x0c); /* ICM105A */ + i2c_write(gspca_dev, 0x01, 0x11, 0x00); + retbyte = i2c_read(gspca_dev, 0x01); + if (retbyte != 0) + return 0x0c; /* ICM105A */ + + start_2wr_probe(dev, 0x0e); /* PAS202BCB */ + reg_w(dev, 0x08, 0x8d); + i2c_write(gspca_dev, 0x03, 0xaa, 0x00); + msleep(500); + retbyte = i2c_read(gspca_dev, 0x03); + if (retbyte != 0) + return 0x0e; /* PAS202BCB */ + + start_2wr_probe(dev, 0x02); /* ?? */ + i2c_write(gspca_dev, 0x01, 0xaa, 0x00); + retbyte = i2c_read(gspca_dev, 0x01); + if (retbyte != 0) + return 0x02; /* ?? */ +ov_check: + reg_r(gspca_dev, 0x0010); /* ?? */ + reg_r(gspca_dev, 0x0010); + + reg_w(dev, 0x01, 0x0000); + reg_w(dev, 0x01, 0x0001); + reg_w(dev, 0x06, 0x0010); /* OmniVision */ + reg_w(dev, 0xa1, 0x008b); + reg_w(dev, 0x08, 0x008d); + msleep(500); + reg_w(dev, 0x01, 0x0012); + i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */ + retbyte = i2c_read(gspca_dev, 0x0a); + checkword = retbyte << 8; + retbyte = i2c_read(gspca_dev, 0x0b); + checkword |= retbyte; + PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", checkword); + switch (checkword) { + case 0x7631: /* OV7630C */ + reg_w(dev, 0x06, 0x0010); + break; + case 0x7620: /* OV7620 */ + case 0x7648: /* OV7648 */ + break; + default: + return -1; /* not OmniVision */ + } + return checkword; +} + +struct sensor_by_chipset_revision { + __u16 revision; + __u8 internal_sensor_id; +}; +static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { + {0xc001, 0x13}, /* MI0360 */ + {0xe001, 0x13}, + {0x8001, 0x13}, + {0x8000, 0x14}, /* CS2102K */ + {0x8400, 0x15}, /* TAS5130K */ + {0, 0} +}; + +static int vga_3wr_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int i; + __u8 retbyte; + __u16 checkword; + +/*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/ + reg_w(dev, 0x02, 0x0010); + reg_r(gspca_dev, 0x10); + reg_w(dev, 0x01, 0x0000); + reg_w(dev, 0x00, 0x0010); + reg_w(dev, 0x01, 0x0001); + reg_w(dev, 0x91, 0x008b); + reg_w(dev, 0x03, 0x0012); + reg_w(dev, 0x01, 0x0012); + reg_w(dev, 0x05, 0x0012); + retbyte = i2c_read(gspca_dev, 0x14); + if (retbyte != 0) + return 0x11; /* HV7131R */ + retbyte = i2c_read(gspca_dev, 0x15); + if (retbyte != 0) + return 0x11; /* HV7131R */ + retbyte = i2c_read(gspca_dev, 0x16); + if (retbyte != 0) + return 0x11; /* HV7131R */ + + reg_w(dev, 0x02, 0x0010); + retbyte = reg_r(gspca_dev, 0x000b); + checkword = retbyte << 8; + retbyte = reg_r(gspca_dev, 0x000a); + checkword |= retbyte; + PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", checkword); + reg_r(gspca_dev, 0x0010); + /* this is tested only once anyway */ + i = 0; + while (chipset_revision_sensor[i].revision) { + if (chipset_revision_sensor[i].revision == checkword) { + sd->chip_revision = checkword; + send_unknown(dev, SENSOR_PB0330); + return chipset_revision_sensor[i].internal_sensor_id; + } + i++; + } + + reg_w(dev, 0x01, 0x0000); + reg_w(dev, 0x01, 0x0001); + reg_w(dev, 0xdd, 0x008b); + reg_w(dev, 0x0a, 0x0010); + reg_w(dev, 0x03, 0x0012); + reg_w(dev, 0x01, 0x0012); + retbyte = i2c_read(gspca_dev, 0x00); + if (retbyte != 0) { + PDEBUG(D_PROBE, "probe 3wr vga type 0a ?"); + return 0x0a; /* ?? */ + } + + reg_w(dev, 0x01, 0x0000); + reg_w(dev, 0x01, 0x0001); + reg_w(dev, 0x98, 0x008b); + reg_w(dev, 0x01, 0x0010); + reg_w(dev, 0x03, 0x0012); + msleep(2); + reg_w(dev, 0x01, 0x0012); + retbyte = i2c_read(gspca_dev, 0x00); + if (retbyte != 0) { + PDEBUG(D_PROBE, "probe 3wr vga type %02x", retbyte); + send_unknown(dev, SENSOR_GC0305); + return retbyte; /* 0x29 = gc0305 - should continue? */ + } + + reg_w(dev, 0x01, 0x0000); /* check OmniVision */ + reg_w(dev, 0x01, 0x0001); + reg_w(dev, 0xa1, 0x008b); + reg_w(dev, 0x08, 0x008d); + reg_w(dev, 0x06, 0x0010); + reg_w(dev, 0x01, 0x0012); + reg_w(dev, 0x05, 0x0012); + if (i2c_read(gspca_dev, 0x1c) == 0x7f /* OV7610 - manufacturer ID */ + && i2c_read(gspca_dev, 0x1d) == 0xa2) { + send_unknown(dev, SENSOR_OV7620); + return 0x06; /* OmniVision confirm ? */ + } + + reg_w(dev, 0x01, 0x00); + reg_w(dev, 0x00, 0x02); + reg_w(dev, 0x01, 0x10); + reg_w(dev, 0x01, 0x01); + reg_w(dev, 0xee, 0x8b); + reg_w(dev, 0x03, 0x12); +/* msleep(150); */ + reg_w(dev, 0x01, 0x12); + reg_w(dev, 0x05, 0x12); + retbyte = i2c_read(gspca_dev, 0x00); /* ID 0 */ + checkword = retbyte << 8; + retbyte = i2c_read(gspca_dev, 0x01); /* ID 1 */ + checkword |= retbyte; + PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", checkword); + if (checkword == 0x2030) { + retbyte = i2c_read(gspca_dev, 0x02); /* revision number */ + PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte); + send_unknown(dev, SENSOR_PO2030); + return checkword; + } + + reg_w(dev, 0x01, 0x00); + reg_w(dev, 0x0a, 0x10); + reg_w(dev, 0xd3, 0x8b); + reg_w(dev, 0x01, 0x01); + reg_w(dev, 0x03, 0x12); + reg_w(dev, 0x01, 0x12); + reg_w(dev, 0x05, 0x01); + reg_w(dev, 0xd3, 0x8b); + retbyte = i2c_read(gspca_dev, 0x01); + if (retbyte != 0) { + PDEBUG(D_PROBE, "probe 3wr vga type 0a ?"); + return 0x0a; /* ?? */ + } + return -1; +} + +static int zcxx_probeSensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int sensor, sensor2; + + switch (sd->sensor) { + case SENSOR_MC501CB: + case SENSOR_TAS5130C_VF0250: + return -1; /* don't probe */ + } + sensor = vga_2wr_probe(gspca_dev); + if (sensor >= 0) { + if (sensor < 0x7600) + return sensor; + /* next probe is needed for OmniVision ? */ + } + sensor2 = vga_3wr_probe(gspca_dev); + if (sensor2 >= 0) { + if (sensor >= 0) + return sensor; + return sensor2; + } + return sif_probe(gspca_dev); +} + +/* 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; + int sensor; + int vga = 1; /* 1: vga, 0: sif */ + static const __u8 gamma[SENSOR_MAX] = { + 5, /* SENSOR_CS2102 0 */ + 5, /* SENSOR_CS2102K 1 */ + 4, /* SENSOR_GC0305 2 */ + 4, /* SENSOR_HDCS2020 3 */ + 4, /* SENSOR_HDCS2020b 4 */ + 4, /* SENSOR_HV7131B 5 */ + 4, /* SENSOR_HV7131C 6 */ + 4, /* SENSOR_ICM105A 7 */ + 4, /* SENSOR_MC501CB 8 */ + 3, /* SENSOR_OV7620 9 */ + 4, /* SENSOR_OV7630C 10 */ + 4, /* SENSOR_PAS106 11 */ + 4, /* SENSOR_PB0330 12 */ + 4, /* SENSOR_PO2030 13 */ + 4, /* SENSOR_TAS5130CK 14 */ + 4, /* SENSOR_TAS5130CXX 15 */ + 3, /* SENSOR_TAS5130C_VF0250 16 */ + }; + + /* define some sensors from the vendor/product */ + sd->sharpness = 2; + switch (id->idVendor) { + case 0x041e: /* Creative */ + switch (id->idProduct) { + case 0x4051: /* zc301 chips */ + case 0x4053: + sd->sensor = SENSOR_TAS5130C_VF0250; + break; + } + break; + case 0x046d: /* Logitech Labtec */ + switch (id->idProduct) { + case 0x08dd: + sd->sensor = SENSOR_MC501CB; + break; + } + break; + case 0x0ac8: /* Vimicro z-star */ + switch (id->idProduct) { + case 0x305b: + sd->sensor = SENSOR_TAS5130C_VF0250; + break; + } + break; + } + sensor = zcxx_probeSensor(gspca_dev); + if (sensor >= 0) + PDEBUG(D_PROBE, "probe sensor -> %02x", sensor); + if ((unsigned) force_sensor < SENSOR_MAX) { + sd->sensor = force_sensor; + PDEBUG(D_PROBE, "sensor forced to %d", force_sensor); + } else { + switch (sensor) { + case -1: + switch (sd->sensor) { + case SENSOR_MC501CB: + PDEBUG(D_PROBE, "Sensor MC501CB"); + break; + case SENSOR_TAS5130C_VF0250: + PDEBUG(D_PROBE, "Sensor Tas5130 (VF0250)"); + break; + default: + PDEBUG(D_PROBE, + "Sensor UNKNOW_0 force Tas5130"); + sd->sensor = SENSOR_TAS5130CXX; + } + break; + case 0: + PDEBUG(D_PROBE, "Find Sensor HV7131B"); + sd->sensor = SENSOR_HV7131B; + break; + case 0x04: + PDEBUG(D_PROBE, "Find Sensor CS2102"); + sd->sensor = SENSOR_CS2102; + break; + case 0x08: + PDEBUG(D_PROBE, "Find Sensor HDCS2020(b)"); + sd->sensor = SENSOR_HDCS2020b; + break; + case 0x0a: + PDEBUG(D_PROBE, + "Find Sensor PB0330. Chip revision %x", + sd->chip_revision); + sd->sensor = SENSOR_PB0330; + break; + case 0x0c: + PDEBUG(D_PROBE, "Find Sensor ICM105A"); + sd->sensor = SENSOR_ICM105A; + break; + case 0x0e: + PDEBUG(D_PROBE, "Find Sensor HDCS2020"); + sd->sensor = SENSOR_HDCS2020; + sd->sharpness = 1; + break; + case 0x0f: + PDEBUG(D_PROBE, "Find Sensor PAS106"); + sd->sensor = SENSOR_PAS106; + vga = 0; /* SIF */ + break; + case 0x10: + case 0x12: + PDEBUG(D_PROBE, "Find Sensor TAS5130"); + sd->sensor = SENSOR_TAS5130CXX; + break; + case 0x11: + PDEBUG(D_PROBE, "Find Sensor HV7131R(c)"); + sd->sensor = SENSOR_HV7131C; + break; + case 0x13: + PDEBUG(D_PROBE, + "Find Sensor MI0360. Chip revision %x", + sd->chip_revision); + sd->sensor = SENSOR_PB0330; + break; + case 0x14: + PDEBUG(D_PROBE, + "Find Sensor CS2102K?. Chip revision %x", + sd->chip_revision); + sd->sensor = SENSOR_CS2102K; + break; + case 0x15: + PDEBUG(D_PROBE, + "Find Sensor TAS5130CK?. Chip revision %x", + sd->chip_revision); + sd->sensor = SENSOR_TAS5130CK; + break; + case 0x29: + PDEBUG(D_PROBE, "Find Sensor GC0305"); + sd->sensor = SENSOR_GC0305; + break; + case 0x2030: + PDEBUG(D_PROBE, "Find Sensor PO2030"); + sd->sensor = SENSOR_PO2030; + sd->sharpness = 0; /* from win traces */ + break; + case 0x7620: + PDEBUG(D_PROBE, "Find Sensor OV7620"); + sd->sensor = SENSOR_OV7620; + break; + case 0x7648: + PDEBUG(D_PROBE, "Find Sensor OV7648"); + sd->sensor = SENSOR_OV7620; /* same sensor (?) */ + break; + default: + PDEBUG(D_ERR|D_PROBE, "Unknown sensor %02x", sensor); + return -EINVAL; + } + } + if (sensor < 0x20) { + if (sensor == -1 || sensor == 0x10 || sensor == 0x12) + reg_w(gspca_dev->dev, 0x02, 0x0010); + else + reg_w(gspca_dev->dev, sensor & 0x0f, 0x0010); + reg_r(gspca_dev, 0x0010); + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; +/*fixme:test*/ + gspca_dev->nbalt--; + if (vga) { + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + } else { + cam->cam_mode = sif_mode; + cam->nmodes = ARRAY_SIZE(sif_mode); + } + sd->qindex = 1; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->gamma = gamma[(int) sd->sensor]; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; + sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value; + + /* switch the led off */ + reg_w(gspca_dev->dev, 0x01, 0x0000); + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev->dev, 0x01, 0x0000); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + const struct usb_action *zc3_init; + int mode; + static const struct usb_action *init_tb[SENSOR_MAX][2] = { + {cs2102_InitialScale, cs2102_Initial}, /* 0 */ + {cs2102K_InitialScale, cs2102K_Initial}, /* 1 */ + {gc0305_Initial, gc0305_InitialScale}, /* 2 */ + {hdcs2020xx_InitialScale, hdcs2020xx_Initial}, /* 3 */ + {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 4 */ + {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 5 */ + {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */ + {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */ + {MC501CB_InitialScale, MC501CB_Initial}, /* 9 */ + {OV7620_mode0, OV7620_mode1}, /* 9 */ + {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */ + {pas106b_InitialScale, pas106b_Initial}, /* 11 */ + {pb0330xx_InitialScale, pb0330xx_Initial}, /* 12 */ +/* or {pb03303x_InitialScale, pb03303x_Initial}, */ + {PO2030_mode0, PO2030_mode1}, /* 13 */ + {tas5130CK_InitialScale, tas5130CK_Initial}, /* 14 */ + {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 15 */ + {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial}, + /* 16 */ + }; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + zc3_init = init_tb[(int) sd->sensor][mode]; + switch (sd->sensor) { + case SENSOR_HV7131B: + case SENSOR_HV7131C: + zcxx_probeSensor(gspca_dev); + break; + case SENSOR_PAS106: + usb_exchange(gspca_dev, pas106b_Initial_com); + break; + case SENSOR_PB0330: + if (mode) { + if (sd->chip_revision == 0xc001 + || sd->chip_revision == 0xe001 + || sd->chip_revision == 0x8001) + zc3_init = pb03303x_Initial; + } else { + if (sd->chip_revision == 0xc001 + || sd->chip_revision == 0xe001 + || sd->chip_revision == 0x8001) + zc3_init = pb03303x_InitialScale; + } + break; + } + usb_exchange(gspca_dev, zc3_init); + + switch (sd->sensor) { + case SENSOR_GC0305: + case SENSOR_OV7620: + case SENSOR_PO2030: + msleep(100); /* ?? */ + reg_r(gspca_dev, 0x0002); /* --> 0x40 */ + reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ + reg_w(dev, 0x15, 0x01ae); + reg_w(dev, 0x0d, 0x003a); + reg_w(dev, 0x02, 0x003b); + reg_w(dev, 0x00, 0x0038); + break; + } + + setmatrix(gspca_dev); + setbrightness(gspca_dev); + switch (sd->sensor) { + case SENSOR_OV7620: + reg_r(gspca_dev, 0x0008); + reg_w(dev, 0x00, 0x0008); + break; + case SENSOR_GC0305: + reg_r(gspca_dev, 0x0008); + /* fall thru */ + case SENSOR_PO2030: + reg_w(dev, 0x03, 0x0008); + break; + } + setsharpness(gspca_dev); + + /* set the gamma tables when not set */ + switch (sd->sensor) { + case SENSOR_CS2102: /* gamma set in xxx_Initial */ + case SENSOR_CS2102K: + case SENSOR_HDCS2020: + case SENSOR_HDCS2020b: + case SENSOR_PB0330: /* pb with chip_revision - see above */ + case SENSOR_OV7630C: + case SENSOR_TAS5130CK: + break; + default: + setcontrast(gspca_dev); + break; + } + setmatrix(gspca_dev); /* one more time? */ + switch (sd->sensor) { + case SENSOR_OV7620: + reg_r(gspca_dev, 0x0180); /* from win */ + reg_w(dev, 0x00, 0x0180); + break; + default: + setquality(gspca_dev); + break; + } + setlightfreq(gspca_dev); + + switch (sd->sensor) { + case SENSOR_GC0305: + case SENSOR_OV7620: + reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ + reg_w(dev, 0x15, 0x01ae); + sd->autogain = 0; + break; + case SENSOR_PO2030: + reg_w(dev, 0x40, 0x0117); /* (from win traces) */ + reg_r(gspca_dev, 0x0180); + break; + } + + setautogain(gspca_dev); + switch (sd->sensor) { + case SENSOR_GC0305: +/* setlightfreq(gspca_dev); ?? (end: 80 -> [18d]) */ + reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ + reg_w(dev, 0x15, 0x01ae); + reg_w(dev, 0x40, 0x0180); + reg_w(dev, 0x40, 0x0117); + reg_r(gspca_dev, 0x0180); + sd->autogain = 1; + setautogain(gspca_dev); + break; + case SENSOR_OV7620: + i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */ + i2c_write(gspca_dev, 0x13, 0xa3, 0x00); + /*fixme: returned value to send? */ + reg_w(dev, 0x40, 0x0117); /* (from win traces) */ + reg_r(gspca_dev, 0x0180); + setautogain(gspca_dev); + msleep(500); + break; + case SENSOR_PO2030: + msleep(500); + reg_r(gspca_dev, 0x0008); + reg_r(gspca_dev, 0x0007); + reg_w(dev, 0x00, 0x0007); /* (from win traces) */ + reg_w(dev, 0x02, 0x0008); + break; + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + send_unknown(gspca_dev->dev, sd->sensor); +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, + __u8 *data, + int len) +{ + + if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + /* put the JPEG header in the new frame */ + jpeg_put_header(gspca_dev, frame, + ((struct sd *) gspca_dev)->qindex, + 0x21); + /* remove the webcam's header: + * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp + * - 'ss ss' is the frame sequence number (BE) + * - 'ww ww' and 'hh hh' are the window dimensions (BE) + * - 'pp pp' is the packet sequence number (BE) + */ + data += 18; + len -= 18; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +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_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (gspca_dev->streaming) + setautogain(gspca_dev); + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gamma; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setlightfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) + setsharpness(gspca_dev); + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = sizeof sd_ctrls / sizeof sd_ctrls[0], + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x041e), DVNM("Creative WebCam Live!")}, +#ifndef CONFIG_USB_ZC0301 + {USB_DEVICE(0x041e, 0x4017), DVNM("Creative Webcam Mobile PD1090")}, + {USB_DEVICE(0x041e, 0x401c), DVNM("Creative NX")}, + {USB_DEVICE(0x041e, 0x401e), DVNM("Creative Nx Pro")}, + {USB_DEVICE(0x041e, 0x401f), DVNM("Creative Webcam Notebook PD1171")}, +#endif + {USB_DEVICE(0x041e, 0x4029), DVNM("Creative WebCam Vista Pro")}, +#ifndef CONFIG_USB_ZC0301 + {USB_DEVICE(0x041e, 0x4034), DVNM("Creative Instant P0620")}, + {USB_DEVICE(0x041e, 0x4035), DVNM("Creative Instant P0620D")}, + {USB_DEVICE(0x041e, 0x4036), DVNM("Creative Live !")}, + {USB_DEVICE(0x041e, 0x403a), DVNM("Creative Nx Pro 2")}, +#endif + {USB_DEVICE(0x041e, 0x4051), DVNM("Creative Notebook Pro (VF0250)")}, + {USB_DEVICE(0x041e, 0x4053), DVNM("Creative Live!Cam Video IM")}, +#ifndef CONFIG_USB_ZC0301 + {USB_DEVICE(0x0458, 0x7007), DVNM("Genius VideoCam V2")}, + {USB_DEVICE(0x0458, 0x700c), DVNM("Genius VideoCam V3")}, + {USB_DEVICE(0x0458, 0x700f), DVNM("Genius VideoCam Web V2")}, +#endif + {USB_DEVICE(0x0461, 0x0a00), DVNM("MicroInnovation WebCam320")}, + {USB_DEVICE(0x046d, 0x08a0), DVNM("Logitech QC IM")}, + {USB_DEVICE(0x046d, 0x08a1), DVNM("Logitech QC IM 0x08A1 +sound")}, + {USB_DEVICE(0x046d, 0x08a2), DVNM("Labtec Webcam Pro")}, + {USB_DEVICE(0x046d, 0x08a3), DVNM("Logitech QC Chat")}, + {USB_DEVICE(0x046d, 0x08a6), DVNM("Logitech QCim")}, + {USB_DEVICE(0x046d, 0x08a7), DVNM("Logitech QuickCam Image")}, + {USB_DEVICE(0x046d, 0x08a9), DVNM("Logitech Notebook Deluxe")}, + {USB_DEVICE(0x046d, 0x08aa), DVNM("Labtec Webcam Notebook")}, + {USB_DEVICE(0x046d, 0x08ac), DVNM("Logitech QuickCam Cool")}, + {USB_DEVICE(0x046d, 0x08ad), DVNM("Logitech QCCommunicate STX")}, +#ifndef CONFIG_USB_ZC0301 + {USB_DEVICE(0x046d, 0x08ae), DVNM("Logitech QuickCam for Notebooks")}, +#endif + {USB_DEVICE(0x046d, 0x08af), DVNM("Logitech QuickCam Cool")}, + {USB_DEVICE(0x046d, 0x08b9), DVNM("Logitech QC IM ???")}, + {USB_DEVICE(0x046d, 0x08d7), DVNM("Logitech QCam STX")}, + {USB_DEVICE(0x046d, 0x08d9), DVNM("Logitech QuickCam IM/Connect")}, + {USB_DEVICE(0x046d, 0x08d8), DVNM("Logitech Notebook Deluxe")}, + {USB_DEVICE(0x046d, 0x08da), DVNM("Logitech QuickCam Messenger")}, + {USB_DEVICE(0x046d, 0x08dd), DVNM("Logitech QuickCam for Notebooks")}, + {USB_DEVICE(0x0471, 0x0325), DVNM("Philips SPC 200 NC")}, + {USB_DEVICE(0x0471, 0x0326), DVNM("Philips SPC 300 NC")}, + {USB_DEVICE(0x0471, 0x032d), DVNM("Philips spc210nc")}, + {USB_DEVICE(0x0471, 0x032e), DVNM("Philips spc315nc")}, + {USB_DEVICE(0x055f, 0xc005), DVNM("Mustek Wcam300A")}, +#ifndef CONFIG_USB_ZC0301 + {USB_DEVICE(0x055f, 0xd003), DVNM("Mustek WCam300A")}, + {USB_DEVICE(0x055f, 0xd004), DVNM("Mustek WCam300 AN")}, +#endif + {USB_DEVICE(0x0698, 0x2003), DVNM("CTX M730V built in")}, + {USB_DEVICE(0x0ac8, 0x0302), DVNM("Z-star Vimicro zc0302")}, +#ifndef CONFIG_USB_ZC0301 + {USB_DEVICE(0x0ac8, 0x301b), DVNM("Z-Star zc301b")}, + {USB_DEVICE(0x0ac8, 0x303b), DVNM("Vimicro 0x303b")}, +#endif + {USB_DEVICE(0x0ac8, 0x305b), DVNM("Z-star Vimicro zc0305b")}, +#ifndef CONFIG_USB_ZC0301 + {USB_DEVICE(0x0ac8, 0x307b), DVNM("Z-Star 307b")}, + {USB_DEVICE(0x10fd, 0x0128), DVNM("Typhoon Webshot II 300k 0x0128")}, + {USB_DEVICE(0x10fd, 0x8050), DVNM("Typhoon Webshot II USB 300k")}, +#endif + {} /* end of entry */ +}; +#undef DVNAME +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +/* USB driver */ +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); + +module_param(force_sensor, int, 0644); +MODULE_PARM_DESC(force_sensor, + "Force sensor. Only for experts!!!"); diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 7b65f5e537f..a30254bed31 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -194,88 +194,6 @@ static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -/* Common (grey or coloured) pinnacle PCTV remote handling - * - */ -static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, - int parity_offset, int marker, int code_modulo) -{ - unsigned char b[4]; - unsigned int start = 0,parity = 0,code = 0; - - /* poll IR chip */ - if (4 != i2c_master_recv(&ir->c,b,4)) { - dprintk(2,"read error\n"); - return -EIO; - } - - for (start = 0; start < ARRAY_SIZE(b); start++) { - if (b[start] == marker) { - code=b[(start+parity_offset+1)%4]; - parity=b[(start+parity_offset)%4]; - } - } - - /* Empty Request */ - if (parity==0) - return 0; - - /* Repeating... */ - if (ir->old == parity) - return 0; - - ir->old = parity; - - /* drop special codes when a key is held down a long time for the grey controller - In this case, the second bit of the code is asserted */ - if (marker == 0xfe && (code & 0x40)) - return 0; - - code %= code_modulo; - - *ir_raw = code; - *ir_key = code; - - dprintk(1,"Pinnacle PCTV key %02x\n", code); - - return 1; -} - -/* The grey pinnacle PCTV remote - * - * There are one issue with this remote: - * - I2c packet does not change when the same key is pressed quickly. The workaround - * is to hold down each key for about half a second, so that another code is generated - * in the i2c packet, and the function can distinguish key presses. - * - * Sylvain Pasche <sylvain.pasche@gmail.com> - */ -int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - - return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff); -} - -EXPORT_SYMBOL_GPL(get_key_pinnacle_grey); - - -/* The new pinnacle PCTV remote (with the colored buttons) - * - * Ricardo Cerqueira <v4l@cerqueira.org> - */ -int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) -{ - /* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE - * - * this is the only value that results in 42 unique - * codes < 128 - */ - - return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88); -} - -EXPORT_SYMBOL_GPL(get_key_pinnacle_color); - /* ----------------------------------------------------------------------- */ static void ir_key_poll(struct IR_i2c *ir) diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c index 4fb8faefe2c..4e05f91a910 100644 --- a/drivers/media/video/ivtv/ivtv-cards.c +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -40,7 +40,7 @@ #define MSP_MONO MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) -#define V4L2_STD_NOT_MN (V4L2_STD_PAL|V4L2_STD_SECAM) +#define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM) /* usual i2c tuner addresses to probe */ static struct ivtv_card_tuner_i2c ivtv_i2c_std = { @@ -300,7 +300,7 @@ static const struct ivtv_card ivtv_card_mpg600 = { .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, .tuners = { /* The PAL tuner is confirmed */ - { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_mpg600, @@ -341,7 +341,7 @@ static const struct ivtv_card ivtv_card_mpg160 = { .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 }, .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, .tuners = { - { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_mpg160, @@ -377,7 +377,7 @@ static const struct ivtv_card ivtv_card_pg600 = { { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, }, .tuners = { - { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_pg600, @@ -418,7 +418,7 @@ static const struct ivtv_card ivtv_card_avc2410 = { on the country/region setting of the user to decide which tuner is available. */ .tuners = { - { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, { .std = V4L2_STD_ALL - V4L2_STD_NTSC_M_JP, .tuner = TUNER_PHILIPS_FM1236_MK3 }, { .std = V4L2_STD_NTSC_M_JP, .tuner = TUNER_PHILIPS_FQ1286 }, @@ -492,7 +492,7 @@ static const struct ivtv_card ivtv_card_tg5000tv = { .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, .composite = 0x0010, .svideo = 0x0020 }, .tuners = { - { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, + { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_tg5000tv, .i2c = &ivtv_i2c_std, @@ -523,7 +523,7 @@ static const struct ivtv_card ivtv_card_va2000 = { { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, }, .tuners = { - { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, + { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_va2000, .i2c = &ivtv_i2c_std, @@ -567,7 +567,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc = { .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, .f44100 = 0x4000, .f48000 = 0x8000 }, .tuners = { - { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, }, .pci_list = ivtv_pci_cx23416gyc, @@ -599,7 +599,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc_nogr = { .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, .f44100 = 0x4000, .f48000 = 0x8000 }, .tuners = { - { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, }, .i2c = &ivtv_i2c_std, @@ -629,7 +629,7 @@ static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = { .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, .f44100 = 0x4000, .f48000 = 0x8000 }, .tuners = { - { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, }, .i2c = &ivtv_i2c_std, @@ -669,7 +669,7 @@ static const struct ivtv_card ivtv_card_gv_mvprx = { .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, .tuners = { /* This card has the Panasonic VP27 tuner */ - { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 }, + { .std = V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 }, }, .pci_list = ivtv_pci_gv_mvprx, .i2c = &ivtv_i2c_std, @@ -706,7 +706,7 @@ static const struct ivtv_card ivtv_card_gv_mvprx2e = { .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, .tuners = { /* This card has the Panasonic VP27 tuner */ - { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 }, + { .std = V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 }, }, .pci_list = ivtv_pci_gv_mvprx2e, .i2c = &ivtv_i2c_std, @@ -741,7 +741,7 @@ static const struct ivtv_card ivtv_card_gotview_pci_dvd = { .gpio_init = { .direction = 0xf000, .initial_value = 0xA000 }, .tuners = { /* This card has a Philips FQ1216ME MK3 tuner */ - { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, }, .pci_list = ivtv_pci_gotview_pci_dvd, .i2c = &ivtv_i2c_std, @@ -780,7 +780,7 @@ static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = { .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 }, .tuners = { /* This card has a Philips FQ1216ME MK5 tuner */ - { .std = V4L2_STD_NOT_MN, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, }, .pci_list = ivtv_pci_gotview_pci_dvd2, .i2c = &ivtv_i2c_std, @@ -858,7 +858,7 @@ static const struct ivtv_card ivtv_card_dctmvtvp1 = { .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, .composite = 0x0010, .svideo = 0x0020}, .tuners = { - { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, + { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_dctmvtvp1, .i2c = &ivtv_i2c_std, @@ -923,7 +923,6 @@ static const struct ivtv_card ivtv_card_club3d = { { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, }, .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, - .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */ .xceive_pin = 12, .tuners = { { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, @@ -959,7 +958,7 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = { { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, }, /* enable line-in */ - .gpio_init = { .direction = 0xe400, .initial_value = 0x4400 }, + .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, .xceive_pin = 10, .tuners = { { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, @@ -1001,7 +1000,7 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = { .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 }, .tuners = { /* This card has a Partsnic PTI-5NF05 tuner */ - { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_TCL_2002N }, + { .std = V4L2_STD_MN, .tuner = TUNER_TCL_2002N }, }, .pci_list = ivtv_pci_aver_pvr150, .i2c = &ivtv_i2c_radio, @@ -1069,7 +1068,7 @@ static const struct ivtv_card ivtv_card_asus_falcon2 = { }, .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, M52790_IN_TUNER }, .tuners = { - { .std = V4L2_STD_525_60|V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 }, }, .pci_list = ivtv_pci_asus_falcon2, .i2c = &ivtv_i2c_std, @@ -1102,7 +1101,7 @@ static const struct ivtv_card ivtv_card_aver_m104 = { }, .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, /* enable line-in + reset tuner */ - .gpio_init = { .direction = 0xe400, .initial_value = 0x4000 }, + .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, .xceive_pin = 10, .tuners = { { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, @@ -1111,6 +1110,41 @@ static const struct ivtv_card ivtv_card_aver_m104 = { .i2c = &ivtv_i2c_std, }; +/* ------------------------------------------------------------------------- */ + +/* Buffalo PC-MV5L/PCI cards */ + +static const struct ivtv_card_pci_info ivtv_pci_buffalo[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x052b }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_buffalo = { + .type = IVTV_CARD_BUFFALO_MV5L, + .name = "Buffalo PC-MV5L/PCI", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, + }, + .xceive_pin = 12, + .tuners = { + { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, + }, + .pci_list = ivtv_pci_buffalo, + .i2c = &ivtv_i2c_std, +}; + static const struct ivtv_card *ivtv_card_list[] = { &ivtv_card_pvr250, &ivtv_card_pvr350, @@ -1137,6 +1171,7 @@ static const struct ivtv_card *ivtv_card_list[] = { &ivtv_card_aver_pvr150, &ivtv_card_aver_ezmaker, &ivtv_card_aver_m104, + &ivtv_card_buffalo, /* Variations of standard cards but with the same PCI IDs. These cards must come last in this list. */ diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h index 748485dcebb..381af1bceef 100644 --- a/drivers/media/video/ivtv/ivtv-cards.h +++ b/drivers/media/video/ivtv/ivtv-cards.h @@ -49,7 +49,8 @@ #define IVTV_CARD_AVER_PVR150PLUS 22 /* AVerMedia PVR-150 Plus */ #define IVTV_CARD_AVER_EZMAKER 23 /* AVerMedia EZMaker PCI Deluxe */ #define IVTV_CARD_AVER_M104 24 /* AverMedia M104 miniPCI card */ -#define IVTV_CARD_LAST 24 +#define IVTV_CARD_BUFFALO_MV5L 25 /* Buffalo PC-MV5L/PCI card */ +#define IVTV_CARD_LAST 25 /* Variants of existing cards but with the same PCI IDs. The driver detects these based on other device information. diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c index c7e449f6397..48e103be718 100644 --- a/drivers/media/video/ivtv/ivtv-controls.c +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -47,12 +47,12 @@ static const u32 *ctrl_classes[] = { NULL }; -static int ivtv_queryctrl(struct ivtv *itv, struct v4l2_queryctrl *qctrl) + +int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) { + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; const char *name; - IVTV_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id); - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); if (qctrl->id == 0) return -EINVAL; @@ -87,21 +87,35 @@ static int ivtv_queryctrl(struct ivtv *itv, struct v4l2_queryctrl *qctrl) return 0; } -static int ivtv_querymenu(struct ivtv *itv, struct v4l2_querymenu *qmenu) +int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu) { + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; struct v4l2_queryctrl qctrl; qctrl.id = qmenu->id; - ivtv_queryctrl(itv, &qctrl); - return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id)); + ivtv_queryctrl(file, fh, &qctrl); + return v4l2_ctrl_query_menu(qmenu, &qctrl, + cx2341x_ctrl_get_menu(&itv->params, qmenu->id)); } -static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) +static int ivtv_try_ctrl(struct file *file, void *fh, + struct v4l2_ext_control *vctrl) { - s32 v = vctrl->value; - - IVTV_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v); + struct v4l2_queryctrl qctrl; + const char **menu_items = NULL; + int err; + + qctrl.id = vctrl->id; + err = ivtv_queryctrl(file, fh, &qctrl); + if (err) + return err; + if (qctrl.type == V4L2_CTRL_TYPE_MENU) + menu_items = v4l2_ctrl_get_menu(qctrl.id); + return v4l2_ctrl_check(vctrl, &qctrl, menu_items); +} +static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) +{ switch (vctrl->id) { /* Standard V4L2 controls */ case V4L2_CID_BRIGHTNESS: @@ -119,7 +133,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl); default: - IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); return -EINVAL; } return 0; @@ -127,8 +141,6 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) { - IVTV_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id); - switch (vctrl->id) { /* Standard V4L2 controls */ case V4L2_CID_BRIGHTNESS: @@ -145,7 +157,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_AUDIO_LOUDNESS: return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl); default: - IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); return -EINVAL; } return 0; @@ -191,119 +203,106 @@ static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fm return 0; } -int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg) +int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) { + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; struct v4l2_control ctrl; - switch (cmd) { - case VIDIOC_QUERYMENU: - IVTV_DEBUG_IOCTL("VIDIOC_QUERYMENU\n"); - return ivtv_querymenu(itv, arg); - - case VIDIOC_QUERYCTRL: - return ivtv_queryctrl(itv, arg); - - case VIDIOC_S_CTRL: - return ivtv_s_ctrl(itv, arg); - - case VIDIOC_G_CTRL: - return ivtv_g_ctrl(itv, arg); - - case VIDIOC_S_EXT_CTRLS: - { - struct v4l2_ext_controls *c = arg; - - if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { - int i; - int err = 0; - - for (i = 0; i < c->count; i++) { - ctrl.id = c->controls[i].id; - ctrl.value = c->controls[i].value; - err = ivtv_s_ctrl(itv, &ctrl); - c->controls[i].value = ctrl.value; - if (err) { - c->error_idx = i; - break; - } + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = ivtv_g_ctrl(itv, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; } - return err; } - IVTV_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n"); - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { - static u32 freqs[3] = { 44100, 48000, 32000 }; - struct cx2341x_mpeg_params p = itv->params; - int err = cx2341x_ext_ctrls(&p, atomic_read(&itv->capturing), arg, cmd); - unsigned idx; - - if (err) - return err; - - if (p.video_encoding != itv->params.video_encoding) { - int is_mpeg1 = p.video_encoding == - V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - struct v4l2_format fmt; - - /* fix videodecoder resolution */ - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1); - fmt.fmt.pix.height = itv->params.height; - itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt); - } - err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p); - if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) { - err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt); + return err; + } + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&itv->params, 0, c, VIDIOC_G_EXT_CTRLS); + return -EINVAL; +} + +int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct v4l2_control ctrl; + + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = ivtv_s_ctrl(itv, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; } - itv->params = p; - itv->dualwatch_stereo_mode = p.audio_properties & 0x0300; - idx = p.audio_properties & 0x03; - /* The audio clock of the digitizer must match the codec sample - rate otherwise you get some very strange effects. */ - if (idx < sizeof(freqs)) - ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[idx]); - return err; } - return -EINVAL; + return err; } + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + static u32 freqs[3] = { 44100, 48000, 32000 }; + struct cx2341x_mpeg_params p = itv->params; + int err = cx2341x_ext_ctrls(&p, atomic_read(&itv->capturing), c, VIDIOC_S_EXT_CTRLS); + unsigned idx; - case VIDIOC_G_EXT_CTRLS: - { - struct v4l2_ext_controls *c = arg; - - if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { - int i; - int err = 0; - - for (i = 0; i < c->count; i++) { - ctrl.id = c->controls[i].id; - ctrl.value = c->controls[i].value; - err = ivtv_g_ctrl(itv, &ctrl); - c->controls[i].value = ctrl.value; - if (err) { - c->error_idx = i; - break; - } - } + if (err) return err; + + if (p.video_encoding != itv->params.video_encoding) { + int is_mpeg1 = p.video_encoding == + V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + struct v4l2_format fmt; + + /* fix videodecoder resolution */ + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1); + fmt.fmt.pix.height = itv->params.height; + itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt); } - IVTV_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n"); - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) - return cx2341x_ext_ctrls(&itv->params, 0, arg, cmd); - return -EINVAL; + err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p); + if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) + err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt); + itv->params = p; + itv->dualwatch_stereo_mode = p.audio_properties & 0x0300; + idx = p.audio_properties & 0x03; + /* The audio clock of the digitizer must match the codec sample + rate otherwise you get some very strange effects. */ + if (idx < sizeof(freqs)) + ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[idx]); + return err; } + return -EINVAL; +} - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *c = arg; +int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - IVTV_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n"); - if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) - return cx2341x_ext_ctrls(&itv->params, atomic_read(&itv->capturing), arg, cmd); - return -EINVAL; - } + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; - default: - return -EINVAL; + for (i = 0; i < c->count; i++) { + err = ivtv_try_ctrl(file, fh, &c->controls[i]); + if (err) { + c->error_idx = i; + break; + } + } + return err; } - return 0; + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&itv->params, atomic_read(&itv->capturing), c, VIDIOC_TRY_EXT_CTRLS); + return -EINVAL; } diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h index bb8a6a5ed2b..1c7721e23c9 100644 --- a/drivers/media/video/ivtv/ivtv-controls.h +++ b/drivers/media/video/ivtv/ivtv-controls.h @@ -21,6 +21,10 @@ #ifndef IVTV_CONTROLS_H #define IVTV_CONTROLS_H -int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a); +int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a); +int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a); +int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a); +int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a); #endif diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 797e636771d..41fd79279bb 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -191,6 +191,7 @@ MODULE_PARM_DESC(cardtype, "\t\t\t23 = AverMedia PVR-150 Plus\n" "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n" "\t\t\t25 = AverMedia M104 (not yet working)\n" + "\t\t\t26 = Buffalo PC-MV5L/PCI\n" "\t\t\t 0 = Autodetect (default)\n" "\t\t\t-1 = Ignore this card\n\t\t"); MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60"); @@ -1019,7 +1020,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, ivtv_cards[ivtv_cards_active] = itv; itv->dev = dev; itv->num = ivtv_cards_active++; - snprintf(itv->name, sizeof(itv->name) - 1, "ivtv%d", itv->num); + snprintf(itv->name, sizeof(itv->name), "ivtv%d", itv->num); IVTV_INFO("Initializing card #%d\n", itv->num); spin_unlock(&ivtv_cards_lock); @@ -1127,6 +1128,12 @@ static int __devinit ivtv_probe(struct pci_dev *dev, /* if no tuner was found, then pick the first tuner in the card list */ if (itv->options.tuner == -1 && itv->card->tuners[0].std) { itv->std = itv->card->tuners[0].std; + if (itv->std & V4L2_STD_PAL) + itv->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H; + else if (itv->std & V4L2_STD_NTSC) + itv->std = V4L2_STD_NTSC_M; + else if (itv->std & V4L2_STD_SECAM) + itv->std = V4L2_STD_SECAM_L; itv->options.tuner = itv->card->tuners[0].tuner; } if (itv->options.radio == -1) @@ -1261,9 +1268,13 @@ err: int ivtv_init_on_first_open(struct ivtv *itv) { struct v4l2_frequency vf; + /* Needed to call ioctls later */ + struct ivtv_open_id fh; int fw_retry_count = 3; int video_input; + fh.itv = itv; + if (test_bit(IVTV_F_I_FAILED, &itv->i_flags)) return -ENXIO; @@ -1311,18 +1322,18 @@ int ivtv_init_on_first_open(struct ivtv *itv) video_input = itv->active_input; itv->active_input++; /* Force update of input */ - ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_INPUT, &video_input); + ivtv_s_input(NULL, &fh, video_input); /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code in one place. */ itv->std++; /* Force full standard initialization */ itv->std_out = itv->std; - ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_FREQUENCY, &vf); + ivtv_s_frequency(NULL, &fh, &vf); if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) { ivtv_init_mpeg_decoder(itv); } - ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_STD, &itv->tuner_std); + ivtv_s_std(NULL, &fh, &itv->tuner_std); /* On a cx23416 this seems to be able to enable DMA to the chip? */ if (!itv->has_cx23415) diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 9d23b1efd36..a08bb3331cf 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -635,7 +635,6 @@ struct ivtv { spinlock_t lock; /* lock access to this struct */ struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ - /* Streams */ int stream_buf_size[IVTV_MAX_STREAMS]; /* stream buffer size */ struct ivtv_stream streams[IVTV_MAX_STREAMS]; /* stream data */ diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index db813e071ce..7ec5c99f9ad 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -582,6 +582,19 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c ivtv_queue_init(&q); set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + /* Start decoder (returns 0 if already started) */ + mutex_lock(&itv->serialize_lock); + rc = ivtv_start_decoding(id, itv->speed); + mutex_unlock(&itv->serialize_lock); + if (rc) { + IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name); + + /* failure, clean up */ + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return rc; + } + retry: /* If possible, just DMA the entire frame - Check the data transfer size since we may get here before the stream has been fully set-up */ @@ -664,18 +677,6 @@ retry: ivtv_enqueue(s, buf, &s->q_full); } - /* Start decoder (returns 0 if already started) */ - mutex_lock(&itv->serialize_lock); - rc = ivtv_start_decoding(id, itv->speed); - mutex_unlock(&itv->serialize_lock); - if (rc) { - IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name); - - /* failure, clean up */ - clear_bit(IVTV_F_S_STREAMING, &s->s_flags); - clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); - return rc; - } if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) { if (s->q_full.length >= itv->dma_data_req_size) { int got_sig; diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c index d8ac09f3cce..bc22905ea20 100644 --- a/drivers/media/video/ivtv/ivtv-gpio.c +++ b/drivers/media/video/ivtv/ivtv-gpio.c @@ -146,15 +146,20 @@ int ivtv_reset_tuner_gpio(void *dev, int cmd, int value) void ivtv_gpio_init(struct ivtv *itv) { - if (itv->card->gpio_init.direction == 0) + u16 pin = 0; + + if (itv->card->xceive_pin) + pin = 1 << itv->card->xceive_pin; + + if ((itv->card->gpio_init.direction | pin) == 0) return; IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); /* init output data then direction */ - write_reg(itv->card->gpio_init.initial_value, IVTV_REG_GPIO_OUT); - write_reg(itv->card->gpio_init.direction, IVTV_REG_GPIO_DIR); + write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT); + write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR); } static struct v4l2_queryctrl gpio_ctrl_mute = { diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index 32129f3ea83..af154238fb9 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -75,10 +75,6 @@ #define IVTV_REG_I2C_GETSCL_OFFSET 0x7008 #define IVTV_REG_I2C_GETSDA_OFFSET 0x700c -#ifndef I2C_ADAP_CLASS_TV_ANALOG -#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG -#endif /* I2C_ADAP_CLASS_TV_ANALOG */ - #define IVTV_CS53L32A_I2C_ADDR 0x11 #define IVTV_M52790_I2C_ADDR 0x48 #define IVTV_CX25840_I2C_ADDR 0x44 @@ -139,7 +135,7 @@ static const u8 hw_addrs[] = { static const char * const hw_devicenames[] = { "cx25840", "saa7115", - "saa7127", + "saa7127_auto", /* saa7127 or saa7129 */ "msp3400", "tuner", "wm8775", diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 26cc0f6699f..52e00a7f311 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -128,37 +128,6 @@ u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt) return set; } -static const struct { - v4l2_std_id std; - char *name; -} enum_stds[] = { - { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" }, - { V4L2_STD_PAL_DK, "PAL-DK" }, - { V4L2_STD_PAL_I, "PAL-I" }, - { V4L2_STD_PAL_M, "PAL-M" }, - { V4L2_STD_PAL_N, "PAL-N" }, - { V4L2_STD_PAL_Nc, "PAL-Nc" }, - { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" }, - { V4L2_STD_SECAM_DK, "SECAM-DK" }, - { V4L2_STD_SECAM_L, "SECAM-L" }, - { V4L2_STD_SECAM_LC, "SECAM-L'" }, - { V4L2_STD_NTSC_M, "NTSC-M" }, - { V4L2_STD_NTSC_M_JP, "NTSC-J" }, - { V4L2_STD_NTSC_M_KR, "NTSC-K" }, -}; - -static const struct v4l2_standard ivtv_std_60hz = -{ - .frameperiod = {.numerator = 1001, .denominator = 30000}, - .framelines = 525, -}; - -static const struct v4l2_standard ivtv_std_50hz = -{ - .frameperiod = {.numerator = 1, .denominator = 25}, - .framelines = 625, -}; - void ivtv_set_osd_alpha(struct ivtv *itv) { ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3, @@ -345,1073 +314,1219 @@ static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id, return 0; } -static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) +static int ivtv_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) { - struct v4l2_register *regs = arg; - unsigned long flags; - volatile u8 __iomem *reg_start; + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE) - reg_start = itv->reg_mem - IVTV_REG_OFFSET; - else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET && - regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE) - reg_start = itv->dec_mem - IVTV_DECODER_OFFSET; - else if (regs->reg >= 0 && regs->reg < IVTV_ENCODER_SIZE) - reg_start = itv->enc_mem; - else + vbifmt->reserved[0] = 0; + vbifmt->reserved[1] = 0; + if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) return -EINVAL; - - spin_lock_irqsave(&ivtv_cards_lock, flags); - if (cmd == VIDIOC_DBG_G_REGISTER) { - regs->val = readl(regs->reg + reg_start); + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + if (itv->is_60hz) { + vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; } else { - writel(regs->val, regs->reg + reg_start); + vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625; + vbifmt->service_lines[0][16] = V4L2_SLICED_VPS; } - spin_unlock_irqrestore(&ivtv_cards_lock, flags); + vbifmt->service_set = ivtv_get_service_set(vbifmt); return 0; } -static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fmt) +static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - switch (fmt->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) - return -EINVAL; - fmt->fmt.pix.width = itv->main_rect.width; - fmt->fmt.pix.height = itv->main_rect.height; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { - switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) { - case IVTV_YUV_MODE_INTERLACED: - fmt->fmt.pix.field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ? - V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB; - break; - case IVTV_YUV_MODE_PROGRESSIVE: - fmt->fmt.pix.field = V4L2_FIELD_NONE; - break; - default: - fmt->fmt.pix.field = V4L2_FIELD_ANY; - break; - } - fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; - fmt->fmt.pix.bytesperline = 720; - fmt->fmt.pix.width = itv->yuv_info.v4l2_src_w; - fmt->fmt.pix.height = itv->yuv_info.v4l2_src_h; - /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ - fmt->fmt.pix.sizeimage = - 1080 * ((fmt->fmt.pix.height + 31) & ~31); - } else if (streamtype == IVTV_ENC_STREAM_TYPE_YUV) { - fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; - /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ - fmt->fmt.pix.sizeimage = - fmt->fmt.pix.height * fmt->fmt.pix.width + - fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); - } else { - fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - fmt->fmt.pix.sizeimage = 128 * 1024; - } - break; - - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - fmt->fmt.pix.width = itv->params.width; - fmt->fmt.pix.height = itv->params.height; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; - if (streamtype == IVTV_ENC_STREAM_TYPE_YUV || - streamtype == IVTV_DEC_STREAM_TYPE_YUV) { - fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; - /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ - fmt->fmt.pix.sizeimage = - fmt->fmt.pix.height * fmt->fmt.pix.width + - fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); - } else { - fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - fmt->fmt.pix.sizeimage = 128 * 1024; - } - break; + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + + pixfmt->width = itv->params.width; + pixfmt->height = itv->params.height; + pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + pixfmt->field = V4L2_FIELD_INTERLACED; + pixfmt->priv = 0; + if (id->type == IVTV_ENC_STREAM_TYPE_YUV) { + pixfmt->pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + pixfmt->sizeimage = + pixfmt->height * pixfmt->width + + pixfmt->height * (pixfmt->width / 2); + pixfmt->bytesperline = 720; + } else { + pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; + pixfmt->sizeimage = 128 * 1024; + pixfmt->bytesperline = 0; + } + return 0; +} - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) - return -EINVAL; - fmt->fmt.win.chromakey = itv->osd_chroma_key; - fmt->fmt.win.global_alpha = itv->osd_global_alpha; - break; +static int ivtv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi; + + vbifmt->sampling_rate = 27000000; + vbifmt->offset = 248; + vbifmt->samples_per_line = itv->vbi.raw_decoder_line_size - 4; + vbifmt->sample_format = V4L2_PIX_FMT_GREY; + vbifmt->start[0] = itv->vbi.start[0]; + vbifmt->start[1] = itv->vbi.start[1]; + vbifmt->count[0] = vbifmt->count[1] = itv->vbi.count; + vbifmt->flags = 0; + vbifmt->reserved[0] = 0; + vbifmt->reserved[1] = 0; + return 0; +} - case V4L2_BUF_TYPE_VBI_CAPTURE: - fmt->fmt.vbi.sampling_rate = 27000000; - fmt->fmt.vbi.offset = 248; - fmt->fmt.vbi.samples_per_line = itv->vbi.raw_decoder_line_size - 4; - fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - fmt->fmt.vbi.start[0] = itv->vbi.start[0]; - fmt->fmt.vbi.start[1] = itv->vbi.start[1]; - fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = itv->vbi.count; - break; +static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - { - struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + vbifmt->reserved[0] = 0; + vbifmt->reserved[1] = 0; + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; - if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) - return -EINVAL; - vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; - memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); - memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); - if (itv->is_60hz) { - vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; - vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; - } else { - vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625; - vbifmt->service_lines[0][16] = V4L2_SLICED_VPS; - } - vbifmt->service_set = ivtv_get_service_set(vbifmt); - break; + if (id->type == IVTV_DEC_STREAM_TYPE_VBI) { + vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 : + V4L2_SLICED_VBI_525; + ivtv_expand_service_set(vbifmt, itv->is_50hz); + return 0; } - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - { - struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + itv->video_dec_func(itv, VIDIOC_G_FMT, fmt); + vbifmt->service_set = ivtv_get_service_set(vbifmt); + return 0; +} - vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; - memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); - memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); +static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; - if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) { - vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 : - V4L2_SLICED_VBI_525; - ivtv_expand_service_set(vbifmt, itv->is_50hz); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + pixfmt->width = itv->main_rect.width; + pixfmt->height = itv->main_rect.height; + pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + pixfmt->field = V4L2_FIELD_INTERLACED; + pixfmt->priv = 0; + if (id->type == IVTV_DEC_STREAM_TYPE_YUV) { + switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) { + case IVTV_YUV_MODE_INTERLACED: + pixfmt->field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ? + V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB; + break; + case IVTV_YUV_MODE_PROGRESSIVE: + pixfmt->field = V4L2_FIELD_NONE; + break; + default: + pixfmt->field = V4L2_FIELD_ANY; break; } - - itv->video_dec_func(itv, VIDIOC_G_FMT, fmt); - vbifmt->service_set = ivtv_get_service_set(vbifmt); - break; - } - case V4L2_BUF_TYPE_VBI_OUTPUT: - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; + pixfmt->pixelformat = V4L2_PIX_FMT_HM12; + pixfmt->bytesperline = 720; + pixfmt->width = itv->yuv_info.v4l2_src_w; + pixfmt->height = itv->yuv_info.v4l2_src_h; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + pixfmt->sizeimage = + 1080 * ((pixfmt->height + 31) & ~31); + } else { + pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; + pixfmt->sizeimage = 128 * 1024; + pixfmt->bytesperline = 0; } return 0; } -static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype, - struct v4l2_format *fmt, int set_fmt) +static int ivtv_g_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt) { - struct yuv_playback_info *yi = &itv->yuv_info; - struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; - u16 set; + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct v4l2_window *winfmt = &fmt->fmt.win; - if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { - struct v4l2_rect r; - int field; + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + winfmt->chromakey = itv->osd_chroma_key; + winfmt->global_alpha = itv->osd_global_alpha; + winfmt->field = V4L2_FIELD_INTERLACED; + winfmt->clips = NULL; + winfmt->clipcount = 0; + winfmt->bitmap = NULL; + winfmt->w.top = winfmt->w.left = 0; + winfmt->w.width = itv->osd_rect.width; + winfmt->w.height = itv->osd_rect.height; + return 0; +} - if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) - return -EINVAL; - field = fmt->fmt.pix.field; - r.top = 0; - r.left = 0; - r.width = fmt->fmt.pix.width; - r.height = fmt->fmt.pix.height; - ivtv_get_fmt(itv, streamtype, fmt); - fmt->fmt.pix.width = r.width; - fmt->fmt.pix.height = r.height; - if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { - fmt->fmt.pix.field = field; - if (fmt->fmt.pix.width < 2) - fmt->fmt.pix.width = 2; - if (fmt->fmt.pix.width > 720) - fmt->fmt.pix.width = 720; - if (fmt->fmt.pix.height < 2) - fmt->fmt.pix.height = 2; - if (fmt->fmt.pix.height > 576) - fmt->fmt.pix.height = 576; - } - if (set_fmt && streamtype == IVTV_DEC_STREAM_TYPE_YUV) { - /* Return now if we already have some frame data */ - if (yi->stream_size) - return -EBUSY; +static int ivtv_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) +{ + return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt); +} - yi->v4l2_src_w = r.width; - yi->v4l2_src_h = r.height; +static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; + int w = fmt->fmt.pix.width; + int h = fmt->fmt.pix.height; + + w = min(w, 720); + w = max(w, 1); + h = min(h, itv->is_50hz ? 576 : 480); + h = max(h, 2); + ivtv_g_fmt_vid_cap(file, fh, fmt); + fmt->fmt.pix.width = w; + fmt->fmt.pix.height = h; + return 0; +} - switch (field) { - case V4L2_FIELD_NONE: - yi->lace_mode = IVTV_YUV_MODE_PROGRESSIVE; - break; - case V4L2_FIELD_ANY: - yi->lace_mode = IVTV_YUV_MODE_AUTO; - break; - case V4L2_FIELD_INTERLACED_BT: - yi->lace_mode = - IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD; - break; - case V4L2_FIELD_INTERLACED_TB: - default: - yi->lace_mode = IVTV_YUV_MODE_INTERLACED; - break; - } - yi->lace_sync_field = (yi->lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1; +static int ivtv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + return ivtv_g_fmt_vbi_cap(file, fh, fmt); +} - if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) - itv->dma_data_req_size = - 1080 * ((yi->v4l2_src_h + 31) & ~31); +static int ivtv_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; - /* Force update of yuv registers */ - yi->yuv_forced_update = 1; - return 0; - } - return 0; - } + if (id->type == IVTV_DEC_STREAM_TYPE_VBI) + return ivtv_g_fmt_sliced_vbi_cap(file, fh, fmt); - if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) { - if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) - return -EINVAL; - if (set_fmt) { - itv->osd_chroma_key = fmt->fmt.win.chromakey; - itv->osd_global_alpha = fmt->fmt.win.global_alpha; - ivtv_set_osd_alpha(itv); - } - return 0; - } + /* set sliced VBI capture format */ + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + vbifmt->reserved[0] = 0; + vbifmt->reserved[1] = 0; - /* set window size */ - if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - struct cx2341x_mpeg_params *p = &itv->params; - int w = fmt->fmt.pix.width; - int h = fmt->fmt.pix.height; - - if (w > 720) w = 720; - else if (w < 1) w = 1; - if (h > (itv->is_50hz ? 576 : 480)) h = (itv->is_50hz ? 576 : 480); - else if (h < 2) h = 2; - ivtv_get_fmt(itv, streamtype, fmt); - fmt->fmt.pix.width = w; - fmt->fmt.pix.height = h; - - if (!set_fmt || (p->width == w && p->height == h)) - return 0; - if (atomic_read(&itv->capturing) > 0) - return -EBUSY; + if (vbifmt->service_set) + ivtv_expand_service_set(vbifmt, itv->is_50hz); + check_service_set(vbifmt, itv->is_50hz); + vbifmt->service_set = ivtv_get_service_set(vbifmt); + return 0; +} - p->width = w; - p->height = h; - if (w != 720 || h != (itv->is_50hz ? 576 : 480)) - p->video_temporal_filter = 0; - else - p->video_temporal_filter = 8; - if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) - fmt->fmt.pix.width /= 2; - itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); - return ivtv_get_fmt(itv, streamtype, fmt); - } +static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ivtv_open_id *id = fh; + s32 w, h; + int field; + int ret; - /* set raw VBI format */ - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (set_fmt && atomic_read(&itv->capturing) > 0) { - return -EBUSY; - } - if (set_fmt) { - itv->vbi.sliced_in->service_set = 0; - itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in); - } - return ivtv_get_fmt(itv, streamtype, fmt); - } + w = fmt->fmt.pix.width; + h = fmt->fmt.pix.height; + field = fmt->fmt.pix.field; + ret = ivtv_g_fmt_vid_out(file, fh, fmt); + fmt->fmt.pix.width = w; + fmt->fmt.pix.height = h; + if (!ret && id->type == IVTV_DEC_STREAM_TYPE_YUV) { + fmt->fmt.pix.field = field; + if (fmt->fmt.pix.width < 2) + fmt->fmt.pix.width = 2; + if (fmt->fmt.pix.width > 720) + fmt->fmt.pix.width = 720; + if (fmt->fmt.pix.height < 2) + fmt->fmt.pix.height = 2; + if (fmt->fmt.pix.height > 576) + fmt->fmt.pix.height = 576; + } + return ret; +} - /* set sliced VBI output - In principle the user could request that only certain - VBI types are output and that the others are ignored. - I.e., suppress CC in the even fields or only output - WSS and no VPS. Currently though there is no choice. */ - if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) - return ivtv_get_fmt(itv, streamtype, fmt); +static int ivtv_try_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + u32 chromakey = fmt->fmt.win.chromakey; + u8 global_alpha = fmt->fmt.win.global_alpha; - /* any else but sliced VBI capture is an error */ - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) return -EINVAL; + ivtv_g_fmt_vid_out_overlay(file, fh, fmt); + fmt->fmt.win.chromakey = chromakey; + fmt->fmt.win.global_alpha = global_alpha; + return 0; +} - if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) - return ivtv_get_fmt(itv, streamtype, fmt); +static int ivtv_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) +{ + return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt); +} - /* set sliced VBI capture format */ - vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; - memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); +static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; + struct cx2341x_mpeg_params *p = &itv->params; + int w = fmt->fmt.pix.width; + int h = fmt->fmt.pix.height; + int ret = ivtv_try_fmt_vid_cap(file, fh, fmt); - if (vbifmt->service_set) - ivtv_expand_service_set(vbifmt, itv->is_50hz); - set = check_service_set(vbifmt, itv->is_50hz); - vbifmt->service_set = ivtv_get_service_set(vbifmt); + if (ret) + return ret; - if (!set_fmt) + if (p->width == w && p->height == h) return 0; - if (set == 0) + + if (atomic_read(&itv->capturing) > 0) + return -EBUSY; + + p->width = w; + p->height = h; + if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) + fmt->fmt.pix.width /= 2; + itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + return ivtv_g_fmt_vid_cap(file, fh, fmt); +} + +static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + + itv->vbi.sliced_in->service_set = 0; + itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in); + return ivtv_g_fmt_vbi_cap(file, fh, fmt); +} + +static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; + int ret = ivtv_try_fmt_sliced_vbi_cap(file, fh, fmt); + + if (ret || id->type == IVTV_DEC_STREAM_TYPE_VBI) + return ret; + + if (check_service_set(vbifmt, itv->is_50hz) == 0) return -EINVAL; - if (atomic_read(&itv->capturing) > 0) { + if (atomic_read(&itv->capturing) > 0) return -EBUSY; - } itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in)); return 0; } -static int ivtv_debug_ioctls(struct file *filp, unsigned int cmd, void *arg) +static int ivtv_s_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv_open_id *id = fh; struct ivtv *itv = id->itv; - struct v4l2_register *reg = arg; + struct yuv_playback_info *yi = &itv->yuv_info; + int ret = ivtv_try_fmt_vid_out(file, fh, fmt); - switch (cmd) { - /* ioctls to allow direct access to the encoder registers for testing */ - case VIDIOC_DBG_G_REGISTER: - if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) - return ivtv_itvc(itv, cmd, arg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); - return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); - - case VIDIOC_DBG_S_REGISTER: - if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) - return ivtv_itvc(itv, cmd, arg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); - return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); - - case VIDIOC_G_CHIP_IDENT: { - struct v4l2_chip_ident *chip = arg; - - chip->ident = V4L2_IDENT_NONE; - chip->revision = 0; - if (reg->match_type == V4L2_CHIP_MATCH_HOST) { - if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) - chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416; - return 0; - } - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR) - return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); - return -EINVAL; - } + if (ret) + return ret; - case VIDIOC_INT_S_AUDIO_ROUTING: { - struct v4l2_routing *route = arg; + if (id->type != IVTV_DEC_STREAM_TYPE_YUV) + return 0; - ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route); - break; - } + /* Return now if we already have some frame data */ + if (yi->stream_size) + return -EBUSY; - case VIDIOC_INT_RESET: { - u32 val = *(u32 *)arg; + yi->v4l2_src_w = fmt->fmt.pix.width; + yi->v4l2_src_h = fmt->fmt.pix.height; - if ((val == 0 && itv->options.newi2c) || (val & 0x01)) { - ivtv_reset_ir_gpio(itv); - } - if (val & 0x02) { - itv->video_dec_func(itv, cmd, NULL); - } + switch (fmt->fmt.pix.field) { + case V4L2_FIELD_NONE: + yi->lace_mode = IVTV_YUV_MODE_PROGRESSIVE; break; - } - + case V4L2_FIELD_ANY: + yi->lace_mode = IVTV_YUV_MODE_AUTO; + break; + case V4L2_FIELD_INTERLACED_BT: + yi->lace_mode = + IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD; + break; + case V4L2_FIELD_INTERLACED_TB: default: - return -EINVAL; + yi->lace_mode = IVTV_YUV_MODE_INTERLACED; + break; } + yi->lace_sync_field = (yi->lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1; + + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) + itv->dma_data_req_size = + 1080 * ((yi->v4l2_src_h + 31) & ~31); + + /* Force update of yuv registers */ + yi->yuv_forced_update = 1; return 0; } -int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg) +static int ivtv_s_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt) { - struct ivtv_open_id *id = NULL; - struct yuv_playback_info *yi = &itv->yuv_info; - u32 data[CX2341X_MBOX_MAX_DATA]; - int streamtype = 0; + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + int ret = ivtv_try_fmt_vid_out_overlay(file, fh, fmt); - if (filp) { - id = (struct ivtv_open_id *)filp->private_data; - streamtype = id->type; + if (ret == 0) { + itv->osd_chroma_key = fmt->fmt.win.chromakey; + itv->osd_global_alpha = fmt->fmt.win.global_alpha; + ivtv_set_osd_alpha(itv); } + return ret; +} - switch (cmd) { - case VIDIOC_G_PRIORITY: - { - enum v4l2_priority *p = arg; +static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_chip_ident *chip) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - *p = v4l2_prio_max(&itv->prio); - break; + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (chip->match_type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(chip->match_type, chip->match_chip)) + chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416; + return 0; } + if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip); + if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR) + return ivtv_call_i2c_client(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip); + return -EINVAL; +} - case VIDIOC_S_PRIORITY: - { - enum v4l2_priority *prio = arg; +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) +{ + struct v4l2_register *regs = arg; + unsigned long flags; + volatile u8 __iomem *reg_start; - return v4l2_prio_change(&itv->prio, &id->prio, *prio); - } + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE) + reg_start = itv->reg_mem - IVTV_REG_OFFSET; + else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET && + regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE) + reg_start = itv->dec_mem - IVTV_DECODER_OFFSET; + else if (regs->reg >= 0 && regs->reg < IVTV_ENCODER_SIZE) + reg_start = itv->enc_mem; + else + return -EINVAL; - case VIDIOC_QUERYCAP:{ - struct v4l2_capability *vcap = arg; + spin_lock_irqsave(&ivtv_cards_lock, flags); + if (cmd == VIDIOC_DBG_G_REGISTER) + regs->val = readl(regs->reg + reg_start); + else + writel(regs->val, regs->reg + reg_start); + spin_unlock_irqrestore(&ivtv_cards_lock, flags); + return 0; +} - memset(vcap, 0, sizeof(*vcap)); - strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver)); - strlcpy(vcap->card, itv->card_name, sizeof(vcap->card)); - strlcpy(vcap->bus_info, pci_name(itv->dev), sizeof(vcap->bus_info)); - vcap->version = IVTV_DRIVER_VERSION; /* version */ - vcap->capabilities = itv->v4l2_cap; /* capabilities */ +static int ivtv_g_register(struct file *file, void *fh, struct v4l2_register *reg) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - /* reserved.. must set to 0! */ - vcap->reserved[0] = vcap->reserved[1] = - vcap->reserved[2] = vcap->reserved[3] = 0; - break; - } + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg); + return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg); +} - case VIDIOC_ENUMAUDIO:{ - struct v4l2_audio *vin = arg; +static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *reg) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - return ivtv_get_audio_input(itv, vin->index, vin); - } + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg); + return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg); +} +#endif - case VIDIOC_G_AUDIO:{ - struct v4l2_audio *vin = arg; +static int ivtv_g_priority(struct file *file, void *fh, enum v4l2_priority *p) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - vin->index = itv->audio_input; - return ivtv_get_audio_input(itv, vin->index, vin); - } + *p = v4l2_prio_max(&itv->prio); - case VIDIOC_S_AUDIO:{ - struct v4l2_audio *vout = arg; + return 0; +} - if (vout->index >= itv->nof_audio_inputs) - return -EINVAL; - itv->audio_input = vout->index; - ivtv_audio_set_io(itv); - break; - } +static int ivtv_s_priority(struct file *file, void *fh, enum v4l2_priority prio) +{ + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; - case VIDIOC_ENUMAUDOUT:{ - struct v4l2_audioout *vin = arg; + return v4l2_prio_change(&itv->prio, &id->prio, prio); +} - /* set it to defaults from our table */ - return ivtv_get_audio_output(itv, vin->index, vin); - } +static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - case VIDIOC_G_AUDOUT:{ - struct v4l2_audioout *vin = arg; + strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver)); + strlcpy(vcap->card, itv->card_name, sizeof(vcap->card)); + strlcpy(vcap->bus_info, pci_name(itv->dev), sizeof(vcap->bus_info)); + vcap->version = IVTV_DRIVER_VERSION; /* version */ + vcap->capabilities = itv->v4l2_cap; /* capabilities */ + return 0; +} - vin->index = 0; - return ivtv_get_audio_output(itv, vin->index, vin); - } +static int ivtv_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - case VIDIOC_S_AUDOUT:{ - struct v4l2_audioout *vout = arg; + return ivtv_get_audio_input(itv, vin->index, vin); +} - return ivtv_get_audio_output(itv, vout->index, vout); - } +static int ivtv_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - case VIDIOC_ENUMINPUT:{ - struct v4l2_input *vin = arg; + vin->index = itv->audio_input; + return ivtv_get_audio_input(itv, vin->index, vin); +} - /* set it to defaults from our table */ - return ivtv_get_input(itv, vin->index, vin); - } +static int ivtv_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - case VIDIOC_ENUMOUTPUT:{ - struct v4l2_output *vout = arg; + if (vout->index >= itv->nof_audio_inputs) + return -EINVAL; - return ivtv_get_output(itv, vout->index, vout); - } + itv->audio_input = vout->index; + ivtv_audio_set_io(itv); - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: { - struct v4l2_format *fmt = arg; + return 0; +} - return ivtv_try_or_set_fmt(itv, id->type, fmt, cmd == VIDIOC_S_FMT); - } +static int ivtv_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vin) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - case VIDIOC_G_FMT: { - struct v4l2_format *fmt = arg; - int type = fmt->type; + /* set it to defaults from our table */ + return ivtv_get_audio_output(itv, vin->index, vin); +} - memset(fmt, 0, sizeof(*fmt)); - fmt->type = type; - return ivtv_get_fmt(itv, id->type, fmt); - } +static int ivtv_g_audout(struct file *file, void *fh, struct v4l2_audioout *vin) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - case VIDIOC_CROPCAP: { - struct v4l2_cropcap *cropcap = arg; + vin->index = 0; + return ivtv_get_audio_output(itv, vin->index, vin); +} - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - cropcap->bounds.top = cropcap->bounds.left = 0; - cropcap->bounds.width = 720; - if (cropcap->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - cropcap->bounds.height = itv->is_50hz ? 576 : 480; - cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10; - cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11; - } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { - if (yi->track_osd) { - cropcap->bounds.width = yi->osd_full_w; - cropcap->bounds.height = yi->osd_full_h; - } else { - cropcap->bounds.width = 720; - cropcap->bounds.height = - itv->is_out_50hz ? 576 : 480; - } - cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10; - cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11; +static int ivtv_s_audout(struct file *file, void *fh, struct v4l2_audioout *vout) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + + return ivtv_get_audio_output(itv, vout->index, vout); +} + +static int ivtv_enum_input(struct file *file, void *fh, struct v4l2_input *vin) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + + /* set it to defaults from our table */ + return ivtv_get_input(itv, vin->index, vin); +} + +static int ivtv_enum_output(struct file *file, void *fh, struct v4l2_output *vout) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + + return ivtv_get_output(itv, vout->index, vout); +} + +static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) +{ + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; + struct yuv_playback_info *yi = &itv->yuv_info; + int streamtype; + + streamtype = id->type; + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + cropcap->bounds.top = cropcap->bounds.left = 0; + cropcap->bounds.width = 720; + if (cropcap->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + cropcap->bounds.height = itv->is_50hz ? 576 : 480; + cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10; + cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11; + } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + if (yi->track_osd) { + cropcap->bounds.width = yi->osd_full_w; + cropcap->bounds.height = yi->osd_full_h; } else { - cropcap->bounds.height = itv->is_out_50hz ? 576 : 480; - cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10; - cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11; + cropcap->bounds.width = 720; + cropcap->bounds.height = + itv->is_out_50hz ? 576 : 480; } - cropcap->defrect = cropcap->bounds; - return 0; + cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10; + cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11; + } else { + cropcap->bounds.height = itv->is_out_50hz ? 576 : 480; + cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10; + cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11; } + cropcap->defrect = cropcap->bounds; + return 0; +} + +static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; + struct yuv_playback_info *yi = &itv->yuv_info; + int streamtype; - case VIDIOC_S_CROP: { - struct v4l2_crop *crop = arg; + streamtype = id->type; - if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && - (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { - if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { - yi->main_rect = crop->c; + if (ivtv_debug & IVTV_DBGFLG_IOCTL) { + printk(KERN_INFO "ivtv%d ioctl: ", itv->num); + /* Should be replaced */ + /* v4l_printk_ioctl(VIDIOC_S_CROP); */ + } + + if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + yi->main_rect = crop->c; + return 0; + } else { + if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, + crop->c.width, crop->c.height, crop->c.left, crop->c.top)) { + itv->main_rect = crop->c; return 0; - } else { - if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, - crop->c.width, crop->c.height, crop->c.left, crop->c.top)) { - itv->main_rect = crop->c; - return 0; - } } - return -EINVAL; } return -EINVAL; } + return -EINVAL; +} - case VIDIOC_G_CROP: { - struct v4l2_crop *crop = arg; +static int ivtv_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) +{ + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; + struct yuv_playback_info *yi = &itv->yuv_info; + int streamtype; - if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && - (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { - if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) - crop->c = yi->main_rect; - else - crop->c = itv->main_rect; - return 0; + streamtype = id->type; + + if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) + crop->c = yi->main_rect; + else + crop->c = itv->main_rect; + return 0; + } + return -EINVAL; +} + +static int ivtv_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) +{ + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12, + { 0, 0, 0, 0 } + }, + { 1, 0, V4L2_FMT_FLAG_COMPRESSED, + "MPEG", V4L2_PIX_FMT_MPEG, + { 0, 0, 0, 0 } } + }; + enum v4l2_buf_type type = fmt->type; + + if (fmt->index > 1) return -EINVAL; - } - case VIDIOC_ENUM_FMT: { - static struct v4l2_fmtdesc formats[] = { - { 0, 0, 0, - "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12, - { 0, 0, 0, 0 } - }, - { 1, 0, V4L2_FMT_FLAG_COMPRESSED, - "MPEG", V4L2_PIX_FMT_MPEG, - { 0, 0, 0, 0 } - } - }; - struct v4l2_fmtdesc *fmt = arg; - enum v4l2_buf_type type = fmt->type; + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; +} - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) - return -EINVAL; - break; - default: - return -EINVAL; +static int ivtv_enum_fmt_vid_out(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12, + { 0, 0, 0, 0 } + }, + { 1, 0, V4L2_FMT_FLAG_COMPRESSED, + "MPEG", V4L2_PIX_FMT_MPEG, + { 0, 0, 0, 0 } } - if (fmt->index > 1) - return -EINVAL; - *fmt = formats[fmt->index]; - fmt->type = type; + }; + enum v4l2_buf_type type = fmt->type; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + + if (fmt->index > 1) + return -EINVAL; + + *fmt = formats[fmt->index]; + fmt->type = type; + + return 0; +} + +static int ivtv_g_input(struct file *file, void *fh, unsigned int *i) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + + *i = itv->active_input; + + return 0; +} + +int ivtv_s_input(struct file *file, void *fh, unsigned int inp) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + + if (inp < 0 || inp >= itv->nof_inputs) + return -EINVAL; + + if (inp == itv->active_input) { + IVTV_DEBUG_INFO("Input unchanged\n"); return 0; } - case VIDIOC_G_INPUT:{ - *(int *)arg = itv->active_input; - break; + if (atomic_read(&itv->capturing) > 0) { + return -EBUSY; } - case VIDIOC_S_INPUT:{ - int inp = *(int *)arg; + IVTV_DEBUG_INFO("Changing input from %d to %d\n", + itv->active_input, inp); - if (inp < 0 || inp >= itv->nof_inputs) - return -EINVAL; + itv->active_input = inp; + /* Set the audio input to whatever is appropriate for the + input type. */ + itv->audio_input = itv->card->video_inputs[inp].audio_index; - if (inp == itv->active_input) { - IVTV_DEBUG_INFO("Input unchanged\n"); - break; - } - if (atomic_read(&itv->capturing) > 0) { - return -EBUSY; - } - IVTV_DEBUG_INFO("Changing input from %d to %d\n", - itv->active_input, inp); + /* prevent others from messing with the streams until + we're finished changing inputs. */ + ivtv_mute(itv); + ivtv_video_set_io(itv); + ivtv_audio_set_io(itv); + ivtv_unmute(itv); - itv->active_input = inp; - /* Set the audio input to whatever is appropriate for the - input type. */ - itv->audio_input = itv->card->video_inputs[inp].audio_index; + return 0; +} - /* prevent others from messing with the streams until - we're finished changing inputs. */ - ivtv_mute(itv); - ivtv_video_set_io(itv); - ivtv_audio_set_io(itv); - ivtv_unmute(itv); - break; - } +static int ivtv_g_output(struct file *file, void *fh, unsigned int *i) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - case VIDIOC_G_OUTPUT:{ - if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) - return -EINVAL; - *(int *)arg = itv->active_output; - break; - } + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; - case VIDIOC_S_OUTPUT:{ - int outp = *(int *)arg; - struct v4l2_routing route; + *i = itv->active_output; - if (outp >= itv->card->nof_outputs) - return -EINVAL; + return 0; +} - if (outp == itv->active_output) { - IVTV_DEBUG_INFO("Output unchanged\n"); - break; - } - IVTV_DEBUG_INFO("Changing output from %d to %d\n", - itv->active_output, outp); +static int ivtv_s_output(struct file *file, void *fh, unsigned int outp) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct v4l2_routing route; - itv->active_output = outp; - route.input = SAA7127_INPUT_TYPE_NORMAL; - route.output = itv->card->video_outputs[outp].video_output; - ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); - break; + if (outp >= itv->card->nof_outputs) + return -EINVAL; + + if (outp == itv->active_output) { + IVTV_DEBUG_INFO("Output unchanged\n"); + return 0; } + IVTV_DEBUG_INFO("Changing output from %d to %d\n", + itv->active_output, outp); - case VIDIOC_G_FREQUENCY:{ - struct v4l2_frequency *vf = arg; + itv->active_output = outp; + route.input = SAA7127_INPUT_TYPE_NORMAL; + route.output = itv->card->video_outputs[outp].video_output; + ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); - if (vf->tuner != 0) - return -EINVAL; - ivtv_call_i2c_clients(itv, cmd, arg); - break; - } + return 0; +} - case VIDIOC_S_FREQUENCY:{ - struct v4l2_frequency vf = *(struct v4l2_frequency *)arg; +static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - if (vf.tuner != 0) - return -EINVAL; + if (vf->tuner != 0) + return -EINVAL; - ivtv_mute(itv); - IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency); - ivtv_call_i2c_clients(itv, cmd, &vf); - ivtv_unmute(itv); - break; - } + ivtv_call_i2c_clients(itv, VIDIOC_G_FREQUENCY, vf); + return 0; +} - case VIDIOC_ENUMSTD:{ - struct v4l2_standard *vs = arg; - int idx = vs->index; +int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - if (idx < 0 || idx >= ARRAY_SIZE(enum_stds)) - return -EINVAL; + if (vf->tuner != 0) + return -EINVAL; - *vs = (enum_stds[idx].std & V4L2_STD_525_60) ? - ivtv_std_60hz : ivtv_std_50hz; - vs->index = idx; - vs->id = enum_stds[idx].std; - strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name)); - break; - } + ivtv_mute(itv); + IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); + ivtv_call_i2c_clients(itv, VIDIOC_S_FREQUENCY, vf); + ivtv_unmute(itv); + return 0; +} - case VIDIOC_G_STD:{ - *(v4l2_std_id *) arg = itv->std; - break; - } +static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - case VIDIOC_S_STD: { - v4l2_std_id std = *(v4l2_std_id *) arg; + *std = itv->std; + return 0; +} - if ((std & V4L2_STD_ALL) == 0) - return -EINVAL; +int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct yuv_playback_info *yi = &itv->yuv_info; - if (std == itv->std) - break; + if ((*std & V4L2_STD_ALL) == 0) + return -EINVAL; - if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) || - atomic_read(&itv->capturing) > 0 || - atomic_read(&itv->decoding) > 0) { - /* Switching standard would turn off the radio or mess - with already running streams, prevent that by - returning EBUSY. */ - return -EBUSY; - } + if (*std == itv->std) + return 0; - itv->std = std; - itv->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0; - itv->params.is_50hz = itv->is_50hz = !itv->is_60hz; - itv->params.width = 720; - itv->params.height = itv->is_50hz ? 576 : 480; - itv->vbi.count = itv->is_50hz ? 18 : 12; - itv->vbi.start[0] = itv->is_50hz ? 6 : 10; - itv->vbi.start[1] = itv->is_50hz ? 318 : 273; - if (itv->hw_flags & IVTV_HW_CX25840) { - itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284; - } - IVTV_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)itv->std); - - /* Tuner */ - ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); - - if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { - /* set display standard */ - itv->std_out = std; - itv->is_out_60hz = itv->is_60hz; - itv->is_out_50hz = itv->is_50hz; - ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out); - ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz); - itv->main_rect.left = itv->main_rect.top = 0; - itv->main_rect.width = 720; - itv->main_rect.height = itv->params.height; - ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, - 720, itv->main_rect.height, 0, 0); - yi->main_rect = itv->main_rect; - if (!itv->osd_info) { - yi->osd_full_w = 720; - yi->osd_full_h = itv->is_out_50hz ? 576 : 480; - } + if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) || + atomic_read(&itv->capturing) > 0 || + atomic_read(&itv->decoding) > 0) { + /* Switching standard would turn off the radio or mess + with already running streams, prevent that by + returning EBUSY. */ + return -EBUSY; + } + + itv->std = *std; + itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; + itv->params.is_50hz = itv->is_50hz = !itv->is_60hz; + itv->params.width = 720; + itv->params.height = itv->is_50hz ? 576 : 480; + itv->vbi.count = itv->is_50hz ? 18 : 12; + itv->vbi.start[0] = itv->is_50hz ? 6 : 10; + itv->vbi.start[1] = itv->is_50hz ? 318 : 273; + + if (itv->hw_flags & IVTV_HW_CX25840) + itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284; + + IVTV_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)itv->std); + + /* Tuner */ + ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + /* set display standard */ + itv->std_out = *std; + itv->is_out_60hz = itv->is_60hz; + itv->is_out_50hz = itv->is_50hz; + ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out); + ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz); + itv->main_rect.left = itv->main_rect.top = 0; + itv->main_rect.width = 720; + itv->main_rect.height = itv->params.height; + ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, + 720, itv->main_rect.height, 0, 0); + yi->main_rect = itv->main_rect; + if (!itv->osd_info) { + yi->osd_full_w = 720; + yi->osd_full_h = itv->is_out_50hz ? 576 : 480; } - break; } + return 0; +} - case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */ - struct v4l2_tuner *vt = arg; +static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) +{ + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; - if (vt->index != 0) - return -EINVAL; + if (vt->index != 0) + return -EINVAL; - ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt); - break; - } + ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt); - case VIDIOC_G_TUNER: { - struct v4l2_tuner *vt = arg; + return 0; +} - if (vt->index != 0) - return -EINVAL; +static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - memset(vt, 0, sizeof(*vt)); - ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt); + if (vt->index != 0) + return -EINVAL; - if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { - strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name)); - vt->type = V4L2_TUNER_RADIO; - } else { - strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name)); - vt->type = V4L2_TUNER_ANALOG_TV; - } - break; + ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt); + + if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { + strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name)); + vt->type = V4L2_TUNER_RADIO; + } else { + strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name)); + vt->type = V4L2_TUNER_ANALOG_TV; } - case VIDIOC_G_SLICED_VBI_CAP: { - struct v4l2_sliced_vbi_cap *cap = arg; - int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; - int f, l; - enum v4l2_buf_type type = cap->type; - - memset(cap, 0, sizeof(*cap)); - cap->type = type; - if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - for (f = 0; f < 2; f++) { - for (l = 0; l < 24; l++) { - if (valid_service_line(f, l, itv->is_50hz)) { - cap->service_lines[f][l] = set; - } - } + return 0; +} + +static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; + int f, l; + + if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + if (valid_service_line(f, l, itv->is_50hz)) + cap->service_lines[f][l] = set; } - return 0; } - if (type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { - if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) - return -EINVAL; - if (itv->is_60hz) { - cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525; - cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525; - } else { - cap->service_lines[0][23] = V4L2_SLICED_WSS_625; - cap->service_lines[0][16] = V4L2_SLICED_VPS; - } - return 0; + return 0; + } + if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) + return -EINVAL; + if (itv->is_60hz) { + cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } else { + cap->service_lines[0][23] = V4L2_SLICED_WSS_625; + cap->service_lines[0][16] = V4L2_SLICED_VPS; } - return -EINVAL; + return 0; } + return -EINVAL; +} - case VIDIOC_G_ENC_INDEX: { - struct v4l2_enc_idx *idx = arg; - struct v4l2_enc_idx_entry *e = idx->entry; - int entries; - int i; - - entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) % - IVTV_MAX_PGM_INDEX; - if (entries > V4L2_ENC_IDX_ENTRIES) - entries = V4L2_ENC_IDX_ENTRIES; - idx->entries = 0; - for (i = 0; i < entries; i++) { - *e = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX]; - if ((e->flags & V4L2_ENC_IDX_FRAME_MASK) <= V4L2_ENC_IDX_FRAME_B) { - idx->entries++; - e++; - } +static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + struct v4l2_enc_idx_entry *e = idx->entry; + int entries; + int i; + + entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) % + IVTV_MAX_PGM_INDEX; + if (entries > V4L2_ENC_IDX_ENTRIES) + entries = V4L2_ENC_IDX_ENTRIES; + idx->entries = 0; + for (i = 0; i < entries; i++) { + *e = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX]; + if ((e->flags & V4L2_ENC_IDX_FRAME_MASK) <= V4L2_ENC_IDX_FRAME_B) { + idx->entries++; + e++; } - itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX; - break; } + itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX; + return 0; +} - case VIDIOC_ENCODER_CMD: - case VIDIOC_TRY_ENCODER_CMD: { - struct v4l2_encoder_cmd *enc = arg; - int try = cmd == VIDIOC_TRY_ENCODER_CMD; - - memset(&enc->raw, 0, sizeof(enc->raw)); - switch (enc->cmd) { - case V4L2_ENC_CMD_START: - IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); - enc->flags = 0; - if (try) - return 0; - return ivtv_start_capture(id); +static int ivtv_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc) +{ + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; - case V4L2_ENC_CMD_STOP: - IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); - enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; - if (try) - return 0; - ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); + + switch (enc->cmd) { + case V4L2_ENC_CMD_START: + IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); + enc->flags = 0; + return ivtv_start_capture(id); + + case V4L2_ENC_CMD_STOP: + IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); + enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; + ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); + return 0; + + case V4L2_ENC_CMD_PAUSE: + IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); + enc->flags = 0; + + if (!atomic_read(&itv->capturing)) + return -EPERM; + if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) return 0; - case V4L2_ENC_CMD_PAUSE: - IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); - enc->flags = 0; - if (try) - return 0; - if (!atomic_read(&itv->capturing)) - return -EPERM; - if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) - return 0; - ivtv_mute(itv); - ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0); - break; + ivtv_mute(itv); + ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0); + break; - case V4L2_ENC_CMD_RESUME: - IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); - enc->flags = 0; - if (try) - return 0; - if (!atomic_read(&itv->capturing)) - return -EPERM; - if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) - return 0; - ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1); - ivtv_unmute(itv); - break; - default: - IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); - return -EINVAL; - } + case V4L2_ENC_CMD_RESUME: + IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); + enc->flags = 0; + + if (!atomic_read(&itv->capturing)) + return -EPERM; + + if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) + return 0; + + ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1); + ivtv_unmute(itv); break; + default: + IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); + return -EINVAL; } - case VIDIOC_G_FBUF: { - struct v4l2_framebuffer *fb = arg; - int pixfmt; - static u32 pixel_format[16] = { - V4L2_PIX_FMT_PAL8, /* Uses a 256-entry RGB colormap */ - V4L2_PIX_FMT_RGB565, - V4L2_PIX_FMT_RGB555, - V4L2_PIX_FMT_RGB444, - V4L2_PIX_FMT_RGB32, - 0, - 0, - 0, - V4L2_PIX_FMT_PAL8, /* Uses a 256-entry YUV colormap */ - V4L2_PIX_FMT_YUV565, - V4L2_PIX_FMT_YUV555, - V4L2_PIX_FMT_YUV444, - V4L2_PIX_FMT_YUV32, - 0, - 0, - 0, - }; + return 0; +} - memset(fb, 0, sizeof(*fb)); - if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) - return -EINVAL; - fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY | - V4L2_FBUF_CAP_GLOBAL_ALPHA; - ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0); - data[0] |= (read_reg(0x2a00) >> 7) & 0x40; - pixfmt = (data[0] >> 3) & 0xf; - fb->fmt.pixelformat = pixel_format[pixfmt]; - fb->fmt.width = itv->osd_rect.width; - fb->fmt.height = itv->osd_rect.height; - fb->base = (void *)itv->osd_video_pbase; - if (itv->osd_chroma_key_state) - fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY; - if (itv->osd_global_alpha_state) - fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA; - pixfmt &= 7; - /* no local alpha for RGB565 or unknown formats */ - if (pixfmt == 1 || pixfmt > 4) - break; +static int ivtv_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + + switch (enc->cmd) { + case V4L2_ENC_CMD_START: + IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); + enc->flags = 0; + return 0; + + case V4L2_ENC_CMD_STOP: + IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); + enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; + return 0; + + case V4L2_ENC_CMD_PAUSE: + IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); + enc->flags = 0; + return 0; + + case V4L2_ENC_CMD_RESUME: + IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); + enc->flags = 0; + return 0; + default: + IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); + return -EINVAL; + } +} + +static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + u32 data[CX2341X_MBOX_MAX_DATA]; + struct yuv_playback_info *yi = &itv->yuv_info; + + int pixfmt; + static u32 pixel_format[16] = { + V4L2_PIX_FMT_PAL8, /* Uses a 256-entry RGB colormap */ + V4L2_PIX_FMT_RGB565, + V4L2_PIX_FMT_RGB555, + V4L2_PIX_FMT_RGB444, + V4L2_PIX_FMT_RGB32, + 0, + 0, + 0, + V4L2_PIX_FMT_PAL8, /* Uses a 256-entry YUV colormap */ + V4L2_PIX_FMT_YUV565, + V4L2_PIX_FMT_YUV555, + V4L2_PIX_FMT_YUV444, + V4L2_PIX_FMT_YUV32, + 0, + 0, + 0, + }; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + return -EINVAL; + if (!itv->osd_video_pbase) + return -EINVAL; + + fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY | + V4L2_FBUF_CAP_GLOBAL_ALPHA; + + ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0); + data[0] |= (read_reg(0x2a00) >> 7) & 0x40; + pixfmt = (data[0] >> 3) & 0xf; + + fb->fmt.pixelformat = pixel_format[pixfmt]; + fb->fmt.width = itv->osd_rect.width; + fb->fmt.height = itv->osd_rect.height; + fb->fmt.field = V4L2_FIELD_INTERLACED; + fb->fmt.bytesperline = fb->fmt.width; + fb->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; + fb->fmt.field = V4L2_FIELD_INTERLACED; + fb->fmt.priv = 0; + if (fb->fmt.pixelformat != V4L2_PIX_FMT_PAL8) + fb->fmt.bytesperline *= 2; + if (fb->fmt.pixelformat == V4L2_PIX_FMT_RGB32 || + fb->fmt.pixelformat == V4L2_PIX_FMT_YUV32) + fb->fmt.bytesperline *= 2; + fb->fmt.sizeimage = fb->fmt.bytesperline * fb->fmt.height; + fb->base = (void *)itv->osd_video_pbase; + fb->flags = 0; + + if (itv->osd_chroma_key_state) + fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY; + + if (itv->osd_global_alpha_state) + fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA; + + pixfmt &= 7; + + /* no local alpha for RGB565 or unknown formats */ + if (pixfmt == 1 || pixfmt > 4) + return 0; + + /* 16-bit formats have inverted local alpha */ + if (pixfmt == 2 || pixfmt == 3) + fb->capability |= V4L2_FBUF_CAP_LOCAL_INV_ALPHA; + else + fb->capability |= V4L2_FBUF_CAP_LOCAL_ALPHA; + + if (itv->osd_local_alpha_state) { /* 16-bit formats have inverted local alpha */ if (pixfmt == 2 || pixfmt == 3) - fb->capability |= V4L2_FBUF_CAP_LOCAL_INV_ALPHA; + fb->flags |= V4L2_FBUF_FLAG_LOCAL_INV_ALPHA; else - fb->capability |= V4L2_FBUF_CAP_LOCAL_ALPHA; - if (itv->osd_local_alpha_state) { - /* 16-bit formats have inverted local alpha */ - if (pixfmt == 2 || pixfmt == 3) - fb->flags |= V4L2_FBUF_FLAG_LOCAL_INV_ALPHA; - else - fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; - } - if (yi->track_osd) - fb->flags |= V4L2_FBUF_FLAG_OVERLAY; - break; + fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; } + if (yi->track_osd) + fb->flags |= V4L2_FBUF_FLAG_OVERLAY; - case VIDIOC_S_FBUF: { - struct v4l2_framebuffer *fb = arg; + return 0; +} - if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) - return -EINVAL; - itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0; - itv->osd_local_alpha_state = - (fb->flags & (V4L2_FBUF_FLAG_LOCAL_ALPHA|V4L2_FBUF_FLAG_LOCAL_INV_ALPHA)) != 0; - itv->osd_chroma_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0; - ivtv_set_osd_alpha(itv); - yi->track_osd = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0; - break; - } +static int ivtv_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) +{ + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; + struct yuv_playback_info *yi = &itv->yuv_info; - case VIDIOC_OVERLAY: { - int *on = arg; + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + return -EINVAL; + if (!itv->osd_video_pbase) + return -EINVAL; - if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) - return -EINVAL; - ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, *on != 0); - break; - } + itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0; + itv->osd_local_alpha_state = + (fb->flags & (V4L2_FBUF_FLAG_LOCAL_ALPHA|V4L2_FBUF_FLAG_LOCAL_INV_ALPHA)) != 0; + itv->osd_chroma_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0; + ivtv_set_osd_alpha(itv); + yi->track_osd = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0; + return ivtv_g_fbuf(file, fh, fb); +} - case VIDIOC_LOG_STATUS: - { - int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT; - struct v4l2_input vidin; - struct v4l2_audio audin; - int i; +static int ivtv_overlay(struct file *file, void *fh, unsigned int on) +{ + struct ivtv_open_id *id = fh; + struct ivtv *itv = id->itv; - IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num); - IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name); - if (itv->hw_flags & IVTV_HW_TVEEPROM) { - struct tveeprom tv; + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + return -EINVAL; - ivtv_read_eeprom(itv, &tv); - } - ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL); - ivtv_get_input(itv, itv->active_input, &vidin); - ivtv_get_audio_input(itv, itv->audio_input, &audin); - IVTV_INFO("Video Input: %s\n", vidin.name); - IVTV_INFO("Audio Input: %s%s\n", audin.name, - (itv->dualwatch_stereo_mode & ~0x300) == 0x200 ? " (Bilingual)" : ""); - if (has_output) { - struct v4l2_output vidout; - struct v4l2_audioout audout; - int mode = itv->output_mode; - static const char * const output_modes[5] = { - "None", - "MPEG Streaming", - "YUV Streaming", - "YUV Frames", - "Passthrough", - }; - static const char * const audio_modes[5] = { - "Stereo", - "Left", - "Right", - "Mono", - "Swapped" - }; - static const char * const alpha_mode[4] = { - "None", - "Global", - "Local", - "Global and Local" - }; - static const char * const pixel_format[16] = { - "ARGB Indexed", - "RGB 5:6:5", - "ARGB 1:5:5:5", - "ARGB 1:4:4:4", - "ARGB 8:8:8:8", - "5", - "6", - "7", - "AYUV Indexed", - "YUV 5:6:5", - "AYUV 1:5:5:5", - "AYUV 1:4:4:4", - "AYUV 8:8:8:8", - "13", - "14", - "15", - }; - - ivtv_get_output(itv, itv->active_output, &vidout); - ivtv_get_audio_output(itv, 0, &audout); - IVTV_INFO("Video Output: %s\n", vidout.name); - IVTV_INFO("Audio Output: %s (Stereo/Bilingual: %s/%s)\n", audout.name, - audio_modes[itv->audio_stereo_mode], - audio_modes[itv->audio_bilingual_mode]); - if (mode < 0 || mode > OUT_PASSTHROUGH) - mode = OUT_NONE; - IVTV_INFO("Output Mode: %s\n", output_modes[mode]); - ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0); - data[0] |= (read_reg(0x2a00) >> 7) & 0x40; - IVTV_INFO("Overlay: %s, Alpha: %s, Pixel Format: %s\n", - data[0] & 1 ? "On" : "Off", - alpha_mode[(data[0] >> 1) & 0x3], - pixel_format[(data[0] >> 3) & 0xf]); - } - IVTV_INFO("Tuner: %s\n", - test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV"); - cx2341x_log_status(&itv->params, itv->name); - IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags); - for (i = 0; i < IVTV_MAX_STREAMS; i++) { - struct ivtv_stream *s = &itv->streams[i]; - - if (s->v4l2dev == NULL || s->buffers == 0) - continue; - IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, - (s->buffers - s->q_free.buffers) * 100 / s->buffers, - (s->buffers * s->buf_size) / 1024, s->buffers); - } - IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted); - IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num); - break; + ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, on != 0); + + return 0; +} + +static int ivtv_log_status(struct file *file, void *fh) +{ + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; + u32 data[CX2341X_MBOX_MAX_DATA]; + + int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT; + struct v4l2_input vidin; + struct v4l2_audio audin; + int i; + + IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num); + IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name); + if (itv->hw_flags & IVTV_HW_TVEEPROM) { + struct tveeprom tv; + + ivtv_read_eeprom(itv, &tv); + } + ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL); + ivtv_get_input(itv, itv->active_input, &vidin); + ivtv_get_audio_input(itv, itv->audio_input, &audin); + IVTV_INFO("Video Input: %s\n", vidin.name); + IVTV_INFO("Audio Input: %s%s\n", audin.name, + (itv->dualwatch_stereo_mode & ~0x300) == 0x200 ? " (Bilingual)" : ""); + if (has_output) { + struct v4l2_output vidout; + struct v4l2_audioout audout; + int mode = itv->output_mode; + static const char * const output_modes[5] = { + "None", + "MPEG Streaming", + "YUV Streaming", + "YUV Frames", + "Passthrough", + }; + static const char * const audio_modes[5] = { + "Stereo", + "Left", + "Right", + "Mono", + "Swapped" + }; + static const char * const alpha_mode[4] = { + "None", + "Global", + "Local", + "Global and Local" + }; + static const char * const pixel_format[16] = { + "ARGB Indexed", + "RGB 5:6:5", + "ARGB 1:5:5:5", + "ARGB 1:4:4:4", + "ARGB 8:8:8:8", + "5", + "6", + "7", + "AYUV Indexed", + "YUV 5:6:5", + "AYUV 1:5:5:5", + "AYUV 1:4:4:4", + "AYUV 8:8:8:8", + "13", + "14", + "15", + }; + + ivtv_get_output(itv, itv->active_output, &vidout); + ivtv_get_audio_output(itv, 0, &audout); + IVTV_INFO("Video Output: %s\n", vidout.name); + IVTV_INFO("Audio Output: %s (Stereo/Bilingual: %s/%s)\n", audout.name, + audio_modes[itv->audio_stereo_mode], + audio_modes[itv->audio_bilingual_mode]); + if (mode < 0 || mode > OUT_PASSTHROUGH) + mode = OUT_NONE; + IVTV_INFO("Output Mode: %s\n", output_modes[mode]); + ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0); + data[0] |= (read_reg(0x2a00) >> 7) & 0x40; + IVTV_INFO("Overlay: %s, Alpha: %s, Pixel Format: %s\n", + data[0] & 1 ? "On" : "Off", + alpha_mode[(data[0] >> 1) & 0x3], + pixel_format[(data[0] >> 3) & 0xf]); } + IVTV_INFO("Tuner: %s\n", + test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV"); + cx2341x_log_status(&itv->params, itv->name); + IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags); + for (i = 0; i < IVTV_MAX_STREAMS; i++) { + struct ivtv_stream *s = &itv->streams[i]; - default: - return -EINVAL; + if (s->v4l2dev == NULL || s->buffers == 0) + continue; + IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, + (s->buffers - s->q_free.buffers) * 100 / s->buffers, + (s->buffers * s->buf_size) / 1024, s->buffers); } + + IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted); + IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num); + return 0; } @@ -1433,7 +1548,7 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) return -EINVAL; if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL) return 0; - if (ivtv_claim_stream(id, id->type)) { + if (ivtv_start_decoding(id, id->type)) { return -EBUSY; } if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) { @@ -1607,121 +1722,30 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) return 0; } -static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, void *arg) +static int ivtv_default(struct file *file, void *fh, int cmd, void *arg) { - struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; - struct ivtv *itv = id->itv; - int ret; - - /* check priority */ - switch (cmd) { - case VIDIOC_S_CTRL: - case VIDIOC_S_STD: - case VIDIOC_S_INPUT: - case VIDIOC_S_OUTPUT: - case VIDIOC_S_TUNER: - case VIDIOC_S_FREQUENCY: - case VIDIOC_S_FMT: - case VIDIOC_S_CROP: - case VIDIOC_S_AUDIO: - case VIDIOC_S_AUDOUT: - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_S_FBUF: - case VIDIOC_OVERLAY: - ret = v4l2_prio_check(&itv->prio, &id->prio); - if (ret) - return ret; - } + struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; switch (cmd) { - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - case VIDIOC_G_CHIP_IDENT: - case VIDIOC_INT_S_AUDIO_ROUTING: - case VIDIOC_INT_RESET: - if (ivtv_debug & IVTV_DBGFLG_IOCTL) { - printk(KERN_INFO "ivtv%d ioctl: ", itv->num); - v4l_printk_ioctl(cmd); - printk("\n"); - } - return ivtv_debug_ioctls(filp, cmd, arg); + case VIDIOC_INT_S_AUDIO_ROUTING: { + struct v4l2_routing *route = arg; - case VIDIOC_G_PRIORITY: - case VIDIOC_S_PRIORITY: - case VIDIOC_QUERYCAP: - case VIDIOC_ENUMINPUT: - case VIDIOC_G_INPUT: - case VIDIOC_S_INPUT: - case VIDIOC_ENUMOUTPUT: - case VIDIOC_G_OUTPUT: - case VIDIOC_S_OUTPUT: - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: - case VIDIOC_ENUM_FMT: - case VIDIOC_CROPCAP: - case VIDIOC_G_CROP: - case VIDIOC_S_CROP: - case VIDIOC_G_FREQUENCY: - case VIDIOC_S_FREQUENCY: - case VIDIOC_ENUMSTD: - case VIDIOC_G_STD: - case VIDIOC_S_STD: - case VIDIOC_S_TUNER: - case VIDIOC_G_TUNER: - case VIDIOC_ENUMAUDIO: - case VIDIOC_S_AUDIO: - case VIDIOC_G_AUDIO: - case VIDIOC_ENUMAUDOUT: - case VIDIOC_S_AUDOUT: - case VIDIOC_G_AUDOUT: - case VIDIOC_G_SLICED_VBI_CAP: - case VIDIOC_LOG_STATUS: - case VIDIOC_G_ENC_INDEX: - case VIDIOC_ENCODER_CMD: - case VIDIOC_TRY_ENCODER_CMD: - case VIDIOC_G_FBUF: - case VIDIOC_S_FBUF: - case VIDIOC_OVERLAY: - if (ivtv_debug & IVTV_DBGFLG_IOCTL) { - printk(KERN_INFO "ivtv%d ioctl: ", itv->num); - v4l_printk_ioctl(cmd); - printk("\n"); - } - return ivtv_v4l2_ioctls(itv, filp, cmd, arg); + ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route); + break; + } - case VIDIOC_QUERYMENU: - case VIDIOC_QUERYCTRL: - case VIDIOC_S_CTRL: - case VIDIOC_G_CTRL: - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_G_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: - if (ivtv_debug & IVTV_DBGFLG_IOCTL) { - printk(KERN_INFO "ivtv%d ioctl: ", itv->num); - v4l_printk_ioctl(cmd); - printk("\n"); - } - return ivtv_control_ioctls(itv, cmd, arg); + case VIDIOC_INT_RESET: { + u32 val = *(u32 *)arg; - case IVTV_IOC_DMA_FRAME: - case VIDEO_GET_PTS: - case VIDEO_GET_FRAME_COUNT: - case VIDEO_GET_EVENT: - case VIDEO_PLAY: - case VIDEO_STOP: - case VIDEO_FREEZE: - case VIDEO_CONTINUE: - case VIDEO_COMMAND: - case VIDEO_TRY_COMMAND: - return ivtv_decoder_ioctls(filp, cmd, arg); + if ((val == 0 && itv->options.newi2c) || (val & 0x01)) + ivtv_reset_ir_gpio(itv); + if (val & 0x02) + itv->video_dec_func(itv, cmd, NULL); + break; + } - case 0x00005401: /* Handle isatty() calls */ - return -EINVAL; default: - return v4l_compat_translate_ioctl(inode, filp, cmd, arg, - ivtv_v4l2_do_ioctl); + return -EINVAL; } return 0; } @@ -1729,7 +1753,11 @@ static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp, static int ivtv_serialized_ioctl(struct ivtv *itv, struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { - /* Filter dvb ioctls that cannot be handled by video_usercopy */ + struct video_device *vfd = video_devdata(filp); + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + int ret; + + /* Filter dvb ioctls that cannot be handled by the v4l ioctl framework */ switch (cmd) { case VIDEO_SELECT_SOURCE: IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n"); @@ -1758,10 +1786,47 @@ static int ivtv_serialized_ioctl(struct ivtv *itv, struct inode *inode, struct f ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); return 0; + case IVTV_IOC_DMA_FRAME: + case VIDEO_GET_PTS: + case VIDEO_GET_FRAME_COUNT: + case VIDEO_GET_EVENT: + case VIDEO_PLAY: + case VIDEO_STOP: + case VIDEO_FREEZE: + case VIDEO_CONTINUE: + case VIDEO_COMMAND: + case VIDEO_TRY_COMMAND: + return ivtv_decoder_ioctls(filp, cmd, (void *)arg); + default: break; } - return video_usercopy(inode, filp, cmd, arg, ivtv_v4l2_do_ioctl); + + /* check priority */ + switch (cmd) { + case VIDIOC_S_CTRL: + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_OUTPUT: + case VIDIOC_S_TUNER: + case VIDIOC_S_FREQUENCY: + case VIDIOC_S_FMT: + case VIDIOC_S_CROP: + case VIDIOC_S_AUDIO: + case VIDIOC_S_AUDOUT: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_S_FBUF: + case VIDIOC_OVERLAY: + ret = v4l2_prio_check(&itv->prio, &id->prio); + if (ret) + return ret; + } + + if (ivtv_debug & IVTV_DBGFLG_IOCTL) + vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; + ret = video_ioctl2(inode, filp, cmd, arg); + vfd->debug = 0; + return ret; } int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, @@ -1776,3 +1841,70 @@ int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, mutex_unlock(&itv->serialize_lock); return res; } + +void ivtv_set_funcs(struct video_device *vdev) +{ + vdev->vidioc_querycap = ivtv_querycap; + vdev->vidioc_g_priority = ivtv_g_priority; + vdev->vidioc_s_priority = ivtv_s_priority; + vdev->vidioc_s_audio = ivtv_s_audio; + vdev->vidioc_g_audio = ivtv_g_audio; + vdev->vidioc_enumaudio = ivtv_enumaudio; + vdev->vidioc_s_audout = ivtv_s_audout; + vdev->vidioc_g_audout = ivtv_g_audout; + vdev->vidioc_enum_input = ivtv_enum_input; + vdev->vidioc_enum_output = ivtv_enum_output; + vdev->vidioc_enumaudout = ivtv_enumaudout; + vdev->vidioc_cropcap = ivtv_cropcap; + vdev->vidioc_s_crop = ivtv_s_crop; + vdev->vidioc_g_crop = ivtv_g_crop; + vdev->vidioc_g_input = ivtv_g_input; + vdev->vidioc_s_input = ivtv_s_input; + vdev->vidioc_g_output = ivtv_g_output; + vdev->vidioc_s_output = ivtv_s_output; + vdev->vidioc_g_frequency = ivtv_g_frequency; + vdev->vidioc_s_frequency = ivtv_s_frequency; + vdev->vidioc_s_tuner = ivtv_s_tuner; + vdev->vidioc_g_tuner = ivtv_g_tuner; + vdev->vidioc_g_enc_index = ivtv_g_enc_index; + vdev->vidioc_g_fbuf = ivtv_g_fbuf; + vdev->vidioc_s_fbuf = ivtv_s_fbuf; + vdev->vidioc_g_std = ivtv_g_std; + vdev->vidioc_s_std = ivtv_s_std; + vdev->vidioc_overlay = ivtv_overlay; + vdev->vidioc_log_status = ivtv_log_status; + vdev->vidioc_enum_fmt_vid_cap = ivtv_enum_fmt_vid_cap; + vdev->vidioc_encoder_cmd = ivtv_encoder_cmd; + vdev->vidioc_try_encoder_cmd = ivtv_try_encoder_cmd; + vdev->vidioc_enum_fmt_vid_out = ivtv_enum_fmt_vid_out; + vdev->vidioc_g_fmt_vid_cap = ivtv_g_fmt_vid_cap; + vdev->vidioc_g_fmt_vbi_cap = ivtv_g_fmt_vbi_cap; + vdev->vidioc_g_fmt_sliced_vbi_cap = ivtv_g_fmt_sliced_vbi_cap; + vdev->vidioc_g_fmt_vid_out = ivtv_g_fmt_vid_out; + vdev->vidioc_g_fmt_vid_out_overlay = ivtv_g_fmt_vid_out_overlay; + vdev->vidioc_g_fmt_sliced_vbi_out = ivtv_g_fmt_sliced_vbi_out; + vdev->vidioc_s_fmt_vid_cap = ivtv_s_fmt_vid_cap; + vdev->vidioc_s_fmt_vbi_cap = ivtv_s_fmt_vbi_cap; + vdev->vidioc_s_fmt_sliced_vbi_cap = ivtv_s_fmt_sliced_vbi_cap; + vdev->vidioc_s_fmt_vid_out = ivtv_s_fmt_vid_out; + vdev->vidioc_s_fmt_vid_out_overlay = ivtv_s_fmt_vid_out_overlay; + vdev->vidioc_s_fmt_sliced_vbi_out = ivtv_s_fmt_sliced_vbi_out; + vdev->vidioc_try_fmt_vid_cap = ivtv_try_fmt_vid_cap; + vdev->vidioc_try_fmt_vbi_cap = ivtv_try_fmt_vbi_cap; + vdev->vidioc_try_fmt_sliced_vbi_cap = ivtv_try_fmt_sliced_vbi_cap; + vdev->vidioc_try_fmt_vid_out = ivtv_try_fmt_vid_out; + vdev->vidioc_try_fmt_vid_out_overlay = ivtv_try_fmt_vid_out_overlay; + vdev->vidioc_try_fmt_sliced_vbi_out = ivtv_try_fmt_sliced_vbi_out; + vdev->vidioc_g_sliced_vbi_cap = ivtv_g_sliced_vbi_cap; + vdev->vidioc_g_chip_ident = ivtv_g_chip_ident; +#ifdef CONFIG_VIDEO_ADV_DEBUG + vdev->vidioc_g_register = ivtv_g_register; + vdev->vidioc_s_register = ivtv_s_register; +#endif + vdev->vidioc_default = ivtv_default; + vdev->vidioc_queryctrl = ivtv_queryctrl; + vdev->vidioc_querymenu = ivtv_querymenu; + vdev->vidioc_g_ext_ctrls = ivtv_g_ext_ctrls; + vdev->vidioc_s_ext_ctrls = ivtv_s_ext_ctrls; + vdev->vidioc_try_ext_ctrls = ivtv_try_ext_ctrls; +} diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/video/ivtv/ivtv-ioctl.h index 4e67f0ed1fc..70188588b4f 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.h +++ b/drivers/media/video/ivtv/ivtv-ioctl.h @@ -24,10 +24,13 @@ u16 ivtv_service2vbi(int type); void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt); -int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg); -int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg); void ivtv_set_osd_alpha(struct ivtv *itv); int ivtv_set_speed(struct ivtv *itv, int speed); +void ivtv_set_funcs(struct video_device *vdev); +int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std); +int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); +int ivtv_s_input(struct file *file, void *fh, unsigned int inp); +int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg); #endif diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index c854285a437..f8883b487f4 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -220,7 +220,8 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) s->v4l2dev->dev = &itv->dev->dev; s->v4l2dev->fops = ivtv_stream_info[type].fops; s->v4l2dev->release = video_device_release; - + s->v4l2dev->tvnorms = V4L2_STD_ALL; + ivtv_set_funcs(s->v4l2dev); return 0; } diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index 73be154f7f0..bdfda48e56b 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -367,6 +367,88 @@ static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source, return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count); } +static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned long p = *ppos; + void *dst; + int err = 0; + unsigned long total_size; + struct ivtv *itv = (struct ivtv *) info->par; + unsigned long dma_offset = + IVTV_DECODER_OFFSET + itv->osd_info->video_rbase; + unsigned long dma_size; + u16 lead = 0, tail = 0; + + if (info->state != FBINFO_STATE_RUNNING) + return -EPERM; + + total_size = info->screen_size; + + if (total_size == 0) + total_size = info->fix.smem_len; + + if (p > total_size) + return -EFBIG; + + if (count > total_size) { + err = -EFBIG; + count = total_size; + } + + if (count + p > total_size) { + if (!err) + err = -ENOSPC; + + count = total_size - p; + } + + dst = (void __force *) (info->screen_base + p); + + if (info->fbops->fb_sync) + info->fbops->fb_sync(info); + + if (!access_ok(VERIFY_READ, buf, count)) { + IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n", + (unsigned long)buf); + err = -EFAULT; + } + + if (!err) { + /* If transfer size > threshold and both src/dst + addresses are aligned, use DMA */ + if (count >= 4096 && + ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) { + /* Odd address = can't DMA. Align */ + if ((unsigned long)dst & 3) { + lead = 4 - ((unsigned long)dst & 3); + memcpy(dst, buf, lead); + buf += lead; + dst += lead; + } + /* DMA resolution is 32 bits */ + if ((count - lead) & 3) + tail = (count - lead) & 3; + /* DMA the data */ + dma_size = count - lead - tail; + err = ivtvfb_prep_dec_dma_to_device(itv, + p + lead + dma_offset, (void *)buf, dma_size); + dst += dma_size; + buf += dma_size; + /* Copy any leftover data */ + if (tail) + memcpy(dst, buf, tail); + } else { + memcpy(dst, buf, count); + } + } + + if (!err) + *ppos += count; + + return (err) ? err : count; +} + static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { DEFINE_WAIT(wait); @@ -708,6 +790,9 @@ static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv) else var->pixclock = pixclock; + itv->osd_rect.width = var->xres; + itv->osd_rect.height = var->yres; + IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", var->xres, var->yres, var->xres_virtual, var->yres_virtual, @@ -824,6 +909,7 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info) static struct fb_ops ivtvfb_ops = { .owner = THIS_MODULE, + .fb_write = ivtvfb_write, .fb_check_var = ivtvfb_check_var, .fb_set_par = ivtvfb_set_par, .fb_setcolreg = ivtvfb_setcolreg, diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c index 8e0160d275c..39bf6b114d5 100644 --- a/drivers/media/video/m52790.c +++ b/drivers/media/video/m52790.c @@ -171,4 +171,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .remove = m52790_remove, .id_table = m52790_id, }; - diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index e7ccbc895d7..2fb5854cf6f 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -1253,7 +1253,7 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) return 0; } -static int vidioc_enum_fmt_cap(struct file *file, void *fh, +static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) { if (f->index > 1) @@ -1283,7 +1283,7 @@ static int vidioc_enum_fmt_cap(struct file *file, void *fh, return 0; } -static int vidioc_try_fmt_cap(struct file *file, void *fh, +static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1316,7 +1316,8 @@ static int vidioc_try_fmt_cap(struct file *file, void *fh, return 0; } -static int vidioc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) { if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1346,7 +1347,8 @@ static int vidioc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) return 0; } -static int vidioc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, + struct v4l2_format *f) { if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1709,10 +1711,10 @@ static struct video_device meye_template = { .vidioc_queryctrl = vidioc_queryctrl, .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, - .vidioc_try_fmt_cap = vidioc_try_fmt_cap, - .vidioc_g_fmt_cap = vidioc_g_fmt_cap, - .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 310dbaba55f..5691e019d19 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -110,6 +110,7 @@ MODULE_PARM_DESC(dolby, "Activates Dolby processsing"); /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x80 >> 1, 0x88 >> 1, I2C_CLIENT_END }; + I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ @@ -333,7 +334,6 @@ void msp_set_audio(struct i2c_client *client) /* ------------------------------------------------------------------------ */ - static void msp_wake_thread(struct i2c_client *client) { struct msp_state *state = i2c_get_clientdata(client); @@ -1004,7 +1004,6 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .id_table = msp_id, }; - /* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c index 7f556859279..1622f70e4dd 100644 --- a/drivers/media/video/msp3400-kthreads.c +++ b/drivers/media/video/msp3400-kthreads.c @@ -480,7 +480,6 @@ int msp3400c_thread(void *data) struct msp3400c_carrier_detect *cd; int count, max1, max2, val1, val2, val, i; - v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n"); set_freezable(); for (;;) { diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 1658fe59039..b31ba4e0932 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -815,7 +815,6 @@ static int mt9v022_remove(struct i2c_client *client) return 0; } - static const struct i2c_device_id mt9v022_id[] = { { "mt9v022", 0 }, { } diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index d7bfd30f74a..ea032f5f2f4 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -682,17 +682,17 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, for (index = 0; index < N_OV7670_FMTS; index++) if (ov7670_formats[index].pixelformat == pix->pixelformat) break; - if (index >= N_OV7670_FMTS) - return -EINVAL; + if (index >= N_OV7670_FMTS) { + /* default to first format */ + index = 0; + pix->pixelformat = ov7670_formats[0].pixelformat; + } if (ret_fmt != NULL) *ret_fmt = ov7670_formats + index; /* * Fields: the OV devices claim to be progressive. */ - if (pix->field == V4L2_FIELD_ANY) - pix->field = V4L2_FIELD_NONE; - else if (pix->field != V4L2_FIELD_NONE) - return -EINVAL; + pix->field = V4L2_FIELD_NONE; /* * Round requested image size down to the nearest * we support, but not below the smallest. @@ -833,7 +833,7 @@ static int ov7670_store_cmatrix(struct i2c_client *client, int matrix[CMATRIX_LEN]) { int i, ret; - unsigned char signbits; + unsigned char signbits = 0; /* * Weird crap seems to exist in the upper part of @@ -1009,7 +1009,7 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) static int ov7670_t_brightness(struct i2c_client *client, int value) { - unsigned char com8, v; + unsigned char com8 = 0, v; int ret; ov7670_read(client, REG_COM8, &com8); @@ -1022,7 +1022,7 @@ static int ov7670_t_brightness(struct i2c_client *client, int value) static int ov7670_q_brightness(struct i2c_client *client, __s32 *value) { - unsigned char v; + unsigned char v = 0; int ret = ov7670_read(client, REG_BRIGHT, &v); *value = ov7670_sm_to_abs(v); @@ -1036,7 +1036,7 @@ static int ov7670_t_contrast(struct i2c_client *client, int value) static int ov7670_q_contrast(struct i2c_client *client, __s32 *value) { - unsigned char v; + unsigned char v = 0; int ret = ov7670_read(client, REG_CONTRAS, &v); *value = v; @@ -1046,7 +1046,7 @@ static int ov7670_q_contrast(struct i2c_client *client, __s32 *value) static int ov7670_q_hflip(struct i2c_client *client, __s32 *value) { int ret; - unsigned char v; + unsigned char v = 0; ret = ov7670_read(client, REG_MVFP, &v); *value = (v & MVFP_MIRROR) == MVFP_MIRROR; @@ -1056,7 +1056,7 @@ static int ov7670_q_hflip(struct i2c_client *client, __s32 *value) static int ov7670_t_hflip(struct i2c_client *client, int value) { - unsigned char v; + unsigned char v = 0; int ret; ret = ov7670_read(client, REG_MVFP, &v); @@ -1074,7 +1074,7 @@ static int ov7670_t_hflip(struct i2c_client *client, int value) static int ov7670_q_vflip(struct i2c_client *client, __s32 *value) { int ret; - unsigned char v; + unsigned char v = 0; ret = ov7670_read(client, REG_MVFP, &v); *value = (v & MVFP_FLIP) == MVFP_FLIP; @@ -1084,7 +1084,7 @@ static int ov7670_q_vflip(struct i2c_client *client, __s32 *value) static int ov7670_t_vflip(struct i2c_client *client, int value) { - unsigned char v; + unsigned char v = 0; int ret; ret = ov7670_read(client, REG_MVFP, &v); diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c index 8063e33f1c8..065c2454113 100644 --- a/drivers/media/video/ovcamchip/ovcamchip_core.c +++ b/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -297,7 +297,6 @@ static int ovcamchip_attach(struct i2c_adapter *adap) switch (adap->id) { case I2C_HW_SMBUS_OV511: case I2C_HW_SMBUS_OV518: - case I2C_HW_SMBUS_OVFX2: case I2C_HW_SMBUS_W9968CF: PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id); break; diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c index 8d859ccd48e..cdedaa55f15 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.h b/drivers/media/video/pvrusb2/pvrusb2-audio.h index 536339b6884..ac54eed3721 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.h +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c index 73dcb1c57ae..7c19ff72e6b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-context.c +++ b/drivers/media/video/pvrusb2/pvrusb2-context.c @@ -1,5 +1,4 @@ /* - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h index 745e270233c..61801291c2a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-context.h +++ b/drivers/media/video/pvrusb2/pvrusb2-context.h @@ -1,5 +1,4 @@ /* - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index 91a42f2473a..0764fbfffb7 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h index c1680053cd6..0371ae6e6e4 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h +++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index 29d50597c88..895859ec495 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h index 54b2844e7a7..66abf77f51f 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h index 707d2d9635d..be79249f862 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debug.h +++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h @@ -1,5 +1,4 @@ /* - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index b53121c78ff..ca892fb78a5 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h index 990b02d35d3..e24ff59f860 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 5bf6d8fda1f..5d036e7e3f0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2007 Mike Isely <isely@pobox.com> * @@ -182,7 +181,7 @@ static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap) return 0; } -struct pvr2_dvb_props pvr2_onair_creator_fe_props = { +static struct pvr2_dvb_props pvr2_onair_creator_fe_props = { .frontend_attach = pvr2_lgdt3303_attach, .tuner_attach = pvr2_lgh06xf_attach, }; @@ -242,7 +241,7 @@ static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap) return 0; } -struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { +static struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { .frontend_attach = pvr2_lgdt3302_attach, .tuner_attach = pvr2_fcv1236d_attach, }; @@ -315,7 +314,7 @@ static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) return 0; } -struct pvr2_dvb_props pvr2_73xxx_dvb_props = { +static struct pvr2_dvb_props pvr2_73xxx_dvb_props = { .frontend_attach = pvr2_tda10048_attach, .tuner_attach = pvr2_73xxx_tda18271_8295_attach, }; @@ -418,12 +417,12 @@ static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) return 0; } -struct pvr2_dvb_props pvr2_750xx_dvb_props = { +static struct pvr2_dvb_props pvr2_750xx_dvb_props = { .frontend_attach = pvr2_s5h1409_attach, .tuner_attach = pvr2_tda18271_8295_attach, }; -struct pvr2_dvb_props pvr2_751xx_dvb_props = { +static struct pvr2_dvb_props pvr2_751xx_dvb_props = { .frontend_attach = pvr2_s5h1411_attach, .tuner_attach = pvr2_tda18271_8295_attach, }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h index d016f8b6c70..e23ce1d2edd 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c index 5ef005947b0..299afa4fa96 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.h b/drivers/media/video/pvrusb2/pvrusb2-eeprom.h index 84242975dea..cca3216f94c 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-eeprom.h +++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c index c46d367f747..a1252d673b4 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.h b/drivers/media/video/pvrusb2/pvrusb2-encoder.h index 54caf2e3c42..232fefbcd1a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.h +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h index abaada31e66..b58369e7f30 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h +++ b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2007 Michael Krufky <mkrufky@linuxtv.org> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index a3fe251d6fd..657f861593b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 0a868888f38..a5217a2cf4c 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -40,6 +39,23 @@ #define TV_MIN_FREQ 55250000L #define TV_MAX_FREQ 850000000L +/* This defines a minimum interval that the decoder must remain quiet + before we are allowed to start it running. */ +#define TIME_MSEC_DECODER_WAIT 50 + +/* This defines a minimum interval that the encoder must remain quiet + before we are allowed to configure it. I had this originally set to + 50msec, but Martin Dauskardt <martin.dauskardt@gmx.de> reports that + things work better when it's set to 100msec. */ +#define TIME_MSEC_ENCODER_WAIT 100 + +/* This defines the minimum interval that the encoder must successfully run + before we consider that the encoder has run at least once since its + firmware has been loaded. This measurement is in important for cases + where we can't do something until we know that the encoder has been run + at least once. */ +#define TIME_MSEC_ENCODER_OK 250 + static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL}; static DEFINE_MUTEX(pvr2_unit_mtx); @@ -67,6 +83,16 @@ MODULE_PARM_DESC(video_std,"specify initial video standard"); module_param_array(tolerance, int, NULL, 0444); MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); +/* US Broadcast channel 7 (175.25 MHz) */ +static int default_tv_freq = 175250000L; +/* 104.3 MHz, a usable FM station for my area */ +static int default_radio_freq = 104300000L; + +module_param_named(tv_freq, default_tv_freq, int, 0444); +MODULE_PARM_DESC(tv_freq, "specify initial television frequency"); +module_param_named(radio_freq, default_radio_freq, int, 0444); +MODULE_PARM_DESC(radio_freq, "specify initial radio frequency"); + #define PVR2_CTL_WRITE_ENDPOINT 0x01 #define PVR2_CTL_READ_ENDPOINT 0x81 @@ -1701,10 +1727,8 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) are, but I set them to something usable in the Chicago area just to make driver testing a little easier. */ - /* US Broadcast channel 7 (175.25 MHz) */ - hdw->freqValTelevision = 175250000L; - /* 104.3 MHz, a usable FM station for my area */ - hdw->freqValRadio = 104300000L; + hdw->freqValTelevision = default_tv_freq; + hdw->freqValRadio = default_radio_freq; // Do not use pvr2_reset_ctl_endpoints() here. It is not // thread-safe against the normal pvr2_send_request() mechanism. @@ -1989,7 +2013,8 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, case V4L2_CTRL_TYPE_MENU: ciptr->type = pvr2_ctl_enum; ciptr->def.type_enum.value_names = - cx2341x_ctrl_get_menu(ciptr->v4l_id); + cx2341x_ctrl_get_menu(&hdw->enc_ctl_state, + ciptr->v4l_id); for (cnt1 = 0; ciptr->def.type_enum.value_names[cnt1] != NULL; cnt1++) { } @@ -2428,22 +2453,38 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) struct pvr2_ctrl *cptr; int disruptive_change; - /* When video standard changes, reset the hres and vres values - - but if the user has pending changes there, then let the changes - take priority. */ + /* Handle some required side effects when the video standard is + changed.... */ if (hdw->std_dirty) { - /* Rewrite the vertical resolution to be appropriate to the - video standard that has been selected. */ int nvres; + int gop_size; if (hdw->std_mask_cur & V4L2_STD_525_60) { nvres = 480; + gop_size = 15; } else { nvres = 576; + gop_size = 12; } + /* Rewrite the vertical resolution to be appropriate to the + video standard that has been selected. */ if (nvres != hdw->res_ver_val) { hdw->res_ver_val = nvres; hdw->res_ver_dirty = !0; } + /* Rewrite the GOP size to be appropriate to the video + standard that has been selected. */ + if (gop_size != hdw->enc_ctl_state.video_gop_size) { + struct v4l2_ext_controls cs; + struct v4l2_ext_control c1; + memset(&cs, 0, sizeof(cs)); + memset(&c1, 0, sizeof(c1)); + cs.controls = &c1; + cs.count = 1; + c1.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE; + c1.value = gop_size; + cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs, + VIDIOC_S_EXT_CTRLS); + } } if (hdw->input_dirty && hdw->state_pathway_ok && @@ -3421,7 +3462,7 @@ static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) } -void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff) +static void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff) { /* change some GPIO data * @@ -3601,7 +3642,9 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw) the encoder. */ if (!hdw->state_encoder_waitok) { hdw->encoder_wait_timer.expires = - jiffies + (HZ*50/1000); + jiffies + + (HZ * TIME_MSEC_ENCODER_WAIT + / 1000); add_timer(&hdw->encoder_wait_timer); } } @@ -3725,7 +3768,7 @@ static int state_eval_encoder_run(struct pvr2_hdw *hdw) hdw->state_encoder_run = !0; if (!hdw->state_encoder_runok) { hdw->encoder_run_timer.expires = - jiffies + (HZ*250/1000); + jiffies + (HZ * TIME_MSEC_ENCODER_OK / 1000); add_timer(&hdw->encoder_run_timer); } } @@ -3800,7 +3843,9 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) but before we did the pending check. */ if (!hdw->state_decoder_quiescent) { hdw->quiescent_timer.expires = - jiffies + (HZ*50/1000); + jiffies + + (HZ * TIME_MSEC_DECODER_WAIT + / 1000); add_timer(&hdw->quiescent_timer); } } diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 20295e0c199..c04956d304a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index 49773764383..ccdb429fc7a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index c650e02ccd0..55f04a0b204 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h index c838df6167f..7fa38683b3b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 793c89a8d67..9d3c18b2474 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h index bd0807b905b..6ef7a1c0e93 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.c b/drivers/media/video/pvrusb2/pvrusb2-io.c index 7aff8b72006..20b6ae0bb40 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-io.c +++ b/drivers/media/video/pvrusb2/pvrusb2-io.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.h b/drivers/media/video/pvrusb2/pvrusb2-io.h index 42fcf8281a8..afb7e87c039 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-io.h +++ b/drivers/media/video/pvrusb2/pvrusb2-io.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c index c572212c9f1..05a1376405e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.h b/drivers/media/video/pvrusb2/pvrusb2-ioread.h index 1d362f83358..100e0780e1a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ioread.h +++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c index 332aced8a5a..ad0d98c2ebb 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c index fdc5a2b49ca..ca9f83a85ca 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-std.c +++ b/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.h b/drivers/media/video/pvrusb2/pvrusb2-std.h index 07c39937534..a35c53d0b32 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-std.h +++ b/drivers/media/video/pvrusb2/pvrusb2-std.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 0ff7a836a8a..46a8c39ba03 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * @@ -71,6 +70,7 @@ struct pvr2_sysfs_ctl_item { struct device_attribute attr_val; struct device_attribute attr_custom; struct pvr2_ctrl *cptr; + int ctl_id; struct pvr2_sysfs *chptr; struct pvr2_sysfs_ctl_item *item_next; struct attribute *attr_gen[7]; @@ -83,38 +83,29 @@ struct pvr2_sysfs_class { struct class class; }; -static ssize_t show_name(int id,struct device *class_dev,char *buf) +static ssize_t show_name(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; const char *name; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - - name = pvr2_ctrl_get_desc(cptr); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",sfp,id,name); - + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_name); + name = pvr2_ctrl_get_desc(cip->cptr); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s", + cip->chptr, cip->ctl_id, name); if (!name) return -EINVAL; - - return scnprintf(buf,PAGE_SIZE,"%s\n",name); + return scnprintf(buf, PAGE_SIZE, "%s\n", name); } -static ssize_t show_type(int id,struct device *class_dev,char *buf) +static ssize_t show_type(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; const char *name; enum pvr2_ctl_type tp; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - - tp = pvr2_ctrl_get_type(cptr); + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_type); + tp = pvr2_ctrl_get_type(cip->cptr); switch (tp) { case pvr2_ctl_int: name = "integer"; break; case pvr2_ctl_enum: name = "enum"; break; @@ -122,403 +113,178 @@ static ssize_t show_type(int id,struct device *class_dev,char *buf) case pvr2_ctl_bool: name = "boolean"; break; default: name = "?"; break; } - pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s",sfp,id,name); - + pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s", + cip->chptr, cip->ctl_id, name); if (!name) return -EINVAL; - - return scnprintf(buf,PAGE_SIZE,"%s\n",name); + return scnprintf(buf, PAGE_SIZE, "%s\n", name); } -static ssize_t show_min(int id,struct device *class_dev,char *buf) +static ssize_t show_min(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; long val; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - val = pvr2_ctrl_get_min(cptr); - - pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",sfp,id,val); - - return scnprintf(buf,PAGE_SIZE,"%ld\n",val); + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_min); + val = pvr2_ctrl_get_min(cip->cptr); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld", + cip->chptr, cip->ctl_id, val); + return scnprintf(buf, PAGE_SIZE, "%ld\n", val); } -static ssize_t show_max(int id,struct device *class_dev,char *buf) +static ssize_t show_max(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; long val; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - val = pvr2_ctrl_get_max(cptr); - - pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",sfp,id,val); - - return scnprintf(buf,PAGE_SIZE,"%ld\n",val); + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_max); + val = pvr2_ctrl_get_max(cip->cptr); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld", + cip->chptr, cip->ctl_id, val); + return scnprintf(buf, PAGE_SIZE, "%ld\n", val); } -static ssize_t show_val_norm(int id,struct device *class_dev,char *buf) +static ssize_t show_val_norm(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; - int val,ret; + struct pvr2_sysfs_ctl_item *cip; + int val; + int ret; unsigned int cnt = 0; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - - ret = pvr2_ctrl_get_value(cptr,&val); + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); + ret = pvr2_ctrl_get_value(cip->cptr, &val); if (ret < 0) return ret; - - ret = pvr2_ctrl_value_to_sym(cptr,~0,val, - buf,PAGE_SIZE-1,&cnt); - + ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val, + buf, PAGE_SIZE - 1, &cnt); pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)", - sfp,id,cnt,buf,val); + cip->chptr, cip->ctl_id, cnt, buf, val); buf[cnt] = '\n'; return cnt+1; } -static ssize_t show_val_custom(int id,struct device *class_dev,char *buf) +static ssize_t show_val_custom(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; - int val,ret; + struct pvr2_sysfs_ctl_item *cip; + int val; + int ret; unsigned int cnt = 0; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - - ret = pvr2_ctrl_get_value(cptr,&val); + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); + ret = pvr2_ctrl_get_value(cip->cptr, &val); if (ret < 0) return ret; - - ret = pvr2_ctrl_custom_value_to_sym(cptr,~0,val, - buf,PAGE_SIZE-1,&cnt); - + ret = pvr2_ctrl_custom_value_to_sym(cip->cptr, ~0, val, + buf, PAGE_SIZE - 1, &cnt); pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)", - sfp,id,cnt,buf,val); + cip->chptr, cip->ctl_id, cnt, buf, val); buf[cnt] = '\n'; return cnt+1; } -static ssize_t show_enum(int id,struct device *class_dev,char *buf) +static ssize_t show_enum(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; long val; - unsigned int bcnt,ccnt,ecnt; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - ecnt = pvr2_ctrl_get_cnt(cptr); + unsigned int bcnt, ccnt, ecnt; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_enum); + ecnt = pvr2_ctrl_get_cnt(cip->cptr); bcnt = 0; for (val = 0; val < ecnt; val++) { - pvr2_ctrl_get_valname(cptr,val,buf+bcnt,PAGE_SIZE-bcnt,&ccnt); + pvr2_ctrl_get_valname(cip->cptr, val, buf + bcnt, + PAGE_SIZE - bcnt, &ccnt); if (!ccnt) continue; bcnt += ccnt; if (bcnt >= PAGE_SIZE) break; buf[bcnt] = '\n'; bcnt++; } - pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",sfp,id); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)", + cip->chptr, cip->ctl_id); return bcnt; } -static ssize_t show_bits(int id,struct device *class_dev,char *buf) +static ssize_t show_bits(struct device *class_dev, + struct device_attribute *attr, + char *buf) { - struct pvr2_ctrl *cptr; - struct pvr2_sysfs *sfp; - int valid_bits,msk; - unsigned int bcnt,ccnt; - - sfp = (struct pvr2_sysfs *)class_dev->driver_data; - if (!sfp) return -EINVAL; - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); - if (!cptr) return -EINVAL; - valid_bits = pvr2_ctrl_get_mask(cptr); + struct pvr2_sysfs_ctl_item *cip; + int valid_bits, msk; + unsigned int bcnt, ccnt; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_bits); + valid_bits = pvr2_ctrl_get_mask(cip->cptr); bcnt = 0; for (msk = 1; valid_bits; msk <<= 1) { if (!(msk & valid_bits)) continue; valid_bits &= ~msk; - pvr2_ctrl_get_valname(cptr,msk,buf+bcnt,PAGE_SIZE-bcnt,&ccnt); + pvr2_ctrl_get_valname(cip->cptr, msk, buf + bcnt, + PAGE_SIZE - bcnt, &ccnt); bcnt += ccnt; if (bcnt >= PAGE_SIZE) break; buf[bcnt] = '\n'; bcnt++; } - pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",sfp,id); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)", + cip->chptr, cip->ctl_id); return bcnt; } -static int store_val_any(int id,int customfl,struct pvr2_sysfs *sfp, +static int store_val_any(struct pvr2_sysfs_ctl_item *cip, int customfl, const char *buf,unsigned int count) { - struct pvr2_ctrl *cptr; int ret; int mask,val; - - cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); if (customfl) { - ret = pvr2_ctrl_custom_sym_to_value(cptr,buf,count,&mask,&val); + ret = pvr2_ctrl_custom_sym_to_value(cip->cptr, buf, count, + &mask, &val); } else { - ret = pvr2_ctrl_sym_to_value(cptr,buf,count,&mask,&val); + ret = pvr2_ctrl_sym_to_value(cip->cptr, buf, count, + &mask, &val); } if (ret < 0) return ret; - ret = pvr2_ctrl_set_mask_value(cptr,mask,val); - pvr2_hdw_commit_ctl(sfp->channel.hdw); + ret = pvr2_ctrl_set_mask_value(cip->cptr, mask, val); + pvr2_hdw_commit_ctl(cip->chptr->channel.hdw); return ret; } -static ssize_t store_val_norm(int id,struct device *class_dev, - const char *buf,size_t count) +static ssize_t store_val_norm(struct device *class_dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; int ret; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val); pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"", - sfp,id,(int)count,buf); - ret = store_val_any(id,0,sfp,buf,count); + cip->chptr, cip->ctl_id, (int)count, buf); + ret = store_val_any(cip, 0, buf, count); if (!ret) ret = count; return ret; } -static ssize_t store_val_custom(int id,struct device *class_dev, - const char *buf,size_t count) +static ssize_t store_val_custom(struct device *class_dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct pvr2_sysfs *sfp; + struct pvr2_sysfs_ctl_item *cip; int ret; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom); pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"", - sfp,id,(int)count,buf); - ret = store_val_any(id,1,sfp,buf,count); + cip->chptr, cip->ctl_id, (int)count, buf); + ret = store_val_any(cip, 1, buf, count); if (!ret) ret = count; return ret; } -/* - Mike Isely <isely@pobox.com> 30-April-2005 - - This next batch of horrible preprocessor hackery is needed because the - kernel's device_attribute mechanism fails to pass the actual - attribute through to the show / store functions, which means we have no - way to package up any attribute-specific parameters, like for example the - control id. So we work around this brain-damage by encoding the control - id into the show / store functions themselves and pick the function based - on the control id we're setting up. These macros try to ease the pain. - Yuck. -*/ - -#define CREATE_SHOW_INSTANCE(sf_name,ctl_id) \ -static ssize_t sf_name##_##ctl_id(struct device *class_dev, \ -struct device_attribute *attr, char *buf) \ -{ return sf_name(ctl_id,class_dev,buf); } - -#define CREATE_STORE_INSTANCE(sf_name,ctl_id) \ -static ssize_t sf_name##_##ctl_id(struct device *class_dev, \ -struct device_attribute *attr, const char *buf, size_t count) \ -{ return sf_name(ctl_id,class_dev,buf,count); } - -#define CREATE_BATCH(ctl_id) \ -CREATE_SHOW_INSTANCE(show_name,ctl_id) \ -CREATE_SHOW_INSTANCE(show_type,ctl_id) \ -CREATE_SHOW_INSTANCE(show_min,ctl_id) \ -CREATE_SHOW_INSTANCE(show_max,ctl_id) \ -CREATE_SHOW_INSTANCE(show_val_norm,ctl_id) \ -CREATE_SHOW_INSTANCE(show_val_custom,ctl_id) \ -CREATE_SHOW_INSTANCE(show_enum,ctl_id) \ -CREATE_SHOW_INSTANCE(show_bits,ctl_id) \ -CREATE_STORE_INSTANCE(store_val_norm,ctl_id) \ -CREATE_STORE_INSTANCE(store_val_custom,ctl_id) \ - -CREATE_BATCH(0) -CREATE_BATCH(1) -CREATE_BATCH(2) -CREATE_BATCH(3) -CREATE_BATCH(4) -CREATE_BATCH(5) -CREATE_BATCH(6) -CREATE_BATCH(7) -CREATE_BATCH(8) -CREATE_BATCH(9) -CREATE_BATCH(10) -CREATE_BATCH(11) -CREATE_BATCH(12) -CREATE_BATCH(13) -CREATE_BATCH(14) -CREATE_BATCH(15) -CREATE_BATCH(16) -CREATE_BATCH(17) -CREATE_BATCH(18) -CREATE_BATCH(19) -CREATE_BATCH(20) -CREATE_BATCH(21) -CREATE_BATCH(22) -CREATE_BATCH(23) -CREATE_BATCH(24) -CREATE_BATCH(25) -CREATE_BATCH(26) -CREATE_BATCH(27) -CREATE_BATCH(28) -CREATE_BATCH(29) -CREATE_BATCH(30) -CREATE_BATCH(31) -CREATE_BATCH(32) -CREATE_BATCH(33) -CREATE_BATCH(34) -CREATE_BATCH(35) -CREATE_BATCH(36) -CREATE_BATCH(37) -CREATE_BATCH(38) -CREATE_BATCH(39) -CREATE_BATCH(40) -CREATE_BATCH(41) -CREATE_BATCH(42) -CREATE_BATCH(43) -CREATE_BATCH(44) -CREATE_BATCH(45) -CREATE_BATCH(46) -CREATE_BATCH(47) -CREATE_BATCH(48) -CREATE_BATCH(49) -CREATE_BATCH(50) -CREATE_BATCH(51) -CREATE_BATCH(52) -CREATE_BATCH(53) -CREATE_BATCH(54) -CREATE_BATCH(55) -CREATE_BATCH(56) -CREATE_BATCH(57) -CREATE_BATCH(58) -CREATE_BATCH(59) - -struct pvr2_sysfs_func_set { - ssize_t (*show_name)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_type)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_min)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_max)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_enum)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_bits)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*show_val_norm)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*store_val_norm)(struct device *, - struct device_attribute *attr, - const char *,size_t); - ssize_t (*show_val_custom)(struct device *, - struct device_attribute *attr, char *); - ssize_t (*store_val_custom)(struct device *, - struct device_attribute *attr, - const char *,size_t); -}; - -#define INIT_BATCH(ctl_id) \ -[ctl_id] = { \ - .show_name = show_name_##ctl_id, \ - .show_type = show_type_##ctl_id, \ - .show_min = show_min_##ctl_id, \ - .show_max = show_max_##ctl_id, \ - .show_enum = show_enum_##ctl_id, \ - .show_bits = show_bits_##ctl_id, \ - .show_val_norm = show_val_norm_##ctl_id, \ - .store_val_norm = store_val_norm_##ctl_id, \ - .show_val_custom = show_val_custom_##ctl_id, \ - .store_val_custom = store_val_custom_##ctl_id, \ -} \ - -static struct pvr2_sysfs_func_set funcs[] = { - INIT_BATCH(0), - INIT_BATCH(1), - INIT_BATCH(2), - INIT_BATCH(3), - INIT_BATCH(4), - INIT_BATCH(5), - INIT_BATCH(6), - INIT_BATCH(7), - INIT_BATCH(8), - INIT_BATCH(9), - INIT_BATCH(10), - INIT_BATCH(11), - INIT_BATCH(12), - INIT_BATCH(13), - INIT_BATCH(14), - INIT_BATCH(15), - INIT_BATCH(16), - INIT_BATCH(17), - INIT_BATCH(18), - INIT_BATCH(19), - INIT_BATCH(20), - INIT_BATCH(21), - INIT_BATCH(22), - INIT_BATCH(23), - INIT_BATCH(24), - INIT_BATCH(25), - INIT_BATCH(26), - INIT_BATCH(27), - INIT_BATCH(28), - INIT_BATCH(29), - INIT_BATCH(30), - INIT_BATCH(31), - INIT_BATCH(32), - INIT_BATCH(33), - INIT_BATCH(34), - INIT_BATCH(35), - INIT_BATCH(36), - INIT_BATCH(37), - INIT_BATCH(38), - INIT_BATCH(39), - INIT_BATCH(40), - INIT_BATCH(41), - INIT_BATCH(42), - INIT_BATCH(43), - INIT_BATCH(44), - INIT_BATCH(45), - INIT_BATCH(46), - INIT_BATCH(47), - INIT_BATCH(48), - INIT_BATCH(49), - INIT_BATCH(50), - INIT_BATCH(51), - INIT_BATCH(52), - INIT_BATCH(53), - INIT_BATCH(54), - INIT_BATCH(55), - INIT_BATCH(56), - INIT_BATCH(57), - INIT_BATCH(58), - INIT_BATCH(59), -}; - - static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) { struct pvr2_sysfs_ctl_item *cip; - struct pvr2_sysfs_func_set *fp; struct pvr2_ctrl *cptr; unsigned int cnt,acnt; int ret; - if ((ctl_id < 0) || (ctl_id >= ARRAY_SIZE(funcs))) { - return; - } - - fp = funcs + ctl_id; cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id); if (!cptr) return; @@ -527,6 +293,7 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip); cip->cptr = cptr; + cip->ctl_id = ctl_id; cip->chptr = sfp; cip->item_next = NULL; @@ -539,19 +306,19 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_name.attr.name = "name"; cip->attr_name.attr.mode = S_IRUGO; - cip->attr_name.show = fp->show_name; + cip->attr_name.show = show_name; cip->attr_type.attr.name = "type"; cip->attr_type.attr.mode = S_IRUGO; - cip->attr_type.show = fp->show_type; + cip->attr_type.show = show_type; cip->attr_min.attr.name = "min_val"; cip->attr_min.attr.mode = S_IRUGO; - cip->attr_min.show = fp->show_min; + cip->attr_min.show = show_min; cip->attr_max.attr.name = "max_val"; cip->attr_max.attr.mode = S_IRUGO; - cip->attr_max.show = fp->show_max; + cip->attr_max.show = show_max; cip->attr_val.attr.name = "cur_val"; cip->attr_val.attr.mode = S_IRUGO; @@ -561,11 +328,11 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_enum.attr.name = "enum_val"; cip->attr_enum.attr.mode = S_IRUGO; - cip->attr_enum.show = fp->show_enum; + cip->attr_enum.show = show_enum; cip->attr_bits.attr.name = "bit_val"; cip->attr_bits.attr.mode = S_IRUGO; - cip->attr_bits.show = fp->show_bits; + cip->attr_bits.show = show_bits; if (pvr2_ctrl_is_writable(cptr)) { cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP; @@ -576,12 +343,12 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_gen[acnt++] = &cip->attr_name.attr; cip->attr_gen[acnt++] = &cip->attr_type.attr; cip->attr_gen[acnt++] = &cip->attr_val.attr; - cip->attr_val.show = fp->show_val_norm; - cip->attr_val.store = fp->store_val_norm; + cip->attr_val.show = show_val_norm; + cip->attr_val.store = store_val_norm; if (pvr2_ctrl_has_custom_symbols(cptr)) { cip->attr_gen[acnt++] = &cip->attr_custom.attr; - cip->attr_custom.show = fp->show_val_custom; - cip->attr_custom.store = fp->store_val_custom; + cip->attr_custom.show = show_val_custom; + cip->attr_custom.store = store_val_custom; } switch (pvr2_ctrl_get_type(cptr)) { case pvr2_ctl_enum: diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.h b/drivers/media/video/pvrusb2/pvrusb2-sysfs.h index ff9373b47f8..6d875bfe799 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.h +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/drivers/media/video/pvrusb2/pvrusb2-tuner.c index 05e65ce2e3a..07775d1aad4 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-tuner.c +++ b/drivers/media/video/pvrusb2/pvrusb2-tuner.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.h b/drivers/media/video/pvrusb2/pvrusb2-tuner.h index 556f12aa916..ef4afaf37b0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-tuner.h +++ b/drivers/media/video/pvrusb2/pvrusb2-tuner.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-util.h b/drivers/media/video/pvrusb2/pvrusb2-util.h index e53aee416f5..92b75544ee2 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-util.h +++ b/drivers/media/video/pvrusb2/pvrusb2-util.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index e9b5d4e9132..0d72dc470fe 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-v4l2.h index 9a995e2d225..34c011a7b10 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.h +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index 2433a316004..4059648c705 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h index 2b917fda02e..4ff5b892b30 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h +++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c index 66b4d36ef76..f6fcf0ac611 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c +++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h index 8aaeff4e1e2..80709096125 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h +++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pvrusb2/pvrusb2.h b/drivers/media/video/pvrusb2/pvrusb2.h index 1a9a4baf12b..240de9b3566 100644 --- a/drivers/media/video/pvrusb2/pvrusb2.h +++ b/drivers/media/video/pvrusb2/pvrusb2.h @@ -1,6 +1,5 @@ /* * - * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index ea53316d211..1cccd5c7704 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -1255,7 +1255,6 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) exactly the same otherwise. */ - /* define local variable for arg */ #define ARG_DEF(ARG_type, ARG_name)\ ARG_type *ARG_name = arg; @@ -1268,7 +1267,6 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) /* copy local variable to arg */ #define ARG_OUT(ARG_name) /* nothing */ - int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) { int ret = 0; diff --git a/drivers/media/video/pwc/pwc-ioctl.h b/drivers/media/video/pwc/pwc-ioctl.h index cec66029976..8c0cae7b3da 100644 --- a/drivers/media/video/pwc/pwc-ioctl.h +++ b/drivers/media/video/pwc/pwc-ioctl.h @@ -54,7 +54,6 @@ #include <linux/types.h> #include <linux/version.h> - /* Enumeration of image sizes */ #define PSZ_SQCIF 0x00 #define PSZ_QSIF 0x01 diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 5ec5bb9a94d..b15f82c4976 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -30,6 +30,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-dev.h> +#include <media/videobuf-dma-sg.h> #include <media/soc_camera.h> #include <linux/videodev2.h> @@ -582,6 +583,19 @@ static struct videobuf_queue_ops pxa_videobuf_ops = { .buf_release = pxa_videobuf_release, }; +static void pxa_camera_init_videobuf(struct videobuf_queue *q, + struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + + /* We must pass NULL as dev pointer, then all pci_* dma operations + * transform to normal dma_* ones. */ + videobuf_queue_sg_init(q, &pxa_videobuf_ops, NULL, &pcdev->lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, + sizeof(struct pxa_buffer), icd); +} + static int mclk_get_divisor(struct pxa_camera_dev *pcdev) { unsigned int mclk_10khz = pcdev->platform_mclk_10khz; @@ -983,34 +997,23 @@ static int pxa_camera_querycap(struct soc_camera_host *ici, return 0; } -static spinlock_t *pxa_camera_spinlock_alloc(struct soc_camera_file *icf) -{ - struct soc_camera_host *ici = - to_soc_camera_host(icf->icd->dev.parent); - struct pxa_camera_dev *pcdev = ici->priv; - - return &pcdev->lock; -} - static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .owner = THIS_MODULE, .add = pxa_camera_add_device, .remove = pxa_camera_remove_device, .set_fmt_cap = pxa_camera_set_fmt_cap, .try_fmt_cap = pxa_camera_try_fmt_cap, + .init_videobuf = pxa_camera_init_videobuf, .reqbufs = pxa_camera_reqbufs, .poll = pxa_camera_poll, .querycap = pxa_camera_querycap, .try_bus_param = pxa_camera_try_bus_param, .set_bus_param = pxa_camera_set_bus_param, - .spinlock_alloc = pxa_camera_spinlock_alloc, }; /* Should be allocated dynamically too, but we have only one. */ static struct soc_camera_host pxa_soc_camera_host = { .drv_name = PXA_CAM_DRV_NAME, - .vbq_ops = &pxa_videobuf_ops, - .msize = sizeof(struct pxa_buffer), .ops = &pxa_soc_camera_host_ops, }; diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c new file mode 100644 index 00000000000..04eb2c3fabd --- /dev/null +++ b/drivers/media/video/s2255drv.c @@ -0,0 +1,2495 @@ +/* + * s2255drv.c - a driver for the Sensoray 2255 USB video capture device + * + * Copyright (C) 2007-2008 by Sensoray Company Inc. + * Dean Anderson + * + * Some video buffer code based on vivi driver: + * + * Sensoray 2255 device supports 4 simultaneous channels. + * The channels are not "crossbar" inputs, they are physically + * attached to separate video decoders. + * + * Because of USB2.0 bandwidth limitations. There is only a + * certain amount of data which may be transferred at one time. + * + * Example maximum bandwidth utilization: + * + * -full size, color mode YUYV or YUV422P: 2 channels at once + * + * -full or half size Grey scale: all 4 channels at once + * + * -half size, color mode YUYV or YUV422P: all 4 channels at once + * + * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels + * at once. + * (TODO: Incorporate videodev2 frame rate(FR) enumeration, + * which is currently experimental.) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/firmware.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/videodev2.h> +#include <linux/version.h> +#include <media/videobuf-vmalloc.h> +#include <media/v4l2-common.h> +#include <linux/vmalloc.h> +#include <linux/usb.h> + +#define FIRMWARE_FILE_NAME "f2255usb.bin" + + + +/* vendor request in */ +#define S2255_VR_IN 0 +/* vendor request out */ +#define S2255_VR_OUT 1 +/* firmware query */ +#define S2255_VR_FW 0x30 +/* USB endpoint number for configuring the device */ +#define S2255_CONFIG_EP 2 +/* maximum time for DSP to start responding after last FW word loaded(ms) */ +#define S2255_DSP_BOOTTIME 400 +/* maximum time to wait for firmware to load (ms) */ +#define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME) +#define S2255_DEF_BUFS 16 +#define MAX_CHANNELS 4 +#define FRAME_MARKER 0x2255DA4AL +#define MAX_PIPE_USBBLOCK (40 * 1024) +#define DEFAULT_PIPE_USBBLOCK (16 * 1024) +#define MAX_CHANNELS 4 +#define MAX_PIPE_BUFFERS 1 +#define SYS_FRAMES 4 +/* maximum size is PAL full size plus room for the marker header(s) */ +#define SYS_FRAMES_MAXSIZE (720 * 288 * 2 * 2 + 4096) +#define DEF_USB_BLOCK (4096) +#define LINE_SZ_4CIFS_NTSC 640 +#define LINE_SZ_2CIFS_NTSC 640 +#define LINE_SZ_1CIFS_NTSC 320 +#define LINE_SZ_4CIFS_PAL 704 +#define LINE_SZ_2CIFS_PAL 704 +#define LINE_SZ_1CIFS_PAL 352 +#define NUM_LINES_4CIFS_NTSC 240 +#define NUM_LINES_2CIFS_NTSC 240 +#define NUM_LINES_1CIFS_NTSC 240 +#define NUM_LINES_4CIFS_PAL 288 +#define NUM_LINES_2CIFS_PAL 288 +#define NUM_LINES_1CIFS_PAL 288 +#define LINE_SZ_DEF 640 +#define NUM_LINES_DEF 240 + + +/* predefined settings */ +#define FORMAT_NTSC 1 +#define FORMAT_PAL 2 + +#define SCALE_4CIFS 1 /* 640x480(NTSC) or 704x576(PAL) */ +#define SCALE_2CIFS 2 /* 640x240(NTSC) or 704x288(PAL) */ +#define SCALE_1CIFS 3 /* 320x240(NTSC) or 352x288(PAL) */ + +#define COLOR_YUVPL 1 /* YUV planar */ +#define COLOR_YUVPK 2 /* YUV packed */ +#define COLOR_Y8 4 /* monochrome */ + +/* frame decimation. Not implemented by V4L yet(experimental in V4L) */ +#define FDEC_1 1 /* capture every frame. default */ +#define FDEC_2 2 /* capture every 2nd frame */ +#define FDEC_3 3 /* capture every 3rd frame */ +#define FDEC_5 5 /* capture every 5th frame */ + +/*------------------------------------------------------- + * Default mode parameters. + *-------------------------------------------------------*/ +#define DEF_SCALE SCALE_4CIFS +#define DEF_COLOR COLOR_YUVPL +#define DEF_FDEC FDEC_1 +#define DEF_BRIGHT 0 +#define DEF_CONTRAST 0x5c +#define DEF_SATURATION 0x80 +#define DEF_HUE 0 + +/* usb config commands */ +#define IN_DATA_TOKEN 0x2255c0de +#define CMD_2255 0xc2255000 +#define CMD_SET_MODE (CMD_2255 | 0x10) +#define CMD_START (CMD_2255 | 0x20) +#define CMD_STOP (CMD_2255 | 0x30) +#define CMD_STATUS (CMD_2255 | 0x40) + +struct s2255_mode { + u32 format; /* input video format (NTSC, PAL) */ + u32 scale; /* output video scale */ + u32 color; /* output video color format */ + u32 fdec; /* frame decimation */ + u32 bright; /* brightness */ + u32 contrast; /* contrast */ + u32 saturation; /* saturation */ + u32 hue; /* hue (NTSC only)*/ + u32 single; /* capture 1 frame at a time (!=0), continuously (==0)*/ + u32 usb_block; /* block size. should be 4096 of DEF_USB_BLOCK */ + u32 restart; /* if DSP requires restart */ +}; + +/* frame structure */ +#define FRAME_STATE_UNUSED 0 +#define FRAME_STATE_FILLING 1 +#define FRAME_STATE_FULL 2 + + +struct s2255_framei { + unsigned long size; + + unsigned long ulState; /* ulState ==0 unused, 1 being filled, 2 full */ + void *lpvbits; /* image data */ + unsigned long cur_size; /* current data copied to it */ +}; + +/* image buffer structure */ +struct s2255_bufferi { + unsigned long dwFrames; /* number of frames in buffer */ + struct s2255_framei frame[SYS_FRAMES]; /* array of FRAME structures */ +}; + +#define DEF_MODEI_NTSC_CONT {FORMAT_NTSC, DEF_SCALE, DEF_COLOR, \ + DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \ + DEF_HUE, 0, DEF_USB_BLOCK, 0} + +struct s2255_dmaqueue { + struct list_head active; + /* thread for acquisition */ + struct task_struct *kthread; + int frame; + struct s2255_dev *dev; + int channel; +}; + +/* for firmware loading, fw_state */ +#define S2255_FW_NOTLOADED 0 +#define S2255_FW_LOADED_DSPWAIT 1 +#define S2255_FW_SUCCESS 2 +#define S2255_FW_FAILED 3 + +struct s2255_fw { + int fw_loaded; + int fw_size; + struct urb *fw_urb; + atomic_t fw_state; + void *pfw_data; + wait_queue_head_t wait_fw; + struct timer_list dsp_wait; + const struct firmware *fw; +}; + +struct s2255_pipeinfo { + u32 max_transfer_size; + u32 cur_transfer_size; + u8 *transfer_buffer; + u32 transfer_flags;; + u32 state; + u32 prev_state; + u32 urb_size; + void *stream_urb; + void *dev; /* back pointer to s2255_dev struct*/ + u32 err_count; + u32 buf_index; + u32 idx; + u32 priority_set; +}; + +struct s2255_fmt; /*forward declaration */ + +struct s2255_dev { + int frames; + int users[MAX_CHANNELS]; + struct mutex lock; + struct mutex open_lock; + int resources[MAX_CHANNELS]; + struct usb_device *udev; + struct usb_interface *interface; + u8 read_endpoint; + + struct s2255_dmaqueue vidq[MAX_CHANNELS]; + struct video_device *vdev[MAX_CHANNELS]; + struct list_head s2255_devlist; + struct timer_list timer; + struct s2255_fw *fw_data; + int board_num; + int is_open; + struct s2255_pipeinfo pipes[MAX_PIPE_BUFFERS]; + struct s2255_bufferi buffer[MAX_CHANNELS]; + struct s2255_mode mode[MAX_CHANNELS]; + const struct s2255_fmt *cur_fmt[MAX_CHANNELS]; + int cur_frame[MAX_CHANNELS]; + int last_frame[MAX_CHANNELS]; + u32 cc; /* current channel */ + int b_acquire[MAX_CHANNELS]; + unsigned long req_image_size[MAX_CHANNELS]; + int bad_payload[MAX_CHANNELS]; + unsigned long frame_count[MAX_CHANNELS]; + int frame_ready; + struct kref kref; + spinlock_t slock; +}; +#define to_s2255_dev(d) container_of(d, struct s2255_dev, kref) + +struct s2255_fmt { + char *name; + u32 fourcc; + int depth; +}; + +/* buffer for one video frame */ +struct s2255_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct s2255_fmt *fmt; +}; + +struct s2255_fh { + struct s2255_dev *dev; + unsigned int resources; + const struct s2255_fmt *fmt; + unsigned int width; + unsigned int height; + struct videobuf_queue vb_vidq; + enum v4l2_buf_type type; + int channel; + /* mode below is the desired mode. + mode in s2255_dev is the current mode that was last set */ + struct s2255_mode mode; +}; + +/* + * TODO: fixme S2255_MAX_USERS. Do not limit open driver handles. + * Limit V4L to one stream at a time. + */ +#define S2255_MAX_USERS 1 + +#define CUR_USB_FWVER 774 /* current cypress EEPROM firmware version */ +#define S2255_MAJOR_VERSION 1 +#define S2255_MINOR_VERSION 13 +#define S2255_RELEASE 0 +#define S2255_VERSION KERNEL_VERSION(S2255_MAJOR_VERSION, \ + S2255_MINOR_VERSION, \ + S2255_RELEASE) + +/* vendor ids */ +#define USB_S2255_VENDOR_ID 0x1943 +#define USB_S2255_PRODUCT_ID 0x2255 +#define S2255_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC) +/* frame prefix size (sent once every frame) */ +#define PREFIX_SIZE 512 + +/* Channels on box are in reverse order */ +static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0}; + +static LIST_HEAD(s2255_devlist); + +static int debug; +static int *s2255_debug = &debug; + +static int s2255_start_readpipe(struct s2255_dev *dev); +static void s2255_stop_readpipe(struct s2255_dev *dev); +static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn); +static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn); +static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, + int chn); +static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, + struct s2255_mode *mode); +static int s2255_board_shutdown(struct s2255_dev *dev); +static void s2255_exit_v4l(struct s2255_dev *dev); +static void s2255_fwload_start(struct s2255_dev *dev); + +#define dprintk(level, fmt, arg...) \ + do { \ + if (*s2255_debug >= (level)) { \ + printk(KERN_DEBUG "s2255: " fmt, ##arg); \ + } \ + } while (0) + + +static struct usb_driver s2255_driver; + + +/* Declare static vars that will be used as parameters */ +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ + +/* start video number */ +static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level(0-100) default 0"); +module_param(vid_limit, int, 0644); +MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)"); +module_param(video_nr, int, 0644); +MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)"); + +/* USB device table */ +static struct usb_device_id s2255_table[] = { + {USB_DEVICE(USB_S2255_VENDOR_ID, USB_S2255_PRODUCT_ID)}, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, s2255_table); + + +#define BUFFER_TIMEOUT msecs_to_jiffies(400) + +/* supported controls */ +static struct v4l2_queryctrl s2255_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = -127, + .maximum = 128, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = DEF_CONTRAST, + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = DEF_SATURATION, + .flags = 0, + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = DEF_HUE, + .flags = 0, + } +}; + +static int qctl_regs[ARRAY_SIZE(s2255_qctrl)]; + +/* image formats. */ +static const struct s2255_fmt formats[] = { + { + .name = "4:2:2, planar, YUV422P", + .fourcc = V4L2_PIX_FMT_YUV422P, + .depth = 16 + + }, { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16 + + }, { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16 + }, { + .name = "8bpp GREY", + .fourcc = V4L2_PIX_FMT_GREY, + .depth = 8 + } +}; + +static int norm_maxw(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL; +} + +static int norm_maxh(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2); +} + +static int norm_minw(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL; +} + +static int norm_minh(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL); +} + + +/* + * TODO: fixme: move YUV reordering to hardware + * converts 2255 planar format to yuyv or uyvy + */ +static void planar422p_to_yuv_packed(const unsigned char *in, + unsigned char *out, + int width, int height, + int fmt) +{ + unsigned char *pY; + unsigned char *pCb; + unsigned char *pCr; + unsigned long size = height * width; + unsigned int i; + pY = (unsigned char *)in; + pCr = (unsigned char *)in + height * width; + pCb = (unsigned char *)in + height * width + (height * width / 2); + for (i = 0; i < size * 2; i += 4) { + out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++; + out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++; + out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++; + out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++; + } + return; +} + + +/* kickstarts the firmware loading. from probe + */ +static void s2255_timer(unsigned long user_data) +{ + struct s2255_fw *data = (struct s2255_fw *)user_data; + dprintk(100, "s2255 timer\n"); + if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { + printk(KERN_ERR "s2255: can't submit urb\n"); + if (data->fw) { + release_firmware(data->fw); + data->fw = NULL; + } + return; + } +} + +/* called when DSP is up and running. DSP is guaranteed to + be running after S2255_DSP_BOOTTIME */ +static void s2255_dsp_running(unsigned long user_data) +{ + struct s2255_fw *data = (struct s2255_fw *)user_data; + dprintk(1, "dsp running\n"); + atomic_set(&data->fw_state, S2255_FW_SUCCESS); + wake_up(&data->wait_fw); + printk(KERN_INFO "s2255: firmware loaded successfully\n"); + return; +} + + +/* this loads the firmware asynchronously. + Originally this was done synchroously in probe. + But it is better to load it asynchronously here than block + inside the probe function. Blocking inside probe affects boot time. + FW loading is triggered by the timer in the probe function +*/ +static void s2255_fwchunk_complete(struct urb *urb) +{ + struct s2255_fw *data = urb->context; + struct usb_device *udev = urb->dev; + int len; + dprintk(100, "udev %p urb %p", udev, urb); + /* TODO: fixme. reflect change in status */ + if (urb->status) { + dev_err(&udev->dev, "URB failed with status %d", urb->status); + return; + } + if (data->fw_urb == NULL) { + dev_err(&udev->dev, "early disconncect\n"); + return; + } +#define CHUNK_SIZE 512 + /* all USB transfers must be done with continuous kernel memory. + can't allocate more than 128k in current linux kernel, so + upload the firmware in chunks + */ + if (data->fw_loaded < data->fw_size) { + len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ? + data->fw_size % CHUNK_SIZE : CHUNK_SIZE; + + if (len < CHUNK_SIZE) + memset(data->pfw_data, 0, CHUNK_SIZE); + + dprintk(100, "completed len %d, loaded %d \n", len, + data->fw_loaded); + + memcpy(data->pfw_data, + (char *) data->fw->data + data->fw_loaded, len); + + usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2), + data->pfw_data, CHUNK_SIZE, + s2255_fwchunk_complete, data); + if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { + dev_err(&udev->dev, "failed submit URB\n"); + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); + return; + } + data->fw_loaded += len; + } else { + init_timer(&data->dsp_wait); + data->dsp_wait.function = s2255_dsp_running; + data->dsp_wait.data = (unsigned long)data; + atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); + mod_timer(&data->dsp_wait, msecs_to_jiffies(S2255_DSP_BOOTTIME) + + jiffies); + } + dprintk(100, "2255 complete done\n"); + return; + +} + +static int s2255_got_frame(struct s2255_dev *dev, int chn) +{ + struct s2255_dmaqueue *dma_q = &dev->vidq[chn]; + struct s2255_buffer *buf; + unsigned long flags = 0; + int rc = 0; + dprintk(2, "wakeup: %p channel: %d\n", &dma_q, chn); + spin_lock_irqsave(&dev->slock, flags); + + if (list_empty(&dma_q->active)) { + dprintk(1, "No active queue to serve\n"); + rc = -1; + goto unlock; + } + buf = list_entry(dma_q->active.next, + struct s2255_buffer, vb.queue); + + if (!waitqueue_active(&buf->vb.done)) { + /* no one active */ + rc = -1; + goto unlock; + } + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + dprintk(100, "[%p/%d] wakeup\n", buf, buf->vb.i); + + s2255_fillbuff(dev, buf, dma_q->channel); + wake_up(&buf->vb.done); + dprintk(2, "wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); +unlock: + spin_unlock_irqrestore(&dev->slock, flags); + return 0; +} + + +static const struct s2255_fmt *format_by_fourcc(int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (-1 == formats[i].fourcc) + continue; + if (formats[i].fourcc == fourcc) + return formats + i; + } + return NULL; +} + + + + +/* video buffer vmalloc implementation based partly on VIVI driver which is + * Copyright (c) 2006 by + * Mauro Carvalho Chehab <mchehab--a.t--infradead.org> + * Ted Walther <ted--a.t--enumera.com> + * John Sokol <sokol--a.t--videotechnology.com> + * http://v4l.videotechnology.com/ + * + */ +static void s2255_fillbuff(struct s2255_dev *dev, struct s2255_buffer *buf, + int chn) +{ + int pos = 0; + struct timeval ts; + const char *tmpbuf; + char *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned long last_frame; + struct s2255_framei *frm; + + if (!vbuf) + return; + + last_frame = dev->last_frame[chn]; + if (last_frame != -1) { + frm = &dev->buffer[chn].frame[last_frame]; + tmpbuf = + (const char *)dev->buffer[chn].frame[last_frame].lpvbits; + switch (buf->fmt->fourcc) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + planar422p_to_yuv_packed((const unsigned char *)tmpbuf, + vbuf, buf->vb.width, + buf->vb.height, + buf->fmt->fourcc); + break; + case V4L2_PIX_FMT_GREY: + memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height); + break; + case V4L2_PIX_FMT_YUV422P: + memcpy(vbuf, tmpbuf, + buf->vb.width * buf->vb.height * 2); + break; + default: + printk(KERN_DEBUG "s2255: unknown format?\n"); + } + dev->last_frame[chn] = -1; + /* done with the frame, free it */ + frm->ulState = 0; + dprintk(4, "freeing buffer\n"); + } else { + printk(KERN_ERR "s2255: =======no frame\n"); + return; + + } + dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n", + (unsigned long)vbuf, pos); + /* tell v4l buffer was filled */ + + buf->vb.field_count++; + do_gettimeofday(&ts); + buf->vb.ts = ts; + buf->vb.state = VIDEOBUF_DONE; +} + + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct s2255_fh *fh = vq->priv_data; + + *size = fh->width * fh->height * (fh->fmt->depth >> 3); + + if (0 == *count) + *count = S2255_DEF_BUFS; + + while (*size * (*count) > vid_limit * 1024 * 1024) + (*count)--; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf) +{ + dprintk(4, "%s\n", __func__); + + videobuf_waiton(&buf->vb, 0, 0); + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct s2255_fh *fh = vq->priv_data; + struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); + int rc; + dprintk(4, "%s, field=%d\n", __func__, field); + if (fh->fmt == NULL) + return -EINVAL; + + if ((fh->width < norm_minw(fh->dev->vdev[fh->channel])) || + (fh->width > norm_maxw(fh->dev->vdev[fh->channel])) || + (fh->height < norm_minh(fh->dev->vdev[fh->channel])) || + (fh->height > norm_maxh(fh->dev->vdev[fh->channel]))) { + dprintk(4, "invalid buffer prepare\n"); + return -EINVAL; + } + + buf->vb.size = fh->width * fh->height * (fh->fmt->depth >> 3); + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) { + dprintk(4, "invalid buffer prepare\n"); + return -EINVAL; + } + + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); + struct s2255_fh *fh = vq->priv_data; + struct s2255_dev *dev = fh->dev; + struct s2255_dmaqueue *vidq = &dev->vidq[fh->channel]; + + dprintk(1, "%s\n", __func__); + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); + struct s2255_fh *fh = vq->priv_data; + dprintk(4, "%s %d\n", __func__, fh->channel); + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops s2255_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + + +static int res_get(struct s2255_dev *dev, struct s2255_fh *fh) +{ + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->resources[fh->channel]) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + dev->resources[fh->channel] = 1; + dprintk(1, "res: get\n"); + mutex_unlock(&dev->lock); + return 1; +} + +static int res_locked(struct s2255_dev *dev, struct s2255_fh *fh) +{ + return dev->resources[fh->channel]; +} + +static void res_free(struct s2255_dev *dev, struct s2255_fh *fh) +{ + dev->resources[fh->channel] = 0; + dprintk(1, "res: put\n"); +} + + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct s2255_fh *fh = file->private_data; + struct s2255_dev *dev = fh->dev; + strlcpy(cap->driver, "s2255", sizeof(cap->driver)); + strlcpy(cap->card, "s2255", sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&dev->udev->dev), + sizeof(cap->bus_info)); + cap->version = S2255_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int index = 0; + if (f) + index = f->index; + + if (index >= ARRAY_SIZE(formats)) + return -EINVAL; + + dprintk(4, "name %s\n", formats[index].name); + strlcpy(f->description, formats[index].name, sizeof(f->description)); + f->pixelformat = formats[index].fourcc; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct s2255_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = f->fmt.pix.width * (fh->fmt->depth >> 3); + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct s2255_fmt *fmt; + enum v4l2_field field; + int b_any_field = 0; + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + int is_ntsc; + + is_ntsc = + (dev->vdev[fh->channel]->current_norm & V4L2_STD_NTSC) ? 1 : 0; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + + if (fmt == NULL) + return -EINVAL; + + field = f->fmt.pix.field; + if (field == V4L2_FIELD_ANY) + b_any_field = 1; + + dprintk(4, "try format %d \n", is_ntsc); + /* supports 3 sizes. see s2255drv.h */ + dprintk(50, "width test %d, height %d\n", + f->fmt.pix.width, f->fmt.pix.height); + if (is_ntsc) { + /* NTSC */ + if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) { + f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2; + if (b_any_field) { + field = V4L2_FIELD_SEQ_TB; + } else if (!((field == V4L2_FIELD_INTERLACED) || + (field == V4L2_FIELD_SEQ_TB) || + (field == V4L2_FIELD_INTERLACED_TB))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + } else { + f->fmt.pix.height = NUM_LINES_1CIFS_NTSC; + if (b_any_field) { + field = V4L2_FIELD_TOP; + } else if (!((field == V4L2_FIELD_TOP) || + (field == V4L2_FIELD_BOTTOM))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + + } + if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC) + f->fmt.pix.width = LINE_SZ_4CIFS_NTSC; + else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC) + f->fmt.pix.width = LINE_SZ_2CIFS_NTSC; + else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC) + f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; + else + f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; + } else { + /* PAL */ + if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) { + f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2; + if (b_any_field) { + field = V4L2_FIELD_SEQ_TB; + } else if (!((field == V4L2_FIELD_INTERLACED) || + (field == V4L2_FIELD_SEQ_TB) || + (field == V4L2_FIELD_INTERLACED_TB))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + } else { + f->fmt.pix.height = NUM_LINES_1CIFS_PAL; + if (b_any_field) { + field = V4L2_FIELD_TOP; + } else if (!((field == V4L2_FIELD_TOP) || + (field == V4L2_FIELD_BOTTOM))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + } + if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) { + dprintk(50, "pal 704\n"); + f->fmt.pix.width = LINE_SZ_4CIFS_PAL; + field = V4L2_FIELD_SEQ_TB; + } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) { + dprintk(50, "pal 352A\n"); + f->fmt.pix.width = LINE_SZ_2CIFS_PAL; + field = V4L2_FIELD_TOP; + } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) { + dprintk(50, "pal 352B\n"); + f->fmt.pix.width = LINE_SZ_1CIFS_PAL; + field = V4L2_FIELD_TOP; + } else { + dprintk(50, "pal 352C\n"); + f->fmt.pix.width = LINE_SZ_1CIFS_PAL; + field = V4L2_FIELD_TOP; + } + } + + dprintk(50, "width %d height %d field %d \n", f->fmt.pix.width, + f->fmt.pix.height, f->fmt.pix.field); + f->fmt.pix.field = field; + f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct s2255_fh *fh = priv; + const struct s2255_fmt *fmt; + struct videobuf_queue *q = &fh->vb_vidq; + int ret; + int norm; + + ret = vidioc_try_fmt_vid_cap(file, fh, f); + + if (ret < 0) + return ret; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + + if (fmt == NULL) + return -EINVAL; + + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + dprintk(1, "queue busy\n"); + ret = -EBUSY; + goto out_s_fmt; + } + + if (res_locked(fh->dev, fh)) { + dprintk(1, "can't change format after started\n"); + ret = -EBUSY; + goto out_s_fmt; + } + + fh->fmt = fmt; + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + norm = norm_minw(fh->dev->vdev[fh->channel]); + if (fh->width > norm_minw(fh->dev->vdev[fh->channel])) { + if (fh->height > norm_minh(fh->dev->vdev[fh->channel])) + fh->mode.scale = SCALE_4CIFS; + else + fh->mode.scale = SCALE_2CIFS; + + } else { + fh->mode.scale = SCALE_1CIFS; + } + + /* color mode */ + switch (fh->fmt->fourcc) { + case V4L2_PIX_FMT_GREY: + fh->mode.color = COLOR_Y8; + break; + case V4L2_PIX_FMT_YUV422P: + fh->mode.color = COLOR_YUVPL; + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + default: + fh->mode.color = COLOR_YUVPK; + break; + } + ret = 0; +out_s_fmt: + mutex_unlock(&q->vb_lock); + return ret; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_reqbufs(&fh->vb_vidq, p); + return rc; +} + +static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_querybuf(&fh->vb_vidq, p); + return rc; +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_qbuf(&fh->vb_vidq, p); + return rc; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK); + return rc; +} + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidioc_cgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) +{ + struct s2255_fh *fh = priv; + + return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); +} +#endif + +/* write to the configuration pipe, synchronously */ +static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf, + int size) +{ + int pipe; + int done; + long retval = -1; + if (udev) { + pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP); + retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500); + } + return retval; +} + +static u32 get_transfer_size(struct s2255_mode *mode) +{ + int linesPerFrame = LINE_SZ_DEF; + int pixelsPerLine = NUM_LINES_DEF; + u32 outImageSize; + u32 usbInSize; + unsigned int mask_mult; + + if (mode == NULL) + return 0; + + if (mode->format == FORMAT_NTSC) { + switch (mode->scale) { + case SCALE_4CIFS: + linesPerFrame = NUM_LINES_4CIFS_NTSC * 2; + pixelsPerLine = LINE_SZ_4CIFS_NTSC; + break; + case SCALE_2CIFS: + linesPerFrame = NUM_LINES_2CIFS_NTSC; + pixelsPerLine = LINE_SZ_2CIFS_NTSC; + break; + case SCALE_1CIFS: + linesPerFrame = NUM_LINES_1CIFS_NTSC; + pixelsPerLine = LINE_SZ_1CIFS_NTSC; + break; + default: + break; + } + } else if (mode->format == FORMAT_PAL) { + switch (mode->scale) { + case SCALE_4CIFS: + linesPerFrame = NUM_LINES_4CIFS_PAL * 2; + pixelsPerLine = LINE_SZ_4CIFS_PAL; + break; + case SCALE_2CIFS: + linesPerFrame = NUM_LINES_2CIFS_PAL; + pixelsPerLine = LINE_SZ_2CIFS_PAL; + break; + case SCALE_1CIFS: + linesPerFrame = NUM_LINES_1CIFS_PAL; + pixelsPerLine = LINE_SZ_1CIFS_PAL; + break; + default: + break; + } + } + outImageSize = linesPerFrame * pixelsPerLine; + if (mode->color != COLOR_Y8) { + /* 2 bytes/pixel if not monochrome */ + outImageSize *= 2; + } + + /* total bytes to send including prefix and 4K padding; + must be a multiple of USB_READ_SIZE */ + usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */ + mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1; + /* if size not a multiple of USB_READ_SIZE */ + if (usbInSize & ~mask_mult) + usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK); + return usbInSize; +} + +static void dump_verify_mode(struct s2255_dev *sdev, struct s2255_mode *mode) +{ + struct device *dev = &sdev->udev->dev; + dev_info(dev, "------------------------------------------------\n"); + dev_info(dev, "verify mode\n"); + dev_info(dev, "format: %d\n", mode->format); + dev_info(dev, "scale: %d\n", mode->scale); + dev_info(dev, "fdec: %d\n", mode->fdec); + dev_info(dev, "color: %d\n", mode->color); + dev_info(dev, "bright: 0x%x\n", mode->bright); + dev_info(dev, "restart: 0x%x\n", mode->restart); + dev_info(dev, "usb_block: 0x%x\n", mode->usb_block); + dev_info(dev, "single: 0x%x\n", mode->single); + dev_info(dev, "------------------------------------------------\n"); +} + +/* + * set mode is the function which controls the DSP. + * the restart parameter in struct s2255_mode should be set whenever + * the image size could change via color format, video system or image + * size. + * When the restart parameter is set, we sleep for ONE frame to allow the + * DSP time to get the new frame + */ +static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, + struct s2255_mode *mode) +{ + int res; + u32 *buffer; + unsigned long chn_rev; + + chn_rev = G_chnmap[chn]; + dprintk(3, "mode scale [%ld] %p %d\n", chn, mode, mode->scale); + dprintk(3, "mode scale [%ld] %p %d\n", chn, &dev->mode[chn], + dev->mode[chn].scale); + dprintk(2, "mode contrast %x\n", mode->contrast); + + /* save the mode */ + dev->mode[chn] = *mode; + dev->req_image_size[chn] = get_transfer_size(mode); + dprintk(1, "transfer size %ld\n", dev->req_image_size[chn]); + + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + return -ENOMEM; + } + + /* set the mode */ + buffer[0] = IN_DATA_TOKEN; + buffer[1] = (u32) chn_rev; + buffer[2] = CMD_SET_MODE; + memcpy(&buffer[3], &dev->mode[chn], sizeof(struct s2255_mode)); + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + if (debug) + dump_verify_mode(dev, mode); + kfree(buffer); + dprintk(1, "set mode done chn %lu, %d\n", chn, res); + + /* wait at least 3 frames before continuing */ + if (mode->restart) + msleep(125); + + /* clear the restart flag */ + dev->mode[chn].restart = 0; + + return res; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + int res; + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + struct s2255_mode *new_mode; + struct s2255_mode *old_mode; + int chn; + int j; + dprintk(4, "%s\n", __func__); + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_err(&dev->udev->dev, "invalid fh type0\n"); + return -EINVAL; + } + if (i != fh->type) { + dev_err(&dev->udev->dev, "invalid fh type1\n"); + return -EINVAL; + } + + if (!res_get(dev, fh)) { + dev_err(&dev->udev->dev, "res get busy\n"); + return -EBUSY; + } + + /* send a set mode command everytime with restart. + in case we switch resolutions or other parameters */ + chn = fh->channel; + new_mode = &fh->mode; + old_mode = &fh->dev->mode[chn]; + + if (new_mode->color != old_mode->color) + new_mode->restart = 1; + else if (new_mode->scale != old_mode->scale) + new_mode->restart = 1; + else if (new_mode->format != old_mode->format) + new_mode->restart = 1; + + s2255_set_mode(dev, chn, new_mode); + new_mode->restart = 0; + *old_mode = *new_mode; + dev->cur_fmt[chn] = fh->fmt; + dprintk(1, "%s[%d]\n", __func__, chn); + dev->last_frame[chn] = -1; + dev->bad_payload[chn] = 0; + dev->cur_frame[chn] = 0; + for (j = 0; j < SYS_FRAMES; j++) { + dev->buffer[chn].frame[j].ulState = 0; + dev->buffer[chn].frame[j].cur_size = 0; + } + res = videobuf_streamon(&fh->vb_vidq); + if (res == 0) { + s2255_start_acquire(dev, chn); + dev->b_acquire[chn] = 1; + } else { + res_free(dev, fh); + } + return res; +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + int res; + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + + dprintk(4, "%s\n, channel: %d", __func__, fh->channel); + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + printk(KERN_ERR "invalid fh type0\n"); + return -EINVAL; + } + if (i != fh->type) { + printk(KERN_ERR "invalid type i\n"); + return -EINVAL; + } + s2255_stop_acquire(dev, fh->channel); + res = videobuf_streamoff(&fh->vb_vidq); + res_free(dev, fh); + return res; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) +{ + struct s2255_fh *fh = priv; + struct s2255_mode *mode; + struct videobuf_queue *q = &fh->vb_vidq; + int ret = 0; + + mutex_lock(&q->vb_lock); + if (videobuf_queue_is_busy(q)) { + dprintk(1, "queue busy\n"); + ret = -EBUSY; + goto out_s_std; + } + + if (res_locked(fh->dev, fh)) { + dprintk(1, "can't change standard after started\n"); + ret = -EBUSY; + goto out_s_std; + } + mode = &fh->mode; + + if (*i & V4L2_STD_NTSC) { + dprintk(4, "vidioc_s_std NTSC\n"); + mode->format = FORMAT_NTSC; + } else if (*i & V4L2_STD_PAL) { + dprintk(4, "vidioc_s_std PAL\n"); + mode->format = FORMAT_PAL; + } else { + ret = -EINVAL; + } +out_s_std: + mutex_unlock(&q->vb_lock); + return ret; +} + +/* Sensoray 2255 is a multiple channel capture device. + It does not have a "crossbar" of inputs. + We use one V4L device per channel. The user must + be aware that certain combinations are not allowed. + For instance, you cannot do full FPS on more than 2 channels(2 videodevs) + at once in color(you can do full fps on 4 channels with greyscale. +*/ +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = S2255_NORMS; + strlcpy(inp->name, "Camera", sizeof(inp->name)); + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + return 0; +} + +/* --- controls ---------------------------------------------- */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) + if (qc->id && qc->id == s2255_qctrl[i].id) { + memcpy(qc, &(s2255_qctrl[i]), sizeof(*qc)); + return 0; + } + + dprintk(4, "query_ctrl -EINVAL %d\n", qc->id); + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) + if (ctrl->id == s2255_qctrl[i].id) { + ctrl->value = qctl_regs[i]; + return 0; + } + dprintk(4, "g_ctrl -EINVAL\n"); + + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + int i; + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + struct s2255_mode *mode; + mode = &fh->mode; + dprintk(4, "vidioc_s_ctrl\n"); + for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) { + if (ctrl->id == s2255_qctrl[i].id) { + if (ctrl->value < s2255_qctrl[i].minimum || + ctrl->value > s2255_qctrl[i].maximum) + return -ERANGE; + + qctl_regs[i] = ctrl->value; + /* update the mode to the corresponding value */ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + mode->bright = ctrl->value; + break; + case V4L2_CID_CONTRAST: + mode->contrast = ctrl->value; + break; + case V4L2_CID_HUE: + mode->hue = ctrl->value; + break; + case V4L2_CID_SATURATION: + mode->saturation = ctrl->value; + break; + } + mode->restart = 0; + /* set mode here. Note: stream does not need restarted. + some V4L programs restart stream unnecessarily + after a s_crtl. + */ + s2255_set_mode(dev, fh->channel, mode); + return 0; + } + } + return -EINVAL; +} + +static int s2255_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct s2255_dev *h, *dev = NULL; + struct s2255_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + int i = 0; + int cur_channel = -1; + dprintk(1, "s2255: open called (minor=%d)\n", minor); + + list_for_each(list, &s2255_devlist) { + h = list_entry(list, struct s2255_dev, s2255_devlist); + for (i = 0; i < MAX_CHANNELS; i++) { + if (h->vdev[i]->minor == minor) { + cur_channel = i; + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + } + + if ((NULL == dev) || (cur_channel == -1)) { + dprintk(1, "s2255: openv4l no dev\n"); + return -ENODEV; + } + + mutex_lock(&dev->open_lock); + + dev->users[cur_channel]++; + if (dev->users[cur_channel] > S2255_MAX_USERS) { + dev->users[cur_channel]--; + mutex_unlock(&dev->open_lock); + printk(KERN_INFO "s2255drv: too many open handles!\n"); + return -EBUSY; + } + + if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_FAILED) { + err("2255 firmware load failed. retrying.\n"); + s2255_fwload_start(dev); + wait_event_timeout(dev->fw_data->wait_fw, + (atomic_read(&dev->fw_data->fw_state) + != S2255_FW_NOTLOADED), + msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + if (atomic_read(&dev->fw_data->fw_state) + != S2255_FW_SUCCESS) { + printk(KERN_INFO "2255 FW load failed after 2 tries\n"); + mutex_unlock(&dev->open_lock); + return -EFAULT; + } + } else if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_NOTLOADED) { + /* give S2255_LOAD_TIMEOUT time for firmware to load in case + driver loaded and then device immediately opened */ + printk(KERN_INFO "%s waiting for firmware load\n", __func__); + wait_event_timeout(dev->fw_data->wait_fw, + (atomic_read(&dev->fw_data->fw_state) + != S2255_FW_NOTLOADED), + msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + if (atomic_read(&dev->fw_data->fw_state) + != S2255_FW_SUCCESS) { + printk(KERN_INFO "2255 firmware not loaded" + "try again\n"); + mutex_unlock(&dev->open_lock); + return -EBUSY; + } + } + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + mutex_unlock(&dev->open_lock); + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->mode = dev->mode[cur_channel]; + fh->fmt = dev->cur_fmt[cur_channel]; + /* default 4CIF NTSC */ + fh->width = LINE_SZ_4CIFS_NTSC; + fh->height = NUM_LINES_4CIFS_NTSC * 2; + fh->channel = cur_channel; + + /* Put all controls at a sane state */ + for (i = 0; i < ARRAY_SIZE(s2255_qctrl); i++) + qctl_regs[i] = s2255_qctrl[i].default_value; + + dprintk(1, "s2255drv: open minor=%d type=%s users=%d\n", + minor, v4l2_type_names[type], dev->users[cur_channel]); + dprintk(2, "s2255drv: open: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", + (unsigned long)fh, (unsigned long)dev, + (unsigned long)&dev->vidq[cur_channel]); + dprintk(4, "s2255drv: open: list_empty active=%d\n", + list_empty(&dev->vidq[cur_channel].active)); + + videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops, + NULL, &dev->slock, + fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct s2255_buffer), fh); + + kref_get(&dev->kref); + mutex_unlock(&dev->open_lock); + return 0; +} + + +static unsigned int s2255_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct s2255_fh *fh = file->private_data; + int rc; + dprintk(100, "%s\n", __func__); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + + rc = videobuf_poll_stream(file, &fh->vb_vidq, wait); + return rc; +} + +static void s2255_destroy(struct kref *kref) +{ + struct s2255_dev *dev = to_s2255_dev(kref); + if (!dev) { + printk(KERN_ERR "s2255drv: kref problem\n"); + return; + } + /* prevent s2255_disconnect from racing s2255_open */ + mutex_lock(&dev->open_lock); + s2255_exit_v4l(dev); + /* device unregistered so no longer possible to open. open_mutex + can be unlocked */ + mutex_unlock(&dev->open_lock); + + /* board shutdown stops the read pipe if it is running */ + s2255_board_shutdown(dev); + + /* make sure firmware still not trying to load */ + if (dev->fw_data->fw_urb) { + dprintk(2, "kill fw_urb\n"); + usb_kill_urb(dev->fw_data->fw_urb); + usb_free_urb(dev->fw_data->fw_urb); + dev->fw_data->fw_urb = NULL; + } + /* + * TODO: fixme(above, below): potentially leaving timers alive. + * do not ignore timeout below if + * it occurs. + */ + + /* make sure we aren't waiting for the DSP */ + if (atomic_read(&dev->fw_data->fw_state) == S2255_FW_LOADED_DSPWAIT) { + /* if we are, wait for the wakeup for fw_success or timeout */ + wait_event_timeout(dev->fw_data->wait_fw, + (atomic_read(&dev->fw_data->fw_state) + == S2255_FW_SUCCESS), + msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + } + + if (dev->fw_data) { + if (dev->fw_data->fw) + release_firmware(dev->fw_data->fw); + kfree(dev->fw_data->pfw_data); + kfree(dev->fw_data); + } + + usb_put_dev(dev->udev); + dprintk(1, "%s", __func__); + kfree(dev); +} + +static int s2255_close(struct inode *inode, struct file *file) +{ + struct s2255_fh *fh = file->private_data; + struct s2255_dev *dev = fh->dev; + int minor = iminor(inode); + if (!dev) + return -ENODEV; + + mutex_lock(&dev->open_lock); + + if (dev->b_acquire[fh->channel]) + s2255_stop_acquire(dev, fh->channel); + res_free(dev, fh); + videobuf_mmap_free(&fh->vb_vidq); + kfree(fh); + dev->users[fh->channel]--; + mutex_unlock(&dev->open_lock); + + kref_put(&dev->kref, s2255_destroy); + dprintk(1, "s2255: close called (minor=%d, users=%d)\n", + minor, dev->users[fh->channel]); + return 0; +} + +static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma) +{ + struct s2255_fh *fh = file->private_data; + int ret; + + if (!fh) + return -ENODEV; + dprintk(4, "mmap called, vma=0x%08lx\n", (unsigned long)vma); + + ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); + + dprintk(4, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); + + return ret; +} + +static const struct file_operations s2255_fops_v4l = { + .owner = THIS_MODULE, + .open = s2255_open, + .release = s2255_close, + .poll = s2255_poll, + .ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .compat_ioctl = v4l_compat_ioctl32, + .mmap = s2255_mmap_v4l, + .llseek = no_llseek, +}; + +static struct video_device template = { + .name = "s2255v", + .type = VID_TYPE_CAPTURE, + .fops = &s2255_fops_v4l, + .minor = -1, + .release = video_device_release, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .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, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidioc_cgmbuf, +#endif + .tvnorms = S2255_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; + +static int s2255_probe_v4l(struct s2255_dev *dev) +{ + int ret; + int i; + int cur_nr = video_nr; + + /* initialize all video 4 linux */ + list_add_tail(&dev->s2255_devlist, &s2255_devlist); + /* register 4 video devices */ + for (i = 0; i < MAX_CHANNELS; i++) { + INIT_LIST_HEAD(&dev->vidq[i].active); + dev->vidq[i].dev = dev; + dev->vidq[i].channel = i; + dev->vidq[i].kthread = NULL; + /* register 4 video devices */ + dev->vdev[i] = video_device_alloc(); + memcpy(dev->vdev[i], &template, sizeof(struct video_device)); + dev->vdev[i]->dev = &dev->interface->dev; + if (video_nr == -1) + ret = video_register_device(dev->vdev[i], + VFL_TYPE_GRABBER, + video_nr); + else + ret = video_register_device(dev->vdev[i], + VFL_TYPE_GRABBER, + cur_nr + i); + dev->vdev[i]->priv = dev; + + if (ret != 0) { + dev_err(&dev->udev->dev, + "failed to register video device!\n"); + return ret; + } + } + printk(KERN_INFO "Sensoray 2255 V4L driver\n"); + return ret; +} + +static void s2255_exit_v4l(struct s2255_dev *dev) +{ + struct list_head *list; + int i; + /* unregister the video devices */ + while (!list_empty(&s2255_devlist)) { + list = s2255_devlist.next; + list_del(list); + } + for (i = 0; i < MAX_CHANNELS; i++) { + if (-1 != dev->vdev[i]->minor) + video_unregister_device(dev->vdev[i]); + else + video_device_release(dev->vdev[i]); + } +} + +/* this function moves the usb stream read pipe data + * into the system buffers. + * returns 0 on success, EAGAIN if more data to process( call this + * function again). + * + * Received frame structure: + * bytes 0-3: marker : 0x2255DA4AL (FRAME_MARKER) + * bytes 4-7: channel: 0-3 + * bytes 8-11: payload size: size of the frame + * bytes 12-payloadsize+12: frame data + */ +static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) +{ + static int dbgsync; /* = 0; */ + char *pdest; + u32 offset = 0; + int bsync = 0; + int btrunc = 0; + char *psrc; + unsigned long copy_size; + unsigned long size; + s32 idx = -1; + struct s2255_framei *frm; + unsigned char *pdata; + unsigned long cur_size; + int bsearch = 0; + struct s2255_bufferi *buf; + dprintk(100, "buffer to user\n"); + + idx = dev->cur_frame[dev->cc]; + buf = &dev->buffer[dev->cc]; + frm = &buf->frame[idx]; + + if (frm->ulState == 0) { + frm->ulState = 1; + frm->cur_size = 0; + bsearch = 1; + } else if (frm->ulState == 2) { + /* system frame was not freed */ + dprintk(2, "sys frame not free. overrun ringbuf\n"); + bsearch = 1; + frm->ulState = 1; + frm->cur_size = 0; + } + + if (bsearch) { + if (*(s32 *) pipe_info->transfer_buffer != FRAME_MARKER) { + u32 jj; + if (dbgsync == 0) { + dprintk(3, "not synched, discarding all packets" + "until marker\n"); + + dbgsync++; + } + pdata = (unsigned char *)pipe_info->transfer_buffer; + for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); + jj++) { + if (*(s32 *) pdata == FRAME_MARKER) { + int cc; + dprintk(3, + "found frame marker at offset:" + " %d [%x %x]\n", jj, pdata[0], + pdata[1]); + offset = jj; + bsync = 1; + cc = *(u32 *) (pdata + sizeof(u32)); + if (cc >= MAX_CHANNELS) { + printk(KERN_ERR + "bad channel\n"); + return -EINVAL; + } + /* reverse it */ + dev->cc = G_chnmap[cc]; + break; + } + pdata++; + } + if (bsync == 0) + return -EINVAL; + } else { + u32 *pword; + u32 payload; + int cc; + dbgsync = 0; + bsync = 1; + pword = (u32 *) pipe_info->transfer_buffer; + cc = pword[1]; + + if (cc >= MAX_CHANNELS) { + printk("invalid channel found. " + "throwing out data!\n"); + return -EINVAL; + } + dev->cc = G_chnmap[cc]; + payload = pword[2]; + if (payload != dev->req_image_size[dev->cc]) { + dprintk(1, "[%d][%d]unexpected payload: %d" + "required: %lu \n", cc, dev->cc, + payload, dev->req_image_size[dev->cc]); + dev->bad_payload[dev->cc]++; + /* discard the bad frame */ + return -EINVAL; + } + + } + } + /* search done. now find out if should be acquiring + on this channel */ + if (!dev->b_acquire[dev->cc]) { + frm->ulState = 0; + return -EINVAL; + } + + idx = dev->cur_frame[dev->cc]; + frm = &dev->buffer[dev->cc].frame[idx]; + + if (frm->ulState == 0) { + frm->ulState = 1; + frm->cur_size = 0; + } else if (frm->ulState == 2) { + /* system frame ring buffer overrun */ + dprintk(2, "sys frame overrun. overwriting frame %d %d\n", + dev->cc, idx); + frm->ulState = 1; + frm->cur_size = 0; + } + + if (bsync) { + /* skip the marker 512 bytes (and offset if out of sync) */ + psrc = (u8 *)pipe_info->transfer_buffer + offset + PREFIX_SIZE; + } else { + psrc = (u8 *)pipe_info->transfer_buffer; + } + + if (frm->lpvbits == NULL) { + dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d", + frm, dev, dev->cc, idx); + return -ENOMEM; + } + + pdest = frm->lpvbits + frm->cur_size; + + if (bsync) { + copy_size = + (pipe_info->cur_transfer_size - offset) - PREFIX_SIZE; + if (copy_size > pipe_info->cur_transfer_size) { + printk("invalid copy size, overflow!\n"); + return -ENOMEM; + } + } else { + copy_size = pipe_info->cur_transfer_size; + } + + cur_size = frm->cur_size; + size = dev->req_image_size[dev->cc]; + + if ((copy_size + cur_size) > size) { + copy_size = size - cur_size; + btrunc = 1; + } + + memcpy(pdest, psrc, copy_size); + cur_size += copy_size; + frm->cur_size += copy_size; + dprintk(50, "cur_size size %lu size %lu \n", cur_size, size); + + if (cur_size >= (size - PREFIX_SIZE)) { + u32 cc = dev->cc; + frm->ulState = 2; + dprintk(2, "****************[%d]Buffer[%d]full*************\n", + cc, idx); + dev->last_frame[cc] = dev->cur_frame[cc]; + dev->cur_frame[cc]++; + /* end of system frame ring buffer, start at zero */ + if ((dev->cur_frame[cc] == SYS_FRAMES) || + (dev->cur_frame[cc] == dev->buffer[cc].dwFrames)) + dev->cur_frame[cc] = 0; + + /* signal the semaphore for this channel */ + if (dev->b_acquire[cc]) + s2255_got_frame(dev, cc); + dev->frame_count[cc]++; + } + /* frame was truncated */ + if (btrunc) { + /* return more data to process */ + return EAGAIN; + } + /* done successfully */ + return 0; +} + +static void s2255_read_video_callback(struct s2255_dev *dev, + struct s2255_pipeinfo *pipe_info) +{ + int res; + dprintk(50, "callback read video \n"); + + if (dev->cc >= MAX_CHANNELS) { + dev->cc = 0; + dev_err(&dev->udev->dev, "invalid channel\n"); + return; + } + /* otherwise copy to the system buffers */ + res = save_frame(dev, pipe_info); + if (res == EAGAIN) + save_frame(dev, pipe_info); + + dprintk(50, "callback read video done\n"); + return; +} + +static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request, + u16 Index, u16 Value, void *TransferBuffer, + s32 TransferBufferLength, int bOut) +{ + int r; + if (!bOut) { + r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + Request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | + USB_DIR_IN, + Value, Index, TransferBuffer, + TransferBufferLength, HZ * 5); + } else { + r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + Value, Index, TransferBuffer, + TransferBufferLength, HZ * 5); + } + return r; +} + +/* + * retrieve FX2 firmware version. future use. + * @param dev pointer to device extension + * @return -1 for fail, else returns firmware version as an int(16 bits) + */ +static int s2255_get_fx2fw(struct s2255_dev *dev) +{ + int fw; + int ret; + unsigned char transBuffer[64]; + ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2, + S2255_VR_IN); + if (ret < 0) + dprintk(2, "get fw error: %x\n", ret); + fw = transBuffer[0] + (transBuffer[1] << 8); + dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]); + return fw; +} + +/* + * Create the system ring buffer to copy frames into from the + * usb read pipe. + */ +static int s2255_create_sys_buffers(struct s2255_dev *dev, unsigned long chn) +{ + unsigned long i; + unsigned long reqsize; + dprintk(1, "create sys buffers\n"); + if (chn >= MAX_CHANNELS) + return -1; + + dev->buffer[chn].dwFrames = SYS_FRAMES; + + /* always allocate maximum size(PAL) for system buffers */ + reqsize = SYS_FRAMES_MAXSIZE; + + if (reqsize > SYS_FRAMES_MAXSIZE) + reqsize = SYS_FRAMES_MAXSIZE; + + for (i = 0; i < SYS_FRAMES; i++) { + /* allocate the frames */ + dev->buffer[chn].frame[i].lpvbits = vmalloc(reqsize); + + dprintk(1, "valloc %p chan %lu, idx %lu, pdata %p\n", + &dev->buffer[chn].frame[i], chn, i, + dev->buffer[chn].frame[i].lpvbits); + dev->buffer[chn].frame[i].size = reqsize; + if (dev->buffer[chn].frame[i].lpvbits == NULL) { + printk(KERN_INFO "out of memory. using less frames\n"); + dev->buffer[chn].dwFrames = i; + break; + } + } + + /* make sure internal states are set */ + for (i = 0; i < SYS_FRAMES; i++) { + dev->buffer[chn].frame[i].ulState = 0; + dev->buffer[chn].frame[i].cur_size = 0; + } + + dev->cur_frame[chn] = 0; + dev->last_frame[chn] = -1; + return 0; +} + +static int s2255_release_sys_buffers(struct s2255_dev *dev, + unsigned long channel) +{ + unsigned long i; + dprintk(1, "release sys buffers\n"); + for (i = 0; i < SYS_FRAMES; i++) { + if (dev->buffer[channel].frame[i].lpvbits) { + dprintk(1, "vfree %p\n", + dev->buffer[channel].frame[i].lpvbits); + vfree(dev->buffer[channel].frame[i].lpvbits); + } + dev->buffer[channel].frame[i].lpvbits = NULL; + } + return 0; +} + +static int s2255_board_init(struct s2255_dev *dev) +{ + int j; + struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT; + int fw_ver; + dprintk(4, "board init: %p", dev); + + for (j = 0; j < MAX_PIPE_BUFFERS; j++) { + struct s2255_pipeinfo *pipe = &dev->pipes[j]; + + memset(pipe, 0, sizeof(*pipe)); + pipe->dev = dev; + pipe->cur_transfer_size = DEFAULT_PIPE_USBBLOCK; + pipe->max_transfer_size = MAX_PIPE_USBBLOCK; + + if (pipe->cur_transfer_size > pipe->max_transfer_size) + pipe->cur_transfer_size = pipe->max_transfer_size; + pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, + GFP_KERNEL); + if (pipe->transfer_buffer == NULL) { + dprintk(1, "out of memory!\n"); + return -ENOMEM; + } + + } + + /* query the firmware */ + fw_ver = s2255_get_fx2fw(dev); + + printk(KERN_INFO "2255 usb firmware version %d \n", fw_ver); + if (fw_ver < CUR_USB_FWVER) + err("usb firmware not up to date %d\n", fw_ver); + + for (j = 0; j < MAX_CHANNELS; j++) { + dev->b_acquire[j] = 0; + dev->mode[j] = mode_def; + dev->cur_fmt[j] = &formats[0]; + dev->mode[j].restart = 1; + dev->req_image_size[j] = get_transfer_size(&mode_def); + dev->frame_count[j] = 0; + /* create the system buffers */ + s2255_create_sys_buffers(dev, j); + } + /* start read pipe */ + s2255_start_readpipe(dev); + + dprintk(1, "S2255: board initialized\n"); + return 0; +} + +static int s2255_board_shutdown(struct s2255_dev *dev) +{ + u32 i; + + dprintk(1, "S2255: board shutdown: %p", dev); + + for (i = 0; i < MAX_CHANNELS; i++) { + if (dev->b_acquire[i]) + s2255_stop_acquire(dev, i); + } + + s2255_stop_readpipe(dev); + + for (i = 0; i < MAX_CHANNELS; i++) + s2255_release_sys_buffers(dev, i); + + /* release transfer buffers */ + for (i = 0; i < MAX_PIPE_BUFFERS; i++) { + struct s2255_pipeinfo *pipe = &dev->pipes[i]; + kfree(pipe->transfer_buffer); + } + return 0; +} + +static void read_pipe_completion(struct urb *purb) +{ + struct s2255_pipeinfo *pipe_info; + struct s2255_dev *dev; + int status; + int pipe; + + pipe_info = purb->context; + dprintk(100, "read pipe completion %p, status %d\n", purb, + purb->status); + if (pipe_info == NULL) { + err("no context !"); + return; + } + + dev = pipe_info->dev; + if (dev == NULL) { + err("no context !"); + return; + } + status = purb->status; + if (status != 0) { + dprintk(2, "read_pipe_completion: err\n"); + return; + } + + if (pipe_info->state == 0) { + dprintk(2, "exiting USB pipe"); + return; + } + + s2255_read_video_callback(dev, pipe_info); + + pipe_info->err_count = 0; + pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); + /* reuse urb */ + usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->cur_transfer_size, + read_pipe_completion, pipe_info); + + if (pipe_info->state != 0) { + if (usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL)) { + dev_err(&dev->udev->dev, "error submitting urb\n"); + usb_free_urb(pipe_info->stream_urb); + } + } else { + dprintk(2, "read pipe complete state 0\n"); + } + return; +} + +static int s2255_start_readpipe(struct s2255_dev *dev) +{ + int pipe; + int retval; + int i; + struct s2255_pipeinfo *pipe_info = dev->pipes; + pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); + dprintk(2, "start pipe IN %d\n", dev->read_endpoint); + + for (i = 0; i < MAX_PIPE_BUFFERS; i++) { + pipe_info->state = 1; + pipe_info->buf_index = (u32) i; + pipe_info->priority_set = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) { + dev_err(&dev->udev->dev, + "ReadStream: Unable to alloc URB"); + return -ENOMEM; + } + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->cur_transfer_size, + read_pipe_completion, pipe_info); + + pipe_info->urb_size = sizeof(pipe_info->stream_urb); + dprintk(4, "submitting URB %p\n", pipe_info->stream_urb); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + printk(KERN_ERR "s2255: start read pipe failed\n"); + return retval; + } + } + + return 0; +} + +/* starts acquisition process */ +static int s2255_start_acquire(struct s2255_dev *dev, unsigned long chn) +{ + unsigned char *buffer; + int res; + unsigned long chn_rev; + int j; + if (chn >= MAX_CHANNELS) { + dprintk(2, "start acquire failed, bad channel %lu\n", chn); + return -1; + } + + chn_rev = G_chnmap[chn]; + dprintk(1, "S2255: start acquire %lu \n", chn); + + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + return -ENOMEM; + } + + dev->last_frame[chn] = -1; + dev->bad_payload[chn] = 0; + dev->cur_frame[chn] = 0; + for (j = 0; j < SYS_FRAMES; j++) { + dev->buffer[chn].frame[j].ulState = 0; + dev->buffer[chn].frame[j].cur_size = 0; + } + + /* send the start command */ + *(u32 *) buffer = IN_DATA_TOKEN; + *((u32 *) buffer + 1) = (u32) chn_rev; + *((u32 *) buffer + 2) = (u32) CMD_START; + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + if (res != 0) + dev_err(&dev->udev->dev, "CMD_START error\n"); + + dprintk(2, "start acquire exit[%lu] %d \n", chn, res); + kfree(buffer); + return 0; +} + +static int s2255_stop_acquire(struct s2255_dev *dev, unsigned long chn) +{ + unsigned char *buffer; + int res; + unsigned long chn_rev; + + if (chn >= MAX_CHANNELS) { + dprintk(2, "stop acquire failed, bad channel %lu\n", chn); + return -1; + } + chn_rev = G_chnmap[chn]; + + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + return -ENOMEM; + } + + /* send the stop command */ + dprintk(4, "stop acquire %lu\n", chn); + *(u32 *) buffer = IN_DATA_TOKEN; + *((u32 *) buffer + 1) = (u32) chn_rev; + *((u32 *) buffer + 2) = CMD_STOP; + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + + if (res != 0) + dev_err(&dev->udev->dev, "CMD_STOP error\n"); + + dprintk(4, "stop acquire: releasing states \n"); + + kfree(buffer); + dev->b_acquire[chn] = 0; + + return 0; +} + +static void s2255_stop_readpipe(struct s2255_dev *dev) +{ + int j; + + if (dev == NULL) { + err("s2255: invalid device"); + return; + } + dprintk(4, "stop read pipe\n"); + for (j = 0; j < MAX_PIPE_BUFFERS; j++) { + struct s2255_pipeinfo *pipe_info = &dev->pipes[j]; + if (pipe_info) { + if (pipe_info->state == 0) + continue; + pipe_info->state = 0; + pipe_info->prev_state = 1; + + } + } + + for (j = 0; j < MAX_PIPE_BUFFERS; j++) { + struct s2255_pipeinfo *pipe_info = &dev->pipes[j]; + if (pipe_info->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe_info->stream_urb); + usb_free_urb(pipe_info->stream_urb); + pipe_info->stream_urb = NULL; + } + } + dprintk(2, "s2255 stop read pipe: %d\n", j); + return; +} + +static void s2255_fwload_start(struct s2255_dev *dev) +{ + dev->fw_data->fw_size = dev->fw_data->fw->size; + atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED); + memcpy(dev->fw_data->pfw_data, + dev->fw_data->fw->data, CHUNK_SIZE); + dev->fw_data->fw_loaded = CHUNK_SIZE; + usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev, + usb_sndbulkpipe(dev->udev, 2), + dev->fw_data->pfw_data, + CHUNK_SIZE, s2255_fwchunk_complete, + dev->fw_data); + mod_timer(&dev->timer, jiffies + HZ); +} + +/* standard usb probe function */ +static int s2255_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct s2255_dev *dev = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + int retval = -ENOMEM; + + dprintk(2, "s2255: probe\n"); + + /* allocate memory for our device state and initialize it to zero */ + dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL); + if (dev == NULL) { + err("s2255: out of memory"); + goto error; + } + + dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); + if (!dev->fw_data) + goto error; + + mutex_init(&dev->lock); + mutex_init(&dev->open_lock); + + /* grab usb_device and save it */ + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + if (dev->udev == NULL) { + dev_err(&interface->dev, "null usb device\n"); + retval = -ENODEV; + goto error; + } + kref_init(&dev->kref); + dprintk(1, "dev: %p, kref: %p udev %p interface %p\n", dev, &dev->kref, + dev->udev, interface); + dev->interface = interface; + /* set up the endpoint information */ + iface_desc = interface->cur_altsetting; + dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints); + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { + /* we found the bulk in endpoint */ + dev->read_endpoint = endpoint->bEndpointAddress; + } + } + + if (!dev->read_endpoint) { + dev_err(&interface->dev, "Could not find bulk-in endpoint"); + goto error; + } + + /* set intfdata */ + usb_set_intfdata(interface, dev); + + dprintk(100, "after intfdata %p\n", dev); + + init_timer(&dev->timer); + dev->timer.function = s2255_timer; + dev->timer.data = (unsigned long)dev->fw_data; + + init_waitqueue_head(&dev->fw_data->wait_fw); + + + dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); + + if (!dev->fw_data->fw_urb) { + dev_err(&interface->dev, "out of memory!\n"); + goto error; + } + dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); + if (!dev->fw_data->pfw_data) { + dev_err(&interface->dev, "out of memory!\n"); + goto error; + } + /* load the first chunk */ + if (request_firmware(&dev->fw_data->fw, + FIRMWARE_FILE_NAME, &dev->udev->dev)) { + printk(KERN_ERR "sensoray 2255 failed to get firmware\n"); + goto error; + } + + /* loads v4l specific */ + s2255_probe_v4l(dev); + /* load 2255 board specific */ + s2255_board_init(dev); + + dprintk(4, "before probe done %p\n", dev); + spin_lock_init(&dev->slock); + + s2255_fwload_start(dev); + dev_info(&interface->dev, "Sensoray 2255 detected\n"); + return 0; +error: + return retval; +} + +/* disconnect routine. when board is removed physically or with rmmod */ +static void s2255_disconnect(struct usb_interface *interface) +{ + struct s2255_dev *dev = NULL; + dprintk(1, "s2255: disconnect interface %p\n", interface); + dev = usb_get_intfdata(interface); + if (dev) { + kref_put(&dev->kref, s2255_destroy); + dprintk(1, "s2255drv: disconnect\n"); + dev_info(&interface->dev, "s2255usb now disconnected\n"); + } + usb_set_intfdata(interface, NULL); +} + +static struct usb_driver s2255_driver = { + .name = "s2255", + .probe = s2255_probe, + .disconnect = s2255_disconnect, + .id_table = s2255_table, +}; + +static int __init usb_s2255_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&s2255_driver); + + if (result) + err("usb_register failed. Error number %d", result); + + dprintk(2, "s2255_init: done\n"); + return result; +} + +static void __exit usb_s2255_exit(void) +{ + usb_deregister(&s2255_driver); +} + +module_init(usb_s2255_init); +module_exit(usb_s2255_exit); + +MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver"); +MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c index 996b49491f5..03e772130b5 100644 --- a/drivers/media/video/saa5246a.c +++ b/drivers/media/video/saa5246a.c @@ -66,6 +66,7 @@ static struct video_device saa_template; /* Declared near bottom */ /* Addresses to scan */ static unsigned short normal_i2c[] = { I2C_ADDRESS, I2C_CLIENT_END }; + I2C_CLIENT_INSMOD; static struct i2c_client client_template; diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index ec8c65dc840..fde99d9ee71 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -130,6 +130,7 @@ static struct video_device saa_template; /* Declared near bottom */ /* Addresses to scan */ static unsigned short normal_i2c[] = {34>>1,I2C_CLIENT_END}; + I2C_CLIENT_INSMOD; static struct i2c_client client_template; diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c index 716ee7f64df..f05024259f0 100644 --- a/drivers/media/video/saa6588.c +++ b/drivers/media/video/saa6588.c @@ -31,7 +31,6 @@ #include <linux/wait.h> #include <asm/uaccess.h> - #include <media/rds.h> /* Addresses to scan */ diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 435c083cc54..bcd1c8f6cf6 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -67,7 +67,6 @@ static unsigned short normal_i2c[] = { 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ I2C_CLIENT_END }; - I2C_CLIENT_INSMOD; struct saa711x_state { @@ -1558,7 +1557,7 @@ static int saa7115_remove(struct i2c_client *client) } static const struct i2c_device_id saa7115_id[] = { - { "saa711x", 1 }, /* autodetect */ + { "saa7115_auto", 1 }, /* autodetect */ { "saa7111", 0 }, { "saa7113", 0 }, { "saa7114", 0 }, @@ -1577,4 +1576,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, .id_table = saa7115_id, }; - diff --git a/drivers/media/video/saa711x.c b/drivers/media/video/saa711x.c deleted file mode 100644 index cedb988574b..00000000000 --- a/drivers/media/video/saa711x.c +++ /dev/null @@ -1,584 +0,0 @@ -/* - * saa711x - Philips SAA711x video decoder driver version 0.0.1 - * - * To do: Now, it handles only saa7113/7114. Should be improved to - * handle all Philips saa711x devices. - * - * Based on saa7113 driver from Dave Perks <dperks@ibm.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/signal.h> -#include <asm/io.h> -#include <asm/pgtable.h> -#include <asm/page.h> -#include <linux/types.h> -#include <asm/uaccess.h> -#include <linux/videodev.h> - -MODULE_DESCRIPTION("Philips SAA711x video decoder driver"); -MODULE_AUTHOR("Dave Perks, Jose Ignacio Gijon, Joerg Heckenbach, Mark McClelland, Dwaine Garden"); -MODULE_LICENSE("GPL"); - -#include <linux/i2c.h> - -#define I2C_NAME(s) (s)->name - -#include <linux/video_decoder.h> - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, " Set the default Debug level. Default: 0 (Off) - (0-1)"); - - -#define dprintk(num, format, args...) \ - do { \ - if (debug >= num) \ - printk(format, ##args); \ - } while (0) - -/* ----------------------------------------------------------------------- */ - -struct saa711x { - unsigned char reg[32]; - - int norm; - int input; - int enable; - int bright; - int contrast; - int hue; - int sat; -}; - -#define I2C_SAA7113 0x4A -#define I2C_SAA7114 0x42 - -/* ----------------------------------------------------------------------- */ - -static inline int -saa711x_write (struct i2c_client *client, - u8 reg, - u8 value) -{ - struct saa711x *decoder = i2c_get_clientdata(client); - - decoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int -saa711x_write_block (struct i2c_client *client, - const u8 *data, - unsigned int len) -{ - int ret = -1; - u8 reg; - - /* the saa711x has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - /* do raw I2C, not smbus compatible */ - struct saa711x *decoder = i2c_get_clientdata(client); - struct i2c_msg msg; - u8 block_data[32]; - - msg.addr = client->addr; - msg.flags = 0; - while (len >= 2) { - msg.buf = (char *) block_data; - msg.len = 0; - block_data[msg.len++] = reg = data[0]; - do { - block_data[msg.len++] = - decoder->reg[reg++] = data[1]; - len -= 2; - data += 2; - } while (len >= 2 && data[0] == reg && - msg.len < 32); - if ((ret = i2c_transfer(client->adapter, - &msg, 1)) < 0) - break; - } - } else { - /* do some slow I2C emulation kind of thing */ - while (len >= 2) { - reg = *data++; - if ((ret = saa711x_write(client, reg, - *data++)) < 0) - break; - len -= 2; - } - } - - return ret; -} - -static int -saa711x_init_decoder (struct i2c_client *client, - struct video_decoder_init *init) -{ - return saa711x_write_block(client, init->data, init->len); -} - -static inline int -saa711x_read (struct i2c_client *client, - u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -/* ----------------------------------------------------------------------- */ - -static const unsigned char saa711x_i2c_init[] = { - 0x00, 0x00, /* PH711x_CHIP_VERSION 00 - ID byte */ - 0x01, 0x08, /* PH711x_INCREMENT_DELAY - (1) (1) (1) (1) IDEL3 IDEL2 IDELL1 IDEL0 */ - 0x02, 0xc0, /* PH711x_ANALOG_INPUT_CONTR_1 - FUSE1 FUSE0 GUDL1 GUDL0 MODE3 MODE2 MODE1 MODE0 */ - 0x03, 0x23, /* PH711x_ANALOG_INPUT_CONTR_2 - (1) HLNRS VBSL WPOFF HOLDG GAFIX GAI28 GAI18 */ - 0x04, 0x00, /* PH711x_ANALOG_INPUT_CONTR_3 - GAI17 GAI16 GAI15 GAI14 GAI13 GAI12 GAI11 GAI10 */ - 0x05, 0x00, /* PH711x_ANALOG_INPUT_CONTR_4 - GAI27 GAI26 GAI25 GAI24 GAI23 GAI22 GAI21 GAI20 */ - 0x06, 0xeb, /* PH711x_HORIZONTAL_SYNC_START - HSB7 HSB6 HSB5 HSB4 HSB3 HSB2 HSB1 HSB0 */ - 0x07, 0xe0, /* PH711x_HORIZONTAL_SYNC_STOP - HSS7 HSS6 HSS5 HSS4 HSS3 HSS2 HSS1 HSS0 */ - 0x08, 0x88, /* PH711x_SYNC_CONTROL - AUFD FSEL FOET HTC1 HTC0 HPLL VNOI1 VNOI0 */ - 0x09, 0x00, /* PH711x_LUMINANCE_CONTROL - BYPS PREF BPSS1 BPSS0 VBLB UPTCV APER1 APER0 */ - 0x0a, 0x80, /* PH711x_LUMINANCE_BRIGHTNESS - BRIG7 BRIG6 BRIG5 BRIG4 BRIG3 BRIG2 BRIG1 BRIG0 */ - 0x0b, 0x47, /* PH711x_LUMINANCE_CONTRAST - CONT7 CONT6 CONT5 CONT4 CONT3 CONT2 CONT1 CONT0 */ - 0x0c, 0x40, /* PH711x_CHROMA_SATURATION - SATN7 SATN6 SATN5 SATN4 SATN3 SATN2 SATN1 SATN0 */ - 0x0d, 0x00, /* PH711x_CHROMA_HUE_CONTROL - HUEC7 HUEC6 HUEC5 HUEC4 HUEC3 HUEC2 HUEC1 HUEC0 */ - 0x0e, 0x01, /* PH711x_CHROMA_CONTROL - CDTO CSTD2 CSTD1 CSTD0 DCCF FCTC CHBW1 CHBW0 */ - 0x0f, 0xaa, /* PH711x_CHROMA_GAIN_CONTROL - ACGC CGAIN6 CGAIN5 CGAIN4 CGAIN3 CGAIN2 CGAIN1 CGAIN0 */ - 0x10, 0x00, /* PH711x_FORMAT_DELAY_CONTROL - OFTS1 OFTS0 HDEL1 HDEL0 VRLN YDEL2 YDEL1 YDEL0 */ - 0x11, 0x1C, /* PH711x_OUTPUT_CONTROL_1 - GPSW1 CM99 GPSW0 HLSEL OEYC OERT VIPB COLO */ - 0x12, 0x01, /* PH711x_OUTPUT_CONTROL_2 - RTSE13 RTSE12 RTSE11 RTSE10 RTSE03 RTSE02 RTSE01 RTSE00 */ - 0x13, 0x00, /* PH711x_OUTPUT_CONTROL_3 - ADLSB (1) (1) OLDSB FIDP (1) AOSL1 AOSL0 */ - 0x14, 0x00, /* RESERVED 14 - (1) (1) (1) (1) (1) (1) (1) (1) */ - 0x15, 0x00, /* PH711x_V_GATE1_START - VSTA7 VSTA6 VSTA5 VSTA4 VSTA3 VSTA2 VSTA1 VSTA0 */ - 0x16, 0x00, /* PH711x_V_GATE1_STOP - VSTO7 VSTO6 VSTO5 VSTO4 VSTO3 VSTO2 VSTO1 VSTO0 */ - 0x17, 0x00, /* PH711x_V_GATE1_MSB - (1) (1) (1) (1) (1) (1) VSTO8 VSTA8 */ -}; - -static int -saa711x_command (struct i2c_client *client, - unsigned int cmd, - void *arg) -{ - struct saa711x *decoder = i2c_get_clientdata(client); - - switch (cmd) { - - case 0: - case DECODER_INIT: - { - struct video_decoder_init *init = arg; - if (NULL != init) - return saa711x_init_decoder(client, init); - else { - struct video_decoder_init vdi; - vdi.data = saa711x_i2c_init; - vdi.len = sizeof(saa711x_i2c_init); - return saa711x_init_decoder(client, &vdi); - } - } - - case DECODER_DUMP: - { - int i; - - for (i = 0; i < 32; i += 16) { - int j; - - printk(KERN_DEBUG "%s: %03x", I2C_NAME(client), i); - for (j = 0; j < 16; ++j) { - printk(" %02x", - saa711x_read(client, i + j)); - } - printk("\n"); - } - } - break; - - case DECODER_GET_CAPABILITIES: - { - struct video_decoder_capability *cap = arg; - - cap->flags = VIDEO_DECODER_PAL | - VIDEO_DECODER_NTSC | - VIDEO_DECODER_SECAM | - VIDEO_DECODER_AUTO | - VIDEO_DECODER_CCIR; - cap->inputs = 8; - cap->outputs = 1; - } - break; - - case DECODER_GET_STATUS: - { - int *iarg = arg; - int status; - int res; - - status = saa711x_read(client, 0x1f); - dprintk(1, KERN_DEBUG "%s status: 0x%02x\n", I2C_NAME(client), - status); - res = 0; - if ((status & (1 << 6)) == 0) { - res |= DECODER_STATUS_GOOD; - } - switch (decoder->norm) { - case VIDEO_MODE_NTSC: - res |= DECODER_STATUS_NTSC; - break; - case VIDEO_MODE_PAL: - res |= DECODER_STATUS_PAL; - break; - case VIDEO_MODE_SECAM: - res |= DECODER_STATUS_SECAM; - break; - default: - case VIDEO_MODE_AUTO: - if ((status & (1 << 5)) != 0) { - res |= DECODER_STATUS_NTSC; - } else { - res |= DECODER_STATUS_PAL; - } - break; - } - if ((status & (1 << 0)) != 0) { - res |= DECODER_STATUS_COLOR; - } - *iarg = res; - } - break; - - case DECODER_SET_GPIO: - { - int *iarg = arg; - if (0 != *iarg) { - saa711x_write(client, 0x11, - (decoder->reg[0x11] | 0x80)); - } else { - saa711x_write(client, 0x11, - (decoder->reg[0x11] & 0x7f)); - } - break; - } - - case DECODER_SET_VBI_BYPASS: - { - int *iarg = arg; - if (0 != *iarg) { - saa711x_write(client, 0x13, - (decoder->reg[0x13] & 0xf0) | 0x0a); - } else { - saa711x_write(client, 0x13, - (decoder->reg[0x13] & 0xf0)); - } - break; - } - - case DECODER_SET_NORM: - { - int *iarg = arg; - - switch (*iarg) { - - case VIDEO_MODE_NTSC: - saa711x_write(client, 0x08, - (decoder->reg[0x08] & 0x3f) | 0x40); - saa711x_write(client, 0x0e, - (decoder->reg[0x0e] & 0x8f)); - break; - - case VIDEO_MODE_PAL: - saa711x_write(client, 0x08, - (decoder->reg[0x08] & 0x3f) | 0x00); - saa711x_write(client, 0x0e, - (decoder->reg[0x0e] & 0x8f)); - break; - - case VIDEO_MODE_SECAM: - saa711x_write(client, 0x08, - (decoder->reg[0x08] & 0x3f) | 0x00); - saa711x_write(client, 0x0e, - (decoder->reg[0x0e] & 0x8f) | 0x50); - break; - - case VIDEO_MODE_AUTO: - saa711x_write(client, 0x08, - (decoder->reg[0x08] & 0x3f) | 0x80); - saa711x_write(client, 0x0e, - (decoder->reg[0x0e] & 0x8f)); - break; - - default: - return -EINVAL; - - } - decoder->norm = *iarg; - } - break; - - case DECODER_SET_INPUT: - { - int *iarg = arg; - if (*iarg < 0 || *iarg > 9) { - return -EINVAL; - } - if (decoder->input != *iarg) { - decoder->input = *iarg; - /* select mode */ - saa711x_write(client, 0x02, - (decoder->reg[0x02] & 0xf0) | decoder->input); - /* bypass chrominance trap for modes 4..7 */ - saa711x_write(client, 0x09, - (decoder->reg[0x09] & 0x7f) | ((decoder->input > 3) ? 0x80 : 0)); - } - } - break; - - case DECODER_SET_OUTPUT: - { - int *iarg = arg; - - /* not much choice of outputs */ - if (*iarg != 0) { - return -EINVAL; - } - } - break; - - case DECODER_ENABLE_OUTPUT: - { - int *iarg = arg; - int enable = (*iarg != 0); - - if (decoder->enable != enable) { - decoder->enable = enable; - - /* RJ: If output should be disabled (for - * playing videos), we also need a open PLL. - * The input is set to 0 (where no input - * source is connected), although this - * is not necessary. - * - * If output should be enabled, we have to - * reverse the above. - */ - - if (decoder->enable) { - saa711x_write(client, 0x02, - (decoder-> - reg[0x02] & 0xf8) | - decoder->input); - saa711x_write(client, 0x08, - (decoder->reg[0x08] & 0xfb)); - saa711x_write(client, 0x11, - (decoder-> - reg[0x11] & 0xf3) | 0x0c); - } else { - saa711x_write(client, 0x02, - (decoder->reg[0x02] & 0xf8)); - saa711x_write(client, 0x08, - (decoder-> - reg[0x08] & 0xfb) | 0x04); - saa711x_write(client, 0x11, - (decoder->reg[0x11] & 0xf3)); - } - } - } - break; - - case DECODER_SET_PICTURE: - { - struct video_picture *pic = arg; - - if (decoder->bright != pic->brightness) { - /* We want 0 to 255 we get 0-65535 */ - decoder->bright = pic->brightness; - saa711x_write(client, 0x0a, decoder->bright >> 8); - } - if (decoder->contrast != pic->contrast) { - /* We want 0 to 127 we get 0-65535 */ - decoder->contrast = pic->contrast; - saa711x_write(client, 0x0b, - decoder->contrast >> 9); - } - if (decoder->sat != pic->colour) { - /* We want 0 to 127 we get 0-65535 */ - decoder->sat = pic->colour; - saa711x_write(client, 0x0c, decoder->sat >> 9); - } - if (decoder->hue != pic->hue) { - /* We want -128 to 127 we get 0-65535 */ - decoder->hue = pic->hue; - saa711x_write(client, 0x0d, - (decoder->hue - 32768) >> 8); - } - } - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ - -/* standard i2c insmod options */ -static unsigned short normal_i2c[] = { - I2C_SAA7113>>1, /* saa7113 */ - I2C_SAA7114>>1, /* saa7114 */ - I2C_CLIENT_END -}; - -I2C_CLIENT_INSMOD; - - -static struct i2c_driver i2c_driver_saa711x; - -static int -saa711x_detect_client (struct i2c_adapter *adapter, - int address, - int kind) -{ - int i; - struct i2c_client *client; - struct saa711x *decoder; - struct video_decoder_init vdi; - - dprintk(1, - KERN_INFO - "saa711x.c: detecting saa711x client on address 0x%x\n", - address << 1); - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return 0; - - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (client == 0) - return -ENOMEM; - client->addr = address; - client->adapter = adapter; - client->driver = &i2c_driver_saa711x; - strlcpy(I2C_NAME(client), "saa711x", sizeof(I2C_NAME(client))); - decoder = kzalloc(sizeof(struct saa711x), GFP_KERNEL); - if (decoder == NULL) { - kfree(client); - return -ENOMEM; - } - decoder->norm = VIDEO_MODE_NTSC; - decoder->input = 0; - decoder->enable = 1; - decoder->bright = 32768; - decoder->contrast = 32768; - decoder->hue = 32768; - decoder->sat = 32768; - i2c_set_clientdata(client, decoder); - - i = i2c_attach_client(client); - if (i) { - kfree(client); - kfree(decoder); - return i; - } - - vdi.data = saa711x_i2c_init; - vdi.len = sizeof(saa711x_i2c_init); - i = saa711x_init_decoder(client, &vdi); - if (i < 0) { - dprintk(1, KERN_ERR "%s_attach error: init status %d\n", - I2C_NAME(client), i); - } else { - dprintk(1, - KERN_INFO - "%s_attach: chip version %x at address 0x%x\n", - I2C_NAME(client), saa711x_read(client, 0x00) >> 4, - client->addr << 1); - } - - return 0; -} - -static int -saa711x_attach_adapter (struct i2c_adapter *adapter) -{ - dprintk(1, - KERN_INFO - "saa711x.c: starting probe for adapter %s (0x%x)\n", - I2C_NAME(adapter), adapter->id); - return i2c_probe(adapter, &addr_data, &saa711x_detect_client); -} - -static int -saa711x_detach_client (struct i2c_client *client) -{ - struct saa711x *decoder = i2c_get_clientdata(client); - int err; - - err = i2c_detach_client(client); - if (err) { - return err; - } - - kfree(decoder); - kfree(client); - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static struct i2c_driver i2c_driver_saa711x = { - .driver = { - .name = "saa711x", - }, - .id = I2C_DRIVERID_SAA711X, - .attach_adapter = saa711x_attach_adapter, - .detach_client = saa711x_detach_client, - .command = saa711x_command, -}; - -static int __init -saa711x_init (void) -{ - return i2c_add_driver(&i2c_driver_saa711x); -} - -static void __exit -saa711x_exit (void) -{ - i2c_del_driver(&i2c_driver_saa711x); -} - -module_init(saa711x_init); -module_exit(saa711x_exit); diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index 79d11a658bd..d0e83fe0ff5 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -666,7 +666,6 @@ static int saa7127_probe(struct i2c_client *client, { struct saa7127_state *state; struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ - int read_result = 0; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -710,20 +709,29 @@ static int saa7127_probe(struct i2c_client *client, saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL); saa7127_set_video_enable(client, 1); - /* Detect if it's an saa7129 */ - read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2); - saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa); - if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { - v4l_info(client, "saa7129 found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, read_result); - saa7127_write_inittab(client, saa7129_init_config_extra); - state->ident = V4L2_IDENT_SAA7129; - } else { - v4l_info(client, "saa7127 found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - state->ident = V4L2_IDENT_SAA7127; + if (id->driver_data) { /* Chip type is already known */ + state->ident = id->driver_data; + } else { /* Needs detection */ + int read_result; + + /* Detect if it's an saa7129 */ + read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2); + saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa); + if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { + saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, + read_result); + state->ident = V4L2_IDENT_SAA7129; + strlcpy(client->name, "saa7129", I2C_NAME_SIZE); + } else { + state->ident = V4L2_IDENT_SAA7127; + strlcpy(client->name, "saa7127", I2C_NAME_SIZE); + } } + + v4l_info(client, "%s found @ 0x%x (%s)\n", client->name, + client->addr << 1, client->adapter->name); + if (state->ident == V4L2_IDENT_SAA7129) + saa7127_write_inittab(client, saa7129_init_config_extra); return 0; } @@ -740,7 +748,11 @@ static int saa7127_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ static struct i2c_device_id saa7127_id[] = { - { "saa7127", 0 }, + { "saa7127_auto", 0 }, /* auto-detection */ + { "saa7126", V4L2_IDENT_SAA7127 }, + { "saa7127", V4L2_IDENT_SAA7127 }, + { "saa7128", V4L2_IDENT_SAA7129 }, + { "saa7129", V4L2_IDENT_SAA7129 }, { } }; MODULE_DEVICE_TABLE(i2c, saa7127_id); @@ -753,4 +765,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .remove = saa7127_remove, .id_table = saa7127_id, }; - diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c index 002e70a33a4..707be175509 100644 --- a/drivers/media/video/saa7134/saa6752hs.c +++ b/drivers/media/video/saa7134/saa6752hs.c @@ -13,7 +13,6 @@ #include <linux/init.h> #include <linux/crc32.h> - #define MPEG_VIDEO_TARGET_BITRATE_MAX 27000 #define MPEG_VIDEO_MAX_BITRATE_MAX 27000 #define MPEG_TOTAL_TARGET_BITRATE_MAX 27000 @@ -21,6 +20,7 @@ /* Addresses to scan */ static unsigned short normal_i2c[] = {0x20, I2C_CLIENT_END}; + I2C_CLIENT_INSMOD; MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder"); @@ -448,6 +448,104 @@ static int handle_ctrl(struct saa6752hs_mpeg_params *params, return 0; } +static int saa6752hs_qctrl(struct saa6752hs_mpeg_params *params, + struct v4l2_queryctrl *qctrl) +{ + int err; + + switch (qctrl->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_L2_BITRATE_256K, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, + V4L2_MPEG_AUDIO_L2_BITRATE_256K); + + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, 1, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + + case V4L2_CID_MPEG_VIDEO_ENCODING: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + + case V4L2_CID_MPEG_VIDEO_ASPECT: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ASPECT_4x3, + V4L2_MPEG_VIDEO_ASPECT_16x9, 1, + V4L2_MPEG_VIDEO_ASPECT_4x3); + + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + err = v4l2_ctrl_query_fill_std(qctrl); + if (err == 0 && + params->vi_bitrate_mode == + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_STREAM_TYPE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_TYPE_MPEG2_TS, + V4L2_MPEG_STREAM_TYPE_MPEG2_TS, 1, + V4L2_MPEG_STREAM_TYPE_MPEG2_TS); + + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + case V4L2_CID_MPEG_VIDEO_BITRATE: + case V4L2_CID_MPEG_STREAM_PID_PMT: + case V4L2_CID_MPEG_STREAM_PID_AUDIO: + case V4L2_CID_MPEG_STREAM_PID_VIDEO: + case V4L2_CID_MPEG_STREAM_PID_PCR: + return v4l2_ctrl_query_fill_std(qctrl); + + default: + break; + } + return -EINVAL; +} + +static int saa6752hs_qmenu(struct saa6752hs_mpeg_params *params, + struct v4l2_querymenu *qmenu) +{ + static const char *mpeg_audio_l2_bitrate[] = { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "256 kbps", + "", + "384 kbps", + NULL + }; + struct v4l2_queryctrl qctrl; + int err; + + qctrl.id = qmenu->id; + err = saa6752hs_qctrl(params, &qctrl); + if (err) + return err; + if (qmenu->id == V4L2_CID_MPEG_AUDIO_L2_BITRATE) + return v4l2_ctrl_query_menu(qmenu, &qctrl, + mpeg_audio_l2_bitrate); + return v4l2_ctrl_query_menu(qmenu, &qctrl, + v4l2_ctrl_get_menu(qmenu->id)); +} + static int saa6752hs_init(struct i2c_client* client) { unsigned char buf[9], buf2[4]; @@ -609,7 +707,6 @@ static int saa6752hs_attach(struct i2c_adapter *adap, int addr, int kind) i2c_attach_client(&h->client); v4l_info(&h->client,"saa6752hs: chip found @ 0x%x\n", addr<<1); - return 0; } @@ -662,6 +759,10 @@ saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) } h->params = params; break; + case VIDIOC_QUERYCTRL: + return saa6752hs_qctrl(&h->params, arg); + case VIDIOC_QUERYMENU: + return saa6752hs_qmenu(&h->params, arg); case VIDIOC_G_FMT: { struct v4l2_format *f = arg; diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index f118de6e367..9929d20320b 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -80,7 +80,6 @@ typedef struct snd_card_saa7134 { } snd_card_saa7134_t; - /* * PCM structure */ @@ -1121,6 +1120,3 @@ late_initcall(saa7134_alsa_init); module_exit(saa7134_alsa_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ricardo Cerqueira"); - - - diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 2618cfa592e..6893f998d29 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -1287,6 +1287,22 @@ struct saa7134_board saa7134_boards[] = { .vmux = 8, }}, }, + [SAA7134_BOARD_AVERMEDIA_M103] = { + /* Massimo Piccioni <dafastidio@libero.it> */ + .name = "AVerMedia MiniPCI DVB-T Hybrid M103", + .audio_clock = 0x187de7, + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + } }, + }, [SAA7134_BOARD_NOVAC_PRIMETV7133] = { /* toshii@netbsd.org */ .name = "Noval Prime TV 7133", @@ -3503,6 +3519,39 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, .gpio = 0x0200000, }, + }, + [SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = { + .name = "ASUSTeK P7131 Analog", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 1 << 21, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000000, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + }, { + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, }, [SAA7134_BOARD_SABRENT_TV_PCB05] = { .name = "Sabrent PCMCIA TV-PCB05", @@ -3940,32 +3989,111 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_BEHOLD_M6] = { /* Igor Kuznetsov <igk@igk.ru> */ /* Andrey Melnikoff <temnota@kmv.ru> */ - .name = "Beholder BeholdTV M6 / BeholdTV M6 Extra", + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV M6", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .inputs = {{ + .inputs = { { .name = name_tv, .vmux = 3, .amux = TV, .tv = 1, - },{ + }, { .name = name_comp1, .vmux = 1, .amux = LINE1, - },{ + }, { .name = name_svideo, .vmux = 8, .amux = LINE1, - }}, + } }, .radio = { .name = name_radio, .amux = LINE2, }, .mpeg = SAA7134_MPEG_EMPRESS, + .video_out = CCIR656, + .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED | + SET_CLOCK_NOT_DELAYED | + SET_CLOCK_INVERTED | + SET_VSYNC_OFF), + }, + [SAA7134_BOARD_BEHOLD_M63] = { + /* Igor Kuznetsov <igk@igk.ru> */ + /* Andrey Melnikoff <temnota@kmv.ru> */ + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV M63", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mpeg = SAA7134_MPEG_EMPRESS, + .video_out = CCIR656, + .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED | + SET_CLOCK_NOT_DELAYED | + SET_CLOCK_INVERTED | + SET_VSYNC_OFF), + }, + [SAA7134_BOARD_BEHOLD_M6_EXTRA] = { + /* Igor Kuznetsov <igk@igk.ru> */ + /* Andrey Melnikoff <temnota@kmv.ru> */ + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV M6 Extra", + .audio_clock = 0x00187de7, + /* FIXME: Must be PHILIPS_FM1216ME_MK5*/ + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mpeg = SAA7134_MPEG_EMPRESS, + .video_out = CCIR656, + .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED | + SET_CLOCK_NOT_DELAYED | + SET_CLOCK_INVERTED | + SET_VSYNC_OFF), }, [SAA7134_BOARD_TWINHAN_DTV_DVB_3056] = { .name = "Twinhan Hybrid DTV-DVB 3056 PCI", @@ -4121,9 +4249,9 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, .tv = 1, }, { - .name = name_comp1, - .vmux = 3, - .amux = LINE2, + .name = name_comp, + .vmux = 0, + .amux = LINE1, }, { .name = name_svideo, .vmux = 8, @@ -4141,6 +4269,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_tv, .vmux = 1, @@ -4150,6 +4279,10 @@ struct saa7134_board saa7134_boards[] = { .name = name_svideo, .vmux = 8, .amux = LINE1, + }, { + .name = name_comp, + .vmux = 0, + .amux = LINE1, } }, .radio = { .name = name_radio, @@ -4163,7 +4296,6 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_tv, .vmux = 1, @@ -5226,13 +5358,13 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x5ace, .subdevice = 0x6193, - .driver_data = SAA7134_BOARD_BEHOLD_M6, + .driver_data = SAA7134_BOARD_BEHOLD_M6_EXTRA, }, { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x5ace, .subdevice = 0x6191, - .driver_data = SAA7134_BOARD_BEHOLD_M6, + .driver_data = SAA7134_BOARD_BEHOLD_M63, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -5284,10 +5416,22 @@ struct pci_device_id saa7134_pci_tbl[] = { }, { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5169, + .subdevice = 0x1502, + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x5ace, .subdevice = 0x6290, .driver_data = SAA7134_BOARD_BEHOLD_H6, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf636, + .driver_data = SAA7134_BOARD_AVERMEDIA_M103, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -5352,6 +5496,7 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev, saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000); switch (dev->board) { case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + case SAA7134_BOARD_AVERMEDIA_M103: saa7134_set_gpio(dev, 23, 0); msleep(10); saa7134_set_gpio(dev, 23, 1); @@ -5493,6 +5638,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: + case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_PROTEUS_2309: case SAA7134_BOARD_AVERMEDIA_A16AR: @@ -5560,6 +5706,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) msleep(10); break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + case SAA7134_BOARD_AVERMEDIA_M103: saa7134_set_gpio(dev, 23, 0); msleep(10); saa7134_set_gpio(dev, 23, 1); @@ -5601,6 +5748,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_HAUPPAUGE_HVR1110: case SAA7134_BOARD_BEHOLD_607_9FM: case SAA7134_BOARD_BEHOLD_M6: + case SAA7134_BOARD_BEHOLD_M63: + case SAA7134_BOARD_BEHOLD_M6_EXTRA: dev->has_remote = SAA7134_REMOTE_I2C; break; case SAA7134_BOARD_AVERMEDIA_A169_B: @@ -5683,6 +5832,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) switch (dev->board) { case SAA7134_BOARD_AVERMEDIA_A16D: case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + case SAA7134_BOARD_AVERMEDIA_M103: ctl.demod = XC3028_FE_ZARLINK456; break; default: @@ -5825,6 +5975,15 @@ int saa7134_board_init2(struct saa7134_dev *dev) i2c_transfer(&dev->i2c_adap, &msg, 1); break; } + case SAA7134_BOARD_ASUSTeK_TVFM7135: + /* The card below is detected as card=53, but is different */ + if (dev->autodetected && (dev->eedata[0x27] == 0x03)) { + dev->board = SAA7134_BOARD_ASUSTeK_P7131_ANALOG; + printk(KERN_INFO "%s: P7131 analog only, using " + "entry of %s\n", + dev->name, saa7134_boards[dev->board].name); + } + break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: hauppauge_eeprom(dev, dev->eedata+0x80); /* break intentionally omitted */ diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 2c19cd0113c..cfee84ee7a8 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -150,7 +150,6 @@ void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value) #if defined(CONFIG_MODULES) && defined(MODULE) - static void request_module_async(struct work_struct *work){ struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk); if (card_is_empress(dev)) diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 341b101b035..be48b9b66a6 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -1263,6 +1263,7 @@ static int dvb_init(struct saa7134_dev *dev) &avermedia_xc3028_mt352_dev, &dev->i2c_adap); attach_xc3028 = 1; + break; case SAA7134_BOARD_MD7134_BRIDGE_2: dev->dvb.frontend = dvb_attach(tda10086_attach, &sd1878_4m, &dev->i2c_adap); @@ -1290,6 +1291,15 @@ static int dvb_init(struct saa7134_dev *dev) fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage; } break; + case SAA7134_BOARD_AVERMEDIA_M103: + saa7134_set_gpio(dev, 25, 0); + msleep(10); + saa7134_set_gpio(dev, 25, 1); + dev->dvb.frontend = dvb_attach(mt352_attach, + &avermedia_xc3028_mt352_dev, + &dev->i2c_adap); + attach_xc3028 = 1; + break; default: wprintk("Huh? unknown DVB card?\n"); break; diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index 3ae71a34082..2a5ab957542 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -208,7 +208,7 @@ static int empress_s_input(struct file *file, void *priv, unsigned int i) return 0; } -static int empress_enum_fmt_cap(struct file *file, void *priv, +static int empress_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (f->index != 0) @@ -220,7 +220,7 @@ static int empress_enum_fmt_cap(struct file *file, void *priv, return 0; } -static int empress_g_fmt_cap(struct file *file, void *priv, +static int empress_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_dev *dev = file->private_data; @@ -233,7 +233,7 @@ static int empress_g_fmt_cap(struct file *file, void *priv, return 0; } -static int empress_s_fmt_cap(struct file *file, void *priv, +static int empress_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_dev *dev = file->private_data; @@ -294,10 +294,20 @@ static int empress_streamoff(struct file *file, void *priv, return videobuf_streamoff(&dev->empress_tsq); } +static int saa7134_i2c_call_saa6752(struct saa7134_dev *dev, + unsigned int cmd, void *arg) +{ + if (dev->mpeg_i2c_client == NULL) + return -EINVAL; + return dev->mpeg_i2c_client->driver->command(dev->mpeg_i2c_client, + cmd, arg); +} + static int empress_s_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *ctrls) { struct saa7134_dev *dev = file->private_data; + int err; /* count == 0 is abused in saa6752hs.c, so that special case is handled here explicitly. */ @@ -307,10 +317,10 @@ static int empress_s_ext_ctrls(struct file *file, void *priv, if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) return -EINVAL; - saa7134_i2c_call_clients(dev, VIDIOC_S_EXT_CTRLS, ctrls); + err = saa7134_i2c_call_saa6752(dev, VIDIOC_S_EXT_CTRLS, ctrls); ts_init_encoder(dev); - return 0; + return err; } static int empress_g_ext_ctrls(struct file *file, void *priv, @@ -320,9 +330,62 @@ static int empress_g_ext_ctrls(struct file *file, void *priv, if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) return -EINVAL; - saa7134_i2c_call_clients(dev, VIDIOC_G_EXT_CTRLS, ctrls); + return saa7134_i2c_call_saa6752(dev, VIDIOC_G_EXT_CTRLS, ctrls); +} - return 0; +static int empress_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + static const u32 user_ctrls[] = { + V4L2_CID_USER_CLASS, + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_AUDIO_VOLUME, + V4L2_CID_AUDIO_MUTE, + V4L2_CID_HFLIP, + 0 + }; + + static const u32 mpeg_ctrls[] = { + V4L2_CID_MPEG_CLASS, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_CID_MPEG_AUDIO_L2_BITRATE, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_CID_MPEG_VIDEO_BITRATE, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 0 + }; + static const u32 *ctrl_classes[] = { + user_ctrls, + mpeg_ctrls, + NULL + }; + struct saa7134_dev *dev = file->private_data; + + c->id = v4l2_ctrl_next(ctrl_classes, c->id); + if (c->id == 0) + return -EINVAL; + if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS) + return v4l2_ctrl_query_fill_std(c); + if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG) + return saa7134_queryctrl(file, priv, c); + return saa7134_i2c_call_saa6752(dev, VIDIOC_QUERYCTRL, c); +} + +static int empress_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *c) +{ + struct saa7134_dev *dev = file->private_data; + + if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + return saa7134_i2c_call_saa6752(dev, VIDIOC_QUERYMENU, c); } static const struct file_operations ts_fops = @@ -348,9 +411,9 @@ static struct video_device saa7134_empress_template = .minor = -1, .vidioc_querycap = empress_querycap, - .vidioc_enum_fmt_cap = empress_enum_fmt_cap, - .vidioc_s_fmt_cap = empress_s_fmt_cap, - .vidioc_g_fmt_cap = empress_g_fmt_cap, + .vidioc_enum_fmt_vid_cap = empress_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = empress_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = empress_g_fmt_vid_cap, .vidioc_reqbufs = empress_reqbufs, .vidioc_querybuf = empress_querybuf, .vidioc_qbuf = empress_qbuf, @@ -363,7 +426,8 @@ static struct video_device saa7134_empress_template = .vidioc_g_input = empress_g_input, .vidioc_s_input = empress_s_input, - .vidioc_queryctrl = saa7134_queryctrl, + .vidioc_queryctrl = empress_queryctrl, + .vidioc_querymenu = empress_querymenu, .vidioc_g_ctrl = saa7134_g_ctrl, .vidioc_s_ctrl = saa7134_s_ctrl, diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index d8af3863f2d..5f713e63768 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -327,6 +327,8 @@ static int attach_inform(struct i2c_client *client) d1printk( "%s i2c attach [addr=0x%x,client=%s]\n", client->driver->driver.name, client->addr, client->name); + if (client->addr == 0x20 && client->driver && client->driver->command) + dev->mpeg_i2c_client = client; /* Am I an i2c remote control? */ diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 76e6501d238..ad08d13dffd 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -198,6 +198,84 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } +/* Common (grey or coloured) pinnacle PCTV remote handling + * + */ +static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, + int parity_offset, int marker, int code_modulo) +{ + unsigned char b[4]; + unsigned int start = 0,parity = 0,code = 0; + + /* poll IR chip */ + if (4 != i2c_master_recv(&ir->c, b, 4)) { + i2cdprintk("read error\n"); + return -EIO; + } + + for (start = 0; start < ARRAY_SIZE(b); start++) { + if (b[start] == marker) { + code=b[(start+parity_offset + 1) % 4]; + parity=b[(start+parity_offset) % 4]; + } + } + + /* Empty Request */ + if (parity == 0) + return 0; + + /* Repeating... */ + if (ir->old == parity) + return 0; + + ir->old = parity; + + /* drop special codes when a key is held down a long time for the grey controller + In this case, the second bit of the code is asserted */ + if (marker == 0xfe && (code & 0x40)) + return 0; + + code %= code_modulo; + + *ir_raw = code; + *ir_key = code; + + i2cdprintk("Pinnacle PCTV key %02x\n", code); + + return 1; +} + +/* The grey pinnacle PCTV remote + * + * There are one issue with this remote: + * - I2c packet does not change when the same key is pressed quickly. The workaround + * is to hold down each key for about half a second, so that another code is generated + * in the i2c packet, and the function can distinguish key presses. + * + * Sylvain Pasche <sylvain.pasche@gmail.com> + */ +static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + + return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff); +} + + +/* The new pinnacle PCTV remote (with the colored buttons) + * + * Ricardo Cerqueira <v4l@cerqueira.org> + */ +static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + /* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE + * + * this is the only value that results in 42 unique + * codes < 128 + */ + + return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88); +} + void saa7134_input_irq(struct saa7134_dev *dev) { struct card_ir *ir = dev->remote; @@ -409,6 +487,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: + case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: ir_codes = ir_codes_asus_pc39; mask_keydown = 0x0040000; rc5_gpio = 1; @@ -540,6 +619,8 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir) break; case SAA7134_BOARD_BEHOLD_607_9FM: case SAA7134_BOARD_BEHOLD_M6: + case SAA7134_BOARD_BEHOLD_M63: + case SAA7134_BOARD_BEHOLD_M6_EXTRA: case SAA7134_BOARD_BEHOLD_H6: snprintf(ir->c.name, sizeof(ir->c.name), "BeholdTV"); ir->get_key = get_key_beholdm6xx; diff --git a/drivers/media/video/saa7134/saa7134-reg.h b/drivers/media/video/saa7134/saa7134-reg.h index 86f5eefdb0f..cf89d96d729 100644 --- a/drivers/media/video/saa7134/saa7134-reg.h +++ b/drivers/media/video/saa7134/saa7134-reg.h @@ -368,6 +368,7 @@ #define SAA7135_DSP_RWCLEAR 0x586 #define SAA7135_DSP_RWCLEAR_RERR 1 +#define SAA7133_I2S_AUDIO_CONTROL 0x591 /* ------------------------------------------------------------------ */ /* * Local variables: diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c index 232af598d94..c5d0b44c179 100644 --- a/drivers/media/video/saa7134/saa7134-tvaudio.c +++ b/drivers/media/video/saa7134/saa7134-tvaudio.c @@ -477,7 +477,6 @@ static int tvaudio_thread(void *data) unsigned int i, audio, nscan; int max1,max2,carrier,rx,mode,lastmode,default_carrier; - set_freezable(); for (;;) { @@ -775,7 +774,6 @@ static int tvaudio_thread_ddep(void *data) struct saa7134_dev *dev = data; u32 value, norms; - set_freezable(); for (;;) { tvaudio_sleep(dev,-1); @@ -873,13 +871,34 @@ void saa7134_enable_i2s(struct saa7134_dev *dev) if (!card_is_empress(dev)) return; - i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01; - /* enable I2S audio output for the mpeg encoder */ - saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80); - saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format); - saa_writeb(SAA7134_I2S_OUTPUT_LEVEL, 0x0F); - saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01); + if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130) + return; + + /* configure GPIO for out */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0E000000, 0x00000000); + + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + /* Set I2S format (SONY) Â */ + saa_writeb(SAA7133_I2S_AUDIO_CONTROL, 0x00); + /* Start I2S */ + saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x11); + break; + + case PCI_DEVICE_ID_PHILIPS_SAA7134: + i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01; + + /* enable I2S audio output for the mpeg encoder */ + saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80); + saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format); + saa_writeb(SAA7134_I2S_OUTPUT_LEVEL, 0x0F); + saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01); + + default: + break; + } } int saa7134_tvaudio_rx2mode(u32 rx) diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 48e1a01718e..1a5137550e7 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1496,7 +1496,7 @@ static int video_mmap(struct file *file, struct vm_area_struct * vma) /* ------------------------------------------------------------------ */ -static int saa7134_try_get_set_fmt_vbi(struct file *file, void *priv, +static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_fh *fh = priv; @@ -1516,7 +1516,7 @@ static int saa7134_try_get_set_fmt_vbi(struct file *file, void *priv, return 0; } -static int saa7134_g_fmt_cap(struct file *file, void *priv, +static int saa7134_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_fh *fh = priv; @@ -1532,7 +1532,7 @@ static int saa7134_g_fmt_cap(struct file *file, void *priv, return 0; } -static int saa7134_g_fmt_overlay(struct file *file, void *priv, +static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_fh *fh = priv; @@ -1546,7 +1546,7 @@ static int saa7134_g_fmt_overlay(struct file *file, void *priv, return 0; } -static int saa7134_try_fmt_cap(struct file *file, void *priv, +static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_fh *fh = priv; @@ -1597,7 +1597,7 @@ static int saa7134_try_fmt_cap(struct file *file, void *priv, return 0; } -static int saa7134_try_fmt_overlay(struct file *file, void *priv, +static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_fh *fh = priv; @@ -1611,13 +1611,13 @@ static int saa7134_try_fmt_overlay(struct file *file, void *priv, return verify_preview(dev, &f->fmt.win); } -static int saa7134_s_fmt_cap(struct file *file, void *priv, +static int saa7134_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_fh *fh = priv; int err; - err = saa7134_try_fmt_cap(file, priv, f); + err = saa7134_try_fmt_vid_cap(file, priv, f); if (0 != err) return err; @@ -1628,7 +1628,7 @@ static int saa7134_s_fmt_cap(struct file *file, void *priv, return 0; } -static int saa7134_s_fmt_overlay(struct file *file, void *priv, +static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_format *f) { struct saa7134_fh *fh = priv; @@ -2028,7 +2028,7 @@ static int saa7134_s_priority(struct file *file, void *f, return v4l2_prio_change(&dev->prio, &fh->prio, prio); } -static int saa7134_enum_fmt_cap(struct file *file, void *priv, +static int saa7134_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (f->index >= FORMATS) @@ -2042,7 +2042,7 @@ static int saa7134_enum_fmt_cap(struct file *file, void *priv, return 0; } -static int saa7134_enum_fmt_overlay(struct file *file, void *priv, +static int saa7134_enum_fmt_vid_overlay(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (saa7134_no_overlay > 0) { @@ -2061,7 +2061,7 @@ static int saa7134_enum_fmt_overlay(struct file *file, void *priv, return 0; } -static int saa7134_enum_fmt_vbi(struct file *file, void *priv, +static int saa7134_enum_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (0 != f->index) @@ -2208,6 +2208,32 @@ static int saa7134_g_parm(struct file *file, void *fh, return 0; } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register (struct file *file, void *priv, + struct v4l2_register *reg) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; + reg->val = saa_readb(reg->reg); + return 0; +} + +static int vidioc_s_register (struct file *file, void *priv, + struct v4l2_register *reg) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; + saa_writeb(reg->reg&0xffffff, reg->val); + return 0; +} +#endif + static int radio_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { @@ -2348,18 +2374,18 @@ struct video_device saa7134_video_template = .fops = &video_fops, .minor = -1, .vidioc_querycap = saa7134_querycap, - .vidioc_enum_fmt_cap = saa7134_enum_fmt_cap, - .vidioc_g_fmt_cap = saa7134_g_fmt_cap, - .vidioc_try_fmt_cap = saa7134_try_fmt_cap, - .vidioc_s_fmt_cap = saa7134_s_fmt_cap, - .vidioc_enum_fmt_overlay = saa7134_enum_fmt_overlay, - .vidioc_g_fmt_overlay = saa7134_g_fmt_overlay, - .vidioc_try_fmt_overlay = saa7134_try_fmt_overlay, - .vidioc_s_fmt_overlay = saa7134_s_fmt_overlay, - .vidioc_enum_fmt_vbi = saa7134_enum_fmt_vbi, - .vidioc_g_fmt_vbi = saa7134_try_get_set_fmt_vbi, - .vidioc_try_fmt_vbi = saa7134_try_get_set_fmt_vbi, - .vidioc_s_fmt_vbi = saa7134_try_get_set_fmt_vbi, + .vidioc_enum_fmt_vid_cap = saa7134_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = saa7134_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = saa7134_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = saa7134_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_overlay = saa7134_enum_fmt_vid_overlay, + .vidioc_g_fmt_vid_overlay = saa7134_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_overlay = saa7134_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = saa7134_s_fmt_vid_overlay, + .vidioc_enum_fmt_vbi_cap = saa7134_enum_fmt_vbi_cap, + .vidioc_g_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, .vidioc_g_audio = saa7134_g_audio, .vidioc_s_audio = saa7134_s_audio, .vidioc_cropcap = saa7134_cropcap, @@ -2391,6 +2417,10 @@ struct video_device saa7134_video_template = .vidioc_g_parm = saa7134_g_parm, .vidioc_g_frequency = saa7134_g_frequency, .vidioc_s_frequency = saa7134_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif .tvnorms = SAA7134_NORMS, .current_norm = V4L2_STD_PAL, }; @@ -2458,13 +2488,14 @@ int saa7134_videoport_init(struct saa7134_dev *dev) int vo = saa7134_boards[dev->board].video_out; int video_reg; unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts; + + /* Configure videoport */ saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]); video_reg = video_out[vo][1]; if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED) video_reg &= ~VP_T_CODE_P_INVERTED; saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg); saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]); - saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]); saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]); video_reg = video_out[vo][5]; if (vid_port_opts & SET_CLOCK_NOT_DELAYED) @@ -2481,6 +2512,9 @@ int saa7134_videoport_init(struct saa7134_dev *dev) saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]); saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]); + /* Start videoport */ + saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]); + return 0; } diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 34ff0d4998f..6927cbea862 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -264,7 +264,10 @@ struct saa7134_format { #define SAA7134_BOARD_AVERMEDIA_A700_PRO 140 #define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141 #define SAA7134_BOARD_BEHOLD_H6 142 - +#define SAA7134_BOARD_BEHOLD_M63 143 +#define SAA7134_BOARD_BEHOLD_M6_EXTRA 144 +#define SAA7134_BOARD_AVERMEDIA_M103 145 +#define SAA7134_BOARD_ASUSTeK_P7131_ANALOG 146 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 @@ -552,6 +555,7 @@ struct saa7134_dev { struct saa7134_ts ts; struct saa7134_dmaqueue ts_q; struct saa7134_mpeg_ops *mops; + struct i2c_client *mpeg_i2c_client; /* SAA7134_MPEG_EMPRESS only */ struct video_device *empress_dev; diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c new file mode 100644 index 00000000000..012005e1a77 --- /dev/null +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -0,0 +1,657 @@ +/* + * V4L2 Driver for SuperH Mobile CEU interface + * + * Copyright (C) 2008 Magnus Damm + * + * Based on V4L2 Driver for PXA camera host - "pxa_camera.c", + * + * Copyright (C) 2006, Sascha Hauer, Pengutronix + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/moduleparam.h> +#include <linux/time.h> +#include <linux/version.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/videodev2.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/soc_camera.h> +#include <media/sh_mobile_ceu.h> +#include <media/videobuf-dma-contig.h> + +/* register offsets for sh7722 / sh7723 */ + +#define CAPSR 0x00 +#define CAPCR 0x04 +#define CAMCR 0x08 +#define CMCYR 0x0c +#define CAMOR 0x10 +#define CAPWR 0x14 +#define CAIFR 0x18 +#define CSTCR 0x20 /* not on sh7723 */ +#define CSECR 0x24 /* not on sh7723 */ +#define CRCNTR 0x28 +#define CRCMPR 0x2c +#define CFLCR 0x30 +#define CFSZR 0x34 +#define CDWDR 0x38 +#define CDAYR 0x3c +#define CDACR 0x40 +#define CDBYR 0x44 +#define CDBCR 0x48 +#define CBDSR 0x4c +#define CFWCR 0x5c +#define CLFCR 0x60 +#define CDOCR 0x64 +#define CDDCR 0x68 +#define CDDAR 0x6c +#define CEIER 0x70 +#define CETCR 0x74 +#define CSTSR 0x7c +#define CSRTR 0x80 +#define CDSSR 0x84 +#define CDAYR2 0x90 +#define CDACR2 0x94 +#define CDBYR2 0x98 +#define CDBCR2 0x9c + +static DEFINE_MUTEX(camera_lock); + +/* per video frame buffer */ +struct sh_mobile_ceu_buffer { + struct videobuf_buffer vb; /* v4l buffer must be first */ + const struct soc_camera_data_format *fmt; +}; + +struct sh_mobile_ceu_dev { + struct device *dev; + struct soc_camera_host ici; + struct soc_camera_device *icd; + + unsigned int irq; + void __iomem *base; + unsigned long video_limit; + + spinlock_t lock; + struct list_head capture; + struct videobuf_buffer *active; + + struct sh_mobile_ceu_info *pdata; +}; + +static void ceu_write(struct sh_mobile_ceu_dev *priv, + unsigned long reg_offs, unsigned long data) +{ + iowrite32(data, priv->base + reg_offs); +} + +static unsigned long ceu_read(struct sh_mobile_ceu_dev *priv, + unsigned long reg_offs) +{ + return ioread32(priv->base + reg_offs); +} + +/* + * Videobuf operations + */ +static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq, + unsigned int *count, + unsigned int *size) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + int bytes_per_pixel = (icd->current_fmt->depth + 7) >> 3; + + *size = PAGE_ALIGN(icd->width * icd->height * bytes_per_pixel); + + if (0 == *count) + *count = 2; + + if (pcdev->video_limit) { + while (*size * *count > pcdev->video_limit) + (*count)--; + } + + dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, + struct sh_mobile_ceu_buffer *buf) +{ + struct soc_camera_device *icd = vq->priv_data; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, + &buf->vb, buf->vb.baddr, buf->vb.bsize); + + if (in_interrupt()) + BUG(); + + videobuf_dma_contig_free(vq, &buf->vb); + dev_dbg(&icd->dev, "%s freed\n", __func__); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) +{ + ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~1); + ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & 0x0317f313); + ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | 1); + + ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~0x10000); + + ceu_write(pcdev, CETCR, 0x0317f313 ^ 0x10); + + if (pcdev->active) { + ceu_write(pcdev, CDAYR, videobuf_to_dma_contig(pcdev->active)); + ceu_write(pcdev, CAPSR, 0x1); /* start capture */ + } +} + +static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct soc_camera_device *icd = vq->priv_data; + struct sh_mobile_ceu_buffer *buf; + int ret; + + buf = container_of(vb, struct sh_mobile_ceu_buffer, vb); + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, + vb, vb->baddr, vb->bsize); + + /* Added list head initialization on alloc */ + WARN_ON(!list_empty(&vb->queue)); + +#ifdef DEBUG + /* This can be useful if you want to see if we actually fill + * the buffer with something */ + memset((void *)vb->baddr, 0xaa, vb->bsize); +#endif + + BUG_ON(NULL == icd->current_fmt); + + if (buf->fmt != icd->current_fmt || + vb->width != icd->width || + vb->height != icd->height || + vb->field != field) { + buf->fmt = icd->current_fmt; + vb->width = icd->width; + vb->height = icd->height; + vb->field = field; + vb->state = VIDEOBUF_NEEDS_INIT; + } + + vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3); + if (0 != vb->baddr && vb->bsize < vb->size) { + ret = -EINVAL; + goto out; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret) + goto fail; + vb->state = VIDEOBUF_PREPARED; + } + + return 0; +fail: + free_buffer(vq, buf); +out: + return ret; +} + +static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + unsigned long flags; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, + vb, vb->baddr, vb->bsize); + + vb->state = VIDEOBUF_ACTIVE; + spin_lock_irqsave(&pcdev->lock, flags); + list_add_tail(&vb->queue, &pcdev->capture); + + if (!pcdev->active) { + pcdev->active = vb; + sh_mobile_ceu_capture(pcdev); + } + + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb)); +} + +static struct videobuf_queue_ops sh_mobile_ceu_videobuf_ops = { + .buf_setup = sh_mobile_ceu_videobuf_setup, + .buf_prepare = sh_mobile_ceu_videobuf_prepare, + .buf_queue = sh_mobile_ceu_videobuf_queue, + .buf_release = sh_mobile_ceu_videobuf_release, +}; + +static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) +{ + struct sh_mobile_ceu_dev *pcdev = data; + struct videobuf_buffer *vb; + unsigned long flags; + + spin_lock_irqsave(&pcdev->lock, flags); + + vb = pcdev->active; + list_del_init(&vb->queue); + + if (!list_empty(&pcdev->capture)) + pcdev->active = list_entry(pcdev->capture.next, + struct videobuf_buffer, queue); + else + pcdev->active = NULL; + + sh_mobile_ceu_capture(pcdev); + + vb->state = VIDEOBUF_DONE; + do_gettimeofday(&vb->ts); + vb->field_count++; + wake_up(&vb->done); + spin_unlock_irqrestore(&pcdev->lock, flags); + + return IRQ_HANDLED; +} + +static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + int ret = -EBUSY; + + mutex_lock(&camera_lock); + + if (pcdev->icd) + goto err; + + dev_info(&icd->dev, + "SuperH Mobile CEU driver attached to camera %d\n", + icd->devnum); + + if (pcdev->pdata->enable_camera) + pcdev->pdata->enable_camera(); + + ret = icd->ops->init(icd); + if (ret) + goto err; + + ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ + while (ceu_read(pcdev, CSTSR) & 1) + msleep(1); + + pcdev->icd = icd; +err: + mutex_unlock(&camera_lock); + + return ret; +} + +static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + + BUG_ON(icd != pcdev->icd); + + /* disable capture, disable interrupts */ + ceu_write(pcdev, CEIER, 0); + ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ + icd->ops->release(icd); + if (pcdev->pdata->disable_camera) + pcdev->pdata->disable_camera(); + + dev_info(&icd->dev, + "SuperH Mobile CEU driver detached from camera %d\n", + icd->devnum); + + pcdev->icd = NULL; +} + +static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, + __u32 pixfmt) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + int ret, buswidth, width, cfszr_width, cdwdr_width; + unsigned long camera_flags, common_flags, value; + + camera_flags = icd->ops->query_bus_param(icd); + common_flags = soc_camera_bus_param_compatible(camera_flags, + pcdev->pdata->flags); + if (!common_flags) + return -EINVAL; + + ret = icd->ops->set_bus_param(icd, common_flags); + if (ret < 0) + return ret; + + switch (common_flags & SOCAM_DATAWIDTH_MASK) { + case SOCAM_DATAWIDTH_8: + buswidth = 8; + break; + case SOCAM_DATAWIDTH_16: + buswidth = 16; + break; + default: + return -EINVAL; + } + + ceu_write(pcdev, CRCNTR, 0); + ceu_write(pcdev, CRCMPR, 0); + + value = 0x00000010; + value |= (common_flags & SOCAM_VSYNC_ACTIVE_LOW) ? (1 << 1) : 0; + value |= (common_flags & SOCAM_HSYNC_ACTIVE_LOW) ? (1 << 0) : 0; + value |= (buswidth == 16) ? (1 << 12) : 0; + ceu_write(pcdev, CAMCR, value); + + ceu_write(pcdev, CAPCR, 0x00300000); + ceu_write(pcdev, CAIFR, 0); + + mdelay(1); + + width = icd->width * (icd->current_fmt->depth / 8); + width = (buswidth == 16) ? width / 2 : width; + cfszr_width = (buswidth == 8) ? width / 2 : width; + cdwdr_width = (buswidth == 16) ? width * 2 : width; + + ceu_write(pcdev, CAMOR, 0); + ceu_write(pcdev, CAPWR, (icd->height << 16) | width); + ceu_write(pcdev, CFLCR, 0); /* data fetch mode - no scaling */ + ceu_write(pcdev, CFSZR, (icd->height << 16) | cfszr_width); + ceu_write(pcdev, CLFCR, 0); /* data fetch mode - no lowpass filter */ + ceu_write(pcdev, CDOCR, 0x00000016); + + ceu_write(pcdev, CDWDR, cdwdr_width); + ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ + + /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */ + /* in data fetch mode: no need for CDACR, CDBYR, CDBCR */ + + return 0; +} + +static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd, + __u32 pixfmt) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + unsigned long camera_flags, common_flags; + + camera_flags = icd->ops->query_bus_param(icd); + common_flags = soc_camera_bus_param_compatible(camera_flags, + pcdev->pdata->flags); + if (!common_flags) + return -EINVAL; + + return 0; +} + +static int sh_mobile_ceu_set_fmt_cap(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + return icd->ops->set_fmt_cap(icd, pixfmt, rect); +} + +static int sh_mobile_ceu_try_fmt_cap(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + /* FIXME: calculate using depth and bus width */ + + if (f->fmt.pix.height < 4) + f->fmt.pix.height = 4; + if (f->fmt.pix.height > 1920) + f->fmt.pix.height = 1920; + if (f->fmt.pix.width < 2) + f->fmt.pix.width = 2; + if (f->fmt.pix.width > 2560) + f->fmt.pix.width = 2560; + f->fmt.pix.width &= ~0x01; + f->fmt.pix.height &= ~0x03; + + /* limit to sensor capabilities */ + return icd->ops->try_fmt_cap(icd, f); +} + +static int sh_mobile_ceu_reqbufs(struct soc_camera_file *icf, + struct v4l2_requestbuffers *p) +{ + int i; + + /* This is for locking debugging only. I removed spinlocks and now I + * check whether .prepare is ever called on a linked buffer, or whether + * a dma IRQ can occur for an in-work or unlinked buffer. Until now + * it hadn't triggered */ + for (i = 0; i < p->count; i++) { + struct sh_mobile_ceu_buffer *buf; + + buf = container_of(icf->vb_vidq.bufs[i], + struct sh_mobile_ceu_buffer, vb); + INIT_LIST_HEAD(&buf->vb.queue); + } + + return 0; +} + +static unsigned int sh_mobile_ceu_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_file *icf = file->private_data; + struct sh_mobile_ceu_buffer *buf; + + buf = list_entry(icf->vb_vidq.stream.next, + struct sh_mobile_ceu_buffer, vb.stream); + + poll_wait(file, &buf->vb.done, pt); + + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return POLLIN|POLLRDNORM; + + return 0; +} + +static int sh_mobile_ceu_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card)); + cap->version = KERNEL_VERSION(0, 0, 5); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + return 0; +} + +static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q, + struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + + videobuf_queue_dma_contig_init(q, + &sh_mobile_ceu_videobuf_ops, + &ici->dev, &pcdev->lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct sh_mobile_ceu_buffer), + icd); +} + +static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { + .owner = THIS_MODULE, + .add = sh_mobile_ceu_add_device, + .remove = sh_mobile_ceu_remove_device, + .set_fmt_cap = sh_mobile_ceu_set_fmt_cap, + .try_fmt_cap = sh_mobile_ceu_try_fmt_cap, + .reqbufs = sh_mobile_ceu_reqbufs, + .poll = sh_mobile_ceu_poll, + .querycap = sh_mobile_ceu_querycap, + .try_bus_param = sh_mobile_ceu_try_bus_param, + .set_bus_param = sh_mobile_ceu_set_bus_param, + .init_videobuf = sh_mobile_ceu_init_videobuf, +}; + +static int sh_mobile_ceu_probe(struct platform_device *pdev) +{ + struct sh_mobile_ceu_dev *pcdev; + struct resource *res; + void __iomem *base; + unsigned int irq; + int err = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!res || !irq) { + dev_err(&pdev->dev, "Not enough CEU platform resources.\n"); + err = -ENODEV; + goto exit; + } + + pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + if (!pcdev) { + dev_err(&pdev->dev, "Could not allocate pcdev\n"); + err = -ENOMEM; + goto exit; + } + + platform_set_drvdata(pdev, pcdev); + INIT_LIST_HEAD(&pcdev->capture); + spin_lock_init(&pcdev->lock); + + pcdev->pdata = pdev->dev.platform_data; + if (!pcdev->pdata) { + err = -EINVAL; + dev_err(&pdev->dev, "CEU platform data not set.\n"); + goto exit_kfree; + } + + base = ioremap_nocache(res->start, res->end - res->start + 1); + if (!base) { + err = -ENXIO; + dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n"); + goto exit_kfree; + } + + pcdev->irq = irq; + pcdev->base = base; + pcdev->video_limit = 0; /* only enabled if second resource exists */ + pcdev->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + err = dma_declare_coherent_memory(&pdev->dev, res->start, + res->start, + (res->end - res->start) + 1, + DMA_MEMORY_MAP | + DMA_MEMORY_EXCLUSIVE); + if (!err) { + dev_err(&pdev->dev, "Unable to declare CEU memory.\n"); + err = -ENXIO; + goto exit_iounmap; + } + + pcdev->video_limit = (res->end - res->start) + 1; + } + + /* request irq */ + err = request_irq(pcdev->irq, sh_mobile_ceu_irq, IRQF_DISABLED, + pdev->dev.bus_id, pcdev); + if (err) { + dev_err(&pdev->dev, "Unable to register CEU interrupt.\n"); + goto exit_release_mem; + } + + pcdev->ici.priv = pcdev; + pcdev->ici.dev.parent = &pdev->dev; + pcdev->ici.nr = pdev->id; + pcdev->ici.drv_name = pdev->dev.bus_id, + pcdev->ici.ops = &sh_mobile_ceu_host_ops, + + err = soc_camera_host_register(&pcdev->ici); + if (err) + goto exit_free_irq; + + return 0; + +exit_free_irq: + free_irq(pcdev->irq, pcdev); +exit_release_mem: + if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) + dma_release_declared_memory(&pdev->dev); +exit_iounmap: + iounmap(base); +exit_kfree: + kfree(pcdev); +exit: + return err; +} + +static int sh_mobile_ceu_remove(struct platform_device *pdev) +{ + struct sh_mobile_ceu_dev *pcdev = platform_get_drvdata(pdev); + + soc_camera_host_unregister(&pcdev->ici); + free_irq(pcdev->irq, pcdev); + if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) + dma_release_declared_memory(&pdev->dev); + iounmap(pcdev->base); + kfree(pcdev); + return 0; +} + +static struct platform_driver sh_mobile_ceu_driver = { + .driver = { + .name = "sh_mobile_ceu", + }, + .probe = sh_mobile_ceu_probe, + .remove = sh_mobile_ceu_remove, +}; + +static int __init sh_mobile_ceu_init(void) +{ + return platform_driver_register(&sh_mobile_ceu_driver); +} + +static void __exit sh_mobile_ceu_exit(void) +{ + return platform_driver_unregister(&sh_mobile_ceu_driver); +} + +module_init(sh_mobile_ceu_init); +module_exit(sh_mobile_ceu_exit); + +MODULE_DESCRIPTION("SuperH Mobile CEU driver"); +MODULE_AUTHOR("Magnus Damm"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index 35223e0d7e4..6ff489baacf 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -44,7 +44,6 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, @@ -57,7 +56,6 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x603f, BRIDGE_SN9C102), }, /* SN9C103 */ { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index d015bfe0095..e39b98f1eca 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -26,6 +26,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-dev.h> +#include <media/videobuf-core.h> #include <media/soc_camera.h> static LIST_HEAD(hosts); @@ -44,7 +45,7 @@ format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc) return NULL; } -static int soc_camera_try_fmt_cap(struct file *file, void *priv, +static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct soc_camera_file *icf = file->private_data; @@ -182,7 +183,6 @@ static int soc_camera_open(struct inode *inode, struct file *file) struct soc_camera_device *icd; struct soc_camera_host *ici; struct soc_camera_file *icf; - spinlock_t *lock; int ret; icf = vmalloc(sizeof(*icf)); @@ -209,13 +209,6 @@ static int soc_camera_open(struct inode *inode, struct file *file) } icf->icd = icd; - - icf->lock = ici->ops->spinlock_alloc(icf); - if (!icf->lock) { - ret = -ENOMEM; - goto esla; - } - icd->use_count++; /* Now we really have to activate the camera */ @@ -233,21 +226,12 @@ static int soc_camera_open(struct inode *inode, struct file *file) file->private_data = icf; dev_dbg(&icd->dev, "camera device open\n"); - /* We must pass NULL as dev pointer, then all pci_* dma operations - * transform to normal dma_* ones. */ - videobuf_queue_sg_init(&icf->vb_vidq, ici->vbq_ops, NULL, icf->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, - ici->msize, icd); + ici->ops->init_videobuf(&icf->vb_vidq, icd); return 0; /* All errors are entered with the video_lock held */ eiciadd: - lock = icf->lock; - icf->lock = NULL; - if (ici->ops->spinlock_free) - ici->ops->spinlock_free(lock); -esla: module_put(ici->ops->owner); emgi: module_put(icd->ops->owner); @@ -263,15 +247,11 @@ static int soc_camera_close(struct inode *inode, struct file *file) struct soc_camera_device *icd = icf->icd; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct video_device *vdev = icd->vdev; - spinlock_t *lock = icf->lock; mutex_lock(&video_lock); icd->use_count--; if (!icd->use_count) ici->ops->remove(icd); - icf->lock = NULL; - if (ici->ops->spinlock_free) - ici->ops->spinlock_free(lock); module_put(icd->ops->owner); module_put(ici->ops->owner); mutex_unlock(&video_lock); @@ -342,7 +322,7 @@ static struct file_operations soc_camera_fops = { }; -static int soc_camera_s_fmt_cap(struct file *file, void *priv, +static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct soc_camera_file *icf = file->private_data; @@ -362,7 +342,7 @@ static int soc_camera_s_fmt_cap(struct file *file, void *priv, /* buswidth may be further adjusted by the ici */ icd->buswidth = data_fmt->depth; - ret = soc_camera_try_fmt_cap(file, icf, f); + ret = soc_camera_try_fmt_vid_cap(file, icf, f); if (ret < 0) return ret; @@ -389,7 +369,7 @@ static int soc_camera_s_fmt_cap(struct file *file, void *priv, return ici->ops->set_bus_param(icd, f->fmt.pix.pixelformat); } -static int soc_camera_enum_fmt_cap(struct file *file, void *priv, +static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct soc_camera_file *icf = file->private_data; @@ -408,7 +388,7 @@ static int soc_camera_enum_fmt_cap(struct file *file, void *priv, return 0; } -static int soc_camera_g_fmt_cap(struct file *file, void *priv, +static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct soc_camera_file *icf = file->private_data; @@ -767,27 +747,12 @@ static void dummy_release(struct device *dev) { } -static spinlock_t *spinlock_alloc(struct soc_camera_file *icf) -{ - spinlock_t *lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL); - - if (lock) - spin_lock_init(lock); - - return lock; -} - -static void spinlock_free(spinlock_t *lock) -{ - kfree(lock); -} - int soc_camera_host_register(struct soc_camera_host *ici) { int ret; struct soc_camera_host *ix; - if (!ici->vbq_ops || !ici->ops->add || !ici->ops->remove) + if (!ici->ops->init_videobuf || !ici->ops->add || !ici->ops->remove) return -EINVAL; /* Number might be equal to the platform device ID */ @@ -811,11 +776,6 @@ int soc_camera_host_register(struct soc_camera_host *ici) if (ret) goto edevr; - if (!ici->ops->spinlock_alloc) { - ici->ops->spinlock_alloc = spinlock_alloc; - ici->ops->spinlock_free = spinlock_free; - } - scan_add_host(ici); return 0; @@ -925,15 +885,15 @@ int soc_camera_video_start(struct soc_camera_device *icd) vdev->minor = -1; vdev->tvnorms = V4L2_STD_UNKNOWN, vdev->vidioc_querycap = soc_camera_querycap; - vdev->vidioc_g_fmt_cap = soc_camera_g_fmt_cap; - vdev->vidioc_enum_fmt_cap = soc_camera_enum_fmt_cap; - vdev->vidioc_s_fmt_cap = soc_camera_s_fmt_cap; + vdev->vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap; + vdev->vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap; + vdev->vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap; vdev->vidioc_enum_input = soc_camera_enum_input; vdev->vidioc_g_input = soc_camera_g_input; vdev->vidioc_s_input = soc_camera_s_input; vdev->vidioc_s_std = soc_camera_s_std; vdev->vidioc_reqbufs = soc_camera_reqbufs; - vdev->vidioc_try_fmt_cap = soc_camera_try_fmt_cap; + vdev->vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap; vdev->vidioc_querybuf = soc_camera_querybuf; vdev->vidioc_qbuf = soc_camera_qbuf; vdev->vidioc_dqbuf = soc_camera_dqbuf; diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c new file mode 100644 index 00000000000..eefb0327ebb --- /dev/null +++ b/drivers/media/video/soc_camera_platform.c @@ -0,0 +1,198 @@ +/* + * Generic Platform Camera Driver + * + * Copyright (C) 2008 Magnus Damm + * Based on mt9m001 driver, + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/soc_camera.h> + +struct soc_camera_platform_info { + int iface; + char *format_name; + unsigned long format_depth; + struct v4l2_pix_format format; + unsigned long bus_param; + int (*set_capture)(struct soc_camera_platform_info *info, int enable); +}; + +struct soc_camera_platform_priv { + struct soc_camera_platform_info *info; + struct soc_camera_device icd; + struct soc_camera_data_format format; +}; + +static struct soc_camera_platform_info * +soc_camera_platform_get_info(struct soc_camera_device *icd) +{ + struct soc_camera_platform_priv *priv; + priv = container_of(icd, struct soc_camera_platform_priv, icd); + return priv->info; +} + +static int soc_camera_platform_init(struct soc_camera_device *icd) +{ + return 0; +} + +static int soc_camera_platform_release(struct soc_camera_device *icd) +{ + return 0; +} + +static int soc_camera_platform_start_capture(struct soc_camera_device *icd) +{ + struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); + return p->set_capture(p, 1); +} + +static int soc_camera_platform_stop_capture(struct soc_camera_device *icd) +{ + struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); + return p->set_capture(p, 0); +} + +static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +static unsigned long +soc_camera_platform_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); + return p->bus_param; +} + +static int soc_camera_platform_set_fmt_cap(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + return 0; +} + +static int soc_camera_platform_try_fmt_cap(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); + + f->fmt.pix.width = p->format.width; + f->fmt.pix.height = p->format.height; + return 0; +} + +static int soc_camera_platform_video_probe(struct soc_camera_device *icd) +{ + struct soc_camera_platform_priv *priv; + priv = container_of(icd, struct soc_camera_platform_priv, icd); + + priv->format.name = priv->info->format_name; + priv->format.depth = priv->info->format_depth; + priv->format.fourcc = priv->info->format.pixelformat; + priv->format.colorspace = priv->info->format.colorspace; + + icd->formats = &priv->format; + icd->num_formats = 1; + + return soc_camera_video_start(icd); +} + +static void soc_camera_platform_video_remove(struct soc_camera_device *icd) +{ + soc_camera_video_stop(icd); +} + +static struct soc_camera_ops soc_camera_platform_ops = { + .owner = THIS_MODULE, + .probe = soc_camera_platform_video_probe, + .remove = soc_camera_platform_video_remove, + .init = soc_camera_platform_init, + .release = soc_camera_platform_release, + .start_capture = soc_camera_platform_start_capture, + .stop_capture = soc_camera_platform_stop_capture, + .set_fmt_cap = soc_camera_platform_set_fmt_cap, + .try_fmt_cap = soc_camera_platform_try_fmt_cap, + .set_bus_param = soc_camera_platform_set_bus_param, + .query_bus_param = soc_camera_platform_query_bus_param, +}; + +static int soc_camera_platform_probe(struct platform_device *pdev) +{ + struct soc_camera_platform_priv *priv; + struct soc_camera_platform_info *p; + struct soc_camera_device *icd; + int ret; + + p = pdev->dev.platform_data; + if (!p) + return -EINVAL; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->info = p; + platform_set_drvdata(pdev, priv); + + icd = &priv->icd; + icd->ops = &soc_camera_platform_ops; + icd->control = &pdev->dev; + icd->width_min = 0; + icd->width_max = priv->info->format.width; + icd->height_min = 0; + icd->height_max = priv->info->format.height; + icd->y_skip_top = 0; + icd->iface = priv->info->iface; + + ret = soc_camera_device_register(icd); + if (ret) + kfree(priv); + + return ret; +} + +static int soc_camera_platform_remove(struct platform_device *pdev) +{ + struct soc_camera_platform_priv *priv = platform_get_drvdata(pdev); + + soc_camera_device_unregister(&priv->icd); + kfree(priv); + return 0; +} + +static struct platform_driver soc_camera_platform_driver = { + .driver = { + .name = "soc_camera_platform", + }, + .probe = soc_camera_platform_probe, + .remove = soc_camera_platform_remove, +}; + +static int __init soc_camera_platform_module_init(void) +{ + return platform_driver_register(&soc_camera_platform_driver); +} + +static void __exit soc_camera_platform_module_exit(void) +{ + return platform_driver_unregister(&soc_camera_platform_driver); +} + +module_init(soc_camera_platform_module_init); +module_exit(soc_camera_platform_module_exit); + +MODULE_DESCRIPTION("SoC Camera Platform driver"); +MODULE_AUTHOR("Magnus Damm"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index b12c60cf5a0..f308c38d744 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -981,7 +981,7 @@ static int stk_vidioc_s_ctrl(struct file *filp, } -static int stk_vidioc_enum_fmt_cap(struct file *filp, +static int stk_vidioc_enum_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_fmtdesc *fmtd) { fmtd->flags = 0; @@ -1025,7 +1025,7 @@ static struct stk_size { { .w = 176, .h = 144, .m = MODE_QCIF, }, }; -static int stk_vidioc_g_fmt_cap(struct file *filp, +static int stk_vidioc_g_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_format *f) { struct v4l2_pix_format *pix_format = &f->fmt.pix; @@ -1054,7 +1054,7 @@ static int stk_vidioc_g_fmt_cap(struct file *filp, return 0; } -static int stk_vidioc_try_fmt_cap(struct file *filp, +static int stk_vidioc_try_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_format *fmtd) { int i; @@ -1131,7 +1131,7 @@ static int stk_setup_format(struct stk_camera *dev) return stk_sensor_configure(dev); } -static int stk_vidioc_s_fmt_cap(struct file *filp, +static int stk_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_format *fmtd) { int ret; @@ -1145,7 +1145,7 @@ static int stk_vidioc_s_fmt_cap(struct file *filp, return -EBUSY; if (dev->owner && dev->owner != filp) return -EBUSY; - ret = stk_vidioc_try_fmt_cap(filp, priv, fmtd); + ret = stk_vidioc_try_fmt_vid_cap(filp, priv, fmtd); if (ret) return ret; dev->owner = filp; @@ -1342,10 +1342,10 @@ static struct video_device stk_v4l_data = { .release = stk_v4l_dev_release, .vidioc_querycap = stk_vidioc_querycap, - .vidioc_enum_fmt_cap = stk_vidioc_enum_fmt_cap, - .vidioc_try_fmt_cap = stk_vidioc_try_fmt_cap, - .vidioc_s_fmt_cap = stk_vidioc_s_fmt_cap, - .vidioc_g_fmt_cap = stk_vidioc_g_fmt_cap, + .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap, .vidioc_enum_input = stk_vidioc_enum_input, .vidioc_s_input = stk_vidioc_s_input, .vidioc_g_input = stk_vidioc_g_input, diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c index 8f0100f67a9..29991d1cf13 100644 --- a/drivers/media/video/tcm825x.c +++ b/drivers/media/video/tcm825x.c @@ -523,6 +523,9 @@ static int ioctl_g_ctrl(struct v4l2_int_device *s, if (val < 0) return val; + if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) + val ^= sensor->platform_data->is_upside_down(); + vc->value = val; return 0; } @@ -556,6 +559,9 @@ static int ioctl_s_ctrl(struct v4l2_int_device *s, if (lvc == NULL) return -EINVAL; + if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) + val ^= sensor->platform_data->is_upside_down(); + val = val << lvc->start_bit; if (tcm825x_write_reg_mask(client, lvc->reg, val)) return -EIO; diff --git a/drivers/media/video/tcm825x.h b/drivers/media/video/tcm825x.h index 966765b66b3..770ebacfa34 100644 --- a/drivers/media/video/tcm825x.h +++ b/drivers/media/video/tcm825x.h @@ -182,6 +182,7 @@ struct tcm825x_platform_data { int (*needs_reset)(struct v4l2_int_device *s, void *buf, struct v4l2_pix_format *fmt); int (*ifparm)(struct v4l2_ifparm *p); + int (*is_upside_down)(void); }; /* Array of image sizes supported by TCM825X. These must be ordered from diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index b4d10f7a4e5..ae75c187da7 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -72,6 +72,7 @@ static unsigned short normal_i2c[] = { I2C_ADDR_TDA7432 >> 1, I2C_CLIENT_END, }; + I2C_CLIENT_INSMOD; /* Structure of address and subaddresses for the tda7432 */ diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 0cee0024278..2437c1a269c 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -34,6 +34,7 @@ static int debug; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); + #define dprintk(args...) \ do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index 3c0557130a7..7a8ce8fb46d 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -30,7 +30,6 @@ #include <linux/i2c.h> #include <linux/init.h> - #include <media/i2c-addr.h> static int debug; /* insmod parameter */ @@ -42,6 +41,7 @@ static unsigned short normal_i2c[] = { I2C_ADDR_TDA9875 >> 1, I2C_CLIENT_END }; + I2C_CLIENT_INSMOD; /* This is a superset of the TDA9875 */ diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c index 9513d8611e8..421c1445e96 100644 --- a/drivers/media/video/tea6415c.c +++ b/drivers/media/video/tea6415c.c @@ -36,6 +36,7 @@ static int debug; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); + #define dprintk(args...) \ do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c index 7fd53367c07..b5c8957d130 100644 --- a/drivers/media/video/tea6420.c +++ b/drivers/media/video/tea6420.c @@ -36,6 +36,7 @@ static int debug; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); + #define dprintk(args...) \ do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c index 28ab9f9d760..9220378a563 100644 --- a/drivers/media/video/tlv320aic23b.c +++ b/drivers/media/video/tlv320aic23b.c @@ -39,7 +39,6 @@ MODULE_LICENSE("GPL"); static unsigned short normal_i2c[] = { 0x34 >> 1, I2C_CLIENT_END }; - I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 0d12ace6166..93d879dc510 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -1298,7 +1298,6 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .id_table = tuner_id, }; - /* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index c77914d99d1..463680b1328 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -69,7 +69,6 @@ typedef struct AUDIOCMD { /* chip description */ struct CHIPDESC { char *name; /* chip name */ - int id; /* ID */ int addr_lo, addr_hi; /* i2c address range */ int registers; /* # of registers */ @@ -1256,7 +1255,6 @@ module_param(ta8874z, int, 0444); static struct CHIPDESC chiplist[] = { { .name = "tda9840", - .id = I2C_DRIVERID_TDA9840, .insmodopt = &tda9840, .addr_lo = I2C_ADDR_TDA9840 >> 1, .addr_hi = I2C_ADDR_TDA9840 >> 1, @@ -1272,7 +1270,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9873h", - .id = I2C_DRIVERID_TDA9873, .checkit = tda9873_checkit, .insmodopt = &tda9873, .addr_lo = I2C_ADDR_TDA985x_L >> 1, @@ -1293,7 +1290,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9874h/a", - .id = I2C_DRIVERID_TDA9874, .checkit = tda9874a_checkit, .initialize = tda9874a_initialize, .insmodopt = &tda9874a, @@ -1306,7 +1302,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9850", - .id = I2C_DRIVERID_TDA9850, .insmodopt = &tda9850, .addr_lo = I2C_ADDR_TDA985x_L >> 1, .addr_hi = I2C_ADDR_TDA985x_H >> 1, @@ -1319,7 +1314,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9855", - .id = I2C_DRIVERID_TDA9855, .insmodopt = &tda9855, .addr_lo = I2C_ADDR_TDA985x_L >> 1, .addr_hi = I2C_ADDR_TDA985x_H >> 1, @@ -1344,7 +1338,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6300", - .id = I2C_DRIVERID_TEA6300, .insmodopt = &tea6300, .addr_lo = I2C_ADDR_TEA6300 >> 1, .addr_hi = I2C_ADDR_TEA6300 >> 1, @@ -1365,7 +1358,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6320", - .id = I2C_DRIVERID_TEA6300, .initialize = tea6320_initialize, .insmodopt = &tea6320, .addr_lo = I2C_ADDR_TEA6300 >> 1, @@ -1387,7 +1379,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6420", - .id = I2C_DRIVERID_TEA6420, .insmodopt = &tea6420, .addr_lo = I2C_ADDR_TEA6420 >> 1, .addr_hi = I2C_ADDR_TEA6420 >> 1, @@ -1400,7 +1391,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda8425", - .id = I2C_DRIVERID_TDA8425, .insmodopt = &tda8425, .addr_lo = I2C_ADDR_TDA8425 >> 1, .addr_hi = I2C_ADDR_TDA8425 >> 1, @@ -1424,7 +1414,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "pic16c54 (PV951)", - .id = I2C_DRIVERID_PIC16C54_PV9, .insmodopt = &pic16c54, .addr_lo = I2C_ADDR_PIC16C54 >> 1, .addr_hi = I2C_ADDR_PIC16C54>> 1, @@ -1440,8 +1429,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "ta8874z", - .id = -1, - /*.id = I2C_DRIVERID_TA8874Z, */ .checkit = ta8874z_checkit, .insmodopt = &ta8874z, .addr_lo = I2C_ADDR_TDA9840 >> 1, diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index a9c5e5adba3..abf685464b7 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -169,7 +169,6 @@ static void usbvision_rvfree(void *mem, unsigned long size) } - #if ENABLE_HEXDUMP static void usbvision_hexdump(const unsigned char *data, int len) { @@ -2317,7 +2316,6 @@ static void usbvision_powerOffTimer(unsigned long data) del_timer(&usbvision->powerOffTimer); INIT_WORK(&usbvision->powerOffWork, call_usbvision_power_off); (void) schedule_work(&usbvision->powerOffWork); - } void usbvision_init_powerOffTimer(struct usb_usbvision *usbvision) @@ -2518,7 +2516,6 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) } } - /* Submit all URBs */ for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) { errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb, @@ -2564,7 +2561,6 @@ void usbvision_stop_isoc(struct usb_usbvision *usbvision) usbvision->sbuf[bufIdx].urb = NULL; } - PDEBUG(DBG_ISOC, "%s: streaming=Stream_Off\n", __func__); usbvision->streaming = Stream_Off; diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index e2274d77ea2..a6d00858b07 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -190,7 +190,6 @@ static u32 functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; } - /* -----exported algorithm data: ------------------------------------- */ static struct i2c_algorithm usbvision_algo = { @@ -514,11 +513,7 @@ static struct i2c_adapter i2c_adap_template = { .id = I2C_HW_B_BT848, /* FIXME */ .client_register = attach_inform, .client_unregister = detach_inform, -#ifdef I2C_ADAP_CLASS_TV_ANALOG - .class = I2C_ADAP_CLASS_TV_ANALOG, -#else .class = I2C_CLASS_TV_ANALOG, -#endif }; static struct i2c_client i2c_client_template = { diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index d97261ab430..cd6c41d6789 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -179,7 +179,6 @@ MODULE_ALIAS(DRIVER_ALIAS); /* /sys/bus/usb/drivers/USBVision Video Grabber */ /*****************************************************************************/ - #define YES_NO(x) ((x) ? "Yes" : "No") static inline struct usb_usbvision *cd_to_usbvision(struct device *cd) @@ -370,7 +369,6 @@ static void usbvision_remove_sysfs(struct video_device *vdev) } } - /* * usbvision_open() * @@ -388,7 +386,6 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) PDEBUG(DBG_IO, "open"); - usbvision_reset_powerOffTimer(usbvision); if (usbvision->user) @@ -442,9 +439,6 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) mutex_unlock(&usbvision->lock); } - if (errCode) { - } - /* prepare queues */ usbvision_empty_framequeues(usbvision); @@ -495,8 +489,6 @@ static int usbvision_v4l2_close(struct inode *inode, struct file *file) } PDEBUG(DBG_IO, "success"); - - return 0; } @@ -998,7 +990,7 @@ static int vidioc_streamoff(struct file *file, return 0; } -static int vidioc_enum_fmt_cap (struct file *file, void *priv, +static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, struct v4l2_fmtdesc *vfd) { if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) { @@ -1012,7 +1004,7 @@ static int vidioc_enum_fmt_cap (struct file *file, void *priv, return 0; } -static int vidioc_g_fmt_cap (struct file *file, void *priv, +static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *vf) { struct video_device *dev = video_devdata(file); @@ -1030,7 +1022,7 @@ static int vidioc_g_fmt_cap (struct file *file, void *priv, return 0; } -static int vidioc_try_fmt_cap (struct file *file, void *priv, +static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, struct v4l2_format *vf) { struct video_device *dev = video_devdata(file); @@ -1060,7 +1052,7 @@ static int vidioc_try_fmt_cap (struct file *file, void *priv, return 0; } -static int vidioc_s_fmt_cap(struct file *file, void *priv, +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) { struct video_device *dev = video_devdata(file); @@ -1068,7 +1060,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, (struct usb_usbvision *) video_get_drvdata(dev); int ret; - if( 0 != (ret=vidioc_try_fmt_cap (file, priv, vf)) ) { + if( 0 != (ret=vidioc_try_fmt_vid_cap (file, priv, vf)) ) { return ret; } @@ -1346,9 +1338,7 @@ static int usbvision_radio_close(struct inode *inode, struct file *file) usbvision_release(usbvision); } - PDEBUG(DBG_IO, "success"); - return errCode; } @@ -1360,7 +1350,6 @@ static int usbvision_vbi_open(struct inode *inode, struct file *file) { /* TODO */ return -ENODEV; - } static int usbvision_vbi_close(struct inode *inode, struct file *file) @@ -1407,10 +1396,10 @@ static struct video_device usbvision_video_template = { .release = video_device_release, .minor = -1, .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, - .vidioc_g_fmt_cap = vidioc_g_fmt_cap, - .vidioc_try_fmt_cap = vidioc_try_fmt_cap, - .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .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, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, @@ -1899,7 +1888,6 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) } PDEBUG(DBG_PROBE, "success"); - } static struct usb_driver usbvision_driver = { diff --git a/drivers/media/video/uvc/Kconfig b/drivers/media/video/uvc/Kconfig new file mode 100644 index 00000000000..c2d9760de83 --- /dev/null +++ b/drivers/media/video/uvc/Kconfig @@ -0,0 +1,17 @@ +config USB_VIDEO_CLASS + tristate "USB Video Class (UVC)" + ---help--- + Support for the USB Video Class (UVC). Currently only video + input devices, such as webcams, are supported. + + For more information see: <http://linux-uvc.berlios.de/> + +config USB_VIDEO_CLASS_INPUT_EVDEV + bool "UVC input events device support" + default y + depends on USB_VIDEO_CLASS && INPUT + ---help--- + This option makes USB Video Class devices register an input device + to report button events. + + If you are in doubt, say Y. diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index f0ee46d1554..3ae95512666 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -1254,3 +1254,4 @@ void uvc_ctrl_init(void) for (; mapping < mend; ++mapping) uvc_ctrl_add_mapping(mapping); } + diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 60ced589f89..f2b2983fe06 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -298,7 +298,8 @@ static int uvc_parse_format(struct uvc_device *dev, switch (buffer[2]) { case VS_FORMAT_UNCOMPRESSED: case VS_FORMAT_FRAME_BASED: - if (buflen < 27) { + n = buffer[2] == VS_FORMAT_UNCOMPRESSED ? 27 : 28; + if (buflen < n) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" "interface %d FORMAT error\n", dev->udev->devnum, @@ -1632,13 +1633,16 @@ static void uvc_disconnect(struct usb_interface *intf) * reference to the uvc_device instance after uvc_v4l2_open() received * the pointer to the device (video_devdata) but before it got the * chance to increase the reference count (kref_get). + * + * Note that the reference can't be released with the lock held, + * otherwise a AB-BA deadlock can occur with videodev_lock that + * videodev acquires in videodev_open() and video_unregister_device(). */ mutex_lock(&uvc_driver.open_mutex); - dev->state |= UVC_DEV_DISCONNECTED; - kref_put(&dev->kref, uvc_delete); - mutex_unlock(&uvc_driver.open_mutex); + + kref_put(&dev->kref, uvc_delete); } static int uvc_suspend(struct usb_interface *intf, pm_message_t message) @@ -1825,6 +1829,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Asus F9SG */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a31, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Syntek (Asus U3S) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1891,6 +1904,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX }, + /* Medion Akoya Mini E1210 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x5986, + .idProduct = 0x0141, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Acer OrbiCam - Unknown vendor */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1953,3 +1975,4 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_VERSION(DRIVER_VERSION); + diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 0923f0e3b3d..7388d0cee3d 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -475,3 +475,4 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, wake_up(&buf->wait); return nextbuf; } + diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c index be9084e5eac..75e678ac54e 100644 --- a/drivers/media/video/uvc/uvc_status.c +++ b/drivers/media/video/uvc/uvc_status.c @@ -22,6 +22,7 @@ /* -------------------------------------------------------------------------- * Input device */ +#ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV static int uvc_input_init(struct uvc_device *dev) { struct usb_device *udev = dev->udev; @@ -67,6 +68,19 @@ static void uvc_input_cleanup(struct uvc_device *dev) input_unregister_device(dev->input); } +static void uvc_input_report_key(struct uvc_device *dev, unsigned int code, + int value) +{ + if (dev->input) + input_report_key(dev->input, code, value); +} + +#else +#define uvc_input_init(dev) +#define uvc_input_cleanup(dev) +#define uvc_input_report_key(dev, code, value) +#endif /* CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV */ + /* -------------------------------------------------------------------------- * Status interrupt endpoint */ @@ -83,8 +97,7 @@ static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len) return; uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", data[1], data[3] ? "pressed" : "released", len); - if (dev->input) - input_report_key(dev->input, BTN_0, data[3]); + uvc_input_report_key(dev, BTN_0, data[3]); } else { uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x " "len %d.\n", data[1], data[2], data[3], len); @@ -203,5 +216,6 @@ int uvc_status_resume(struct uvc_device *dev) if (dev->int_urb == NULL) return 0; - return usb_submit_urb(dev->int_urb, GFP_KERNEL); + return usb_submit_urb(dev->int_urb, GFP_NOIO); } + diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 2e0a66575bb..b5a11eb8f9f 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -1032,7 +1032,7 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) { struct video_device *vdev = video_devdata(file); struct uvc_video_device *video = video_get_drvdata(vdev); - struct uvc_buffer *buffer; + struct uvc_buffer *uninitialized_var(buffer); struct page *page; unsigned long addr, start, size; unsigned int i; @@ -1103,3 +1103,4 @@ struct file_operations uvc_fops = { .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll, }; + diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 6faf1fb2161..ad63794fda7 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -554,9 +554,56 @@ static void uvc_video_complete(struct urb *urb) } /* + * Free transfer buffers. + */ +static void uvc_free_urb_buffers(struct uvc_video_device *video) +{ + unsigned int i; + + for (i = 0; i < UVC_URBS; ++i) { + if (video->urb_buffer[i]) { + usb_buffer_free(video->dev->udev, video->urb_size, + video->urb_buffer[i], video->urb_dma[i]); + video->urb_buffer[i] = NULL; + } + } + + video->urb_size = 0; +} + +/* + * Allocate transfer buffers. This function can be called with buffers + * already allocated when resuming from suspend, in which case it will + * return without touching the buffers. + * + * Return 0 on success or -ENOMEM when out of memory. + */ +static int uvc_alloc_urb_buffers(struct uvc_video_device *video, + unsigned int size) +{ + unsigned int i; + + /* Buffers are already allocated, bail out. */ + if (video->urb_size) + return 0; + + for (i = 0; i < UVC_URBS; ++i) { + video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev, + size, GFP_KERNEL, &video->urb_dma[i]); + if (video->urb_buffer[i] == NULL) { + uvc_free_urb_buffers(video); + return -ENOMEM; + } + } + + video->urb_size = size; + return 0; +} + +/* * Uninitialize isochronous/bulk URBs and free transfer buffers. */ -static void uvc_uninit_video(struct uvc_video_device *video) +static void uvc_uninit_video(struct uvc_video_device *video, int free_buffers) { struct urb *urb; unsigned int i; @@ -566,19 +613,12 @@ static void uvc_uninit_video(struct uvc_video_device *video) continue; usb_kill_urb(urb); - /* urb->transfer_buffer_length is not touched by USB core, so - * we can use it here as the buffer length. - */ - if (video->urb_buffer[i]) { - usb_buffer_free(video->dev->udev, - urb->transfer_buffer_length, - video->urb_buffer[i], urb->transfer_dma); - video->urb_buffer[i] = NULL; - } - usb_free_urb(urb); video->urb[i] = NULL; } + + if (free_buffers) + uvc_free_urb_buffers(video); } /* @@ -586,7 +626,7 @@ static void uvc_uninit_video(struct uvc_video_device *video) * is given by the endpoint. */ static int uvc_init_video_isoc(struct uvc_video_device *video, - struct usb_host_endpoint *ep) + struct usb_host_endpoint *ep, gfp_t gfp_flags) { struct urb *urb; unsigned int npackets, i, j; @@ -610,18 +650,13 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, size = npackets * psize; + if (uvc_alloc_urb_buffers(video, size) < 0) + return -ENOMEM; + for (i = 0; i < UVC_URBS; ++i) { - urb = usb_alloc_urb(npackets, GFP_KERNEL); + urb = usb_alloc_urb(npackets, gfp_flags); if (urb == NULL) { - uvc_uninit_video(video); - return -ENOMEM; - } - - video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev, - size, GFP_KERNEL, &urb->transfer_dma); - if (video->urb_buffer[i] == NULL) { - usb_free_urb(urb); - uvc_uninit_video(video); + uvc_uninit_video(video, 1); return -ENOMEM; } @@ -632,6 +667,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->interval = ep->desc.bInterval; urb->transfer_buffer = video->urb_buffer[i]; + urb->transfer_dma = video->urb_dma[i]; urb->complete = uvc_video_complete; urb->number_of_packets = npackets; urb->transfer_buffer_length = size; @@ -652,7 +688,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, * given by the endpoint. */ static int uvc_init_video_bulk(struct uvc_video_device *video, - struct usb_host_endpoint *ep) + struct usb_host_endpoint *ep, gfp_t gfp_flags) { struct urb *urb; unsigned int pipe, i; @@ -671,20 +707,15 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, if (size > psize * UVC_MAX_ISO_PACKETS) size = psize * UVC_MAX_ISO_PACKETS; + if (uvc_alloc_urb_buffers(video, size) < 0) + return -ENOMEM; + pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress); for (i = 0; i < UVC_URBS; ++i) { - urb = usb_alloc_urb(0, GFP_KERNEL); + urb = usb_alloc_urb(0, gfp_flags); if (urb == NULL) { - uvc_uninit_video(video); - return -ENOMEM; - } - - video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev, - size, GFP_KERNEL, &urb->transfer_dma); - if (video->urb_buffer[i] == NULL) { - usb_free_urb(urb); - uvc_uninit_video(video); + uvc_uninit_video(video, 1); return -ENOMEM; } @@ -692,6 +723,7 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, video->urb_buffer[i], size, uvc_video_complete, video); urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + urb->transfer_dma = video->urb_dma[i]; video->urb[i] = urb; } @@ -702,7 +734,7 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, /* * Initialize isochronous/bulk URBs and allocate transfer buffers. */ -static int uvc_init_video(struct uvc_video_device *video) +static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) { struct usb_interface *intf = video->streaming->intf; struct usb_host_interface *alts; @@ -747,7 +779,7 @@ static int uvc_init_video(struct uvc_video_device *video) if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0) return ret; - ret = uvc_init_video_isoc(video, ep); + ret = uvc_init_video_isoc(video, ep, gfp_flags); } else { /* Bulk endpoint, proceed to URB initialization. */ ep = uvc_find_endpoint(&intf->altsetting[0], @@ -755,7 +787,7 @@ static int uvc_init_video(struct uvc_video_device *video) if (ep == NULL) return -EIO; - ret = uvc_init_video_bulk(video, ep); + ret = uvc_init_video_bulk(video, ep, gfp_flags); } if (ret < 0) @@ -763,10 +795,10 @@ static int uvc_init_video(struct uvc_video_device *video) /* Submit the URBs. */ for (i = 0; i < UVC_URBS; ++i) { - if ((ret = usb_submit_urb(video->urb[i], GFP_KERNEL)) < 0) { + if ((ret = usb_submit_urb(video->urb[i], gfp_flags)) < 0) { uvc_printk(KERN_ERR, "Failed to submit URB %u " "(%d).\n", i, ret); - uvc_uninit_video(video); + uvc_uninit_video(video, 1); return ret; } } @@ -791,7 +823,7 @@ int uvc_video_suspend(struct uvc_video_device *video) return 0; video->frozen = 1; - uvc_uninit_video(video); + uvc_uninit_video(video, 0); usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); return 0; } @@ -818,7 +850,7 @@ int uvc_video_resume(struct uvc_video_device *video) if (!uvc_queue_streaming(&video->queue)) return 0; - if ((ret = uvc_init_video(video)) < 0) + if ((ret = uvc_init_video(video, GFP_NOIO)) < 0) uvc_queue_enable(&video->queue, 0); return ret; @@ -920,7 +952,7 @@ int uvc_video_enable(struct uvc_video_device *video, int enable) int ret; if (!enable) { - uvc_uninit_video(video); + uvc_uninit_video(video, 1); usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); uvc_queue_enable(&video->queue, 0); @@ -930,5 +962,6 @@ int uvc_video_enable(struct uvc_video_device *video, int enable) if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) return ret; - return uvc_init_video(video); + return uvc_init_video(video, GFP_KERNEL); } + diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index a995a780db1..bafe3406e30 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -602,6 +602,8 @@ struct uvc_video_device { struct urb *urb[UVC_URBS]; char *urb_buffer[UVC_URBS]; + dma_addr_t urb_dma[UVC_URBS]; + unsigned int urb_size; __u8 last_fid; }; @@ -794,3 +796,4 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, #endif /* __KERNEL__ */ #endif + diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c new file mode 100644 index 00000000000..03f20acb668 --- /dev/null +++ b/drivers/media/video/videobuf-dma-contig.c @@ -0,0 +1,418 @@ +/* + * helper functions for physically contiguous capture buffers + * + * The functions support hardware lacking scatter gather support + * (i.e. the buffers must be linear in physical memory) + * + * Copyright (c) 2008 Magnus Damm + * + * Based on videobuf-vmalloc.c, + * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/dma-mapping.h> +#include <media/videobuf-dma-contig.h> + +struct videobuf_dma_contig_memory { + u32 magic; + void *vaddr; + dma_addr_t dma_handle; + unsigned long size; +}; + +#define MAGIC_DC_MEM 0x0733ac61 +#define MAGIC_CHECK(is, should) \ + if (unlikely((is) != (should))) { \ + pr_err("magic mismatch: %x expected %x\n", is, should); \ + BUG(); \ + } + +static void +videobuf_vm_open(struct vm_area_struct *vma) +{ + struct videobuf_mapping *map = vma->vm_private_data; + + dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", + map, map->count, vma->vm_start, vma->vm_end); + + map->count++; +} + +static void videobuf_vm_close(struct vm_area_struct *vma) +{ + struct videobuf_mapping *map = vma->vm_private_data; + struct videobuf_queue *q = map->q; + int i; + + dev_dbg(map->q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", + map, map->count, vma->vm_start, vma->vm_end); + + map->count--; + if (0 == map->count) { + struct videobuf_dma_contig_memory *mem; + + dev_dbg(map->q->dev, "munmap %p q=%p\n", map, q); + mutex_lock(&q->vb_lock); + + /* We need first to cancel streams, before unmapping */ + if (q->streaming) + videobuf_queue_cancel(q); + + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (NULL == q->bufs[i]) + continue; + + if (q->bufs[i]->map != map) + continue; + + mem = q->bufs[i]->priv; + if (mem) { + /* This callback is called only if kernel has + allocated memory and this memory is mmapped. + In this case, memory should be freed, + in order to do memory unmap. + */ + + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + + /* vfree is not atomic - can't be + called with IRQ's disabled + */ + dev_dbg(map->q->dev, "buf[%d] freeing %p\n", + i, mem->vaddr); + + dma_free_coherent(q->dev, mem->size, + mem->vaddr, mem->dma_handle); + mem->vaddr = NULL; + } + + q->bufs[i]->map = NULL; + q->bufs[i]->baddr = 0; + } + + kfree(map); + + mutex_unlock(&q->vb_lock); + } +} + +static struct vm_operations_struct videobuf_vm_ops = { + .open = videobuf_vm_open, + .close = videobuf_vm_close, +}; + +static void *__videobuf_alloc(size_t size) +{ + struct videobuf_dma_contig_memory *mem; + struct videobuf_buffer *vb; + + vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); + if (vb) { + mem = vb->priv = ((char *)vb) + size; + mem->magic = MAGIC_DC_MEM; + } + + return vb; +} + +static void *__videobuf_to_vmalloc(struct videobuf_buffer *buf) +{ + struct videobuf_dma_contig_memory *mem = buf->priv; + + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + + return mem->vaddr; +} + +static int __videobuf_iolock(struct videobuf_queue *q, + struct videobuf_buffer *vb, + struct v4l2_framebuffer *fbuf) +{ + struct videobuf_dma_contig_memory *mem = vb->priv; + + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + + switch (vb->memory) { + case V4L2_MEMORY_MMAP: + dev_dbg(q->dev, "%s memory method MMAP\n", __func__); + + /* All handling should be done by __videobuf_mmap_mapper() */ + if (!mem->vaddr) { + dev_err(q->dev, "memory is not alloced/mmapped.\n"); + return -EINVAL; + } + break; + 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. + */ + if (vb->baddr) + return -EINVAL; + + mem->size = PAGE_ALIGN(vb->size); + mem->vaddr = dma_alloc_coherent(q->dev, mem->size, + &mem->dma_handle, GFP_KERNEL); + if (!mem->vaddr) { + dev_err(q->dev, "dma_alloc_coherent %ld failed\n", + mem->size); + return -ENOMEM; + } + + dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n", + mem->vaddr, mem->size); + break; + case V4L2_MEMORY_OVERLAY: + default: + dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int __videobuf_sync(struct videobuf_queue *q, + struct videobuf_buffer *buf) +{ + struct videobuf_dma_contig_memory *mem = buf->priv; + + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + + dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size, + DMA_FROM_DEVICE); + return 0; +} + +static int __videobuf_mmap_free(struct videobuf_queue *q) +{ + unsigned int i; + + dev_dbg(q->dev, "%s\n", __func__); + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (q->bufs[i] && q->bufs[i]->map) + return -EBUSY; + } + + return 0; +} + +static int __videobuf_mmap_mapper(struct videobuf_queue *q, + struct vm_area_struct *vma) +{ + struct videobuf_dma_contig_memory *mem; + struct videobuf_mapping *map; + unsigned int first; + int retval; + unsigned long size, offset = vma->vm_pgoff << PAGE_SHIFT; + + dev_dbg(q->dev, "%s\n", __func__); + if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + /* look for first buffer to map */ + for (first = 0; first < VIDEO_MAX_FRAME; first++) { + if (!q->bufs[first]) + continue; + + if (V4L2_MEMORY_MMAP != q->bufs[first]->memory) + continue; + if (q->bufs[first]->boff == offset) + break; + } + if (VIDEO_MAX_FRAME == first) { + dev_dbg(q->dev, "invalid user space offset [offset=0x%lx]\n", + offset); + return -EINVAL; + } + + /* create mapping + update buffer list */ + map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); + if (!map) + return -ENOMEM; + + q->bufs[first]->map = map; + map->start = vma->vm_start; + map->end = vma->vm_end; + map->q = q; + + q->bufs[first]->baddr = vma->vm_start; + + mem = q->bufs[first]->priv; + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + + mem->size = PAGE_ALIGN(q->bufs[first]->bsize); + mem->vaddr = dma_alloc_coherent(q->dev, mem->size, + &mem->dma_handle, GFP_KERNEL); + if (!mem->vaddr) { + dev_err(q->dev, "dma_alloc_coherent size %ld failed\n", + mem->size); + goto error; + } + dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n", + mem->vaddr, mem->size); + + /* Try to remap memory */ + + size = vma->vm_end - vma->vm_start; + size = (size < mem->size) ? size : mem->size; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + retval = remap_pfn_range(vma, vma->vm_start, + mem->dma_handle >> PAGE_SHIFT, + size, vma->vm_page_prot); + if (retval) { + dev_err(q->dev, "mmap: remap failed with error %d. ", retval); + dma_free_coherent(q->dev, mem->size, + mem->vaddr, mem->dma_handle); + goto error; + } + + vma->vm_ops = &videobuf_vm_ops; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = map; + + dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", + map, q, vma->vm_start, vma->vm_end, + (long int) q->bufs[first]->bsize, + vma->vm_pgoff, first); + + videobuf_vm_open(vma); + + return 0; + +error: + kfree(map); + return -ENOMEM; +} + +static int __videobuf_copy_to_user(struct videobuf_queue *q, + char __user *data, size_t count, + int nonblocking) +{ + struct videobuf_dma_contig_memory *mem = q->read_buf->priv; + void *vaddr; + + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + BUG_ON(!mem->vaddr); + + /* copy to userspace */ + if (count > q->read_buf->size - q->read_off) + count = q->read_buf->size - q->read_off; + + vaddr = mem->vaddr; + + if (copy_to_user(data, vaddr + q->read_off, count)) + return -EFAULT; + + return count; +} + +static int __videobuf_copy_stream(struct videobuf_queue *q, + char __user *data, size_t count, size_t pos, + int vbihack, int nonblocking) +{ + unsigned int *fc; + struct videobuf_dma_contig_memory *mem = q->read_buf->priv; + + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + + if (vbihack) { + /* dirty, undocumented hack -- pass the frame counter + * within the last four bytes of each vbi data block. + * We need that one to maintain backward compatibility + * to all vbi decoding software out there ... */ + fc = (unsigned int *)mem->vaddr; + fc += (q->read_buf->size >> 2) - 1; + *fc = q->read_buf->field_count >> 1; + dev_dbg(q->dev, "vbihack: %d\n", *fc); + } + + /* copy stuff using the common method */ + count = __videobuf_copy_to_user(q, data, count, nonblocking); + + if ((count == -EFAULT) && (pos == 0)) + return -EFAULT; + + return count; +} + +static struct videobuf_qtype_ops qops = { + .magic = MAGIC_QTYPE_OPS, + + .alloc = __videobuf_alloc, + .iolock = __videobuf_iolock, + .sync = __videobuf_sync, + .mmap_free = __videobuf_mmap_free, + .mmap_mapper = __videobuf_mmap_mapper, + .video_copy_to_user = __videobuf_copy_to_user, + .copy_stream = __videobuf_copy_stream, + .vmalloc = __videobuf_to_vmalloc, +}; + +void videobuf_queue_dma_contig_init(struct videobuf_queue *q, + struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv) +{ + videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, + priv, &qops); +} +EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init); + +dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf) +{ + struct videobuf_dma_contig_memory *mem = buf->priv; + + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + + return mem->dma_handle; +} +EXPORT_SYMBOL_GPL(videobuf_to_dma_contig); + +void videobuf_dma_contig_free(struct videobuf_queue *q, + struct videobuf_buffer *buf) +{ + struct videobuf_dma_contig_memory *mem = buf->priv; + + /* mmapped memory can't be freed here, otherwise mmapped region + would be released, while still needed. In this case, the memory + release should happen inside videobuf_vm_close(). + So, it should free memory only if the memory were allocated for + read() operation. + */ + if ((buf->memory != V4L2_MEMORY_USERPTR) || !buf->baddr) + return; + + if (!mem) + return; + + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + + dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle); + mem->vaddr = NULL; +} +EXPORT_SYMBOL_GPL(videobuf_dma_contig_free); + +MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers"); +MODULE_AUTHOR("Magnus Damm"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index 03a7b946bd5..bc6d5aba0fe 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -1,7 +1,7 @@ /* * helper functions for SG DMA video4linux capture buffers * - * The functions expect the hardware being able to scatter gatter + * The functions expect the hardware being able to scatter gather * (i.e. the buffers are not linear in physical memory, but fragmented * into PAGE_SIZE chunks). They also assume the driver does not need * to touch the video data. @@ -80,17 +80,15 @@ struct scatterlist* videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) { struct scatterlist *sglist; - int i = 0; + int i; if (NULL == pages[0]) return NULL; - sglist = kcalloc(nr_pages, sizeof(*sglist), GFP_KERNEL); + sglist = kmalloc(nr_pages * sizeof(*sglist), GFP_KERNEL); if (NULL == sglist) return NULL; sg_init_table(sglist, nr_pages); - if (NULL == pages[0]) - goto nopage; if (PageHighMem(pages[0])) /* DMA to highmem pages might not work */ goto highmem; diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c index 6e4d73ec685..b56cffcbfd4 100644 --- a/drivers/media/video/videobuf-dvb.c +++ b/drivers/media/video/videobuf-dvb.c @@ -13,7 +13,6 @@ * (at your option) any later version. */ - #include <linux/module.h> #include <linux/init.h> #include <linux/device.h> @@ -257,4 +256,3 @@ EXPORT_SYMBOL(videobuf_dvb_unregister); * compile-command: "make DVB=1" * End: */ - diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index c91e1d8e380..a868b7ed75f 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -1,7 +1,7 @@ /* * helper functions for vmalloc video4linux capture buffers * - * The functions expect the hardware being able to scatter gatter + * The functions expect the hardware being able to scatter gather * (i.e. the buffers are not linear in physical memory, but fragmented * into PAGE_SIZE chunks). They also assume the driver does not need * to touch the video data. diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 7649860a388..6616e657055 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -17,15 +17,19 @@ */ #define dbgarg(cmd, fmt, arg...) \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { \ + do { \ + if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { \ printk(KERN_DEBUG "%s: ", vfd->name); \ v4l_printk_ioctl(cmd); \ printk(" " fmt, ## arg); \ - } + } \ + } while (0) #define dbgarg2(fmt, arg...) \ - if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ - printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg); + do { \ + if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ + printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\ + } while (0) #include <linux/module.h> #include <linux/types.h> @@ -138,7 +142,7 @@ EXPORT_SYMBOL(v4l2_video_std_construct); /* ----------------------------------------------------------------- */ /* some arrays for pretty-printing debug messages of enum types */ -char *v4l2_field_names[] = { +const char *v4l2_field_names[] = { [V4L2_FIELD_ANY] = "any", [V4L2_FIELD_NONE] = "none", [V4L2_FIELD_TOP] = "top", @@ -152,19 +156,19 @@ char *v4l2_field_names[] = { }; EXPORT_SYMBOL(v4l2_field_names); -char *v4l2_type_names[] = { - [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "video-cap", - [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "video-over", - [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "video-out", +const char *v4l2_type_names[] = { + [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "vid-cap", + [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "vid-overlay", + [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "vid-out", [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap", [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out", - [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over", + [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay", }; EXPORT_SYMBOL(v4l2_type_names); -static char *v4l2_memory_names[] = { +static const char *v4l2_memory_names[] = { [V4L2_MEMORY_MMAP] = "mmap", [V4L2_MEMORY_USERPTR] = "userptr", [V4L2_MEMORY_OVERLAY] = "overlay", @@ -278,6 +282,7 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", [_IOC_NR(VIDIOC_G_CHIP_IDENT)] = "VIDIOC_G_CHIP_IDENT", + [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", #endif }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -371,6 +376,14 @@ EXPORT_SYMBOL(v4l_printk_ioctl); * sysfs stuff */ +static ssize_t show_index(struct device *cd, + struct device_attribute *attr, char *buf) +{ + struct video_device *vfd = container_of(cd, struct video_device, + class_dev); + return sprintf(buf, "%i\n", vfd->index); +} + static ssize_t show_name(struct device *cd, struct device_attribute *attr, char *buf) { @@ -379,6 +392,12 @@ static ssize_t show_name(struct device *cd, return sprintf(buf, "%.*s\n", (int)sizeof(vfd->name), vfd->name); } +static struct device_attribute video_device_attrs[] = { + __ATTR(name, S_IRUGO, show_name, NULL), + __ATTR(index, S_IRUGO, show_index, NULL), + __ATTR_NULL +}; + struct video_device *video_device_alloc(void) { struct video_device *vfd; @@ -407,11 +426,6 @@ static void video_release(struct device *cd) vfd->release(vfd); } -static struct device_attribute video_device_attrs[] = { - __ATTR(name, S_IRUGO, show_name, NULL), - __ATTR_NULL -}; - static struct class video_class = { .name = VIDEO_NAME, .dev_attrs = video_device_attrs, @@ -650,7 +664,7 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd, p->field, p->sequence, prt_names(p->memory, v4l2_memory_names), p->m.userptr, p->length); - dbgarg2 ("timecode= %02d:%02d:%02d type=%d, " + dbgarg2("timecode=%02d:%02d:%02d type=%d, " "flags=0x%08d, frames=%d, userbits=0x%08x\n", tc->hours,tc->minutes,tc->seconds, tc->type, tc->flags, tc->frames, *(__u32 *) tc->userbits); @@ -659,7 +673,7 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd, static inline void dbgrect(struct video_device *vfd, char *s, struct v4l2_rect *r) { - dbgarg2 ("%sRect start at %dx%d, size= %dx%d\n", s, r->left, r->top, + dbgarg2("%sRect start at %dx%d, size=%dx%d\n", s, r->left, r->top, r->width, r->height); }; @@ -677,40 +691,85 @@ static inline void v4l_print_pix_fmt (struct video_device *vfd, fmt->bytesperline, fmt->sizeimage, fmt->colorspace); }; +static inline void v4l_print_ext_ctrls(unsigned int cmd, + struct video_device *vfd, struct v4l2_ext_controls *c, int show_vals) +{ + __u32 i; + + if (!(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) + return; + dbgarg(cmd, ""); + printk(KERN_CONT "class=0x%x", c->ctrl_class); + for (i = 0; i < c->count; i++) { + if (show_vals) + printk(KERN_CONT " id/val=0x%x/0x%x", + c->controls[i].id, c->controls[i].value); + else + printk(KERN_CONT " id=0x%x", c->controls[i].id); + } + printk(KERN_CONT "\n"); +}; + +static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) +{ + __u32 i; + + /* zero the reserved fields */ + c->reserved[0] = c->reserved[1] = 0; + for (i = 0; i < c->count; i++) { + c->controls[i].reserved2[0] = 0; + c->controls[i].reserved2[1] = 0; + } + /* V4L2_CID_PRIVATE_BASE cannot be used as control class + when using extended controls. + Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL + is it allowed for backwards compatibility. + */ + if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE) + return 0; + /* Check that all controls are from the same control class. */ + for (i = 0; i < c->count; i++) { + if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) { + c->error_idx = i; + return 0; + } + } + return 1; +} static int check_fmt (struct video_device *vfd, enum v4l2_buf_type type) { switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (vfd->vidioc_try_fmt_cap) + if (vfd->vidioc_try_fmt_vid_cap) return (0); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (vfd->vidioc_try_fmt_overlay) + if (vfd->vidioc_try_fmt_vid_overlay) return (0); break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (vfd->vidioc_try_fmt_vbi) + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (vfd->vidioc_try_fmt_vid_out) return (0); break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (vfd->vidioc_try_fmt_vbi_output) + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_try_fmt_vid_out_overlay) return (0); break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (vfd->vidioc_try_fmt_vbi_capture) + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (vfd->vidioc_try_fmt_vbi_cap) return (0); break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (vfd->vidioc_try_fmt_video_output) + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (vfd->vidioc_try_fmt_vbi_out) return (0); break; - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (vfd->vidioc_try_fmt_vbi_output) + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (vfd->vidioc_try_fmt_sliced_vbi_cap) return (0); break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (vfd->vidioc_try_fmt_output_overlay) + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (vfd->vidioc_try_fmt_sliced_vbi_out) return (0); break; case V4L2_BUF_TYPE_PRIVATE: @@ -827,46 +886,37 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (vfd->vidioc_enum_fmt_cap) - ret=vfd->vidioc_enum_fmt_cap(file, fh, f); + if (vfd->vidioc_enum_fmt_vid_cap) + ret = vfd->vidioc_enum_fmt_vid_cap(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (vfd->vidioc_enum_fmt_overlay) - ret=vfd->vidioc_enum_fmt_overlay(file, fh, f); + if (vfd->vidioc_enum_fmt_vid_overlay) + ret = vfd->vidioc_enum_fmt_vid_overlay(file, + fh, f); break; +#if 1 + /* V4L2_BUF_TYPE_VBI_CAPTURE should not support VIDIOC_ENUM_FMT + * according to the spec. The bttv and saa7134 drivers support + * it though, so just warn that this is deprecated and will be + * removed in the near future. */ case V4L2_BUF_TYPE_VBI_CAPTURE: - if (vfd->vidioc_enum_fmt_vbi) - ret=vfd->vidioc_enum_fmt_vbi(file, fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (vfd->vidioc_enum_fmt_vbi_output) - ret=vfd->vidioc_enum_fmt_vbi_output(file, - fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (vfd->vidioc_enum_fmt_vbi_capture) - ret=vfd->vidioc_enum_fmt_vbi_capture(file, - fh, f); + if (vfd->vidioc_enum_fmt_vbi_cap) { + printk(KERN_WARNING "vidioc_enum_fmt_vbi_cap will be removed in 2.6.28!\n"); + ret = vfd->vidioc_enum_fmt_vbi_cap(file, fh, f); + } break; +#endif case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (vfd->vidioc_enum_fmt_video_output) - ret=vfd->vidioc_enum_fmt_video_output(file, - fh, f); - break; - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (vfd->vidioc_enum_fmt_vbi_output) - ret=vfd->vidioc_enum_fmt_vbi_output(file, - fh, f); - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (vfd->vidioc_enum_fmt_output_overlay) - ret=vfd->vidioc_enum_fmt_output_overlay(file, fh, f); + if (vfd->vidioc_enum_fmt_vid_out) + ret = vfd->vidioc_enum_fmt_vid_out(file, fh, f); break; case V4L2_BUF_TYPE_PRIVATE: if (vfd->vidioc_enum_fmt_type_private) - ret=vfd->vidioc_enum_fmt_type_private(file, + ret = vfd->vidioc_enum_fmt_type_private(file, fh, f); break; + default: + break; } if (!ret) dbgarg (cmd, "index=%d, type=%d, flags=%d, " @@ -882,54 +932,56 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_FMT: { struct v4l2_format *f = (struct v4l2_format *)arg; - enum v4l2_buf_type type=f->type; - memset(&f->fmt.pix,0,sizeof(f->fmt.pix)); - f->type=type; + memset(f->fmt.raw_data, 0, sizeof(f->fmt.raw_data)); /* FIXME: Should be one dump per type */ - dbgarg (cmd, "type=%s\n", prt_names(type, - v4l2_type_names)); + dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); - switch (type) { + switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (vfd->vidioc_g_fmt_cap) - ret=vfd->vidioc_g_fmt_cap(file, fh, f); + if (vfd->vidioc_g_fmt_vid_cap) + ret = vfd->vidioc_g_fmt_vid_cap(file, fh, f); if (!ret) - v4l_print_pix_fmt(vfd,&f->fmt.pix); + v4l_print_pix_fmt(vfd, &f->fmt.pix); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (vfd->vidioc_g_fmt_overlay) - ret=vfd->vidioc_g_fmt_overlay(file, fh, f); - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (vfd->vidioc_g_fmt_vbi) - ret=vfd->vidioc_g_fmt_vbi(file, fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (vfd->vidioc_g_fmt_vbi_output) - ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (vfd->vidioc_g_fmt_vbi_capture) - ret=vfd->vidioc_g_fmt_vbi_capture(file, fh, f); + if (vfd->vidioc_g_fmt_vid_overlay) + ret = vfd->vidioc_g_fmt_vid_overlay(file, + fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (vfd->vidioc_g_fmt_video_output) - ret=vfd->vidioc_g_fmt_video_output(file, - fh, f); + if (vfd->vidioc_g_fmt_vid_out) + ret = vfd->vidioc_g_fmt_vid_out(file, fh, f); + if (!ret) + v4l_print_pix_fmt(vfd, &f->fmt.pix); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (vfd->vidioc_g_fmt_output_overlay) - ret=vfd->vidioc_g_fmt_output_overlay(file, fh, f); + if (vfd->vidioc_g_fmt_vid_out_overlay) + ret = vfd->vidioc_g_fmt_vid_out_overlay(file, + fh, f); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (vfd->vidioc_g_fmt_vbi_cap) + ret = vfd->vidioc_g_fmt_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_VBI_OUTPUT: - if (vfd->vidioc_g_fmt_vbi_output) - ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f); + if (vfd->vidioc_g_fmt_vbi_out) + ret = vfd->vidioc_g_fmt_vbi_out(file, fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (vfd->vidioc_g_fmt_sliced_vbi_cap) + ret = vfd->vidioc_g_fmt_sliced_vbi_cap(file, + fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (vfd->vidioc_g_fmt_sliced_vbi_out) + ret = vfd->vidioc_g_fmt_sliced_vbi_out(file, + fh, f); break; case V4L2_BUF_TYPE_PRIVATE: if (vfd->vidioc_g_fmt_type_private) - ret=vfd->vidioc_g_fmt_type_private(file, + ret = vfd->vidioc_g_fmt_type_private(file, fh, f); break; } @@ -941,48 +993,50 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, struct v4l2_format *f = (struct v4l2_format *)arg; /* FIXME: Should be one dump per type */ - dbgarg (cmd, "type=%s\n", prt_names(f->type, - v4l2_type_names)); + dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - v4l_print_pix_fmt(vfd,&f->fmt.pix); - if (vfd->vidioc_s_fmt_cap) - ret=vfd->vidioc_s_fmt_cap(file, fh, f); + v4l_print_pix_fmt(vfd, &f->fmt.pix); + if (vfd->vidioc_s_fmt_vid_cap) + ret = vfd->vidioc_s_fmt_vid_cap(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (vfd->vidioc_s_fmt_overlay) - ret=vfd->vidioc_s_fmt_overlay(file, fh, f); - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (vfd->vidioc_s_fmt_vbi) - ret=vfd->vidioc_s_fmt_vbi(file, fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (vfd->vidioc_s_fmt_vbi_output) - ret=vfd->vidioc_s_fmt_vbi_output(file, fh, f); - break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (vfd->vidioc_s_fmt_vbi_capture) - ret=vfd->vidioc_s_fmt_vbi_capture(file, fh, f); + if (vfd->vidioc_s_fmt_vid_overlay) + ret = vfd->vidioc_s_fmt_vid_overlay(file, + fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (vfd->vidioc_s_fmt_video_output) - ret=vfd->vidioc_s_fmt_video_output(file, - fh, f); + v4l_print_pix_fmt(vfd, &f->fmt.pix); + if (vfd->vidioc_s_fmt_vid_out) + ret = vfd->vidioc_s_fmt_vid_out(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (vfd->vidioc_s_fmt_output_overlay) - ret=vfd->vidioc_s_fmt_output_overlay(file, fh, f); + if (vfd->vidioc_s_fmt_vid_out_overlay) + ret = vfd->vidioc_s_fmt_vid_out_overlay(file, + fh, f); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (vfd->vidioc_s_fmt_vbi_cap) + ret = vfd->vidioc_s_fmt_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_VBI_OUTPUT: - if (vfd->vidioc_s_fmt_vbi_output) - ret=vfd->vidioc_s_fmt_vbi_output(file, - fh, f); + if (vfd->vidioc_s_fmt_vbi_out) + ret = vfd->vidioc_s_fmt_vbi_out(file, fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + if (vfd->vidioc_s_fmt_sliced_vbi_cap) + ret = vfd->vidioc_s_fmt_sliced_vbi_cap(file, + fh, f); + break; + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (vfd->vidioc_s_fmt_sliced_vbi_out) + ret = vfd->vidioc_s_fmt_sliced_vbi_out(file, + fh, f); break; case V4L2_BUF_TYPE_PRIVATE: if (vfd->vidioc_s_fmt_type_private) - ret=vfd->vidioc_s_fmt_type_private(file, + ret = vfd->vidioc_s_fmt_type_private(file, fh, f); break; } @@ -997,46 +1051,48 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, v4l2_type_names)); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (vfd->vidioc_try_fmt_cap) - ret=vfd->vidioc_try_fmt_cap(file, fh, f); + if (vfd->vidioc_try_fmt_vid_cap) + ret = vfd->vidioc_try_fmt_vid_cap(file, fh, f); if (!ret) - v4l_print_pix_fmt(vfd,&f->fmt.pix); + v4l_print_pix_fmt(vfd, &f->fmt.pix); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (vfd->vidioc_try_fmt_overlay) - ret=vfd->vidioc_try_fmt_overlay(file, fh, f); + if (vfd->vidioc_try_fmt_vid_overlay) + ret = vfd->vidioc_try_fmt_vid_overlay(file, + fh, f); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (vfd->vidioc_try_fmt_vid_out) + ret = vfd->vidioc_try_fmt_vid_out(file, fh, f); + if (!ret) + v4l_print_pix_fmt(vfd, &f->fmt.pix); + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_try_fmt_vid_out_overlay) + ret = vfd->vidioc_try_fmt_vid_out_overlay(file, + fh, f); break; case V4L2_BUF_TYPE_VBI_CAPTURE: - if (vfd->vidioc_try_fmt_vbi) - ret=vfd->vidioc_try_fmt_vbi(file, fh, f); + if (vfd->vidioc_try_fmt_vbi_cap) + ret = vfd->vidioc_try_fmt_vbi_cap(file, fh, f); break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (vfd->vidioc_try_fmt_vbi_output) - ret=vfd->vidioc_try_fmt_vbi_output(file, - fh, f); + case V4L2_BUF_TYPE_VBI_OUTPUT: + if (vfd->vidioc_try_fmt_vbi_out) + ret = vfd->vidioc_try_fmt_vbi_out(file, fh, f); break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (vfd->vidioc_try_fmt_vbi_capture) - ret=vfd->vidioc_try_fmt_vbi_capture(file, - fh, f); - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (vfd->vidioc_try_fmt_video_output) - ret=vfd->vidioc_try_fmt_video_output(file, + if (vfd->vidioc_try_fmt_sliced_vbi_cap) + ret = vfd->vidioc_try_fmt_sliced_vbi_cap(file, fh, f); break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (vfd->vidioc_try_fmt_output_overlay) - ret=vfd->vidioc_try_fmt_output_overlay(file, fh, f); - break; - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (vfd->vidioc_try_fmt_vbi_output) - ret=vfd->vidioc_try_fmt_vbi_output(file, + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + if (vfd->vidioc_try_fmt_sliced_vbi_out) + ret = vfd->vidioc_try_fmt_sliced_vbi_out(file, fh, f); break; case V4L2_BUF_TYPE_PRIVATE: if (vfd->vidioc_try_fmt_type_private) - ret=vfd->vidioc_try_fmt_type_private(file, + ret = vfd->vidioc_try_fmt_type_private(file, fh, f); break; } @@ -1120,29 +1176,29 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, } case VIDIOC_G_FBUF: { - struct v4l2_framebuffer *p=arg; + struct v4l2_framebuffer *p = arg; + if (!vfd->vidioc_g_fbuf) break; - ret=vfd->vidioc_g_fbuf(file, fh, arg); + ret = vfd->vidioc_g_fbuf(file, fh, arg); if (!ret) { - dbgarg (cmd, "capability=%d, flags=%d, base=0x%08lx\n", - p->capability,p->flags, + dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", + p->capability, p->flags, (unsigned long)p->base); - v4l_print_pix_fmt (vfd, &p->fmt); + v4l_print_pix_fmt(vfd, &p->fmt); } break; } case VIDIOC_S_FBUF: { - struct v4l2_framebuffer *p=arg; + struct v4l2_framebuffer *p = arg; + if (!vfd->vidioc_s_fbuf) break; - - dbgarg (cmd, "capability=%d, flags=%d, base=0x%08lx\n", - p->capability,p->flags,(unsigned long)p->base); - v4l_print_pix_fmt (vfd, &p->fmt); - ret=vfd->vidioc_s_fbuf(file, fh, arg); - + dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", + p->capability, p->flags, (unsigned long)p->base); + v4l_print_pix_fmt(vfd, &p->fmt); + ret = vfd->vidioc_s_fbuf(file, fh, arg); break; } case VIDIOC_STREAMON: @@ -1194,7 +1250,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, v4l2_video_std_construct(p, curr_id, descr); p->index = index; - dbgarg(cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, " + dbgarg(cmd, "index=%d, id=0x%Lx, name=%s, fps=%d/%d, " "framelines=%d\n", p->index, (unsigned long long)p->id, p->name, p->frameperiod.numerator, @@ -1208,18 +1264,22 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, { v4l2_std_id *id = arg; - *id = vfd->current_norm; - - dbgarg (cmd, "value=%08Lx\n", (long long unsigned) *id); + ret = 0; + /* Calls the specific handler */ + if (vfd->vidioc_g_std) + ret = vfd->vidioc_g_std(file, fh, id); + else + *id = vfd->current_norm; - ret=0; + if (!ret) + dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id); break; } case VIDIOC_S_STD: { v4l2_std_id *id = arg,norm; - dbgarg (cmd, "value=%08Lx\n", (long long unsigned) *id); + dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id); norm = (*id) & vfd->tvnorms; if ( vfd->tvnorms && !norm) /* Check if std is supported */ @@ -1295,6 +1355,25 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, } /* ------ output switching ---------- */ + case VIDIOC_ENUMOUTPUT: + { + struct v4l2_output *p = arg; + int i = p->index; + + if (!vfd->vidioc_enum_output) + break; + memset(p, 0, sizeof(*p)); + p->index = i; + + ret = vfd->vidioc_enum_output(file, fh, p); + if (!ret) + dbgarg(cmd, "index=%d, name=%s, type=%d, " + "audioset=0x%x, " + "modulator=%d, std=0x%08Lx\n", + p->index, p->name, p->type, p->audioset, + p->modulator, (unsigned long long)p->std); + break; + } case VIDIOC_G_OUTPUT: { unsigned int *i = arg; @@ -1320,132 +1399,172 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, /* --- controls ---------------------------------------------- */ case VIDIOC_QUERYCTRL: { - struct v4l2_queryctrl *p=arg; + struct v4l2_queryctrl *p = arg; if (!vfd->vidioc_queryctrl) break; - ret=vfd->vidioc_queryctrl(file, fh, p); - + ret = vfd->vidioc_queryctrl(file, fh, p); if (!ret) - dbgarg (cmd, "id=%d, type=%d, name=%s, " - "min/max=%d/%d," - " step=%d, default=%d, flags=0x%08x\n", - p->id,p->type,p->name,p->minimum, - p->maximum,p->step,p->default_value, - p->flags); + dbgarg(cmd, "id=0x%x, type=%d, name=%s, min/max=%d/%d, " + "step=%d, default=%d, flags=0x%08x\n", + p->id, p->type, p->name, + p->minimum, p->maximum, + p->step, p->default_value, p->flags); + else + dbgarg(cmd, "id=0x%x\n", p->id); break; } case VIDIOC_G_CTRL: { struct v4l2_control *p = arg; - if (!vfd->vidioc_g_ctrl) + if (vfd->vidioc_g_ctrl) + ret = vfd->vidioc_g_ctrl(file, fh, p); + else if (vfd->vidioc_g_ext_ctrls) { + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); + ctrls.count = 1; + ctrls.controls = &ctrl; + ctrl.id = p->id; + ctrl.value = p->value; + if (check_ext_ctrls(&ctrls, 1)) { + ret = vfd->vidioc_g_ext_ctrls(file, fh, &ctrls); + if (ret == 0) + p->value = ctrl.value; + } + } else break; - dbgarg(cmd, "Enum for index=%d\n", p->id); - - ret=vfd->vidioc_g_ctrl(file, fh, p); if (!ret) - dbgarg2 ( "id=%d, value=%d\n", p->id, p->value); + dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); + else + dbgarg(cmd, "id=0x%x\n", p->id); break; } case VIDIOC_S_CTRL: { struct v4l2_control *p = arg; + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + + if (!vfd->vidioc_s_ctrl && !vfd->vidioc_s_ext_ctrls) + break; - if (!vfd->vidioc_s_ctrl) + dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); + + if (vfd->vidioc_s_ctrl) { + ret = vfd->vidioc_s_ctrl(file, fh, p); + break; + } + if (!vfd->vidioc_s_ext_ctrls) break; - dbgarg (cmd, "id=%d, value=%d\n", p->id, p->value); - ret=vfd->vidioc_s_ctrl(file, fh, p); + ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); + ctrls.count = 1; + ctrls.controls = &ctrl; + ctrl.id = p->id; + ctrl.value = p->value; + if (check_ext_ctrls(&ctrls, 1)) + ret = vfd->vidioc_s_ext_ctrls(file, fh, &ctrls); break; } case VIDIOC_G_EXT_CTRLS: { struct v4l2_ext_controls *p = arg; - if (vfd->vidioc_g_ext_ctrls) { - dbgarg(cmd, "count=%d\n", p->count); - - ret=vfd->vidioc_g_ext_ctrls(file, fh, p); - } + p->error_idx = p->count; + if (!vfd->vidioc_g_ext_ctrls) + break; + if (check_ext_ctrls(p, 0)) + ret = vfd->vidioc_g_ext_ctrls(file, fh, p); + v4l_print_ext_ctrls(cmd, vfd, p, !ret); break; } case VIDIOC_S_EXT_CTRLS: { struct v4l2_ext_controls *p = arg; - if (vfd->vidioc_s_ext_ctrls) { - dbgarg(cmd, "count=%d\n", p->count); - - ret=vfd->vidioc_s_ext_ctrls(file, fh, p); - } + p->error_idx = p->count; + if (!vfd->vidioc_s_ext_ctrls) + break; + v4l_print_ext_ctrls(cmd, vfd, p, 1); + if (check_ext_ctrls(p, 0)) + ret = vfd->vidioc_s_ext_ctrls(file, fh, p); break; } case VIDIOC_TRY_EXT_CTRLS: { struct v4l2_ext_controls *p = arg; - if (vfd->vidioc_try_ext_ctrls) { - dbgarg(cmd, "count=%d\n", p->count); - - ret=vfd->vidioc_try_ext_ctrls(file, fh, p); - } + p->error_idx = p->count; + if (!vfd->vidioc_try_ext_ctrls) + break; + v4l_print_ext_ctrls(cmd, vfd, p, 1); + if (check_ext_ctrls(p, 0)) + ret = vfd->vidioc_try_ext_ctrls(file, fh, p); break; } case VIDIOC_QUERYMENU: { - struct v4l2_querymenu *p=arg; + struct v4l2_querymenu *p = arg; + if (!vfd->vidioc_querymenu) break; - ret=vfd->vidioc_querymenu(file, fh, p); + ret = vfd->vidioc_querymenu(file, fh, p); if (!ret) - dbgarg (cmd, "id=%d, index=%d, name=%s\n", - p->id,p->index,p->name); + dbgarg(cmd, "id=0x%x, index=%d, name=%s\n", + p->id, p->index, p->name); + else + dbgarg(cmd, "id=0x%x, index=%d\n", + p->id, p->index); break; } /* --- audio ---------------------------------------------- */ case VIDIOC_ENUMAUDIO: { - struct v4l2_audio *p=arg; + struct v4l2_audio *p = arg; if (!vfd->vidioc_enumaudio) break; - dbgarg(cmd, "Enum for index=%d\n", p->index); - ret=vfd->vidioc_enumaudio(file, fh, p); + ret = vfd->vidioc_enumaudio(file, fh, p); if (!ret) - dbgarg2("index=%d, name=%s, capability=%d, " - "mode=%d\n",p->index,p->name, + dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " + "mode=0x%x\n", p->index, p->name, p->capability, p->mode); + else + dbgarg(cmd, "index=%d\n", p->index); break; } case VIDIOC_G_AUDIO: { - struct v4l2_audio *p=arg; - __u32 index=p->index; + struct v4l2_audio *p = arg; + __u32 index = p->index; if (!vfd->vidioc_g_audio) break; - memset(p,0,sizeof(*p)); - p->index=index; - dbgarg(cmd, "Get for index=%d\n", p->index); - ret=vfd->vidioc_g_audio(file, fh, p); + memset(p, 0, sizeof(*p)); + p->index = index; + ret = vfd->vidioc_g_audio(file, fh, p); if (!ret) - dbgarg2("index=%d, name=%s, capability=%d, " - "mode=%d\n",p->index, - p->name,p->capability, p->mode); + dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " + "mode=0x%x\n", p->index, + p->name, p->capability, p->mode); + else + dbgarg(cmd, "index=%d\n", p->index); break; } case VIDIOC_S_AUDIO: { - struct v4l2_audio *p=arg; + struct v4l2_audio *p = arg; if (!vfd->vidioc_s_audio) break; - dbgarg(cmd, "index=%d, name=%s, capability=%d, " - "mode=%d\n", p->index, p->name, + dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " + "mode=0x%x\n", p->index, p->name, p->capability, p->mode); - ret=vfd->vidioc_s_audio(file, fh, p); + ret = vfd->vidioc_s_audio(file, fh, p); break; } case VIDIOC_ENUMAUDOUT: @@ -1521,9 +1640,9 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, struct v4l2_crop *p=arg; if (!vfd->vidioc_g_crop) break; + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); ret=vfd->vidioc_g_crop(file, fh, p); if (!ret) { - dbgarg(cmd, "type=%d\n", p->type); dbgrect(vfd, "", &p->c); } break; @@ -1533,21 +1652,24 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, struct v4l2_crop *p=arg; if (!vfd->vidioc_s_crop) break; - dbgarg(cmd, "type=%d\n", p->type); + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); dbgrect(vfd, "", &p->c); ret=vfd->vidioc_s_crop(file, fh, p); break; } case VIDIOC_CROPCAP: { - struct v4l2_cropcap *p=arg; + struct v4l2_cropcap *p = arg; + /*FIXME: Should also show v4l2_fract pixelaspect */ if (!vfd->vidioc_cropcap) break; - dbgarg(cmd, "type=%d\n", p->type); - dbgrect(vfd, "bounds ", &p->bounds); - dbgrect(vfd, "defrect ", &p->defrect); - ret=vfd->vidioc_cropcap(file, fh, p); + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); + ret = vfd->vidioc_cropcap(file, fh, p); + if (!ret) { + dbgrect(vfd, "bounds ", &p->bounds); + dbgrect(vfd, "defrect ", &p->defrect); + } break; } case VIDIOC_G_JPEGCOMP: @@ -1590,26 +1712,26 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, } case VIDIOC_ENCODER_CMD: { - struct v4l2_encoder_cmd *p=arg; + struct v4l2_encoder_cmd *p = arg; if (!vfd->vidioc_encoder_cmd) break; - ret=vfd->vidioc_encoder_cmd(file, fh, p); + memset(&p->raw, 0, sizeof(p->raw)); + ret = vfd->vidioc_encoder_cmd(file, fh, p); if (!ret) - dbgarg (cmd, "cmd=%d, flags=%d\n", - p->cmd,p->flags); + dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); break; } case VIDIOC_TRY_ENCODER_CMD: { - struct v4l2_encoder_cmd *p=arg; + struct v4l2_encoder_cmd *p = arg; if (!vfd->vidioc_try_encoder_cmd) break; - ret=vfd->vidioc_try_encoder_cmd(file, fh, p); + memset(&p->raw, 0, sizeof(p->raw)); + ret = vfd->vidioc_try_encoder_cmd(file, fh, p); if (!ret) - dbgarg (cmd, "cmd=%d, flags=%d\n", - p->cmd,p->flags); + dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); break; } case VIDIOC_G_PARM: @@ -1649,54 +1771,57 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, } case VIDIOC_G_TUNER: { - struct v4l2_tuner *p=arg; - __u32 index=p->index; + struct v4l2_tuner *p = arg; + __u32 index = p->index; if (!vfd->vidioc_g_tuner) break; - memset(p,0,sizeof(*p)); - p->index=index; + memset(p, 0, sizeof(*p)); + p->index = index; - ret=vfd->vidioc_g_tuner(file, fh, p); + ret = vfd->vidioc_g_tuner(file, fh, p); if (!ret) - dbgarg (cmd, "index=%d, name=%s, type=%d, " - "capability=%d, rangelow=%d, " + dbgarg(cmd, "index=%d, name=%s, type=%d, " + "capability=0x%x, rangelow=%d, " "rangehigh=%d, signal=%d, afc=%d, " - "rxsubchans=%d, audmode=%d\n", + "rxsubchans=0x%x, audmode=%d\n", p->index, p->name, p->type, p->capability, p->rangelow, - p->rangehigh, p->rxsubchans, - p->audmode, p->signal, p->afc); + p->rangehigh, p->signal, p->afc, + p->rxsubchans, p->audmode); break; } case VIDIOC_S_TUNER: { - struct v4l2_tuner *p=arg; + struct v4l2_tuner *p = arg; + if (!vfd->vidioc_s_tuner) break; - dbgarg (cmd, "index=%d, name=%s, type=%d, " - "capability=%d, rangelow=%d, rangehigh=%d, " - "signal=%d, afc=%d, rxsubchans=%d, " - "audmode=%d\n",p->index, p->name, p->type, - p->capability, p->rangelow,p->rangehigh, - p->rxsubchans, p->audmode, p->signal, - p->afc); - ret=vfd->vidioc_s_tuner(file, fh, p); + dbgarg(cmd, "index=%d, name=%s, type=%d, " + "capability=0x%x, rangelow=%d, " + "rangehigh=%d, signal=%d, afc=%d, " + "rxsubchans=0x%x, audmode=%d\n", + p->index, p->name, p->type, + p->capability, p->rangelow, + p->rangehigh, p->signal, p->afc, + p->rxsubchans, p->audmode); + ret = vfd->vidioc_s_tuner(file, fh, p); break; } case VIDIOC_G_FREQUENCY: { - struct v4l2_frequency *p=arg; + struct v4l2_frequency *p = arg; + if (!vfd->vidioc_g_frequency) break; - memset(p,0,sizeof(*p)); + memset(p->reserved, 0, sizeof(p->reserved)); - ret=vfd->vidioc_g_frequency(file, fh, p); + ret = vfd->vidioc_g_frequency(file, fh, p); if (!ret) - dbgarg (cmd, "tuner=%d, type=%d, frequency=%d\n", - p->tuner,p->type,p->frequency); + dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", + p->tuner, p->type, p->frequency); break; } case VIDIOC_S_FREQUENCY: @@ -1711,12 +1836,17 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, } case VIDIOC_G_SLICED_VBI_CAP: { - struct v4l2_sliced_vbi_cap *p=arg; + struct v4l2_sliced_vbi_cap *p = arg; + __u32 type = p->type; + if (!vfd->vidioc_g_sliced_vbi_cap) break; - ret=vfd->vidioc_g_sliced_vbi_cap(file, fh, p); + memset(p, 0, sizeof(*p)); + p->type = type; + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); + ret = vfd->vidioc_g_sliced_vbi_cap(file, fh, p); if (!ret) - dbgarg (cmd, "service_set=%d\n", p->service_set); + dbgarg2("service_set=%d\n", p->service_set); break; } case VIDIOC_LOG_STATUS: @@ -1763,13 +1893,23 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret = vfd->vidioc_default(file, fh, cmd, arg); break; } + case VIDIOC_S_HW_FREQ_SEEK: + { + struct v4l2_hw_freq_seek *p = arg; + if (!vfd->vidioc_s_hw_freq_seek) + break; + dbgarg(cmd, + "tuner=%d, type=%d, seek_upward=%d, wrap_around=%d\n", + p->tuner, p->type, p->seek_upward, p->wrap_around); + ret = vfd->vidioc_s_hw_freq_seek(file, fh, p); + break; + } } /* switch */ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { - if (ret<0) { - printk("%s: err: on ", vfd->name); + if (ret < 0) { v4l_print_ioctl(vfd->name, cmd); - printk("\n"); + printk(KERN_CONT " error %d\n", ret); } } @@ -1871,8 +2011,55 @@ out: } EXPORT_SYMBOL(video_ioctl2); +/** + * get_index - assign stream number based on parent device + * @vdev: video_device to assign index number to, vdev->dev should be assigned + * @num: -1 if auto assign, requested number otherwise + * + * + * returns -ENFILE if num is already in use, a free index number if + * successful. + */ +static int get_index(struct video_device *vdev, int num) +{ + u32 used = 0; + const int max_index = sizeof(used) * 8 - 1; + int i; + + /* Currently a single v4l driver instance cannot create more than + 32 devices. + Increase to u64 or an array of u32 if more are needed. */ + if (num > max_index) { + printk(KERN_ERR "videodev: %s num is too large\n", __func__); + return -EINVAL; + } + + for (i = 0; i < VIDEO_NUM_DEVICES; i++) { + if (video_device[i] != NULL && + video_device[i] != vdev && + video_device[i]->dev == vdev->dev) { + used |= 1 << video_device[i]->index; + } + } + + if (num >= 0) { + if (used & (1 << num)) + return -ENFILE; + return num; + } + + i = ffz(used); + return i > max_index ? -ENFILE : i; +} + static const struct file_operations video_fops; +int video_register_device(struct video_device *vfd, int type, int nr) +{ + return video_register_device_index(vfd, type, nr, -1); +} +EXPORT_SYMBOL(video_register_device); + /** * video_register_device - register video4linux devices * @vfd: video device structure we want to register @@ -1898,7 +2085,8 @@ static const struct file_operations video_fops; * %VFL_TYPE_RADIO - A radio card */ -int video_register_device(struct video_device *vfd, int type, int nr) +int video_register_device_index(struct video_device *vfd, int type, int nr, + int index) { int i=0; int base; @@ -1955,20 +2143,29 @@ int video_register_device(struct video_device *vfd, int type, int nr) } video_device[i]=vfd; vfd->minor=i; + + ret = get_index(vfd, index); + vfd->index = ret; + mutex_unlock(&videodev_lock); + + if (ret < 0) { + printk(KERN_ERR "%s: get_index failed\n", __func__); + goto fail_minor; + } + mutex_init(&vfd->lock); /* sysfs class */ memset(&vfd->class_dev, 0x00, sizeof(vfd->class_dev)); - if (vfd->dev) - vfd->class_dev.parent = vfd->dev; vfd->class_dev.class = &video_class; vfd->class_dev.devt = MKDEV(VIDEO_MAJOR, vfd->minor); + if (vfd->dev) + vfd->class_dev.parent = vfd->dev; sprintf(vfd->class_dev.bus_id, "%s%d", name_base, i - base); ret = device_register(&vfd->class_dev); if (ret < 0) { - printk(KERN_ERR "%s: device_register failed\n", - __func__); + printk(KERN_ERR "%s: device_register failed\n", __func__); goto fail_minor; } @@ -1988,7 +2185,7 @@ fail_minor: mutex_unlock(&videodev_lock); return ret; } -EXPORT_SYMBOL(video_register_device); +EXPORT_SYMBOL(video_register_device_index); /** * video_unregister_device - unregister a video4linux device diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 5ff9a58b613..059b01c11dc 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -39,6 +39,8 @@ #include <linux/highmem.h> #include <linux/freezer.h> +#define VIVI_MODULE_NAME "vivi" + /* Wake up at about 30 fps */ #define WAKE_NUMERATOR 30 #define WAKE_DENOMINATOR 1001 @@ -47,7 +49,7 @@ #include "font.h" #define VIVI_MAJOR_VERSION 0 -#define VIVI_MINOR_VERSION 4 +#define VIVI_MINOR_VERSION 5 #define VIVI_RELEASE 0 #define VIVI_VERSION \ KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE) @@ -630,7 +632,7 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static int vidioc_enum_fmt_cap(struct file *file, void *priv, +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (f->index > 0) @@ -641,7 +643,7 @@ static int vidioc_enum_fmt_cap(struct file *file, void *priv, return 0; } -static int vidioc_g_fmt_cap(struct file *file, void *priv, +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vivi_fh *fh = priv; @@ -658,7 +660,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv, return (0); } -static int vidioc_try_fmt_cap(struct file *file, void *priv, +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vivi_fh *fh = priv; @@ -706,13 +708,13 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv, } /*FIXME: This seems to be generic enough to be at videodev2 */ -static int vidioc_s_fmt_cap(struct file *file, void *priv, +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vivi_fh *fh = priv; struct videobuf_queue *q = &fh->vb_vidq; - int ret = vidioc_try_fmt_cap(file, fh, f); + int ret = vidioc_try_fmt_vid_cap(file, fh, f); if (ret < 0) return (ret); @@ -1017,10 +1019,15 @@ static int vivi_release(void) list_del(list); dev = list_entry(list, struct vivi_dev, vivi_devlist); - if (-1 != dev->vfd->minor) + if (-1 != dev->vfd->minor) { video_unregister_device(dev->vfd); - else + printk(KERN_INFO "%s: /dev/video%d unregistered.\n", + VIVI_MODULE_NAME, dev->vfd->minor); + } else { video_device_release(dev->vfd); + printk(KERN_INFO "%s: /dev/video%d released.\n", + VIVI_MODULE_NAME, dev->vfd->minor); + } kfree(dev); } @@ -1066,10 +1073,10 @@ static struct video_device vivi_template = { .release = video_device_release, .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, - .vidioc_g_fmt_cap = vidioc_g_fmt_cap, - .vidioc_try_fmt_cap = vidioc_try_fmt_cap, - .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .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, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, @@ -1131,6 +1138,8 @@ static int __init vivi_init(void) video_nr++; dev->vfd = vfd; + printk(KERN_INFO "%s: V4L2 device registered as /dev/video%d\n", + VIVI_MODULE_NAME, vfd->minor); } if (ret < 0) { @@ -1138,7 +1147,9 @@ static int __init vivi_init(void) printk(KERN_INFO "Error %d while loading vivi driver\n", ret); } else printk(KERN_INFO "Video Technology Magazine Virtual Video " - "Capture Board successfully loaded.\n"); + "Capture Board ver %u.%u.%u successfully loaded.\n", + (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF, + VIVI_VERSION & 0xFF); return ret; } diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c index a1f76ee032e..cbecb3cbbba 100644 --- a/drivers/media/video/vp27smpx.c +++ b/drivers/media/video/vp27smpx.c @@ -166,4 +166,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .remove = vp27smpx_remove, .id_table = vp27smpx_id, }; - diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index fc50299caa3..7be47a25585 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -327,4 +327,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .remove = wm8739_remove, .id_table = wm8739_id, }; - diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index 506378a508b..c2ab70a04a7 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -42,7 +42,6 @@ MODULE_LICENSE("GPL"); static unsigned short normal_i2c[] = { 0x36 >> 1, I2C_CLIENT_END }; - I2C_CLIENT_INSMOD; @@ -230,4 +229,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .remove = wm8775_remove, .id_table = wm8775_id, }; - diff --git a/drivers/media/video/zoran_card.c b/drivers/media/video/zoran_card.c index 006d48847e2..0929edb2d4f 100644 --- a/drivers/media/video/zoran_card.c +++ b/drivers/media/video/zoran_card.c @@ -59,8 +59,6 @@ #include "zoran_device.h" #include "zoran_procfs.h" -#define I2C_NAME(x) (x)->name - extern const struct zoran_format zoran_formats[]; static int card[BUZ_MAX] = { -1, -1, -1, -1 }; @@ -360,14 +358,6 @@ i2cid_to_modulename (u16 i2c_id) case I2C_DRIVERID_VPX3220: name = "vpx3220"; break; -/* case I2C_DRIVERID_VPX3224: - name = "vpx3224"; - break; - case I2C_DRIVERID_MSE3000: - name = "mse3000"; - break;*/ - default: - break; } return name; @@ -388,8 +378,6 @@ codecid_to_modulename (u16 codecid) case CODEC_TYPE_ZR36016: name = "zr36016"; break; - default: - break; } return name; @@ -430,7 +418,6 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { .type = DC10_old, .name = "DC10(old)", .i2c_decoder = I2C_DRIVERID_VPX3220, - /*.i2c_encoder = I2C_DRIVERID_MSE3000,*/ .video_codec = CODEC_TYPE_ZR36050, .video_vfe = CODEC_TYPE_ZR36016, @@ -809,7 +796,7 @@ clientunreg_unlock_and_return: return res; } -static struct i2c_algo_bit_data zoran_i2c_bit_data_template = { +static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = { .setsda = zoran_i2c_setsda, .setscl = zoran_i2c_setscl, .getsda = zoran_i2c_getsda, @@ -818,24 +805,17 @@ static struct i2c_algo_bit_data zoran_i2c_bit_data_template = { .timeout = 100, }; -static struct i2c_adapter zoran_i2c_adapter_template = { - .name = "zr36057", - .id = I2C_HW_B_ZR36067, - .algo = NULL, - .client_register = zoran_i2c_client_register, - .client_unregister = zoran_i2c_client_unregister, -}; - static int zoran_register_i2c (struct zoran *zr) { memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template, sizeof(struct i2c_algo_bit_data)); zr->i2c_algo.data = zr; - memcpy(&zr->i2c_adapter, &zoran_i2c_adapter_template, - sizeof(struct i2c_adapter)); - strncpy(I2C_NAME(&zr->i2c_adapter), ZR_DEVNAME(zr), - sizeof(I2C_NAME(&zr->i2c_adapter)) - 1); + zr->i2c_adapter.id = I2C_HW_B_ZR36067; + zr->i2c_adapter.client_register = zoran_i2c_client_register; + zr->i2c_adapter.client_unregister = zoran_i2c_client_unregister; + strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), + sizeof(zr->i2c_adapter.name)); i2c_set_adapdata(&zr->i2c_adapter, zr); zr->i2c_adapter.algo_data = &zr->i2c_algo; zr->i2c_adapter.dev.parent = &zr->pci_dev->dev; @@ -1147,7 +1127,7 @@ zr36057_init (struct zoran *zr) goto exit_free; } for (j = 0; j < BUZ_NUM_STAT_COM; j++) { - zr->stat_com[j] = 1; /* mark as unavailable to zr36057 */ + zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ } /* diff --git a/drivers/media/video/zoran_driver.c b/drivers/media/video/zoran_driver.c index 5394d7a5cfe..c0675921fe2 100644 --- a/drivers/media/video/zoran_driver.c +++ b/drivers/media/video/zoran_driver.c @@ -94,7 +94,6 @@ V4L2_CAP_VIDEO_OVERLAY \ ) -#include <asm/byteorder.h> #if defined(CONFIG_VIDEO_V4L1_COMPAT) #define ZFMT(pal, fcc, cs) \ @@ -2795,7 +2794,7 @@ zoran_do_ioctl (struct inode *inode, { struct v4l2_format *fmt = arg; int i, res = 0; - __u32 printformat; + __le32 printformat; dprintk(3, KERN_DEBUG "%s: VIDIOC_S_FMT - type=%d, ", ZR_DEVNAME(zr), fmt->type); @@ -3040,7 +3039,7 @@ zoran_do_ioctl (struct inode *inode, { int i, res = 0; struct v4l2_framebuffer *fb = arg; - __u32 printformat = __cpu_to_le32(fb->fmt.pixelformat); + __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat); dprintk(3, KERN_DEBUG diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index a0e49dc6630..485df2e3613 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -521,7 +521,7 @@ static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv, return 0; } -static int zr364xx_vidioc_enum_fmt_cap(struct file *file, +static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { if (f->index > 0) @@ -537,7 +537,7 @@ static int zr364xx_vidioc_enum_fmt_cap(struct file *file, return 0; } -static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv, +static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct video_device *vdev = video_devdata(file); @@ -564,7 +564,7 @@ static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv, return 0; } -static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv, +static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct video_device *vdev = video_devdata(file); @@ -589,7 +589,7 @@ static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv, return 0; } -static int zr364xx_vidioc_s_fmt_cap(struct file *file, void *priv, +static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { struct video_device *vdev = video_devdata(file); @@ -770,10 +770,10 @@ static struct video_device zr364xx_template = { .minor = -1, .vidioc_querycap = zr364xx_vidioc_querycap, - .vidioc_enum_fmt_cap = zr364xx_vidioc_enum_fmt_cap, - .vidioc_try_fmt_cap = zr364xx_vidioc_try_fmt_cap, - .vidioc_s_fmt_cap = zr364xx_vidioc_s_fmt_cap, - .vidioc_g_fmt_cap = zr364xx_vidioc_g_fmt_cap, + .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = zr364xx_vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = zr364xx_vidioc_g_fmt_vid_cap, .vidioc_enum_input = zr364xx_vidioc_enum_input, .vidioc_g_input = zr364xx_vidioc_g_input, .vidioc_s_input = zr364xx_vidioc_s_input, |