diff options
Diffstat (limited to 'linux/drivers/media/video')
54 files changed, 6622 insertions, 216 deletions
diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 78dfb13ca..1909ef6e3 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -445,7 +445,8 @@ static int dvb_register(struct cx23885_tsport *port) .fname = XC3028L_DEFAULT_FIRMWARE, .max_len = 64, .demod = 5000, - .d2633 = 1 + /* This is true for all demods with v36 firmware? */ + .type = XC2028_D2633, }; fe = dvb_attach(xc2028_attach, diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 87e1b6ced..215a724ce 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1482,15 +1482,26 @@ static const struct cx88_board cx88_boards[] = { .name = "Pinnacle Hybrid PCTV", .tuner_type = TUNER_XC2028, .tuner_addr = 0x61, + .radio_type = TUNER_XC2028, + .radio_addr = 0x61, .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x00001, }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, + .gpio0 = 0x004fb, + .gpio1 = 0x010ef, + .audioroute = 1, }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, + .gpio0 = 0x004fb, + .gpio1 = 0x010ef, + .audioroute = 1, } }, .radio = { .type = CX88_RADIO, @@ -1498,10 +1509,7 @@ static const struct cx88_board cx88_boards[] = { .gpio1 = 0x010ff, .gpio2 = 0x0ff, }, -#if 0 - /* needs some more GPIO work */ .mpeg = CX88_MPEG_DVB, -#endif }, [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = { .name = "Winfast TV2000 XP Global", @@ -1665,6 +1673,36 @@ static const struct cx88_board cx88_boards[] = { .gpio2 = 0x0cfb, }, }, + [CX88_BOARD_PROLINK_PV_GLOBAL_XTREME] = { + .name = "Prolink Pixelview Global Extreme", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x04fb, + .gpio1 = 0x04080, + .gpio2 = 0x0cf7, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x04fb, + .gpio1 = 0x04080, + .gpio2 = 0x0cfb, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x04fb, + .gpio1 = 0x04080, + .gpio2 = 0x0cfb, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x04ff, + .gpio1 = 0x04080, + .gpio2 = 0x0cf7, + }, + }, /* Both radio, analog and ATSC work with this board. However, for analog to work, s5h1409 gate should be open, otherwise, tuner-xc3028 won't be detected. @@ -2163,6 +2201,10 @@ static const struct cx88_subid cx88_subids[] = { .subdevice = 0x4935, .card = CX88_BOARD_PROLINK_PV_8000GT, }, { + .subvendor = 0x1554, + .subdevice = 0x4976, + .card = CX88_BOARD_PROLINK_PV_GLOBAL_XTREME, + }, { .subvendor = 0x17de, .subdevice = 0x08c1, .card = CX88_BOARD_KWORLD_ATSC_120, @@ -2466,6 +2508,7 @@ static int cx88_xc2028_tuner_callback(struct cx88_core *core, return cx88_xc3028_geniatech_tuner_callback(core, command, arg); case CX88_BOARD_PROLINK_PV_8000GT: + case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: return cx88_pv_8000gt_callback(core, command, arg); case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: @@ -2619,6 +2662,7 @@ static void cx88_card_setup_pre_i2c(struct cx88_core *core) udelay(1000); break; + case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: case CX88_BOARD_PROLINK_PV_8000GT: cx_write(MO_GP2_IO, 0xcf7); mdelay(50); @@ -2666,11 +2710,16 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: ctl->demod = XC3028_FE_OREN538; break; + case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: case CX88_BOARD_PROLINK_PV_8000GT: /* - * This board uses non-MTS firmware + * Those boards uses non-MTS firmware */ break; + case CX88_BOARD_PINNACLE_HYBRID_PCTV: + ctl->demod = XC3028_FE_ZARLINK456; + ctl->mts = 1; + break; default: ctl->demod = XC3028_FE_OREN538; ctl->mts = 1; diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index a65ea03a0..da1ad2b6b 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -455,6 +455,12 @@ static struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = { .if_khz = 5380, }; +static struct zl10353_config cx88_pinnacle_hybrid_pctv = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .if2 = 45600, +}; + static struct zl10353_config cx88_geniatech_x8000_mt = { .demod_address = (0x1e >> 1), .no_tuner = 1, @@ -897,10 +903,13 @@ static int dvb_register(struct cx8802_dev *dev) break; case CX88_BOARD_PINNACLE_HYBRID_PCTV: dev->dvb.frontend = dvb_attach(zl10353_attach, - &cx88_geniatech_x8000_mt, + &cx88_pinnacle_hybrid_pctv, &core->i2c_adap); - if (attach_xc3028(0x61, dev) < 0) - goto frontend_detach; + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.i2c_gate_ctrl = NULL; + if (attach_xc3028(0x61, dev) < 0) + goto frontend_detach; + } break; case CX88_BOARD_GENIATECH_X8000_MT: dev->ts_gen_cntrl = 0x00; diff --git a/linux/drivers/media/video/cx88/cx88-input.c b/linux/drivers/media/video/cx88/cx88-input.c index 3cdc473de..bcecb5bbd 100644 --- a/linux/drivers/media/video/cx88/cx88-input.c +++ b/linux/drivers/media/video/cx88/cx88-input.c @@ -274,6 +274,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->polling = 1; /* ms */ break; case CX88_BOARD_PROLINK_PV_8000GT: + case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: ir_codes = ir_codes_pixelview_new; ir->gpio_addr = MO_GP1_IO; ir->mask_keycode = 0x3f; diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c index 3a7a9ed17..c4fdb6da2 100644 --- a/linux/drivers/media/video/cx88/cx88-video.c +++ b/linux/drivers/media/video/cx88/cx88-video.c @@ -1065,6 +1065,8 @@ static int video_open(struct inode *inode, struct file *file) } unlock_kernel(); + atomic_inc(&core->users); + return 0; } @@ -1152,7 +1154,8 @@ static int video_release(struct inode *inode, struct file *file) file->private_data = NULL; kfree(fh); - cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); + if(atomic_dec_and_test(&dev->core->users)) + cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); return 0; } diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index 6f5b2963b..e42ce0016 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -228,6 +228,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_OMICOM_SS4_PCI 71 #define CX88_BOARD_TBS_8920 72 #define CX88_BOARD_TEVII_S420 73 +#define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, @@ -352,6 +353,7 @@ struct cx88_core { struct mutex lock; /* various v4l controls */ u32 freq; + atomic_t users; /* cx88-video needs to access cx8802 for hybrid tuner pll access. */ struct cx8802_dev *dvbdev; diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index 14ef4da24..d6856b331 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -127,10 +127,10 @@ static void em28xx_audio_isocirq(struct urb *urb) if (oldptr + length >= runtime->buffer_size) { unsigned int cnt = - runtime->buffer_size - oldptr - 1; + runtime->buffer_size - oldptr; memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride); - memcpy(runtime->dma_area, cp + cnt, + memcpy(runtime->dma_area, cp + cnt * stride, length * stride - cnt * stride); } else { memcpy(runtime->dma_area + oldptr * stride, cp, diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 38874e024..4f078da31 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -578,7 +578,7 @@ struct em28xx_board em28xx_boards[] = { }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, + .amux = 3, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, @@ -931,22 +931,23 @@ struct em28xx_board em28xx_boards[] = { }, [EM2880_BOARD_KWORLD_DVB_310U] = { .name = "KWorld DVB-T 310U", - .valid = EM28XX_BOARD_NOT_VALIDATED, .vchannels = 3, .tuner_type = TUNER_XC2028, + .has_dvb = 1, + .mts_firmware = 1, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, - .amux = 0, + .amux = EM28XX_AMUX_VIDEO, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, - .amux = 1, - }, { + .amux = EM28XX_AMUX_AC97_LINE_IN, + }, { /* S-video has not been tested yet */ .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, - .amux = 1, + .amux = EM28XX_AMUX_AC97_LINE_IN, } }, }, [EM2881_BOARD_DNT_DA2_HYBRID] = { @@ -1118,6 +1119,10 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2880_BOARD_KWORLD_DVB_305U }, { USB_DEVICE(0xeb1a, 0xe310), .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD }, +#if 0 + { USB_DEVICE(0xeb1a, 0xe310), + .driver_info = EM2880_BOARD_KWORLD_DVB_310U }, +#endif { USB_DEVICE(0xeb1a, 0xa316), .driver_info = EM2883_BOARD_KWORLD_HYBRID_A316 }, { USB_DEVICE(0xeb1a, 0xe320), @@ -1184,9 +1189,6 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII }, { USB_DEVICE(0x093b, 0xa005), .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U }, -#if 0 - EM2880_BOARD_KWORLD_DVB_310U }, -#endif { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -1264,6 +1266,7 @@ static struct em28xx_reg_seq em2882_terratec_hybrid_xs_digital[] = { static struct em28xx_hash_table em28xx_eeprom_hash [] = { /* P/N: SA 60002070465 Tuner: TVF7533-MF */ {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, + {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -1749,6 +1752,20 @@ void em28xx_card_setup(struct em28xx *dev) break; case EM2820_BOARD_UNKNOWN: case EM2800_BOARD_UNKNOWN: + /* + * The K-WORLD DVB-T 310U is detected as an MSI Digivox AD. + * + * This occurs because they share identical USB vendor and + * product IDs. + * + * What we do here is look up the EEPROM hash of the K-WORLD + * and if it is found then we decide that we do not have + * a DIGIVOX and reset the device to the K-WORLD instead. + * + * This solution is only valid if they do not share eeprom + * hash identities which has not been determined as yet. + */ + case EM2880_BOARD_MSI_DIGIVOX_AD: if (!em28xx_hint_board(dev)) em28xx_set_model(dev); break; diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index 03c212e5a..b98749b8d 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -452,6 +452,15 @@ static int dvb_init(struct em28xx *dev) goto out_free; } break; + case EM2880_BOARD_KWORLD_DVB_310U: + dvb->frontend = dvb_attach(zl10353_attach, + &em28xx_zl10353_with_xc3028, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" " isn't supported yet\n", diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig index 42b90742b..aa7f3eb7e 100644 --- a/linux/drivers/media/video/gspca/Kconfig +++ b/linux/drivers/media/video/gspca/Kconfig @@ -1,13 +1,203 @@ -config USB_GSPCA - tristate "USB GSPCA driver" +menuconfig USB_GSPCA + tristate "GSPCA based webcams" depends on VIDEO_V4L2 + default m ---help--- - Say Y here if you want support for various USB webcams. + Say Y here if you want to enable selecting webcams based + on the GSPCA framework. - See <file:Documentation/video4linux/gspca.txt> for more info. + 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. + 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. + To compile this driver as modules, choose M here: the + modules will be called gspca_main. + + +if USB_GSPCA && VIDEO_V4L2 + +source "drivers/media/video/gspca/m5602/Kconfig" + +config USB_GSPCA_CONEX + tristate "Conexant Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the Conexant chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_conex. + +config USB_GSPCA_ETOMS + tristate "Etoms USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the Etoms chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_etoms. + +config USB_GSPCA_MARS + tristate "Mars USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the Mars chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_mars. + +config USB_GSPCA_OV519 + tristate "OV519 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the OV519 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_ov519. + +config USB_GSPCA_PAC207 + tristate "Pixart PAC207 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the PAC207 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_pac207. + +config USB_GSPCA_PAC7311 + tristate "Pixart PAC7311 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the PAC7311 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_pac7311. + +config USB_GSPCA_SONIXB + tristate "SN9C102 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SONIXB chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_sonixb. + +config USB_GSPCA_SONIXJ + tristate "SONIX JPEG USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SONIXJ chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_sonixj + +config USB_GSPCA_SPCA500 + tristate "SPCA500 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA500 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca500. + +config USB_GSPCA_SPCA501 + tristate "SPCA501 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA501 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca501. + +config USB_GSPCA_SPCA505 + tristate "SPCA505 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA505 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca505. + +config USB_GSPCA_SPCA506 + tristate "SPCA506 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA506 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca506. + +config USB_GSPCA_SPCA508 + tristate "SPCA508 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA508 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca508. + +config USB_GSPCA_SPCA561 + tristate "SPCA561 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SPCA561 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca561. + +config USB_GSPCA_STK014 + tristate "Syntek DV4000 (STK014) USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the STK014 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_stk014. + +config USB_GSPCA_SPCA5XX + tristate "SPCA5xx USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the Sunplus + SPCA504(abc) SPCA533 SPCA536 chips. + + To compile this driver as a module, choose M here: the + module will be called gspca_spca5xx. + +config USB_GSPCA_T613 + tristate "T613 (JPEG Compliance) USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the T613 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_t613. + +config USB_GSPCA_TV8531 + tristate "TV8532 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the TV8531 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_tv8532. + +config USB_GSPCA_VC032X + tristate "VC032X USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the VC032X chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_vc032x. + +config USB_GSPCA_ZC3XX + tristate "VC3xx USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the ZC3XX chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_zc3xx. + +endif diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile index e68a89652..b87322f4a 100644 --- a/linux/drivers/media/video/gspca/Makefile +++ b/linux/drivers/media/video/gspca/Makefile @@ -1,29 +1,46 @@ -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 +obj-$(CONFIG_USB_GSPCA) += gspca_main.o +obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o +obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o +obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o +obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o +obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o +obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o +obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o +obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o +obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o +obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o +obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o +obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o +obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o +obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o +obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o +obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o +obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o +obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o +obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o +obj-$(CONFIG_USB_GSPCA_ZC3XX) += 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 + +obj-$(CONFIG_USB_M5602) += m5602/ -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/linux/drivers/media/video/gspca/conex.c b/linux/drivers/media/video/gspca/conex.c index 4d9f4cc25..a9d51ba7c 100644 --- a/linux/drivers/media/video/gspca/conex.c +++ b/linux/drivers/media/video/gspca/conex.c @@ -837,12 +837,13 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { cx11646_initsize(gspca_dev); cx11646_fw(gspca_dev); cx_sensor(gspca_dev); cx11646_jpeg(gspca_dev); + return 0; } static void sd_stop0(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/etoms.c b/linux/drivers/media/video/gspca/etoms.c index 86f3932c2..28869ef26 100644 --- a/linux/drivers/media/video/gspca/etoms.c +++ b/linux/drivers/media/video/gspca/etoms.c @@ -691,7 +691,7 @@ static int sd_init(struct gspca_dev *gspca_dev) } /* -- start the camera -- */ -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -704,6 +704,7 @@ static void sd_start(struct gspca_dev *gspca_dev) reg_w_val(gspca_dev, ET_RESET_ALL, 0x08); et_video(gspca_dev, 1); /* video on */ + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/finepix.c b/linux/drivers/media/video/gspca/finepix.c new file mode 100644 index 000000000..65d3cbfe6 --- /dev/null +++ b/linux/drivers/media/video/gspca/finepix.c @@ -0,0 +1,466 @@ +/* + * Fujifilm Finepix subdriver + * + * Copyright (C) 2008 Frank Zago + * + * 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 "finepix" + +#include "gspca.h" + +MODULE_AUTHOR("Frank Zago <frank@zago.net>"); +MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver"); +MODULE_LICENSE("GPL"); + +/* Default timeout, in ms */ +#define FPIX_TIMEOUT (HZ / 10) + +/* Maximum transfer size to use. The windows driver reads by chunks of + * 0x2000 bytes, so do the same. Note: reading more seems to work + * too. */ +#define FPIX_MAX_TRANSFER 0x2000 + +/* Structure to hold all of our device specific stuff */ +struct usb_fpix { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + /* + * USB stuff + */ + struct usb_ctrlrequest ctrlreq; + struct urb *control_urb; + struct timer_list bulk_timer; + + enum { + FPIX_NOP, /* inactive, else streaming */ + FPIX_RESET, /* must reset */ + FPIX_REQ_FRAME, /* requesting a frame */ + FPIX_READ_FRAME, /* reading frame */ + } state; + + /* + * Driver stuff + */ + struct delayed_work wqe; + struct completion can_close; + int streaming; +}; + +/* Delay after which claim the next frame. If the delay is too small, + * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms + * will fail every 4 or 5 frames, but 30ms is perfect. */ +#define NEXT_FRAME_DELAY (((HZ * 30) + 999) / 1000) + +#define dev_new_state(new_state) { \ + PDEBUG(D_STREAM, "new state from %d to %d at %s:%d", \ + dev->state, new_state, __func__, __LINE__); \ + dev->state = new_state; \ +} + +/* These cameras only support 320x200. */ +static struct v4l2_pix_format fpix_mode[1] = { + { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0} +}; + +/* Reads part of a frame */ +static void read_frame_part(struct usb_fpix *dev) +{ + int ret; + + PDEBUG(D_STREAM, "read_frame_part"); + + /* Reads part of a frame */ + ret = usb_submit_urb(dev->gspca_dev.urb[0], GFP_ATOMIC); + if (ret) { + dev_new_state(FPIX_RESET); + schedule_delayed_work(&dev->wqe, 1); + PDEBUG(D_STREAM, "usb_submit_urb failed with %d", + ret); + } else { + /* Sometimes we never get a callback, so use a timer. + * Is this masking a bug somewhere else? */ + dev->bulk_timer.expires = jiffies + msecs_to_jiffies(150); + add_timer(&dev->bulk_timer); + } +} + +/* Callback for URBs. */ +static void urb_callback(struct urb *urb) +{ + struct gspca_dev *gspca_dev = urb->context; + struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; + + PDEBUG(D_PACK, + "enter urb_callback - status=%d, length=%d", + urb->status, urb->actual_length); + + if (dev->state == FPIX_READ_FRAME) + del_timer(&dev->bulk_timer); + + if (urb->status != 0) { + /* We kill a stuck urb every 50 frames on average, so don't + * display a log message for that. */ + if (urb->status != -ECONNRESET) + PDEBUG(D_STREAM, "bad URB status %d", urb->status); + dev_new_state(FPIX_RESET); + schedule_delayed_work(&dev->wqe, 1); + } + + switch (dev->state) { + case FPIX_REQ_FRAME: + dev_new_state(FPIX_READ_FRAME); + read_frame_part(dev); + break; + + case FPIX_READ_FRAME: { + unsigned char *data = urb->transfer_buffer; + struct gspca_frame *frame; + + frame = gspca_get_i_frame(&dev->gspca_dev); + if (frame == NULL) + gspca_dev->last_packet_type = DISCARD_PACKET; + if (urb->actual_length < FPIX_MAX_TRANSFER || + (data[urb->actual_length-2] == 0xff && + data[urb->actual_length-1] == 0xd9)) { + + /* If the result is less than what was asked + * for, then it's the end of the + * frame. Sometime the jpeg is not complete, + * but there's nothing we can do. We also end + * here if the the jpeg ends right at the end + * of the frame. */ + if (frame) + gspca_frame_add(gspca_dev, LAST_PACKET, + frame, + data, urb->actual_length); + dev_new_state(FPIX_REQ_FRAME); + schedule_delayed_work(&dev->wqe, NEXT_FRAME_DELAY); + } else { + + /* got a partial image */ + if (frame) + gspca_frame_add(gspca_dev, + gspca_dev->last_packet_type + == LAST_PACKET + ? FIRST_PACKET : INTER_PACKET, + frame, + data, urb->actual_length); + read_frame_part(dev); + } + break; + } + + case FPIX_NOP: + case FPIX_RESET: + PDEBUG(D_STREAM, "invalid state %d", dev->state); + break; + } +} + +/* Request a new frame */ +static void request_frame(struct usb_fpix *dev) +{ + int ret; + struct gspca_dev *gspca_dev = &dev->gspca_dev; + + /* Setup command packet */ + memset(gspca_dev->usb_buf, 0, 12); + gspca_dev->usb_buf[0] = 0xd3; + gspca_dev->usb_buf[7] = 0x01; + + /* Request a frame */ + dev->ctrlreq.bRequestType = + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + dev->ctrlreq.bRequest = USB_REQ_GET_STATUS; + dev->ctrlreq.wValue = 0; + dev->ctrlreq.wIndex = 0; + dev->ctrlreq.wLength = cpu_to_le16(12); + + usb_fill_control_urb(dev->control_urb, + gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + (unsigned char *) &dev->ctrlreq, + gspca_dev->usb_buf, + 12, urb_callback, gspca_dev); + + ret = usb_submit_urb(dev->control_urb, GFP_ATOMIC); + if (ret) { + dev_new_state(FPIX_RESET); + schedule_delayed_work(&dev->wqe, 1); + PDEBUG(D_STREAM, "usb_submit_urb failed with %d", ret); + } +} + +/*--------------------------------------------------------------------------*/ + +/* State machine. */ +static void fpix_sm(struct work_struct *work) +{ + struct usb_fpix *dev = container_of(work, struct usb_fpix, wqe.work); + + PDEBUG(D_STREAM, "fpix_sm state %d", dev->state); + + /* verify that the device wasn't unplugged */ + if (!dev->gspca_dev.present) { + PDEBUG(D_STREAM, "device is gone"); + dev_new_state(FPIX_NOP); + complete(&dev->can_close); + return; + } + + if (!dev->streaming) { + PDEBUG(D_STREAM, "stopping state machine"); + dev_new_state(FPIX_NOP); + complete(&dev->can_close); + return; + } + + switch (dev->state) { + case FPIX_RESET: + dev_new_state(FPIX_REQ_FRAME); + schedule_delayed_work(&dev->wqe, HZ / 10); + break; + + case FPIX_REQ_FRAME: + /* get an image */ + request_frame(dev); + break; + + case FPIX_NOP: + case FPIX_READ_FRAME: + PDEBUG(D_STREAM, "invalid state %d", dev->state); + break; + } +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam = &gspca_dev->cam; + + cam->cam_mode = fpix_mode; + cam->nmodes = 1; + cam->epaddr = 0x01; /* todo: correct for all cams? */ + cam->bulk_size = FPIX_MAX_TRANSFER; + +/* gspca_dev->nbalt = 1; * use bulk transfer */ + return 0; +} + +/* Stop streaming and free the ressources allocated by sd_start. */ +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; + + dev->streaming = 0; + + /* Stop the state machine */ + if (dev->state != FPIX_NOP) + wait_for_completion(&dev->can_close); + + usb_free_urb(dev->control_urb); + dev->control_urb = NULL; +} + +/* Kill an URB that hasn't completed. */ +static void timeout_kill(unsigned long data) +{ + struct urb *urb = (struct urb *) data; + + usb_unlink_urb(urb); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; + + INIT_DELAYED_WORK(&dev->wqe, fpix_sm); + + init_timer(&dev->bulk_timer); + dev->bulk_timer.function = timeout_kill; + + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; + int ret; + int size_ret; + + /* Reset bulk in endpoint */ + usb_clear_halt(gspca_dev->dev, gspca_dev->cam.epaddr); + + /* Init the device */ + memset(gspca_dev->usb_buf, 0, 12); + gspca_dev->usb_buf[0] = 0xc6; + gspca_dev->usb_buf[8] = 0x20; + + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_GET_STATUS, + USB_DIR_OUT | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf, + 12, FPIX_TIMEOUT); + + if (ret != 12) { + PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret); + ret = -EIO; + goto error; + } + + /* Read the result of the command. Ignore the result, for it + * varies with the device. */ + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, + gspca_dev->cam.epaddr), + gspca_dev->usb_buf, FPIX_MAX_TRANSFER, &size_ret, + FPIX_TIMEOUT); + if (ret != 0) { + PDEBUG(D_STREAM, "usb_bulk_msg failed (%d)", ret); + ret = -EIO; + goto error; + } + + /* Request a frame, but don't read it */ + memset(gspca_dev->usb_buf, 0, 12); + gspca_dev->usb_buf[0] = 0xd3; + gspca_dev->usb_buf[7] = 0x01; + + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_GET_STATUS, + USB_DIR_OUT | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf, + 12, FPIX_TIMEOUT); + if (ret != 12) { + PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret); + ret = -EIO; + goto error; + } + + /* Again, reset bulk in endpoint */ + usb_clear_halt(gspca_dev->dev, gspca_dev->cam.epaddr); + + /* Allocate a control URB */ + dev->control_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->control_urb) { + PDEBUG(D_STREAM, "No free urbs available"); + ret = -EIO; + goto error; + } + + /* Various initializations. */ + init_completion(&dev->can_close); + dev->bulk_timer.data = (unsigned long)dev->gspca_dev.urb[0]; + dev->gspca_dev.urb[0]->complete = urb_callback; + dev->streaming = 1; + + /* Schedule a frame request. */ + dev_new_state(FPIX_REQ_FRAME); + schedule_delayed_work(&dev->wqe, 1); + + return 0; + +error: + /* Free the ressources */ + sd_stopN(gspca_dev); + return ret; +} + +/* Table of supported USB devices */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x04cb, 0x0104)}, + {USB_DEVICE(0x04cb, 0x0109)}, + {USB_DEVICE(0x04cb, 0x010b)}, + {USB_DEVICE(0x04cb, 0x010f)}, + {USB_DEVICE(0x04cb, 0x0111)}, + {USB_DEVICE(0x04cb, 0x0113)}, + {USB_DEVICE(0x04cb, 0x0115)}, + {USB_DEVICE(0x04cb, 0x0117)}, + {USB_DEVICE(0x04cb, 0x0119)}, + {USB_DEVICE(0x04cb, 0x011b)}, + {USB_DEVICE(0x04cb, 0x011d)}, + {USB_DEVICE(0x04cb, 0x0121)}, + {USB_DEVICE(0x04cb, 0x0123)}, + {USB_DEVICE(0x04cb, 0x0125)}, + {USB_DEVICE(0x04cb, 0x0127)}, + {USB_DEVICE(0x04cb, 0x0129)}, + {USB_DEVICE(0x04cb, 0x012b)}, + {USB_DEVICE(0x04cb, 0x012d)}, + {USB_DEVICE(0x04cb, 0x012f)}, + {USB_DEVICE(0x04cb, 0x0131)}, + {USB_DEVICE(0x04cb, 0x013b)}, + {USB_DEVICE(0x04cb, 0x013d)}, + {USB_DEVICE(0x04cb, 0x013f)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, +}; + +/* -- 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 usb_fpix), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "registered"); + 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/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 8c98c7a05..12e3a4360 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -29,6 +29,7 @@ #include <linux/string.h> #include <linux/pagemap.h> #include <linux/io.h> +#include <linux/kref.h> #include <asm/page.h> #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) #include <asm/uaccess.h> @@ -106,6 +107,22 @@ static struct vm_operations_struct gspca_vm_ops = { .close = gspca_vm_close, }; +/* get the current input frame buffer */ +struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev) +{ + struct gspca_frame *frame; + int i; + + i = gspca_dev->fr_i; + i = gspca_dev->fr_queue[i]; + frame = &gspca_dev->frame[i]; + if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) + != V4L2_BUF_FLAG_QUEUED) + return NULL; + return frame; +} +EXPORT_SYMBOL(gspca_get_i_frame); + /* * fill a video frame from an URB and resubmit */ @@ -114,7 +131,7 @@ static void fill_frame(struct gspca_dev *gspca_dev, { struct gspca_frame *frame; __u8 *data; /* address of data in the iso message */ - int i, j, len, st; + int i, len, st; cam_pkt_op pkt_scan; if (urb->status != 0) { @@ -128,11 +145,8 @@ static void fill_frame(struct gspca_dev *gspca_dev, 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) { + frame = gspca_get_i_frame(gspca_dev); + if (!frame) { gspca_dev->last_packet_type = DISCARD_PACKET; break; } @@ -195,7 +209,6 @@ static void bulk_irq(struct urb *urb { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; struct gspca_frame *frame; - int j; PDEBUG(D_PACK, "bulk irq"); if (!gspca_dev->streaming) @@ -209,11 +222,8 @@ static void bulk_irq(struct urb *urb } /* 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) { + frame = gspca_get_i_frame(gspca_dev); + if (!frame) { gspca_dev->last_packet_type = DISCARD_PACKET; } else { PDEBUG(D_PACK, "packet l:%d", urb->actual_length); @@ -237,7 +247,7 @@ static void bulk_irq(struct urb *urb * On LAST_PACKET, a new frame is returned. */ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, - int packet_type, + enum gspca_packet_type packet_type, struct gspca_frame *frame, const __u8 *data, int len) @@ -279,7 +289,7 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, } gspca_dev->last_packet_type = packet_type; - /* if last packet, wake the application and advance in the queue */ + /* if last packet, wake up 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; @@ -317,7 +327,6 @@ 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; @@ -421,7 +430,7 @@ static void destroy_urbs(struct gspca_dev *gspca_dev) } /* - * search an input transfer endpoint in an alternate setting + * look for an input transfer endpoint in an alternate setting */ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, __u8 epaddr, @@ -445,7 +454,7 @@ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, } /* - * search an input (isoc or bulk) endpoint + * look for an input (isoc or bulk) endpoint * * The endpoint is defined by the subdriver. * Use only the first isoc (some Zoran - 0x0572:0x0001 - have two such ep). @@ -461,25 +470,34 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); ep = NULL; i = gspca_dev->alt; /* previous alt setting */ + + /* try isoc */ while (--i > 0) { /* alt 0 is unusable */ ep = alt_xfer(&intf->altsetting[i], gspca_dev->cam.epaddr, - gspca_dev->bulk - ? USB_ENDPOINT_XFER_BULK - : USB_ENDPOINT_XFER_ISOC); + USB_ENDPOINT_XFER_ISOC); if (ep) break; } + + /* if no isoc, try bulk */ if (ep == NULL) { - err("no transfer endpoint found"); - return NULL; + ep = alt_xfer(&intf->altsetting[0], + gspca_dev->cam.epaddr, + USB_ENDPOINT_XFER_BULK); + if (ep == NULL) { + err("no transfer endpoint found"); + return NULL; + } } PDEBUG(D_STREAM, "use alt %d ep 0x%02x", i, ep->desc.bEndpointAddress); - ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); - if (ret < 0) { - err("set interface err %d", ret); - return NULL; + if (i > 0) { + 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; @@ -497,9 +515,10 @@ static int create_urbs(struct gspca_dev *gspca_dev, /* 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)); - if (!gspca_dev->bulk) { + if (gspca_dev->alt != 0) { /* isoc */ + + /* 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; @@ -508,9 +527,11 @@ static int create_urbs(struct gspca_dev *gspca_dev, "isoc %d pkts size %d = bsize:%d", npkt, psize, bsize); nurbs = DEF_NURBS; - } else { + } else { /* bulk */ npkt = 0; - bsize = psize; + bsize = gspca_dev->cam. bulk_size; + if (bsize == 0) + bsize = psize; PDEBUG(D_STREAM, "bulk bsize:%d", bsize); nurbs = 1; } @@ -542,7 +563,7 @@ static int create_urbs(struct gspca_dev *gspca_dev, urb->pipe = usb_rcvisocpipe(gspca_dev->dev, ep->desc.bEndpointAddress); urb->transfer_flags = URB_ISO_ASAP - | URB_NO_TRANSFER_DMA_MAP; + | URB_NO_TRANSFER_DMA_MAP; urb->interval = ep->desc.bInterval; urb->complete = isoc_irq; urb->number_of_packets = npkt; @@ -553,6 +574,7 @@ static int create_urbs(struct gspca_dev *gspca_dev, } else { /* bulk */ urb->pipe = usb_rcvbulkpipe(gspca_dev->dev, ep->desc.bEndpointAddress), + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; urb->complete = bulk_irq; } } @@ -585,12 +607,16 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) goto out; /* start the cam */ - gspca_dev->sd_desc->start(gspca_dev); + ret = gspca_dev->sd_desc->start(gspca_dev); + if (ret < 0) { + destroy_urbs(gspca_dev); + goto out; + } gspca_dev->streaming = 1; atomic_set(&gspca_dev->nevent, 0); - /* start the bulk transfer is done by the subdriver */ - if (gspca_dev->bulk) + /* bulk transfers are started by the subdriver */ + if (gspca_dev->alt == 0) break; /* submit the URBs */ @@ -624,7 +650,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) return ret; } -/* Note both the queue and the usb lock should be hold when calling this */ +/* Note: both the queue and the usb locks should be held when calling this */ static void gspca_stream_off(struct gspca_dev *gspca_dev) { gspca_dev->streaming = 0; @@ -830,6 +856,16 @@ out: return ret; } +static void gspca_delete(struct kref *kref) +{ + struct gspca_dev *gspca_dev = container_of(kref, struct gspca_dev, kref); + + PDEBUG(D_STREAM, "device deleted"); + + kfree(gspca_dev->usb_buf); + kfree(gspca_dev); +} + static int dev_open(struct inode *inode, struct file *file) { struct gspca_dev *gspca_dev; @@ -849,13 +885,19 @@ static int dev_open(struct inode *inode, struct file *file) goto out; } gspca_dev->users++; + + /* one more user */ + kref_get(&gspca_dev->kref); + file->private_data = gspca_dev; #ifdef GSPCA_DEBUG /* activate the v4l2 debug */ if (gspca_debug & D_V4L2) - gspca_dev->vdev.debug |= 3; + gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL + | V4L2_DEBUG_IOCTL_ARG; else - gspca_dev->vdev.debug &= ~3; + gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL + | V4L2_DEBUG_IOCTL_ARG); #endif ret = 0; out: @@ -889,7 +931,11 @@ static int dev_close(struct inode *inode, struct file *file) } file->private_data = NULL; mutex_unlock(&gspca_dev->queue_lock); + PDEBUG(D_STREAM, "close done"); + + kref_put(&gspca_dev->kref, gspca_delete); + return 0; } @@ -900,7 +946,6 @@ static int vidioc_querycap(struct file *file, void *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); */ if (gspca_dev->dev->product != NULL) { strncpy(cap->card, gspca_dev->dev->product, sizeof cap->card); @@ -1534,7 +1579,6 @@ static int vidioc_qbuf(struct file *file, void *priv, } 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; @@ -1681,7 +1725,7 @@ static ssize_t dev_read(struct file *file, char __user *data, } /* if the process slept for more than 1 second, - * get anewer frame */ + * get a newer frame */ frame = &gspca_dev->frame[v4l2_buf.index]; if (--n < 0) break; /* avoid infinite loop */ @@ -1799,21 +1843,21 @@ int gspca_dev_probe(struct usb_interface *intf, if (dev_size < sizeof *gspca_dev) dev_size = sizeof *gspca_dev; gspca_dev = kzalloc(dev_size, GFP_KERNEL); - if (gspca_dev == NULL) { + if (!gspca_dev) { err("couldn't kzalloc gspca struct"); - return -EIO; + return -ENOMEM; } + kref_init(&gspca_dev->kref); gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); if (!gspca_dev->usb_buf) { err("out of memory"); - ret = -EIO; + ret = -ENOMEM; goto out; } 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 and initialize the USB device */ @@ -1852,8 +1896,7 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_PROBE, "probe ok"); return 0; out: - kfree(gspca_dev->usb_buf); - kfree(gspca_dev); + kref_put(&gspca_dev->kref, gspca_delete); return ret; } EXPORT_SYMBOL(gspca_dev_probe); @@ -1868,8 +1911,8 @@ void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); - if (!gspca_dev) - return; + usb_set_intfdata(intf, NULL); + gspca_dev->present = 0; mutex_lock(&gspca_dev->queue_lock); mutex_lock(&gspca_dev->usb_lock); @@ -1877,16 +1920,12 @@ void gspca_disconnect(struct usb_interface *intf) 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->usb_buf); - kfree(gspca_dev); + + kref_put(&gspca_dev->kref, gspca_delete); + PDEBUG(D_PROBE, "disconnect complete"); } EXPORT_SYMBOL(gspca_disconnect); diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 78a35a88c..4779dd0b0 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -56,6 +56,7 @@ extern int gspca_debug; /* device information - set at probe time */ struct cam { + int bulk_size; /* buffer size when image transfer by bulk */ struct v4l2_pix_format *cam_mode; /* size nmodes */ char nmodes; __u8 epaddr; @@ -93,7 +94,7 @@ struct sd_desc { /* mandatory operations */ cam_cf_op config; /* called on probe */ cam_op init; /* called on probe and resume */ - cam_v_op start; /* called on stream on */ + cam_op start; /* called on stream on */ cam_pkt_op pkt_scan; /* optional operations */ cam_v_op stopN; /* called on stream off - main alt */ @@ -105,10 +106,12 @@ struct sd_desc { }; /* 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 +enum gspca_packet_type { + DISCARD_PACKET, + FIRST_PACKET, + INTER_PACKET, + LAST_PACKET +}; struct gspca_frame { __u8 *data; /* frame buffer */ @@ -121,6 +124,7 @@ struct gspca_dev { struct video_device vdev; /* !! must be the first item */ struct file_operations fops; struct usb_device *dev; + struct kref kref; struct file *capt_file; /* file doing video capture */ struct cam cam; /* device information */ @@ -143,7 +147,6 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ - __u8 bulk; /* image transfer by isoc (0) or bulk (1) */ __u8 curr_mode; /* current camera mode */ __u32 pixfmt; /* current mode parameters */ __u16 width; @@ -174,10 +177,11 @@ int gspca_dev_probe(struct usb_interface *intf, struct module *module); void gspca_disconnect(struct usb_interface *intf); struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, - int packet_type, + enum gspca_packet_type packet_type, struct gspca_frame *frame, const __u8 *data, int len); +struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev); #ifdef CONFIG_PM int gspca_suspend(struct usb_interface *intf, pm_message_t message); int gspca_resume(struct usb_interface *intf); diff --git a/linux/drivers/media/video/gspca/m5602/Kconfig b/linux/drivers/media/video/gspca/m5602/Kconfig new file mode 100644 index 000000000..5a69016ed --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/Kconfig @@ -0,0 +1,11 @@ +config USB_M5602 + tristate "ALi USB m5602 Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the + ALi m5602 connected to various image sensors. + + See <file:Documentation/video4linux/m5602.txt> for more info. + + To compile this driver as a module, choose M here: the + module will be called gspca_m5602. diff --git a/linux/drivers/media/video/gspca/m5602/Makefile b/linux/drivers/media/video/gspca/m5602/Makefile new file mode 100644 index 000000000..672fb6bb5 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_USB_M5602) += gspca_m5602.o + +gspca_m5602-objs := m5602_core.o \ + m5602_ov9650.o \ + m5602_mt9m111.o \ + m5602_po1030.o \ + m5602_s5k83a.o \ + m5602_s5k4aa.o diff --git a/linux/drivers/media/video/gspca/m5602/m5602_bridge.h b/linux/drivers/media/video/gspca/m5602/m5602_bridge.h new file mode 100644 index 000000000..6b2441e84 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_bridge.h @@ -0,0 +1,182 @@ +/* + * USB Driver for ALi m5602 based webcams + * + * Copyright (C) 2008 Erik Andren + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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. + * + */ + +#ifndef M5602_BRIDGE_H_ +#define M5602_BRIDGE_H_ + +#include "gspca.h" + +#define MODULE_NAME "ALi m5602" + +/*****************************************************************************/ + +#undef PDEBUG +#undef info +#undef err + +#define err(format, arg...) printk(KERN_ERR KBUILD_MODNAME ": " \ + format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO KBUILD_MODNAME ": " \ + format "\n" , ## arg) + +/* Debug parameters */ +#define DBG_INIT 0x1 +#define DBG_PROBE 0x2 +#define DBG_V4L2 0x4 +#define DBG_TRACE 0x8 +#define DBG_DATA 0x10 +#define DBG_V4L2_CID 0x20 +#define DBG_GSPCA 0x40 + +#define PDEBUG(level, fmt, args...) \ + do { \ + if (debug & level) \ + info("[%s:%d] " fmt, __func__, __LINE__ , \ + ## args); \ + } while (0) + +/*****************************************************************************/ + +#define M5602_XB_SENSOR_TYPE 0x00 +#define M5602_XB_SENSOR_CTRL 0x01 +#define M5602_XB_LINE_OF_FRAME_H 0x02 +#define M5602_XB_LINE_OF_FRAME_L 0x03 +#define M5602_XB_PIX_OF_LINE_H 0x04 +#define M5602_XB_PIX_OF_LINE_L 0x05 +#define M5602_XB_VSYNC_PARA 0x06 +#define M5602_XB_HSYNC_PARA 0x07 +#define M5602_XB_TEST_MODE_1 0x08 +#define M5602_XB_TEST_MODE_2 0x09 +#define M5602_XB_SIG_INI 0x0a +#define M5602_XB_DS_PARA 0x0e +#define M5602_XB_TRIG_PARA 0x0f +#define M5602_XB_CLK_PD 0x10 +#define M5602_XB_MCU_CLK_CTRL 0x12 +#define M5602_XB_MCU_CLK_DIV 0x13 +#define M5602_XB_SEN_CLK_CTRL 0x14 +#define M5602_XB_SEN_CLK_DIV 0x15 +#define M5602_XB_AUD_CLK_CTRL 0x16 +#define M5602_XB_AUD_CLK_DIV 0x17 +#define M5602_XB_DEVCTR1 0x41 +#define M5602_XB_EPSETR0 0x42 +#define M5602_XB_EPAFCTR 0x47 +#define M5602_XB_EPBFCTR 0x49 +#define M5602_XB_EPEFCTR 0x4f +#define M5602_XB_TEST_REG 0x53 +#define M5602_XB_ALT2SIZE 0x54 +#define M5602_XB_ALT3SIZE 0x55 +#define M5602_XB_OBSFRAME 0x56 +#define M5602_XB_PWR_CTL 0x59 +#define M5602_XB_ADC_CTRL 0x60 +#define M5602_XB_ADC_DATA 0x61 +#define M5602_XB_MISC_CTRL 0x62 +#define M5602_XB_SNAPSHOT 0x63 +#define M5602_XB_SCRATCH_1 0x64 +#define M5602_XB_SCRATCH_2 0x65 +#define M5602_XB_SCRATCH_3 0x66 +#define M5602_XB_SCRATCH_4 0x67 +#define M5602_XB_I2C_CTRL 0x68 +#define M5602_XB_I2C_CLK_DIV 0x69 +#define M5602_XB_I2C_DEV_ADDR 0x6a +#define M5602_XB_I2C_REG_ADDR 0x6b +#define M5602_XB_I2C_DATA 0x6c +#define M5602_XB_I2C_STATUS 0x6d +#define M5602_XB_GPIO_DAT_H 0x70 +#define M5602_XB_GPIO_DAT_L 0x71 +#define M5602_XB_GPIO_DIR_H 0x72 +#define M5602_XB_GPIO_DIR_L 0x73 +#define M5602_XB_GPIO_EN_H 0x74 +#define M5602_XB_GPIO_EN_L 0x75 +#define M5602_XB_GPIO_DAT 0x76 +#define M5602_XB_GPIO_DIR 0x77 +#define M5602_XB_MISC_CTL 0x70 + +#define I2C_BUSY 0x80 + +/*****************************************************************************/ + +/* Driver info */ +#define DRIVER_AUTHOR "ALi m5602 Linux Driver Project" +#define DRIVER_DESC "ALi m5602 webcam driver" + +#define M5602_ISOC_ENDPOINT_ADDR 0x81 +#define M5602_INTR_ENDPOINT_ADDR 0x82 + +#define M5602_MAX_FRAMES 32 +#define M5602_URBS 2 +#define M5602_ISOC_PACKETS 14 + +#define M5602_URB_TIMEOUT msecs_to_jiffies(2 * M5602_ISOC_PACKETS) +#define M5602_URB_MSG_TIMEOUT 5000 +#define M5602_FRAME_TIMEOUT 2 + +/*****************************************************************************/ + +/* A skeleton used for sending messages to the m5602 bridge */ +static const unsigned char bridge_urb_skeleton[] = { + 0x13, 0x00, 0x81, 0x00 +}; + +/* A skeleton used for sending messages to the sensor */ +static const unsigned char sensor_urb_skeleton[] = { + 0x23, M5602_XB_GPIO_EN_H, 0x81, 0x06, + 0x23, M5602_XB_MISC_CTRL, 0x81, 0x80, + 0x13, M5602_XB_I2C_DEV_ADDR, 0x81, 0x00, + 0x13, M5602_XB_I2C_REG_ADDR, 0x81, 0x00, + 0x13, M5602_XB_I2C_DATA, 0x81, 0x00, + 0x13, M5602_XB_I2C_CTRL, 0x81, 0x11 +}; + +/* m5602 device descriptor, currently it just wraps the m5602_camera struct */ +struct sd { + struct gspca_dev gspca_dev; + + /* The name of the m5602 camera */ + char *name; + + /* A pointer to the currently connected sensor */ + struct m5602_sensor *sensor; + + struct sd_desc *desc; + + /* The current frame's id, used to detect frame boundaries */ + u8 frame_id; + + /* The current frame count */ + u32 frame_count; +}; + +int m5602_read_bridge( + struct sd *sd, u8 address, u8 *i2c_data); + +int m5602_write_bridge( + struct sd *sd, u8 address, u8 i2c_data); + +int m5602_configure(struct gspca_dev *gspca_dev, + const struct usb_device_id *id); + +int m5602_init(struct gspca_dev *gspca_dev); + +void m5602_start_transfer(struct gspca_dev *gspca_dev); + +void m5602_stop_transfer(struct gspca_dev *gspca_dev); + +void m5602_urb_complete(struct gspca_dev *gspca_dev, struct gspca_frame *frame, + __u8 *data, int len); + +#endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_core.c b/linux/drivers/media/video/gspca/m5602/m5602_core.c new file mode 100644 index 000000000..30efe7d4b --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_core.c @@ -0,0 +1,308 @@ +/* + * USB Driver for ALi m5602 based webcams + * + * Copyright (C) 2008 Erik Andren + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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. + * + */ + +#include "m5602_ov9650.h" +#include "m5602_mt9m111.h" +#include "m5602_po1030.h" +#include "m5602_s5k83a.h" +#include "m5602_s5k4aa.h" + +/* Kernel module parameters */ +int force_sensor; +int dump_bridge; +int dump_sensor; +unsigned int debug; + +static const __devinitdata struct usb_device_id m5602_table[] = { + {USB_DEVICE(0x0402, 0x5602)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, m5602_table); + +/* sub-driver description, the ctrl and nctrl is filled at probe time */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = m5602_configure, + .init = m5602_init, + .start = m5602_start_transfer, + .stopN = m5602_stop_transfer, + .pkt_scan = m5602_urb_complete +}; + +/* Reads a byte from the m5602 */ +int m5602_read_bridge(struct sd *sd, u8 address, u8 *i2c_data) +{ + int err; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + err = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0x04, 0xc0, 0x14, + 0x8100 + address, buf, + 1, M5602_URB_MSG_TIMEOUT); + *i2c_data = buf[0]; + + PDEBUG(DBG_TRACE, "Reading bridge register 0x%x containing 0x%x", + address, *i2c_data); + + /* usb_control_msg(...) returns the number of bytes sent upon success, + mask that and return zero upon success instead*/ + return (err < 0) ? err : 0; +} + +/* Writes a byte to to the m5602 */ +int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data) +{ + int err; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + PDEBUG(DBG_TRACE, "Writing bridge register 0x%x with 0x%x", + address, i2c_data); + + memcpy(buf, bridge_urb_skeleton, + sizeof(bridge_urb_skeleton)); + buf[1] = address; + buf[3] = i2c_data; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x19, + 0x0000, buf, + 4, M5602_URB_MSG_TIMEOUT); + + /* usb_control_msg(...) returns the number of bytes sent upon success, + mask that and return zero upon success instead */ + return (err < 0) ? err : 0; +} + +/* Dump all the registers of the m5602 bridge, + unfortunately this breaks the camera until it's power cycled */ +static void m5602_dump_bridge(struct sd *sd) +{ + int i; + for (i = 0; i < 0x80; i++) { + unsigned char val = 0; + m5602_read_bridge(sd, i, &val); + info("ALi m5602 address 0x%x contains 0x%x", i, val); + } + info("Warning: The camera probably won't work until it's power cycled"); +} + +int m5602_probe_sensor(struct sd *sd) +{ + /* Try the po1030 */ + sd->sensor = &po1030; + if (!sd->sensor->probe(sd)) + return 0; + + /* Try the mt9m111 sensor */ + sd->sensor = &mt9m111; + if (!sd->sensor->probe(sd)) + return 0; + + /* Try the s5k4aa */ + sd->sensor = &s5k4aa; + if (!sd->sensor->probe(sd)) + return 0; + + /* Try the ov9650 */ + sd->sensor = &ov9650; + if (!sd->sensor->probe(sd)) + return 0; + + /* Try the s5k83a */ + sd->sensor = &s5k83a; + if (!sd->sensor->probe(sd)) + return 0; + + /* More sensor probe function goes here */ + info("Failed to find a sensor"); + sd->sensor = NULL; + return -ENODEV; +} + +int m5602_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err; + + PDEBUG(DBG_TRACE, "Initializing ALi m5602 webcam"); + /* Run the init sequence */ + err = sd->sensor->init(sd); + + return err; +} + +void m5602_start_transfer(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + /* Send start command to the camera */ + const u8 buffer[4] = {0x13, 0xf9, 0x0f, 0x01}; + memcpy(buf, buffer, sizeof(buffer)); + usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), + 0x04, 0x40, 0x19, 0x0000, buf, + 4, M5602_URB_MSG_TIMEOUT); + + PDEBUG(DBG_V4L2, "Transfer started"); +} + +void m5602_urb_complete(struct gspca_dev *gspca_dev, struct gspca_frame *frame, + __u8 *data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (len < 6) { + PDEBUG(DBG_DATA, "Packet is less than 6 bytes"); + return; + } + + /* Frame delimiter: ff xx xx xx ff ff */ + if (data[0] == 0xff && data[4] == 0xff && data[5] == 0xff && + data[2] != sd->frame_id) { + PDEBUG(DBG_DATA, "Frame delimiter detected"); + sd->frame_id = data[2]; + + /* Remove the extra fluff appended on each header */ + data += 6; + len -= 6; + + /* Complete the last frame (if any) */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, 0); + sd->frame_count++; + + /* Create a new frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + + PDEBUG(DBG_V4L2, "Starting new frame %d", + sd->frame_count); + + } else { + int cur_frame_len = frame->data_end - frame->data; + + /* Remove urb header */ + data += 4; + len -= 4; + + if (cur_frame_len + len <= frame->v4l2_buf.length) { + PDEBUG(DBG_DATA, "Continuing frame %d copying %d bytes", + sd->frame_count, len); + + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, len); + } else if (frame->v4l2_buf.length - cur_frame_len > 0) { + /* Add the remaining data up to frame size */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, + frame->v4l2_buf.length - cur_frame_len); + } + } +} + +void m5602_stop_transfer(struct gspca_dev *gspca_dev) +{ + /* Is there are a command to stop a data transfer? */ +} + +/* this function is called at probe time */ +int m5602_configure(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + int err; + + PDEBUG(DBG_GSPCA, "m5602_configure start"); + + cam = &gspca_dev->cam; + cam->epaddr = M5602_ISOC_ENDPOINT_ADDR; + sd->desc = &sd_desc; + + if (dump_bridge) + m5602_dump_bridge(sd); + + /* Probe sensor */ + err = m5602_probe_sensor(sd); + if (err) + goto fail; + + PDEBUG(DBG_GSPCA, "m5602_configure end"); + return 0; + +fail: + PDEBUG(DBG_GSPCA, "m5602_configure failed"); + cam->cam_mode = NULL; + cam->nmodes = 0; + + return err; +} + +static int m5602_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 = m5602_table, + .probe = m5602_probe, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif + .disconnect = gspca_disconnect +}; + +/* -- module insert / remove -- */ +static int __init mod_m5602_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "m5602 module registered"); + return 0; +} +static void __exit mod_m5602_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "m5602 module deregistered"); +} + +module_init(mod_m5602_init); +module_exit(mod_m5602_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "toggles debug on/off"); + +module_param(force_sensor, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(force_sensor, + "force detection of sensor, " + "1 = OV9650, 2 = S5K83A, 3 = S5K4AA, 4 = MT9M111, 5 = PO1030"); + +module_param(dump_bridge, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup"); + +module_param(dump_sensor, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dump_sensor, "Dumps all usb sensor registers " + "at startup providing a sensor is found"); diff --git a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c new file mode 100644 index 000000000..ea2250217 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c @@ -0,0 +1,345 @@ +/* + * Driver for the mt9m111 sensor + * + * Copyright (C) 2008 Erik Andren + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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. + * + */ + +#include "m5602_mt9m111.h" + +int mt9m111_probe(struct sd *sd) +{ + u8 data[2] = {0x00, 0x00}; + int i; + + if (force_sensor) { + if (force_sensor == MT9M111_SENSOR) { + info("Forcing a %s sensor", mt9m111.name); + goto sensor_found; + } + /* If we want to force another sensor, don't try to probe this + * one */ + return -ENODEV; + } + + info("Probing for a mt9m111 sensor"); + + /* Do the preinit */ + for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) { + if (preinit_mt9m111[i][0] == BRIDGE) { + m5602_write_bridge(sd, + preinit_mt9m111[i][1], + preinit_mt9m111[i][2]); + } else { + data[0] = preinit_mt9m111[i][2]; + data[1] = preinit_mt9m111[i][3]; + mt9m111_write_sensor(sd, + preinit_mt9m111[i][1], data, 2); + } + } + + if (mt9m111_read_sensor(sd, MT9M111_SC_CHIPVER, data, 2)) + return -ENODEV; + + if ((data[0] == 0x14) && (data[1] == 0x3a)) { + info("Detected a mt9m111 sensor"); + goto sensor_found; + } + + return -ENODEV; + +sensor_found: + sd->gspca_dev.cam.cam_mode = mt9m111.modes; + sd->gspca_dev.cam.nmodes = mt9m111.nmodes; + sd->desc->ctrls = mt9m111.ctrls; + sd->desc->nctrls = mt9m111.nctrls; + return 0; +} + +int mt9m111_init(struct sd *sd) +{ + int i, err; + + /* Init the sensor */ + for (i = 0; i < ARRAY_SIZE(init_mt9m111); i++) { + u8 data[2]; + + if (init_mt9m111[i][0] == BRIDGE) { + err = m5602_write_bridge(sd, + init_mt9m111[i][1], + init_mt9m111[i][2]); + } else { + data[0] = init_mt9m111[i][2]; + data[1] = init_mt9m111[i][3]; + err = mt9m111_write_sensor(sd, + init_mt9m111[i][1], data, 2); + } + } + + if (dump_sensor) + mt9m111_dump_registers(sd); + + return (err < 0) ? err : 0; +} + +int mt9m111_power_down(struct sd *sd) +{ + return 0; +} + +int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 data[2] = {0x00, 0x00}; + struct sd *sd = (struct sd *) gspca_dev; + + err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, + data, 2); + *val = data[0] & MT9M111_RMB_MIRROR_ROWS; + PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", *val); + + return (err < 0) ? err : 0; +} + +int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 data[2] = {0x00, 0x00}; + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d", val); + + /* Set the correct page map */ + err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); + if (err < 0) + goto out; + + err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + if (err < 0) + goto out; + + data[0] = (data[0] & 0xfe) | val; + err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, + data, 2); +out: + return (err < 0) ? err : 0; +} + +int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 data[2] = {0x00, 0x00}; + struct sd *sd = (struct sd *) gspca_dev; + + err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, + data, 2); + *val = data[0] & MT9M111_RMB_MIRROR_COLS; + PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", *val); + + return (err < 0) ? err : 0; +} + +int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 data[2] = {0x00, 0x00}; + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d", val); + + /* Set the correct page map */ + err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); + if (err < 0) + goto out; + + err = mt9m111_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + if (err < 0) + goto out; + + data[0] = (data[0] & 0xfd) | ((val << 1) & 0x02); + err = mt9m111_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, + data, 2); +out: + return (err < 0) ? err : 0; +} + +int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err, tmp; + u8 data[2] = {0x00, 0x00}; + struct sd *sd = (struct sd *) gspca_dev; + + err = mt9m111_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2); + tmp = ((data[1] << 8) | data[0]); + + *val = ((tmp & (1 << 10)) * 2) | + ((tmp & (1 << 9)) * 2) | + ((tmp & (1 << 8)) * 2) | + (tmp & 0x7f); + + PDEBUG(DBG_V4L2_CID, "Read gain %d", *val); + + return (err < 0) ? err : 0; +} + +int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err, tmp; + u8 data[2] = {0x00, 0x00}; + struct sd *sd = (struct sd *) gspca_dev; + + /* Set the correct page map */ + err = mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); + if (err < 0) + goto out; + + if (val >= INITIAL_MAX_GAIN * 2 * 2 * 2) + return -EINVAL; + + if ((val >= INITIAL_MAX_GAIN * 2 * 2) && + (val < (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2)) + tmp = (1 << 10) | (val << 9) | + (val << 8) | (val / 8); + else if ((val >= INITIAL_MAX_GAIN * 2) && + (val < INITIAL_MAX_GAIN * 2 * 2)) + tmp = (1 << 9) | (1 << 8) | (val / 4); + else if ((val >= INITIAL_MAX_GAIN) && + (val < INITIAL_MAX_GAIN * 2)) + tmp = (1 << 8) | (val / 2); + else + tmp = val; + + data[1] = (tmp & 0xff00) >> 8; + data[0] = (tmp & 0xff); + PDEBUG(DBG_V4L2_CID, "tmp=%d, data[1]=%d, data[0]=%d", tmp, + data[1], data[0]); + + err = mt9m111_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN, + data, 2); +out: + return (err < 0) ? err : 0; +} + +int mt9m111_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) { + int err, i; + + do { + err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); + } while ((*i2c_data & I2C_BUSY) && !err); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, + sd->sensor->i2c_slave_id); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x1a); + if (err < 0) + goto out; + + for (i = 0; i < len && !err; i++) { + err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); + + PDEBUG(DBG_TRACE, "Reading sensor register " + "0x%x contains 0x%x ", address, *i2c_data); + } +out: + return (err < 0) ? err : 0; +} + +int mt9m111_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + u8 *p; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + /* No sensor with a data width larger + than 16 bits has yet been seen, nor with 0 :p*/ + if (len > 2 || !len) + return -EINVAL; + + memcpy(buf, sensor_urb_skeleton, + sizeof(sensor_urb_skeleton)); + + buf[11] = sd->sensor->i2c_slave_id; + buf[15] = address; + + p = buf + 16; + + /* Copy a four byte write sequence for each byte to be written to */ + for (i = 0; i < len; i++) { + memcpy(p, sensor_urb_skeleton + 16, 4); + p[3] = i2c_data[i]; + p += 4; + PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x", + address, i2c_data[i]); + } + + /* Copy the tailer */ + memcpy(p, sensor_urb_skeleton + 20, 4); + + /* Set the total length */ + p[3] = 0x10 + len; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x19, + 0x0000, buf, + 20 + len * 4, M5602_URB_MSG_TIMEOUT); + + return (err < 0) ? err : 0; +} + +void mt9m111_dump_registers(struct sd *sd) +{ + u8 address, value[2] = {0x00, 0x00}; + + info("Dumping the mt9m111 register state"); + + info("Dumping the mt9m111 sensor core registers"); + value[1] = MT9M111_SENSOR_CORE; + mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); + for (address = 0; address < 0xff; address++) { + mt9m111_read_sensor(sd, address, value, 2); + info("register 0x%x contains 0x%x%x", + address, value[0], value[1]); + } + + info("Dumping the mt9m111 color pipeline registers"); + value[1] = MT9M111_COLORPIPE; + mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); + for (address = 0; address < 0xff; address++) { + mt9m111_read_sensor(sd, address, value, 2); + info("register 0x%x contains 0x%x%x", + address, value[0], value[1]); + } + + info("Dumping the mt9m111 camera control registers"); + value[1] = MT9M111_CAMERA_CONTROL; + mt9m111_write_sensor(sd, MT9M111_PAGE_MAP, value, 2); + for (address = 0; address < 0xff; address++) { + mt9m111_read_sensor(sd, address, value, 2); + info("register 0x%x contains 0x%x%x", + address, value[0], value[1]); + } + + info("mt9m111 register state dump complete"); +} diff --git a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h new file mode 100644 index 000000000..f914547b9 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h @@ -0,0 +1,1020 @@ +/* + * Driver for the mt9m111 sensor + * + * Copyright (C) 2008 Erik Andren + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * Some defines taken from the mt9m111 sensor driver + * Copyright (C) 2008, Robert Jarzmik <robert.jarzmik@free.fr> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#ifndef M5602_MT9M111_H_ +#define M5602_MT9M111_H_ + +#include "m5602_sensor.h" + +/*****************************************************************************/ + +#define MT9M111_SC_CHIPVER 0x00 +#define MT9M111_SC_ROWSTART 0x01 +#define MT9M111_SC_COLSTART 0x02 +#define MT9M111_SC_WINDOW_HEIGHT 0x03 +#define MT9M111_SC_WINDOW_WIDTH 0x04 +#define MT9M111_SC_HBLANK_CONTEXT_B 0x05 +#define MT9M111_SC_VBLANK_CONTEXT_B 0x06 +#define MT9M111_SC_HBLANK_CONTEXT_A 0x07 +#define MT9M111_SC_VBLANK_CONTEXT_A 0x08 +#define MT9M111_SC_SHUTTER_WIDTH 0x09 +#define MT9M111_SC_ROW_SPEED 0x0a + +#define MT9M111_SC_EXTRA_DELAY 0x0b +#define MT9M111_SC_SHUTTER_DELAY 0x0c +#define MT9M111_SC_RESET 0x0d +#define MT9M111_SC_R_MODE_CONTEXT_B 0x20 +#define MT9M111_SC_R_MODE_CONTEXT_A 0x21 +#define MT9M111_SC_FLASH_CONTROL 0x23 +#define MT9M111_SC_GREEN_1_GAIN 0x2b +#define MT9M111_SC_BLUE_GAIN 0x2c +#define MT9M111_SC_RED_GAIN 0x2d +#define MT9M111_SC_GREEN_2_GAIN 0x2e +#define MT9M111_SC_GLOBAL_GAIN 0x2f + +#define MT9M111_RMB_MIRROR_ROWS (1 << 0) +#define MT9M111_RMB_MIRROR_COLS (1 << 1) + +#define MT9M111_CONTEXT_CONTROL 0xc8 +#define MT9M111_PAGE_MAP 0xf0 +#define MT9M111_BYTEWISE_ADDRESS 0xf1 + +#define MT9M111_CP_OPERATING_MODE_CTL 0x06 +#define MT9M111_CP_LUMA_OFFSET 0x34 +#define MT9M111_CP_LUMA_CLIP 0x35 +#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A 0x3a +#define MT9M111_CP_LENS_CORRECTION_1 0x3b +#define MT9M111_CP_DEFECT_CORR_CONTEXT_A 0x4c +#define MT9M111_CP_DEFECT_CORR_CONTEXT_B 0x4d +#define MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B 0x9b +#define MT9M111_CP_GLOBAL_CLK_CONTROL 0xb3 + +#define MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18 0x65 +#define MT9M111_CC_AWB_PARAMETER_7 0x28 + +#define MT9M111_SENSOR_CORE 0x00 +#define MT9M111_COLORPIPE 0x01 +#define MT9M111_CAMERA_CONTROL 0x02 + +#define INITIAL_MAX_GAIN 64 +#define DEFAULT_GAIN 283 + +/*****************************************************************************/ + +/* Kernel module parameters */ +extern int force_sensor; +extern int dump_sensor; +extern unsigned int debug; + +int mt9m111_probe(struct sd *sd); +int mt9m111_init(struct sd *sd); +int mt9m111_power_down(struct sd *sd); + +int mt9m111_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + +int mt9m111_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + +void mt9m111_dump_registers(struct sd *sd); + +int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val); + +static struct m5602_sensor mt9m111 = { + .name = "MT9M111", + + .i2c_slave_id = 0xba, + + .probe = mt9m111_probe, + .init = mt9m111_init, + .power_down = mt9m111_power_down, + + .read_sensor = mt9m111_read_sensor, + .write_sensor = mt9m111_write_sensor, + + .nctrls = 3, + .ctrls = { + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = mt9m111_set_vflip, + .get = mt9m111_get_vflip + }, { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = mt9m111_set_hflip, + .get = mt9m111_get_hflip + }, { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0, + .maximum = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2, + .step = 1, + .default_value = DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = mt9m111_set_hflip, + .get = mt9m111_get_hflip + } + }, + + .nmodes = 1, + .modes = { + { + M5602_DEFAULT_FRAME_WIDTH, + M5602_DEFAULT_FRAME_HEIGHT, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT, + .bytesperline = M5602_DEFAULT_FRAME_WIDTH, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + } + } +}; + +static const unsigned char preinit_mt9m111[][4] = +{ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0xff, 0xf7}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00} +}; + +static const unsigned char init_mt9m111[][4] = +{ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0xff, 0xff}, + {SENSOR, MT9M111_SC_RESET, 0xff, 0xff}, + {SENSOR, MT9M111_SC_RESET, 0xff, 0xde}, + {SENSOR, MT9M111_SC_RESET, 0xff, 0xff}, + {SENSOR, MT9M111_SC_RESET, 0xff, 0xf7}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, + + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00}, + + {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0xff, 0xff}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x05}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, + {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10}, + {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01}, + {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, + {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00}, + + {SENSOR, 0xcd, 0x00, 0x0e}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00}, + {SENSOR, 0xd0, 0x00, 0x40}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, + {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, + {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, + {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, + {SENSOR, 0x33, 0x03, 0x49}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, + + {SENSOR, 0x33, 0x03, 0x49}, + {SENSOR, 0x34, 0xc0, 0x19}, + {SENSOR, 0x3f, 0x20, 0x20}, + {SENSOR, 0x40, 0x20, 0x20}, + {SENSOR, 0x5a, 0xc0, 0x0a}, + {SENSOR, 0x70, 0x7b, 0x0a}, + {SENSOR, 0x71, 0xff, 0x00}, + {SENSOR, 0x72, 0x19, 0x0e}, + {SENSOR, 0x73, 0x18, 0x0f}, + {SENSOR, 0x74, 0x57, 0x32}, + {SENSOR, 0x75, 0x56, 0x34}, + {SENSOR, 0x76, 0x73, 0x35}, + {SENSOR, 0x77, 0x30, 0x12}, + {SENSOR, 0x78, 0x79, 0x02}, + {SENSOR, 0x79, 0x75, 0x06}, + {SENSOR, 0x7a, 0x77, 0x0a}, + {SENSOR, 0x7b, 0x78, 0x09}, + {SENSOR, 0x7c, 0x7d, 0x06}, + {SENSOR, 0x7d, 0x31, 0x10}, + {SENSOR, 0x7e, 0x00, 0x7e}, + {SENSOR, 0x80, 0x59, 0x04}, + {SENSOR, 0x81, 0x59, 0x04}, + {SENSOR, 0x82, 0x57, 0x0a}, + {SENSOR, 0x83, 0x58, 0x0b}, + {SENSOR, 0x84, 0x47, 0x0c}, + {SENSOR, 0x85, 0x48, 0x0e}, + {SENSOR, 0x86, 0x5b, 0x02}, + {SENSOR, 0x87, 0x00, 0x5c}, + {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08}, + {SENSOR, 0x60, 0x00, 0x80}, + {SENSOR, 0x61, 0x00, 0x00}, + {SENSOR, 0x62, 0x00, 0x00}, + {SENSOR, 0x63, 0x00, 0x00}, + {SENSOR, 0x64, 0x00, 0x00}, + + {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, + {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18}, + {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04}, + {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08}, + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38}, + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38}, + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03}, + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03}, + {SENSOR, 0x30, 0x04, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4}, + {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x09}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x04}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00}, + {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x05}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, + {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10}, + {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01}, + {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, + {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, + + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00}, + {SENSOR, 0xcd, 0x00, 0x0e}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00}, + {SENSOR, 0xd0, 0x00, 0x40}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, + {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, + {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, + {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, + {SENSOR, 0x33, 0x03, 0x49}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, + + {SENSOR, 0x33, 0x03, 0x49}, + {SENSOR, 0x34, 0xc0, 0x19}, + {SENSOR, 0x3f, 0x20, 0x20}, + {SENSOR, 0x40, 0x20, 0x20}, + {SENSOR, 0x5a, 0xc0, 0x0a}, + {SENSOR, 0x70, 0x7b, 0x0a}, + {SENSOR, 0x71, 0xff, 0x00}, + {SENSOR, 0x72, 0x19, 0x0e}, + {SENSOR, 0x73, 0x18, 0x0f}, + {SENSOR, 0x74, 0x57, 0x32}, + {SENSOR, 0x75, 0x56, 0x34}, + {SENSOR, 0x76, 0x73, 0x35}, + {SENSOR, 0x77, 0x30, 0x12}, + {SENSOR, 0x78, 0x79, 0x02}, + {SENSOR, 0x79, 0x75, 0x06}, + {SENSOR, 0x7a, 0x77, 0x0a}, + {SENSOR, 0x7b, 0x78, 0x09}, + {SENSOR, 0x7c, 0x7d, 0x06}, + {SENSOR, 0x7d, 0x31, 0x10}, + {SENSOR, 0x7e, 0x00, 0x7e}, + {SENSOR, 0x80, 0x59, 0x04}, + {SENSOR, 0x81, 0x59, 0x04}, + {SENSOR, 0x82, 0x57, 0x0a}, + {SENSOR, 0x83, 0x58, 0x0b}, + {SENSOR, 0x84, 0x47, 0x0c}, + {SENSOR, 0x85, 0x48, 0x0e}, + {SENSOR, 0x86, 0x5b, 0x02}, + {SENSOR, 0x87, 0x00, 0x5c}, + {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08}, + {SENSOR, 0x60, 0x00, 0x80}, + {SENSOR, 0x61, 0x00, 0x00}, + {SENSOR, 0x62, 0x00, 0x00}, + {SENSOR, 0x63, 0x00, 0x00}, + {SENSOR, 0x64, 0x00, 0x00}, + + {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, + {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18}, + {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04}, + {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08}, + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38}, + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38}, + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03}, + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03}, + {SENSOR, 0x30, 0x04, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4}, + {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x09}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x04}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, + + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00}, + {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x05}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, + {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10}, + {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01}, + {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, + {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, + + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00}, + {SENSOR, 0xcd, 0x00, 0x0e}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00}, + {SENSOR, 0xd0, 0x00, 0x40}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, + {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, + {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, + {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, + {SENSOR, 0x33, 0x03, 0x49}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, + + {SENSOR, 0x33, 0x03, 0x49}, + {SENSOR, 0x34, 0xc0, 0x19}, + {SENSOR, 0x3f, 0x20, 0x20}, + {SENSOR, 0x40, 0x20, 0x20}, + {SENSOR, 0x5a, 0xc0, 0x0a}, + {SENSOR, 0x70, 0x7b, 0x0a}, + {SENSOR, 0x71, 0xff, 0x00}, + {SENSOR, 0x72, 0x19, 0x0e}, + {SENSOR, 0x73, 0x18, 0x0f}, + {SENSOR, 0x74, 0x57, 0x32}, + {SENSOR, 0x75, 0x56, 0x34}, + {SENSOR, 0x76, 0x73, 0x35}, + {SENSOR, 0x77, 0x30, 0x12}, + {SENSOR, 0x78, 0x79, 0x02}, + {SENSOR, 0x79, 0x75, 0x06}, + {SENSOR, 0x7a, 0x77, 0x0a}, + {SENSOR, 0x7b, 0x78, 0x09}, + {SENSOR, 0x7c, 0x7d, 0x06}, + {SENSOR, 0x7d, 0x31, 0x10}, + {SENSOR, 0x7e, 0x00, 0x7e}, + {SENSOR, 0x80, 0x59, 0x04}, + {SENSOR, 0x81, 0x59, 0x04}, + {SENSOR, 0x82, 0x57, 0x0a}, + {SENSOR, 0x83, 0x58, 0x0b}, + {SENSOR, 0x84, 0x47, 0x0c}, + {SENSOR, 0x85, 0x48, 0x0e}, + {SENSOR, 0x86, 0x5b, 0x02}, + {SENSOR, 0x87, 0x00, 0x5c}, + {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08}, + {SENSOR, 0x60, 0x00, 0x80}, + {SENSOR, 0x61, 0x00, 0x00}, + {SENSOR, 0x62, 0x00, 0x00}, + {SENSOR, 0x63, 0x00, 0x00}, + {SENSOR, 0x64, 0x00, 0x00}, + + {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, + {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18}, + {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04}, + {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08}, + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38}, + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38}, + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03}, + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03}, + {SENSOR, 0x30, 0x04, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4}, + {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x09}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x04}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x05}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + + {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, + {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10}, + {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01}, + {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, + {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, + + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, 0xcd, 0x00, 0x0e}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, 0xd0, 0x00, 0x40}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, + {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, 0x33, 0x03, 0x49}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + + {SENSOR, 0x33, 0x03, 0x49}, + {SENSOR, 0x34, 0xc0, 0x19}, + {SENSOR, 0x3f, 0x20, 0x20}, + {SENSOR, 0x40, 0x20, 0x20}, + {SENSOR, 0x5a, 0xc0, 0x0a}, + {SENSOR, 0x70, 0x7b, 0x0a}, + {SENSOR, 0x71, 0xff, 0x00}, + {SENSOR, 0x72, 0x19, 0x0e}, + {SENSOR, 0x73, 0x18, 0x0f}, + {SENSOR, 0x74, 0x57, 0x32}, + {SENSOR, 0x75, 0x56, 0x34}, + {SENSOR, 0x76, 0x73, 0x35}, + {SENSOR, 0x77, 0x30, 0x12}, + {SENSOR, 0x78, 0x79, 0x02}, + {SENSOR, 0x79, 0x75, 0x06}, + {SENSOR, 0x7a, 0x77, 0x0a}, + {SENSOR, 0x7b, 0x78, 0x09}, + {SENSOR, 0x7c, 0x7d, 0x06}, + {SENSOR, 0x7d, 0x31, 0x10}, + {SENSOR, 0x7e, 0x00, 0x7e}, + {SENSOR, 0x80, 0x59, 0x04}, + {SENSOR, 0x81, 0x59, 0x04}, + {SENSOR, 0x82, 0x57, 0x0a}, + {SENSOR, 0x83, 0x58, 0x0b}, + {SENSOR, 0x84, 0x47, 0x0c}, + {SENSOR, 0x85, 0x48, 0x0e}, + {SENSOR, 0x86, 0x5b, 0x02}, + {SENSOR, 0x87, 0x00, 0x5c}, + {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08}, + {SENSOR, 0x60, 0x00, 0x80}, + {SENSOR, 0x61, 0x00, 0x00}, + {SENSOR, 0x62, 0x00, 0x00}, + {SENSOR, 0x63, 0x00, 0x00}, + {SENSOR, 0x64, 0x00, 0x00}, + {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, + {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, + {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, + {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x0f}, + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, + {SENSOR, 0x30, 0x04, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe3, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90}, + {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xe6}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x09}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x04}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x05}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, + {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10}, + {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01}, + {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, + {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, + {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, + + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, 0xcd, 0x00, 0x0e}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, 0xd0, 0x00, 0x40}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, + {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + {SENSOR, 0x33, 0x03, 0x49}, + {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, + {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, + {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, + + {SENSOR, 0x33, 0x03, 0x49}, + {SENSOR, 0x34, 0xc0, 0x19}, + {SENSOR, 0x3f, 0x20, 0x20}, + {SENSOR, 0x40, 0x20, 0x20}, + {SENSOR, 0x5a, 0xc0, 0x0a}, + {SENSOR, 0x70, 0x7b, 0x0a}, + {SENSOR, 0x71, 0xff, 0x00}, + {SENSOR, 0x72, 0x19, 0x0e}, + {SENSOR, 0x73, 0x18, 0x0f}, + {SENSOR, 0x74, 0x57, 0x32}, + {SENSOR, 0x75, 0x56, 0x34}, + {SENSOR, 0x76, 0x73, 0x35}, + {SENSOR, 0x77, 0x30, 0x12}, + {SENSOR, 0x78, 0x79, 0x02}, + {SENSOR, 0x79, 0x75, 0x06}, + {SENSOR, 0x7a, 0x77, 0x0a}, + {SENSOR, 0x7b, 0x78, 0x09}, + {SENSOR, 0x7c, 0x7d, 0x06}, + {SENSOR, 0x7d, 0x31, 0x10}, + {SENSOR, 0x7e, 0x00, 0x7e}, + {SENSOR, 0x80, 0x59, 0x04}, + {SENSOR, 0x81, 0x59, 0x04}, + {SENSOR, 0x82, 0x57, 0x0a}, + {SENSOR, 0x83, 0x58, 0x0b}, + {SENSOR, 0x84, 0x47, 0x0c}, + {SENSOR, 0x85, 0x48, 0x0e}, + {SENSOR, 0x86, 0x5b, 0x02}, + {SENSOR, 0x87, 0x00, 0x5c}, + {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08}, + {SENSOR, 0x60, 0x00, 0x80}, + {SENSOR, 0x61, 0x00, 0x00}, + {SENSOR, 0x62, 0x00, 0x00}, + {SENSOR, 0x63, 0x00, 0x00}, + {SENSOR, 0x64, 0x00, 0x00}, + + {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, + {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, + {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, + {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x0f}, + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, + {SENSOR, 0x30, 0x04, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + + {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, + /* Set number of blank rows chosen to 400 */ + {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90}, + /* Set the global gain to 283 (of 512) */ + {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x03, 0x63} +}; + +#endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c new file mode 100644 index 000000000..31c589625 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c @@ -0,0 +1,546 @@ +/* + * Driver for the ov9650 sensor + * + * Copyright (C) 2008 Erik Andren + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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. + * + */ + +#include "m5602_ov9650.h" + +int ov9650_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + + /* The ov9650 registers have a max depth of one byte */ + if (len > 1 || !len) + return -EINVAL; + + do { + err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); + } while ((*i2c_data & I2C_BUSY) && !err); + + m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, + ov9650.i2c_slave_id); + m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); + m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len); + m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08); + + for (i = 0; i < len; i++) { + err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); + + PDEBUG(DBG_TRACE, "Reading sensor register " + "0x%x containing 0x%x ", address, *i2c_data); + } + return (err < 0) ? err : 0; +} + +int ov9650_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + u8 *p; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + /* The ov9650 only supports one byte writes */ + if (len > 1 || !len) + return -EINVAL; + + memcpy(buf, sensor_urb_skeleton, + sizeof(sensor_urb_skeleton)); + + buf[11] = sd->sensor->i2c_slave_id; + buf[15] = address; + + /* Special case larger sensor writes */ + p = buf + 16; + + /* Copy a four byte write sequence for each byte to be written to */ + for (i = 0; i < len; i++) { + memcpy(p, sensor_urb_skeleton + 16, 4); + p[3] = i2c_data[i]; + p += 4; + PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x", + address, i2c_data[i]); + } + + /* Copy the tailer */ + memcpy(p, sensor_urb_skeleton + 20, 4); + + /* Set the total length */ + p[3] = 0x10 + len; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x19, + 0x0000, buf, + 20 + len * 4, M5602_URB_MSG_TIMEOUT); + + return (err < 0) ? err : 0; +} + +int ov9650_probe(struct sd *sd) +{ + u8 prod_id = 0, ver_id = 0, i; + + if (force_sensor) { + if (force_sensor == OV9650_SENSOR) { + info("Forcing an %s sensor", ov9650.name); + goto sensor_found; + } + /* If we want to force another sensor, + don't try to probe this one */ + return -ENODEV; + } + + info("Probing for an ov9650 sensor"); + + /* Run the pre-init to actually probe the unit */ + for (i = 0; i < ARRAY_SIZE(preinit_ov9650); i++) { + u8 data = preinit_ov9650[i][2]; + if (preinit_ov9650[i][0] == SENSOR) + ov9650_write_sensor(sd, + preinit_ov9650[i][1], &data, 1); + else + m5602_write_bridge(sd, preinit_ov9650[i][1], data); + } + + if (ov9650_read_sensor(sd, OV9650_PID, &prod_id, 1)) + return -ENODEV; + + if (ov9650_read_sensor(sd, OV9650_VER, &ver_id, 1)) + return -ENODEV; + + if ((prod_id == 0x96) && (ver_id == 0x52)) { + info("Detected an ov9650 sensor"); + goto sensor_found; + } + + return -ENODEV; + +sensor_found: + sd->gspca_dev.cam.cam_mode = ov9650.modes; + sd->gspca_dev.cam.nmodes = ov9650.nmodes; + sd->desc->ctrls = ov9650.ctrls; + sd->desc->nctrls = ov9650.nctrls; + return 0; +} + +int ov9650_init(struct sd *sd) +{ + int i, err = 0; + u8 data; + + if (dump_sensor) + ov9650_dump_registers(sd); + + for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) { + data = init_ov9650[i][2]; + if (init_ov9650[i][0] == SENSOR) + err = ov9650_write_sensor(sd, init_ov9650[i][1], + &data, 1); + else + err = m5602_write_bridge(sd, init_ov9650[i][1], data); + } + + if (!err && dmi_check_system(ov9650_flip_dmi_table)) { + info("vflip quirk active"); + data = 0x30; + err = ov9650_write_sensor(sd, OV9650_MVFP, &data, 1); + } + + return (err < 0) ? err : 0; +} + +int ov9650_power_down(struct sd *sd) +{ + int i; + for (i = 0; i < ARRAY_SIZE(power_down_ov9650); i++) { + u8 data = power_down_ov9650[i][2]; + if (power_down_ov9650[i][0] == SENSOR) + ov9650_write_sensor(sd, + power_down_ov9650[i][1], &data, 1); + else + m5602_write_bridge(sd, power_down_ov9650[i][1], data); + } + + return 0; +} + +int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 i2c_data; + int err; + + err = ov9650_read_sensor(sd, OV9650_COM1, &i2c_data, 1); + if (err < 0) + goto out; + *val = i2c_data & 0x03; + + err = ov9650_read_sensor(sd, OV9650_AECH, &i2c_data, 1); + if (err < 0) + goto out; + *val |= (i2c_data << 2); + + err = ov9650_read_sensor(sd, OV9650_AECHM, &i2c_data, 1); + if (err < 0) + goto out; + *val |= (i2c_data & 0x3f) << 10; + + PDEBUG(DBG_V4L2_CID, "Read exposure %d", *val); +out: + return (err < 0) ? err : 0; +} + +int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 i2c_data; + int err; + + PDEBUG(DBG_V4L2_CID, "Set exposure to %d", + val & 0xffff); + + /* The 6 MSBs */ + i2c_data = (val >> 10) & 0x3f; + err = ov9650_write_sensor(sd, OV9650_AECHM, + &i2c_data, 1); + if (err < 0) + goto out; + + /* The 8 middle bits */ + i2c_data = (val >> 2) & 0xff; + err = ov9650_write_sensor(sd, OV9650_AECH, + &i2c_data, 1); + if (err < 0) + goto out; + + /* The 2 LSBs */ + i2c_data = val & 0x03; + err = ov9650_write_sensor(sd, OV9650_COM1, &i2c_data, 1); + +out: + return (err < 0) ? err : 0; +} + +int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1); + *val = (i2c_data & 0x03) << 8; + + err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1); + *val |= i2c_data; + PDEBUG(DBG_V4L2_CID, "Read gain %d", *val); + return (err < 0) ? err : 0; +} + +int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + /* The 2 MSB */ + /* Read the OV9650_VREF register first to avoid + corrupting the VREF high and low bits */ + ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1); + /* Mask away all uninteresting bits */ + i2c_data = ((val & 0x0300) >> 2) | + (i2c_data & 0x3F); + err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1); + + /* The 8 LSBs */ + i2c_data = val & 0xff; + err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); + return (err < 0) ? err : 0; +} + +int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = ov9650_read_sensor(sd, OV9650_RED, &i2c_data, 1); + *val = i2c_data; + + PDEBUG(DBG_V4L2_CID, "Read red gain %d", *val); + + return (err < 0) ? err : 0; +} + +int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(DBG_V4L2_CID, "Set red gain to %d", + val & 0xff); + + i2c_data = val & 0xff; + err = ov9650_write_sensor(sd, OV9650_RED, &i2c_data, 1); + + return (err < 0) ? err : 0; +} + +int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = ov9650_read_sensor(sd, OV9650_BLUE, &i2c_data, 1); + *val = i2c_data; + + PDEBUG(DBG_V4L2_CID, "Read blue gain %d", *val); + + return (err < 0) ? err : 0; +} + +int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(DBG_V4L2_CID, "Set blue gain to %d", + val & 0xff); + + i2c_data = val & 0xff; + err = ov9650_write_sensor(sd, OV9650_BLUE, &i2c_data, 1); + + return (err < 0) ? err : 0; +} + +int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); + if (dmi_check_system(ov9650_flip_dmi_table)) + *val = ((i2c_data & OV9650_HFLIP) >> 5) ? 0 : 1; + else + *val = (i2c_data & OV9650_HFLIP) >> 5; + PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", *val); + + return (err < 0) ? err : 0; +} + +int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d", val); + err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); + if (err < 0) + goto out; + + if (dmi_check_system(ov9650_flip_dmi_table)) + i2c_data = ((i2c_data & 0xdf) | + (((val ? 0 : 1) & 0x01) << 5)); + else + i2c_data = ((i2c_data & 0xdf) | + ((val & 0x01) << 5)); + + err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); +out: + return (err < 0) ? err : 0; +} + +int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); + if (dmi_check_system(ov9650_flip_dmi_table)) + *val = ((i2c_data & 0x10) >> 4) ? 0 : 1; + else + *val = (i2c_data & 0x10) >> 4; + PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", *val); + + return (err < 0) ? err : 0; +} + +int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d", val); + err = ov9650_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); + if (err < 0) + goto out; + + if (dmi_check_system(ov9650_flip_dmi_table)) + i2c_data = ((i2c_data & 0xef) | + (((val ? 0 : 1) & 0x01) << 4)); + else + i2c_data = ((i2c_data & 0xef) | + ((val & 0x01) << 4)); + + err = ov9650_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); +out: + return (err < 0) ? err : 0; +} + +int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1); + if (err < 0) + goto out; + *val = (i2c_data & 0x03) << 8; + + err = ov9650_read_sensor(sd, OV9650_GAIN, &i2c_data, 1); + *val |= i2c_data; + PDEBUG(DBG_V4L2_CID, "Read gain %d", *val); +out: + return (err < 0) ? err : 0; +} + +int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(DBG_V4L2_CID, "Set gain to %d", val & 0x3ff); + + /* Read the OV9650_VREF register first to avoid + corrupting the VREF high and low bits */ + err = ov9650_read_sensor(sd, OV9650_VREF, &i2c_data, 1); + if (err < 0) + goto out; + + /* Mask away all uninteresting bits */ + i2c_data = ((val & 0x0300) >> 2) | (i2c_data & 0x3F); + err = ov9650_write_sensor(sd, OV9650_VREF, &i2c_data, 1); + if (err < 0) + goto out; + + /* The 8 LSBs */ + i2c_data = val & 0xff; + err = ov9650_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); + +out: + return (err < 0) ? err : 0; +} + +int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1); + *val = (i2c_data & OV9650_AWB_EN) >> 1; + PDEBUG(DBG_V4L2_CID, "Read auto white balance %d", *val); + + return (err < 0) ? err : 0; +} + +int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(DBG_V4L2_CID, "Set auto white balance to %d", val); + err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1); + if (err < 0) + goto out; + + i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); + err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1); +out: + return (err < 0) ? err : 0; +} + +int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1); + *val = (i2c_data & OV9650_AGC_EN) >> 2; + PDEBUG(DBG_V4L2_CID, "Read auto gain control %d", *val); + + return (err < 0) ? err : 0; +} + +int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(DBG_V4L2_CID, "Set auto gain control to %d", val); + err = ov9650_read_sensor(sd, OV9650_COM8, &i2c_data, 1); + if (err < 0) + goto out; + + i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); + err = ov9650_write_sensor(sd, OV9650_COM8, &i2c_data, 1); +out: + return (err < 0) ? err : 0; +} + +void ov9650_dump_registers(struct sd *sd) +{ + int address; + info("Dumping the ov9650 register state"); + for (address = 0; address < 0xa9; address++) { + u8 value; + ov9650_read_sensor(sd, address, &value, 1); + info("register 0x%x contains 0x%x", + address, value); + } + + info("ov9650 register state dump complete"); + + info("Probing for which registers that are read/write"); + for (address = 0; address < 0xff; address++) { + u8 old_value, ctrl_value; + u8 test_value[2] = {0xff, 0xff}; + + ov9650_read_sensor(sd, address, &old_value, 1); + ov9650_write_sensor(sd, address, test_value, 1); + ov9650_read_sensor(sd, address, &ctrl_value, 1); + + if (ctrl_value == test_value[0]) + info("register 0x%x is writeable", address); + else + info("register 0x%x is read only", address); + + /* Restore original value */ + ov9650_write_sensor(sd, address, &old_value, 1); + } +} diff --git a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h new file mode 100644 index 000000000..70429b411 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h @@ -0,0 +1,501 @@ +/* + * Driver for the ov9650 sensor + * + * Copyright (C) 2008 Erik Andren + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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. + * + */ + +#ifndef M5602_OV9650_H_ +#define M5602_OV9650_H_ + +#include <linux/dmi.h> + +#include "m5602_sensor.h" + +/*****************************************************************************/ + +#define OV9650_GAIN 0x00 +#define OV9650_BLUE 0x01 +#define OV9650_RED 0x02 +#define OV9650_VREF 0x03 +#define OV9650_COM1 0x04 +#define OV9650_BAVE 0x05 +#define OV9650_GEAVE 0x06 +#define OV9650_RSVD7 0x07 +#define OV9650_PID 0x0a +#define OV9650_VER 0x0b +#define OV9650_COM3 0x0c +#define OV9650_COM5 0x0e +#define OV9650_COM6 0x0f +#define OV9650_AECH 0x10 +#define OV9650_CLKRC 0x11 +#define OV9650_COM7 0x12 +#define OV9650_COM8 0x13 +#define OV9650_COM9 0x14 +#define OV9650_COM10 0x15 +#define OV9650_RSVD16 0x16 +#define OV9650_HSTART 0x17 +#define OV9650_HSTOP 0x18 +#define OV9650_VSTRT 0x19 +#define OV9650_VSTOP 0x1a +#define OV9650_PSHFT 0x1b +#define OV9650_MVFP 0x1e +#define OV9650_AEW 0x24 +#define OV9650_AEB 0x25 +#define OV9650_VPT 0x26 +#define OV9650_BBIAS 0x27 +#define OV9650_GbBIAS 0x28 +#define OV9650_Gr_COM 0x29 +#define OV9650_RBIAS 0x2c +#define OV9650_HREF 0x32 +#define OV9650_CHLF 0x33 +#define OV9650_ARBLM 0x34 +#define OV9650_RSVD35 0x35 +#define OV9650_RSVD36 0x36 +#define OV9650_ADC 0x37 +#define OV9650_ACOM38 0x38 +#define OV9650_OFON 0x39 +#define OV9650_TSLB 0x3a +#define OV9650_COM12 0x3c +#define OV9650_COM13 0x3d +#define OV9650_COM15 0x40 +#define OV9650_COM16 0x41 +#define OV9650_LCC1 0x62 +#define OV9650_LCC2 0x63 +#define OV9650_LCC3 0x64 +#define OV9650_LCC4 0x65 +#define OV9650_LCC5 0x66 +#define OV9650_HV 0x69 +#define OV9650_DBLV 0x6b +#define OV9650_COM21 0x8b +#define OV9650_COM22 0x8c +#define OV9650_COM24 0x8e +#define OV9650_DBLC1 0x8f +#define OV9650_RSVD94 0x94 +#define OV9650_RSVD95 0x95 +#define OV9650_RSVD96 0x96 +#define OV9650_LCCFB 0x9d +#define OV9650_LCCFR 0x9e +#define OV9650_AECHM 0xa1 +#define OV9650_COM26 0xa5 +#define OV9650_ACOMA8 0xa8 +#define OV9650_ACOMA9 0xa9 + +#define OV9650_REGISTER_RESET (1 << 7) +#define OV9650_VGA_SELECT (1 << 6) +#define OV9650_RGB_SELECT (1 << 2) +#define OV9650_RAW_RGB_SELECT (1 << 0) + +#define OV9650_FAST_AGC_AEC (1 << 7) +#define OV9650_AEC_UNLIM_STEP_SIZE (1 << 6) +#define OV9650_BANDING (1 << 5) +#define OV9650_AGC_EN (1 << 2) +#define OV9650_AWB_EN (1 << 1) +#define OV9650_AEC_EN (1 << 0) + +#define OV9650_VARIOPIXEL (1 << 2) +#define OV9650_SYSTEM_CLK_SEL (1 << 7) +#define OV9650_SLAM_MODE (1 << 4) + +#define OV9650_VFLIP (1 << 4) +#define OV9650_HFLIP (1 << 5) + +#define GAIN_DEFAULT 0x14 +#define RED_GAIN_DEFAULT 0x70 +#define BLUE_GAIN_DEFAULT 0x20 +#define EXPOSURE_DEFAULT 0x5003 + +/*****************************************************************************/ + +/* Kernel module parameters */ +extern int force_sensor; +extern int dump_sensor; +extern unsigned int debug; + +int ov9650_probe(struct sd *sd); +int ov9650_init(struct sd *sd); +int ov9650_power_down(struct sd *sd); + +int ov9650_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); +int ov9650_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + +void ov9650_dump_registers(struct sd *sd); + +int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val); +int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); +int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); +int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); +int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); +int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); +int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val); +int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val); +int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val); +int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val); +int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); + +static struct m5602_sensor ov9650 = { + .name = "OV9650", + .i2c_slave_id = 0x60, + .probe = ov9650_probe, + .init = ov9650_init, + .power_down = ov9650_power_down, + .read_sensor = ov9650_read_sensor, + .write_sensor = ov9650_write_sensor, + + .nctrls = 8, + .ctrls = { + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0xffff, + .step = 0x1, + .default_value = EXPOSURE_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_exposure, + .get = ov9650_get_exposure + }, { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0x3ff, + .step = 0x1, + .default_value = GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_gain, + .get = ov9650_get_gain + }, { + { + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = RED_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_red_balance, + .get = ov9650_get_red_balance + }, { + { + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = BLUE_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_blue_balance, + .get = ov9650_get_blue_balance + }, { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov9650_set_hflip, + .get = ov9650_get_hflip + }, { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov9650_set_vflip, + .get = ov9650_get_vflip + }, { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto white balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov9650_set_auto_white_balance, + .get = ov9650_get_auto_white_balance + }, { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto gain control", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov9650_set_auto_gain, + .get = ov9650_get_auto_gain + } + }, + + .nmodes = 1, + .modes = { + { + M5602_DEFAULT_FRAME_WIDTH, + M5602_DEFAULT_FRAME_HEIGHT, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT, + .bytesperline = M5602_DEFAULT_FRAME_WIDTH, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + } + } +}; + +static const unsigned char preinit_ov9650[][3] = +{ + /* [INITCAM] */ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, + /* Reset chip */ + {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, + /* Enable double clock */ + {SENSOR, OV9650_CLKRC, 0x80}, + /* Do something out of spec with the power */ + {SENSOR, OV9650_OFON, 0x40} +}; + +static const unsigned char init_ov9650[][3] = +{ + /* [INITCAM] */ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a}, + /* Reset chip */ + {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, + /* Enable double clock */ + {SENSOR, OV9650_CLKRC, 0x80}, + /* Do something out of spec with the power */ + {SENSOR, OV9650_OFON, 0x40}, + + /* Set QQVGA */ + {SENSOR, OV9650_COM1, 0x20}, + /* Set fast AGC/AEC algorithm with unlimited step size */ + {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC | + OV9650_AEC_UNLIM_STEP_SIZE | + OV9650_AWB_EN | OV9650_AGC_EN}, + + {SENSOR, OV9650_CHLF, 0x10}, + {SENSOR, OV9650_ARBLM, 0xbf}, + {SENSOR, OV9650_ACOM38, 0x81}, + /* Turn off color matrix coefficient double option */ + {SENSOR, OV9650_COM16, 0x00}, + /* Enable color matrix for RGB/YUV, Delay Y channel, + set output Y/UV delay to 1 */ + {SENSOR, OV9650_COM13, 0x19}, + /* Enable digital BLC, Set output mode to U Y V Y */ + {SENSOR, OV9650_TSLB, 0x0c}, + /* Limit the AGC/AEC stable upper region */ + {SENSOR, OV9650_COM24, 0x00}, + /* Enable HREF and some out of spec things */ + {SENSOR, OV9650_COM12, 0x73}, + /* Set all DBLC offset signs to positive and + do some out of spec stuff */ + {SENSOR, OV9650_DBLC1, 0xdf}, + {SENSOR, OV9650_COM21, 0x06}, + {SENSOR, OV9650_RSVD35, 0x91}, + /* Necessary, no camera stream without it */ + {SENSOR, OV9650_RSVD16, 0x06}, + {SENSOR, OV9650_RSVD94, 0x99}, + {SENSOR, OV9650_RSVD95, 0x99}, + {SENSOR, OV9650_RSVD96, 0x04}, + /* Enable full range output */ + {SENSOR, OV9650_COM15, 0x0}, + /* Enable HREF at optical black, enable ADBLC bias, + enable ADBLC, reset timings at format change */ + {SENSOR, OV9650_COM6, 0x4b}, + /* Subtract 32 from the B channel bias */ + {SENSOR, OV9650_BBIAS, 0xa0}, + /* Subtract 32 from the Gb channel bias */ + {SENSOR, OV9650_GbBIAS, 0xa0}, + /* Do not bypass the analog BLC and to some out of spec stuff */ + {SENSOR, OV9650_Gr_COM, 0x00}, + /* Subtract 32 from the R channel bias */ + {SENSOR, OV9650_RBIAS, 0xa0}, + /* Subtract 32 from the R channel bias */ + {SENSOR, OV9650_RBIAS, 0x0}, + {SENSOR, OV9650_COM26, 0x80}, + {SENSOR, OV9650_ACOMA9, 0x98}, + /* Set the AGC/AEC stable region upper limit */ + {SENSOR, OV9650_AEW, 0x68}, + /* Set the AGC/AEC stable region lower limit */ + {SENSOR, OV9650_AEB, 0x5c}, + /* Set the high and low limit nibbles to 3 */ + {SENSOR, OV9650_VPT, 0xc3}, + /* Set the Automatic Gain Ceiling (AGC) to 128x, + drop VSYNC at frame drop, + limit exposure timing, + drop frame when the AEC step is larger than the exposure gap */ + {SENSOR, OV9650_COM9, 0x6e}, + /* Set VSYNC negative, Set RESET to SLHS (slave mode horizontal sync) + and set PWDN to SLVS (slave mode vertical sync) */ + {SENSOR, OV9650_COM10, 0x42}, + /* Set horizontal column start high to default value */ + {SENSOR, OV9650_HSTART, 0x1a}, + /* Set horizontal column end */ + {SENSOR, OV9650_HSTOP, 0xbf}, + /* Complementing register to the two writes above */ + {SENSOR, OV9650_HREF, 0xb2}, + /* Set vertical row start high bits */ + {SENSOR, OV9650_VSTRT, 0x02}, + /* Set vertical row end low bits */ + {SENSOR, OV9650_VSTOP, 0x7e}, + /* Set complementing vertical frame control */ + {SENSOR, OV9650_VREF, 0x10}, + /* Set raw RGB output format with VGA resolution */ + {SENSOR, OV9650_COM7, OV9650_VGA_SELECT | + OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT}, + {SENSOR, OV9650_ADC, 0x04}, + {SENSOR, OV9650_HV, 0x40}, + /* Enable denoise, and white-pixel erase */ + {SENSOR, OV9650_COM22, 0x23}, + + /* Set the high bits of the exposure value */ + {SENSOR, OV9650_AECH, ((EXPOSURE_DEFAULT & 0xff00) >> 8)}, + + /* Set the low bits of the exposure value */ + {SENSOR, OV9650_COM1, (EXPOSURE_DEFAULT & 0xff)}, + {SENSOR, OV9650_GAIN, GAIN_DEFAULT}, + {SENSOR, OV9650_BLUE, BLUE_GAIN_DEFAULT}, + {SENSOR, OV9650_RED, RED_GAIN_DEFAULT}, + + {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL}, + {SENSOR, OV9650_COM5, OV9650_SYSTEM_CLK_SEL}, + + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, + {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x09}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x5e}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0xde} +}; + +static const unsigned char power_down_ov9650[][3] = +{ + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {SENSOR, OV9650_COM7, 0x80}, + {SENSOR, OV9650_OFON, 0xf4}, + {SENSOR, OV9650_MVFP, 0x80}, + {SENSOR, OV9650_DBLV, 0x3f}, + {SENSOR, OV9650_RSVD36, 0x49}, + {SENSOR, OV9650_COM7, 0x05}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0} +}; + +/* Vertically and horizontally flips the image if matched, needed for machines + where the sensor is mounted upside down */ +static const struct dmi_system_id ov9650_flip_dmi_table[] = { + { + .ident = "ASUS A6VC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6VC") + } + }, + { + .ident = "ASUS A6VM", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") + } + }, + { + .ident = "ASUS A6JC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") + } + }, + { + .ident = "ASUS A6Kt", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt") + } + }, + { } +}; + +#endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_po1030.c b/linux/drivers/media/video/gspca/m5602/m5602_po1030.c new file mode 100644 index 000000000..08c015bde --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_po1030.c @@ -0,0 +1,336 @@ +/* + * Driver for the po1030 sensor + * + * Copyright (c) 2008 Erik Andren + * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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. + * + */ + +#include "m5602_po1030.h" + +int po1030_probe(struct sd *sd) +{ + u8 prod_id = 0, ver_id = 0, i; + + if (force_sensor) { + if (force_sensor == PO1030_SENSOR) { + info("Forcing a %s sensor", po1030.name); + goto sensor_found; + } + /* If we want to force another sensor, don't try to probe this + * one */ + return -ENODEV; + } + + info("Probing for a po1030 sensor"); + + /* Run the pre-init to actually probe the unit */ + for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) { + u8 data = preinit_po1030[i][2]; + if (preinit_po1030[i][0] == SENSOR) + po1030_write_sensor(sd, + preinit_po1030[i][1], &data, 1); + else + m5602_write_bridge(sd, preinit_po1030[i][1], data); + } + + if (po1030_read_sensor(sd, 0x3, &prod_id, 1)) + return -ENODEV; + + if (po1030_read_sensor(sd, 0x4, &ver_id, 1)) + return -ENODEV; + + if ((prod_id == 0x02) && (ver_id == 0xef)) { + info("Detected a po1030 sensor"); + goto sensor_found; + } + return -ENODEV; + +sensor_found: + sd->gspca_dev.cam.cam_mode = po1030.modes; + sd->gspca_dev.cam.nmodes = po1030.nmodes; + sd->desc->ctrls = po1030.ctrls; + sd->desc->nctrls = po1030.nctrls; + return 0; +} + +int po1030_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + + do { + err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); + } while ((*i2c_data & I2C_BUSY) && !err); + + m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, + sd->sensor->i2c_slave_id); + m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); + m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x10 + len); + m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08); + + for (i = 0; i < len; i++) { + err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); + + PDEBUG(DBG_TRACE, "Reading sensor register " + "0x%x containing 0x%x ", address, *i2c_data); + } + return (err < 0) ? err : 0; +} + +int po1030_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + u8 *p; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + /* The po1030 only supports one byte writes */ + if (len > 1 || !len) + return -EINVAL; + + memcpy(buf, sensor_urb_skeleton, sizeof(sensor_urb_skeleton)); + + buf[11] = sd->sensor->i2c_slave_id; + buf[15] = address; + + p = buf + 16; + + /* Copy a four byte write sequence for each byte to be written to */ + for (i = 0; i < len; i++) { + memcpy(p, sensor_urb_skeleton + 16, 4); + p[3] = i2c_data[i]; + p += 4; + PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x", + address, i2c_data[i]); + } + + /* Copy the footer */ + memcpy(p, sensor_urb_skeleton + 20, 4); + + /* Set the total length */ + p[3] = 0x10 + len; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x19, + 0x0000, buf, + 20 + len * 4, M5602_URB_MSG_TIMEOUT); + + return (err < 0) ? err : 0; +} + +int po1030_init(struct sd *sd) +{ + int i, err = 0; + + /* Init the sensor */ + for (i = 0; i < ARRAY_SIZE(init_po1030); i++) { + u8 data[2] = {0x00, 0x00}; + + switch (init_po1030[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + init_po1030[i][1], + init_po1030[i][2]); + break; + + case SENSOR: + data[0] = init_po1030[i][2]; + err = po1030_write_sensor(sd, + init_po1030[i][1], data, 1); + break; + + case SENSOR_LONG: + data[0] = init_po1030[i][2]; + data[1] = init_po1030[i][3]; + err = po1030_write_sensor(sd, + init_po1030[i][1], data, 2); + break; + default: + info("Invalid stream command, exiting init"); + return -EINVAL; + } + } + + if (dump_sensor) + po1030_dump_registers(sd); + + return (err < 0) ? err : 0; +} + +int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 i2c_data; + int err; + + err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_H, + &i2c_data, 1); + if (err < 0) + goto out; + *val = (i2c_data << 8); + + err = po1030_read_sensor(sd, PO1030_REG_INTEGLINES_M, + &i2c_data, 1); + *val |= i2c_data; + + PDEBUG(DBG_V4L2_CID, "Exposure read as %d", *val); +out: + return (err < 0) ? err : 0; +} + +int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 i2c_data; + int err; + + PDEBUG(DBG_V4L2, "Set exposure to %d", val & 0xffff); + + i2c_data = ((val & 0xff00) >> 8); + PDEBUG(DBG_V4L2, "Set exposure to high byte to 0x%x", + i2c_data); + + err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_H, + &i2c_data, 1); + if (err < 0) + goto out; + + i2c_data = (val & 0xff); + PDEBUG(DBG_V4L2, "Set exposure to low byte to 0x%x", + i2c_data); + err = po1030_write_sensor(sd, PO1030_REG_INTEGLINES_M, + &i2c_data, 1); + +out: + return (err < 0) ? err : 0; +} + +int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 i2c_data; + int err; + + err = po1030_read_sensor(sd, PO1030_REG_GLOBALGAIN, + &i2c_data, 1); + *val = i2c_data; + PDEBUG(DBG_V4L2_CID, "Read global gain %d", *val); + + return (err < 0) ? err : 0; +} + +int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 i2c_data; + int err; + + i2c_data = val & 0xff; + PDEBUG(DBG_V4L2, "Set global gain to %d", i2c_data); + err = po1030_write_sensor(sd, PO1030_REG_GLOBALGAIN, + &i2c_data, 1); + return (err < 0) ? err : 0; +} + +int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 i2c_data; + int err; + + err = po1030_read_sensor(sd, PO1030_REG_RED_GAIN, + &i2c_data, 1); + *val = i2c_data; + PDEBUG(DBG_V4L2_CID, "Read red gain %d", *val); + return (err < 0) ? err : 0; +} + +int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 i2c_data; + int err; + + i2c_data = val & 0xff; + PDEBUG(DBG_V4L2, "Set red gain to %d", i2c_data); + err = po1030_write_sensor(sd, PO1030_REG_RED_GAIN, + &i2c_data, 1); + return (err < 0) ? err : 0; +} + +int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 i2c_data; + int err; + + err = po1030_read_sensor(sd, PO1030_REG_BLUE_GAIN, + &i2c_data, 1); + *val = i2c_data; + PDEBUG(DBG_V4L2_CID, "Read blue gain %d", *val); + + return (err < 0) ? err : 0; +} + +int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 i2c_data; + int err; + i2c_data = val & 0xff; + PDEBUG(DBG_V4L2, "Set blue gain to %d", i2c_data); + err = po1030_write_sensor(sd, PO1030_REG_BLUE_GAIN, + &i2c_data, 1); + + return (err < 0) ? err : 0; +} + +int po1030_power_down(struct sd *sd) +{ + return 0; +} + +void po1030_dump_registers(struct sd *sd) +{ + int address; + u8 value = 0; + + info("Dumping the po1030 sensor core registers"); + for (address = 0; address < 0x7f; address++) { + po1030_read_sensor(sd, address, &value, 1); + info("register 0x%x contains 0x%x", + address, value); + } + + info("po1030 register state dump complete"); + + info("Probing for which registers that are read/write"); + for (address = 0; address < 0xff; address++) { + u8 old_value, ctrl_value; + u8 test_value[2] = {0xff, 0xff}; + + po1030_read_sensor(sd, address, &old_value, 1); + po1030_write_sensor(sd, address, test_value, 1); + po1030_read_sensor(sd, address, &ctrl_value, 1); + + if (ctrl_value == test_value[0]) + info("register 0x%x is writeable", address); + else + info("register 0x%x is read only", address); + + /* Restore original value */ + po1030_write_sensor(sd, address, &old_value, 1); + } +} diff --git a/linux/drivers/media/video/gspca/m5602/m5602_po1030.h b/linux/drivers/media/video/gspca/m5602/m5602_po1030.h new file mode 100644 index 000000000..db86a9287 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_po1030.h @@ -0,0 +1,478 @@ +/* + * Driver for the po1030 sensor. + * This is probably a pixel plus sensor but we haven't identified it yet + * + * Copyright (c) 2008 Erik Andren + * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * Register defines taken from Pascal Stangs Proxycon Armlib + * + * 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. + * + */ + +#ifndef M5602_PO1030_H_ +#define M5602_PO1030_H_ + +#include "m5602_sensor.h" + +/*****************************************************************************/ + +#define PO1030_REG_DEVID_H 0x00 +#define PO1030_REG_DEVID_L 0x01 +#define PO1030_REG_FRAMEWIDTH_H 0x04 +#define PO1030_REG_FRAMEWIDTH_L 0x05 +#define PO1030_REG_FRAMEHEIGHT_H 0x06 +#define PO1030_REG_FRAMEHEIGHT_L 0x07 +#define PO1030_REG_WINDOWX_H 0x08 +#define PO1030_REG_WINDOWX_L 0x09 +#define PO1030_REG_WINDOWY_H 0x0a +#define PO1030_REG_WINDOWY_L 0x0b +#define PO1030_REG_WINDOWWIDTH_H 0x0c +#define PO1030_REG_WINDOWWIDTH_L 0x0d +#define PO1030_REG_WINDOWHEIGHT_H 0x0e +#define PO1030_REG_WINDOWHEIGHT_L 0x0f + +#define PO1030_REG_GLOBALIBIAS 0x12 +#define PO1030_REG_PIXELIBIAS 0x13 + +#define PO1030_REG_GLOBALGAIN 0x15 +#define PO1030_REG_RED_GAIN 0x16 +#define PO1030_REG_GREEN_1_GAIN 0x17 +#define PO1030_REG_BLUE_GAIN 0x18 +#define PO1030_REG_GREEN_2_GAIN 0x19 + +#define PO1030_REG_INTEGLINES_H 0x1a +#define PO1030_REG_INTEGLINES_M 0x1b +#define PO1030_REG_INTEGLINES_L 0x1c + +#define PO1030_REG_CONTROL1 0x1d +#define PO1030_REG_CONTROL2 0x1e +#define PO1030_REG_CONTROL3 0x1f +#define PO1030_REG_CONTROL4 0x20 + +#define PO1030_REG_PERIOD50_H 0x23 +#define PO1030_REG_PERIOD50_L 0x24 +#define PO1030_REG_PERIOD60_H 0x25 +#define PO1030_REG_PERIOD60_L 0x26 +#define PO1030_REG_REGCLK167 0x27 +#define PO1030_REG_DELTA50 0x28 +#define PO1030_REG_DELTA60 0x29 + +#define PO1030_REG_ADCOFFSET 0x2c + +/* Gamma Correction Coeffs */ +#define PO1030_REG_GC0 0x2d +#define PO1030_REG_GC1 0x2e +#define PO1030_REG_GC2 0x2f +#define PO1030_REG_GC3 0x30 +#define PO1030_REG_GC4 0x31 +#define PO1030_REG_GC5 0x32 +#define PO1030_REG_GC6 0x33 +#define PO1030_REG_GC7 0x34 + +/* Color Transform Matrix */ +#define PO1030_REG_CT0 0x35 +#define PO1030_REG_CT1 0x36 +#define PO1030_REG_CT2 0x37 +#define PO1030_REG_CT3 0x38 +#define PO1030_REG_CT4 0x39 +#define PO1030_REG_CT5 0x3a +#define PO1030_REG_CT6 0x3b +#define PO1030_REG_CT7 0x3c +#define PO1030_REG_CT8 0x3d + +#define PO1030_REG_AUTOCTRL1 0x3e +#define PO1030_REG_AUTOCTRL2 0x3f + +#define PO1030_REG_YTARGET 0x40 +#define PO1030_REG_GLOBALGAINMIN 0x41 +#define PO1030_REG_GLOBALGAINMAX 0x42 + +/* Output format control */ +#define PO1030_REG_OUTFORMCTRL1 0x5a +#define PO1030_REG_OUTFORMCTRL2 0x5b +#define PO1030_REG_OUTFORMCTRL3 0x5c +#define PO1030_REG_OUTFORMCTRL4 0x5d +#define PO1030_REG_OUTFORMCTRL5 0x5e + +/* Imaging coefficients */ +#define PO1030_REG_YBRIGHT 0x73 +#define PO1030_REG_YCONTRAST 0x74 +#define PO1030_REG_YSATURATION 0x75 + +/*****************************************************************************/ + +#define PO1030_GLOBAL_GAIN_DEFAULT 0x12 +#define PO1030_EXPOSURE_DEFAULT 0xf0ff +#define PO1030_BLUE_GAIN_DEFAULT 0x40 +#define PO1030_RED_GAIN_DEFAULT 0x40 + +/*****************************************************************************/ + +/* Kernel module parameters */ +extern int force_sensor; +extern int dump_sensor; +extern unsigned int debug; + +int po1030_probe(struct sd *sd); +int po1030_init(struct sd *sd); +int po1030_power_down(struct sd *sd); + +void po1030_dump_registers(struct sd *sd); + +int po1030_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); +int po1030_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + +int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val); +int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); +int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); +int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); +int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); + +static struct m5602_sensor po1030 = { + .name = "PO1030", + + .i2c_slave_id = 0xdc, + + .probe = po1030_probe, + .init = po1030_init, + .power_down = po1030_power_down, + + .nctrls = 4, + .ctrls = { + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = PO1030_GLOBAL_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_gain, + .get = po1030_get_gain + }, { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0xffff, + .step = 0x1, + .default_value = PO1030_EXPOSURE_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_exposure, + .get = po1030_get_exposure + }, { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = PO1030_RED_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_red_balance, + .get = po1030_get_red_balance + }, { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = PO1030_BLUE_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_blue_balance, + .get = po1030_get_blue_balance + } + }, + .nmodes = 1, + .modes = { + { + M5602_DEFAULT_FRAME_WIDTH, + M5602_DEFAULT_FRAME_HEIGHT, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT, + .bytesperline = M5602_DEFAULT_FRAME_WIDTH, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + } + } +}; + +static const unsigned char preinit_po1030[][3] = +{ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + + {SENSOR, PO1030_REG_AUTOCTRL2, 0x24}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, + {BRIDGE, M5602_XB_SIG_INI, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x02}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xec}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x87}, + {BRIDGE, M5602_XB_SIG_INI, 0x00}, + + {SENSOR, PO1030_REG_AUTOCTRL2, 0x24}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00} +}; + +static const unsigned char init_po1030[][4] = +{ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + /*sequence 1*/ + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, + + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + /*end of sequence 1*/ + + /*sequence 2 (same as stop sequence)*/ + {SENSOR, PO1030_REG_AUTOCTRL2, 0x24}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + /*end of sequence 2*/ + + /*sequence 5*/ + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, + {BRIDGE, M5602_XB_SIG_INI, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x02}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xec}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x87}, + {BRIDGE, M5602_XB_SIG_INI, 0x00}, + /*end of sequence 5*/ + + /*sequence 2 stop */ + {SENSOR, PO1030_REG_AUTOCTRL2, 0x24}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + /*end of sequence 2 stop */ + +/* --------------------------------- + * end of init - begin of start + * --------------------------------- */ + + /*sequence 3*/ + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + /*end of sequence 3*/ + /*sequence 4*/ + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, + + {SENSOR, PO1030_REG_AUTOCTRL2, 0x04}, + + /* Set the width to 751 */ + {SENSOR, PO1030_REG_FRAMEWIDTH_H, 0x02}, + {SENSOR, PO1030_REG_FRAMEWIDTH_L, 0xef}, + + /* Set the height to 540 */ + {SENSOR, PO1030_REG_FRAMEHEIGHT_H, 0x02}, + {SENSOR, PO1030_REG_FRAMEHEIGHT_L, 0x1c}, + + /* Set the x window to 1 */ + {SENSOR, PO1030_REG_WINDOWX_H, 0x00}, + {SENSOR, PO1030_REG_WINDOWX_L, 0x01}, + + /* Set the y window to 1 */ + {SENSOR, PO1030_REG_WINDOWY_H, 0x00}, + {SENSOR, PO1030_REG_WINDOWX_L, 0x01}, + + {SENSOR, PO1030_REG_WINDOWWIDTH_H, 0x02}, + {SENSOR, PO1030_REG_WINDOWWIDTH_L, 0x87}, + {SENSOR, PO1030_REG_WINDOWHEIGHT_H, 0x01}, + {SENSOR, PO1030_REG_WINDOWHEIGHT_L, 0xe3}, + + {SENSOR, PO1030_REG_OUTFORMCTRL2, 0x04}, + {SENSOR, PO1030_REG_OUTFORMCTRL2, 0x04}, + {SENSOR, PO1030_REG_AUTOCTRL1, 0x08}, + {SENSOR, PO1030_REG_CONTROL2, 0x03}, + {SENSOR, 0x21, 0x90}, + {SENSOR, PO1030_REG_YTARGET, 0x60}, + {SENSOR, 0x59, 0x13}, + {SENSOR, PO1030_REG_OUTFORMCTRL1, 0x40}, + {SENSOR, 0x5f, 0x00}, + {SENSOR, 0x60, 0x80}, + {SENSOR, 0x78, 0x14}, + {SENSOR, 0x6f, 0x01}, + {SENSOR, PO1030_REG_CONTROL1, 0x18}, + {SENSOR, PO1030_REG_GLOBALGAINMAX, 0x14}, + {SENSOR, 0x63, 0x38}, + {SENSOR, 0x64, 0x38}, + {SENSOR, PO1030_REG_CONTROL1, 0x58}, + {SENSOR, PO1030_REG_RED_GAIN, 0x30}, + {SENSOR, PO1030_REG_GREEN_1_GAIN, 0x30}, + {SENSOR, PO1030_REG_BLUE_GAIN, 0x30}, + {SENSOR, PO1030_REG_GREEN_2_GAIN, 0x30}, + {SENSOR, PO1030_REG_GC0, 0x10}, + {SENSOR, PO1030_REG_GC1, 0x20}, + {SENSOR, PO1030_REG_GC2, 0x40}, + {SENSOR, PO1030_REG_GC3, 0x60}, + {SENSOR, PO1030_REG_GC4, 0x80}, + {SENSOR, PO1030_REG_GC5, 0xa0}, + {SENSOR, PO1030_REG_GC6, 0xc0}, + {SENSOR, PO1030_REG_GC7, 0xff}, + /*end of sequence 4*/ + /*sequence 5*/ + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, + {BRIDGE, M5602_XB_SIG_INI, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x02}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xec}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x7e}, + {BRIDGE, M5602_XB_SIG_INI, 0x00}, + /*end of sequence 5*/ + + /*sequence 6*/ + /* Changing 40 in f0 the image becomes green in bayer mode and red in + * rgb mode */ + {SENSOR, PO1030_REG_RED_GAIN, PO1030_RED_GAIN_DEFAULT}, + /* in changing 40 in f0 the image becomes green in bayer mode and red in + * rgb mode */ + {SENSOR, PO1030_REG_BLUE_GAIN, PO1030_BLUE_GAIN_DEFAULT}, + + /* with a very low lighted environment increase the exposure but + * decrease the FPS (Frame Per Second) */ + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + + /* Controls high exposure more than SENSOR_LOW_EXPOSURE, use only in + * low lighted environment (f0 is more than ff ?)*/ + {SENSOR, PO1030_REG_INTEGLINES_H, ((PO1030_EXPOSURE_DEFAULT >> 2) + & 0xff)}, + + /* Controls middle exposure, use only in high lighted environment */ + {SENSOR, PO1030_REG_INTEGLINES_M, PO1030_EXPOSURE_DEFAULT & 0xff}, + + /* Controls clarity (not sure) */ + {SENSOR, PO1030_REG_INTEGLINES_L, 0x00}, + /* Controls gain (the image is more lighted) */ + {SENSOR, PO1030_REG_GLOBALGAIN, PO1030_GLOBAL_GAIN_DEFAULT}, + + /* Sets the width */ + {SENSOR, PO1030_REG_FRAMEWIDTH_H, 0x02}, + {SENSOR, PO1030_REG_FRAMEWIDTH_L, 0xef} + /*end of sequence 6*/ +}; + +#endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c new file mode 100644 index 000000000..682025653 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -0,0 +1,463 @@ +/* + * Driver for the s5k4aa sensor + * + * Copyright (C) 2008 Erik Andren + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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. + * + */ + +#include "m5602_s5k4aa.h" + +int s5k4aa_probe(struct sd *sd) +{ + u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75}; + int i, err = 0; + + if (force_sensor) { + if (force_sensor == S5K4AA_SENSOR) { + info("Forcing a %s sensor", s5k4aa.name); + goto sensor_found; + } + /* If we want to force another sensor, don't try to probe this + * one */ + return -ENODEV; + } + + info("Probing for a s5k4aa sensor"); + + /* Preinit the sensor */ + for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) { + u8 data[2] = {0x00, 0x00}; + + switch (preinit_s5k4aa[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + preinit_s5k4aa[i][1], + preinit_s5k4aa[i][2]); + break; + + case SENSOR: + data[0] = preinit_s5k4aa[i][2]; + err = s5k4aa_write_sensor(sd, + preinit_s5k4aa[i][1], + data, 1); + break; + + case SENSOR_LONG: + data[0] = preinit_s5k4aa[i][2]; + data[1] = preinit_s5k4aa[i][3]; + err = s5k4aa_write_sensor(sd, + preinit_s5k4aa[i][1], + data, 2); + break; + default: + info("Invalid stream command, exiting init"); + return -EINVAL; + } + } + + /* Test some registers, but we don't know their exact meaning yet */ + if (s5k4aa_read_sensor(sd, 0x00, prod_id, sizeof(prod_id))) + return -ENODEV; + + if (memcmp(prod_id, expected_prod_id, sizeof(prod_id))) + return -ENODEV; + else + info("Detected a s5k4aa sensor"); +sensor_found: + sd->gspca_dev.cam.cam_mode = s5k4aa.modes; + sd->gspca_dev.cam.nmodes = s5k4aa.nmodes; + sd->desc->ctrls = s5k4aa.ctrls; + sd->desc->nctrls = s5k4aa.nctrls; + + return 0; +} + +int s5k4aa_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + + do { + err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); + } while ((*i2c_data & I2C_BUSY) && !err); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, + sd->sensor->i2c_slave_id); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len); + if (err < 0) + goto out; + + do { + err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); + } while ((*i2c_data & I2C_BUSY) && !err); + if (err < 0) + goto out; + + for (i = 0; (i < len) & !err; i++) { + err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); + + PDEBUG(DBG_TRACE, "Reading sensor register " + "0x%x containing 0x%x ", address, *i2c_data); + } +out: + return (err < 0) ? err : 0; +} + +int s5k4aa_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + u8 *p; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + /* No sensor with a data width larger than 16 bits has yet been seen */ + if (len > 2 || !len) + return -EINVAL; + + memcpy(buf, sensor_urb_skeleton, + sizeof(sensor_urb_skeleton)); + + buf[11] = sd->sensor->i2c_slave_id; + buf[15] = address; + + /* Special case larger sensor writes */ + p = buf + 16; + + /* Copy a four byte write sequence for each byte to be written to */ + for (i = 0; i < len; i++) { + memcpy(p, sensor_urb_skeleton + 16, 4); + p[3] = i2c_data[i]; + p += 4; + PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x", + address, i2c_data[i]); + } + + /* Copy the tailer */ + memcpy(p, sensor_urb_skeleton + 20, 4); + + /* Set the total length */ + p[3] = 0x10 + len; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x19, + 0x0000, buf, + 20 + len * 4, M5602_URB_MSG_TIMEOUT); + + return (err < 0) ? err : 0; +} + +int s5k4aa_init(struct sd *sd) +{ + int i, err = 0; + + for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) { + u8 data[2] = {0x00, 0x00}; + + switch (init_s5k4aa[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + init_s5k4aa[i][1], + init_s5k4aa[i][2]); + break; + + case SENSOR: + data[0] = init_s5k4aa[i][2]; + err = s5k4aa_write_sensor(sd, + init_s5k4aa[i][1], data, 1); + break; + + case SENSOR_LONG: + data[0] = init_s5k4aa[i][2]; + data[1] = init_s5k4aa[i][3]; + err = s5k4aa_write_sensor(sd, + init_s5k4aa[i][1], data, 2); + break; + default: + info("Invalid stream command, exiting init"); + return -EINVAL; + } + } + + if (dump_sensor) + s5k4aa_dump_registers(sd); + + if (!err && dmi_check_system(s5k4aa_vflip_dmi_table)) { + u8 data = 0x02; + info("vertical flip quirk active"); + s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + s5k4aa_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); + data |= S5K4AA_RM_V_FLIP; + data &= ~S5K4AA_RM_H_FLIP; + s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + + /* Decrement COLSTART to preserve color order (BGGR) */ + s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + data--; + s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + + /* Increment ROWSTART to preserve color order (BGGR) */ + s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + data++; + s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + } + + return (err < 0) ? err : 0; +} + +int s5k4aa_power_down(struct sd *sd) +{ + return 0; +} + +int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + goto out; + + err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); + if (err < 0) + goto out; + + *val = data << 8; + err = s5k4aa_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); + *val |= data; + PDEBUG(DBG_V4L2_CID, "Read exposure %d", *val); +out: + return (err < 0) ? err : 0; +} + +int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + PDEBUG(DBG_V4L2_CID, "Set exposure to %d", val); + err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + goto out; + data = (val >> 8) & 0xff; + err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); + if (err < 0) + goto out; + data = val & 0xff; + err = s5k4aa_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); +out: + return (err < 0) ? err : 0; +} + +int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + goto out; + + err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + *val = (data & S5K4AA_RM_V_FLIP) >> 7; + PDEBUG(DBG_V4L2_CID, "Read vertical flip %d", *val); + +out: + return (err < 0) ? err : 0; +} + +int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + PDEBUG(DBG_V4L2_CID, "Set vertical flip to %d", val); + err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + goto out; + err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + if (err < 0) + goto out; + data = ((data & ~S5K4AA_RM_V_FLIP) + | ((val & 0x01) << 7)); + err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + if (err < 0) + goto out; + + if (val) { + err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + if (err < 0) + goto out; + + data++; + err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + } else { + err = s5k4aa_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + if (err < 0) + goto out; + + data--; + err = s5k4aa_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + } +out: + return (err < 0) ? err : 0; +} + +int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + goto out; + + err = s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + *val = (data & S5K4AA_RM_H_FLIP) >> 6; + PDEBUG(DBG_V4L2_CID, "Read horizontal flip %d", *val); +out: + return (err < 0) ? err : 0; +} + +int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + PDEBUG(DBG_V4L2_CID, "Set horizontal flip to %d", + val); + err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + goto out; + err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + if (err < 0) + goto out; + + data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6)); + err = s5k4aa_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + if (err < 0) + goto out; + + if (val) { + err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + if (err < 0) + goto out; + data++; + err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + if (err < 0) + goto out; + } else { + err = s5k4aa_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + if (err < 0) + goto out; + data--; + err = s5k4aa_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + } +out: + return (err < 0) ? err : 0; +} + +int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + goto out; + + err = s5k4aa_read_sensor(sd, S5K4AA_GAIN_2, &data, 1); + *val = data; + PDEBUG(DBG_V4L2_CID, "Read gain %d", *val); + +out: + return (err < 0) ? err : 0; +} + +int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + PDEBUG(DBG_V4L2_CID, "Set gain to %d", val); + err = s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + goto out; + + data = val & 0xff; + err = s5k4aa_write_sensor(sd, S5K4AA_GAIN_2, &data, 1); + +out: + return (err < 0) ? err : 0; +} + +void s5k4aa_dump_registers(struct sd *sd) +{ + int address; + u8 page, old_page; + s5k4aa_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); + for (page = 0; page < 16; page++) { + s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); + info("Dumping the s5k4aa register state for page 0x%x", page); + for (address = 0; address <= 0xff; address++) { + u8 value = 0; + s5k4aa_read_sensor(sd, address, &value, 1); + info("register 0x%x contains 0x%x", + address, value); + } + } + info("s5k4aa register state dump complete"); + + for (page = 0; page < 16; page++) { + s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1); + info("Probing for which registers that are " + "read/write for page 0x%x", page); + for (address = 0; address <= 0xff; address++) { + u8 old_value, ctrl_value, test_value = 0xff; + + s5k4aa_read_sensor(sd, address, &old_value, 1); + s5k4aa_write_sensor(sd, address, &test_value, 1); + s5k4aa_read_sensor(sd, address, &ctrl_value, 1); + + if (ctrl_value == test_value) + info("register 0x%x is writeable", address); + else + info("register 0x%x is read only", address); + + /* Restore original value */ + s5k4aa_write_sensor(sd, address, &old_value, 1); + } + } + info("Read/write register probing complete"); + s5k4aa_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1); +} diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h new file mode 100644 index 000000000..5d808a750 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h @@ -0,0 +1,368 @@ +/* + * Driver for the s5k4aa sensor + * + * Copyright (C) 2008 Erik Andren + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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. + * + */ + +#ifndef M5602_S5K4AA_H_ +#define M5602_S5K4AA_H_ + +#include <linux/dmi.h> + +#include "m5602_sensor.h" + +/*****************************************************************************/ + +#define S5K4AA_PAGE_MAP 0xec + +#define S5K4AA_PAGE_MAP_0 0x00 +#define S5K4AA_PAGE_MAP_1 0x01 +#define S5K4AA_PAGE_MAP_2 0x02 + +/* Sensor register definitions for page 0x02 */ +#define S5K4AA_READ_MODE 0x03 +#define S5K4AA_ROWSTART_HI 0x04 +#define S5K4AA_ROWSTART_LO 0x05 +#define S5K4AA_COLSTART_HI 0x06 +#define S5K4AA_COLSTART_LO 0x07 +#define S5K4AA_WINDOW_HEIGHT_HI 0x08 +#define S5K4AA_WINDOW_HEIGHT_LO 0x09 +#define S5K4AA_WINDOW_WIDTH_HI 0x0a +#define S5K4AA_WINDOW_WIDTH_LO 0x0b +#define S5K4AA_GLOBAL_GAIN__ 0x0f /* Only a guess ATM !!! */ +#define S5K4AA_H_BLANK_HI__ 0x1d /* Only a guess ATM !!! sync lost + if too low, reduces frame rate + if too high */ +#define S5K4AA_H_BLANK_LO__ 0x1e /* Only a guess ATM !!! */ +#define S5K4AA_EXPOSURE_HI 0x17 +#define S5K4AA_EXPOSURE_LO 0x18 +#define S5K4AA_GAIN_1 0x1f /* (digital?) gain : 5 bits */ +#define S5K4AA_GAIN_2 0x20 /* (analogue?) gain : 7 bits */ + +#define S5K4AA_RM_ROW_SKIP_4X 0x08 +#define S5K4AA_RM_ROW_SKIP_2X 0x04 +#define S5K4AA_RM_COL_SKIP_4X 0x02 +#define S5K4AA_RM_COL_SKIP_2X 0x01 +#define S5K4AA_RM_H_FLIP 0x40 +#define S5K4AA_RM_V_FLIP 0x80 + +/*****************************************************************************/ + +/* Kernel module parameters */ +extern int force_sensor; +extern int dump_sensor; +extern unsigned int debug; + +int s5k4aa_probe(struct sd *sd); +int s5k4aa_init(struct sd *sd); +int s5k4aa_power_down(struct sd *sd); + +void s5k4aa_dump_registers(struct sd *sd); + +int s5k4aa_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); +int s5k4aa_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + +int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val); + +static struct m5602_sensor s5k4aa = { + .name = "S5K4AA", + .probe = s5k4aa_probe, + .init = s5k4aa_init, + .power_down = s5k4aa_power_down, + .read_sensor = s5k4aa_read_sensor, + .write_sensor = s5k4aa_write_sensor, + .i2c_slave_id = 0x5a, + .nctrls = 4, + .ctrls = { + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = s5k4aa_set_vflip, + .get = s5k4aa_get_vflip + + }, { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = s5k4aa_set_hflip, + .get = s5k4aa_get_hflip + + }, { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0xa0, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k4aa_set_gain, + .get = s5k4aa_get_gain + }, { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 13, + .maximum = 0xfff, + .step = 1, + .default_value = 0x100, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k4aa_set_exposure, + .get = s5k4aa_get_exposure + } + }, + + .nmodes = 1, + .modes = { + { + M5602_DEFAULT_FRAME_WIDTH, + M5602_DEFAULT_FRAME_HEIGHT, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT, + .bytesperline = M5602_DEFAULT_FRAME_WIDTH, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + } + } +}; + +static const unsigned char preinit_s5k4aa[][4] = +{ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, + + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, + + {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00} +}; + +static const unsigned char init_s5k4aa[][4] = +{ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, + + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x14, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, + + {SENSOR, S5K4AA_PAGE_MAP, 0x07, 0x00}, + {SENSOR, 0x36, 0x01, 0x00}, + {SENSOR, S5K4AA_PAGE_MAP, 0x00, 0x00}, + {SENSOR, 0x7b, 0xff, 0x00}, + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + {SENSOR, 0x0c, 0x05, 0x00}, + {SENSOR, 0x02, 0x0e, 0x00}, + {SENSOR, S5K4AA_GAIN_1, 0x0f, 0x00}, + {SENSOR, S5K4AA_GAIN_2, 0x00, 0x00}, + {SENSOR, S5K4AA_GLOBAL_GAIN__, 0x01, 0x00}, + {SENSOR, 0x11, 0x00, 0x00}, + {SENSOR, 0x12, 0x00, 0x00}, + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + {SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00}, + {SENSOR, 0x37, 0x00, 0x00}, + {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00}, + {SENSOR, S5K4AA_ROWSTART_LO, 0x2a, 0x00}, + {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00}, + {SENSOR, S5K4AA_COLSTART_LO, 0x0b, 0x00}, + {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00}, + {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc4, 0x00}, + {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00}, + {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x08, 0x00}, + {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00}, + {SENSOR, S5K4AA_H_BLANK_LO__, 0x48, 0x00}, + {SENSOR, S5K4AA_EXPOSURE_HI, 0x00, 0x00}, + {SENSOR, S5K4AA_EXPOSURE_LO, 0x43, 0x00}, + {SENSOR, 0x11, 0x04, 0x00}, + {SENSOR, 0x12, 0xc3, 0x00}, + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + /* VSYNC_PARA, VSYNC_PARA : img height 480 = 0x01e0 */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + /* HSYNC_PARA, HSYNC_PARA : img width 640 = 0x0280 */ + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */ + + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP | S5K4AA_RM_ROW_SKIP_2X + | S5K4AA_RM_COL_SKIP_2X, 0x00}, + /* 0x37 : Fix image stability when light is too bright and improves + * image quality in 640x480, but worsens it in 1280x1024 */ + {SENSOR, 0x37, 0x01, 0x00}, + /* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */ + {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00}, + {SENSOR, S5K4AA_ROWSTART_LO, 0x2a, 0x00}, + {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00}, + {SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00}, + /* window_height_hi, window_height_lo : 960 = 0x03c0 */ + {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00}, + {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00}, + /* window_width_hi, window_width_lo : 1280 = 0x0500 */ + {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00}, + {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00}, + {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00}, + {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */ + {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00}, + {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00}, + {SENSOR, 0x11, 0x04, 0x00}, + {SENSOR, 0x12, 0xc3, 0x00}, + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + {SENSOR, 0x02, 0x0e, 0x00}, + {SENSOR_LONG, S5K4AA_GLOBAL_GAIN__, 0x0f, 0x00}, + {SENSOR, S5K4AA_GAIN_1, 0x0b, 0x00}, + {SENSOR, S5K4AA_GAIN_2, 0xa0, 0x00} +}; + +static const struct dmi_system_id s5k4aa_vflip_dmi_table[] = { + { + .ident = "Fujitsu-Siemens Amilo Xa 2528", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528") + } + }, + { + .ident = "Fujitsu-Siemens Amilo Xi 2550", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550") + } + }, + { + .ident = "MSI GX700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), + DMI_MATCH(DMI_BIOS_DATE, "07/26/2007") + } + }, + { } +}; + +#endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c new file mode 100644 index 000000000..c1ff967b1 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -0,0 +1,333 @@ +/* + * Driver for the s5k83a sensor + * + * Copyright (C) 2008 Erik Andren + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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. + * + */ + +#include "m5602_s5k83a.h" + +int s5k83a_probe(struct sd *sd) +{ + u8 prod_id = 0, ver_id = 0; + int i, err = 0; + + if (force_sensor) { + if (force_sensor == S5K83A_SENSOR) { + info("Forcing a %s sensor", s5k83a.name); + goto sensor_found; + } + /* If we want to force another sensor, don't try to probe this + * one */ + return -ENODEV; + } + + info("Probing for a s5k83a sensor"); + + /* Preinit the sensor */ + for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) { + u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]}; + if (preinit_s5k83a[i][0] == SENSOR) + err = s5k83a_write_sensor(sd, preinit_s5k83a[i][1], + data, 2); + else + err = m5602_write_bridge(sd, preinit_s5k83a[i][1], + data[0]); + } + + /* We don't know what register (if any) that contain the product id + * Just pick the first addresses that seem to produce the same results + * on multiple machines */ + if (s5k83a_read_sensor(sd, 0x00, &prod_id, 1)) + return -ENODEV; + + if (s5k83a_read_sensor(sd, 0x01, &ver_id, 1)) + return -ENODEV; + + if ((prod_id == 0xff) || (ver_id == 0xff)) + return -ENODEV; + else + info("Detected a s5k83a sensor"); + +sensor_found: + sd->gspca_dev.cam.cam_mode = s5k83a.modes; + sd->gspca_dev.cam.nmodes = s5k83a.nmodes; + sd->desc->ctrls = s5k83a.ctrls; + sd->desc->nctrls = s5k83a.nctrls; + return 0; +} + +int s5k83a_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + + do { + err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); + } while ((*i2c_data & I2C_BUSY) && !err); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, + sd->sensor->i2c_slave_id); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); + if (err < 0) + goto out; + + err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len); + if (err < 0) + goto out; + + do { + err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); + } while ((*i2c_data & I2C_BUSY) && !err); + + if (err < 0) + goto out; + for (i = 0; i < len && !len; i++) { + err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); + + PDEBUG(DBG_TRACE, "Reading sensor register " + "0x%x containing 0x%x ", address, *i2c_data); + } + +out: + return (err < 0) ? err : 0; +} + +int s5k83a_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len) +{ + int err, i; + u8 *p; + struct usb_device *udev = sd->gspca_dev.dev; + __u8 *buf = sd->gspca_dev.usb_buf; + + /* No sensor with a data width larger than 16 bits has yet been seen */ + if (len > 2 || !len) + return -EINVAL; + + memcpy(buf, sensor_urb_skeleton, + sizeof(sensor_urb_skeleton)); + + buf[11] = sd->sensor->i2c_slave_id; + buf[15] = address; + + /* Special case larger sensor writes */ + p = buf + 16; + + /* Copy a four byte write sequence for each byte to be written to */ + for (i = 0; i < len; i++) { + memcpy(p, sensor_urb_skeleton + 16, 4); + p[3] = i2c_data[i]; + p += 4; + PDEBUG(DBG_TRACE, "Writing sensor register 0x%x with 0x%x", + address, i2c_data[i]); + } + + /* Copy the tailer */ + memcpy(p, sensor_urb_skeleton + 20, 4); + + /* Set the total length */ + p[3] = 0x10 + len; + + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x04, 0x40, 0x19, + 0x0000, buf, + 20 + len * 4, M5602_URB_MSG_TIMEOUT); + + return (err < 0) ? err : 0; +} + +int s5k83a_init(struct sd *sd) +{ + int i, err = 0; + + for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) { + u8 data[2] = {0x00, 0x00}; + + switch (init_s5k83a[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + init_s5k83a[i][1], + init_s5k83a[i][2]); + break; + + case SENSOR: + data[0] = init_s5k83a[i][2]; + err = s5k83a_write_sensor(sd, + init_s5k83a[i][1], data, 1); + break; + + case SENSOR_LONG: + data[0] = init_s5k83a[i][2]; + data[1] = init_s5k83a[i][3]; + err = s5k83a_write_sensor(sd, + init_s5k83a[i][1], data, 2); + break; + default: + info("Invalid stream command, exiting init"); + return -EINVAL; + } + } + + if (dump_sensor) + s5k83a_dump_registers(sd); + + return (err < 0) ? err : 0; +} + +int s5k83a_power_down(struct sd *sd) +{ + return 0; +} + +void s5k83a_dump_registers(struct sd *sd) +{ + int address; + u8 page, old_page; + s5k83a_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); + + for (page = 0; page < 16; page++) { + s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); + info("Dumping the s5k83a register state for page 0x%x", page); + for (address = 0; address <= 0xff; address++) { + u8 val = 0; + s5k83a_read_sensor(sd, address, &val, 1); + info("register 0x%x contains 0x%x", + address, val); + } + } + info("s5k83a register state dump complete"); + + for (page = 0; page < 16; page++) { + s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); + info("Probing for which registers that are read/write " + "for page 0x%x", page); + for (address = 0; address <= 0xff; address++) { + u8 old_val, ctrl_val, test_val = 0xff; + + s5k83a_read_sensor(sd, address, &old_val, 1); + s5k83a_write_sensor(sd, address, &test_val, 1); + s5k83a_read_sensor(sd, address, &ctrl_val, 1); + + if (ctrl_val == test_val) + info("register 0x%x is writeable", address); + else + info("register 0x%x is read only", address); + + /* Restore original val */ + s5k83a_write_sensor(sd, address, &old_val, 1); + } + } + info("Read/write register probing complete"); + s5k83a_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); +} + +int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + + err = s5k83a_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2); + data[1] = data[1] << 1; + *val = data[1]; + + return (err < 0) ? err : 0; +} + +int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + + data[0] = 0x00; + data[1] = 0x20; + err = s5k83a_write_sensor(sd, 0x14, data, 2); + if (err < 0) + return err; + + data[0] = 0x01; + data[1] = 0x00; + err = s5k83a_write_sensor(sd, 0x0d, data, 2); + if (err < 0) + return err; + + /* FIXME: This is not sane, we need to figure out the composition + of these registers */ + data[0] = val >> 3; /* brightness, high 5 bits */ + data[1] = val >> 1; /* brightness, high 7 bits */ + err = s5k83a_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2); + + return (err < 0) ? err : 0; +} + +int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 data; + struct sd *sd = (struct sd *) gspca_dev; + + err = s5k83a_read_sensor(sd, S5K83A_WHITENESS, &data, 1); + + *val = data; + return (err < 0) ? err : 0; +} + +int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 data[1]; + struct sd *sd = (struct sd *) gspca_dev; + + data[0] = val; + err = s5k83a_write_sensor(sd, S5K83A_WHITENESS, data, 1); + + return (err < 0) ? err : 0; +} + +int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + int err; + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + + err = s5k83a_read_sensor(sd, S5K83A_GAIN, data, 2); + + data[1] = data[1] & 0x3f; + if (data[1] > S5K83A_MAXIMUM_GAIN) + data[1] = S5K83A_MAXIMUM_GAIN; + + *val = data[1]; + + return (err < 0) ? err : 0; +} + +int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err = 0; + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + + data[0] = 0; + data[1] = val; + err = s5k83a_write_sensor(sd, S5K83A_GAIN, data, 2); + + return (err < 0) ? err : 0; +} diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h new file mode 100644 index 000000000..67432b5a5 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h @@ -0,0 +1,444 @@ +/* + * Driver for the s5k83a sensor + * + * Copyright (C) 2008 Erik Andren + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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. + * + */ + +#ifndef M5602_S5K83A_H_ +#define M5602_S5K83A_H_ + +#include "m5602_sensor.h" + +#define S5K83A_PAGE_MAP 0xec +#define S5K83A_GAIN 0x18 +#define S5K83A_WHITENESS 0x0a +#define S5K83A_BRIGHTNESS 0x1b + +#define S5K83A_DEFAULT_BRIGHTNESS 0x71 +#define S5K83A_DEFAULT_WHITENESS 0x7e +#define S5K83A_DEFAULT_GAIN 0x00 +#define S5K83A_MAXIMUM_GAIN 0x3c + +/*****************************************************************************/ + +/* Kernel module parameters */ +extern int force_sensor; +extern int dump_sensor; +extern unsigned int debug; + + +int s5k83a_probe(struct sd *sd); +int s5k83a_init(struct sd *sd); +int s5k83a_power_down(struct sd *sd); + +void s5k83a_dump_registers(struct sd *sd); + +int s5k83a_read_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); +int s5k83a_write_sensor(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + +int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val); +int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); +int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val); +int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val); +int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val); +int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct m5602_sensor s5k83a = { + .name = "S5K83A", + .probe = s5k83a_probe, + .init = s5k83a_init, + .power_down = s5k83a_power_down, + .read_sensor = s5k83a_read_sensor, + .write_sensor = s5k83a_write_sensor, + .i2c_slave_id = 0x5a, + .nctrls = 3, + .ctrls = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "brightness", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = S5K83A_DEFAULT_BRIGHTNESS, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k83a_set_brightness, + .get = s5k83a_get_brightness + + }, { + { + .id = V4L2_CID_WHITENESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "whiteness", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = S5K83A_DEFAULT_WHITENESS, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k83a_set_whiteness, + .get = s5k83a_get_whiteness, + }, { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = S5K83A_MAXIMUM_GAIN, + .step = 0x01, + .default_value = S5K83A_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k83a_set_gain, + .get = s5k83a_get_gain + } + }, + .nmodes = 1, + .modes = { + { + M5602_DEFAULT_FRAME_WIDTH, + M5602_DEFAULT_FRAME_HEIGHT, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT, + .bytesperline = M5602_DEFAULT_FRAME_WIDTH, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + + } + } +}; + +static const unsigned char preinit_s5k83a[][4] = +{ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, + + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, + + {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00} +}; + +/* This could probably be considerably shortened. + I don't have the hardware to experiment with it, patches welcome +*/ +static const unsigned char init_s5k83a[][4] = +{ + {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00}, + {SENSOR, 0xaf, 0x01, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00}, + {SENSOR, 0x7b, 0xff, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + {SENSOR, 0x01, 0x50, 0x00}, + {SENSOR, 0x12, 0x20, 0x00}, + {SENSOR, 0x17, 0x40, 0x00}, + {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00}, + {SENSOR, 0x1c, 0x00, 0x00}, + {SENSOR, 0x02, 0x70, 0x00}, + {SENSOR, 0x03, 0x0b, 0x00}, + {SENSOR, 0x04, 0xf0, 0x00}, + {SENSOR, 0x05, 0x0b, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + {SENSOR, 0x06, 0x71, 0x00}, + {SENSOR, 0x07, 0xe8, 0x00}, + {SENSOR, 0x08, 0x02, 0x00}, + {SENSOR, 0x09, 0x88, 0x00}, + {SENSOR, 0x14, 0x00, 0x00}, + {SENSOR, 0x15, 0x20, 0x00}, + {SENSOR, 0x19, 0x00, 0x00}, + {SENSOR, 0x1a, 0x98, 0x00}, + {SENSOR, 0x0f, 0x02, 0x00}, + {SENSOR, 0x10, 0xe5, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + {SENSOR_LONG, 0x14, 0x00, 0x20}, + {SENSOR_LONG, 0x0d, 0x00, 0x7d}, + {SENSOR_LONG, 0x1b, 0x0d, 0x05}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00}, + + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, + + {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00}, + {SENSOR, 0xaf, 0x01, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + /* ff ( init value )is very dark) || 71 and f0 better */ + {SENSOR, 0x7b, 0xff, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + {SENSOR, 0x01, 0x50, 0x00}, + {SENSOR, 0x12, 0x20, 0x00}, + {SENSOR, 0x17, 0x40, 0x00}, + {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00}, + {SENSOR, 0x1c, 0x00, 0x00}, + {SENSOR, 0x02, 0x70, 0x00}, + /* some values like 0x10 give a blue-purple image */ + {SENSOR, 0x03, 0x0b, 0x00}, + {SENSOR, 0x04, 0xf0, 0x00}, + {SENSOR, 0x05, 0x0b, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + /* under 80 don't work, highter depend on value */ + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, + + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + {SENSOR, 0x06, 0x71, 0x00}, + {SENSOR, 0x07, 0xe8, 0x00}, + {SENSOR, 0x08, 0x02, 0x00}, + {SENSOR, 0x09, 0x88, 0x00}, + {SENSOR, 0x14, 0x00, 0x00}, + {SENSOR, 0x15, 0x20, 0x00}, + {SENSOR, 0x19, 0x00, 0x00}, + {SENSOR, 0x1a, 0x98, 0x00}, + {SENSOR, 0x0f, 0x02, 0x00}, + {SENSOR, 0x10, 0xe5, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + {SENSOR_LONG, 0x14, 0x00, 0x20}, + {SENSOR_LONG, 0x0d, 0x00, 0x7d}, + {SENSOR_LONG, 0x1b, 0x0d, 0x05}, + + /* The following sequence is useless after a clean boot + but is necessary after resume from suspend */ + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, + {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, + + {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00}, + {SENSOR, 0xaf, 0x01, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00}, + {SENSOR, 0x7b, 0xff, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + {SENSOR, 0x01, 0x50, 0x00}, + {SENSOR, 0x12, 0x20, 0x00}, + {SENSOR, 0x17, 0x40, 0x00}, + {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00}, + {SENSOR, 0x1c, 0x00, 0x00}, + {SENSOR, 0x02, 0x70, 0x00}, + {SENSOR, 0x03, 0x0b, 0x00}, + {SENSOR, 0x04, 0xf0, 0x00}, + {SENSOR, 0x05, 0x0b, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + {SENSOR, 0x06, 0x71, 0x00}, + {SENSOR, 0x07, 0xe8, 0x00}, + {SENSOR, 0x08, 0x02, 0x00}, + {SENSOR, 0x09, 0x88, 0x00}, + {SENSOR, 0x14, 0x00, 0x00}, + {SENSOR, 0x15, 0x20, 0x00}, + {SENSOR, 0x19, 0x00, 0x00}, + {SENSOR, 0x1a, 0x98, 0x00}, + {SENSOR, 0x0f, 0x02, 0x00}, + + {SENSOR, 0x10, 0xe5, 0x00}, + {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + {SENSOR_LONG, 0x14, 0x00, 0x20}, + {SENSOR_LONG, 0x0d, 0x00, 0x7d}, + {SENSOR_LONG, 0x1b, 0x0d, 0x05}, + + /* normal colors + (this is value after boot, but after tries can be different) */ + {SENSOR, 0x00, 0x06, 0x00}, + + /* set default brightness */ + {SENSOR_LONG, 0x14, 0x00, 0x20}, + {SENSOR_LONG, 0x0d, 0x01, 0x00}, + {SENSOR_LONG, 0x1b, S5K83A_DEFAULT_BRIGHTNESS >> 3, + S5K83A_DEFAULT_BRIGHTNESS >> 1}, + + /* set default whiteness */ + {SENSOR, S5K83A_WHITENESS, S5K83A_DEFAULT_WHITENESS, 0x00}, + + /* set default gain */ + {SENSOR_LONG, 0x18, 0x00, S5K83A_DEFAULT_GAIN} +}; + +#endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_sensor.h b/linux/drivers/media/video/gspca/m5602/m5602_sensor.h new file mode 100644 index 000000000..930fcaab4 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_sensor.h @@ -0,0 +1,76 @@ +/* + * USB Driver for ALi m5602 based webcams + * + * Copyright (C) 2008 Erik Andren + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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. + * + */ + +#ifndef M5602_SENSOR_H_ +#define M5602_SENSOR_H_ + +#include "m5602_bridge.h" + +#define M5602_DEFAULT_FRAME_WIDTH 640 +#define M5602_DEFAULT_FRAME_HEIGHT 480 + +#define M5602_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10) + +/* Enumerates all supported sensors */ +enum sensors { + OV9650_SENSOR = 1, + S5K83A_SENSOR = 2, + S5K4AA_SENSOR = 3, + MT9M111_SENSOR = 4, + PO1030_SENSOR = 5 +}; + +/* Enumerates all possible instruction types */ +enum instruction { + BRIDGE, + SENSOR, + SENSOR_LONG +}; + +struct m5602_sensor { + /* Defines the name of a sensor */ + char name[32]; + + /* What i2c address the sensor is connected to */ + u8 i2c_slave_id; + + /* Probes if the sensor is connected */ + int (*probe)(struct sd *sd); + + /* Performs a initialization sequence */ + int (*init)(struct sd *sd); + + /* Performs a power down sequence */ + int (*power_down)(struct sd *sd); + + /* Reads a sensor register */ + int (*read_sensor)(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + + /* Writes to a sensor register */ + int (*write_sensor)(struct sd *sd, const u8 address, + u8 *i2c_data, const u8 len); + + int nctrls; + struct ctrl ctrls[M5602_MAX_CTRLS]; + + char nmodes; + struct v4l2_pix_format modes[]; +}; + +#endif diff --git a/linux/drivers/media/video/gspca/mars.c b/linux/drivers/media/video/gspca/mars.c index 4d5db47ba..277ca34a8 100644 --- a/linux/drivers/media/video/gspca/mars.c +++ b/linux/drivers/media/video/gspca/mars.c @@ -134,7 +134,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { int err_code; __u8 *data; @@ -143,9 +143,10 @@ static void sd_start(struct gspca_dev *gspca_dev) 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) { + err_code = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 8); + if (err_code < 0) { PDEBUG(D_ERR|D_STREAM, "Set packet size: set interface error"); - return; + return err_code; } data = gspca_dev->usb_buf; @@ -154,7 +155,7 @@ static void sd_start(struct gspca_dev *gspca_dev) err_code = reg_w(gspca_dev, data[0], 2); if (err_code < 0) - return; + return err_code; /* Initialize the MR97113 chip register @@ -180,14 +181,14 @@ static void sd_start(struct gspca_dev *gspca_dev) err_code = reg_w(gspca_dev, data[0], 11); if (err_code < 0) - return; + return err_code; 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; + return err_code; data[0] = 0x3c; /* address */ /* if (gspca_dev->width == 1280) */ @@ -198,7 +199,7 @@ static void sd_start(struct gspca_dev *gspca_dev) * (unit: 4KB) 200KB */ err_code = reg_w(gspca_dev, data[0], 2); if (err_code < 0) - return; + return err_code; if (0) { /* fixed dark-gain */ data[1] = 0; /* reg 94, Y Gain (1.75) */ @@ -240,13 +241,13 @@ static void sd_start(struct gspca_dev *gspca_dev) err_code = reg_w(gspca_dev, data[0], 6); if (err_code < 0) - return; + return err_code; 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; + return err_code; /* * initialize the value of MI sensor... @@ -326,6 +327,7 @@ static void sd_start(struct gspca_dev *gspca_dev) data[0] = 0x00; data[1] = 0x4d; /* ISOC transfering enable... */ reg_w(gspca_dev, data[0], 2); + return err_code; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/ov519.c b/linux/drivers/media/video/gspca/ov519.c index bb2842114..895bcf082 100644 --- a/linux/drivers/media/video/gspca/ov519.c +++ b/linux/drivers/media/video/gspca/ov519.c @@ -1879,7 +1879,7 @@ static int set_ov_sensor_window(struct sd *sd) } /* -- start the camera -- */ -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int ret; @@ -1896,9 +1896,10 @@ static void sd_start(struct gspca_dev *gspca_dev) goto out; PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt); ov51x_led_control(sd, 1); - return; + return 0; out: PDEBUG(D_ERR, "camera start error:%d", ret); + return ret; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/pac207.c b/linux/drivers/media/video/gspca/pac207.c index 34e923d00..0b0c573d0 100644 --- a/linux/drivers/media/video/gspca/pac207.c +++ b/linux/drivers/media/video/gspca/pac207.c @@ -281,7 +281,7 @@ static int sd_init(struct gspca_dev *gspca_dev) } /* -- start the camera -- */ -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u8 mode; @@ -323,6 +323,7 @@ static void sd_start(struct gspca_dev *gspca_dev) sd->sof_read = 0; sd->autogain_ignore_frames = 0; atomic_set(&sd->avg_lum, -1); + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/pac7311.c b/linux/drivers/media/video/gspca/pac7311.c index 296ba82e6..a122634e0 100644 --- a/linux/drivers/media/video/gspca/pac7311.c +++ b/linux/drivers/media/video/gspca/pac7311.c @@ -689,7 +689,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -738,6 +738,7 @@ static void sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x01); else reg_w(gspca_dev, 0x78, 0x05); + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/sonixb.c b/linux/drivers/media/video/gspca/sonixb.c index 53cb724bf..4bae911da 100644 --- a/linux/drivers/media/video/gspca/sonixb.c +++ b/linux/drivers/media/video/gspca/sonixb.c @@ -922,7 +922,7 @@ static int sd_init(struct gspca_dev *gspca_dev) } /* -- start the camera -- */ -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam = &gspca_dev->cam; @@ -1006,6 +1006,7 @@ static void sd_start(struct gspca_dev *gspca_dev) sd->frames_to_drop = 0; sd->autogain_ignore_frames = 0; atomic_set(&sd->avg_lum, -1); + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index a77164581..f68faf6bd 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -144,7 +144,7 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define VFLIP_DEF 0 +#define VFLIP_DEF 1 .default_value = VFLIP_DEF, }, .set = sd_setvflip, @@ -241,17 +241,6 @@ static const __u8 sn_ov7648[] = { }; static const __u8 sn_ov7660[] = { -#if 0 -/*jfm: from win trace */ -/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, -/* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ - 0x03, 0x00, 0x01, 0x01, 0x14, 0x28, 0x1e, 0x00, -/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -#else /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ @@ -260,7 +249,6 @@ static const __u8 sn_ov7660[] = { 0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20, /* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -#endif }; /* sequence specific to the sensors - !! index = SENSOR_xxx */ @@ -279,14 +267,12 @@ static const __u8 gamma_def[] = { 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff }; +/* color matrix and offsets */ static const __u8 reg84[] = { - 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe5, 0x0f, - 0xe4, 0x0f, 0x38, 0x00, 0x3e, 0x00, 0xc3, 0x0f, -#if 1 - 0xf7, 0x0f, 0x00, 0x00, 0x00 -#else - 0xf7, 0x0f, 0x0a, 0x00, 0x00 -#endif + 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, /* YR YG YB gains */ + 0xe8, 0x0f, 0xda, 0x0f, 0x40, 0x00, /* UR UG UB */ + 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */ + 0x00, 0x00, 0x00 /* YUV offsets */ }; static const __u8 hv7131r_sensor_init[][8] = { {0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, @@ -743,6 +729,7 @@ static void i2c_w8(struct gspca_dev *gspca_dev, 0x08, 0, /* value, index */ gspca_dev->usb_buf, 8, 500); + msleep(2); } /* read 5 bytes in gspca_dev->usb_buf */ @@ -1022,13 +1009,13 @@ static int sd_init(struct gspca_dev *gspca_dev) case BRIDGE_SN9C105: if (regF1 != 0x11) return -ENODEV; - reg_w(gspca_dev, 0x02, regGpio, 2); + reg_w(gspca_dev, 0x01, regGpio, 2); break; case BRIDGE_SN9C120: if (regF1 != 0x12) return -ENODEV; regGpio[1] = 0x70; - reg_w(gspca_dev, 0x02, regGpio, 2); + reg_w(gspca_dev, 0x01, regGpio, 2); break; default: /* case BRIDGE_SN9C110: */ @@ -1130,20 +1117,17 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev, static void setbrightcont(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - unsigned val; + int val; __u8 reg84_full[0x15]; - memset(reg84_full, 0, sizeof reg84_full); - val = sd->contrast * 0x20 / CONTRAST_MAX + 0x10; /* 10..30 */ - reg84_full[2] = val; - reg84_full[0] = (val + 1) / 2; - reg84_full[4] = (val + 1) / 5; - if (val > BRIGHTNESS_DEF) - val = (sd->brightness - BRIGHTNESS_DEF) * 0x20 + memcpy(reg84_full, reg84, sizeof reg84_full); + val = sd->contrast * 0x30 / CONTRAST_MAX + 0x10; /* 10..40 */ + reg84_full[0] = (val + 1) / 2; /* red */ + reg84_full[2] = val; /* green */ + reg84_full[4] = (val + 1) / 5; /* blue */ + val = (sd->brightness - BRIGHTNESS_DEF) * 0x10 / BRIGHTNESS_MAX; - else - val = 0; - reg84_full[0x12] = val; /* 00..1f */ + reg84_full[0x12] = val & 0x1f; /* 5:0 signed value */ reg_w(gspca_dev, 0x84, reg84_full, sizeof reg84_full); } @@ -1231,7 +1215,7 @@ static void setvflip(struct sd *sd) } /* -- start the camera -- */ -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; @@ -1242,7 +1226,7 @@ static void sd_start(struct gspca_dev *gspca_dev) static const __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; static const __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ static const __u8 CE_ov76xx[] = - { 0x32, 0xdd, 0x32, 0xdd }; /* OV7630/48 */ + { 0x32, 0xdd, 0x32, 0xdd }; sn9c1xx = sn_tb[(int) sd->sensor]; configure_gpio(gspca_dev, sn9c1xx); @@ -1284,20 +1268,15 @@ static void sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x20, gamma_def, sizeof gamma_def); for (i = 0; i < 8; i++) reg_w(gspca_dev, 0x84, reg84, sizeof reg84); -#if 0 - switch (sd->bridge) { - case BRIDGE_SN9C325: - reg_w1(gspca_dev, 0x9a, 0x0a); - reg_w1(gspca_dev, 0x99, 0x60); + switch (sd->sensor) { + case SENSOR_OV7660: + reg_w1(gspca_dev, 0x9a, 0x05); break; default: -#endif reg_w1(gspca_dev, 0x9a, 0x08); reg_w1(gspca_dev, 0x99, 0x59); -#if 0 break; } -#endif mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; if (mode) @@ -1349,14 +1328,8 @@ static void sd_start(struct gspca_dev *gspca_dev) /* reg1 = 0x44; */ /* reg1 = 0x46; (done) */ } else { -#if 0 -/*jfm: from win trace */ reg17 = 0xa2; /* 640 */ - reg1 = 0x40; -#else - reg17 = 0x22; /* 640 MCKSIZE */ - reg1 = 0x06; -#endif + reg1 = 0x44; } break; } @@ -1365,6 +1338,7 @@ static void sd_start(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_OV7630: case SENSOR_OV7648: + case SENSOR_OV7660: reg_w(gspca_dev, 0xce, CE_ov76xx, 4); break; default: @@ -1397,6 +1371,7 @@ static void sd_start(struct gspca_dev *gspca_dev) } setautogain(gspca_dev); reg_w1(gspca_dev, 0x01, reg1); + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) @@ -1677,7 +1652,9 @@ static const __devinitdata struct usb_device_id device_table[] = { /* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */ {USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)}, -/* {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x??)}, */ +#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE + {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)}, +#endif /* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */ /* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */ /* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */ diff --git a/linux/drivers/media/video/gspca/spca500.c b/linux/drivers/media/video/gspca/spca500.c index c33bfe48c..a3f616709 100644 --- a/linux/drivers/media/video/gspca/spca500.c +++ b/linux/drivers/media/video/gspca/spca500.c @@ -677,7 +677,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int err; @@ -884,6 +884,7 @@ static void sd_start(struct gspca_dev *gspca_dev) write_vector(gspca_dev, Clicksmart510_defaults); break; } + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/spca501.c b/linux/drivers/media/video/gspca/spca501.c index 49e512cac..979344907 100644 --- a/linux/drivers/media/video/gspca/spca501.c +++ b/linux/drivers/media/video/gspca/spca501.c @@ -1991,7 +1991,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; int mode; @@ -2023,6 +2023,7 @@ static void sd_start(struct gspca_dev *gspca_dev) setbrightness(gspca_dev); setcontrast(gspca_dev); setcolors(gspca_dev); + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/spca505.c b/linux/drivers/media/video/gspca/spca505.c index e33666df4..62bab3cc1 100644 --- a/linux/drivers/media/video/gspca/spca505.c +++ b/linux/drivers/media/video/gspca/spca505.c @@ -757,7 +757,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; int ret; @@ -802,6 +802,7 @@ static void sd_start(struct gspca_dev *gspca_dev) /* reg_write(dev, 0x5, 0x0, 0x0); */ /* reg_write(dev, 0x5, 0x0, 0x1); */ /* reg_write(dev, 0x5, 0x11, 0x2); */ + return ret; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/spca506.c b/linux/drivers/media/video/gspca/spca506.c index 195dce96e..645ee9d44 100644 --- a/linux/drivers/media/video/gspca/spca506.c +++ b/linux/drivers/media/video/gspca/spca506.c @@ -422,7 +422,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; __u16 norme; @@ -549,6 +549,7 @@ static void sd_start(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "webcam started"); spca506_GetNormeInput(gspca_dev, &norme, &channel); spca506_SetNormeInput(gspca_dev, norme, channel); + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/spca508.c b/linux/drivers/media/video/gspca/spca508.c index 5e3f66ae2..c9360cdb8 100644 --- a/linux/drivers/media/video/gspca/spca508.c +++ b/linux/drivers/media/video/gspca/spca508.c @@ -1566,7 +1566,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { int mode; @@ -1584,6 +1584,7 @@ static void sd_start(struct gspca_dev *gspca_dev) break; } reg_write(gspca_dev->dev, 0x8112, 0x10 | 0x20); + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index b1329a149..5b65dd66b 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -152,7 +152,7 @@ static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value) ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0, /* request */ - USB_TYPE_VENDOR | USB_RECIP_DEVICE, + USB_DIR_OUT | 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) @@ -225,7 +225,7 @@ static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) reg_w_val(gspca_dev->dev, 0x8802, (mode | 0x01)); do { reg_r(gspca_dev, 0x8803, 1); - if (!gspca_dev->usb_buf) + if (!gspca_dev->usb_buf[0]) break; } while (--retry); if (retry == 0) @@ -713,7 +713,7 @@ static void setautogain(struct gspca_dev *gspca_dev) sd->ag_cnt = -1; } -static void sd_start_12a(struct gspca_dev *gspca_dev) +static int sd_start_12a(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; int Clck = 0x8a; /* lower 0x8X values lead to fps > 30 */ @@ -739,8 +739,9 @@ static void sd_start_12a(struct gspca_dev *gspca_dev) setwhite(gspca_dev); setautogain(gspca_dev); setexposure(gspca_dev); + return 0; } -static void sd_start_72a(struct gspca_dev *gspca_dev) +static int sd_start_72a(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; int Clck; @@ -764,6 +765,7 @@ static void sd_start_72a(struct gspca_dev *gspca_dev) reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ reg_w_val(dev, 0x8112, 0x10 | 0x20); setautogain(gspca_dev); + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) @@ -1078,7 +1080,7 @@ static struct ctrl sd_ctrls_12a[] = { { .id = V4L2_CID_DO_WHITE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "While Balance", + .name = "White Balance", .minimum = WHITE_MIN, .maximum = WHITE_MAX, .step = 1, diff --git a/linux/drivers/media/video/gspca/stk014.c b/linux/drivers/media/video/gspca/stk014.c index 2f2de429e..d9d64911f 100644 --- a/linux/drivers/media/video/gspca/stk014.c +++ b/linux/drivers/media/video/gspca/stk014.c @@ -324,7 +324,7 @@ static int sd_init(struct gspca_dev *gspca_dev) } /* -- start the camera -- */ -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { int ret, value; @@ -374,9 +374,10 @@ static void sd_start(struct gspca_dev *gspca_dev) set_par(gspca_dev, 0x01000000); set_par(gspca_dev, 0x01000000); PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt); - return; + return 0; out: PDEBUG(D_ERR|D_STREAM, "camera start err %d", ret); + return ret; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/sunplus.c b/linux/drivers/media/video/gspca/sunplus.c index e8e5c47ba..3a972ff1c 100644 --- a/linux/drivers/media/video/gspca/sunplus.c +++ b/linux/drivers/media/video/gspca/sunplus.c @@ -992,7 +992,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; @@ -1073,6 +1073,7 @@ static void sd_start(struct gspca_dev *gspca_dev) break; } sp5xx_initContBrigHueRegisters(gspca_dev); + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/t613.c b/linux/drivers/media/video/gspca/t613.c index c7673661a..06f3b8718 100644 --- a/linux/drivers/media/video/gspca/t613.c +++ b/linux/drivers/media/video/gspca/t613.c @@ -868,7 +868,7 @@ static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { int mode; @@ -921,6 +921,7 @@ static void sd_start(struct gspca_dev *gspca_dev) seteffect(gspca_dev); setflip(gspca_dev); #endif + return 0; } static void sd_pkt_scan(struct gspca_dev *gspca_dev, @@ -938,7 +939,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, return; } - if (data[len - 1] == 0xff && data[len] == 0xd9) { + if (data[len - 2] == 0xff && data[len - 1] == 0xd9) { /* Just in case, i have seen packets with the marker, * other's do not include it... */ data += 2; diff --git a/linux/drivers/media/video/gspca/tv8532.c b/linux/drivers/media/video/gspca/tv8532.c index 01de48d4e..8204dabb1 100644 --- a/linux/drivers/media/video/gspca/tv8532.c +++ b/linux/drivers/media/video/gspca/tv8532.c @@ -390,7 +390,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) } /* -- start the camera -- */ -static void sd_start(struct gspca_dev *gspca_dev) +static int 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); @@ -443,6 +443,7 @@ static void sd_start(struct gspca_dev *gspca_dev) /************************************************/ tv_8532_PollReg(gspca_dev); reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */ + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index cfb66c257..95531138e 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -1698,7 +1698,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]); } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; const __u8 *GammaT = NULL; @@ -1782,7 +1782,7 @@ static void sd_start(struct gspca_dev *gspca_dev) break; default: PDEBUG(D_PROBE, "Damned !! no sensor found Bye"); - return; + return -EMEDIUMTYPE; } if (GammaT && MatrixT) { put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a); @@ -1818,6 +1818,7 @@ static void sd_start(struct gspca_dev *gspca_dev) setautogain(gspca_dev); setlightfreq(gspca_dev); } + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c index 3153bd4cd..0b1871cec 100644 --- a/linux/drivers/media/video/gspca/zc3xx.c +++ b/linux/drivers/media/video/gspca/zc3xx.c @@ -7183,7 +7183,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static void sd_start(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; @@ -7336,6 +7336,7 @@ static void sd_start(struct gspca_dev *gspca_dev) reg_w(dev, 0x02, 0x0008); break; } + return 0; } static void sd_stop0(struct gspca_dev *gspca_dev) diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 9280fb314..135250cf5 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -3299,6 +3299,7 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_HAUPPAUGE_HVR1110] = { /* Thomas Genty <tomlohave@gmail.com> */ + /* David Bentham <db260179@hotmail.com> */ .name = "Hauppauge WinTV-HVR1110 DVB-T/Hybrid", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, @@ -3307,23 +3308,26 @@ struct saa7134_board saa7134_boards[] = { .radio_addr = ADDR_UNSET, .tuner_config = 1, .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200100, .inputs = {{ .name = name_tv, .vmux = 1, .amux = TV, .tv = 1, - },{ - .name = name_comp1, - .vmux = 3, - .amux = LINE2, /* FIXME: audio doesn't work on svideo/composite */ - },{ - .name = name_svideo, - .vmux = 8, - .amux = LINE2, /* FIXME: audio doesn't work on svideo/composite */ - }}, + .gpio = 0x0000100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, .radio = { .name = name_radio, - .amux = TV, + .amux = TV, + .gpio = 0x0200100, }, }, [SAA7134_BOARD_CINERGY_HT_PCMCIA] = { @@ -3427,6 +3431,42 @@ struct saa7134_board saa7134_boards[] = { .amux = 0, }, }, + [SAA7134_BOARD_ENCORE_ENLTV_FM53] = { + .name = "Encore ENLTV-FM v5.3", + .audio_clock = 0x00200000, + .tuner_type = TUNER_TNF_5335MF, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x7000, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = 1, + .tv = 1, + .gpio = 0x50000, + }, { + .name = name_comp1, + .vmux = 3, + .amux = 2, + .gpio = 0x2000, + }, { + .name = name_svideo, + .vmux = 8, + .amux = 2, + .gpio = 0x2000, + } }, + .radio = { + .name = name_radio, + .vmux = 1, + .amux = 1, + }, + .mute = { + .name = name_mute, + .gpio = 0xf000, + .amux = 0, + }, + }, [SAA7134_BOARD_CINERGY_HT_PCI] = { .name = "Terratec Cinergy HT PCI", .audio_clock = 0x00187de7, @@ -3670,6 +3710,40 @@ struct saa7134_board saa7134_boards[] = { .tv = 1, }}, }, + [SAA7134_BOARD_AVERMEDIA_M135A] = { + .name = "Avermedia PCI pure analog (M135A)", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .gpiomask = 0x020200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x00200000, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x01, + }, + }, [SAA7134_BOARD_BEHOLD_401] = { /* Beholder Intl. Ltd. 2008 */ /*Dmitry Belimov <d.belimov@gmail.com> */ @@ -4478,6 +4552,65 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200000, }, }, + [SAA7134_BOARD_REAL_ANGEL_220] = { + .name = "Zogis Real Angel 220", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_TNF_5335MF, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x801a8087, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + .gpio = 0x624000, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + .gpio = 0x624000, + }, { + .name = name_svideo, + .vmux = 1, + .amux = LINE1, + .gpio = 0x624000, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x624001, + }, + .mute = { + .name = name_mute, + .amux = TV, + }, + }, + [SAA7134_BOARD_ADS_INSTANT_HDTV_PCI] = { + .name = "ADS Tech Instant HDTV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TUV1236D, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp, + .vmux = 4, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -4846,6 +4979,12 @@ struct pci_device_id saa7134_pci_tbl[] = { },{ .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf11d, + .driver_data = SAA7134_BOARD_AVERMEDIA_M135A, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, .subvendor = PCI_VENDOR_ID_PHILIPS, .subdevice = 0x2004, @@ -5226,6 +5365,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM, },{ .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1a7f, + .subdevice = 0x2008, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM53, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x153b, .subdevice = 0x1175, @@ -5484,6 +5629,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .driver_data = SAA7134_BOARD_VIDEOMATE_T750, }, { .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ + .subvendor = 0x1421, + .subdevice = 0x0380, + .driver_data = SAA7134_BOARD_ADS_INSTANT_HDTV_PCI, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x5169, .subdevice = 0x1502, @@ -5755,6 +5906,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_STUDIO_507: case SAA7134_BOARD_AVERMEDIA_GO_007_FM: case SAA7134_BOARD_AVERMEDIA_777: + case SAA7134_BOARD_AVERMEDIA_M135A: /* case SAA7134_BOARD_SABRENT_SBTTVFM: */ /* not finished yet */ case SAA7134_BOARD_VIDEOMATE_TV_PVR: case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: @@ -5779,6 +5931,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_A16AR: case SAA7134_BOARD_ENCORE_ENLTV: case SAA7134_BOARD_ENCORE_ENLTV_FM: + case SAA7134_BOARD_ENCORE_ENLTV_FM53: case SAA7134_BOARD_10MOONSTVMASTER3: case SAA7134_BOARD_BEHOLD_401: case SAA7134_BOARD_BEHOLD_403: @@ -5791,6 +5944,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_505FM: case SAA7134_BOARD_BEHOLD_507_9FM: case SAA7134_BOARD_GENIUS_TVGO_A11MCE: + case SAA7134_BOARD_REAL_ANGEL_220: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -6174,6 +6328,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) i2c_transfer(&dev->i2c_adap, &msg, 1); break; } + case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI: case SAA7134_BOARD_KWORLD_ATSC110: { /* enable tuner */ diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index ce8bcc967..c839114a0 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -760,6 +760,10 @@ static int saa7134_hw_enable2(struct saa7134_dev *dev) irq2_mask |= SAA7134_IRQ2_INTE_GPIO18A; } + if (dev->has_remote == SAA7134_REMOTE_I2C) { + request_module("ir-kbd-i2c"); + } + saa_writel(SAA7134_IRQ1, 0); saa_writel(SAA7134_IRQ2, irq2_mask); diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 4fb5c0d41..5222f1728 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -1156,6 +1156,7 @@ static int dvb_init(struct saa7134_dev *dev) dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, NULL, DVB_PLL_TDHU2); break; + case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI: case SAA7134_BOARD_KWORLD_ATSC110: dev->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110, &dev->i2c_adap); diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c index a9f6df2dd..cb93661fb 100644 --- a/linux/drivers/media/video/saa7134/saa7134-input.c +++ b/linux/drivers/media/video/saa7134/saa7134-input.c @@ -401,6 +401,12 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPMODE0, 0x4); saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4); break; + case SAA7134_BOARD_AVERMEDIA_M135A: + ir_codes = ir_codes_avermedia_m135a; + mask_keydown = 0x0040000; + mask_keycode = 0x00013f; + nec_gpio = 1; + break; case SAA7134_BOARD_AVERMEDIA_777: case SAA7134_BOARD_AVERMEDIA_A16AR: ir_codes = ir_codes_avermedia; @@ -509,6 +515,12 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keyup = 0x040000; polling = 50; // ms break; + case SAA7134_BOARD_ENCORE_ENLTV_FM53: + ir_codes = ir_codes_encore_enltv_fm53; + mask_keydown = 0x0040000; + mask_keycode = 0x00007f; + nec_gpio = 1; + break; case SAA7134_BOARD_10MOONSTVMASTER3: ir_codes = ir_codes_encore_enltv; mask_keycode = 0x5f80000; @@ -521,6 +533,12 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keydown = 0xf00000; polling = 50; /* ms */ break; + case SAA7134_BOARD_REAL_ANGEL_220: + ir_codes = ir_codes_real_audio_220_32_keys; + mask_keycode = 0x3f00; + mask_keyup = 0x4000; + polling = 50; /* ms */ + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 1e7fcf3fd..b704b5f40 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -271,6 +271,10 @@ struct saa7134_format { #define SAA7134_BOARD_AVERMEDIA_M103 145 #define SAA7134_BOARD_ASUSTeK_P7131_ANALOG 146 #define SAA7134_BOARD_ASUSTeK_TIGER_3IN1 147 +#define SAA7134_BOARD_ENCORE_ENLTV_FM53 148 +#define SAA7134_BOARD_AVERMEDIA_M135A 149 +#define SAA7134_BOARD_REAL_ANGEL_220 150 +#define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 |