From 8d420c25b3747f612563ce8706add4ffb84be126 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 5 Sep 2008 14:33:54 -0300 Subject: Pinnacle Hybrid PCTV Pro (pctv310c) DVB-T support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Stéphane Voltz This patch against latest mercurial makes DVB-T working on Pinnacle Hybrid PCTV Pro (pctv310c). In cx88-dvb.c, a specific zl10353_config is created with the if2 inferred from the old comment in the currently used config. It is then used for attach, and i2c_gate_ctrl is set to NULL. The entry in cx88-cards.c is modified with GPIO gathered from windows with regspy, and DVB enabled. The frontend is set to XC3028_FE_ZARLINK456 to match the zl10353_config. It is working great with the freeview channels I can receive. Signed-off-by: Stéphane Voltz Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/cx88/cx88-cards.c | 18 +++++++++++++++--- linux/drivers/media/video/cx88/cx88-dvb.c | 15 ++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 1fd7782fd..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", @@ -2708,6 +2716,10 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) * 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; -- cgit v1.2.3 From 18e48d2b209435d81fc598fefd3df73bcecee702 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 10 Sep 2008 07:57:09 +0200 Subject: gspca: sonixj webcam 0c45:60fe added. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index a77164581..7bc6ac2bc 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1677,7 +1677,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??)}, */ -- cgit v1.2.3 From e7cd79e8f08b3f9fc578bd92afc58117049774d8 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 14 Sep 2008 09:17:46 +0200 Subject: gspca: Bad check of returned status in i2c_read() spca561. From: Shane This makes auto gain functional on 04fc:0561. Priority: high Signed-off-by: Shane Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/spca561.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index 0250ee96d..910c550cd 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -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) -- cgit v1.2.3 From 58e2c9ce366522dbee31bb65dcb5fd662d3a5309 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 15 Sep 2008 09:18:51 +0200 Subject: gspca: USB direction lacking in spca561. From: Shane Priority: normal Signed-off-by: Shane Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/spca561.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index 910c550cd..81b9866c0 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) -- cgit v1.2.3 From a753269dec3b227332dad3285386c0f37c6f6240 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 15 Sep 2008 10:20:38 +0200 Subject: gspca: Set the right V4L2_DEBUG values in the main driver. From: Frank Zago Priority: normal Signed-off-by: Frank Zago Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 8c98c7a05..5d7e56d9e 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -853,9 +853,11 @@ static int dev_open(struct inode *inode, struct file *file) #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: -- cgit v1.2.3 From 7b960fac24dbec9cd02cba8d72415391e2d44556 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 20 Sep 2008 10:44:21 +0200 Subject: gspca: New exported function to retrieve the current frame buffer. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 33 +++++++++++++++++++++------------ linux/drivers/media/video/gspca/gspca.h | 1 + 2 files changed, 22 insertions(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 5d7e56d9e..c9bcc8098 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -106,6 +106,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 +130,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 +144,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 +208,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 +221,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); diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 78a35a88c..72a288573 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -178,6 +178,7 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, 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); -- cgit v1.2.3 From 4da45e91dbbd552ebcee4d48b856c66e6ca9f839 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 20 Sep 2008 11:39:08 +0200 Subject: gspca: Return error code from stream start functions. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/conex.c | 3 ++- linux/drivers/media/video/gspca/etoms.c | 3 ++- linux/drivers/media/video/gspca/gspca.c | 6 +++++- linux/drivers/media/video/gspca/gspca.h | 2 +- linux/drivers/media/video/gspca/mars.c | 20 +++++++++++--------- linux/drivers/media/video/gspca/ov519.c | 5 +++-- linux/drivers/media/video/gspca/pac207.c | 3 ++- linux/drivers/media/video/gspca/pac7311.c | 3 ++- linux/drivers/media/video/gspca/sonixb.c | 3 ++- linux/drivers/media/video/gspca/sonixj.c | 3 ++- linux/drivers/media/video/gspca/spca500.c | 3 ++- linux/drivers/media/video/gspca/spca501.c | 3 ++- linux/drivers/media/video/gspca/spca505.c | 3 ++- linux/drivers/media/video/gspca/spca506.c | 3 ++- linux/drivers/media/video/gspca/spca508.c | 3 ++- linux/drivers/media/video/gspca/spca561.c | 6 ++++-- linux/drivers/media/video/gspca/stk014.c | 5 +++-- linux/drivers/media/video/gspca/sunplus.c | 3 ++- linux/drivers/media/video/gspca/t613.c | 3 ++- linux/drivers/media/video/gspca/tv8532.c | 3 ++- linux/drivers/media/video/gspca/vc032x.c | 5 +++-- linux/drivers/media/video/gspca/zc3xx.c | 3 ++- 22 files changed, 60 insertions(+), 34 deletions(-) (limited to 'linux') 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/gspca.c b/linux/drivers/media/video/gspca/gspca.c index c9bcc8098..1e710345f 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -594,7 +594,11 @@ 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); diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 72a288573..b0bdae194 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -93,7 +93,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 */ 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 7bc6ac2bc..27a89dcf6 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -1231,7 +1231,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; @@ -1397,6 +1397,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) 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 81b9866c0..02f4ac274 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -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) 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..665a783aa 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, 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) -- cgit v1.2.3 From 8f68a062bef2680b44e463181bdcccc0c81f5934 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 21 Sep 2008 08:28:55 +0200 Subject: gspca: Add a delay after writing to the sonixj sensors. From: Jean-Francois Moine Priority: high Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 1 + 1 file changed, 1 insertion(+) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 27a89dcf6..0a492260d 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -743,6 +743,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 */ -- cgit v1.2.3 From 0230e0f17d4f6d95b6579a7af451022018fe9bc3 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 22 Sep 2008 07:57:56 +0200 Subject: gspca: Bad webcam name of 046d:092f in documentation. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index 308ce4ee2..a845667ac 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -82,7 +82,7 @@ spca561 046d:092b Labtec Webcam Plus spca561 046d:092c Logitech QC chat Elch2 spca561 046d:092d Logitech QC Elch2 spca561 046d:092e Logitech QC Elch2 -spca561 046d:092f Logitech QC Elch2 +spca561 046d:092f Logitech QuickCam Express Plus sunplus 046d:0960 Logitech ClickSmart 420 sunplus 0471:0322 Philips DMVC1300K zc3xx 0471:0325 Philips SPC 200 NC -- cgit v1.2.3 From a678015363153bdbdf1729f02a1e0be0c2ef6137 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 22 Sep 2008 08:14:25 +0200 Subject: gspca: Vertical flip the image by default in sonixj. From: Jean-Francois Moine Priority: high Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 0a492260d..4cad52769 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, -- cgit v1.2.3 From b9e694c843a9f52b30c41427c2a28be34e252d22 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Thu, 25 Sep 2008 04:48:53 +0300 Subject: Kconfig correction for USB card modification with SI2109/2110 demodulator. From: Igor M. Liplianin Kconfig correction for USB card modification with SI2109/2110 demodulator. Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/dvb-usb/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig index aee3b5dd6..0131424e1 100644 --- a/linux/drivers/media/dvb/dvb-usb/Kconfig +++ b/linux/drivers/media/dvb/dvb-usb/Kconfig @@ -252,6 +252,7 @@ config DVB_USB_DW2102 select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_CX24116 if !DVB_FE_CUSTOMISE + select DVB_SI21XX if !DVB_FE_CUSTOMISE help Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers and the TeVii S650. -- cgit v1.2.3 From 90c4877707444823952b5c1bf995395b73e0c0c4 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Thu, 25 Sep 2008 05:18:27 +0300 Subject: Kconfig dependency fix for DW2002 card with ST STV0288 demodulator. From: Igor M. Liplianin Kconfig dependency fix for DW2002 card with ST STV0288 demodulator. Signed-off-by: Igor M. Liplianin --- linux/drivers/media/dvb/dm1105/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dm1105/Kconfig b/linux/drivers/media/dvb/dm1105/Kconfig index 2af5fffe1..1332301ef 100644 --- a/linux/drivers/media/dvb/dm1105/Kconfig +++ b/linux/drivers/media/dvb/dm1105/Kconfig @@ -3,6 +3,8 @@ config DVB_DM1105 depends on DVB_CORE && PCI && I2C select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_STV0288 if !DVB_FE_CUSTOMISE + select DVB_STB6000 if !DVB_FE_CUSTOMISE select DVB_CX24116 if !DVB_FE_CUSTOMISE select DVB_SI21XX if !DVB_FE_CUSTOMISE help -- cgit v1.2.3 From 607559123926c97eda4e7f7920ddd392408fd1e3 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 25 Sep 2008 15:51:11 -0400 Subject: cx88: Bugfix: all client disconnects put the frontend to sleep. From: Darron Broad From the author: "This fixes the problem where previously all client disconnects put the analogue frontend into standby. In the following example, the first command is succesfully watching TV but the second command which returns EBUSY detunes the receiver by entering it into the standby state. tvtime -d /dev/video0 & cat /dev/video0 " Priority: normal Signed-off-by: Steven Toth Signed-off-by: Darron Broad --- linux/drivers/media/video/cx88/cx88-video.c | 5 ++++- linux/drivers/media/video/cx88/cx88.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'linux') 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 238370681..38702eb4f 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -352,6 +352,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; -- cgit v1.2.3 From dd77367427f2ae6d28b7038885cb6e4d4a64631a Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 25 Sep 2008 23:04:52 -0400 Subject: S2API: Removed the typedef for the commands, used defines instead. From: Steven Toth Priority: normal Signed-off-by: Steven Toth --- linux/include/linux/dvb/frontend.h | 114 +++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 63 deletions(-) (limited to 'linux') diff --git a/linux/include/linux/dvb/frontend.h b/linux/include/linux/dvb/frontend.h index 6793deada..9c5d64864 100644 --- a/linux/include/linux/dvb/frontend.h +++ b/linux/include/linux/dvb/frontend.h @@ -246,71 +246,59 @@ struct dvb_frontend_event { struct dvb_frontend_parameters parameters; }; -/* TODO: Turn this into a series of defines, so future maintainers - * don't insert random new commands and break backwards - * binary compatability. - */ -typedef enum dtv_cmd_types { - DTV_UNDEFINED, - DTV_TUNE, - DTV_CLEAR, - - DTV_FREQUENCY, - DTV_MODULATION, - - /* XXX PB: I would like to have field which describes the - * bandwidth of a channel in Hz or kHz - maybe we can remove the - * DTV_BANDWIDTH now and put a compat layer */ - DTV_BANDWIDTH_HZ, - - DTV_INVERSION, - DTV_DISEQC_MASTER, - DTV_SYMBOL_RATE, - DTV_INNER_FEC, - DTV_VOLTAGE, - DTV_TONE, - DTV_PILOT, - DTV_ROLLOFF, - - DTV_DISEQC_SLAVE_REPLY, - - /* Basic enumeration set for querying unlimited capabilities */ - DTV_FE_CAPABILITY_COUNT, - DTV_FE_CAPABILITY, - - /* New commands are always appended */ - DTV_DELIVERY_SYSTEM, +/* S2API Commands */ +#define DTV_UNDEFINED 0 +#define DTV_TUNE 1 +#define DTV_CLEAR 2 +#define DTV_FREQUENCY 3 +#define DTV_MODULATION 4 +#define DTV_BANDWIDTH_HZ 5 +#define DTV_INVERSION 6 +#define DTV_DISEQC_MASTER 7 +#define DTV_SYMBOL_RATE 8 +#define DTV_INNER_FEC 9 +#define DTV_VOLTAGE 10 +#define DTV_TONE 11 +#define DTV_PILOT 12 +#define DTV_ROLLOFF 13 +#define DTV_DISEQC_SLAVE_REPLY 14 + +/* Basic enumeration set for querying unlimited capabilities */ +#define DTV_FE_CAPABILITY_COUNT 15 +#define DTV_FE_CAPABILITY 16 +#define DTV_DELIVERY_SYSTEM 17 + #if 0 - /* ISDB */ - /* maybe a dup of DTV_ISDB_SOUND_BROADCASTING_SUBCHANNEL_ID ??? */ - DTV_ISDB_SEGMENT_IDX, - DTV_ISDB_SEGMENT_WIDTH, /* 1, 3 or 13 ??? */ - - /* the central segment can be received independently or 1/3 seg in SB-mode */ - DTV_ISDB_PARTIAL_RECEPTION, - /* sound broadcasting is used 0 = 13segment, 1 = 1 or 3 see DTV_ISDB_PARTIAL_RECEPTION */ - DTV_ISDB_SOUND_BROADCASTING, - - /* only used in SB */ - /* determines the initial PRBS of the segment (to match with 13seg channel) */ - DTV_ISDB_SOUND_BROADCASTING_SUBCHANNEL_ID, - - DTV_ISDB_LAYERA_FEC, - DTV_ISDB_LAYERA_MODULATION, - DTV_ISDB_LAYERA_SEGMENT_WIDTH, - DTV_ISDB_LAYERA_TIME_INTERLEAVER, - - DTV_ISDB_LAYERB_FEC, - DTV_ISDB_LAYERB_MODULATION, - DTV_ISDB_LAYERB_SEGMENT_WIDTH, - DTV_ISDB_LAYERB_TIME_INTERLEAVING, - - DTV_ISDB_LAYERC_FEC, - DTV_ISDB_LAYERC_MODULATION, - DTV_ISDB_LAYERC_SEGMENT_WIDTH, - DTV_ISDB_LAYERC_TIME_INTERLEAVING, +/* ISDB */ +/* maybe a dup of DTV_ISDB_SOUND_BROADCASTING_SUBCHANNEL_ID ??? */ +#define DTV_ISDB_SEGMENT_IDX 18 +/* 1, 3 or 13 ??? */ +#define DTV_ISDB_SEGMENT_WIDTH 19 + +/* the central segment can be received independently or 1/3 seg in SB-mode */ +#define DTV_ISDB_PARTIAL_RECEPTION 20 +/* sound broadcasting is used 0 = 13segment, 1 = 1 or 3 see DTV_ISDB_PARTIAL_RECEPTION */ +#define DTV_ISDB_SOUND_BROADCASTING 21 + +/* only used in SB */ +/* determines the initial PRBS of the segment (to match with 13seg channel) */ +#define DTV_ISDB_SOUND_BROADCASTING_SUBCHANNEL_ID 22 + +#define DTV_ISDB_LAYERA_FEC 23 +#define DTV_ISDB_LAYERA_MODULATION 24 +#define DTV_ISDB_LAYERA_SEGMENT_WIDTH 25 +#define DTV_ISDB_LAYERA_TIME_INTERLEAVER 26 + +#define DTV_ISDB_LAYERB_FEC 27 +#define DTV_ISDB_LAYERB_MODULATION 28 +#define DTV_ISDB_LAYERB_SEGMENT_WIDTH 29 +#define DTV_ISDB_LAYERB_TIME_INTERLEAVING 30 + +#define DTV_ISDB_LAYERC_FEC 31 +#define DTV_ISDB_LAYERC_MODULATION 32 +#define DTV_ISDB_LAYERC_SEGMENT_WIDTH 33 +#define DTV_ISDB_LAYERC_TIME_INTERLEAVING 34 #endif -} dtv_cmd_types_t; typedef enum fe_pilot { PILOT_ON, -- cgit v1.2.3 From 506cfbf07f4322d8a43fd8bd0055c142db5cf615 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 25 Sep 2008 23:16:25 -0400 Subject: S2API: Implement result codes for individual commands From: Steven Toth This allows application developers to determine which particular command in a sequence is invalid, or failing with error. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 12 ++++++++---- linux/include/linux/dvb/frontend.h | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 5c1193524..9f3a61827 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1377,8 +1377,10 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, goto out; } - for (i = 0; i < tvps->num; i++) - err |= dtv_property_process_set(fe, tvp + i, inode, file); + for (i = 0; i < tvps->num; i++) { + (tvp + i)->result = dtv_property_process_set(fe, tvp + i, inode, file); + err |= (tvp + i)->result; + } if(fe->dtv_property_cache.state == DTV_TUNE) { printk("%s() Property cache is full, tuning\n", __FUNCTION__); @@ -1410,8 +1412,10 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, goto out; } - for (i = 0; i < tvps->num; i++) - err |= dtv_property_process_get(fe, tvp + i, inode, file); + for (i = 0; i < tvps->num; i++) { + (tvp + i)->result = dtv_property_process_get(fe, tvp + i, inode, file); + err |= (tvp + i)->result; + } if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) { err = -EFAULT; diff --git a/linux/include/linux/dvb/frontend.h b/linux/include/linux/dvb/frontend.h index 9c5d64864..ecac80d32 100644 --- a/linux/include/linux/dvb/frontend.h +++ b/linux/include/linux/dvb/frontend.h @@ -354,6 +354,7 @@ struct dtv_property { void *reserved2; } buffer; } u; + int result; } __attribute__ ((packed)); /* No more than 16 properties during any given ioctl */ -- cgit v1.2.3 From 1bded05663a0aea1702e531a19694c6b670dc7d2 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 25 Sep 2008 23:29:49 -0400 Subject: S2API: Add DTV_API_VERSION command From: Steven Toth This allows application developers to query the dvb-core API version dynamically, helping developers understand whether certain features will be available. Priority: normal Signed-off-by: Steven Toth --- linux/drivers/media/dvb/dvb-core/dvb_frontend.c | 9 +++++++++ linux/include/linux/dvb/frontend.h | 1 + linux/include/linux/dvb/version.h | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 9f3a61827..6f0dc252f 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -45,6 +45,7 @@ #include "dvb_frontend.h" #include "dvbdev.h" #include "compat.h" +#include static int dvb_frontend_debug; static int dvb_shutdown_timeout; @@ -906,6 +907,11 @@ struct dtv_cmds_h dtv_cmds[] = { .set = 0, }, #endif + [DTV_API_VERSION] = { + .name = "DTV_API_VERSION", + .cmd = DTV_API_VERSION, + .set = 0, + }, }; void dtv_property_dump(struct dtv_property *tvp) @@ -1210,6 +1216,9 @@ int dtv_property_process_get(struct dvb_frontend *fe, struct dtv_property *tvp, case DTV_TONE: tvp->u.data = fe->dtv_property_cache.sectone; break; + case DTV_API_VERSION: + tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR; + break; default: r = -1; } diff --git a/linux/include/linux/dvb/frontend.h b/linux/include/linux/dvb/frontend.h index ecac80d32..d28f21796 100644 --- a/linux/include/linux/dvb/frontend.h +++ b/linux/include/linux/dvb/frontend.h @@ -299,6 +299,7 @@ struct dvb_frontend_event { #define DTV_ISDB_LAYERC_SEGMENT_WIDTH 33 #define DTV_ISDB_LAYERC_TIME_INTERLEAVING 34 #endif +#define DTV_API_VERSION 35 typedef enum fe_pilot { PILOT_ON, diff --git a/linux/include/linux/dvb/version.h b/linux/include/linux/dvb/version.h index 126e0c26c..25b823b81 100644 --- a/linux/include/linux/dvb/version.h +++ b/linux/include/linux/dvb/version.h @@ -23,7 +23,7 @@ #ifndef _DVBVERSION_H_ #define _DVBVERSION_H_ -#define DVB_API_VERSION 3 -#define DVB_API_VERSION_MINOR 2 +#define DVB_API_VERSION 5 +#define DVB_API_VERSION_MINOR 0 #endif /*_DVBVERSION_H_*/ -- cgit v1.2.3 From 68d0e9d6d8ed6edafb5ece328415a155ee7134e9 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 26 Sep 2008 10:52:52 +0200 Subject: gspca: URB_NO_TRANSFER_DMA_MAP is not useful for isoc transfers. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 1e710345f..ed79540f3 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -550,8 +550,7 @@ static int create_urbs(struct gspca_dev *gspca_dev, if (npkt != 0) { /* ISOC */ urb->pipe = usb_rcvisocpipe(gspca_dev->dev, ep->desc.bEndpointAddress); - urb->transfer_flags = URB_ISO_ASAP - | URB_NO_TRANSFER_DMA_MAP; + urb->transfer_flags = URB_ISO_ASAP; urb->interval = ep->desc.bInterval; urb->complete = isoc_irq; urb->number_of_packets = npkt; -- cgit v1.2.3 From 55fbc71be89d201885f37104e2a62b0b705f0469 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 26 Sep 2008 12:43:54 +0200 Subject: gspca: Fixed a few typos in comments. From: Frank Zago Priority: normal Signed-off-by: Frank Zago Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index ed79540f3..3c57f45e8 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -288,7 +288,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; @@ -430,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, @@ -454,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). @@ -601,7 +601,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->streaming = 1; atomic_set(&gspca_dev->nevent, 0); - /* start the bulk transfer is done by the subdriver */ + /* bulk transfers are started by the subdriver */ if (gspca_dev->bulk) break; @@ -636,7 +636,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; @@ -1695,7 +1695,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 */ -- cgit v1.2.3 From 8c77729bce0b4f9d458b0e3ae1b7d066404c2b27 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Sun, 28 Sep 2008 00:37:48 -0400 Subject: em28xx: HVR-900 B3C0 - fix audio clicking issue From: Wiktor Grebla Fixed audio clicking problem which could be heard when using analog tv or composite input Priority: high Signed-off-by: Wiktor Grebla Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/video/em28xx/em28xx-audio.c | 4 ++-- linux/drivers/media/video/em28xx/em28xx-cards.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'linux') 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..620f659a9 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, -- cgit v1.2.3 From 3cc22850f84b04ad4d59e5f4a0711ad3d43a4204 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 28 Sep 2008 12:43:00 +0200 Subject: gspca: URB_NO_TRANSFER_DMA_MAP is required for isoc and bulk transfers. From: Frank Zago Priority: normal Signed-off-by: Frank Zago Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 3c57f45e8..40a115440 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -550,7 +550,8 @@ static int create_urbs(struct gspca_dev *gspca_dev, if (npkt != 0) { /* ISOC */ urb->pipe = usb_rcvisocpipe(gspca_dev->dev, ep->desc.bEndpointAddress); - urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_flags = URB_ISO_ASAP + | URB_NO_TRANSFER_DMA_MAP; urb->interval = ep->desc.bInterval; urb->complete = isoc_irq; urb->number_of_packets = npkt; @@ -561,6 +562,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; } } -- cgit v1.2.3 From 2de9197854f8c42c4fd586ef569716840acf369b Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 28 Sep 2008 13:12:22 +0200 Subject: gspca: Use a kref to avoid potentialy blocking forever in disconnect. From: Frank Zago Priority: normal Signed-off-by: Frank Zago Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 39 +++++++++++++++++++++++---------- linux/drivers/media/video/gspca/gspca.h | 1 + 2 files changed, 28 insertions(+), 12 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 40a115440..f35c1038f 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) #include @@ -844,6 +845,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; @@ -863,6 +874,10 @@ 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 */ @@ -905,7 +920,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; } @@ -1819,6 +1838,7 @@ int gspca_dev_probe(struct usb_interface *intf, err("couldn't kzalloc gspca struct"); return -EIO; } + kref_init(&gspca_dev->kref); gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); if (!gspca_dev->usb_buf) { err("out of memory"); @@ -1868,8 +1888,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); @@ -1884,8 +1903,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); @@ -1893,16 +1912,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 b0bdae194..192dffdcd 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -121,6 +121,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 */ -- cgit v1.2.3 From 53de9e63c4e8349588c201473f059bcab83ce918 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 29 Sep 2008 10:57:32 +0200 Subject: gspca: Image transfer by bulk uses altsetting 0 with any buffer size. From: Jean-Francois Moine - gspca_dev field 'bulk_size' added. - when only one altsetting usable, do image transfer by bulk. Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 42 +++++++++++++++++++++------------ linux/drivers/media/video/gspca/gspca.h | 2 +- 2 files changed, 28 insertions(+), 16 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index f35c1038f..753ae36af 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -471,25 +471,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; @@ -507,9 +516,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; @@ -518,9 +528,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; } @@ -605,7 +617,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) atomic_set(&gspca_dev->nevent, 0); /* bulk transfers are started by the subdriver */ - if (gspca_dev->bulk) + if (gspca_dev->alt == 0) break; /* submit the URBs */ diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 192dffdcd..f9006542c 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; @@ -144,7 +145,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; -- cgit v1.2.3 From 4a933aa7215adbf5851c96145ed1096d860b45b3 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 29 Sep 2008 11:59:36 +0200 Subject: gspca: New subdriver 'finepix' added. From: Frank Zago Priority: normal Signed-off-by: Frank Zago Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 23 ++ linux/drivers/media/video/gspca/Makefile | 3 +- linux/drivers/media/video/gspca/finepix.c | 465 ++++++++++++++++++++++++++++++ 3 files changed, 490 insertions(+), 1 deletion(-) create mode 100644 linux/drivers/media/video/gspca/finepix.c (limited to 'linux') diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index a845667ac..04c612eb8 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -97,6 +97,29 @@ sunplus 04a5:3003 Benq DC 1300 sunplus 04a5:3008 Benq DC 1500 sunplus 04a5:300a Benq DC 3410 spca500 04a5:300c Benq DC 1016 +finepix 04cb:0104 Fujifilm FinePix 4800 +finepix 04cb:0109 Fujifilm FinePix A202 +finepix 04cb:010b Fujifilm FinePix A203 +finepix 04cb:010f Fujifilm FinePix A204 +finepix 04cb:0111 Fujifilm FinePix A205 +finepix 04cb:0113 Fujifilm FinePix A210 +finepix 04cb:0115 Fujifilm FinePix A303 +finepix 04cb:0117 Fujifilm FinePix A310 +finepix 04cb:0119 Fujifilm FinePix F401 +finepix 04cb:011b Fujifilm FinePix F402 +finepix 04cb:011d Fujifilm FinePix F410 +finepix 04cb:0121 Fujifilm FinePix F601 +finepix 04cb:0123 Fujifilm FinePix F700 +finepix 04cb:0125 Fujifilm FinePix M603 +finepix 04cb:0127 Fujifilm FinePix S300 +finepix 04cb:0129 Fujifilm FinePix S304 +finepix 04cb:012b Fujifilm FinePix S500 +finepix 04cb:012d Fujifilm FinePix S602 +finepix 04cb:012f Fujifilm FinePix S700 +finepix 04cb:0131 Fujifilm FinePix unknown model +finepix 04cb:013b Fujifilm FinePix unknown model +finepix 04cb:013d Fujifilm FinePix unknown model +finepix 04cb:013f Fujifilm FinePix F420 sunplus 04f1:1001 JVC GC A50 spca561 04fc:0561 Flexcam 100 sunplus 04fc:500c Sunplus CA500C diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile index e68a89652..78bad0459 100644 --- a/linux/drivers/media/video/gspca/Makefile +++ b/linux/drivers/media/video/gspca/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_USB_GSPCA) += gspca_main.o \ - gspca_conex.o gspca_etoms.o gspca_mars.o \ + gspca_conex.o gspca_etoms.o gspca_finepix.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 \ @@ -9,6 +9,7 @@ obj-$(CONFIG_USB_GSPCA) += gspca_main.o \ gspca_main-objs := gspca.o gspca_conex-objs := conex.o gspca_etoms-objs := etoms.o +gspca_finepix-objs := finepix.o gspca_mars-objs := mars.o gspca_ov519-objs := ov519.o gspca_pac207-objs := pac207.o diff --git a/linux/drivers/media/video/gspca/finepix.c b/linux/drivers/media/video/gspca/finepix.c new file mode 100644 index 000000000..f75db9585 --- /dev/null +++ b/linux/drivers/media/video/gspca/finepix.c @@ -0,0 +1,465 @@ +/* + * 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 "); +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; + break; + } + 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. */ + 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 */ + 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); -- cgit v1.2.3 From 37652be2369266834832ee1fccea03b5afac7161 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 29 Sep 2008 12:03:06 +0200 Subject: gspca: Remove the duplicated EOF (ff d9) in t613. From: Jean-Francois Moine Priority: normal Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/t613.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/t613.c b/linux/drivers/media/video/gspca/t613.c index 665a783aa..06f3b8718 100644 --- a/linux/drivers/media/video/gspca/t613.c +++ b/linux/drivers/media/video/gspca/t613.c @@ -939,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; -- cgit v1.2.3 From 0bde6f869cdcf61aec023543219643c10f59e23a Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Tue, 30 Sep 2008 01:46:41 -0400 Subject: em28xx: Add detection for K-WORLD DVB-T 310U From: Darron Broad Correct firmware type to MTS Correct audio routing for composite/s-video Add DVB-T detection. This patch uses the eeprom hash method for detection as the vendor/product ids are also used for the DIGIVOX_AD. This may be a clone of the same product. Explanatory text has been added prior to the hask look-up in anticipation that it may help others. The following has been tested to work: Analogue TV (PAL-I) Composite In DVB-T (UK Crystal Palace) USB AUDIO The following has not been tested but probably works: S-Video In Priority: high Signed-off-by: Darron Broad Signed-off-by: Douglas Schilling Landgraf --- linux/Documentation/video4linux/CARDLIST.em28xx | 2 +- linux/drivers/media/video/em28xx/em28xx-cards.c | 33 +++++++++++++++++++------ linux/drivers/media/video/em28xx/em28xx-dvb.c | 9 +++++++ 3 files changed, 35 insertions(+), 9 deletions(-) (limited to 'linux') diff --git a/linux/Documentation/video4linux/CARDLIST.em28xx b/linux/Documentation/video4linux/CARDLIST.em28xx index b9181b523..187cc48d0 100644 --- a/linux/Documentation/video4linux/CARDLIST.em28xx +++ b/linux/Documentation/video4linux/CARDLIST.em28xx @@ -46,7 +46,7 @@ 45 -> Pinnacle PCTV DVB-T (em2870) 46 -> Compro, VideoMate U3 (em2870) [185b:2870] 47 -> KWorld DVB-T 305U (em2880) [eb1a:e305] - 48 -> KWorld DVB-T 310U (em2880) + 48 -> KWorld DVB-T 310U (em2880) [eb1a:e310] 49 -> MSI DigiVox A/D (em2880) [eb1a:e310] 50 -> MSI DigiVox A/D II (em2880) [eb1a:e320] 51 -> Terratec Hybrid XS Secam (em2880) [0ccd:004c] diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index 38874e024..5b839e662 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -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", -- cgit v1.2.3 From 167acb4f34fff2cc1e98ee5146c17bb25401cba2 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 30 Sep 2008 08:55:33 +0200 Subject: gspca: Restart the state machine when no frame buffer in finepix. From: Frank Zago Priority: normal Signed-off-by: Frank Zago Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/finepix.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/finepix.c b/linux/drivers/media/video/gspca/finepix.c index f75db9585..65d3cbfe6 100644 --- a/linux/drivers/media/video/gspca/finepix.c +++ b/linux/drivers/media/video/gspca/finepix.c @@ -135,10 +135,8 @@ static void urb_callback(struct urb *urb) struct gspca_frame *frame; frame = gspca_get_i_frame(&dev->gspca_dev); - if (frame == NULL) { + if (frame == NULL) gspca_dev->last_packet_type = DISCARD_PACKET; - break; - } if (urb->actual_length < FPIX_MAX_TRANSFER || (data[urb->actual_length-2] == 0xff && data[urb->actual_length-1] == 0xd9)) { @@ -149,18 +147,21 @@ static void urb_callback(struct urb *urb) * but there's nothing we can do. We also end * here if the the jpeg ends right at the end * of the frame. */ - gspca_frame_add(gspca_dev, LAST_PACKET, - frame, - data, urb->actual_length); + 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 */ - gspca_frame_add(gspca_dev, - gspca_dev->last_packet_type == LAST_PACKET - ? FIRST_PACKET : INTER_PACKET, - frame, + 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); } -- cgit v1.2.3 From da3df4c5ba560664e1ca1ef8c66b55ba67bf4f0f Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 1 Oct 2008 09:51:53 +0200 Subject: gspca: Subdriver m5602 (ALi) added. From: Erik Andren This patch adds support for the ALi m5602 usb bridge and is based on the gspca framework. It contains code for communicating with 5 different sensors: OmniVision OV9650, Pixel Plus PO1030, Samsung S5K83A, S5K4AA and finally Micron MT9M111. Priority: high Signed-off-by: Erik Andren Signed-off-by: Jean-Francois Moine --- linux/Documentation/video4linux/gspca.txt | 1 + linux/Documentation/video4linux/m5602.txt | 12 + linux/drivers/media/video/gspca/Kconfig | 3 + linux/drivers/media/video/gspca/Makefile | 3 + linux/drivers/media/video/gspca/m5602/Kconfig | 11 + linux/drivers/media/video/gspca/m5602/Makefile | 8 + .../drivers/media/video/gspca/m5602/m5602_bridge.h | 180 ++++ linux/drivers/media/video/gspca/m5602/m5602_core.c | 321 ++++++ .../media/video/gspca/m5602/m5602_mt9m111.c | 343 +++++++ .../media/video/gspca/m5602/m5602_mt9m111.h | 1020 ++++++++++++++++++++ .../drivers/media/video/gspca/m5602/m5602_ov9650.c | 544 +++++++++++ .../drivers/media/video/gspca/m5602/m5602_ov9650.h | 501 ++++++++++ .../drivers/media/video/gspca/m5602/m5602_po1030.c | 334 +++++++ .../drivers/media/video/gspca/m5602/m5602_po1030.h | 478 +++++++++ .../drivers/media/video/gspca/m5602/m5602_s5k4aa.c | 460 +++++++++ .../drivers/media/video/gspca/m5602/m5602_s5k4aa.h | 368 +++++++ .../drivers/media/video/gspca/m5602/m5602_s5k83a.c | 331 +++++++ .../drivers/media/video/gspca/m5602/m5602_s5k83a.h | 444 +++++++++ .../drivers/media/video/gspca/m5602/m5602_sensor.h | 76 ++ 19 files changed, 5438 insertions(+) create mode 100644 linux/Documentation/video4linux/m5602.txt create mode 100644 linux/drivers/media/video/gspca/m5602/Kconfig create mode 100644 linux/drivers/media/video/gspca/m5602/Makefile create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_bridge.h create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_core.c create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_ov9650.c create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_ov9650.h create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_po1030.c create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_po1030.h create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h create mode 100644 linux/drivers/media/video/gspca/m5602/m5602_sensor.h (limited to 'linux') diff --git a/linux/Documentation/video4linux/gspca.txt b/linux/Documentation/video4linux/gspca.txt index 04c612eb8..004818fab 100644 --- a/linux/Documentation/video4linux/gspca.txt +++ b/linux/Documentation/video4linux/gspca.txt @@ -7,6 +7,7 @@ The modules are: xxxx vend:prod ---- spca501 0000:0000 MystFromOri Unknow Camera +m5602 0402:5602 ALi Video Camera Controller spca501 040a:0002 Kodak DVC-325 spca500 040a:0300 Kodak EZ200 zc3xx 041e:041e Creative WebCam Live! diff --git a/linux/Documentation/video4linux/m5602.txt b/linux/Documentation/video4linux/m5602.txt new file mode 100644 index 000000000..4450ab13f --- /dev/null +++ b/linux/Documentation/video4linux/m5602.txt @@ -0,0 +1,12 @@ +This document describes the ALi m5602 bridge connected +to the following supported sensors: +OmniVision OV9650, +Samsung s5k83a, +Samsung s5k4aa, +Micron mt9m111, +Pixel plus PO1030 + +This driver mimics the windows drivers, which have a braindead implementation sending bayer-encoded frames at VGA resolution. +In a perfect world we should be able to reprogram the m5602 and the connected sensor in hardware instead, supporting a range of resolutions and pixelformats + +Anyway, have fun and please report any bugs to m560x-driver-devel@lists.sourceforge.net diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig index 42b90742b..fd31099e3 100644 --- a/linux/drivers/media/video/gspca/Kconfig +++ b/linux/drivers/media/video/gspca/Kconfig @@ -11,3 +11,6 @@ config USB_GSPCA To compile this driver as modules, choose M here: the modules will be called gspca_xxxx. + +source "drivers/media/video/gspca/m5602/Kconfig" + diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile index 78bad0459..488ed97c5 100644 --- a/linux/drivers/media/video/gspca/Makefile +++ b/linux/drivers/media/video/gspca/Makefile @@ -28,3 +28,6 @@ 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/ + diff --git a/linux/drivers/media/video/gspca/m5602/Kconfig b/linux/drivers/media/video/gspca/m5602/Kconfig new file mode 100644 index 000000000..7de33e221 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/Kconfig @@ -0,0 +1,11 @@ +config USB_M5602 + tristate "USB ALi m5602 Webcam support" + 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 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..21fe74f99 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_bridge.h @@ -0,0 +1,180 @@ +/* + * 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 + * + * 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; + + /* 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..aee503d96 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_core.c @@ -0,0 +1,321 @@ +/* + * 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 + * + * 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)) { + sd_desc.ctrls = po1030.ctrls; + sd_desc.nctrls = po1030.nctrls; + return 0; + } + + /* Try the mt9m111 sensor */ + sd->sensor = &mt9m111; + if (!sd->sensor->probe(sd)) { + sd_desc.ctrls = mt9m111.ctrls; + sd_desc.nctrls = mt9m111.nctrls; + return 0; + } + + /* Try the s5k4aa */ + sd->sensor = &s5k4aa; + if (!sd->sensor->probe(sd)) { + sd_desc.ctrls = s5k4aa.ctrls; + sd_desc.nctrls = s5k4aa.nctrls; + return 0; + } + + /* Try the ov9650 */ + sd->sensor = &ov9650; + if (!sd->sensor->probe(sd)) { + sd_desc.ctrls = ov9650.ctrls; + sd_desc.nctrls = ov9650.nctrls; + return 0; + } + + /* Try the s5k83a */ + sd->sensor = &s5k83a; + if (!sd->sensor->probe(sd)) { + sd_desc.ctrls = s5k83a.ctrls; + sd_desc.nctrls = s5k83a.nctrls; + 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); + + /* Create a new frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + + PDEBUG(DBG_V4L2, "Starting new frame. First urb contained %d", + len); + + } 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 copying %d bytes", + 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; + + 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..17f04dd5e --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c @@ -0,0 +1,343 @@ +/* + * 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 + * + * 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; + 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 + * + * 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 + * + * 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..74c3ffec0 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c @@ -0,0 +1,544 @@ +/* + * 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 + * + * 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; + 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 + * + * 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 + +#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..14a8f929d --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_po1030.c @@ -0,0 +1,334 @@ +/* + * 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 + * + * 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; + 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 + * + * 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..3a2ae7a2e --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -0,0 +1,460 @@ +/* + * 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 + * + * 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; + 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 + * + * 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 + +#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..a4d6a8163 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -0,0 +1,331 @@ +/* + * 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 + * + * 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; + 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 + * + * 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 + * + * 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 -- cgit v1.2.3 From c623a52a2c3a4d36c4b41b8e5b2c54b71e364786 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Wed, 1 Oct 2008 08:40:59 -0400 Subject: radio-mr800: Add driver for AverMedia MR 800 USB FM radio devices From: Alexey Klimov This patch creates a new usb-radio driver, radio-mr800.c, that supports the AverMedia MR 800 USB FM radio devices. This device plugs into both the USB and an analog audio input, so this thing only deals with initialization and frequency setting, the audio data has to be handled by a sound driver. Priority: normal Signed-off-by: Alexey Klimov Signed-off-by: Douglas Schilling Landgraf --- linux/drivers/media/radio/Kconfig | 12 + linux/drivers/media/radio/Makefile | 1 + linux/drivers/media/radio/radio-mr800.c | 628 ++++++++++++++++++++++++++++++++ 3 files changed, 641 insertions(+) create mode 100644 linux/drivers/media/radio/radio-mr800.c (limited to 'linux') diff --git a/linux/drivers/media/radio/Kconfig b/linux/drivers/media/radio/Kconfig index 1b41b3f77..e51d707e5 100644 --- a/linux/drivers/media/radio/Kconfig +++ b/linux/drivers/media/radio/Kconfig @@ -361,4 +361,16 @@ config USB_SI470X To compile this driver as a module, choose M here: the module will be called radio-silabs. +config USB_MR800 + tristate "AverMedia MR 800 USB FM radio support" + depends on USB && VIDEO_V4L2 + ---help--- + Say Y here if you want to connect this type of radio to your + computer's USB port. Note that the audio is not digital, and + you must connect the line out connector to a sound card or a + set of speakers. + + To compile this driver as a module, choose M here: the + module will be called radio-mr800. + endif # RADIO_ADAPTERS diff --git a/linux/drivers/media/radio/Makefile b/linux/drivers/media/radio/Makefile index 7ca71ab96..240ec63cd 100644 --- a/linux/drivers/media/radio/Makefile +++ b/linux/drivers/media/radio/Makefile @@ -18,5 +18,6 @@ obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_USB_SI470X) += radio-si470x.o +obj-$(CONFIG_USB_MR800) += radio-mr800.o EXTRA_CFLAGS += -Isound diff --git a/linux/drivers/media/radio/radio-mr800.c b/linux/drivers/media/radio/radio-mr800.c new file mode 100644 index 000000000..a33717c48 --- /dev/null +++ b/linux/drivers/media/radio/radio-mr800.c @@ -0,0 +1,628 @@ +/* + * A driver for the AverMedia MR 800 USB FM radio. This device plugs + * into both the USB and an analog audio input, so this thing + * only deals with initialization and frequency setting, the + * audio data has to be handled by a sound driver. + * + * Copyright (c) 2008 Alexey Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Big thanks to authors of dsbr100.c and radio-si470x.c + * + * When work was looked pretty good, i discover this: + * http://av-usbradio.sourceforge.net/index.php + * http://sourceforge.net/projects/av-usbradio/ + * Latest release of theirs project was in 2005. + * Probably, this driver could be improved trough using their + * achievements (specifications given). + * So, we have smth to begin with. + * + * History: + * Version 0.01: First working version. + * It's required to blacklist AverMedia USB Radio + * in usbhid/hid-quirks.c + * + * Many things to do: + * - Correct power managment of device (suspend & resume) + * - Make x86 independance (little-endian and big-endian stuff) + * - Add code for scanning and smooth tuning + * - Checked and add stereo&mono stuff + * - Add code for sensitivity value + * - Correct mistakes + * - In Japan another FREQ_MIN and FREQ_MAX + */ + +/* kernel includes */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for KERNEL_VERSION MACRO */ + +/* driver and module definitions */ +#define DRIVER_AUTHOR "Alexey Klimov " +#define DRIVER_DESC "AverMedia MR 800 USB FM radio driver" +#define DRIVER_VERSION "0.01" +#define RADIO_VERSION KERNEL_VERSION(0, 0, 1) + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define USB_AMRADIO_VENDOR 0x07ca +#define USB_AMRADIO_PRODUCT 0xb800 + +/* Probably USB_TIMEOUT should be modified in module parameter */ +#define BUFFER_LENGTH 8 +#define USB_TIMEOUT 500 + +/* Frequency limits in MHz -- these are European values. For Japanese +devices, that would be 76 and 91. */ +#define FREQ_MIN 87.5 +#define FREQ_MAX 108.0 +#define FREQ_MUL 16000 + +/* module parameter */ +static int radio_nr = -1; +module_param(radio_nr, int, 0); +MODULE_PARM_DESC(radio_nr, "Radio Nr"); + +static struct v4l2_queryctrl radio_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, +/* HINT: the disabled controls are only here to satify kradio and such apps */ + { .id = V4L2_CID_AUDIO_VOLUME, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_BALANCE, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_BASS, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_TREBLE, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, + { + .id = V4L2_CID_AUDIO_LOUDNESS, + .flags = V4L2_CTRL_FLAG_DISABLED, + }, +}; + +static int usb_amradio_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void usb_amradio_disconnect(struct usb_interface *intf); +static int usb_amradio_open(struct inode *inode, struct file *file); +static int usb_amradio_close(struct inode *inode, struct file *file); +static int usb_amradio_suspend(struct usb_interface *intf, + pm_message_t message); +static int usb_amradio_resume(struct usb_interface *intf); + +/* Data for one (physical) device */ +struct amradio_device { + /* reference to USB and video device */ + struct usb_device *usbdev; + struct video_device *videodev; + + unsigned char *buffer; + struct mutex lock; /* buffer locking */ + int curfreq; + int stereo; + int users; + int removed; + int muted; +}; + +/* USB Device ID List */ +static struct usb_device_id usb_amradio_device_table[] = { + {USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT, + USB_CLASS_HID, 0, 0) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_amradio_device_table); + +/* USB subsystem interface */ +static struct usb_driver usb_amradio_driver = { + .name = "radio-mr800", + .probe = usb_amradio_probe, + .disconnect = usb_amradio_disconnect, + .suspend = usb_amradio_suspend, + .resume = usb_amradio_resume, + .reset_resume = usb_amradio_resume, + .id_table = usb_amradio_device_table, + .supports_autosuspend = 1, +}; + +/* switch on radio. Send 8 bytes to device. */ +static int amradio_start(struct amradio_device *radio) +{ + int retval; + int size; + + mutex_lock(&radio->lock); + + radio->buffer[0] = 0x00; + radio->buffer[1] = 0x55; + radio->buffer[2] = 0xaa; + radio->buffer[3] = 0x00; + radio->buffer[4] = 0xab; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), + (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); + + if (retval) { + mutex_unlock(&radio->lock); + return retval; + } + + mutex_unlock(&radio->lock); + + radio->muted = 0; + + return retval; +} + +/* switch off radio */ +static int amradio_stop(struct amradio_device *radio) +{ + int retval; + int size; + + mutex_lock(&radio->lock); + + radio->buffer[0] = 0x00; + radio->buffer[1] = 0x55; + radio->buffer[2] = 0xaa; + radio->buffer[3] = 0x00; + radio->buffer[4] = 0xab; + radio->buffer[5] = 0x01; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x00; + + retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), + (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); + + if (retval) { + mutex_unlock(&radio->lock); + return retval; + } + + mutex_unlock(&radio->lock); + + radio->muted = 1; + + return retval; +} + +/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ +static int amradio_setfreq(struct amradio_device *radio, int freq) +{ + int retval; + int size; + unsigned short freq_send = 0x13 + (freq >> 3) / 25; + + mutex_lock(&radio->lock); + + radio->buffer[0] = 0x00; + radio->buffer[1] = 0x55; + radio->buffer[2] = 0xaa; + radio->buffer[3] = 0x03; + radio->buffer[4] = 0xa4; + radio->buffer[5] = 0x00; + radio->buffer[6] = 0x00; + radio->buffer[7] = 0x08; + + retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), + (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); + + if (retval) { + mutex_unlock(&radio->lock); + return retval; + } + + /* frequency is calculated from freq_send and placed in first 2 bytes */ + radio->buffer[0] = (freq_send >> 8) & 0xff; + radio->buffer[1] = freq_send & 0xff; + radio->buffer[2] = 0x01; + radio->buffer[3] = 0x00; + radio->buffer[4] = 0x00; + /* 5 and 6 bytes of buffer already = 0x00 */ + radio->buffer[7] = 0x00; + + retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), + (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); + + if (retval) { + mutex_unlock(&radio->lock); + return retval; + } + + mutex_unlock(&radio->lock); + + radio->stereo = 0; + + return retval; +} + +/* USB subsystem interface begins here */ + +/* handle unplugging of the device, release data structures +if nothing keeps us from doing it. If something is still +keeping us busy, the release callback of v4l will take care +of releasing it. */ +static void usb_amradio_disconnect(struct usb_interface *intf) +{ + struct amradio_device *radio = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (radio) { + video_unregister_device(radio->videodev); + radio->videodev = NULL; + if (radio->users) { + kfree(radio->buffer); + kfree(radio); + } else { + radio->removed = 1; + } + } +} + +/* vidioc_querycap - query device capabilities */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-mr800", sizeof(v->driver)); + strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card)); + sprintf(v->bus_info, "USB"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +/* vidioc_g_tuner - get tuner attributes */ +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + + if (v->index > 0) + return -EINVAL; + +/* TODO: Add function which look is signal stereo or not + * amradio_getstat(radio); + */ + radio->stereo = -1; + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = FREQ_MIN * FREQ_MUL; + v->rangehigh = FREQ_MAX * FREQ_MUL; + v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + v->capability = V4L2_TUNER_CAP_LOW; + if (radio->stereo) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xffff; /* Can't get the signal strength, sad.. */ + v->afc = 0; /* Don't know what is this */ + return 0; +} + +/* vidioc_s_tuner - set tuner attributes */ +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} + +/* vidioc_s_frequency - set tuner radio frequency */ +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + + radio->curfreq = f->frequency; + if (amradio_setfreq(radio, radio->curfreq) < 0) + warn("Set frequency failed"); + return 0; +} + +/* vidioc_g_frequency - get tuner radio frequency */ +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + + f->type = V4L2_TUNER_RADIO; + f->frequency = radio->curfreq; + return 0; +} + +/* vidioc_queryctrl - enumerate control items */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return 0; + } + } + return -EINVAL; +} + +/* vidioc_g_ctrl - get the value of a control */ +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = radio->muted; + return 0; + } + return -EINVAL; +} + +/* vidioc_s_ctrl - set the value of a control */ +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + if (amradio_stop(radio) < 0) { + warn("amradio_stop() failed"); + return -1; + } + } else { + if (amradio_start(radio) < 0) { + warn("amradio_start() failed"); + return -1; + } + } + return 0; + } + return -EINVAL; +} + +/* vidioc_g_audio - get audio attributes */ +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +/* vidioc_s_audio - set audio attributes */ +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; +} + +/* vidioc_g_input - get input */ +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +/* vidioc_s_input - set input */ +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +/* open device - amradio_start() and amradio_setfreq() */ +static int usb_amradio_open(struct inode *inode, struct file *file) +{ + struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + + radio->users = 1; + radio->muted = 1; + + if (amradio_start(radio) < 0) { + warn("Radio did not start up properly"); + radio->users = 0; + return -EIO; + } + if (amradio_setfreq(radio, radio->curfreq) < 0) + warn("Set frequency failed"); + return 0; +} + +/*close device - free driver structures */ +static int usb_amradio_close(struct inode *inode, struct file *file) +{ + struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + + if (!radio) + return -ENODEV; + radio->users = 0; + if (radio->removed) { + kfree(radio->buffer); + kfree(radio); + } + return 0; +} + +/* Suspend device - stop device. Need to be checked and fixed */ +static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct amradio_device *radio = usb_get_intfdata(intf); + + if (amradio_stop(radio) < 0) + warn("amradio_stop() failed"); + + info("radio-mr800: Going into suspend.."); + + return 0; +} + +/* Resume device - start device. Need to be checked and fixed */ +static int usb_amradio_resume(struct usb_interface *intf) +{ + struct amradio_device *radio = usb_get_intfdata(intf); + + if (amradio_start(radio) < 0) + warn("amradio_start() failed"); + + info("radio-mr800: Coming out of suspend.."); + + return 0; +} + +/* File system interface */ +static const struct file_operations usb_amradio_fops = { + .owner = THIS_MODULE, + .open = usb_amradio_open, + .release = usb_amradio_close, + .ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT + .compat_ioctl = v4l_compat_ioctl32, +#endif + .llseek = no_llseek, +}; + +static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, +}; + +/* V4L2 interface */ +static struct video_device amradio_videodev_template = { + .name = "AverMedia MR 800 USB FM Radio", + .fops = &usb_amradio_fops, + .ioctl_ops = &usb_amradio_ioctl_ops, + .release = video_device_release, +}; + +/* check if the device is present and register with v4l and +usb if it is */ +static int usb_amradio_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct amradio_device *radio; + + radio = kmalloc(sizeof(struct amradio_device), GFP_KERNEL); + + if (!(radio)) + return -ENOMEM; + + radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); + + if (!(radio->buffer)) { + kfree(radio); + return -ENOMEM; + } + + radio->videodev = video_device_alloc(); + + if (!(radio->videodev)) { + kfree(radio->buffer); + kfree(radio); + return -ENOMEM; + } + + memcpy(radio->videodev, &amradio_videodev_template, + sizeof(amradio_videodev_template)); + + radio->removed = 0; + radio->users = 0; + radio->usbdev = interface_to_usbdev(intf); + radio->curfreq = 95.16 * FREQ_MUL; + + mutex_init(&radio->lock); + + video_set_drvdata(radio->videodev, radio); + if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) { + warn("Could not register video device"); + video_device_release(radio->videodev); + kfree(radio->buffer); + kfree(radio); + return -EIO; + } + + usb_set_intfdata(intf, radio); + return 0; +} + +static int __init amradio_init(void) +{ + int retval = usb_register(&usb_amradio_driver); + + info(DRIVER_VERSION " " DRIVER_DESC); + if (retval) + err("usb_register failed. Error number %d", retval); + return retval; +} + +static void __exit amradio_exit(void) +{ + usb_deregister(&usb_amradio_driver); +} + +module_init(amradio_init); +module_exit(amradio_exit); + -- cgit v1.2.3 From 00e62c6f5fc6e951578281935ddfdc51cfd5a317 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 2 Oct 2008 13:06:59 +0200 Subject: gspca: Bad init values for sonixj ov7660. From: Jean-Francois Moine Priority: high Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/sonixj.c | 38 +++++++------------------------- 1 file changed, 8 insertions(+), 30 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 4cad52769..f5b6ab9e5 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -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 */ @@ -1023,13 +1011,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: */ @@ -1243,7 +1231,7 @@ static int 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); @@ -1285,20 +1273,15 @@ static int 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) @@ -1350,14 +1333,8 @@ static int 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; } @@ -1366,6 +1343,7 @@ static int 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: -- cgit v1.2.3 From 9b00af9d7ad2c53c0df02fbba1c4cdc4465e4081 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 3 Oct 2008 13:46:50 +0200 Subject: gspca: Cleanup code and small changes. From: Erik Andren - convert some #define to enum. - remove some comments. - return ENOMEM on memory allocation failure. Priority: normal Signed-off-by: Erik Andren Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/gspca.c | 12 ++++-------- linux/drivers/media/video/gspca/gspca.h | 12 +++++++----- linux/drivers/media/video/gspca/m5602/m5602_core.c | 1 - 3 files changed, 11 insertions(+), 14 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index 753ae36af..12e3a4360 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -247,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) @@ -327,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; @@ -947,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); @@ -1581,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; @@ -1846,22 +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 */ diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index f9006542c..4779dd0b0 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -106,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 */ @@ -175,7 +177,7 @@ 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); diff --git a/linux/drivers/media/video/gspca/m5602/m5602_core.c b/linux/drivers/media/video/gspca/m5602/m5602_core.c index aee503d96..80fd4e73a 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_core.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_core.c @@ -146,7 +146,6 @@ int m5602_probe_sensor(struct sd *sd) return 0; } - /* More sensor probe function goes here */ info("Failed to find a sensor"); sd->sensor = NULL; -- cgit v1.2.3 From b919f302ebe29af34e422ccc0a1af08542bd5df4 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 3 Oct 2008 14:28:45 +0200 Subject: gspca: Frame counter in ALi m5602. From: Erik Andren Priority: normal Signed-off-by: Erik Andren Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/m5602/m5602_core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/m5602/m5602_core.c b/linux/drivers/media/video/gspca/m5602/m5602_core.c index 80fd4e73a..5d5666d4b 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_core.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_core.c @@ -146,6 +146,7 @@ int m5602_probe_sensor(struct sd *sd) return 0; } + /* More sensor probe function goes here */ info("Failed to find a sensor"); sd->sensor = NULL; @@ -202,12 +203,13 @@ void m5602_urb_complete(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* 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. First urb contained %d", - len); + PDEBUG(DBG_V4L2, "Starting new frame %d", + sd->frame_count); } else { int cur_frame_len = frame->data_end - frame->data; @@ -217,8 +219,8 @@ void m5602_urb_complete(struct gspca_dev *gspca_dev, struct gspca_frame *frame, len -= 4; if (cur_frame_len + len <= frame->v4l2_buf.length) { - PDEBUG(DBG_DATA, "Continuing frame copying %d bytes", - len); + PDEBUG(DBG_DATA, "Continuing frame %d copying %d bytes", + sd->frame_count, len); gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); -- cgit v1.2.3 From 53fe2488319296910abb4d88ead573224a1f99b2 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 3 Oct 2008 20:29:02 +0200 Subject: gspca: Moves some sensor initialization to each sensor in m5602. From: Erik Andren Priority: normal Signed-off-by: Erik Andren Signed-off-by: Jean-Francois Moine --- .../drivers/media/video/gspca/m5602/m5602_bridge.h | 2 ++ linux/drivers/media/video/gspca/m5602/m5602_core.c | 28 ++++++---------------- .../media/video/gspca/m5602/m5602_mt9m111.c | 2 ++ .../drivers/media/video/gspca/m5602/m5602_ov9650.c | 2 ++ .../drivers/media/video/gspca/m5602/m5602_po1030.c | 2 ++ .../drivers/media/video/gspca/m5602/m5602_s5k4aa.c | 3 +++ .../drivers/media/video/gspca/m5602/m5602_s5k83a.c | 2 ++ 7 files changed, 20 insertions(+), 21 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/m5602/m5602_bridge.h b/linux/drivers/media/video/gspca/m5602/m5602_bridge.h index 21fe74f99..6b2441e84 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_bridge.h +++ b/linux/drivers/media/video/gspca/m5602/m5602_bridge.h @@ -152,6 +152,8 @@ struct sd { /* 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; diff --git a/linux/drivers/media/video/gspca/m5602/m5602_core.c b/linux/drivers/media/video/gspca/m5602/m5602_core.c index 5d5666d4b..30efe7d4b 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_core.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_core.c @@ -108,44 +108,28 @@ int m5602_probe_sensor(struct sd *sd) { /* Try the po1030 */ sd->sensor = &po1030; - if (!sd->sensor->probe(sd)) { - sd_desc.ctrls = po1030.ctrls; - sd_desc.nctrls = po1030.nctrls; + if (!sd->sensor->probe(sd)) return 0; - } /* Try the mt9m111 sensor */ sd->sensor = &mt9m111; - if (!sd->sensor->probe(sd)) { - sd_desc.ctrls = mt9m111.ctrls; - sd_desc.nctrls = mt9m111.nctrls; + if (!sd->sensor->probe(sd)) return 0; - } /* Try the s5k4aa */ sd->sensor = &s5k4aa; - if (!sd->sensor->probe(sd)) { - sd_desc.ctrls = s5k4aa.ctrls; - sd_desc.nctrls = s5k4aa.nctrls; + if (!sd->sensor->probe(sd)) return 0; - } /* Try the ov9650 */ sd->sensor = &ov9650; - if (!sd->sensor->probe(sd)) { - sd_desc.ctrls = ov9650.ctrls; - sd_desc.nctrls = ov9650.nctrls; + if (!sd->sensor->probe(sd)) return 0; - } /* Try the s5k83a */ sd->sensor = &s5k83a; - if (!sd->sensor->probe(sd)) { - sd_desc.ctrls = s5k83a.ctrls; - sd_desc.nctrls = s5k83a.nctrls; + if (!sd->sensor->probe(sd)) return 0; - } - /* More sensor probe function goes here */ info("Failed to find a sensor"); @@ -246,8 +230,10 @@ int m5602_configure(struct gspca_dev *gspca_dev, 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); diff --git a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c index 17f04dd5e..ea2250217 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c @@ -62,6 +62,8 @@ int mt9m111_probe(struct sd *sd) 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; } diff --git a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c index 74c3ffec0..31c589625 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c @@ -132,6 +132,8 @@ int ov9650_probe(struct sd *sd) 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; } diff --git a/linux/drivers/media/video/gspca/m5602/m5602_po1030.c b/linux/drivers/media/video/gspca/m5602/m5602_po1030.c index 14a8f929d..08c015bde 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_po1030.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_po1030.c @@ -59,6 +59,8 @@ int po1030_probe(struct sd *sd) 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; } diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index 3a2ae7a2e..682025653 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -78,6 +78,9 @@ int s5k4aa_probe(struct sd *sd) 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; } diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c index a4d6a8163..c1ff967b1 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -63,6 +63,8 @@ int s5k83a_probe(struct sd *sd) 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; } -- cgit v1.2.3 From 3bc8323b3a289667837799641e6046c3b99cf4c1 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 3 Oct 2008 20:47:03 +0200 Subject: gspca: Subdriver selection at config time. From: Erik Andren Priority: normal Signed-off-by: Erik Andren Signed-off-by: Jean-Francois Moine --- linux/drivers/media/video/gspca/Kconfig | 203 +++++++++++++++++++++++++- linux/drivers/media/video/gspca/Makefile | 73 +++++---- linux/drivers/media/video/gspca/m5602/Kconfig | 4 +- 3 files changed, 240 insertions(+), 40 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig index fd31099e3..aa7f3eb7e 100644 --- a/linux/drivers/media/video/gspca/Kconfig +++ b/linux/drivers/media/video/gspca/Kconfig @@ -1,16 +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 for more info. + See 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 488ed97c5..b510b06fd 100644 --- a/linux/drivers/media/video/gspca/Makefile +++ b/linux/drivers/media/video/gspca/Makefile @@ -1,33 +1,46 @@ -obj-$(CONFIG_USB_GSPCA) += gspca_main.o \ - gspca_conex.o gspca_etoms.o gspca_finepix.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_SONXIJ) += 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_finepix-objs := finepix.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 +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/ +obj-$(CONFIG_USB_M5602) += m5602/ diff --git a/linux/drivers/media/video/gspca/m5602/Kconfig b/linux/drivers/media/video/gspca/m5602/Kconfig index 7de33e221..5a69016ed 100644 --- a/linux/drivers/media/video/gspca/m5602/Kconfig +++ b/linux/drivers/media/video/gspca/m5602/Kconfig @@ -1,5 +1,5 @@ config USB_M5602 - tristate "USB ALi m5602 Webcam support" + 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 @@ -8,4 +8,4 @@ config USB_M5602 See for more info. To compile this driver as a module, choose M here: the - module will be called gspca-m5602. + module will be called gspca_m5602. -- cgit v1.2.3 From 9e12f044a86db8366796e7720a9c40316db0cb4a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 4 Oct 2008 14:17:02 -0300 Subject: gspca: Adjust control values and restore compilation of sonixj. From: Jean-Francois Moine - no compilation since last changeset - brightness is a signed value - better values of the color matrix Priority: normal Signed-off-by: Jean-Francois Moine [mchehab@redhat.com: fix a merge conflict] Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/video/gspca/Makefile | 2 +- linux/drivers/media/video/gspca/sonixj.c | 31 +++++++++++++------------------ 2 files changed, 14 insertions(+), 19 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile index b510b06fd..b87322f4a 100644 --- a/linux/drivers/media/video/gspca/Makefile +++ b/linux/drivers/media/video/gspca/Makefile @@ -6,7 +6,7 @@ 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_SONXIJ) += gspca_sonixj.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 diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index f5b6ab9e5..f68faf6bd 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -267,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}, @@ -1119,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); } -- cgit v1.2.3 From f48cc706c1b203e3e10d3e30380e96d27c5bf858 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 4 Oct 2008 14:18:37 -0300 Subject: Whitespace cleanups From: Mauro Carvalho Chehab Priority: normal Signed-off-by: Mauro Carvalho Chehab --- linux/drivers/media/dvb/dvb-usb/dw2102.c | 2 +- linux/drivers/media/dvb/frontends/Kconfig | 2 +- linux/drivers/media/dvb/frontends/si21xx.c | 2 +- linux/drivers/media/video/gspca/spca561.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'linux') diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.c b/linux/drivers/media/dvb/dvb-usb/dw2102.c index b5f81f761..20ba3f129 100644 --- a/linux/drivers/media/dvb/dvb-usb/dw2102.c +++ b/linux/drivers/media/dvb/dvb-usb/dw2102.c @@ -1,4 +1,4 @@ -/* DVB USB framework compliant Linux driver for the +/* DVB USB framework compliant Linux driver for the * DVBWorld DVB-S 2101, 2102, DVB-S2 2104 Card * * Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 9c2f76309..96b93e21a 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -48,7 +48,7 @@ config DVB_STV0288 depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help - A DVB-S tuner module. Say Y when you want to support this frontend. + A DVB-S tuner module. Say Y when you want to support this frontend. config DVB_STB6000 tristate "ST STB6000 silicon tuner" diff --git a/linux/drivers/media/dvb/frontends/si21xx.c b/linux/drivers/media/dvb/frontends/si21xx.c index bad4ce9b8..2f69a173d 100644 --- a/linux/drivers/media/dvb/frontends/si21xx.c +++ b/linux/drivers/media/dvb/frontends/si21xx.c @@ -989,7 +989,7 @@ static struct dvb_frontend_ops si21xx_ops = { .diseqc_send_burst = si21xx_send_diseqc_burst, .set_tone = si21xx_set_tone, .set_voltage = si21xx_set_voltage, - + .set_property = si21xx_set_property, .get_property = si21xx_get_property, .set_frontend = si21xx_set_frontend, diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index 02f4ac274..5b65dd66b 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -1080,7 +1080,7 @@ static struct ctrl sd_ctrls_12a[] = { { .id = V4L2_CID_DO_WHITE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "White Balance", + .name = "White Balance", .minimum = WHITE_MIN, .maximum = WHITE_MAX, .step = 1, -- cgit v1.2.3