]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
[media] gspca-topro: convert to the control framework
authorHans Verkuil <hans.verkuil@cisco.com>
Mon, 21 May 2012 08:37:39 +0000 (05:37 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 30 Jul 2012 21:36:01 +0000 (18:36 -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/topro.c

index c6326d177a3df6952a0cfb30db042638963f496c..a6055246cb9d720c13dbd880c167a6aec94ba79c 100644 (file)
@@ -120,24 +120,13 @@ static const u8 jpeg_head[] = {
 #define JPEG_HDR_SZ 521
 };
 
-enum e_ctrl {
-       EXPOSURE,
-       QUALITY,
-       SHARPNESS,
-       RGAIN,
-       GAIN,
-       BGAIN,
-       GAMMA,
-       AUTOGAIN,
-       NCTRLS          /* number of controls */
-};
-
-#define AUTOGAIN_DEF 1
-
 struct sd {
        struct gspca_dev gspca_dev;     /* !! must be the first item */
-
-       struct gspca_ctrl ctrls[NCTRLS];
+       struct v4l2_ctrl *jpegqual;
+       struct v4l2_ctrl *sharpness;
+       struct v4l2_ctrl *gamma;
+       struct v4l2_ctrl *blue;
+       struct v4l2_ctrl *red;
 
        u8 framerate;
        u8 quality;             /* webcam current JPEG quality (0..16) */
@@ -1415,32 +1404,33 @@ static void soi763a_6810_init(struct gspca_dev *gspca_dev)
 }
 
 /* set the gain and exposure */
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 expo, s32 gain,
+                                                       s32 blue, s32 red)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
        if (sd->sensor == SENSOR_CX0342) {
-               int expo;
-
-               expo = (sd->ctrls[EXPOSURE].val << 2) - 1;
+               expo = (expo << 2) - 1;
                i2c_w(gspca_dev, CX0342_EXPO_LINE_L, expo);
                i2c_w(gspca_dev, CX0342_EXPO_LINE_H, expo >> 8);
                if (sd->bridge == BRIDGE_TP6800)
                        i2c_w(gspca_dev, CX0342_RAW_GBGAIN_H,
-                                               sd->ctrls[GAIN].val >> 8);
-               i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, sd->ctrls[GAIN].val);
+                                               gain >> 8);
+               i2c_w(gspca_dev, CX0342_RAW_GBGAIN_L, gain);
                if (sd->bridge == BRIDGE_TP6800)
                        i2c_w(gspca_dev, CX0342_RAW_GRGAIN_H,
-                                               sd->ctrls[GAIN].val >> 8);
-               i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, sd->ctrls[GAIN].val);
-               if (sd->bridge == BRIDGE_TP6800)
-                       i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
-                                               sd->ctrls[BGAIN].val >> 8);
-               i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, sd->ctrls[BGAIN].val);
-               if (sd->bridge == BRIDGE_TP6800)
-                       i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
-                                               sd->ctrls[RGAIN].val >> 8);
-               i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, sd->ctrls[RGAIN].val);
+                                       gain >> 8);
+               i2c_w(gspca_dev, CX0342_RAW_GRGAIN_L, gain);
+               if (sd->sensor == SENSOR_CX0342) {
+                       if (sd->bridge == BRIDGE_TP6800)
+                               i2c_w(gspca_dev, CX0342_RAW_BGAIN_H,
+                                               blue >> 8);
+                       i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, blue);
+                       if (sd->bridge == BRIDGE_TP6800)
+                               i2c_w(gspca_dev, CX0342_RAW_RGAIN_H,
+                                               red >> 8);
+                       i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, red);
+               }
                i2c_w(gspca_dev, CX0342_SYS_CTRL_0,
                                sd->bridge == BRIDGE_TP6800 ? 0x80 : 0x81);
                return;
