Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Functions for auto gain.
0004  *
0005  * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com>
0006  */
0007 #include "gspca.h"
0008 
0009 /* auto gain and exposure algorithm based on the knee algorithm described here:
0010    http://ytse.tricolour.net/docs/LowLightOptimization.html
0011 
0012    Returns 0 if no changes were made, 1 if the gain and or exposure settings
0013    where changed. */
0014 int gspca_expo_autogain(
0015             struct gspca_dev *gspca_dev,
0016             int avg_lum,
0017             int desired_avg_lum,
0018             int deadzone,
0019             int gain_knee,
0020             int exposure_knee)
0021 {
0022     s32 gain, orig_gain, exposure, orig_exposure;
0023     int i, steps, retval = 0;
0024 
0025     if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
0026         return 0;
0027 
0028     orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
0029     orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
0030 
0031     /* If we are of a multiple of deadzone, do multiple steps to reach the
0032        desired lumination fast (with the risc of a slight overshoot) */
0033     steps = abs(desired_avg_lum - avg_lum) / deadzone;
0034 
0035     gspca_dbg(gspca_dev, D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n",
0036           avg_lum, desired_avg_lum, steps);
0037 
0038     for (i = 0; i < steps; i++) {
0039         if (avg_lum > desired_avg_lum) {
0040             if (gain > gain_knee)
0041                 gain--;
0042             else if (exposure > exposure_knee)
0043                 exposure--;
0044             else if (gain > gspca_dev->gain->default_value)
0045                 gain--;
0046             else if (exposure > gspca_dev->exposure->minimum)
0047                 exposure--;
0048             else if (gain > gspca_dev->gain->minimum)
0049                 gain--;
0050             else
0051                 break;
0052         } else {
0053             if (gain < gspca_dev->gain->default_value)
0054                 gain++;
0055             else if (exposure < exposure_knee)
0056                 exposure++;
0057             else if (gain < gain_knee)
0058                 gain++;
0059             else if (exposure < gspca_dev->exposure->maximum)
0060                 exposure++;
0061             else if (gain < gspca_dev->gain->maximum)
0062                 gain++;
0063             else
0064                 break;
0065         }
0066     }
0067 
0068     if (gain != orig_gain) {
0069         v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
0070         retval = 1;
0071     }
0072     if (exposure != orig_exposure) {
0073         v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
0074         retval = 1;
0075     }
0076 
0077     if (retval)
0078         gspca_dbg(gspca_dev, D_FRAM, "autogain: changed gain: %d, expo: %d\n",
0079               gain, exposure);
0080     return retval;
0081 }
0082 EXPORT_SYMBOL(gspca_expo_autogain);
0083 
0084 /* Autogain + exposure algorithm for cameras with a coarse exposure control
0085    (usually this means we can only control the clockdiv to change exposure)
0086    As changing the clockdiv so that the fps drops from 30 to 15 fps for
0087    example, will lead to a huge exposure change (it effectively doubles),
0088    this algorithm normally tries to only adjust the gain (between 40 and
0089    80 %) and if that does not help, only then changes exposure. This leads
0090    to a much more stable image then using the knee algorithm which at
0091    certain points of the knee graph will only try to adjust exposure,
0092    which leads to oscillating as one exposure step is huge.
0093 
0094    Returns 0 if no changes were made, 1 if the gain and or exposure settings
0095    where changed. */
0096 int gspca_coarse_grained_expo_autogain(
0097             struct gspca_dev *gspca_dev,
0098             int avg_lum,
0099             int desired_avg_lum,
0100             int deadzone)
0101 {
0102     s32 gain_low, gain_high, gain, orig_gain, exposure, orig_exposure;
0103     int steps, retval = 0;
0104 
0105     if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
0106         return 0;
0107 
0108     orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
0109     orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
0110 
0111     gain_low  = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
0112             5 * 2 + gspca_dev->gain->minimum;
0113     gain_high = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
0114             5 * 4 + gspca_dev->gain->minimum;
0115 
0116     /* If we are of a multiple of deadzone, do multiple steps to reach the
0117        desired lumination fast (with the risc of a slight overshoot) */
0118     steps = (desired_avg_lum - avg_lum) / deadzone;
0119 
0120     gspca_dbg(gspca_dev, D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n",
0121           avg_lum, desired_avg_lum, steps);
0122 
0123     if ((gain + steps) > gain_high &&
0124         exposure < gspca_dev->exposure->maximum) {
0125         gain = gain_high;
0126         gspca_dev->exp_too_low_cnt++;
0127         gspca_dev->exp_too_high_cnt = 0;
0128     } else if ((gain + steps) < gain_low &&
0129            exposure > gspca_dev->exposure->minimum) {
0130         gain = gain_low;
0131         gspca_dev->exp_too_high_cnt++;
0132         gspca_dev->exp_too_low_cnt = 0;
0133     } else {
0134         gain += steps;
0135         if (gain > gspca_dev->gain->maximum)
0136             gain = gspca_dev->gain->maximum;
0137         else if (gain < gspca_dev->gain->minimum)
0138             gain = gspca_dev->gain->minimum;
0139         gspca_dev->exp_too_high_cnt = 0;
0140         gspca_dev->exp_too_low_cnt = 0;
0141     }
0142 
0143     if (gspca_dev->exp_too_high_cnt > 3) {
0144         exposure--;
0145         gspca_dev->exp_too_high_cnt = 0;
0146     } else if (gspca_dev->exp_too_low_cnt > 3) {
0147         exposure++;
0148         gspca_dev->exp_too_low_cnt = 0;
0149     }
0150 
0151     if (gain != orig_gain) {
0152         v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
0153         retval = 1;
0154     }
0155     if (exposure != orig_exposure) {
0156         v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
0157         retval = 1;
0158     }
0159 
0160     if (retval)
0161         gspca_dbg(gspca_dev, D_FRAM, "autogain: changed gain: %d, expo: %d\n",
0162               gain, exposure);
0163     return retval;
0164 }
0165 EXPORT_SYMBOL(gspca_coarse_grained_expo_autogain);