[media] gspca_ov534: Convert to the control framework
authorAntonio Ospite <ospite@studenti.unina.it>
Wed, 16 May 2012 21:42:46 +0000 (18:42 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 30 Jul 2012 21:20:44 +0000 (18:20 -0300)
Signed-off-by: Antonio Ospite <ospite@studenti.unina.it>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/gspca/ov534.c

index 80c81dd6d68b3ce4d6a40566449b89a110ce05a4..78d11dd6781707a0e10e35d1ac24c5ab577307a8 100644 (file)
@@ -35,6 +35,7 @@
 #include "gspca.h"
 
 #include <linux/fixp-arith.h>
+#include <media/v4l2-ctrls.h>
 
 #define OV534_REG_ADDRESS      0xf1    /* sensor address */
 #define OV534_REG_SUBADDR      0xf2
@@ -53,29 +54,28 @@ MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
 MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver");
 MODULE_LICENSE("GPL");
 
-/* controls */
-enum e_ctrl {
-       HUE,
-       SATURATION,
-       BRIGHTNESS,
-       CONTRAST,
-       GAIN,
-       EXPOSURE,
-       AGC,
-       AWB,
-       AEC,
-       SHARPNESS,
-       HFLIP,
-       VFLIP,
-       LIGHTFREQ,
-       NCTRLS          /* number of controls */
-};
-
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
 
-       struct gspca_ctrl ctrls[NCTRLS];
+       struct v4l2_ctrl_handler ctrl_handler;
+       struct v4l2_ctrl *hue;
+       struct v4l2_ctrl *saturation;
+       struct v4l2_ctrl *brightness;
+       struct v4l2_ctrl *contrast;
+       struct { /* gain control cluster */
+               struct v4l2_ctrl *autogain;
+               struct v4l2_ctrl *gain;
+       };
+       struct v4l2_ctrl *autowhitebalance;
+       struct { /* exposure control cluster */
+               struct v4l2_ctrl *autoexposure;
+               struct v4l2_ctrl *exposure;
+       };
+       struct v4l2_ctrl *sharpness;
+       struct v4l2_ctrl *hflip;
+       struct v4l2_ctrl *vflip;
+       struct v4l2_ctrl *plfreq;
 
        __u32 last_pts;
        u16 last_fid;
@@ -89,181 +89,9 @@ enum sensors {
        NSENSORS
 };
 
-/* V4L2 controls supported by the driver */
-static void sethue(struct gspca_dev *gspca_dev);
-static void setsaturation(struct gspca_dev *gspca_dev);
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setagc(struct gspca_dev *gspca_dev);
-static void setawb(struct gspca_dev *gspca_dev);
-static void setaec(struct gspca_dev *gspca_dev);
-static void setsharpness(struct gspca_dev *gspca_dev);
-static void sethvflip(struct gspca_dev *gspca_dev);
-static void setlightfreq(struct gspca_dev *gspca_dev);
-
 static int sd_start(struct gspca_dev *gspca_dev);
 static void sd_stopN(struct gspca_dev *gspca_dev);
 
-static const struct ctrl sd_ctrls[] = {
-[HUE] = {
-               {
-                       .id      = V4L2_CID_HUE,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Hue",
-                       .minimum = -90,
-                       .maximum = 90,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = sethue
-       },
-[SATURATION] = {
-               {
-                       .id      = V4L2_CID_SATURATION,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Saturation",
-                       .minimum = 0,
-                       .maximum = 255,
-                       .step    = 1,
-                       .default_value = 64,
-               },
-               .set_control = setsaturation
-       },
-[BRIGHTNESS] = {
-               {
-                       .id      = V4L2_CID_BRIGHTNESS,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Brightness",
-                       .minimum = 0,
-                       .maximum = 255,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = setbrightness
-       },
-[CONTRAST] = {
-               {
-                       .id      = V4L2_CID_CONTRAST,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Contrast",
-                       .minimum = 0,
-                       .maximum = 255,
-                       .step    = 1,
-                       .default_value = 32,
-               },
-               .set_control = setcontrast
-       },
-[GAIN] = {
-               {
-                       .id      = V4L2_CID_GAIN,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Main Gain",
-                       .minimum = 0,
-                       .maximum = 63,
-                       .step    = 1,
-                       .default_value = 20,
-               },
-               .set_control = setgain
-       },
-[EXPOSURE] = {
-               {
-                       .id      = V4L2_CID_EXPOSURE,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Exposure",
-                       .minimum = 0,
-                       .maximum = 255,
-                       .step    = 1,
-                       .default_value = 120,
-               },
-               .set_control = setexposure
-       },
-[AGC] = {
-               {
-                       .id      = V4L2_CID_AUTOGAIN,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "Auto Gain",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 1,
-               },
-               .set_control = setagc
-       },
-[AWB] = {
-               {
-                       .id      = V4L2_CID_AUTO_WHITE_BALANCE,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "Auto White Balance",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 1,
-               },
-               .set_control = setawb
-       },
-[AEC] = {
-               {
-                       .id      = V4L2_CID_EXPOSURE_AUTO,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "Auto Exposure",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 1,
-               },
-               .set_control = setaec
-       },
-[SHARPNESS] = {
-               {
-                       .id      = V4L2_CID_SHARPNESS,
-                       .type    = V4L2_CTRL_TYPE_INTEGER,
-                       .name    = "Sharpness",
-                       .minimum = 0,
-                       .maximum = 63,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = setsharpness
-       },
-[HFLIP] = {
-               {
-                       .id      = V4L2_CID_HFLIP,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "HFlip",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = sethvflip
-       },
-[VFLIP] = {
-               {
-                       .id      = V4L2_CID_VFLIP,
-                       .type    = V4L2_CTRL_TYPE_BOOLEAN,
-                       .name    = "VFlip",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = sethvflip
-       },
-[LIGHTFREQ] = {
-               {
-                       .id      = V4L2_CID_POWER_LINE_FREQUENCY,
-                       .type    = V4L2_CTRL_TYPE_MENU,
-                       .name    = "Light Frequency Filter",
-                       .minimum = 0,
-                       .maximum = 1,
-                       .step    = 1,
-                       .default_value = 0,
-               },
-               .set_control = setlightfreq
-       },
-};
 
 static const struct v4l2_pix_format ov772x_mode[] = {
        {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
@@ -972,12 +800,10 @@ static void set_frame_rate(struct gspca_dev *gspca_dev)
        PDEBUG(D_PROBE, "frame_rate: %d", r->fps);
 }
 
-static void sethue(struct gspca_dev *gspca_dev)
+static void sethue(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int val;
 
-       val = sd->ctrls[HUE].val;
        if (sd->sensor == SENSOR_OV767x) {
                /* TBD */
        } else {
@@ -1014,12 +840,10 @@ static void sethue(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setsaturation(struct gspca_dev *gspca_dev)
+static void setsaturation(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int val;
 
-       val = sd->ctrls[SATURATION].val;
        if (sd->sensor == SENSOR_OV767x) {
                int i;
                static u8 color_tb[][6] = {
@@ -1040,12 +864,10 @@ static void setsaturation(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int val;
 
-       val = sd->ctrls[BRIGHTNESS].val;
        if (sd->sensor == SENSOR_OV767x) {
                if (val < 0)
                        val = 0x80 - val;
@@ -1055,27 +877,18 @@ static void setbrightness(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
 
-       val = sd->ctrls[CONTRAST].val;
        if (sd->sensor == SENSOR_OV767x)
                sccb_reg_write(gspca_dev, 0x56, val);   /* contras */
        else
                sccb_reg_write(gspca_dev, 0x9c, val);
 }
 
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
-
-       if (sd->ctrls[AGC].val)
-               return;
-
-       val = sd->ctrls[GAIN].val;
        switch (val & 0x30) {
        case 0x00:
                val &= 0x0f;
@@ -1097,15 +910,15 @@ static void setgain(struct gspca_dev *gspca_dev)
        sccb_reg_write(gspca_dev, 0x00, val);
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static s32 getgain(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
+       return sccb_reg_read(gspca_dev, 0x00);
+}
 
-       if (sd->ctrls[AEC].val)
-               return;
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
 
-       val = sd->ctrls[EXPOSURE].val;
        if (sd->sensor == SENSOR_OV767x) {
 
                /* set only aec[9:2] */
@@ -1123,11 +936,23 @@ static void setexposure(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setagc(struct gspca_dev *gspca_dev)
+static s32 getexposure(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       if (sd->ctrls[AGC].val) {
+       if (sd->sensor == SENSOR_OV767x) {
+               /* get only aec[9:2] */
+               return sccb_reg_read(gspca_dev, 0x10);  /* aech */
+       } else {
+               u8 hi = sccb_reg_read(gspca_dev, 0x08);
+               u8 lo = sccb_reg_read(gspca_dev, 0x10);
+               return (hi << 8 | lo) >> 1;
+       }
+}
+
+static void setagc(struct gspca_dev *gspca_dev, s32 val)
+{
+       if (val) {
                sccb_reg_write(gspca_dev, 0x13,
                                sccb_reg_read(gspca_dev, 0x13) | 0x04);
                sccb_reg_write(gspca_dev, 0x64,
@@ -1137,16 +962,14 @@ static void setagc(struct gspca_dev *gspca_dev)
                                sccb_reg_read(gspca_dev, 0x13) & ~0x04);
                sccb_reg_write(gspca_dev, 0x64,
                                sccb_reg_read(gspca_dev, 0x64) & ~0x03);
-
-               setgain(gspca_dev);
        }
 }
 
-static void setawb(struct gspca_dev *gspca_dev)
+static void setawb(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       if (sd->ctrls[AWB].val) {
+       if (val) {
                sccb_reg_write(gspca_dev, 0x13,
                                sccb_reg_read(gspca_dev, 0x13) | 0x02);
                if (sd->sensor == SENSOR_OV772x)
@@ -1161,7 +984,7 @@ static void setawb(struct gspca_dev *gspca_dev)
        }
 }
 
-static void setaec(struct gspca_dev *gspca_dev)
+static void setaec(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 data;
@@ -1169,31 +992,25 @@ static void setaec(struct gspca_dev *gspca_dev)
        data = sd->sensor == SENSOR_OV767x ?
                        0x05 :          /* agc + aec */
                        0x01;           /* agc */
-       if (sd->ctrls[AEC].val)
+       switch (val) {
+       case V4L2_EXPOSURE_AUTO:
                sccb_reg_write(gspca_dev, 0x13,
                                sccb_reg_read(gspca_dev, 0x13) | data);
-       else {
+               break;
+       case V4L2_EXPOSURE_MANUAL:
                sccb_reg_write(gspca_dev, 0x13,
                                sccb_reg_read(gspca_dev, 0x13) & ~data);
-               if (sd->sensor == SENSOR_OV767x)
-                       sd->ctrls[EXPOSURE].val =
-                               sccb_reg_read(gspca_dev, 10);   /* aech */
-               else
-                       setexposure(gspca_dev);
+               break;
        }
 }
 
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
-
-       val = sd->ctrls[SHARPNESS].val;
        sccb_reg_write(gspca_dev, 0x91, val);   /* Auto de-noise threshold */
        sccb_reg_write(gspca_dev, 0x8e, val);   /* De-noise threshold */
 }
 
-static void sethvflip(struct gspca_dev *gspca_dev)
+static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 val;
@@ -1201,28 +1018,27 @@ static void sethvflip(struct gspca_dev *gspca_dev)
        if (sd->sensor == SENSOR_OV767x) {
                val = sccb_reg_read(gspca_dev, 0x1e);   /* mvfp */
                val &= ~0x30;
-               if (sd->ctrls[HFLIP].val)
+               if (hflip)
                        val |= 0x20;
-               if (sd->ctrls[VFLIP].val)
+               if (vflip)
                        val |= 0x10;
                sccb_reg_write(gspca_dev, 0x1e, val);
        } else {
                val = sccb_reg_read(gspca_dev, 0x0c);
                val &= ~0xc0;
-               if (sd->ctrls[HFLIP].val == 0)
+               if (hflip == 0)
                        val |= 0x40;
-               if (sd->ctrls[VFLIP].val == 0)
+               if (vflip == 0)
                        val |= 0x80;
                sccb_reg_write(gspca_dev, 0x0c, val);
        }
 }
 
-static void setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u8 val;
 
-       val = sd->ctrls[LIGHTFREQ].val ? 0x9e : 0x00;
+       val = val ? 0x9e : 0x00;
        if (sd->sensor == SENSOR_OV767x) {
                sccb_reg_write(gspca_dev, 0x2a, 0x00);
                if (val)
@@ -1241,8 +1057,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
 
        cam = &gspca_dev->cam;
 
-       cam->ctrls = sd->ctrls;
-
        cam->cam_mode = ov772x_mode;
        cam->nmodes = ARRAY_SIZE(ov772x_mode);
 
@@ -1251,6 +1065,195 @@ static int sd_config(struct gspca_dev *gspca_dev,
        return 0;
 }
 
+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;
+}
+
 /* this function is called at probe and resume time */
 static int sd_init(struct gspca_dev *gspca_dev)
 {
@@ -1286,24 +1289,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
 
        if ((sensor_id & 0xfff0) == 0x7670) {
                sd->sensor = SENSOR_OV767x;
-               gspca_dev->ctrl_dis = (1 << HUE) |
-                                       (1 << GAIN) |
-                                       (1 << AGC) |
-                                       (1 << SHARPNESS);       /* auto */
-               sd->ctrls[SATURATION].min = 0,
-               sd->ctrls[SATURATION].max = 6,
-               sd->ctrls[SATURATION].def = 3,
-               sd->ctrls[BRIGHTNESS].min = -127;
-               sd->ctrls[BRIGHTNESS].max = 127;
-               sd->ctrls[BRIGHTNESS].def = 0;
-               sd->ctrls[CONTRAST].max = 0x80;
-               sd->ctrls[CONTRAST].def = 0x40;
-               sd->ctrls[EXPOSURE].min = 0x08;
-               sd->ctrls[EXPOSURE].max = 0x60;
-               sd->ctrls[EXPOSURE].def = 0x13;
-               sd->ctrls[SHARPNESS].max = 9;
-               sd->ctrls[SHARPNESS].def = 4;
-               sd->ctrls[HFLIP].def = 1;
                gspca_dev->cam.cam_mode = ov767x_mode;
                gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode);
        } else {
@@ -1366,22 +1351,23 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        set_frame_rate(gspca_dev);
 
-       if (!(gspca_dev->ctrl_dis & (1 << HUE)))
-               sethue(gspca_dev);
-       setsaturation(gspca_dev);
-       if (!(gspca_dev->ctrl_dis & (1 << AGC)))
-               setagc(gspca_dev);
-       setawb(gspca_dev);
-       setaec(gspca_dev);
-       if (!(gspca_dev->ctrl_dis & (1 << GAIN)))
-               setgain(gspca_dev);
-       setexposure(gspca_dev);
-       setbrightness(gspca_dev);
-       setcontrast(gspca_dev);
-       if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS)))
-               setsharpness(gspca_dev);
-       sethvflip(gspca_dev);
-       setlightfreq(gspca_dev);
+       if (sd->hue)
+               sethue(gspca_dev, v4l2_ctrl_g_ctrl(sd->hue));
+       setsaturation(gspca_dev, v4l2_ctrl_g_ctrl(sd->saturation));
+       if (sd->autogain)
+               setagc(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
+       setawb(gspca_dev, v4l2_ctrl_g_ctrl(sd->autowhitebalance));
+       setaec(gspca_dev, v4l2_ctrl_g_ctrl(sd->autoexposure));
+       if (sd->gain)
+               setgain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
+       setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
+       setbrightness(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness));
+       setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast));
+       if (sd->sharpness)
+               setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
+       sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip),
+                 v4l2_ctrl_g_ctrl(sd->vflip));
+       setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq));
 
        ov534_set_led(gspca_dev, 1);
        ov534_reg_write(gspca_dev, 0xe0, 0x00);
@@ -1483,25 +1469,6 @@ scan_next:
        } while (remaining_len > 0);
 }
 
-static int sd_querymenu(struct gspca_dev *gspca_dev,
-               struct v4l2_querymenu *menu)
-{
-       switch (menu->id) {
-       case V4L2_CID_POWER_LINE_FREQUENCY:
-               switch (menu->index) {
-               case 0:         /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
-                       strcpy((char *) menu->name, "Disabled");
-                       return 0;
-               case 1:         /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
-                       strcpy((char *) menu->name, "50 Hz");
-                       return 0;
-               }
-               break;
-       }
-
-       return -EINVAL;
-}
-
 /* get stream parameters (framerate) */
 static void sd_get_streamparm(struct gspca_dev *gspca_dev,
                             struct v4l2_streamparm *parm)
@@ -1536,14 +1503,12 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
 /* sub-driver description */
 static const struct sd_desc sd_desc = {
        .name     = MODULE_NAME,
-       .ctrls    = sd_ctrls,
-       .nctrls   = ARRAY_SIZE(sd_ctrls),
        .config   = sd_config,
        .init     = sd_init,
+       .init_controls = sd_init_controls,
        .start    = sd_start,
        .stopN    = sd_stopN,
        .pkt_scan = sd_pkt_scan,
-       .querymenu = sd_querymenu,
        .get_streamparm = sd_get_streamparm,
        .set_streamparm = sd_set_streamparm,
 };