@@ -1448,10 +1438,10 @@ static void setexposure(struct gspca_dev *gspca_dev)
 
        /* soi763a */
        i2c_w(gspca_dev, 0x10,          /* AEC_H (exposure time) */
-                        sd->ctrls[EXPOSURE].val);
+                        expo);
 /*     i2c_w(gspca_dev, 0x76, 0x02);    * AEC_L ([1:0] */
        i2c_w(gspca_dev, 0x00,          /* gain */
-                        sd->ctrls[GAIN].val);
+                        gain);
 }
 
 /* set the JPEG quantization tables */
@@ -1472,12 +1462,10 @@ static void set_dqt(struct gspca_dev *gspca_dev, u8 q)
 }
 
 /* set the JPEG compression quality factor */
-static void setquality(struct gspca_dev *gspca_dev)
+static void setquality(struct gspca_dev *gspca_dev, s32 q)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       u16 q;
 
-       q = sd->ctrls[QUALITY].val;
        if (q != 16)
                q = 15 - q;
 
@@ -1508,10 +1496,9 @@ static const u8 color_gain[NSENSORS][18] = {
         0xd5, 0x00, 0x46, 0x03, 0xdc, 0x03},   /* V R/G/B */
 };
 
-static void setgamma(struct gspca_dev *gspca_dev)
+static void setgamma(struct gspca_dev *gspca_dev, s32 gamma)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int gamma;
 #define NGAMMA 6
        static const u8 gamma_tb[NGAMMA][3][1024] = {
            {                           /* gamma 0 - from tp6800 + soi763a */
@@ -3836,7 +3823,6 @@ static void setgamma(struct gspca_dev *gspca_dev)
        if (sd->bridge == BRIDGE_TP6810)
                reg_w(gspca_dev, 0x02, 0x28);
 /*     msleep(50); */
-       gamma = sd->ctrls[GAMMA].val;
        bulk_w(gspca_dev, 0x00, gamma_tb[gamma][0], 1024);
        bulk_w(gspca_dev, 0x01, gamma_tb[gamma][1], 1024);
        bulk_w(gspca_dev, 0x02, gamma_tb[gamma][2], 1024);
@@ -3864,43 +3850,35 @@ static void setgamma(struct gspca_dev *gspca_dev)
 /*     msleep(50); */
 }
 
-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;
 
        if (sd->bridge == BRIDGE_TP6800) {
-               val = sd->ctrls[SHARPNESS].val
-                               | 0x08;         /* grid compensation enable */
+               val |= 0x08;            /* grid compensation enable */
                if (gspca_dev->width == 640)
                        reg_w(gspca_dev, TP6800_R78_FORMAT, 0x00); /* vga */
                else
                        val |= 0x04;            /* scaling down enable */
                reg_w(gspca_dev, TP6800_R5D_DEMOSAIC_CFG, val);
        } else {
-               val = (sd->ctrls[SHARPNESS].val << 5) | 0x08;
+               val = (val << 5) | 0x08;
                reg_w(gspca_dev, 0x59, val);
        }
 }
 
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       if (gspca_dev->ctrl_dis & (1 << AUTOGAIN))
-               return;
-       if (sd->ctrls[AUTOGAIN].val) {
-               sd->ag_cnt = AG_CNT_START;
-               gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
-       } else {
-               sd->ag_cnt = -1;
-               gspca_dev->ctrl_inac &= ~((1 << EXPOSURE) | (1 << GAIN));
-       }
+       sd->ag_cnt = val ? AG_CNT_START : -1;
 }
 
 /* set the resolution for sensor cx0342 */
 static void set_resolution(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
+
        reg_w(gspca_dev, TP6800_R21_ENDP_1_CTL, 0x00);
        if (gspca_dev->width == 320) {
                reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x06);
@@ -3926,8 +3904,9 @@ static void set_resolution(struct gspca_dev *gspca_dev)
        i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
        bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
                                ARRAY_SIZE(color_gain[0]));
