aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2010-02-01 11:18:37 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-02-26 13:11:08 -0500
commit26984b09952d01afabaa38e747fbee083dde0374 (patch)
treeffcb10b2cac77eb4951ad1c3d1f7ddf3b6ed74bb /drivers/media
parent4efcfa0af11d75fa6311ef62180eefcd654be3c1 (diff)
V4L/DVB: gscpa_sonixb: Differentiate between sensors with a coarse and fine expo ctrl
Some sensors only allow controlling the exposure by changing the clockdiv, which means that exposure takes pretty large jumps (for example when going from a div of 1 to 2, the exposure doubles). Use 2 different entries in the controls array for these 2 different types of exposure (always disabling one), and; Add a new autogain algorithm for the coarse exposure case, which normally changes the gain setting, only touching the exposure setting if the desired luminance cannot be reached with gain, and after an exposure change once more first tries gain, etc. Thus avoiding exposure changes, and the resulting oscilation because of the huge steps these exposure controls take. 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/coarse_expo_autogain.h116
-rw-r--r--drivers/media/video/gspca/gspca.c2
-rw-r--r--drivers/media/video/gspca/sonixb.c113
3 files changed, 193 insertions, 38 deletions
diff --git a/drivers/media/video/gspca/coarse_expo_autogain.h b/drivers/media/video/gspca/coarse_expo_autogain.h
new file mode 100644
index 000000000000..1cb9d941eaf6
--- /dev/null
+++ b/drivers/media/video/gspca/coarse_expo_autogain.h
@@ -0,0 +1,116 @@
1/*
2 * Auto gain algorithm for camera's with a coarse exposure control
3 *
4 * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21/* Autogain + exposure algorithm for cameras with a coarse exposure control
22 (usually this means we can only control the clockdiv to change exposure)
23 As changing the clockdiv so that the fps drops from 30 to 15 fps for
24 example, will lead to a huge exposure change (it effectively doubles),
25 this algorithm normally tries to only adjust the gain (between 40 and
26 80 %) and if that does not help, only then changes exposure. This leads
27 to a much more stable image then using the knee algorithm which at
28 certain points of the knee graph will only try to adjust exposure,
29 which leads to oscilating as one exposure step is huge.
30
31 Note this assumes that the sd struct for the cam in question has
32 exp_too_high_cnt and exp_too_high_cnt int members for use by this function.
33
34 Returns 0 if no changes were made, 1 if the gain and or exposure settings
35 where changed. */
36static int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev,
37 int avg_lum, int desired_avg_lum, int deadzone)
38{
39 int i, steps, gain, orig_gain, exposure, orig_exposure;
40 int gain_low, gain_high;
41 const struct ctrl *gain_ctrl = NULL;
42 const struct ctrl *exposure_ctrl = NULL;
43 struct sd *sd = (struct sd *) gspca_dev;
44 int retval = 0;
45
46 for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
47 if (gspca_dev->ctrl_dis & (1 << i))
48 continue;
49 if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN)
50 gain_ctrl = &gspca_dev->sd_desc->ctrls[i];
51 if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE)
52 exposure_ctrl = &gspca_dev->sd_desc->ctrls[i];
53 }
54 if (!gain_ctrl || !exposure_ctrl) {
55 PDEBUG(D_ERR, "Error: gspca_coarse_grained_expo_autogain "
56 "called on cam without gain or exposure");
57 return 0;
58 }
59
60 if (gain_ctrl->get(gspca_dev, &gain) ||
61 exposure_ctrl->get(gspca_dev, &exposure))
62 return 0;
63
64 orig_gain = gain;
65 orig_exposure = exposure;
66 gain_low =
67 (gain_ctrl->qctrl.maximum - gain_ctrl->qctrl.minimum) / 5 * 2;
68 gain_low += gain_ctrl->qctrl.minimum;
69 gain_high =
70 (gain_ctrl->qctrl.maximum - gain_ctrl->qctrl.minimum) / 5 * 4;
71 gain_high += gain_ctrl->qctrl.minimum;
72
73 /* If we are of a multiple of deadzone, do multiple steps to reach the
74 desired lumination fast (with the risc of a slight overshoot) */
75 steps = (desired_avg_lum - avg_lum) / deadzone;
76
77 PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
78 avg_lum, desired_avg_lum, steps);
79
80 if ((gain + steps) > gain_high &&
81 sd->exposure < exposure_ctrl->qctrl.maximum) {
82 gain = gain_high;
83 sd->exp_too_low_cnt++;
84 } else if ((gain + steps) < gain_low &&
85 sd->exposure > exposure_ctrl->qctrl.minimum) {
86 gain = gain_low;
87 sd->exp_too_high_cnt++;
88 } else {
89 gain += steps;
90 if (gain > gain_ctrl->qctrl.maximum)
91 gain = gain_ctrl->qctrl.maximum;
92 else if (gain < gain_ctrl->qctrl.minimum)
93 gain = gain_ctrl->qctrl.minimum;
94 sd->exp_too_high_cnt = 0;
95 sd->exp_too_low_cnt = 0;
96 }
97
98 if (sd->exp_too_high_cnt > 3) {
99 exposure--;
100 sd->exp_too_high_cnt = 0;
101 } else if (sd->exp_too_low_cnt > 3) {
102 exposure++;
103 sd->exp_too_low_cnt = 0;
104 }
105
106 if (gain != orig_gain) {
107 gain_ctrl->set(gspca_dev, gain);
108 retval = 1;
109 }
110 if (exposure != orig_exposure) {
111 exposure_ctrl->set(gspca_dev, exposure);
112 retval = 1;
113 }
114
115 return retval;
116}
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index 96a77697eb1e..2c662ed91b5b 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -2417,6 +2417,8 @@ int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
2417 int retval = 0; 2417 int retval = 0;
2418 2418
2419 for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { 2419 for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
2420 if (gspca_dev->ctrl_dis & (1 << i))
2421 continue;
2420 if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN) 2422 if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN)
2421 gain_ctrl = &gspca_dev->sd_desc->ctrls[i]; 2423 gain_ctrl = &gspca_dev->sd_desc->ctrls[i];
2422 if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE) 2424 if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE)
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index 4356423c6f99..dc2d88cdd4a2 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -54,9 +54,11 @@ struct sd {
54 struct gspca_dev gspca_dev; /* !! must be the first item */ 54 struct gspca_dev gspca_dev; /* !! must be the first item */
55 atomic_t avg_lum; 55 atomic_t avg_lum;
56 int prev_avg_lum; 56 int prev_avg_lum;
57 int exp_too_low_cnt;
58 int exp_too_high_cnt;
57 59
60 unsigned short exposure;
58 unsigned char gain; 61 unsigned char gain;
59 unsigned char exposure;
60 unsigned char brightness; 62 unsigned char brightness;
61 unsigned char autogain; 63 unsigned char autogain;
62 unsigned char autogain_ignore_frames; 64 unsigned char autogain_ignore_frames;
@@ -97,13 +99,15 @@ struct sensor_data {
97/* sensor_data flags */ 99/* sensor_data flags */
98#define F_GAIN 0x01 /* has gain */ 100#define F_GAIN 0x01 /* has gain */
99#define F_SIF 0x02 /* sif or vga */ 101#define F_SIF 0x02 /* sif or vga */
102#define F_COARSE_EXPO 0x04 /* exposure control is coarse */
100 103
101/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */ 104/* priv field of struct v4l2_pix_format flags (do not use low nibble!) */
102#define MODE_RAW 0x10 /* raw bayer mode */ 105#define MODE_RAW 0x10 /* raw bayer mode */
103#define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */ 106#define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */
104 107
105/* ctrl_dis helper macros */ 108/* ctrl_dis helper macros */
106#define NO_EXPO ((1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX)) 109#define NO_EXPO ((1 << EXPOSURE_IDX) | (1 << COARSE_EXPOSURE_IDX) | \
110 (1 << AUTOGAIN_IDX))
107#define NO_FREQ (1 << FREQ_IDX) 111#define NO_FREQ (1 << FREQ_IDX)
108#define NO_BRIGHTNESS (1 << BRIGHTNESS_IDX) 112#define NO_BRIGHTNESS (1 << BRIGHTNESS_IDX)
109 113
@@ -129,11 +133,10 @@ struct sensor_data {
129} 133}
130 134
131/* We calculate the autogain at the end of the transfer of a frame, at this 135/* We calculate the autogain at the end of the transfer of a frame, at this
132 moment a frame with the old settings is being transmitted, and a frame is 136 moment a frame with the old settings is being captured and transmitted. So
133 being captured with the old settings. So if we adjust the autogain we must 137 if we adjust the gain or exposure we must ignore atleast the next frame for
134 ignore atleast the 2 next frames for the new settings to come into effect 138 the new settings to come into effect before doing any other adjustments. */
135 before doing any other adjustments */ 139#define AUTOGAIN_IGNORE_FRAMES 1
136#define AUTOGAIN_IGNORE_FRAMES 3
137 140
138/* V4L2 controls supported by the driver */ 141/* V4L2 controls supported by the driver */
139static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); 142static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
@@ -185,10 +188,10 @@ static const struct ctrl sd_ctrls[] = {
185 .id = V4L2_CID_EXPOSURE, 188 .id = V4L2_CID_EXPOSURE,
186 .type = V4L2_CTRL_TYPE_INTEGER, 189 .type = V4L2_CTRL_TYPE_INTEGER,
187 .name = "Exposure", 190 .name = "Exposure",
188#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ 191#define EXPOSURE_DEF 33 /* 33 ms / 30 fps */
189#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ 192#define EXPOSURE_KNEE 100 /* 100 ms / 10 fps */
190 .minimum = 0, 193 .minimum = 0,
191 .maximum = 255, 194 .maximum = 511,
192 .step = 1, 195 .step = 1,
193 .default_value = EXPOSURE_DEF, 196 .default_value = EXPOSURE_DEF,
194 .flags = 0, 197 .flags = 0,
@@ -196,7 +199,23 @@ static const struct ctrl sd_ctrls[] = {
196 .set = sd_setexposure, 199 .set = sd_setexposure,
197 .get = sd_getexposure, 200 .get = sd_getexposure,
198 }, 201 },
199#define AUTOGAIN_IDX 3 202#define COARSE_EXPOSURE_IDX 3
203 {
204 {
205 .id = V4L2_CID_EXPOSURE,
206 .type = V4L2_CTRL_TYPE_INTEGER,
207 .name = "Exposure",
208#define COARSE_EXPOSURE_DEF 2 /* 30 fps */
209 .minimum = 2,
210 .maximum = 15,
211 .step = 1,
212 .default_value = COARSE_EXPOSURE_DEF,
213 .flags = 0,
214 },
215 .set = sd_setexposure,
216 .get = sd_getexposure,
217 },
218#define AUTOGAIN_IDX 4
200 { 219 {
201 { 220 {
202 .id = V4L2_CID_AUTOGAIN, 221 .id = V4L2_CID_AUTOGAIN,
@@ -212,7 +231,7 @@ static const struct ctrl sd_ctrls[] = {
212 .set = sd_setautogain, 231 .set = sd_setautogain,
213 .get = sd_getautogain, 232 .get = sd_getautogain,
214 }, 233 },
215#define FREQ_IDX 4 234#define FREQ_IDX 5
216 { 235 {
217 { 236 {
218 .id = V4L2_CID_POWER_LINE_FREQUENCY, 237 .id = V4L2_CID_POWER_LINE_FREQUENCY,
@@ -507,10 +526,10 @@ SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ,
507 0), 526 0),
508SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0, 527SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0,
509 NO_EXPO|NO_FREQ, 0), 528 NO_EXPO|NO_FREQ, 0),
510SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF, 529SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL,
511 NO_BRIGHTNESS|NO_FREQ, 0), 530 F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0),
512SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF, 531SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL,
513 NO_BRIGHTNESS|NO_FREQ, 0), 532 F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0),
514SENS(initTas5130, NULL, tas5130_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, 533SENS(initTas5130, NULL, tas5130_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ,
515 0), 534 0),
516}; 535};
@@ -721,16 +740,10 @@ static void setexposure(struct gspca_dev *gspca_dev)
721 switch (sd->sensor) { 740 switch (sd->sensor) {
722 case SENSOR_TAS5110C: 741 case SENSOR_TAS5110C:
723 case SENSOR_TAS5110D: { 742 case SENSOR_TAS5110D: {
724 __u8 reg;
725
726 /* register 19's high nibble contains the sn9c10x clock divider 743 /* register 19's high nibble contains the sn9c10x clock divider
727 The high nibble configures the no fps according to the 744 The high nibble configures the no fps according to the
728 formula: 60 / high_nibble. With a maximum of 30 fps */ 745 formula: 60 / high_nibble. With a maximum of 30 fps */
729 reg = 120 * sd->exposure / 1000; 746 __u8 reg = sd->exposure;
730 if (reg < 2)
731 reg = 2;
732 else if (reg > 15)
733 reg = 15;
734 reg = (reg << 4) | 0x0b; 747 reg = (reg << 4) | 0x0b;
735 reg_w(gspca_dev, 0x19, &reg, 1); 748 reg_w(gspca_dev, 0x19, &reg, 1);
736 break; 749 break;
@@ -766,7 +779,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
766 } else 779 } else
767 reg10_max = 0x41; 780 reg10_max = 0x41;
768 781
769 reg11 = (60 * sd->exposure + 999) / 1000; 782 reg11 = (30 * sd->exposure + 999) / 1000;
770 if (reg11 < 1) 783 if (reg11 < 1)
771 reg11 = 1; 784 reg11 = 1;
772 else if (reg11 > 16) 785 else if (reg11 > 16)
@@ -778,8 +791,8 @@ static void setexposure(struct gspca_dev *gspca_dev)
778 reg11 = 3; 791 reg11 = 3;
779 792
780 /* frame exposure time in ms = 1000 * reg11 / 30 -> 793 /* frame exposure time in ms = 1000 * reg11 / 30 ->
781 reg10 = sd->exposure * 2 * reg10_max / (1000 * reg11 / 30) */ 794 reg10 = sd->exposure * reg10_max / (1000 * reg11 / 30) */
782 reg10 = (sd->exposure * 60 * reg10_max) / (1000 * reg11); 795 reg10 = (sd->exposure * 30 * reg10_max) / (1000 * reg11);
783 796
784 /* Don't allow this to get below 10 when using autogain, the 797 /* Don't allow this to get below 10 when using autogain, the
785 steps become very large (relatively) when below 10 causing 798 steps become very large (relatively) when below 10 causing
@@ -839,30 +852,43 @@ static void setfreq(struct gspca_dev *gspca_dev)
839 } 852 }
840} 853}
841 854
855#include "coarse_expo_autogain.h"
856
842static void do_autogain(struct gspca_dev *gspca_dev) 857static void do_autogain(struct gspca_dev *gspca_dev)
843{ 858{
844 int deadzone, desired_avg_lum; 859 int deadzone, desired_avg_lum, result;
845 struct sd *sd = (struct sd *) gspca_dev; 860 struct sd *sd = (struct sd *) gspca_dev;
846 int avg_lum = atomic_read(&sd->avg_lum); 861 int avg_lum = atomic_read(&sd->avg_lum);
847 862
848 if (avg_lum == -1) 863 if (avg_lum == -1 || !sd->autogain)
864 return;
865
866 if (sd->autogain_ignore_frames > 0) {
867 sd->autogain_ignore_frames--;
849 return; 868 return;
869 }
850 870
851 /* SIF / VGA sensors have a different autoexposure area and thus 871 /* SIF / VGA sensors have a different autoexposure area and thus
852 different avg_lum values for the same picture brightness */ 872 different avg_lum values for the same picture brightness */
853 if (sensor_data[sd->sensor].flags & F_SIF) { 873 if (sensor_data[sd->sensor].flags & F_SIF) {
854 deadzone = 1000; 874 deadzone = 500;
855 desired_avg_lum = 7000; 875 /* SIF sensors tend to overexpose, so keep this small */
876 desired_avg_lum = 5000;
856 } else { 877 } else {
857 deadzone = 3000; 878 deadzone = 1500;
858 desired_avg_lum = 23000; 879 desired_avg_lum = 23000;
859 } 880 }
860 881
861 if (sd->autogain_ignore_frames > 0) 882 if (sensor_data[sd->sensor].flags & F_COARSE_EXPO)
862 sd->autogain_ignore_frames--; 883 result = gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
863 else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, 884 sd->brightness * desired_avg_lum / 127,
864 sd->brightness * desired_avg_lum / 127, 885 deadzone);
865 deadzone, GAIN_KNEE, EXPOSURE_KNEE)) { 886 else
887 result = gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
888 sd->brightness * desired_avg_lum / 127,
889 deadzone, GAIN_KNEE, EXPOSURE_KNEE);
890
891 if (result) {
866 PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d", 892 PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d",
867 (int)sd->gain, (int)sd->exposure); 893 (int)sd->gain, (int)sd->exposure);
868 sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; 894 sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES;
@@ -897,7 +923,13 @@ static int sd_config(struct gspca_dev *gspca_dev,
897 923
898 sd->brightness = BRIGHTNESS_DEF; 924 sd->brightness = BRIGHTNESS_DEF;
899 sd->gain = GAIN_DEF; 925 sd->gain = GAIN_DEF;
900 sd->exposure = EXPOSURE_DEF; 926 if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) {
927 sd->exposure = COARSE_EXPOSURE_DEF;
928 gspca_dev->ctrl_dis |= (1 << EXPOSURE_IDX);
929 } else {
930 sd->exposure = EXPOSURE_DEF;
931 gspca_dev->ctrl_dis |= (1 << COARSE_EXPOSURE_IDX);
932 }
901 if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX)) 933 if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
902 sd->autogain = 0; /* Disable do_autogain callback */ 934 sd->autogain = 0; /* Disable do_autogain callback */
903 else 935 else
@@ -1001,6 +1033,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
1001 1033
1002 sd->frames_to_drop = 0; 1034 sd->frames_to_drop = 0;
1003 sd->autogain_ignore_frames = 0; 1035 sd->autogain_ignore_frames = 0;
1036 sd->exp_too_high_cnt = 0;
1037 sd->exp_too_low_cnt = 0;
1004 atomic_set(&sd->avg_lum, -1); 1038 atomic_set(&sd->avg_lum, -1);
1005 return 0; 1039 return 0;
1006} 1040}
@@ -1159,11 +1193,14 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
1159 struct sd *sd = (struct sd *) gspca_dev; 1193 struct sd *sd = (struct sd *) gspca_dev;
1160 1194
1161 sd->autogain = val; 1195 sd->autogain = val;
1196 sd->exp_too_high_cnt = 0;
1197 sd->exp_too_low_cnt = 0;
1198
1162 /* when switching to autogain set defaults to make sure 1199 /* when switching to autogain set defaults to make sure
1163 we are on a valid point of the autogain gain / 1200 we are on a valid point of the autogain gain /
1164 exposure knee graph, and give this change time to 1201 exposure knee graph, and give this change time to
1165 take effect before doing autogain. */ 1202 take effect before doing autogain. */
1166 if (sd->autogain) { 1203 if (sd->autogain && !(sensor_data[sd->sensor].flags & F_COARSE_EXPO)) {
1167 sd->exposure = EXPOSURE_DEF; 1204 sd->exposure = EXPOSURE_DEF;
1168 sd->gain = GAIN_DEF; 1205 sd->gain = GAIN_DEF;
1169 if (gspca_dev->streaming) { 1206 if (gspca_dev->streaming) {