diff options
author | Hans de Goede <j.w.r.degoede@hhs.nl> | 2008-07-10 09:40:53 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2008-07-20 06:25:59 -0400 |
commit | dcef3237b652e1c02093feac0f443485a144f035 (patch) | |
tree | 81df2d056ef1c4179868512076d9339dbd98680a /drivers/media/video | |
parent | d0d0e39bc5912793405d3f84ffc982fa400e6cc0 (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/media/video')
-rw-r--r-- | drivers/media/video/gspca/gspca.c | 88 | ||||
-rw-r--r-- | drivers/media/video/gspca/gspca.h | 2 | ||||
-rw-r--r-- | drivers/media/video/gspca/pac207.c | 68 | ||||
-rw-r--r-- | drivers/media/video/gspca/sonixb.c | 301 |
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 | } |
1791 | EXPORT_SYMBOL(gspca_disconnect); | 1791 | EXPORT_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. */ | ||
1800 | int 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 | } | ||
1879 | EXPORT_SYMBOL(gspca_auto_gain_n_exposure); | ||
1880 | |||
1793 | /* -- module insert / remove -- */ | 1881 | /* -- module insert / remove -- */ |
1794 | static int __init gspca_init(void) | 1882 | static 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); |
173 | int 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> */ | ||
362 | static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) | 360 | static 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 | ||
426 | static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev, | 376 | static 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"); | |||
35 | struct sd { | 35 | struct 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 */ |
63 | static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); | 80 | static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); |
64 | static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); | 81 | static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); |
65 | static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); | 82 | static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); |
66 | static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); | 83 | static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); |
84 | static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); | ||
85 | static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); | ||
86 | static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); | ||
87 | static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); | ||
67 | 88 | ||
68 | static struct ctrl sd_ctrls[] = { | 89 | static 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 | |||
191 | static const __u8 initOv7630[] = { | 238 | static 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; |
486 | err: | 535 | err: |
487 | PDEBUG(D_ERR, "i2c error brightness"); | 536 | PDEBUG(D_ERR, "i2c error brightness"); |
488 | } | 537 | } |
489 | static void setcontrast(struct gspca_dev *gspca_dev) | 538 | |
539 | static 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; | ||
567 | err: | ||
568 | PDEBUG(D_ERR, "i2c error gain"); | ||
569 | } | ||
570 | |||
571 | static 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 | |||
590 | static 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, ®, 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 | |||
622 | static 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, ®17_19[1], 2); | 902 | reg_w(dev, 0x18, ®17_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 | ||
761 | static void sd_stopN(struct gspca_dev *gspca_dev) | 913 | static 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 | ||
826 | static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) | 987 | static 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 | |||
997 | static 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 | |||
1005 | static 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 | |||
1015 | static 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 | |||
1023 | static 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 | ||
836 | static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) | 1045 | static 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 */ |
845 | static struct sd_desc sd_desc = { | 1054 | static 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), |