-       setgamma(gspca_dev);
-       setquality(gspca_dev);
+       setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+       if (sd->sensor == SENSOR_SOI763A)
+               setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
 }
 
 /* convert the frame rate to a tp68x0 value */
@@ -3963,7 +3942,7 @@ static int get_fr_idx(struct gspca_dev *gspca_dev)
        return i;
 }
 
-static void setframerate(struct gspca_dev *gspca_dev)
+static void setframerate(struct gspca_dev *gspca_dev, s32 val)
 {
        struct sd *sd = (struct sd *) gspca_dev;
        u8 fr_idx;
@@ -3974,7 +3953,7 @@ static void setframerate(struct gspca_dev *gspca_dev)
                reg_r(gspca_dev, 0x7b);
                reg_w(gspca_dev, 0x7b,
                        sd->sensor == SENSOR_CX0342 ? 0x10 : 0x90);
-               if (sd->ctrls[EXPOSURE].val >= 128)
+               if (val >= 128)
                        fr_idx = 0xf0;          /* lower frame rate */
        }
 
@@ -3984,43 +3963,43 @@ static void setframerate(struct gspca_dev *gspca_dev)
                i2c_w(gspca_dev, CX0342_AUTO_ADC_CALIB, 0x01);
 }
 
-static void setrgain(struct gspca_dev *gspca_dev)
+static void setrgain(struct gspca_dev *gspca_dev, s32 rgain)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       int rgain;
-
-       rgain = sd->ctrls[RGAIN].val;
        i2c_w(gspca_dev, CX0342_RAW_RGAIN_H, rgain >> 8);
        i2c_w(gspca_dev, CX0342_RAW_RGAIN_L, rgain);
        i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
 }
 
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_setgain(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
+       s32 val = gspca_dev->gain->val;
 
        if (sd->sensor == SENSOR_CX0342) {
-               sd->ctrls[BGAIN].val = sd->ctrls[BGAIN].val
-                                       * val / sd->ctrls[GAIN].val;
-               if (sd->ctrls[BGAIN].val > 4095)
-                       sd->ctrls[BGAIN].val = 4095;
-               sd->ctrls[RGAIN].val = sd->ctrls[RGAIN].val
-                                       * val / sd->ctrls[GAIN].val;
-               if (sd->ctrls[RGAIN].val > 4095)
-                       sd->ctrls[RGAIN].val = 4095;
+               s32 old = gspca_dev->gain->cur.val ?
+                                       gspca_dev->gain->cur.val : 1;
+
+               sd->blue->val = sd->blue->val * val / old;
+               if (sd->blue->val > 4095)
+                       sd->blue->val = 4095;
+               sd->red->val = sd->red->val * val / old;
+               if (sd->red->val > 4095)
+                       sd->red->val = 4095;
+       }
+       if (gspca_dev->streaming) {
+               if (sd->sensor == SENSOR_CX0342)
+                       setexposure(gspca_dev, gspca_dev->exposure->val,
+                                       gspca_dev->gain->val,
+                                       sd->blue->val, sd->red->val);
+               else
+                       setexposure(gspca_dev, gspca_dev->exposure->val,
+                                       gspca_dev->gain->val, 0, 0);
        }
-       sd->ctrls[GAIN].val = val;
-       if (gspca_dev->streaming)
-               setexposure(gspca_dev);
        return gspca_dev->usb_err;
 }
 
-static void setbgain(struct gspca_dev *gspca_dev)
+static void setbgain(struct gspca_dev *gspca_dev, s32 bgain)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       int bgain;
-
-       bgain = sd->ctrls[BGAIN].val;
        i2c_w(gspca_dev, CX0342_RAW_BGAIN_H, bgain >> 8);
        i2c_w(gspca_dev, CX0342_RAW_BGAIN_L, bgain);
        i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x80);
@@ -4040,7 +4019,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
                        framerates : framerates_6810;
 
        sd->framerate = 30;             /* default: 30 fps */
