[media] gspca-mr97310a: convert to the control framework
authorHans Verkuil <hans.verkuil@cisco.com>
Mon, 14 May 2012 12:45:06 +0000 (09:45 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 30 Jul 2012 21:23:18 +0000 (18:23 -0300)
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/gspca/mr97310a.c

index d73e5bd3dbf7e52095e723fea53dac63f8ff6dde..3ede41b674682f6a9afb1dbffcb4c5f9f95ea635 100644 (file)
@@ -67,6 +67,7 @@
 #define MR97310A_CS_GAIN_MAX           0x7ff
 #define MR97310A_CS_GAIN_DEFAULT       0x110
 
+#define MR97310A_CID_CLOCKDIV (V4L2_CTRL_CLASS_USER + 0x1000)
 #define MR97310A_MIN_CLOCKDIV_MIN      3
 #define MR97310A_MIN_CLOCKDIV_MAX      8
 #define MR97310A_MIN_CLOCKDIV_DEFAULT  3
@@ -84,17 +85,15 @@ MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)");
 /* specific webcam descriptor */
 struct sd {
        struct gspca_dev gspca_dev;  /* !! must be the first item */
+       struct { /* exposure/min_clockdiv control cluster */
+               struct v4l2_ctrl *exposure;
+               struct v4l2_ctrl *min_clockdiv;
+       };
        u8 sof_read;
        u8 cam_type;    /* 0 is CIF and 1 is VGA */
        u8 sensor_type; /* We use 0 and 1 here, too. */
        u8 do_lcd_stop;
        u8 adj_colors;
-
-       int brightness;
-       u16 exposure;
-       u32 gain;
-       u8 contrast;
-       u8 min_clockdiv;
 };
 
 struct sensor_w_data {
@@ -105,132 +104,6 @@ struct sensor_w_data {
 };
 
 static void sd_stopN(struct gspca_dev *gspca_dev);
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val);
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static void setgain(struct gspca_dev *gspca_dev);
-static void setcontrast(struct gspca_dev *gspca_dev);
-
-/* V4L2 controls supported by the driver */
-static const struct ctrl sd_ctrls[] = {
-/* Separate brightness control description for Argus QuickClix as it has
- * different limits from the other mr97310a cameras, and separate gain
- * control for Sakar CyberPix camera. */
-       {
-#define NORM_BRIGHTNESS_IDX 0
-               {
-                       .id = V4L2_CID_BRIGHTNESS,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Brightness",
-                       .minimum = -254,
-                       .maximum = 255,
-                       .step = 1,
-                       .default_value = MR97310A_BRIGHTNESS_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setbrightness,
-               .get = sd_getbrightness,
-       },
-       {
-#define ARGUS_QC_BRIGHTNESS_IDX 1
-               {
-                       .id = V4L2_CID_BRIGHTNESS,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Brightness",
-                       .minimum = 0,
-                       .maximum = 15,
-                       .step = 1,
-                       .default_value = MR97310A_BRIGHTNESS_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setbrightness,
-               .get = sd_getbrightness,
-       },
-       {
-#define EXPOSURE_IDX 2
-               {
-                       .id = V4L2_CID_EXPOSURE,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Exposure",
-                       .minimum = MR97310A_EXPOSURE_MIN,
-                       .maximum = MR97310A_EXPOSURE_MAX,
-                       .step = 1,
-                       .default_value = MR97310A_EXPOSURE_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setexposure,
-               .get = sd_getexposure,
-       },
-       {
-#define GAIN_IDX 3
-               {
-                       .id = V4L2_CID_GAIN,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Gain",
-                       .minimum = MR97310A_GAIN_MIN,
-                       .maximum = MR97310A_GAIN_MAX,
-                       .step = 1,
-                       .default_value = MR97310A_GAIN_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setgain,
-               .get = sd_getgain,
-       },
-       {
-#define SAKAR_CS_GAIN_IDX 4
-               {
-                       .id = V4L2_CID_GAIN,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Gain",
-                       .minimum = MR97310A_CS_GAIN_MIN,
-                       .maximum = MR97310A_CS_GAIN_MAX,
-                       .step = 1,
-                       .default_value = MR97310A_CS_GAIN_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setgain,
-               .get = sd_getgain,
-       },
-       {
-#define CONTRAST_IDX 5
-               {
-                       .id = V4L2_CID_CONTRAST,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Contrast",
-                       .minimum = MR97310A_CONTRAST_MIN,
-                       .maximum = MR97310A_CONTRAST_MAX,
-                       .step = 1,
-                       .default_value = MR97310A_CONTRAST_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setcontrast,
-               .get = sd_getcontrast,
-       },
-       {
-#define MIN_CLOCKDIV_IDX 6
-               {
-                       .id = V4L2_CID_PRIVATE_BASE,
-                       .type = V4L2_CTRL_TYPE_INTEGER,
-                       .name = "Minimum Clock Divider",
-                       .minimum = MR97310A_MIN_CLOCKDIV_MIN,
-                       .maximum = MR97310A_MIN_CLOCKDIV_MAX,
-                       .step = 1,
-                       .default_value = MR97310A_MIN_CLOCKDIV_DEFAULT,
-                       .flags = 0,
-               },
-               .set = sd_setmin_clockdiv,
-               .get = sd_getmin_clockdiv,
-       },
-};
 
 static const struct v4l2_pix_format vga_mode[] = {
        {160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
@@ -481,7 +354,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
 {
        struct sd *sd = (struct sd *) gspca_dev;
        struct cam *cam;
-       int gain_default = MR97310A_GAIN_DEFAULT;
        int err_code;
 
        cam = &gspca_dev->cam;
@@ -615,52 +487,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
                       sd->sensor_type);
        }
 
-       /* Setup controls depending on camera type */
-       if (sd->cam_type == CAM_TYPE_CIF) {
-               /* No brightness for sensor_type 0 */
-               if (sd->sensor_type == 0)
-                       gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
-                                             (1 << ARGUS_QC_BRIGHTNESS_IDX) |
-                                             (1 << CONTRAST_IDX) |
-                                             (1 << SAKAR_CS_GAIN_IDX);
-               else
-                       gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
-                                             (1 << CONTRAST_IDX) |
-                                             (1 << SAKAR_CS_GAIN_IDX) |
-                                             (1 << MIN_CLOCKDIV_IDX);
-       } else {
-               /* All controls need to be disabled if VGA sensor_type is 0 */
-               if (sd->sensor_type == 0)
-                       gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
-                                             (1 << ARGUS_QC_BRIGHTNESS_IDX) |
-                                             (1 << EXPOSURE_IDX) |
-                                             (1 << GAIN_IDX) |
-                                             (1 << CONTRAST_IDX) |
-                                             (1 << SAKAR_CS_GAIN_IDX) |
-                                             (1 << MIN_CLOCKDIV_IDX);
-               else if (sd->sensor_type == 2) {
-                       gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
-                                             (1 << ARGUS_QC_BRIGHTNESS_IDX) |
-                                             (1 << GAIN_IDX) |
-                                             (1 << MIN_CLOCKDIV_IDX);
-                       gain_default = MR97310A_CS_GAIN_DEFAULT;
-               } else if (sd->do_lcd_stop)
-                       /* Argus QuickClix has different brightness limits */
-                       gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
-                                             (1 << CONTRAST_IDX) |
-                                             (1 << SAKAR_CS_GAIN_IDX);
-               else
-                       gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
-                                             (1 << CONTRAST_IDX) |
-                                             (1 << SAKAR_CS_GAIN_IDX);
-       }
-
-       sd->brightness = MR97310A_BRIGHTNESS_DEFAULT;
-       sd->exposure = MR97310A_EXPOSURE_DEFAULT;
-       sd->gain = gain_default;
-       sd->contrast = MR97310A_CONTRAST_DEFAULT;
-       sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT;
-
        return 0;
 }
 
@@ -952,11 +778,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
        if (err_code < 0)
                return err_code;
 
-       setbrightness(gspca_dev);
-       setcontrast(gspca_dev);
-       setexposure(gspca_dev);
-       setgain(gspca_dev);
-
        return isoc_enable(gspca_dev);
 }
 
@@ -971,37 +792,25 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
                lcd_stop(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;
-       u8 val;
        u8 sign_reg = 7;  /* This reg and the next one used on CIF cams. */
        u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
        static const u8 quick_clix_table[] =
        /*        0  1  2   3  4  5  6  7  8  9  10  11  12  13  14  15 */
                { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9,  7, 10, 13, 11, 14, 15};
-       /*
-        * This control is disabled for CIF type 1 and VGA type 0 cameras.
-        * It does not quite act linearly for the Argus QuickClix camera,
-        * but it does control brightness. The values are 0 - 15 only, and
-        * the table above makes them act consecutively.
-        */
-       if ((gspca_dev->ctrl_dis & (1 << NORM_BRIGHTNESS_IDX)) &&
-           (gspca_dev->ctrl_dis & (1 << ARGUS_QC_BRIGHTNESS_IDX)))
-               return;
-
        if (sd->cam_type == CAM_TYPE_VGA) {
                sign_reg += 4;
                value_reg += 4;
        }
 
        /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
-       if (sd->brightness > 0) {
+       if (val > 0) {
                sensor_write1(gspca_dev, sign_reg, 0x00);
-               val = sd->brightness;
        } else {
                sensor_write1(gspca_dev, sign_reg, 0x01);
-               val = (257 - sd->brightness);
+               val = 257 - val;
        }
        /* Use lookup table for funky Argus QuickClix brightness */
        if (sd->do_lcd_stop)
@@ -1010,23 +819,20 @@ static void setbrightness(struct gspca_dev *gspca_dev)
        sensor_write1(gspca_dev, value_reg, val);
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 min_clockdiv)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        int exposure = MR97310A_EXPOSURE_DEFAULT;
        u8 buf[2];
 
-       if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
-               return;
-
        if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
                /* This cam does not like exposure settings < 300,
                   so scale 0 - 4095 to 300 - 4095 */
-               exposure = (sd->exposure * 9267) / 10000 + 300;
+               exposure = (expo * 9267) / 10000 + 300;
                sensor_write1(gspca_dev, 3, exposure >> 4);
                sensor_write1(gspca_dev, 4, exposure & 0x0f);
        } else if (sd->sensor_type == 2) {
-               exposure = sd->exposure;
+               exposure = expo;
                exposure >>= 3;
                sensor_write1(gspca_dev, 3, exposure >> 8);
                sensor_write1(gspca_dev, 4, exposure & 0xff);
@@ -1038,11 +844,11 @@ static void setexposure(struct gspca_dev *gspca_dev)
 
                   Note our 0 - 4095 exposure is mapped to 0 - 511
                   milliseconds exposure time */
-               u8 clockdiv = (60 * sd->exposure + 7999) / 8000;
+               u8 clockdiv = (60 * expo + 7999) / 8000;
 
                /* Limit framerate to not exceed usb bandwidth */
-               if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320)
-                       clockdiv = sd->min_clockdiv;
+               if (clockdiv < min_clockdiv && gspca_dev->width >= 320)
+                       clockdiv = min_clockdiv;
                else if (clockdiv < 2)
                        clockdiv = 2;
 
@@ -1051,7 +857,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
 
                /* Frame exposure time in ms = 1000 * clockdiv / 60 ->
                exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
-               exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv);
+               exposure = (60 * 511 * expo) / (8000 * clockdiv);
                if (exposure > 511)
                        exposure = 511;
 
@@ -1065,125 +871,148 @@ static void setexposure(struct gspca_dev *gspca_dev)
        }
 }
 
-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 gainreg;
 
-       if ((gspca_dev->ctrl_dis & (1 << GAIN_IDX)) &&
-           (gspca_dev->ctrl_dis & (1 << SAKAR_CS_GAIN_IDX)))
-               return;
-
        if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1)
-               sensor_write1(gspca_dev, 0x0e, sd->gain);
+               sensor_write1(gspca_dev, 0x0e, val);
        else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2)
                for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) {
-                       sensor_write1(gspca_dev, gainreg, sd->gain >> 8);
-                       sensor_write1(gspca_dev, gainreg + 1, sd->gain & 0xff);
+                       sensor_write1(gspca_dev, gainreg, val >> 8);
+                       sensor_write1(gspca_dev, gainreg + 1, val & 0xff);
                }
        else
-               sensor_write1(gspca_dev, 0x10, sd->gain);
-}
-
-static void setcontrast(struct gspca_dev *gspca_dev)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
-               return;
-
-       sensor_write1(gspca_dev, 0x1c, sd->contrast);
-}
-
-
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->brightness = val;
-       if (gspca_dev->streaming)
-               setbrightness(gspca_dev);
-       return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->brightness;
-       return 0;
-}
-
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->exposure = val;
-       if (gspca_dev->streaming)
-               setexposure(gspca_dev);
-       return 0;
+               sensor_write1(gspca_dev, 0x10, val);
 }
 
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->exposure;
-       return 0;
+       sensor_write1(gspca_dev, 0x1c, val);
 }
 
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
 
-       sd->gain = val;
-       if (gspca_dev->streaming)
-               setgain(gspca_dev);
-       return 0;
-}
+       gspca_dev->usb_err = 0;
 
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->gain;
-       return 0;
-}
+       if (!gspca_dev->streaming)
+               return 0;
 
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->contrast = val;
-       if (gspca_dev->streaming)
-               setcontrast(gspca_dev);
-       return 0;
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               setbrightness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_CONTRAST:
+               setcontrast(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_EXPOSURE:
+               setexposure(gspca_dev, sd->exposure->val,
+                           sd->min_clockdiv ? sd->min_clockdiv->val : 0);
+               break;
+       case V4L2_CID_GAIN:
+               setgain(gspca_dev, ctrl->val);
+               break;
+       }
+       return gspca_dev->usb_err;
 }
 
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+       .s_ctrl = sd_s_ctrl,
+};
 
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       *val = sd->contrast;
-       return 0;
-}
-
-static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_init_controls(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+       static const struct v4l2_ctrl_config clockdiv = {
+               .ops = &sd_ctrl_ops,
+               .id = MR97310A_CID_CLOCKDIV,
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .name = "Minimum Clock Divider",
+               .min = MR97310A_MIN_CLOCKDIV_MIN,
+               .max = MR97310A_MIN_CLOCKDIV_MAX,
+               .step = 1,
+               .def = MR97310A_MIN_CLOCKDIV_DEFAULT,
+       };
+       bool has_brightness = false;
+       bool has_argus_brightness = false;
+       bool has_contrast = false;
+       bool has_gain = false;
+       bool has_cs_gain = false;
+       bool has_exposure = false;
+       bool has_clockdiv = false;
 
-       sd->min_clockdiv = val;
-       if (gspca_dev->streaming)
-               setexposure(gspca_dev);
-       return 0;
-}
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 4);
 
-static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
+       /* Setup controls depending on camera type */
+       if (sd->cam_type == CAM_TYPE_CIF) {
+               /* No brightness for sensor_type 0 */
+               if (sd->sensor_type == 0)
+                       has_exposure = has_gain = has_clockdiv = true;
+               else
+                       has_exposure = has_gain = has_brightness = true;
+       } else {
+               /* All controls need to be disabled if VGA sensor_type is 0 */
+               if (sd->sensor_type == 0)
+                       ; /* no controls! */
+               else if (sd->sensor_type == 2)
+                       has_exposure = has_cs_gain = has_contrast = true;
+               else if (sd->do_lcd_stop)
+                       has_exposure = has_gain = has_argus_brightness =
+                               has_clockdiv = true;
+               else
+                       has_exposure = has_gain = has_brightness =
+                               has_clockdiv = true;
+       }
 
-       *val = sd->min_clockdiv;
+       /* Separate brightness control description for Argus QuickClix as it has
+        * different limits from the other mr97310a cameras, and separate gain
+        * control for Sakar CyberPix camera. */
+       /*
+        * This control is disabled for CIF type 1 and VGA type 0 cameras.
+        * It does not quite act linearly for the Argus QuickClix camera,
+        * but it does control brightness. The values are 0 - 15 only, and
+        * the table above makes them act consecutively.
+        */
+       if (has_brightness)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, -254, 255, 1,
+                       MR97310A_BRIGHTNESS_DEFAULT);
+       else if (has_argus_brightness)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 15, 1,
+                       MR97310A_BRIGHTNESS_DEFAULT);
+       if (has_contrast)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_CONTRAST, MR97310A_CONTRAST_MIN,
+                       MR97310A_CONTRAST_MAX, 1, MR97310A_CONTRAST_DEFAULT);
+       if (has_gain)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, MR97310A_GAIN_MIN, MR97310A_GAIN_MAX,
+                       1, MR97310A_GAIN_DEFAULT);
+       else if (has_cs_gain)
+               v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN,
+                       MR97310A_CS_GAIN_MIN, MR97310A_CS_GAIN_MAX,
+                       1, MR97310A_CS_GAIN_DEFAULT);
+       if (has_exposure)
+               sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, MR97310A_EXPOSURE_MIN,
+                       MR97310A_EXPOSURE_MAX, 1, MR97310A_EXPOSURE_DEFAULT);
+       if (has_clockdiv)
+               sd->min_clockdiv = v4l2_ctrl_new_custom(hdl, &clockdiv, NULL);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       if (has_exposure && has_clockdiv)
+               v4l2_ctrl_cluster(2, &sd->exposure);
        return 0;
 }
 
@@ -1221,10 +1050,9 @@ static void sd_pkt_scan(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,