Path: blob/master/drivers/media/video/gspca/autogain_functions.h
17653 views
/*1* Functions for auto gain.2*3* Copyright (C) 2010-2011 Hans de Goede <[email protected]>4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation; either version 2 of the License, or8* (at your option) any later version.9*10* This program is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License16* along with this program; if not, write to the Free Software17* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA18*/1920/* auto gain and exposure algorithm based on the knee algorithm described here:21http://ytse.tricolour.net/docs/LowLightOptimization.html2223Returns 0 if no changes were made, 1 if the gain and or exposure settings24where changed. */25static inline int auto_gain_n_exposure(26struct gspca_dev *gspca_dev,27int avg_lum,28int desired_avg_lum,29int deadzone,30int gain_knee,31int exposure_knee)32{33struct sd *sd = (struct sd *) gspca_dev;34int i, steps, gain, orig_gain, exposure, orig_exposure;35int retval = 0;3637orig_gain = gain = sd->ctrls[GAIN].val;38orig_exposure = exposure = sd->ctrls[EXPOSURE].val;3940/* If we are of a multiple of deadzone, do multiple steps to reach the41desired lumination fast (with the risc of a slight overshoot) */42steps = abs(desired_avg_lum - avg_lum) / deadzone;4344PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",45avg_lum, desired_avg_lum, steps);4647for (i = 0; i < steps; i++) {48if (avg_lum > desired_avg_lum) {49if (gain > gain_knee)50gain--;51else if (exposure > exposure_knee)52exposure--;53else if (gain > sd->ctrls[GAIN].def)54gain--;55else if (exposure > sd->ctrls[EXPOSURE].min)56exposure--;57else if (gain > sd->ctrls[GAIN].min)58gain--;59else60break;61} else {62if (gain < sd->ctrls[GAIN].def)63gain++;64else if (exposure < exposure_knee)65exposure++;66else if (gain < gain_knee)67gain++;68else if (exposure < sd->ctrls[EXPOSURE].max)69exposure++;70else if (gain < sd->ctrls[GAIN].max)71gain++;72else73break;74}75}7677if (gain != orig_gain) {78sd->ctrls[GAIN].val = gain;79setgain(gspca_dev);80retval = 1;81}82if (exposure != orig_exposure) {83sd->ctrls[EXPOSURE].val = exposure;84setexposure(gspca_dev);85retval = 1;86}8788if (retval)89PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",90gain, exposure);91return retval;92}9394/* Autogain + exposure algorithm for cameras with a coarse exposure control95(usually this means we can only control the clockdiv to change exposure)96As changing the clockdiv so that the fps drops from 30 to 15 fps for97example, will lead to a huge exposure change (it effectively doubles),98this algorithm normally tries to only adjust the gain (between 40 and9980 %) and if that does not help, only then changes exposure. This leads100to a much more stable image then using the knee algorithm which at101certain points of the knee graph will only try to adjust exposure,102which leads to oscilating as one exposure step is huge.103104Note this assumes that the sd struct for the cam in question has105exp_too_high_cnt and exp_too_high_cnt int members for use by this function.106107Returns 0 if no changes were made, 1 if the gain and or exposure settings108where changed. */109static inline int coarse_grained_expo_autogain(110struct gspca_dev *gspca_dev,111int avg_lum,112int desired_avg_lum,113int deadzone)114{115struct sd *sd = (struct sd *) gspca_dev;116int steps, gain, orig_gain, exposure, orig_exposure;117int gain_low, gain_high;118int retval = 0;119120orig_gain = gain = sd->ctrls[GAIN].val;121orig_exposure = exposure = sd->ctrls[EXPOSURE].val;122123gain_low = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 2;124gain_low += sd->ctrls[GAIN].min;125gain_high = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 4;126gain_high += sd->ctrls[GAIN].min;127128/* If we are of a multiple of deadzone, do multiple steps to reach the129desired lumination fast (with the risc of a slight overshoot) */130steps = (desired_avg_lum - avg_lum) / deadzone;131132PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",133avg_lum, desired_avg_lum, steps);134135if ((gain + steps) > gain_high &&136exposure < sd->ctrls[EXPOSURE].max) {137gain = gain_high;138sd->exp_too_low_cnt++;139sd->exp_too_high_cnt = 0;140} else if ((gain + steps) < gain_low &&141exposure > sd->ctrls[EXPOSURE].min) {142gain = gain_low;143sd->exp_too_high_cnt++;144sd->exp_too_low_cnt = 0;145} else {146gain += steps;147if (gain > sd->ctrls[GAIN].max)148gain = sd->ctrls[GAIN].max;149else if (gain < sd->ctrls[GAIN].min)150gain = sd->ctrls[GAIN].min;151sd->exp_too_high_cnt = 0;152sd->exp_too_low_cnt = 0;153}154155if (sd->exp_too_high_cnt > 3) {156exposure--;157sd->exp_too_high_cnt = 0;158} else if (sd->exp_too_low_cnt > 3) {159exposure++;160sd->exp_too_low_cnt = 0;161}162163if (gain != orig_gain) {164sd->ctrls[GAIN].val = gain;165setgain(gspca_dev);166retval = 1;167}168if (exposure != orig_exposure) {169sd->ctrls[EXPOSURE].val = exposure;170setexposure(gspca_dev);171retval = 1;172}173174if (retval)175PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",176gain, exposure);177return retval;178}179180181