-       gspca_dev->cam.ctrls = sd->ctrls;
        return 0;
 }
 
@@ -4108,32 +4086,16 @@ static int sd_init(struct gspca_dev *gspca_dev)
        }
        if (sd->sensor == SENSOR_SOI763A) {
                pr_info("Sensor soi763a\n");
-               sd->ctrls[GAMMA].def = sd->bridge == BRIDGE_TP6800 ? 0 : 1;
-               sd->ctrls[GAIN].max = 15;
-               sd->ctrls[GAIN].def = 3;
-               gspca_dev->ctrl_dis = (1 << RGAIN) | (1 << BGAIN);
                if (sd->bridge == BRIDGE_TP6810) {
                        soi763a_6810_init(gspca_dev);
-#if AUTOGAIN_DEF
-                       gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
-#endif
-               } else {
-                       gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
                }
        } else {
                pr_info("Sensor cx0342\n");
                if (sd->bridge == BRIDGE_TP6810) {
                        cx0342_6810_init(gspca_dev);
-#if AUTOGAIN_DEF
-                       gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
-#endif
-               } else {
-                       gspca_dev->ctrl_dis |= (1 << AUTOGAIN);
                }
        }
 
-       if (sd->bridge == BRIDGE_TP6810)
-               sd->ctrls[QUALITY].def = 0;     /* auto quality */
        set_dqt(gspca_dev, 0);
        return 0;
 }
@@ -4207,8 +4169,9 @@ static void set_led(struct gspca_dev *gspca_dev, int on)
 
 static void cx0342_6800_start(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
        static const struct cmd reg_init[] = {
-/*fixme: is this usefull?*/
+               /* fixme: is this useful? */
                {TP6800_R17_GPIO_IO, 0x9f},
                {TP6800_R16_GPIO_PD, 0x40},
                {TP6800_R10_SIF_TYPE, 0x00},    /* i2c 8 bits */
@@ -4279,13 +4242,21 @@ static void cx0342_6800_start(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
        i2c_w(gspca_dev, CX0342_EXPO_LINE_H, 0x00);
        i2c_w(gspca_dev, CX0342_SYS_CTRL_0, 0x01);
-       setexposure(gspca_dev);
+       if (sd->sensor == SENSOR_CX0342)
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain),
+                       v4l2_ctrl_g_ctrl(sd->blue),
+                       v4l2_ctrl_g_ctrl(sd->red));
+       else
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
        set_led(gspca_dev, 1);
        set_resolution(gspca_dev);
 }
 
 static void cx0342_6810_start(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
        static const struct cmd sensor_init_2[] = {
                {CX0342_EXPO_LINE_L, 0x6f},
                {CX0342_EXPO_LINE_H, 0x02},
@@ -4366,10 +4337,10 @@ static void cx0342_6810_start(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, 0x07, 0x85);
                reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01);      /* qvga */
        }
-       setgamma(gspca_dev);
+       setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
        reg_w_buf(gspca_dev, tp6810_bridge_start,
                        ARRAY_SIZE(tp6810_bridge_start));
-       setsharpness(gspca_dev);
+       setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
        bulk_w(gspca_dev, 0x03, color_gain[SENSOR_CX0342],
                                ARRAY_SIZE(color_gain[0]));
        reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0x87);
@@ -4380,11 +4351,12 @@ static void cx0342_6810_start(struct gspca_dev *gspca_dev)
        i2c_w_buf(gspca_dev, sensor_init_5, ARRAY_SIZE(sensor_init_5));
 
        set_led(gspca_dev, 1);
