diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-03-10 15:02:37 +0000 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-03-10 15:02:37 +0000 |
commit | fad837c16cdd856c68ce2e1335ad0fe836ed8ecd (patch) | |
tree | 1a6babdc2ac7e5388c482e93505fdfaf5ff97f61 /drivers/media/video/gspca/sonixb.c | |
parent | 51c6ab130642ed975681df843c772dda48a1d2ed (diff) | |
parent | 57d54889cd00db2752994b389ba714138652e60c (diff) |
Merge commit 'v2.6.34-rc1' into for-2.6.35
Diffstat (limited to 'drivers/media/video/gspca/sonixb.c')
-rw-r--r-- | drivers/media/video/gspca/sonixb.c | 451 |
1 files changed, 330 insertions, 121 deletions
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index ddff2b5ee5c..785eeb4c201 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -42,6 +42,7 @@ Reg Use #define MODULE_NAME "sonixb" +#include <linux/input.h> #include "gspca.h" MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); @@ -53,9 +54,11 @@ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ atomic_t avg_lum; int prev_avg_lum; + int exp_too_low_cnt; + int exp_too_high_cnt; + unsigned short exposure; unsigned char gain; - unsigned char exposure; unsigned char brightness; unsigned char autogain; unsigned char autogain_ignore_frames; @@ -73,8 +76,9 @@ struct sd { #define SENSOR_OV7630 2 #define SENSOR_PAS106 3 #define SENSOR_PAS202 4 -#define SENSOR_TAS5110 5 -#define SENSOR_TAS5130CXX 6 +#define SENSOR_TAS5110C 5 +#define SENSOR_TAS5110D 6 +#define SENSOR_TAS5130CXX 7 __u8 reg11; }; @@ -95,13 +99,15 @@ struct sensor_data { /* sensor_data flags */ #define F_GAIN 0x01 /* has gain */ #define F_SIF 0x02 /* sif or vga */ +#define F_COARSE_EXPO 0x04 /* exposure control is coarse */ /* priv field of struct v4l2_pix_format flags (do not use low nibble!) */ #define MODE_RAW 0x10 /* raw bayer mode */ #define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */ /* ctrl_dis helper macros */ -#define NO_EXPO ((1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX)) +#define NO_EXPO ((1 << EXPOSURE_IDX) | (1 << COARSE_EXPOSURE_IDX) | \ + (1 << AUTOGAIN_IDX)) #define NO_FREQ (1 << FREQ_IDX) #define NO_BRIGHTNESS (1 << BRIGHTNESS_IDX) @@ -127,11 +133,10 @@ struct sensor_data { } /* We calculate the autogain at the end of the transfer of a frame, at this - moment a frame with the old settings is being transmitted, and a frame is - being captured with the old settings. So if we adjust the autogain we must - ignore atleast the 2 next frames for the new settings to come into effect - before doing any other adjustments */ -#define AUTOGAIN_IGNORE_FRAMES 3 + moment a frame with the old settings is being captured and transmitted. So + if we adjust the gain or exposure we must ignore atleast the next frame for + the new settings to come into effect before doing any other adjustments. */ +#define AUTOGAIN_IGNORE_FRAMES 1 /* V4L2 controls supported by the driver */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); @@ -145,7 +150,7 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { #define BRIGHTNESS_IDX 0 { { @@ -171,7 +176,7 @@ static struct ctrl sd_ctrls[] = { .maximum = 255, .step = 1, #define GAIN_DEF 127 -#define GAIN_KNEE 200 +#define GAIN_KNEE 230 .default_value = GAIN_DEF, }, .set = sd_setgain, @@ -183,10 +188,10 @@ static struct ctrl sd_ctrls[] = { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Exposure", -#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ -#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ +#define EXPOSURE_DEF 66 /* 33 ms / 30 fps (except on PASXXX) */ +#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PASXXX) */ .minimum = 0, - .maximum = 255, + .maximum = 1023, .step = 1, .default_value = EXPOSURE_DEF, .flags = 0, @@ -194,7 +199,23 @@ static struct ctrl sd_ctrls[] = { .set = sd_setexposure, .get = sd_getexposure, }, -#define AUTOGAIN_IDX 3 +#define COARSE_EXPOSURE_IDX 3 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", +#define COARSE_EXPOSURE_DEF 2 /* 30 fps */ + .minimum = 2, + .maximum = 15, + .step = 1, + .default_value = COARSE_EXPOSURE_DEF, + .flags = 0, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, +#define AUTOGAIN_IDX 4 { { .id = V4L2_CID_AUTOGAIN, @@ -210,7 +231,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, -#define FREQ_IDX 4 +#define FREQ_IDX 5 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -219,7 +240,7 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ .step = 1, -#define FREQ_DEF 1 +#define FREQ_DEF 0 .default_value = FREQ_DEF, }, .set = sd_setfreq, @@ -345,7 +366,7 @@ static const __u8 initOv7630[] = { }; static const __u8 initOv7630_3[] = { 0x44, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0x80, /* r01 .. r08 */ - 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, /* r09 .. r10 */ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ 0x28, 0x1e, /* H & V sizes r15 .. r16 */ 0x68, 0x8f, MCK_INIT1, /* r17 .. r19 */ @@ -387,6 +408,30 @@ static const __u8 initPas106[] = { 0x18, 0x10, 0x02, 0x02, 0x09, 0x07 }; /* compression 0x86 mckinit1 0x2b */ + +/* "Known" PAS106B registers: + 0x02 clock divider + 0x03 Variable framerate bits 4-11 + 0x04 Var framerate bits 0-3, one must leave the 4 msb's at 0 !! + The variable framerate control must never be set lower then 300, + which sets the framerate at 90 / reg02, otherwise vsync is lost. + 0x05 Shutter Time Line Offset, this can be used as an exposure control: + 0 = use full frame time, 255 = no exposure at all + Note this may never be larger then "var-framerate control" / 2 - 2. + When var-framerate control is < 514, no exposure is reached at the max + allowed value for the framerate control value, rather then at 255. + 0x06 Shutter Time Pixel Offset, like reg05 this influences exposure, but + only a very little bit, leave at 0xcd + 0x07 offset sign bit (bit0 1 > negative offset) + 0x08 offset + 0x09 Blue Gain + 0x0a Green1 Gain + 0x0b Green2 Gain + 0x0c Red Gain + 0x0e Global gain + 0x13 Write 1 to commit settings to sensor +*/ + static const __u8 pas106_sensor_init[][8] = { /* Pixel Clock Divider 6 */ { 0xa1, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14 }, @@ -433,37 +478,55 @@ static const __u8 initPas202[] = { 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x0a, - 0x28, 0x1e, 0x28, 0x89, 0x20, + 0x28, 0x1e, 0x20, 0x89, 0x20, 0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c }; + +/* "Known" PAS202BCB registers: + 0x02 clock divider + 0x04 Variable framerate bits 6-11 (*) + 0x05 Var framerate bits 0-5, one must leave the 2 msb's at 0 !! + 0x07 Blue Gain + 0x08 Green Gain + 0x09 Red Gain + 0x0b offset sign bit (bit0 1 > negative offset) + 0x0c offset + 0x0e Unknown image is slightly brighter when bit 0 is 0, if reg0f is 0 too, + leave at 1 otherwise we get a jump in our exposure control + 0x0f Exposure 0-255, 0 = use full frame time, 255 = no exposure at all + 0x10 Master gain 0 - 31 + 0x11 write 1 to apply changes + (*) The variable framerate control must never be set lower then 500 + which sets the framerate at 30 / reg02, otherwise vsync is lost. +*/ static const __u8 pas202_sensor_init[][8] = { - {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}, + /* Set the clock divider to 4 -> 30 / 4 = 7.5 fps, we would like + to set it lower, but for some reason the bridge starts missing + vsync's then */ + {0xa0, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x10}, {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10}, {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10}, - {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x00, 0x32, 0x10}, + {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x01, 0x32, 0x10}, {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10}, {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10}, {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10}, {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10}, {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, - {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x10}, - {0xb0, 0x40, 0x0e, 0x00, 0x3d, 0x00, 0x63, 0x10}, - - {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, - {0xa0, 0x40, 0x10, 0x08, 0x3d, 0x00, 0x63, 0x15}, - {0xa0, 0x40, 0x02, 0x04, 0x3d, 0x00, 0x63, 0x16}, - {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, - {0xb0, 0x40, 0x0e, 0x00, 0x31, 0x00, 0x63, 0x16}, - {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, - {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}, - {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, }; -static const __u8 initTas5110[] = { +static const __u8 initTas5110c[] = { 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x45, 0x09, 0x0a, + 0x00, 0x00, 0x00, 0x45, 0x09, 0x0a, + 0x16, 0x12, 0x60, 0x86, 0x2b, + 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 +}; +/* Same as above, except a different hstart */ +static const __u8 initTas5110d[] = { + 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x41, 0x09, 0x0a, 0x16, 0x12, 0x60, 0x86, 0x2b, 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 }; @@ -476,7 +539,7 @@ static const __u8 tas5110_sensor_init[][8] = { static const __u8 initTas5130[] = { 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x68, 0x0c, 0x0a, + 0x00, 0x00, 0x00, 0x68, 0x0c, 0x0a, 0x28, 0x1e, 0x60, COMP, MCK_INIT, 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c }; @@ -493,12 +556,14 @@ SENS(initHv7131, NULL, hv7131_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, 0), SENS(initOv6650, NULL, ov6650_sensor_init, NULL, NULL, F_GAIN|F_SIF, 0, 0x60), SENS(initOv7630, initOv7630_3, ov7630_sensor_init, NULL, ov7630_sensor_init_3, F_GAIN, 0, 0x21), -SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ, +SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_GAIN|F_SIF, NO_FREQ, 0), -SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0, - NO_EXPO|NO_FREQ, 0), -SENS(initTas5110, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF, - NO_BRIGHTNESS|NO_FREQ, 0), +SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, F_GAIN, + NO_FREQ, 0), +SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL, + F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), +SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL, + F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), SENS(initTas5130, NULL, tas5130_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, 0), }; @@ -587,42 +652,28 @@ static void setbrightness(struct gspca_dev *gspca_dev) goto err; break; } - case SENSOR_PAS106: { - __u8 i2c1[] = - {0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14}; - - i2c1[3] = sd->brightness >> 3; - i2c1[2] = 0x0e; - if (i2c_w(gspca_dev, i2c1) < 0) - goto err; - i2c1[3] = 0x01; - i2c1[2] = 0x13; - if (i2c_w(gspca_dev, i2c1) < 0) - goto err; - break; - } + case SENSOR_PAS106: case SENSOR_PAS202: { - /* __u8 i2cpexpo1[] = - {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x16}; */ - __u8 i2cpexpo[] = - {0xb0, 0x40, 0x0e, 0x01, 0xab, 0x00, 0x63, 0x16}; - __u8 i2cp202[] = - {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}; - static __u8 i2cpdoit[] = - {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}; - - /* change reg 0x10 */ - i2cpexpo[4] = 0xff - sd->brightness; -/* if(i2c_w(gspca_dev,i2cpexpo1) < 0) - goto err; */ -/* if(i2c_w(gspca_dev,i2cpdoit) < 0) - goto err; */ - if (i2c_w(gspca_dev, i2cpexpo) < 0) - goto err; - if (i2c_w(gspca_dev, i2cpdoit) < 0) - goto err; - i2cp202[3] = sd->brightness >> 3; - if (i2c_w(gspca_dev, i2cp202) < 0) + __u8 i2cpbright[] = + {0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16}; + __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; + + /* PAS106 uses reg 7 and 8 instead of b and c */ + if (sd->sensor == SENSOR_PAS106) { + i2cpbright[2] = 7; + i2cpdoit[2] = 0x13; + } + + if (sd->brightness < 127) { + /* change reg 0x0b, signreg */ + i2cpbright[3] = 0x01; + /* set reg 0x0c, offset */ + i2cpbright[4] = 127 - sd->brightness; + } else + i2cpbright[4] = sd->brightness - 127; + + if (i2c_w(gspca_dev, i2cpbright) < 0) goto err; if (i2c_w(gspca_dev, i2cpdoit) < 0) goto err; @@ -652,7 +703,8 @@ static void setsensorgain(struct gspca_dev *gspca_dev) switch (sd->sensor) { - case SENSOR_TAS5110: { + case SENSOR_TAS5110C: + case SENSOR_TAS5110D: { __u8 i2c[] = {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; @@ -674,6 +726,37 @@ static void setsensorgain(struct gspca_dev *gspca_dev) goto err; break; } + case SENSOR_PAS106: + case SENSOR_PAS202: { + __u8 i2cpgain[] = + {0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15}; + __u8 i2cpcolorgain[] = + {0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15}; + __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; + + /* PAS106 uses different regs (and has split green gains) */ + if (sd->sensor == SENSOR_PAS106) { + i2cpgain[2] = 0x0e; + i2cpcolorgain[0] = 0xd0; + i2cpcolorgain[2] = 0x09; + i2cpdoit[2] = 0x13; + } + + i2cpgain[3] = sd->gain >> 3; + i2cpcolorgain[3] = sd->gain >> 4; + i2cpcolorgain[4] = sd->gain >> 4; + i2cpcolorgain[5] = sd->gain >> 4; + i2cpcolorgain[6] = sd->gain >> 4; + + if (i2c_w(gspca_dev, i2cpgain) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpcolorgain) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpdoit) < 0) + goto err; + break; + } } return; err: @@ -684,19 +767,21 @@ static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u8 gain; - __u8 rgb_value; + __u8 buf[2] = { 0, 0 }; + + if (sensor_data[sd->sensor].flags & F_GAIN) { + /* Use the sensor gain to do the actual gain */ + setsensorgain(gspca_dev); + return; + } gain = sd->gain >> 4; /* red and blue gain */ - rgb_value = gain << 4 | gain; - reg_w(gspca_dev, 0x10, &rgb_value, 1); + buf[0] = gain << 4 | gain; /* green gain */ - rgb_value = gain; - reg_w(gspca_dev, 0x11, &rgb_value, 1); - - if (sensor_data[sd->sensor].flags & F_GAIN) - setsensorgain(gspca_dev); + buf[1] = gain; + reg_w(gspca_dev, 0x10, buf, 2); } static void setexposure(struct gspca_dev *gspca_dev) @@ -704,17 +789,12 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; switch (sd->sensor) { - case SENSOR_TAS5110: { - __u8 reg; - + case SENSOR_TAS5110C: + case SENSOR_TAS5110D: { /* register 19's high nibble contains the sn9c10x clock divider The high nibble configures the no fps according to the formula: 60 / high_nibble. With a maximum of 30 fps */ - reg = 120 * sd->exposure / 1000; - if (reg < 2) - reg = 2; - else if (reg > 15) - reg = 15; + __u8 reg = sd->exposure; reg = (reg << 4) | 0x0b; reg_w(gspca_dev, 0x19, ®, 1); break; @@ -750,20 +830,21 @@ static void setexposure(struct gspca_dev *gspca_dev) } else reg10_max = 0x41; - reg11 = (60 * sd->exposure + 999) / 1000; + reg11 = (15 * sd->exposure + 999) / 1000; if (reg11 < 1) reg11 = 1; else if (reg11 > 16) reg11 = 16; - /* In 640x480, if the reg11 has less than 3, the image is - unstable (not enough bandwidth). */ - if (gspca_dev->width == 640 && reg11 < 3) - reg11 = 3; + /* In 640x480, if the reg11 has less than 4, the image is + unstable (the bridge goes into a higher compression mode + which we have not reverse engineered yet). */ + if (gspca_dev->width == 640 && reg11 < 4) + reg11 = 4; /* frame exposure time in ms = 1000 * reg11 / 30 -> - reg10 = sd->exposure * 2 * reg10_max / (1000 * reg11 / 30) */ - reg10 = (sd->exposure * 60 * reg10_max) / (1000 * reg11); + reg10 = (sd->exposure / 2) * reg10_max / (1000 * reg11 / 30) */ + reg10 = (sd->exposure * 15 * reg10_max) / (1000 * reg11); /* Don't allow this to get below 10 when using autogain, the steps become very large (relatively) when below 10 causing @@ -786,10 +867,85 @@ static void setexposure(struct gspca_dev *gspca_dev) if (i2c_w(gspca_dev, i2c) == 0) sd->reg11 = reg11; else - PDEBUG(D_ERR, "i2c error exposure"); + goto err; + break; + } + case SENSOR_PAS202: { + __u8 i2cpframerate[] = + {0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16}; + __u8 i2cpexpo[] = + {0xa0, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x16}; + const __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; + int framerate_ctrl; + + /* The exposure knee for the autogain algorithm is 200 + (100 ms / 10 fps on other sensors), for values below this + use the control for setting the partial frame expose time, + above that use variable framerate. This way we run at max + framerate (640x480@7.5 fps, 320x240@10fps) until the knee + is reached. Using the variable framerate control above 200 + is better then playing around with both clockdiv + partial + frame exposure times (like we are doing with the ov chips), + as that sometimes leads to jumps in the exposure control, + which are bad for auto exposure. */ + if (sd->exposure < 200) { + i2cpexpo[3] = 255 - (sd->exposure * 255) / 200; + framerate_ctrl = 500; + } else { + /* The PAS202's exposure control goes from 0 - 4095, + but anything below 500 causes vsync issues, so scale + our 200-1023 to 500-4095 */ + framerate_ctrl = (sd->exposure - 200) * 1000 / 229 + + 500; + } + + i2cpframerate[3] = framerate_ctrl >> 6; + i2cpframerate[4] = framerate_ctrl & 0x3f; + if (i2c_w(gspca_dev, i2cpframerate) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpexpo) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpdoit) < 0) + goto err; + break; + } + case SENSOR_PAS106: { + __u8 i2cpframerate[] = + {0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14}; + __u8 i2cpexpo[] = + {0xa1, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x14}; + const __u8 i2cpdoit[] = + {0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14}; + int framerate_ctrl; + + /* For values below 150 use partial frame exposure, above + that use framerate ctrl */ + if (sd->exposure < 150) { + i2cpexpo[3] = 150 - sd->exposure; + framerate_ctrl = 300; + } else { + /* The PAS106's exposure control goes from 0 - 4095, + but anything below 300 causes vsync issues, so scale + our 150-1023 to 300-4095 */ + framerate_ctrl = (sd->exposure - 150) * 1000 / 230 + + 300; + } + + i2cpframerate[3] = framerate_ctrl >> 4; + i2cpframerate[4] = framerate_ctrl & 0x0f; + if (i2c_w(gspca_dev, i2cpframerate) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpexpo) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpdoit) < 0) + goto err; break; } } + return; +err: + PDEBUG(D_ERR, "i2c error exposure"); } static void setfreq(struct gspca_dev *gspca_dev) @@ -823,30 +979,43 @@ static void setfreq(struct gspca_dev *gspca_dev) } } +#include "coarse_expo_autogain.h" + static void do_autogain(struct gspca_dev *gspca_dev) { - int deadzone, desired_avg_lum; + int deadzone, desired_avg_lum, result; struct sd *sd = (struct sd *) gspca_dev; int avg_lum = atomic_read(&sd->avg_lum); - if (avg_lum == -1) + if (avg_lum == -1 || !sd->autogain) return; + if (sd->autogain_ignore_frames > 0) { + sd->autogain_ignore_frames--; + return; + } + /* SIF / VGA sensors have a different autoexposure area and thus different avg_lum values for the same picture brightness */ if (sensor_data[sd->sensor].flags & F_SIF) { - deadzone = 1000; - desired_avg_lum = 7000; + deadzone = 500; + /* SIF sensors tend to overexpose, so keep this small */ + desired_avg_lum = 5000; } else { - deadzone = 3000; - desired_avg_lum = 23000; + deadzone = 1500; + desired_avg_lum = 18000; } - if (sd->autogain_ignore_frames > 0) - sd->autogain_ignore_frames--; - else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, - sd->brightness * desired_avg_lum / 127, - deadzone, GAIN_KNEE, EXPOSURE_KNEE)) { + if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) + result = gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, + sd->brightness * desired_avg_lum / 127, + deadzone); + else + result = gspca_auto_gain_n_exposure(gspca_dev, avg_lum, + sd->brightness * desired_avg_lum / 127, + deadzone, GAIN_KNEE, EXPOSURE_KNEE); + + if (result) { PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d", (int)sd->gain, (int)sd->exposure); sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; @@ -881,7 +1050,13 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = BRIGHTNESS_DEF; sd->gain = GAIN_DEF; - sd->exposure = EXPOSURE_DEF; + if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) { + sd->exposure = COARSE_EXPOSURE_DEF; + gspca_dev->ctrl_dis |= (1 << EXPOSURE_IDX); + } else { + sd->exposure = EXPOSURE_DEF; + gspca_dev->ctrl_dis |= (1 << COARSE_EXPOSURE_IDX); + } if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX)) sd->autogain = 0; /* Disable do_autogain callback */ else @@ -917,9 +1092,6 @@ static int sd_start(struct gspca_dev *gspca_dev) reg12_19[6] = sn9c10x[0x18 - 1] | (mode << 4); /* Special cases where reg 17 and or 19 value depends on mode */ switch (sd->sensor) { - case SENSOR_PAS202: - reg12_19[5] = mode ? 0x24 : 0x20; - break; case SENSOR_TAS5130CXX: /* probably not mode specific at all most likely the upper nibble of 0x19 is exposure (clock divider) just as with @@ -955,6 +1127,16 @@ static int sd_start(struct gspca_dev *gspca_dev) sensor_data[sd->sensor].sensor_bridge_init_size[ sd->bridge]); + /* Mode specific sensor setup */ + switch (sd->sensor) { + case SENSOR_PAS202: { + const __u8 i2cpclockdiv[] = + {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}; + /* clockdiv from 4 to 3 (7.5 -> 10 fps) when in low res mode */ + if (mode) + i2c_w(gspca_dev, i2cpclockdiv); + } + } /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */ reg_w(gspca_dev, 0x15, ®12_19[3], 2); /* compression register */ @@ -985,6 +1167,8 @@ static int sd_start(struct gspca_dev *gspca_dev) sd->frames_to_drop = 0; sd->autogain_ignore_frames = 0; + sd->exp_too_high_cnt = 0; + sd->exp_too_low_cnt = 0; atomic_set(&sd->avg_lum, -1); return 0; } @@ -1143,11 +1327,14 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->autogain = val; + sd->exp_too_high_cnt = 0; + sd->exp_too_low_cnt = 0; + /* when switching to autogain set defaults to make sure we are on a valid point of the autogain gain / exposure knee graph, and give this change time to take effect before doing autogain. */ - if (sd->autogain) { + if (sd->autogain && !(sensor_data[sd->sensor].flags & F_COARSE_EXPO)) { sd->exposure = EXPOSURE_DEF; sd->gain = GAIN_DEF; if (gspca_dev->streaming) { @@ -1207,6 +1394,25 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +#ifdef CONFIG_INPUT +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrupt packet length */ +{ + int ret = -EINVAL; + + if (len == 1 && data[0] == 1) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + + return ret; +} +#endif + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -1219,6 +1425,9 @@ static const struct sd_desc sd_desc = { .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, .dq_callback = do_autogain, +#ifdef CONFIG_INPUT + .int_pkt_scan = sd_int_pkt_scan, +#endif }; /* -- module initialisation -- */ @@ -1227,21 +1436,21 @@ static const struct sd_desc sd_desc = { static const struct usb_device_id device_table[] __devinitconst = { - {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110, 102)}, /* TAS5110C1B */ - {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110, 101)}, /* TAS5110C1B */ + {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110C, 102)}, /* TAS5110C1B */ + {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110C, 101)}, /* TAS5110C1B */ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110, 101)}, /* TAS5110D */ + {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110D, 101)}, /* TAS5110D */ +#endif {USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)}, {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)}, -#endif {USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)}, {USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)}, {USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)}, +#endif {USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)}, {USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)}, -#endif {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)}, {USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE |