Commit dcef3237 authored by Hans de Goede's avatar Hans de Goede Committed by Mauro Carvalho Chehab

V4L/DVB (8348): gspca: Add auto gain/exposure to sonixb and tas5110 / ov6650 sensors.

sonixb:   Do auto gain for tas5110 / ov6650 sensors.
pac207:   Move the auto_gain function to gspca.
gspca:    New function gspca_auto_gain_n_exposure().
Signed-off-by: default avatarHans de Goede <j.w.r.degoede@hhs.nl>
Signed-off-by: default avatarJean-Francois Moine <moinejf@free.fr>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent d0d0e39b
......@@ -1790,6 +1790,94 @@ void gspca_disconnect(struct usb_interface *intf)
}
EXPORT_SYMBOL(gspca_disconnect);
/* -- cam driver utility functions -- */
/* auto gain and exposure algorithm based on the knee algorithm described here:
http://ytse.tricolour.net/docs/LowLightOptimization.html
Returns 0 if no changes were made, 1 if the gain and or exposure settings
where changed. */
int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee)
{
int i, steps, gain, orig_gain, exposure, orig_exposure, autogain;
const struct ctrl *gain_ctrl = NULL;
const struct ctrl *exposure_ctrl = NULL;
const struct ctrl *autogain_ctrl = NULL;
int retval = 0;
for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN)
gain_ctrl = &gspca_dev->sd_desc->ctrls[i];
if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE)
exposure_ctrl = &gspca_dev->sd_desc->ctrls[i];
if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_AUTOGAIN)
autogain_ctrl = &gspca_dev->sd_desc->ctrls[i];
}
if (!gain_ctrl || !exposure_ctrl || !autogain_ctrl) {
PDEBUG(D_ERR, "Error: gspca_auto_gain_n_exposure called "
"on cam without (auto)gain/exposure");
return 0;
}
if (gain_ctrl->get(gspca_dev, &gain) ||
exposure_ctrl->get(gspca_dev, &exposure) ||
autogain_ctrl->get(gspca_dev, &autogain) || !autogain)
return 0;
orig_gain = gain;
orig_exposure = exposure;
/* If we are of a multiple of deadzone, do multiple steps to reach the
desired lumination fast (with the risc of a slight overshoot) */
steps = abs(desired_avg_lum - avg_lum) / deadzone;
PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n",
avg_lum, desired_avg_lum, steps);
for (i = 0; i < steps; i++) {
if (avg_lum > desired_avg_lum) {
if (gain > gain_knee)
gain--;
else if (exposure > exposure_knee)
exposure--;
else if (gain > gain_ctrl->qctrl.default_value)
gain--;
else if (exposure > exposure_ctrl->qctrl.minimum)
exposure--;
else if (gain > gain_ctrl->qctrl.minimum)
gain--;
else
break;
} else {
if (gain < gain_ctrl->qctrl.default_value)
gain++;
else if (exposure < exposure_knee)
exposure++;
else if (gain < gain_knee)
gain++;
else if (exposure < exposure_ctrl->qctrl.maximum)
exposure++;
else if (gain < gain_ctrl->qctrl.maximum)
gain++;
else
break;
}
}
if (gain != orig_gain) {
gain_ctrl->set(gspca_dev, gain);
retval = 1;
}
if (exposure != orig_exposure) {
exposure_ctrl->set(gspca_dev, exposure);
retval = 1;
}
return retval;
}
EXPORT_SYMBOL(gspca_auto_gain_n_exposure);
/* -- module insert / remove -- */
static int __init gspca_init(void)
{
......
......@@ -170,4 +170,6 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
struct gspca_frame *frame,
const __u8 *data,
int len);
int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee);
#endif /* GSPCAV2_H */
......@@ -357,70 +357,20 @@ static void sd_close(struct gspca_dev *gspca_dev)
{
}
/* auto gain and exposure algorithm based on the knee algorithm described here:
* <http://ytse.tricolour.net/docs/LowLightOptimization.html> */
static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int i, steps, desired_avg_lum;
int orig_gain = sd->gain;
int orig_exposure = sd->exposure;
int avg_lum = atomic_read(&sd->avg_lum);
if (!sd->autogain || avg_lum == -1)
if (avg_lum == -1)
return;
if (sd->autogain_ignore_frames > 0) {
if (sd->autogain_ignore_frames > 0)
sd->autogain_ignore_frames--;
return;
}
/* correct desired lumination for the configured brightness */
desired_avg_lum = 100 + sd->brightness / 2;
/* If we are of a multiple of deadzone, do multiple step to reach the
desired lumination fast (with the risc of a slight overshoot) */
steps = abs(desired_avg_lum - avg_lum) / PAC207_AUTOGAIN_DEADZONE;
for (i = 0; i < steps; i++) {
if (avg_lum > desired_avg_lum) {
if (sd->gain > PAC207_GAIN_KNEE)
sd->gain--;
else if (sd->exposure > PAC207_EXPOSURE_KNEE)
sd->exposure--;
else if (sd->gain > PAC207_GAIN_DEFAULT)
sd->gain--;
else if (sd->exposure > PAC207_EXPOSURE_MIN)
sd->exposure--;
else if (sd->gain > PAC207_GAIN_MIN)
sd->gain--;
else
break;
} else {
if (sd->gain < PAC207_GAIN_DEFAULT)
sd->gain++;
else if (sd->exposure < PAC207_EXPOSURE_KNEE)
sd->exposure++;
else if (sd->gain < PAC207_GAIN_KNEE)
sd->gain++;
else if (sd->exposure < PAC207_EXPOSURE_MAX)
sd->exposure++;
else if (sd->gain < PAC207_GAIN_MAX)
sd->gain++;
else
break;
}
}
if (sd->exposure != orig_exposure || sd->gain != orig_gain) {
if (sd->exposure != orig_exposure)
pac207_write_reg(gspca_dev, 0x0002, sd->exposure);
if (sd->gain != orig_gain)
pac207_write_reg(gspca_dev, 0x000e, sd->gain);
pac207_write_reg(gspca_dev, 0x13, 0x01); /* load reg to sen */
pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
100 + sd->brightness / 2, PAC207_AUTOGAIN_DEADZONE,
PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE))
sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES;
}
}
static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev,
......@@ -546,10 +496,6 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
/* don't allow mucking with exposure when using autogain */
if (sd->autogain)
return -EINVAL;
sd->exposure = val;
if (gspca_dev->streaming)
setexposure(gspca_dev);
......@@ -568,10 +514,6 @@ static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
/* don't allow mucking with gain when using autogain */
if (sd->autogain)
return -EINVAL;
sd->gain = val;
if (gspca_dev->streaming)
setgain(gspca_dev);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment