aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorHans de Goede <j.w.r.degoede@hhs.nl>2008-07-10 09:40:53 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-07-20 06:25:59 -0400
commitdcef3237b652e1c02093feac0f443485a144f035 (patch)
tree81df2d056ef1c4179868512076d9339dbd98680a /drivers
parentd0d0e39bc5912793405d3f84ffc982fa400e6cc0 (diff)
V4L/DVB (8348): gspca: Add auto gain/exposure to sonixb and tas5110 / ov6650 sensors.
sonixb: Do auto gain for tas5110 / ov6650 sensors. pac207: Move the auto_gain function to gspca. gspca: New function gspca_auto_gain_n_exposure(). Signed-off-by: Hans de Goede <j.w.r.degoede@hhs.nl> Signed-off-by: Jean-Francois Moine <moinejf@free.fr> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/video/gspca/gspca.c88
-rw-r--r--drivers/media/video/gspca/gspca.h2
-rw-r--r--drivers/media/video/gspca/pac207.c68
-rw-r--r--drivers/media/video/gspca/sonixb.c301
4 files changed, 350 insertions, 109 deletions
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index 09f190c689d5..2ffb00ab0811 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -1790,6 +1790,94 @@ void gspca_disconnect(struct usb_interface *intf)
1790} 1790}
1791EXPORT_SYMBOL(gspca_disconnect); 1791EXPORT_SYMBOL(gspca_disconnect);
1792 1792
1793/* -- cam driver utility functions -- */
1794
1795/* auto gain and exposure algorithm based on the knee algorithm described here:
1796 http://ytse.tricolour.net/docs/LowLightOptimization.html
1797
1798 Returns 0 if no changes were made, 1 if the gain and or exposure settings
1799 where changed. */
1800int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
1801 int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee)
1802{
1803 int i, steps, gain, orig_gain, exposure, orig_exposure, autogain;
1804 const struct ctrl *gain_ctrl = NULL;
1805 const struct ctrl *exposure_ctrl = NULL;
1806 const struct ctrl *autogain_ctrl = NULL;
1807 int retval = 0;
1808
1809 for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
1810 if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN)
1811 gain_ctrl = &gspca_dev->sd_desc->ctrls[i];
1812 if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE)
1813 exposure_ctrl = &gspca_dev->sd_desc->ctrls[i];
1814 if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_AUTOGAIN)
1815 autogain_ctrl = &gspca_dev->sd_desc->ctrls[i];
1816 }
1817 if (!gain_ctrl || !exposure_ctrl || !autogain_ctrl) {
1818 PDEBUG(D_ERR, "Error: gspca_auto_gain_n_exposure called "
1819 "on cam without (auto)gain/exposure");
1820 return 0;
1821 }
1822
1823 if (gain_ctrl->get(gspca_dev, &gain) ||
1824 exposure_ctrl->get(gspca_dev, &exposure) ||
1825 autogain_ctrl->get(gspca_dev, &autogain) || !autogain)
1826 return 0;
1827
1828 orig_gain = gain;
1829 orig_exposure = exposure;
1830
1831 /* If we are of a multiple of deadzone, do multiple steps to reach the
1832 desired lumination fast (with the risc of a slight overshoot) */
1833 steps = abs(desired_avg_lum - avg_lum) / deadzone;
1834
1835 PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n",
1836 avg_lum, desired_avg_lum, steps);
1837
1838 for (i = 0; i < steps; i++) {
1839 if (avg_lum > desired_avg_lum) {
1840 if (gain > gain_knee)
1841 gain--;
1842 else if (exposure > exposure_knee)
1843 exposure--;
1844 else if (gain > gain_ctrl->qctrl.default_value)
1845 gain--;
1846 else if (exposure > exposure_ctrl->qctrl.minimum)
1847 exposure--;
1848 else if (gain > gain_ctrl->qctrl.minimum)
1849 gain--;
1850 else
1851 break;
1852 } else {
1853 if (gain < gain_ctrl->qctrl.default_value)
1854 gain++;
1855 else if (exposure < exposure_knee)
1856 exposure++;
1857 else if (gain < gain_knee)
1858 gain++;
1859 else if (exposure < exposure_ctrl->qctrl.maximum)
1860 exposure++;
1861 else if (gain < gain_ctrl->qctrl.maximum)
1862 gain++;
1863 else
1864 break;
1865 }
1866 }
1867
1868 if (gain != orig_gain) {
1869 gain_ctrl->set(gspca_dev, gain);
1870 retval = 1;
1871 }
1872 if (exposure != orig_exposure) {
1873 exposure_ctrl->set(gspca_dev, exposure);
1874 retval = 1;
1875 }
1876
1877 return retval;
1878}
1879EXPORT_SYMBOL(gspca_auto_gain_n_exposure);
1880
1793/* -- module insert / remove -- */ 1881/* -- module insert / remove -- */
1794static int __init gspca_init(void) 1882static int __init gspca_init(void)
1795{ 1883{
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
index 9b645af81a07..78fccefcd576 100644
--- a/drivers/media/video/gspca/gspca.h
+++ b/drivers/media/video/gspca/gspca.h
@@ -170,4 +170,6 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
170 struct gspca_frame *frame, 170 struct gspca_frame *frame,
171 const __u8 *data, 171 const __u8 *data,
172 int len); 172 int len);
173int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
174 int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee);
173#endif /* GSPCAV2_H */ 175#endif /* GSPCAV2_H */
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
index 4f197c1f4a76..5d68d3f42262 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/video/gspca/pac207.c
@@ -357,70 +357,20 @@ static void sd_close(struct gspca_dev *gspca_dev)
357{ 357{
358} 358}
359 359
360/* auto gain and exposure algorithm based on the knee algorithm described here:
361 * <http://ytse.tricolour.net/docs/LowLightOptimization.html> */
362static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) 360static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
363{ 361{
364 struct sd *sd = (struct sd *) gspca_dev; 362 struct sd *sd = (struct sd *) gspca_dev;
365 int i, steps, desired_avg_lum;
366 int orig_gain = sd->gain;
367 int orig_exposure = sd->exposure;
368 int avg_lum = atomic_read(&sd->avg_lum); 363 int avg_lum = atomic_read(&sd->avg_lum);
369 364
370 if (!sd->autogain || avg_lum == -1) 365 if (avg_lum == -1)
371 return; 366 return;
372 367
373 if (sd->autogain_ignore_frames > 0) { 368 if (sd->autogain_ignore_frames > 0)
374 sd->autogain_ignore_frames--; 369 sd->autogain_ignore_frames--;
375 return; 370 else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
376 } 371 100 + sd->brightness / 2, PAC207_AUTOGAIN_DEADZONE,
377 372 PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE))
378 /* correct desired lumination for the configured brightness */
379 desired_avg_lum = 100 + sd->brightness / 2;
380
381 /* If we are of a multiple of deadzone, do multiple step to reach the
382 desired lumination fast (with the risc of a slight overshoot) */
383 steps = abs(desired_avg_lum - avg_lum) / PAC207_AUTOGAIN_DEADZONE;
384
385 for (i = 0; i < steps; i++) {
386 if (avg_lum > desired_avg_lum) {
387 if (sd->gain > PAC207_GAIN_KNEE)
388 sd->gain--;
389 else if (sd->exposure > PAC207_EXPOSURE_KNEE)
390 sd->exposure--;
391 else if (sd->gain > PAC207_GAIN_DEFAULT)
392 sd->gain--;
393 else if (sd->exposure > PAC207_EXPOSURE_MIN)
394 sd->exposure--;
395 else if (sd->gain > PAC207_GAIN_MIN)
396 sd->gain--;
397 else
398 break;
399 } else {
400 if (sd->gain < PAC207_GAIN_DEFAULT)
401 sd->gain++;
402 else if (sd->exposure < PAC207_EXPOSURE_KNEE)
403 sd->exposure++;
404 else if (sd->gain < PAC207_GAIN_KNEE)
405 sd->gain++;
406 else if (sd->exposure < PAC207_EXPOSURE_MAX)
407 sd->exposure++;
408 else if (sd->gain < PAC207_GAIN_MAX)
409 sd->gain++;
410 else
411 break;
412 }
413 }
414
415 if (sd->exposure != orig_exposure || sd->gain != orig_gain) {
416 if (sd->exposure != orig_exposure)
417 pac207_write_reg(gspca_dev, 0x0002, sd->exposure);
418 if (sd->gain != orig_gain)
419 pac207_write_reg(gspca_dev, 0x000e, sd->gain);
420 pac207_write_reg(gspca_dev, 0x13, 0x01); /* load reg to sen */
421 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
422 sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES; 373 sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES;
423 }
424} 374}
425 375
426static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev, 376static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev,
@@ -546,10 +496,6 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
546{ 496{
547 struct sd *sd = (struct sd *) gspca_dev; 497 struct sd *sd = (struct sd *) gspca_dev;
548 498
549 /* don't allow mucking with exposure when using autogain */
550 if (sd->autogain)
551 return -EINVAL;
552
553 sd->exposure = val; 499 sd->exposure = val;
554 if (gspca_dev->streaming) 500 if (gspca_dev->streaming)
555 setexposure(gspca_dev); 501 setexposure(gspca_dev);
@@ -568,10 +514,6 @@ static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
568{ 514{
569 struct sd *sd = (struct sd *) gspca_dev; 515 struct sd *sd = (struct sd *) gspca_dev;
570 516
571 /* don't allow mucking with gain when using autogain */
572 if (sd->autogain)
573 return -EINVAL;
574
575 sd->gain = val; 517 sd->gain = val;
576 if (gspca_dev->streaming) 518 if (gspca_dev->streaming)
577 setgain(gspca_dev); 519 setgain(gspca_dev);
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index 95a6a8e98b97..274df69e6f6d 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -35,11 +35,19 @@ MODULE_LICENSE("GPL");
35struct sd { 35struct sd {
36 struct gspca_dev gspca_dev; /* !! must be the first item */ 36 struct gspca_dev gspca_dev; /* !! must be the first item */
37 37
38 struct sd_desc sd_desc; /* our nctrls differ dependend upon the
39 sensor, so we use a per cam copy */
40 atomic_t avg_lum;
41
42 unsigned short gain;
43 unsigned short exposure;
38 unsigned char brightness; 44 unsigned char brightness;
39 unsigned char contrast; 45 unsigned char autogain;
46 unsigned char autogain_ignore_frames;
40 47
41 unsigned char fr_h_sz; /* size of frame header */ 48 unsigned char fr_h_sz; /* size of frame header */
42 char sensor; /* Type of image sensor chip */ 49 char sensor; /* Type of image sensor chip */
50 char sensor_has_gain;
43#define SENSOR_HV7131R 0 51#define SENSOR_HV7131R 0
44#define SENSOR_OV6650 1 52#define SENSOR_OV6650 1
45#define SENSOR_OV7630 2 53#define SENSOR_OV7630 2
@@ -59,11 +67,24 @@ struct sd {
59 67
60#define SYS_CLK 0x04 68#define SYS_CLK 0x04
61 69
70/* We calculate the autogain at the end of the transfer of a frame, at this
71 moment a frame with the old settings is being transmitted, and a frame is
72 being captured with the old settings. So if we adjust the autogain we must
73 ignore atleast the 2 next frames for the new settings to come into effect
74 before doing any other adjustments */
75#define AUTOGAIN_IGNORE_FRAMES 3
76#define AUTOGAIN_DEADZONE 500
77#define DESIRED_AVG_LUM 7000
78
62/* V4L2 controls supported by the driver */ 79/* V4L2 controls supported by the driver */
63static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); 80static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
64static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); 81static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
65static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); 82static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
66static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); 83static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
84static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
85static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
86static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
87static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
67 88
68static struct ctrl sd_ctrls[] = { 89static struct ctrl sd_ctrls[] = {
69#define SD_BRIGHTNESS 0 90#define SD_BRIGHTNESS 0
@@ -75,24 +96,59 @@ static struct ctrl sd_ctrls[] = {
75 .minimum = 0, 96 .minimum = 0,
76 .maximum = 255, 97 .maximum = 255,
77 .step = 1, 98 .step = 1,
78 .default_value = 127, 99#define BRIGHTNESS_DEF 127
100 .default_value = BRIGHTNESS_DEF,
79 }, 101 },
80 .set = sd_setbrightness, 102 .set = sd_setbrightness,
81 .get = sd_getbrightness, 103 .get = sd_getbrightness,
82 }, 104 },
83#define SD_CONTRAST 1 105#define SD_GAIN 1
84 { 106 {
85 { 107 {
86 .id = V4L2_CID_CONTRAST, 108 .id = V4L2_CID_GAIN,
87 .type = V4L2_CTRL_TYPE_INTEGER, 109 .type = V4L2_CTRL_TYPE_INTEGER,
88 .name = "Contrast", 110 .name = "Gain",
89 .minimum = 0, 111 .minimum = 0,
90 .maximum = 255, 112 .maximum = 511,
91 .step = 1, 113 .step = 1,
92 .default_value = 127, 114#define GAIN_DEF 255
115#define GAIN_KNEE 400
116 .default_value = GAIN_DEF,
93 }, 117 },
94 .set = sd_setcontrast, 118 .set = sd_setgain,
95 .get = sd_getcontrast, 119 .get = sd_getgain,
120 },
121#define SD_EXPOSURE 2
122 {
123 {
124 .id = V4L2_CID_EXPOSURE,
125 .type = V4L2_CTRL_TYPE_INTEGER,
126 .name = "Exposure",
127#define EXPOSURE_DEF 0
128#define EXPOSURE_KNEE 353 /* 10 fps */
129 .minimum = 0,
130 .maximum = 511,
131 .step = 1,
132 .default_value = EXPOSURE_DEF,
133 .flags = 0,
134 },
135 .set = sd_setexposure,
136 .get = sd_getexposure,
137 },
138#define SD_AUTOGAIN 3
139 {
140 {
141 .id = V4L2_CID_AUTOGAIN,
142 .type = V4L2_CTRL_TYPE_BOOLEAN,
143 .name = "Automatic Gain (and Exposure)",
144 .minimum = 0,
145 .maximum = 1,
146 .step = 1,
147 .default_value = 1,
148 .flags = 0,
149 },
150 .set = sd_setautogain,
151 .get = sd_getautogain,
96 }, 152 },
97}; 153};
98 154
@@ -153,8 +209,12 @@ static const __u8 ov6650_sensor_init[][8] =
153 /* Bright, contrast, etc are set througth SCBB interface. 209 /* Bright, contrast, etc are set througth SCBB interface.
154 * AVCAP on win2 do not send any data on this controls. */ 210 * AVCAP on win2 do not send any data on this controls. */
155 /* Anyway, some registers appears to alter bright and constrat */ 211 /* Anyway, some registers appears to alter bright and constrat */
212
213 /* Reset sensor */
156 {0xa0, 0x60, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, 214 {0xa0, 0x60, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10},
215 /* Set clock register 0x11 low nibble is clock divider */
157 {0xd0, 0x60, 0x11, 0xc0, 0x1b, 0x18, 0xc1, 0x10}, 216 {0xd0, 0x60, 0x11, 0xc0, 0x1b, 0x18, 0xc1, 0x10},
217 /* Next some unknown stuff */
158 {0xb0, 0x60, 0x15, 0x00, 0x02, 0x18, 0xc1, 0x10}, 218 {0xb0, 0x60, 0x15, 0x00, 0x02, 0x18, 0xc1, 0x10},
159/* {0xa0, 0x60, 0x1b, 0x01, 0x02, 0x18, 0xc1, 0x10}, 219/* {0xa0, 0x60, 0x1b, 0x01, 0x02, 0x18, 0xc1, 0x10},
160 * THIS SET GREEN SCREEN 220 * THIS SET GREEN SCREEN
@@ -163,31 +223,18 @@ static const __u8 ov6650_sensor_init[][8] =
163 {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, /* format out? */ 223 {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, /* format out? */
164 {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, 224 {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10},
165 {0xa0, 0x60, 0x30, 0x3d, 0x0A, 0xd8, 0xa4, 0x10}, 225 {0xa0, 0x60, 0x30, 0x3d, 0x0A, 0xd8, 0xa4, 0x10},
226 /* Disable autobright ? */
166 {0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10}, 227 {0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10},
228 /* Some more unknown stuff */
167 {0xa0, 0x60, 0x68, 0x04, 0x68, 0xd8, 0xa4, 0x10}, 229 {0xa0, 0x60, 0x68, 0x04, 0x68, 0xd8, 0xa4, 0x10},
168 {0xd0, 0x60, 0x17, 0x24, 0xd6, 0x04, 0x94, 0x10}, /* Clipreg */ 230 {0xd0, 0x60, 0x17, 0x24, 0xd6, 0x04, 0x94, 0x10}, /* Clipreg */
169 {0xa0, 0x60, 0x10, 0x5d, 0x99, 0x04, 0x94, 0x16},
170 {0xa0, 0x60, 0x2d, 0x0a, 0x99, 0x04, 0x94, 0x16},
171 {0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16},
172 {0xa0, 0x60, 0x33, 0x40, 0x99, 0x04, 0x94, 0x16},
173 {0xa0, 0x60, 0x11, 0xc0, 0x99, 0x04, 0x94, 0x16},
174 {0xa0, 0x60, 0x00, 0x16, 0x99, 0x04, 0x94, 0x15}, /* bright / Lumino */
175 {0xa0, 0x60, 0x2b, 0xab, 0x99, 0x04, 0x94, 0x15},
176 /* ?flicker o brillo */
177 {0xa0, 0x60, 0x2d, 0x2a, 0x99, 0x04, 0x94, 0x15},
178 {0xa0, 0x60, 0x2d, 0x2b, 0x99, 0x04, 0x94, 0x16},
179 {0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16},
180 {0xa0, 0x60, 0x33, 0x00, 0x99, 0x04, 0x94, 0x16},
181 {0xa0, 0x60, 0x10, 0x57, 0x99, 0x04, 0x94, 0x16}, 231 {0xa0, 0x60, 0x10, 0x57, 0x99, 0x04, 0x94, 0x16},
182 {0xa0, 0x60, 0x2d, 0x2b, 0x99, 0x04, 0x94, 0x16}, 232 /* Framerate adjust register for artificial light 50 hz flicker
183 {0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16}, 233 compensation, identical to ov6630 0x2b register, see 6630 datasheet.
184 /* Low Light (Enabled: 0x32 0x1 | Disabled: 0x32 0x00) */ 234 0x4f -> (30 fps -> 25 fps), 0x00 -> no adjustment */
185 {0xa0, 0x60, 0x33, 0x29, 0x99, 0x04, 0x94, 0x16}, 235 {0xa0, 0x60, 0x2b, 0x4f, 0x99, 0x04, 0x94, 0x15},
186 /* Low Ligth (Enabled: 0x33 0x13 | Disabled: 0x33 0x29) */
187/* {0xa0, 0x60, 0x11, 0xc1, 0x99, 0x04, 0x94, 0x16}, */
188 {0xa0, 0x60, 0x00, 0x17, 0x99, 0x04, 0x94, 0x15}, /* clip? r */
189 {0xa0, 0x60, 0x00, 0x18, 0x99, 0x04, 0x94, 0x15}, /* clip? r */
190}; 236};
237
191static const __u8 initOv7630[] = { 238static const __u8 initOv7630[] = {
192 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */ 239 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */
193 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ 240 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */
@@ -469,8 +516,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
469 goto err; 516 goto err;
470 break; 517 break;
471 } 518 }
472 case SENSOR_TAS5130CXX: 519 case SENSOR_TAS5130CXX: {
473 case SENSOR_TAS5110: {
474 __u8 i2c[] = 520 __u8 i2c[] =
475 {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; 521 {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10};
476 522
@@ -481,24 +527,112 @@ static void setbrightness(struct gspca_dev *gspca_dev)
481 goto err; 527 goto err;
482 break; 528 break;
483 } 529 }
530 case SENSOR_TAS5110:
531 /* FIXME figure out howto control brightness on TAS5110 */
532 break;
484 } 533 }
485 return; 534 return;
486err: 535err:
487 PDEBUG(D_ERR, "i2c error brightness"); 536 PDEBUG(D_ERR, "i2c error brightness");
488} 537}
489static void setcontrast(struct gspca_dev *gspca_dev) 538
539static void setsensorgain(struct gspca_dev *gspca_dev)
540{
541 struct sd *sd = (struct sd *) gspca_dev;
542 unsigned short gain;
543
544 gain = (sd->gain + 1) >> 1;
545 if (gain > 255)
546 gain = 255;
547
548 switch (sd->sensor) {
549
550 case SENSOR_TAS5110: {
551 __u8 i2c[] =
552 {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10};
553
554 i2c[4] = 255 - gain;
555 if (i2c_w(gspca_dev->dev, i2c) < 0)
556 goto err;
557 break; }
558
559 case SENSOR_OV6650: {
560 __u8 i2c[] = {0xa0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
561 i2c[3] = gain;
562 if (i2c_w(gspca_dev->dev, i2c) < 0)
563 goto err;
564 break; }
565 }
566 return;
567err:
568 PDEBUG(D_ERR, "i2c error gain");
569}
570
571static void setgain(struct gspca_dev *gspca_dev)
490{ 572{
491 struct sd *sd = (struct sd *) gspca_dev; 573 struct sd *sd = (struct sd *) gspca_dev;
492 __u8 gain; 574 __u8 gain;
493 __u8 rgb_value; 575 __u8 rgb_value;
494 576
495 gain = sd->contrast >> 4; 577 gain = sd->gain >> 5;
578
496 /* red and blue gain */ 579 /* red and blue gain */
497 rgb_value = gain << 4 | gain; 580 rgb_value = gain << 4 | gain;
498 reg_w(gspca_dev->dev, 0x10, &rgb_value, 1); 581 reg_w(gspca_dev->dev, 0x10, &rgb_value, 1);
499 /* green gain */ 582 /* green gain */
500 rgb_value = gain; 583 rgb_value = gain;
501 reg_w(gspca_dev->dev, 0x11, &rgb_value, 1); 584 reg_w(gspca_dev->dev, 0x11, &rgb_value, 1);
585
586 if (sd->sensor_has_gain)
587 setsensorgain(gspca_dev);
588}
589
590static void setexposure(struct gspca_dev *gspca_dev)
591{
592 struct sd *sd = (struct sd *) gspca_dev;
593 /* translate 0 - 255 to a number of fps in a 30 - 1 scale */
594 int fps = 30 - sd->exposure * 29 / 511;
595
596 switch (sd->sensor) {
597 case SENSOR_TAS5110: {
598 __u8 reg;
599
600 /* register 19's high nibble contains the sn9c10x clock divider
601 The high nibble configures the no fps according to the
602 formula: 60 / high_nibble. With a maximum of 30 fps */
603 reg = 60 / fps;
604 if (reg > 15)
605 reg = 15;
606 reg = (reg << 4) | 0x0b;
607 reg_w(gspca_dev->dev, 0x19, &reg, 1);
608 break; }
609 case SENSOR_OV6650: {
610 __u8 i2c[] = {0xa0, 0x60, 0x11, 0xc0, 0x00, 0x00, 0x00, 0x10};
611 i2c[3] = 30 / fps - 1;
612 if (i2c[3] > 15)
613 i2c[3] = 15;
614 i2c[3] |= 0xc0;
615 if (i2c_w(gspca_dev->dev, i2c) < 0)
616 PDEBUG(D_ERR, "i2c error exposure");
617 break; }
618 }
619}
620
621
622static void do_autogain(struct gspca_dev *gspca_dev)
623{
624 struct sd *sd = (struct sd *) gspca_dev;
625 int avg_lum = atomic_read(&sd->avg_lum);
626
627 if (avg_lum == -1)
628 return;
629
630 if (sd->autogain_ignore_frames > 0)
631 sd->autogain_ignore_frames--;
632 else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
633 sd->brightness * DESIRED_AVG_LUM / 127,
634 AUTOGAIN_DEADZONE, GAIN_KNEE, EXPOSURE_KNEE))
635 sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
502} 636}
503 637
504/* this function is called at probe time */ 638/* this function is called at probe time */
@@ -511,7 +645,13 @@ static int sd_config(struct gspca_dev *gspca_dev,
511 __u16 product; 645 __u16 product;
512 int sif = 0; 646 int sif = 0;
513 647
648 /* nctrls depends upon the sensor, so we use a per cam copy */
649 memcpy(&sd->sd_desc, gspca_dev->sd_desc, sizeof(struct sd_desc));
650 gspca_dev->sd_desc = &sd->sd_desc;
651
514 sd->fr_h_sz = 12; /* default size of the frame header */ 652 sd->fr_h_sz = 12; /* default size of the frame header */
653 sd->sd_desc.nctrls = 2; /* default no ctrls */
654
515/* vendor = id->idVendor; */ 655/* vendor = id->idVendor; */
516 product = id->idProduct; 656 product = id->idProduct;
517/* switch (vendor) { */ 657/* switch (vendor) { */
@@ -521,6 +661,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
521 case 0x6005: /* SN9C101 */ 661 case 0x6005: /* SN9C101 */
522 case 0x6007: /* SN9C101 */ 662 case 0x6007: /* SN9C101 */
523 sd->sensor = SENSOR_TAS5110; 663 sd->sensor = SENSOR_TAS5110;
664 sd->sensor_has_gain = 1;
665 sd->sd_desc.nctrls = 4;
666 sd->sd_desc.dq_callback = do_autogain;
524 sif = 1; 667 sif = 1;
525 break; 668 break;
526 case 0x6009: /* SN9C101 */ 669 case 0x6009: /* SN9C101 */
@@ -531,6 +674,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
531 break; 674 break;
532 case 0x6011: /* SN9C101 - SN9C101G */ 675 case 0x6011: /* SN9C101 - SN9C101G */
533 sd->sensor = SENSOR_OV6650; 676 sd->sensor = SENSOR_OV6650;
677 sd->sensor_has_gain = 1;
678 sd->sd_desc.nctrls = 4;
679 sd->sd_desc.dq_callback = do_autogain;
534 sif = 1; 680 sif = 1;
535 break; 681 break;
536 case 0x6019: /* SN9C101 */ 682 case 0x6019: /* SN9C101 */
@@ -570,8 +716,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
570 cam->cam_mode = sif_mode; 716 cam->cam_mode = sif_mode;
571 cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; 717 cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
572 } 718 }
573 sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; 719 sd->brightness = BRIGHTNESS_DEF;
574 sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; 720 sd->gain = GAIN_DEF;
721 sd->exposure = EXPOSURE_DEF;
722 sd->autogain = 1;
575 if (sd->sensor == SENSOR_OV7630_3) /* jfm: from win trace */ 723 if (sd->sensor == SENSOR_OV7630_3) /* jfm: from win trace */
576 reg_w(gspca_dev->dev, 0x01, probe_ov7630, sizeof probe_ov7630); 724 reg_w(gspca_dev->dev, 0x01, probe_ov7630, sizeof probe_ov7630);
577 return 0; 725 return 0;
@@ -754,8 +902,12 @@ static void sd_start(struct gspca_dev *gspca_dev)
754 reg_w(dev, 0x18, &reg17_19[1], 2); 902 reg_w(dev, 0x18, &reg17_19[1], 2);
755 msleep(20); 903 msleep(20);
756 904
757 setcontrast(gspca_dev); 905 setgain(gspca_dev);
758 setbrightness(gspca_dev); 906 setbrightness(gspca_dev);
907 setexposure(gspca_dev);
908
909 sd->autogain_ignore_frames = 0;
910 atomic_set(&sd->avg_lum, -1);
759} 911}
760 912
761static void sd_stopN(struct gspca_dev *gspca_dev) 913static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -779,8 +931,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
779 unsigned char *data, /* isoc packet */ 931 unsigned char *data, /* isoc packet */
780 int len) /* iso packet length */ 932 int len) /* iso packet length */
781{ 933{
782 struct sd *sd;
783 int i; 934 int i;
935 struct sd *sd = (struct sd *) gspca_dev;
784 936
785 if (len > 6 && len < 24) { 937 if (len > 6 && len < 24) {
786 for (i = 0; i < len - 6; i++) { 938 for (i = 0; i < len - 6; i++) {
@@ -792,7 +944,16 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
792 && data[5 + i] == 0x96) { /* start of frame */ 944 && data[5 + i] == 0x96) { /* start of frame */
793 frame = gspca_frame_add(gspca_dev, LAST_PACKET, 945 frame = gspca_frame_add(gspca_dev, LAST_PACKET,
794 frame, data, 0); 946 frame, data, 0);
795 sd = (struct sd *) gspca_dev; 947 if (i < (len - 10)) {
948 atomic_set(&sd->avg_lum, data[i + 8] +
949 (data[i + 9] << 8));
950 } else {
951 atomic_set(&sd->avg_lum, -1);
952#ifdef CONFIG_VIDEO_ADV_DEBUG
953 PDEBUG(D_STREAM, "packet too short to "
954 "get avg brightness");
955#endif
956 }
796 data += i + sd->fr_h_sz; 957 data += i + sd->fr_h_sz;
797 len -= i + sd->fr_h_sz; 958 len -= i + sd->fr_h_sz;
798 gspca_frame_add(gspca_dev, FIRST_PACKET, 959 gspca_frame_add(gspca_dev, FIRST_PACKET,
@@ -823,26 +984,74 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
823 return 0; 984 return 0;
824} 985}
825 986
826static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) 987static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
988{
989 struct sd *sd = (struct sd *) gspca_dev;
990
991 sd->gain = val;
992 if (gspca_dev->streaming)
993 setgain(gspca_dev);
994 return 0;
995}
996
997static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
827{ 998{
828 struct sd *sd = (struct sd *) gspca_dev; 999 struct sd *sd = (struct sd *) gspca_dev;
829 1000
830 sd->contrast = val; 1001 *val = sd->gain;
1002 return 0;
1003}
1004
1005static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
1006{
1007 struct sd *sd = (struct sd *) gspca_dev;
1008
1009 sd->exposure = val;
831 if (gspca_dev->streaming) 1010 if (gspca_dev->streaming)
832 setcontrast(gspca_dev); 1011 setexposure(gspca_dev);
1012 return 0;
1013}
1014
1015static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
1016{
1017 struct sd *sd = (struct sd *) gspca_dev;
1018
1019 *val = sd->exposure;
1020 return 0;
1021}
1022
1023static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
1024{
1025 struct sd *sd = (struct sd *) gspca_dev;
1026
1027 sd->autogain = val;
1028 /* when switching to autogain set defaults to make sure
1029 we are on a valid point of the autogain gain /
1030 exposure knee graph, and give this change time to
1031 take effect before doing autogain. */
1032 if (sd->autogain) {
1033 sd->exposure = EXPOSURE_DEF;
1034 sd->gain = GAIN_DEF;
1035 if (gspca_dev->streaming) {
1036 sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
1037 setexposure(gspca_dev);
1038 setgain(gspca_dev);
1039 }
1040 }
1041
833 return 0; 1042 return 0;
834} 1043}
835 1044
836static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) 1045static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
837{ 1046{
838 struct sd *sd = (struct sd *) gspca_dev; 1047 struct sd *sd = (struct sd *) gspca_dev;
839 1048
840 *val = sd->contrast; 1049 *val = sd->autogain;
841 return 0; 1050 return 0;
842} 1051}
843 1052
844/* sub-driver description */ 1053/* sub-driver description */
845static struct sd_desc sd_desc = { 1054static const struct sd_desc sd_desc = {
846 .name = MODULE_NAME, 1055 .name = MODULE_NAME,
847 .ctrls = sd_ctrls, 1056 .ctrls = sd_ctrls,
848 .nctrls = ARRAY_SIZE(sd_ctrls), 1057 .nctrls = ARRAY_SIZE(sd_ctrls),