/* * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam * * vv6410.c - VV6410 Sensor Implementation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "quickcam.h" #include "helper.h" #include "vv6410.h" /** * Set window size */ #define isaweb(DEV) ((DEV)->descriptor.idProduct==0x850) static int vv6410_set_window(struct usb_device *dev, int x, int y, int width, int height, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; usb_quickcam_i2c_new(&i2cbuff); // x offset x = qcmax(1,x); usb_quickcam_i2c_add(&i2cbuff,0x57,x >> 8); usb_quickcam_i2c_add(&i2cbuff,0x58,x & 0xff); // y offset y = qcmax(1,y); usb_quickcam_i2c_add(&i2cbuff,0x59,y >> 8); usb_quickcam_i2c_add(&i2cbuff,0x5a,y & 0xff); // Set the real if (sensor_ctrl->mode) { sensor_ctrl->width=180; sensor_ctrl->height=148; } else { sensor_ctrl->width=356; sensor_ctrl->height=292; } // line length if (sensor_ctrl->mode) { if (isaweb(dev)) width=250; else width=360; /* 180 * 2 (CLK-DIV is 2) */ } else { if (isaweb(dev)) width=416; else width=712; /* 356 * 2 */ } usb_quickcam_i2c_add(&i2cbuff,0x52, width >> 8); usb_quickcam_i2c_add(&i2cbuff,0x53, width & 0xff); // field length (num lines) if (sensor_ctrl->mode) height=160; /* nearest of 148 = 10 * 16 */ else height=320; // 304; /* nearest of 292 = 19 * 16 */ usb_quickcam_i2c_add(&i2cbuff,0x61,height >> 8); usb_quickcam_i2c_add(&i2cbuff,0x62,height & 0xff); // usb_quickcam_i2c_add(&i2cbuff,0x25,0x02); return(usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR)); } /* start grabbing */ static int vv6410_start(struct usb_device * dev, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; usb_quickcam_i2c_new(&i2cbuff); if (sensor_ctrl->mode) usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL, 0xc0); // 0x80 else usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL, 0x00); // 0x80 if (isaweb(dev)) { if (usb_quickcam_set1(dev, 0x1445, 1) < 0) { printk(KERN_ERR "vv6410_stop: Can't turn on led\n"); return (-2); } } // usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR); // return(vv6410_set_window(dev,0,0,352, 288)); // Hacked! return(usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR)); } /* stop grabbing */ static int vv6410_stop(struct usb_device * dev, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; if (isaweb(dev)) { if (usb_quickcam_set1(dev, 0x1445, 0) < 0) { printk(KERN_ERR "vv6410_stop: turn off led\n"); return (-2); } } usb_quickcam_i2c_new(&i2cbuff); if (sensor_ctrl->mode) usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL,0xc2); // sleep mode. else usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL,0x02); // sleep mode. if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) return (-2); return(0); } /* * initialise parameters for vv6410 sensor. * Just try to send the same commands as Windows Quickcam soft. */ static int vv6410_init(struct usb_device *dev, int mode, int *rgain, int *bgain, int *ggain, struct sensorctrl *sensor_ctrl) { struct quickcam_i2c i2cbuff; int line_length = mode?250:416;//415; if (mode) { sensor_ctrl->mode=2; // quater. sensor_ctrl->width = 180; sensor_ctrl->height = 148; } else { sensor_ctrl->mode=0; sensor_ctrl->width = 356; sensor_ctrl->height = 292; } if (*rgain<=0 || *rgain>255) *rgain=RGAIN_DEF; if (*bgain<=0 || *bgain>255) *bgain=BGAIN_DEF; if (*ggain<=0 || *ggain>255) *ggain=GGAIN_DEF; if (usb_quickcam_set1(dev, STV_REG23, 5) < 0) // was 5. goto error; if (!isaweb(dev)) { /* logitech quickcam web has 0x850 as idProduct */ if (usb_quickcam_set1(dev, 0x1446, 1) < 0) goto error; } if (usb_quickcam_set1(dev,STV_SCAN_RATE, 0x00) < 0) goto error; if (usb_quickcam_set1(dev, 0x1423, 0x04) < 0) goto error; if (usb_quickcam_set1(dev, STV_REG00, 0x1b) < 0) // 0x0b goto error; usb_quickcam_i2c_new(&i2cbuff); usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL,0x04); // reset to defaults if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_quickcam_i2c_out VV6410_CONTROL(0x04) failed\n"); goto error; } // CIF or QCIF and sleep. if (isaweb(dev)) usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL,(mode?0xa2:0x02)); else usb_quickcam_i2c_add(&i2cbuff,VV6410_CONTROL,(mode?0xc2:0x02)); if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_quickcam_i2c_out VV6410_CONTROL(0xc2) failed\n"); goto error; } usb_quickcam_i2c_add(&i2cbuff,VV6410_GAIN,0xfb); if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_quickcam_i2c_out VV6410_GAIN(0x24) failed\n"); goto error; } if (usb_quickcam_set1(dev, STV_REG04, 0x07) < 0) goto error; if (usb_quickcam_set1(dev, STV_REG03, 0x45) < 0) goto error; /* set window size */ if (vv6410_set_window(dev,0,0,48,64,sensor_ctrl) < 0) { printk(KERN_ERR "vv6410_set_window failed"); goto error; } /* EXPERIMENTAL */ /* * line length default is 415 so it's the value we use to * calculate values for registers 0x20-0x21 * Ref. DS Pag. 67 */ usb_quickcam_i2c_add(&i2cbuff,0x20,mode? ((line_length-23)>>8):((line_length-51)>>8)); usb_quickcam_i2c_add(&i2cbuff,0x21, mode?((line_length-23)&0xff):((line_length-51)&0xff)); usb_quickcam_i2c_add(&i2cbuff,0x22,mode?0x00:0x01); //usb_quickcam_i2c_add(&i2cbuff,0x23,mode?0x9e:0x3e); usb_quickcam_i2c_add(&i2cbuff,0x23,mode?158:318&0xff); usb_quickcam_i2c_add(&i2cbuff,0x24,0xfa); // clock divisor. usb_quickcam_i2c_add(&i2cbuff,0x25,0x01); if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_control_msg gain1 failed"); goto error; } /* if (isaweb(dev)) { //EXPERIMENTAL: dark/black pixel cancellation usb_quickcam_i2c_add(&i2cbuff,0x3e,0x01); usb_quickcam_i2c_add(&i2cbuff,0x72,0x01); if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_control_msg dark/black pixel failed"); goto error; } } */ if (usb_quickcam_set1(dev, STV_REG01 , 0xb7) < 0) goto error; if (usb_quickcam_set1(dev, STV_REG02 , 0xa7) < 0) goto error; // setup usb_quickcam_i2c_add(&i2cbuff,0x11,0x18); // 0x18 or Jochen 0x40 usb_quickcam_i2c_add(&i2cbuff,0x14,0x55); // was 0x55 usb_quickcam_i2c_add(&i2cbuff,0x15,0x10); // 0x10 or Jochen:0x00 usb_quickcam_i2c_add(&i2cbuff,0x16,0x81); // Pre clock dividor. usb_quickcam_i2c_add(&i2cbuff,0x17,0x18); // they are reserved. usb_quickcam_i2c_add(&i2cbuff,0x18,0x00); usb_quickcam_i2c_add(&i2cbuff,0x77,0x5e); usb_quickcam_i2c_add(&i2cbuff,0x78,0x04);// 0x04 or Jochen:0x00 if (isaweb(dev)) usb_quickcam_i2c_add(&i2cbuff,0x79,0x11);//audio init if (usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR) < 0) { printk(KERN_ERR "usb_control_msg setup failed"); goto error; } if (usb_quickcam_set2(dev, STV_ISO_SIZE, isaweb(dev)?1023:600) < 0) // 0x380|orig:600 goto error; if (usb_quickcam_set1(dev, STV_Y_CTRL, 1) < 0) goto error; if (usb_quickcam_set1(dev,STV_SCAN_RATE, mode?0x00:0x10) < 0) goto error; if (!isaweb(dev)) { /* logitech quickam web has 0x0850 as idProduct */ if (usb_quickcam_set1(dev, STV_X_CTRL, 0x14) < 0) goto error; } return 0; error: printk(KERN_ERR "vv6410_init failed\n"); return(-1); } static int vv6410_set_shutter(struct usb_device *dev, int sval, int xval) { struct quickcam_i2c i2cbuff; int ret; usb_quickcam_i2c_new(&i2cbuff); sval = sval * 2; usb_quickcam_i2c_add(&i2cbuff,0x23,sval&255); usb_quickcam_i2c_add(&i2cbuff,0x22,sval>>8); ret = usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR); if (isaweb(dev)) usb_quickcam_set1(dev, 0x1704 , 0x01); return(ret); } static int vv6410_set_gains(struct usb_device *dev, int rgain, int bgain, int ggain) { struct quickcam_i2c i2cbuff; int gain; int ret; gain = (rgain+bgain+2*ggain)/4; if (gain>=0xF0) gain = 0xE0; gain = gain>>4; gain = 0xF0+gain; usb_quickcam_i2c_new(&i2cbuff); usb_quickcam_i2c_add(&i2cbuff,VV6410_GAIN,gain); ret = usb_quickcam_i2c_send(dev,&i2cbuff,VV6410_ADDR); if (isaweb(dev)) usb_quickcam_set1(dev, 0x1704 , 0x01); return(ret); } static int vv6410_set_size(struct usb_device *dev, int mode) { return 0; } void load_vv6410_mod(struct sensorctrl *sensor_ctrl) { sensor_ctrl->init = vv6410_init; sensor_ctrl->set_shutter = vv6410_set_shutter; sensor_ctrl->set_gains = vv6410_set_gains; sensor_ctrl->set_window = vv6410_set_window; sensor_ctrl->set_size = vv6410_set_size; sensor_ctrl->start = vv6410_start; sensor_ctrl->stop = vv6410_stop; }