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