-/*     setquality(gspca_dev); */
+/*     setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); */
 }
 
 static void soi763a_6800_start(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
        static const struct cmd reg_init[] = {
                {TP6800_R79_QUALITY, 0x04},
                {TP6800_R79_QUALITY, 0x01},
@@ -4484,19 +4456,28 @@ static void soi763a_6800_start(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, TP6800_R5C_EDGE_THRLD, 0x10);
        reg_w(gspca_dev, TP6800_R54_DARK_CFG, 0x00);
 
-       setsharpness(gspca_dev);
+       setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
 
        bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
                                ARRAY_SIZE(color_gain[0]));
 
        set_led(gspca_dev, 1);
-       setexposure(gspca_dev);
-       setquality(gspca_dev);
-       setgamma(gspca_dev);
+       if (sd->sensor == SENSOR_CX0342)
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain),
+                       v4l2_ctrl_g_ctrl(sd->blue),
+                       v4l2_ctrl_g_ctrl(sd->red));
+       else
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+       if (sd->sensor == SENSOR_SOI763A)
+               setquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual));
+       setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
 }
 
 static void soi763a_6810_start(struct gspca_dev *gspca_dev)
 {
+       struct sd *sd = (struct sd *) gspca_dev;
        static const struct cmd bridge_init_2[] = {
                {TP6800_R7A_BLK_THRLD, 0x00},
                {TP6800_R79_QUALITY, 0x04},
@@ -4520,7 +4501,14 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
        reg_w(gspca_dev, 0x22, gspca_dev->alt);
        bulk_w(gspca_dev, 0x03, color_null, sizeof color_null);
        reg_w(gspca_dev, 0x59, 0x40);
-       setexposure(gspca_dev);
+       if (sd->sensor == SENSOR_CX0342)
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain),
+                       v4l2_ctrl_g_ctrl(sd->blue),
+                       v4l2_ctrl_g_ctrl(sd->red));
+       else
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
        reg_w_buf(gspca_dev, bridge_init_2, ARRAY_SIZE(bridge_init_2));
        reg_w_buf(gspca_dev, tp6810_ov_init_common,
                        ARRAY_SIZE(tp6810_ov_init_common));
@@ -4534,7 +4522,7 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, 0x07, 0x85);
                reg_w(gspca_dev, TP6800_R78_FORMAT, 0x01);      /* qvga */
        }
-       setgamma(gspca_dev);
+       setgamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
        reg_w_buf(gspca_dev, tp6810_bridge_start,
                        ARRAY_SIZE(tp6810_bridge_start));
 
@@ -4545,12 +4533,19 @@ static void soi763a_6810_start(struct gspca_dev *gspca_dev)
 
        reg_w(gspca_dev, 0x00, 0x00);
 
-       setsharpness(gspca_dev);
+       setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
        bulk_w(gspca_dev, 0x03, color_gain[SENSOR_SOI763A],
                                ARRAY_SIZE(color_gain[0]));
        set_led(gspca_dev, 1);
        reg_w(gspca_dev, TP6800_R3F_FRAME_RATE, 0xf0);
-       setexposure(gspca_dev);
+       if (sd->sensor == SENSOR_CX0342)
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain),
+                       v4l2_ctrl_g_ctrl(sd->blue),
+                       v4l2_ctrl_g_ctrl(sd->red));
+       else
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                       v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
        reg_w_buf(gspca_dev, bridge_init_6, ARRAY_SIZE(bridge_init_6));
 }
 
@@ -4576,12 +4571,25 @@ static int sd_start(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, 0x80, 0x03);
                reg_w(gspca_dev, 0x82, gspca_dev->curr_mode ? 0x0a : 0x0e);
 
-               setexposure(gspca_dev);
-               setquality(gspca_dev);
-               setautogain(gspca_dev);
+               if (sd->sensor == SENSOR_CX0342)
+                       setexposure(gspca_dev,
+                               v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                               v4l2_ctrl_g_ctrl(gspca_dev->gain),
+                               v4l2_ctrl_g_ctrl(sd->blue),
+                               v4l2_ctrl_g_ctrl(sd->red));
+               else
+                       setexposure(gspca_dev,
+                               v4l2_ctrl_g_ctrl(gspca_dev->exposure),
+                               v4l2_ctrl_g_ctrl(gspca_dev->gain), 0, 0);
+               if (sd->sensor == SENSOR_SOI763A)
+                       setquality(gspca_dev,
+                                  v4l2_ctrl_g_ctrl(sd->jpegqual));
+               if (sd->bridge == BRIDGE_TP6810)
+                       setautogain(gspca_dev,
+                                   v4l2_ctrl_g_ctrl(gspca_dev->autogain));
        }
 
