diff options
Diffstat (limited to 'drivers/media/video/gspca/gspca.c')
-rw-r--r-- | drivers/media/video/gspca/gspca.c | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 09f190c689d..2ffb00ab081 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -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) { |