Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] gspca: New file autogain_functions.h

This file contains functions for the auto gain.
It must be used with the new control mechanism.

Acked-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Jean-François Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

authored by

Jean-François Moine and committed by
Mauro Carvalho Chehab
fa85bb6f 4e5144ee

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