+static int ov534_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ gspca_dev->usb_err = 0;
+ if (ctrl->val && sd->gain && gspca_dev->streaming)
+ sd->gain->val = getgain(gspca_dev);
+ return gspca_dev->usb_err;
+
+ case V4L2_CID_EXPOSURE_AUTO:
+ gspca_dev->usb_err = 0;
+ if (ctrl->val == V4L2_EXPOSURE_AUTO && sd->exposure &&
+ gspca_dev->streaming)
+ sd->exposure->val = getexposure(gspca_dev);
+ return gspca_dev->usb_err;
+ }
+ return -EINVAL;
+}
+
+static int ov534_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct sd *sd = container_of(ctrl->handler, struct sd, ctrl_handler);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+ gspca_dev->usb_err = 0;
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HUE:
+ sethue(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setsaturation(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ /* case V4L2_CID_GAIN: */
+ setagc(gspca_dev, ctrl->val);
+ if (!gspca_dev->usb_err && !ctrl->val && sd->gain)
+ setgain(gspca_dev, sd->gain->val);
+ break;
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ setawb(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE_AUTO:
+ /* case V4L2_CID_EXPOSURE: */
+ setaec(gspca_dev, ctrl->val);
+ if (!gspca_dev->usb_err && ctrl->val == V4L2_EXPOSURE_MANUAL &&
+ sd->exposure)
+ setexposure(gspca_dev, sd->exposure->val);
+ break;
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ sethvflip(gspca_dev, ctrl->val, sd->vflip->val);
+ break;
+ case V4L2_CID_VFLIP:
+ sethvflip(gspca_dev, sd->hflip->val, ctrl->val);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setlightfreq(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops ov534_ctrl_ops = {
+ .g_volatile_ctrl = ov534_g_volatile_ctrl,
+ .s_ctrl = ov534_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &sd->ctrl_handler;
+ /* parameters with different values between the supported sensors */
+ int saturation_min;
+ int saturation_max;
+ int saturation_def;
+ int brightness_min;
+ int brightness_max;
+ int brightness_def;
+ int contrast_max;
+ int contrast_def;
+ int exposure_min;
+ int exposure_max;
+ int exposure_def;
+ int hflip_def;
+
+ if (sd->sensor == SENSOR_OV767x) {
+ saturation_min = 0,
+ saturation_max = 6,
+ saturation_def = 3,
+ brightness_min = -127;
+ brightness_max = 127;
+ brightness_def = 0;
+ contrast_max = 0x80;
+ contrast_def = 0x40;
+ exposure_min = 0x08;
+ exposure_max = 0x60;
+ exposure_def = 0x13;
+ hflip_def = 1;
+ } else {
+ saturation_min = 0,
+ saturation_max = 255,
+ saturation_def = 64,
+ brightness_min = 0;
+ brightness_max = 255;
+ brightness_def = 0;
+ contrast_max = 255;
+ contrast_def = 32;
+ exposure_min = 0;
+ exposure_max = 255;
+ exposure_def = 120;
+ hflip_def = 0;
+ }
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+
+ v4l2_ctrl_handler_init(hdl, 13);
+
+ if (sd->sensor == SENSOR_OV772x)
+ sd->hue = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_HUE, -90, 90, 1, 0);
+
+ sd->saturation = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_SATURATION, saturation_min, saturation_max, 1,
+ saturation_def);
+ sd->brightness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, brightness_min, brightness_max, 1,
+ brightness_def);
+ sd->contrast = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, contrast_max, 1, contrast_def);
+
+ if (sd->sensor == SENSOR_OV772x) {
+ sd->autogain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ sd->gain = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_GAIN, 0, 63, 1, 20);
+ }
+
+ sd->autoexposure = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops,
+ V4L2_CID_EXPOSURE_AUTO,
+ V4L2_EXPOSURE_MANUAL, 0,
+ V4L2_EXPOSURE_AUTO);
+ sd->exposure = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_EXPOSURE, exposure_min, exposure_max, 1,
+ exposure_def);
+
+ sd->autowhitebalance = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
+
+ if (sd->sensor == SENSOR_OV772x)
+ sd->sharpness = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 63, 1, 0);
+
+ sd->hflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, hflip_def);
+ sd->vflip = v4l2_ctrl_new_std(hdl, &ov534_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &ov534_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+
+ if (sd->sensor == SENSOR_OV772x)
+ v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true);
+
+ v4l2_ctrl_auto_cluster(2, &sd->autoexposure, V4L2_EXPOSURE_MANUAL,
+ true);
+
+ return 0;
+}
+