]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/media/usb/gspca/autogain_functions.h
[media] rename most media/video usb drivers to media/usb
[mirror_ubuntu-artful-kernel.git] / drivers / media / usb / gspca / autogain_functions.h
1 /*
2 * Functions for auto gain.
3 *
4 * Copyright (C) 2010-2011 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 #ifdef WANT_REGULAR_AUTOGAIN
22 /* auto gain and exposure algorithm based on the knee algorithm described here:
23 http://ytse.tricolour.net/docs/LowLightOptimization.html
24
25 Returns 0 if no changes were made, 1 if the gain and or exposure settings
26 where changed. */
27 static inline int auto_gain_n_exposure(
28 struct gspca_dev *gspca_dev,
29 int avg_lum,
30 int desired_avg_lum,
31 int deadzone,
32 int gain_knee,
33 int exposure_knee)
34 {
35 struct sd *sd = (struct sd *) gspca_dev;
36 int i, steps, gain, orig_gain, exposure, orig_exposure;
37 int retval = 0;
38
39 orig_gain = gain = sd->ctrls[GAIN].val;
40 orig_exposure = exposure = sd->ctrls[EXPOSURE].val;
41
42 /* If we are of a multiple of deadzone, do multiple steps to reach the
43 desired lumination fast (with the risc of a slight overshoot) */
44 steps = abs(desired_avg_lum - avg_lum) / deadzone;
45
46 PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
47 avg_lum, desired_avg_lum, steps);
48
49 for (i = 0; i < steps; i++) {
50 if (avg_lum > desired_avg_lum) {
51 if (gain > gain_knee)
52 gain--;
53 else if (exposure > exposure_knee)
54 exposure--;
55 else if (gain > sd->ctrls[GAIN].def)
56 gain--;
57 else if (exposure > sd->ctrls[EXPOSURE].min)
58 exposure--;
59 else if (gain > sd->ctrls[GAIN].min)
60 gain--;
61 else
62 break;
63 } else {
64 if (gain < sd->ctrls[GAIN].def)
65 gain++;
66 else if (exposure < exposure_knee)
67 exposure++;
68 else if (gain < gain_knee)
69 gain++;
70 else if (exposure < sd->ctrls[EXPOSURE].max)
71 exposure++;
72 else if (gain < sd->ctrls[GAIN].max)
73 gain++;
74 else
75 break;
76 }
77 }
78
79 if (gain != orig_gain) {
80 sd->ctrls[GAIN].val = gain;
81 setgain(gspca_dev);
82 retval = 1;
83 }
84 if (exposure != orig_exposure) {
85 sd->ctrls[EXPOSURE].val = exposure;
86 setexposure(gspca_dev);
87 retval = 1;
88 }
89
90 if (retval)
91 PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
92 gain, exposure);
93 return retval;
94 }
95 #endif
96
97 #ifdef WANT_COARSE_EXPO_AUTOGAIN
98 /* Autogain + exposure algorithm for cameras with a coarse exposure control
99 (usually this means we can only control the clockdiv to change exposure)
100 As changing the clockdiv so that the fps drops from 30 to 15 fps for
101 example, will lead to a huge exposure change (it effectively doubles),
102 this algorithm normally tries to only adjust the gain (between 40 and
103 80 %) and if that does not help, only then changes exposure. This leads
104 to a much more stable image then using the knee algorithm which at
105 certain points of the knee graph will only try to adjust exposure,
106 which leads to oscilating as one exposure step is huge.
107
108 Note this assumes that the sd struct for the cam in question has
109 exp_too_low_cnt and exp_too_high_cnt int members for use by this function.
110
111 Returns 0 if no changes were made, 1 if the gain and or exposure settings
112 where changed. */
113 static inline int coarse_grained_expo_autogain(
114 struct gspca_dev *gspca_dev,
115 int avg_lum,
116 int desired_avg_lum,
117 int deadzone)
118 {
119 struct sd *sd = (struct sd *) gspca_dev;
120 int steps, gain, orig_gain, exposure, orig_exposure;
121 int gain_low, gain_high;
122 int retval = 0;
123
124 orig_gain = gain = sd->ctrls[GAIN].val;
125 orig_exposure = exposure = sd->ctrls[EXPOSURE].val;
126
127 gain_low = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 2;
128 gain_low += sd->ctrls[GAIN].min;
129 gain_high = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 4;
130 gain_high += sd->ctrls[GAIN].min;
131
132 /* If we are of a multiple of deadzone, do multiple steps to reach the
133 desired lumination fast (with the risc of a slight overshoot) */
134 steps = (desired_avg_lum - avg_lum) / deadzone;
135
136 PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
137 avg_lum, desired_avg_lum, steps);
138
139 if ((gain + steps) > gain_high &&
140 exposure < sd->ctrls[EXPOSURE].max) {
141 gain = gain_high;
142 sd->exp_too_low_cnt++;
143 sd->exp_too_high_cnt = 0;
144 } else if ((gain + steps) < gain_low &&
145 exposure > sd->ctrls[EXPOSURE].min) {
146 gain = gain_low;
147 sd->exp_too_high_cnt++;
148 sd->exp_too_low_cnt = 0;
149 } else {
150 gain += steps;
151 if (gain > sd->ctrls[GAIN].max)
152 gain = sd->ctrls[GAIN].max;
153 else if (gain < sd->ctrls[GAIN].min)
154 gain = sd->ctrls[GAIN].min;
155 sd->exp_too_high_cnt = 0;
156 sd->exp_too_low_cnt = 0;
157 }
158
159 if (sd->exp_too_high_cnt > 3) {
160 exposure--;
161 sd->exp_too_high_cnt = 0;
162 } else if (sd->exp_too_low_cnt > 3) {
163 exposure++;
164 sd->exp_too_low_cnt = 0;
165 }
166
167 if (gain != orig_gain) {
168 sd->ctrls[GAIN].val = gain;
169 setgain(gspca_dev);
170 retval = 1;
171 }
172 if (exposure != orig_exposure) {
173 sd->ctrls[EXPOSURE].val = exposure;
174 setexposure(gspca_dev);
175 retval = 1;
176 }
177
178 if (retval)
179 PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
180 gain, exposure);
181 return retval;
182 }
183 #endif