-       setframerate(gspca_dev);
+       setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
 
        return gspca_dev->usb_err;
 }
@@ -4672,12 +4680,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        }
 }
 
-/* -- do autogain -- */
-/* gain setting is done in setexposure() for tp6810 */
-static void setgain(struct gspca_dev *gspca_dev) {}
-#define WANT_REGULAR_AUTOGAIN
-#include "autogain_functions.h"
-
 static void sd_dq_callback(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
@@ -4739,17 +4741,19 @@ static void sd_dq_callback(struct gspca_dev *gspca_dev)
                        luma /= 4;
                reg_w(gspca_dev, 0x7d, 0x00);
 
-               expo = sd->ctrls[EXPOSURE].val;
-               ret = auto_gain_n_exposure(gspca_dev, luma,
+               expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+               ret = gspca_expo_autogain(gspca_dev, luma,
                                60,     /* desired luma */
                                6,      /* dead zone */
                                2,      /* gain knee */
                                70);    /* expo knee */
                sd->ag_cnt = AG_CNT_START;
                if (sd->bridge == BRIDGE_TP6810) {
-                       if ((expo >= 128 && sd->ctrls[EXPOSURE].val < 128)
-                        || (expo < 128 && sd->ctrls[EXPOSURE].val >= 128))
-                               setframerate(gspca_dev);
+                       int new_expo = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+
+                       if ((expo >= 128 && new_expo < 128)
+                        || (expo < 128 && new_expo >= 128))
+                               setframerate(gspca_dev, new_expo);
                }
                break;
        }
@@ -4789,7 +4793,7 @@ static void sd_set_streamparm(struct gspca_dev *gspca_dev,
 
        sd->framerate = tpf->denominator / tpf->numerator;
        if (gspca_dev->streaming)
-               setframerate(gspca_dev);
+               setframerate(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
 
        /* Return the actual framerate */
        i = get_fr_idx(gspca_dev);
@@ -4806,12 +4810,10 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
-       if (sd->sensor == SENSOR_SOI763A)
-               jpeg_set_qual(sd->jpeg_hdr, jcomp->quality);
-/*     else
-               fixme: TODO
-*/
-       return gspca_dev->usb_err;
+       if (sd->sensor != SENSOR_SOI763A)
+               return -ENOTTY;
+       v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+       return 0;
 }
 
 static int sd_get_jcomp(struct gspca_dev *gspca_dev,
@@ -4819,118 +4821,109 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
 {
        struct sd *sd = (struct sd *) gspca_dev;
 
+       if (sd->sensor != SENSOR_SOI763A)
+               return -ENOTTY;
        memset(jcomp, 0, sizeof *jcomp);
-       jcomp->quality = jpeg_q[sd->quality];
+       jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
        jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
                        | V4L2_JPEG_MARKER_DQT;
        return 0;
 }
 
-static struct ctrl sd_ctrls[NCTRLS] = {
-[EXPOSURE] = {
-           {
-               .id = V4L2_CID_EXPOSURE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Exposure",
-               .minimum = 0x01,
-               .maximum = 0xdc,
-               .step = 1,
-               .default_value = 0x4e,
-           },
-           .set_control = setexposure
-       },
-[QUALITY] = {
-           {
-               .id = V4L2_CID_PRIVATE_BASE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Compression quality",
-               .minimum = 0,
-               .maximum = 15,
-               .step = 1,
-               .default_value = 13,
-           },
-           .set_control = setquality
-       },
-[RGAIN] = {
-           {
-               .id = V4L2_CID_RED_BALANCE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Red balance",
-               .minimum = 0,
-               .maximum = 4095,
-               .step = 1,
-               .default_value = 256,
-           },
-           .set_control = setrgain
-       },
-[GAIN] = {
-           {
-               .id = V4L2_CID_GAIN,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Gain",
-               .minimum = 0,
-               .maximum = 4095,
-               .step = 1,
-               .default_value = 256,
-           },
-           .set = sd_setgain
-       },
-[BGAIN] = {
-           {
-               .id = V4L2_CID_BLUE_BALANCE,
-               .type = V4L2_CTRL_TYPE_INTEGER,
-               .name = "Blue balance",
-               .minimum = 0,
-               .maximum = 4095,
-               .step = 1,
-               .default_value = 256,
-           },
-           .set_control = setbgain
-       },
-[SHARPNESS] = {
-           {
-               .id      = V4L2_CID_SHARPNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Sharpness",
-               .minimum = 0,
-               .maximum = 3,
-               .step    = 1,
-               .default_value = 2,
-           },
-           .set_control = setsharpness
-       },
-[GAMMA] = {
-           {
-               .id      = V4L2_CID_GAMMA,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Gamma",
-               .minimum = 0,
-               .maximum = NGAMMA - 1,
-               .step    = 1,
-               .default_value = 1,
-           },
-           .set_control = setgamma
-       },
-[AUTOGAIN] = {
-           {
-               .id      = V4L2_CID_AUTOGAIN,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Auto Gain",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-               .default_value = AUTOGAIN_DEF
-           },
-           .set_control = setautogain
-       },
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       struct gspca_dev *gspca_dev =
+               container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+       struct sd *sd = (struct sd *)gspca_dev;
+
+       gspca_dev->usb_err = 0;
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_SHARPNESS:
+               setsharpness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_GAMMA:
+               setgamma(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_BLUE_BALANCE:
+               setbgain(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_RED_BALANCE:
+               setrgain(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_EXPOSURE:
+               sd_setgain(gspca_dev);
+               break;
+       case V4L2_CID_AUTOGAIN:
+               if (ctrl->val)
+                       break;
+               sd_setgain(gspca_dev);
+               break;
+       case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+               jpeg_set_qual(sd->jpeg_hdr, 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_init_controls(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *)gspca_dev;
+       struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 4);
+       gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 1, 0xdc, 1, 0x4e);
+       if (sd->sensor == SENSOR_CX0342) {
+               sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_RED_BALANCE, 0, 4095, 1, 256);
+               sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 256);
+       }
+       if (sd->sensor == SENSOR_SOI763A)
+               gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 15, 1, 3);
+       else
+               gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAIN, 0, 4095, 1, 256);
+       sd->sharpness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_SHARPNESS, 0, 3, 1, 2);
+       sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_GAMMA, 0, NGAMMA - 1, 1,
+                       (sd->sensor == SENSOR_SOI763A &&
+                        sd->bridge == BRIDGE_TP6800) ? 0 : 1);
+       if (sd->bridge == BRIDGE_TP6810)
+               gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+       if (sd->sensor == SENSOR_SOI763A)
+               sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+                       V4L2_CID_JPEG_COMPRESSION_QUALITY,
+                       0, 15, 1, (sd->bridge == BRIDGE_TP6810) ? 0 : 13);
+
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       if (gspca_dev->autogain)
+               v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+       else
+               v4l2_ctrl_cluster(2, &gspca_dev->exposure);
+       return 0;
+}
+
 static const struct sd_desc sd_desc = {
        .name = KBUILD_MODNAME,
-       .ctrls = sd_ctrls,
-       .nctrls = NCTRLS,
        .config = sd_config,
        .init = sd_init,
+       .init_controls = sd_init_controls,
        .isoc_init = sd_isoc_init,
        .start = sd_start,
        .stopN = sd_stopN,