aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/gspca/sonixb.c
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/video/gspca/sonixb.c
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/video/gspca/sonixb.c')
-rw-r--r--drivers/media/video/gspca/sonixb.c113
1 files changed, 75 insertions, 38 deletions
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) {