18cd058423f22072c1147d3e99161252e1e72333aHans de Goede/*
28cd058423f22072c1147d3e99161252e1e72333aHans de Goede * Functions for auto gain.
38cd058423f22072c1147d3e99161252e1e72333aHans de Goede *
48cd058423f22072c1147d3e99161252e1e72333aHans de Goede * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com>
58cd058423f22072c1147d3e99161252e1e72333aHans de Goede *
68cd058423f22072c1147d3e99161252e1e72333aHans de Goede * This program is free software; you can redistribute it and/or modify
78cd058423f22072c1147d3e99161252e1e72333aHans de Goede * it under the terms of the GNU General Public License as published by
88cd058423f22072c1147d3e99161252e1e72333aHans de Goede * the Free Software Foundation; either version 2 of the License, or
98cd058423f22072c1147d3e99161252e1e72333aHans de Goede * (at your option) any later version.
108cd058423f22072c1147d3e99161252e1e72333aHans de Goede *
118cd058423f22072c1147d3e99161252e1e72333aHans de Goede * This program is distributed in the hope that it will be useful,
128cd058423f22072c1147d3e99161252e1e72333aHans de Goede * but WITHOUT ANY WARRANTY; without even the implied warranty of
138cd058423f22072c1147d3e99161252e1e72333aHans de Goede * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
148cd058423f22072c1147d3e99161252e1e72333aHans de Goede * GNU General Public License for more details.
158cd058423f22072c1147d3e99161252e1e72333aHans de Goede *
168cd058423f22072c1147d3e99161252e1e72333aHans de Goede * You should have received a copy of the GNU General Public License
178cd058423f22072c1147d3e99161252e1e72333aHans de Goede * along with this program; if not, write to the Free Software
188cd058423f22072c1147d3e99161252e1e72333aHans de Goede * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
198cd058423f22072c1147d3e99161252e1e72333aHans de Goede */
208cd058423f22072c1147d3e99161252e1e72333aHans de Goede#include "gspca.h"
218cd058423f22072c1147d3e99161252e1e72333aHans de Goede
228cd058423f22072c1147d3e99161252e1e72333aHans de Goede/* auto gain and exposure algorithm based on the knee algorithm described here:
238cd058423f22072c1147d3e99161252e1e72333aHans de Goede   http://ytse.tricolour.net/docs/LowLightOptimization.html
248cd058423f22072c1147d3e99161252e1e72333aHans de Goede
258cd058423f22072c1147d3e99161252e1e72333aHans de Goede   Returns 0 if no changes were made, 1 if the gain and or exposure settings
268cd058423f22072c1147d3e99161252e1e72333aHans de Goede   where changed. */
278cd058423f22072c1147d3e99161252e1e72333aHans de Goedeint gspca_expo_autogain(
288cd058423f22072c1147d3e99161252e1e72333aHans de Goede			struct gspca_dev *gspca_dev,
298cd058423f22072c1147d3e99161252e1e72333aHans de Goede			int avg_lum,
308cd058423f22072c1147d3e99161252e1e72333aHans de Goede			int desired_avg_lum,
318cd058423f22072c1147d3e99161252e1e72333aHans de Goede			int deadzone,
328cd058423f22072c1147d3e99161252e1e72333aHans de Goede			int gain_knee,
338cd058423f22072c1147d3e99161252e1e72333aHans de Goede			int exposure_knee)
348cd058423f22072c1147d3e99161252e1e72333aHans de Goede{
358cd058423f22072c1147d3e99161252e1e72333aHans de Goede	s32 gain, orig_gain, exposure, orig_exposure;
368cd058423f22072c1147d3e99161252e1e72333aHans de Goede	int i, steps, retval = 0;
378cd058423f22072c1147d3e99161252e1e72333aHans de Goede
388cd058423f22072c1147d3e99161252e1e72333aHans de Goede	if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
398cd058423f22072c1147d3e99161252e1e72333aHans de Goede	        return 0;
408cd058423f22072c1147d3e99161252e1e72333aHans de Goede
418cd058423f22072c1147d3e99161252e1e72333aHans de Goede	orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
428cd058423f22072c1147d3e99161252e1e72333aHans de Goede	orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
438cd058423f22072c1147d3e99161252e1e72333aHans de Goede
448cd058423f22072c1147d3e99161252e1e72333aHans de Goede	/* If we are of a multiple of deadzone, do multiple steps to reach the
458cd058423f22072c1147d3e99161252e1e72333aHans de Goede	   desired lumination fast (with the risc of a slight overshoot) */
468cd058423f22072c1147d3e99161252e1e72333aHans de Goede	steps = abs(desired_avg_lum - avg_lum) / deadzone;
478cd058423f22072c1147d3e99161252e1e72333aHans de Goede
488cd058423f22072c1147d3e99161252e1e72333aHans de Goede	PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
498cd058423f22072c1147d3e99161252e1e72333aHans de Goede		avg_lum, desired_avg_lum, steps);
508cd058423f22072c1147d3e99161252e1e72333aHans de Goede
518cd058423f22072c1147d3e99161252e1e72333aHans de Goede	for (i = 0; i < steps; i++) {
528cd058423f22072c1147d3e99161252e1e72333aHans de Goede		if (avg_lum > desired_avg_lum) {
538cd058423f22072c1147d3e99161252e1e72333aHans de Goede			if (gain > gain_knee)
548cd058423f22072c1147d3e99161252e1e72333aHans de Goede				gain--;
558cd058423f22072c1147d3e99161252e1e72333aHans de Goede			else if (exposure > exposure_knee)
568cd058423f22072c1147d3e99161252e1e72333aHans de Goede				exposure--;
578cd058423f22072c1147d3e99161252e1e72333aHans de Goede			else if (gain > gspca_dev->gain->default_value)
588cd058423f22072c1147d3e99161252e1e72333aHans de Goede				gain--;
598cd058423f22072c1147d3e99161252e1e72333aHans de Goede			else if (exposure > gspca_dev->exposure->minimum)
608cd058423f22072c1147d3e99161252e1e72333aHans de Goede				exposure--;
618cd058423f22072c1147d3e99161252e1e72333aHans de Goede			else if (gain > gspca_dev->gain->minimum)
628cd058423f22072c1147d3e99161252e1e72333aHans de Goede				gain--;
638cd058423f22072c1147d3e99161252e1e72333aHans de Goede			else
648cd058423f22072c1147d3e99161252e1e72333aHans de Goede				break;
658cd058423f22072c1147d3e99161252e1e72333aHans de Goede		} else {
668cd058423f22072c1147d3e99161252e1e72333aHans de Goede			if (gain < gspca_dev->gain->default_value)
678cd058423f22072c1147d3e99161252e1e72333aHans de Goede				gain++;
688cd058423f22072c1147d3e99161252e1e72333aHans de Goede			else if (exposure < exposure_knee)
698cd058423f22072c1147d3e99161252e1e72333aHans de Goede				exposure++;
708cd058423f22072c1147d3e99161252e1e72333aHans de Goede			else if (gain < gain_knee)
718cd058423f22072c1147d3e99161252e1e72333aHans de Goede				gain++;
728cd058423f22072c1147d3e99161252e1e72333aHans de Goede			else if (exposure < gspca_dev->exposure->maximum)
738cd058423f22072c1147d3e99161252e1e72333aHans de Goede				exposure++;
748cd058423f22072c1147d3e99161252e1e72333aHans de Goede			else if (gain < gspca_dev->gain->maximum)
758cd058423f22072c1147d3e99161252e1e72333aHans de Goede				gain++;
768cd058423f22072c1147d3e99161252e1e72333aHans de Goede			else
778cd058423f22072c1147d3e99161252e1e72333aHans de Goede				break;
788cd058423f22072c1147d3e99161252e1e72333aHans de Goede		}
798cd058423f22072c1147d3e99161252e1e72333aHans de Goede	}
808cd058423f22072c1147d3e99161252e1e72333aHans de Goede
818cd058423f22072c1147d3e99161252e1e72333aHans de Goede	if (gain != orig_gain) {
828cd058423f22072c1147d3e99161252e1e72333aHans de Goede	        v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
838cd058423f22072c1147d3e99161252e1e72333aHans de Goede		retval = 1;
848cd058423f22072c1147d3e99161252e1e72333aHans de Goede	}
858cd058423f22072c1147d3e99161252e1e72333aHans de Goede	if (exposure != orig_exposure) {
868cd058423f22072c1147d3e99161252e1e72333aHans de Goede	        v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
878cd058423f22072c1147d3e99161252e1e72333aHans de Goede		retval = 1;
888cd058423f22072c1147d3e99161252e1e72333aHans de Goede	}
898cd058423f22072c1147d3e99161252e1e72333aHans de Goede
908cd058423f22072c1147d3e99161252e1e72333aHans de Goede	if (retval)
918cd058423f22072c1147d3e99161252e1e72333aHans de Goede		PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
928cd058423f22072c1147d3e99161252e1e72333aHans de Goede			gain, exposure);
938cd058423f22072c1147d3e99161252e1e72333aHans de Goede	return retval;
948cd058423f22072c1147d3e99161252e1e72333aHans de Goede}
958cd058423f22072c1147d3e99161252e1e72333aHans de GoedeEXPORT_SYMBOL(gspca_expo_autogain);
968cd058423f22072c1147d3e99161252e1e72333aHans de Goede
978cd058423f22072c1147d3e99161252e1e72333aHans de Goede/* Autogain + exposure algorithm for cameras with a coarse exposure control
988cd058423f22072c1147d3e99161252e1e72333aHans de Goede   (usually this means we can only control the clockdiv to change exposure)
998cd058423f22072c1147d3e99161252e1e72333aHans de Goede   As changing the clockdiv so that the fps drops from 30 to 15 fps for
1008cd058423f22072c1147d3e99161252e1e72333aHans de Goede   example, will lead to a huge exposure change (it effectively doubles),
1018cd058423f22072c1147d3e99161252e1e72333aHans de Goede   this algorithm normally tries to only adjust the gain (between 40 and
1028cd058423f22072c1147d3e99161252e1e72333aHans de Goede   80 %) and if that does not help, only then changes exposure. This leads
1038cd058423f22072c1147d3e99161252e1e72333aHans de Goede   to a much more stable image then using the knee algorithm which at
1048cd058423f22072c1147d3e99161252e1e72333aHans de Goede   certain points of the knee graph will only try to adjust exposure,
1058cd058423f22072c1147d3e99161252e1e72333aHans de Goede   which leads to oscilating as one exposure step is huge.
1068cd058423f22072c1147d3e99161252e1e72333aHans de Goede
1078cd058423f22072c1147d3e99161252e1e72333aHans de Goede   Returns 0 if no changes were made, 1 if the gain and or exposure settings
1088cd058423f22072c1147d3e99161252e1e72333aHans de Goede   where changed. */
1098cd058423f22072c1147d3e99161252e1e72333aHans de Goedeint gspca_coarse_grained_expo_autogain(
1108cd058423f22072c1147d3e99161252e1e72333aHans de Goede			struct gspca_dev *gspca_dev,
1118cd058423f22072c1147d3e99161252e1e72333aHans de Goede			int avg_lum,
1128cd058423f22072c1147d3e99161252e1e72333aHans de Goede			int desired_avg_lum,
1138cd058423f22072c1147d3e99161252e1e72333aHans de Goede			int deadzone)
1148cd058423f22072c1147d3e99161252e1e72333aHans de Goede{
1158cd058423f22072c1147d3e99161252e1e72333aHans de Goede	s32 gain_low, gain_high, gain, orig_gain, exposure, orig_exposure;
1168cd058423f22072c1147d3e99161252e1e72333aHans de Goede	int steps, retval = 0;
1178cd058423f22072c1147d3e99161252e1e72333aHans de Goede
1188cd058423f22072c1147d3e99161252e1e72333aHans de Goede	if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
1198cd058423f22072c1147d3e99161252e1e72333aHans de Goede	        return 0;
1208cd058423f22072c1147d3e99161252e1e72333aHans de Goede
1218cd058423f22072c1147d3e99161252e1e72333aHans de Goede	orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
1228cd058423f22072c1147d3e99161252e1e72333aHans de Goede	orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
1238cd058423f22072c1147d3e99161252e1e72333aHans de Goede
1240d5e8c4313c83dc2d60519a219d517a13ba8a432Hans Verkuil	gain_low  = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
1258cd058423f22072c1147d3e99161252e1e72333aHans de Goede		    5 * 2 + gspca_dev->gain->minimum;
1260d5e8c4313c83dc2d60519a219d517a13ba8a432Hans Verkuil	gain_high = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
1278cd058423f22072c1147d3e99161252e1e72333aHans de Goede		    5 * 4 + gspca_dev->gain->minimum;
1288cd058423f22072c1147d3e99161252e1e72333aHans de Goede
1298cd058423f22072c1147d3e99161252e1e72333aHans de Goede	/* If we are of a multiple of deadzone, do multiple steps to reach the
1308cd058423f22072c1147d3e99161252e1e72333aHans de Goede	   desired lumination fast (with the risc of a slight overshoot) */
1318cd058423f22072c1147d3e99161252e1e72333aHans de Goede	steps = (desired_avg_lum - avg_lum) / deadzone;
1328cd058423f22072c1147d3e99161252e1e72333aHans de Goede
1338cd058423f22072c1147d3e99161252e1e72333aHans de Goede	PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
1348cd058423f22072c1147d3e99161252e1e72333aHans de Goede		avg_lum, desired_avg_lum, steps);
1358cd058423f22072c1147d3e99161252e1e72333aHans de Goede
1368cd058423f22072c1147d3e99161252e1e72333aHans de Goede	if ((gain + steps) > gain_high &&
1378cd058423f22072c1147d3e99161252e1e72333aHans de Goede	    exposure < gspca_dev->exposure->maximum) {
1388cd058423f22072c1147d3e99161252e1e72333aHans de Goede		gain = gain_high;
1398cd058423f22072c1147d3e99161252e1e72333aHans de Goede		gspca_dev->exp_too_low_cnt++;
1408cd058423f22072c1147d3e99161252e1e72333aHans de Goede		gspca_dev->exp_too_high_cnt = 0;
1418cd058423f22072c1147d3e99161252e1e72333aHans de Goede	} else if ((gain + steps) < gain_low &&
1428cd058423f22072c1147d3e99161252e1e72333aHans de Goede		   exposure > gspca_dev->exposure->minimum) {
1438cd058423f22072c1147d3e99161252e1e72333aHans de Goede		gain = gain_low;
1448cd058423f22072c1147d3e99161252e1e72333aHans de Goede		gspca_dev->exp_too_high_cnt++;
1458cd058423f22072c1147d3e99161252e1e72333aHans de Goede		gspca_dev->exp_too_low_cnt = 0;
1468cd058423f22072c1147d3e99161252e1e72333aHans de Goede	} else {
1478cd058423f22072c1147d3e99161252e1e72333aHans de Goede		gain += steps;
1488cd058423f22072c1147d3e99161252e1e72333aHans de Goede		if (gain > gspca_dev->gain->maximum)
1498cd058423f22072c1147d3e99161252e1e72333aHans de Goede			gain = gspca_dev->gain->maximum;
1508cd058423f22072c1147d3e99161252e1e72333aHans de Goede		else if (gain < gspca_dev->gain->minimum)
1518cd058423f22072c1147d3e99161252e1e72333aHans de Goede			gain = gspca_dev->gain->minimum;
1528cd058423f22072c1147d3e99161252e1e72333aHans de Goede		gspca_dev->exp_too_high_cnt = 0;
1538cd058423f22072c1147d3e99161252e1e72333aHans de Goede		gspca_dev->exp_too_low_cnt = 0;
1548cd058423f22072c1147d3e99161252e1e72333aHans de Goede	}
1558cd058423f22072c1147d3e99161252e1e72333aHans de Goede
1568cd058423f22072c1147d3e99161252e1e72333aHans de Goede	if (gspca_dev->exp_too_high_cnt > 3) {
1578cd058423f22072c1147d3e99161252e1e72333aHans de Goede		exposure--;
1588cd058423f22072c1147d3e99161252e1e72333aHans de Goede		gspca_dev->exp_too_high_cnt = 0;
1598cd058423f22072c1147d3e99161252e1e72333aHans de Goede	} else if (gspca_dev->exp_too_low_cnt > 3) {
1608cd058423f22072c1147d3e99161252e1e72333aHans de Goede		exposure++;
1618cd058423f22072c1147d3e99161252e1e72333aHans de Goede		gspca_dev->exp_too_low_cnt = 0;
1628cd058423f22072c1147d3e99161252e1e72333aHans de Goede	}
1638cd058423f22072c1147d3e99161252e1e72333aHans de Goede
1648cd058423f22072c1147d3e99161252e1e72333aHans de Goede	if (gain != orig_gain) {
1658cd058423f22072c1147d3e99161252e1e72333aHans de Goede	        v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
1668cd058423f22072c1147d3e99161252e1e72333aHans de Goede		retval = 1;
1678cd058423f22072c1147d3e99161252e1e72333aHans de Goede	}
1688cd058423f22072c1147d3e99161252e1e72333aHans de Goede	if (exposure != orig_exposure) {
1698cd058423f22072c1147d3e99161252e1e72333aHans de Goede	        v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
1708cd058423f22072c1147d3e99161252e1e72333aHans de Goede		retval = 1;
1718cd058423f22072c1147d3e99161252e1e72333aHans de Goede	}
1728cd058423f22072c1147d3e99161252e1e72333aHans de Goede
1738cd058423f22072c1147d3e99161252e1e72333aHans de Goede	if (retval)
1748cd058423f22072c1147d3e99161252e1e72333aHans de Goede		PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
1758cd058423f22072c1147d3e99161252e1e72333aHans de Goede			gain, exposure);
1768cd058423f22072c1147d3e99161252e1e72333aHans de Goede	return retval;
1778cd058423f22072c1147d3e99161252e1e72333aHans de Goede}
1788cd058423f22072c1147d3e99161252e1e72333aHans de GoedeEXPORT_SYMBOL(gspca_coarse_grained_expo_autogain);
179