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