diff options
author | Hans de Goede <hdegoede@redhat.com> | 2010-02-17 09:59:19 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-02-26 13:11:09 -0500 |
commit | 5fb2dde28122e74bbab9dae3cc04bcbc8c08b332 (patch) | |
tree | aaea47e29bf8a3783d5112c5000b3d4974f27e67 /drivers/media | |
parent | 0e4b91c30605ae030d4ff63c5160e54c1bc2682c (diff) |
V4L/DVB: gspca_pac7302: much improved exposure control
My experience with fixing up the controls for the PAS sensors in
sonixb, has lead me to re-investigate the exposure control for the
pac7302. I've now found a regular exposure register in register bank 3
(which seems to be the sensor registers bank), and with this added a proper
fine grained exposure control. This patch also updates the do_autogain
function to work properly with this new finer grained control.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/gspca/pac7302.c | 105 | ||||
-rw-r--r-- | drivers/media/video/gspca/pac_common.h | 9 |
2 files changed, 54 insertions, 60 deletions
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 19f0cd892dba..2a68220d1ada 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c | |||
@@ -24,33 +24,26 @@ | |||
24 | */ | 24 | */ |
25 | 25 | ||
26 | /* Some documentation about various registers as determined by trial and error. | 26 | /* Some documentation about various registers as determined by trial and error. |
27 | When the register addresses differ between the 7202 and the 7311 the 2 | ||
28 | different addresses are written as 7302addr/7311addr, when one of the 2 | ||
29 | addresses is a - sign that register description is not valid for the | ||
30 | matching IC. | ||
31 | 27 | ||
32 | Register page 1: | 28 | Register page 1: |
33 | 29 | ||
34 | Address Description | 30 | Address Description |
35 | -/0x08 Unknown compressor related, must always be 8 except when not | ||
36 | in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! | ||
37 | -/0x1b Auto white balance related, bit 0 is AWB enable (inverted) | ||
38 | bits 345 seem to toggle per color gains on/off (inverted) | ||
39 | 0x78 Global control, bit 6 controls the LED (inverted) | 31 | 0x78 Global control, bit 6 controls the LED (inverted) |
40 | -/0x80 JPEG compression ratio ? Best not touched | ||
41 | 32 | ||
42 | Register page 3/4: | 33 | Register page 3: |
43 | 34 | ||
44 | Address Description | 35 | Address Description |
45 | 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on | 36 | 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on |
46 | the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? | 37 | the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? |
47 | -/0x0f Master gain 1-245, low value = high gain | 38 | 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps |
48 | 0x10/- Master gain 0-31 | 39 | 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps, |
49 | -/0x10 Another gain 0-15, limited influence (1-2x gain I guess) | 40 | 63 -> ~27 fps, the 2 msb's must always be 1 !! |
41 | 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0: | ||
42 | 1 -> ~30 fps, 2 -> ~20 fps | ||
43 | 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time | ||
44 | 0x0f Exposure bit 8, 0-448, 448 = no exposure at all | ||
45 | 0x10 Master gain 0-31 | ||
50 | 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused | 46 | 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused |
51 | -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to | ||
52 | completely disable the analog amplification block. Set to 0x68 | ||
53 | for max gain, 0x14 for minimal gain. | ||
54 | 47 | ||
55 | The registers are accessed in the following functions: | 48 | The registers are accessed in the following functions: |
56 | 49 | ||
@@ -89,8 +82,8 @@ struct sd { | |||
89 | unsigned char red_balance; | 82 | unsigned char red_balance; |
90 | unsigned char blue_balance; | 83 | unsigned char blue_balance; |
91 | unsigned char gain; | 84 | unsigned char gain; |
92 | unsigned char exposure; | ||
93 | unsigned char autogain; | 85 | unsigned char autogain; |
86 | unsigned short exposure; | ||
94 | __u8 hflip; | 87 | __u8 hflip; |
95 | __u8 vflip; | 88 | __u8 vflip; |
96 | u8 flags; | 89 | u8 flags; |
@@ -128,7 +121,6 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); | |||
128 | static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); | 121 | static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); |
129 | 122 | ||
130 | static const struct ctrl sd_ctrls[] = { | 123 | static const struct ctrl sd_ctrls[] = { |
131 | /* This control is pac7302 only */ | ||
132 | { | 124 | { |
133 | { | 125 | { |
134 | .id = V4L2_CID_BRIGHTNESS, | 126 | .id = V4L2_CID_BRIGHTNESS, |
@@ -144,7 +136,6 @@ static const struct ctrl sd_ctrls[] = { | |||
144 | .set = sd_setbrightness, | 136 | .set = sd_setbrightness, |
145 | .get = sd_getbrightness, | 137 | .get = sd_getbrightness, |
146 | }, | 138 | }, |
147 | /* This control is for both the 7302 and the 7311 */ | ||
148 | { | 139 | { |
149 | { | 140 | { |
150 | .id = V4L2_CID_CONTRAST, | 141 | .id = V4L2_CID_CONTRAST, |
@@ -160,7 +151,6 @@ static const struct ctrl sd_ctrls[] = { | |||
160 | .set = sd_setcontrast, | 151 | .set = sd_setcontrast, |
161 | .get = sd_getcontrast, | 152 | .get = sd_getcontrast, |
162 | }, | 153 | }, |
163 | /* This control is pac7302 only */ | ||
164 | { | 154 | { |
165 | { | 155 | { |
166 | .id = V4L2_CID_SATURATION, | 156 | .id = V4L2_CID_SATURATION, |
@@ -218,7 +208,6 @@ static const struct ctrl sd_ctrls[] = { | |||
218 | .set = sd_setbluebalance, | 208 | .set = sd_setbluebalance, |
219 | .get = sd_getbluebalance, | 209 | .get = sd_getbluebalance, |
220 | }, | 210 | }, |
221 | /* All controls below are for both the 7302 and the 7311 */ | ||
222 | { | 211 | { |
223 | { | 212 | { |
224 | .id = V4L2_CID_GAIN, | 213 | .id = V4L2_CID_GAIN, |
@@ -241,11 +230,10 @@ static const struct ctrl sd_ctrls[] = { | |||
241 | .type = V4L2_CTRL_TYPE_INTEGER, | 230 | .type = V4L2_CTRL_TYPE_INTEGER, |
242 | .name = "Exposure", | 231 | .name = "Exposure", |
243 | .minimum = 0, | 232 | .minimum = 0, |
244 | #define EXPOSURE_MAX 255 | 233 | .maximum = 1023, |
245 | .maximum = EXPOSURE_MAX, | ||
246 | .step = 1, | 234 | .step = 1, |
247 | #define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ | 235 | #define EXPOSURE_DEF 66 /* 33 ms / 30 fps */ |
248 | #define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ | 236 | #define EXPOSURE_KNEE 133 /* 66 ms / 15 fps */ |
249 | .default_value = EXPOSURE_DEF, | 237 | .default_value = EXPOSURE_DEF, |
250 | }, | 238 | }, |
251 | .set = sd_setexposure, | 239 | .set = sd_setexposure, |
@@ -381,7 +369,7 @@ static const __u8 start_7302[] = { | |||
381 | #define SKIP 0xaa | 369 | #define SKIP 0xaa |
382 | /* page 3 - the value SKIP says skip the index - see reg_w_page() */ | 370 | /* page 3 - the value SKIP says skip the index - see reg_w_page() */ |
383 | static const __u8 page3_7302[] = { | 371 | static const __u8 page3_7302[] = { |
384 | 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16, | 372 | 0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16, |
385 | 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, | 373 | 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, |
386 | 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 374 | 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
387 | 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, | 375 | 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, |
@@ -653,23 +641,39 @@ static void setgain(struct gspca_dev *gspca_dev) | |||
653 | static void setexposure(struct gspca_dev *gspca_dev) | 641 | static void setexposure(struct gspca_dev *gspca_dev) |
654 | { | 642 | { |
655 | struct sd *sd = (struct sd *) gspca_dev; | 643 | struct sd *sd = (struct sd *) gspca_dev; |
656 | __u8 reg; | 644 | __u8 clockdiv; |
657 | 645 | __u16 exposure; | |
658 | /* register 2 of frame 3/4 contains the clock divider configuring the | 646 | |
659 | no fps according to the formula: 60 / reg. sd->exposure is the | 647 | /* register 2 of frame 3 contains the clock divider configuring the |
660 | desired exposure time in ms. */ | 648 | no fps according to the formula: 90 / reg. sd->exposure is the |
661 | reg = 120 * sd->exposure / 1000; | 649 | desired exposure time in 0.5 ms. */ |
662 | if (reg < 2) | 650 | clockdiv = (90 * sd->exposure + 1999) / 2000; |
663 | reg = 2; | 651 | |
664 | else if (reg > 63) | 652 | /* Note clockdiv = 3 also works, but when running at 30 fps, depending |
665 | reg = 63; | 653 | on the scene being recorded, the camera switches to another |
666 | 654 | quantization table for certain JPEG blocks, and we don't know how | |
667 | /* On the pac7302 reg2 MUST be a multiple of 3, so round it to | 655 | to decompress these blocks. So we cap the framerate at 15 fps */ |
668 | the nearest multiple of 3, except when between 6 and 12? */ | 656 | if (clockdiv < 6) |
669 | if (reg < 6 || reg > 12) | 657 | clockdiv = 6; |
670 | reg = ((reg + 1) / 3) * 3; | 658 | else if (clockdiv > 63) |
659 | clockdiv = 63; | ||
660 | |||
661 | /* reg2 MUST be a multiple of 3, except when between 6 and 12? | ||
662 | Always round up, otherwise we cannot get the desired frametime | ||
663 | using the partial frame time exposure control */ | ||
664 | if (clockdiv < 6 || clockdiv > 12) | ||
665 | clockdiv = ((clockdiv + 2) / 3) * 3; | ||
666 | |||
667 | /* frame exposure time in ms = 1000 * clockdiv / 90 -> | ||
668 | exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */ | ||
669 | exposure = (sd->exposure * 45 * 448) / (1000 * clockdiv); | ||
670 | /* 0 = use full frametime, 448 = no exposure, reverse it */ | ||
671 | exposure = 448 - exposure; | ||
672 | |||
671 | reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ | 673 | reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ |
672 | reg_w(gspca_dev, 0x02, reg); | 674 | reg_w(gspca_dev, 0x02, clockdiv); |
675 | reg_w(gspca_dev, 0x0e, exposure & 0xff); | ||
676 | reg_w(gspca_dev, 0x0f, exposure >> 8); | ||
673 | 677 | ||
674 | /* load registers to sensor (Bit 0, auto clear) */ | 678 | /* load registers to sensor (Bit 0, auto clear) */ |
675 | reg_w(gspca_dev, 0x11, 0x01); | 679 | reg_w(gspca_dev, 0x11, 0x01); |
@@ -756,22 +760,13 @@ static void do_autogain(struct gspca_dev *gspca_dev) | |||
756 | { | 760 | { |
757 | struct sd *sd = (struct sd *) gspca_dev; | 761 | struct sd *sd = (struct sd *) gspca_dev; |
758 | int avg_lum = atomic_read(&sd->avg_lum); | 762 | int avg_lum = atomic_read(&sd->avg_lum); |
759 | int desired_lum, deadzone; | 763 | int desired_lum; |
764 | const int deadzone = 30; | ||
760 | 765 | ||
761 | if (avg_lum == -1) | 766 | if (avg_lum == -1) |
762 | return; | 767 | return; |
763 | 768 | ||
764 | desired_lum = 270 + sd->brightness * 4; | 769 | desired_lum = 270 + sd->brightness; |
765 | /* Hack hack, with the 7202 the first exposure step is | ||
766 | pretty large, so if we're about to make the first | ||
767 | exposure increase make the deadzone large to avoid | ||
768 | oscilating */ | ||
769 | if (desired_lum > avg_lum && sd->gain == GAIN_DEF && | ||
770 | sd->exposure > EXPOSURE_DEF && | ||
771 | sd->exposure < 42) | ||
772 | deadzone = 90; | ||
773 | else | ||
774 | deadzone = 30; | ||
775 | 770 | ||
776 | if (sd->autogain_ignore_frames > 0) | 771 | if (sd->autogain_ignore_frames > 0) |
777 | sd->autogain_ignore_frames--; | 772 | sd->autogain_ignore_frames--; |
diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h index 20f67d9b8c06..8462a7c1a338 100644 --- a/drivers/media/video/gspca/pac_common.h +++ b/drivers/media/video/gspca/pac_common.h | |||
@@ -24,11 +24,10 @@ | |||
24 | */ | 24 | */ |
25 | 25 | ||
26 | /* We calculate the autogain at the end of the transfer of a frame, at this | 26 | /* We calculate the autogain at the end of the transfer of a frame, at this |
27 | moment a frame with the old settings is being transmitted, and a frame is | 27 | moment a frame with the old settings is being captured and transmitted. So |
28 | being captured with the old settings. So if we adjust the autogain we must | 28 | if we adjust the gain or exposure we must ignore atleast the next frame for |
29 | ignore atleast the 2 next frames for the new settings to come into effect | 29 | the new settings to come into effect before doing any other adjustments. */ |
30 | before doing any other adjustments */ | 30 | #define PAC_AUTOGAIN_IGNORE_FRAMES 2 |
31 | #define PAC_AUTOGAIN_IGNORE_FRAMES 3 | ||
32 | 31 | ||
33 | static const unsigned char pac_sof_marker[5] = | 32 | static const unsigned char pac_sof_marker[5] = |
34 | { 0xff, 0xff, 0x00, 0xff, 0x96 }; | 33 | { 0xff, 0xff, 0x00, 0xff, 0x96 }; |