[media] gspca_zc3xx: Convert to the control framework
authorHans Verkuil <hans.verkuil@cisco.com>
Sun, 6 May 2012 12:28:28 +0000 (09:28 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 14 May 2012 12:36:05 +0000 (09:36 -0300)
The initial version was done by HV, corrections were made by HdG, and some
final small changes again by HV.

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/zc3xx.c

index 998017e..a8839fb 100644 (file)
@@ -35,26 +35,23 @@ static int force_sensor = -1;
 #define REG08_DEF 3            /* default JPEG compression (75%) */
 #include "zc3xx-reg.h"
 
-/* controls */
-enum e_ctrl {
-       BRIGHTNESS,
-       CONTRAST,
-       EXPOSURE,
-       GAMMA,
-       AUTOGAIN,
-       LIGHTFREQ,
-       SHARPNESS,
-       QUALITY,
-       NCTRLS          /* number of controls */
-};
-
-#define AUTOGAIN_DEF 1
-
 /* 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 { /* gamma/brightness/contrast control cluster */
+               struct v4l2_ctrl *gamma;
+               struct v4l2_ctrl *brightness;
+               struct v4l2_ctrl *contrast;
+       };
+       struct { /* autogain/exposure control cluster */
+               struct v4l2_ctrl *autogain;
+               struct v4l2_ctrl *exposure;
+       };
+       struct v4l2_ctrl *plfreq;
+       struct v4l2_ctrl *sharpness;
+       struct v4l2_ctrl *jpegqual;
 
        struct work_struct work;
        struct workqueue_struct *work_thread;
@@ -94,114 +91,6 @@ enum sensors {
        SENSOR_MAX
 };
 
-/* V4L2 controls supported by the driver */
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static void setlightfreq(struct gspca_dev *gspca_dev);
-static void setsharpness(struct gspca_dev *gspca_dev);
-static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val);
-
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
-           {
-               .id      = V4L2_CID_BRIGHTNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Brightness",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 128,
-           },
-           .set_control = setcontrast
-       },
-[CONTRAST] = {
-           {
-               .id      = V4L2_CID_CONTRAST,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Contrast",
-               .minimum = 0,
-               .maximum = 255,
-               .step    = 1,
-               .default_value = 128,
-           },
-           .set_control = setcontrast
-       },
-[EXPOSURE] = {
-           {
-               .id      = V4L2_CID_EXPOSURE,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Exposure",
-               .minimum = 0x30d,
-               .maximum        = 0x493e,
-               .step           = 1,
-               .default_value  = 0x927
-           },
-           .set_control = setexposure
-       },
-[GAMMA] = {
-           {
-               .id      = V4L2_CID_GAMMA,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Gamma",
-               .minimum = 1,
-               .maximum = 6,
-               .step    = 1,
-               .default_value = 4,
-           },
-           .set_control = setcontrast
-       },
-[AUTOGAIN] = {
-           {
-               .id      = V4L2_CID_AUTOGAIN,
-               .type    = V4L2_CTRL_TYPE_BOOLEAN,
-               .name    = "Auto Gain",
-               .minimum = 0,
-               .maximum = 1,
-               .step    = 1,
-               .default_value = AUTOGAIN_DEF,
-               .flags   = V4L2_CTRL_FLAG_UPDATE
-           },
-           .set = sd_setautogain
-       },
-[LIGHTFREQ] = {
-           {
-               .id      = V4L2_CID_POWER_LINE_FREQUENCY,
-               .type    = V4L2_CTRL_TYPE_MENU,
-               .name    = "Light frequency filter",
-               .minimum = 0,
-               .maximum = 2,   /* 0: 0, 1: 50Hz, 2:60Hz */
-               .step    = 1,
-               .default_value = 0,
-           },
-           .set_control = setlightfreq
-       },
-[SHARPNESS] = {
-           {
-               .id      = V4L2_CID_SHARPNESS,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Sharpness",
-               .minimum = 0,
-               .maximum = 3,
-               .step    = 1,
-               .default_value = 2,
-           },
-           .set_control = setsharpness
-       },
-[QUALITY] = {
-           {
-               .id      = V4L2_CID_JPEG_COMPRESSION_QUALITY,
-               .type    = V4L2_CTRL_TYPE_INTEGER,
-               .name    = "Compression Quality",
-               .minimum = 50,
-               .maximum = 87,
-               .step    = 1,
-               .default_value = 75,
-           },
-           .set = sd_setquality
-       },
-};
-
 static const struct v4l2_pix_format vga_mode[] = {
        {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
                .bytesperline = 320,
@@ -5821,10 +5710,8 @@ static void setmatrix(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, matrix[i], 0x010a + i);
 }
 
-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;
-       int sharpness;
        static const u8 sharpness_tb[][2] = {
                {0x02, 0x03},
                {0x04, 0x07},
@@ -5832,19 +5719,18 @@ static void setsharpness(struct gspca_dev *gspca_dev)
                {0x10, 0x1e}
        };
 
-       sharpness = sd->ctrls[SHARPNESS].val;
-       reg_w(gspca_dev, sharpness_tb[sharpness][0], 0x01c6);
+       reg_w(gspca_dev, sharpness_tb[val][0], 0x01c6);
        reg_r(gspca_dev, 0x01c8);
        reg_r(gspca_dev, 0x01c9);
        reg_r(gspca_dev, 0x01ca);
-       reg_w(gspca_dev, sharpness_tb[sharpness][1], 0x01cb);
+       reg_w(gspca_dev, sharpness_tb[val][1], 0x01cb);
 }
 
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev,
+               s32 gamma, s32 brightness, s32 contrast)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
        const u8 *Tgamma;
-       int g, i, brightness, contrast, adj, gp1, gp2;
+       int g, i, adj, gp1, gp2;
        u8 gr[16];
        static const u8 delta_b[16] =           /* delta for brightness */
                {0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d,
@@ -5867,10 +5753,10 @@ static void setcontrast(struct gspca_dev *gspca_dev)
                 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff},
        };
 
-       Tgamma = gamma_tb[sd->ctrls[GAMMA].val - 1];
+       Tgamma = gamma_tb[gamma - 1];
 
-       contrast = ((int) sd->ctrls[CONTRAST].val - 128); /* -128 / 127 */
-       brightness = ((int) sd->ctrls[BRIGHTNESS].val - 128); /* -128 / 92 */
+       contrast -= 128; /* -128 / 127 */
+       brightness -= 128; /* -128 / 92 */
        adj = 0;
        gp1 = gp2 = 0;
        for (i = 0; i < 16; i++) {
@@ -5897,25 +5783,15 @@ static void setcontrast(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, gr[i], 0x0130 + i);    /* gradient */
 }
 
-static void getexposure(struct gspca_dev *gspca_dev)
+static s32 getexposure(struct gspca_dev *gspca_dev)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       if (sd->sensor != SENSOR_HV7131R)
-               return;
-       sd->ctrls[EXPOSURE].val = (i2c_read(gspca_dev, 0x25) << 9)
+       return (i2c_read(gspca_dev, 0x25) << 9)
                | (i2c_read(gspca_dev, 0x26) << 1)
                | (i2c_read(gspca_dev, 0x27) >> 7);
 }
 
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       int val;
-
-       if (sd->sensor != SENSOR_HV7131R)
-               return;
-       val = sd->ctrls[EXPOSURE].val;
        i2c_write(gspca_dev, 0x25, val >> 9, 0x00);
        i2c_write(gspca_dev, 0x26, val >> 1, 0x00);
        i2c_write(gspca_dev, 0x27, val << 7, 0x00);
@@ -5934,7 +5810,7 @@ static void setquality(struct gspca_dev *gspca_dev)
  *     60Hz, for American lighting
  *     0 = No Fliker (for outdoore usage)
  */
-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;
        int i, mode;
@@ -6018,7 +5894,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
                 tas5130c_60HZ, tas5130c_60HZScale},
        };
 
-       i = sd->ctrls[LIGHTFREQ].val * 2;
+       i = val * 2;
        mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
        if (mode)
                i++;                    /* 320x240 */
@@ -6028,14 +5904,14 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
        usb_exchange(gspca_dev, zc3_freq);
        switch (sd->sensor) {
        case SENSOR_GC0305:
-               if (mode                                /* if 320x240 */
-                   && sd->ctrls[LIGHTFREQ].val == 1)   /* and 50Hz */
+               if (mode                /* if 320x240 */
+                   && val == 1)        /* and 50Hz */
                        reg_w(gspca_dev, 0x85, 0x018d);
                                        /* win: 0x80, 0x018d */
                break;
        case SENSOR_OV7620:
-               if (!mode) {                            /* if 640x480 */
-                       if (sd->ctrls[LIGHTFREQ].val != 0) /* and filter */
+               if (!mode) {            /* if 640x480 */
+                       if (val != 0)   /* and filter */
                                reg_w(gspca_dev, 0x40, 0x0002);
                        else
                                reg_w(gspca_dev, 0x44, 0x0002);
@@ -6047,16 +5923,9 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
        }
 }
 
-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;
-       u8 autoval;
-
-       if (sd->ctrls[AUTOGAIN].val)
-               autoval = 0x42;
-       else
-               autoval = 0x02;
-       reg_w(gspca_dev, autoval, 0x0180);
+       reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180);
 }
 
 /*
@@ -6450,7 +6319,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
        /* define some sensors from the vendor/product */
        sd->sensor = id->driver_info;
 
-       gspca_dev->cam.ctrls = sd->ctrls;
        sd->reg08 = REG08_DEF;
 
        INIT_WORK(&sd->work, transfer_update);
@@ -6458,12 +6326,85 @@ static int sd_config(struct gspca_dev *gspca_dev,
        return 0;
 }
 
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
+static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 {
-       struct sd *sd = (struct sd *) gspca_dev;
-       struct cam *cam;
-       int sensor;
+       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->exposure && gspca_dev->streaming)
+                       sd->exposure->val = getexposure(gspca_dev);
+               return gspca_dev->usb_err;
+       }
+       return -EINVAL;
+}
+
+static int zcxx_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;
+       int i, qual;
+
+       gspca_dev->usb_err = 0;
+
+       if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) {
+               qual = sd->reg08 >> 1;
+
+               for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) {
+                       if (ctrl->val <= jpeg_qual[i])
+                               break;
+               }
+               if (i > 0 && i == qual && ctrl->val < jpeg_qual[i])
+                       i--;
+
+               /* With high quality settings we need max bandwidth */
+               if (i >= 2 && gspca_dev->streaming &&
+                   !gspca_dev->cam.needs_full_bandwidth)
+                       return -EBUSY;
+
+               sd->reg08 = (i << 1) | 1;
+               ctrl->val = jpeg_qual[i];
+       }
+
+       if (!gspca_dev->streaming)
+               return 0;
+
+       switch (ctrl->id) {
+       /* gamma/brightness/contrast cluster */
+       case V4L2_CID_GAMMA:
+               setcontrast(gspca_dev, sd->gamma->val,
+                               sd->brightness->val, sd->contrast->val);
+               break;
+       /* autogain/exposure cluster */
+       case V4L2_CID_AUTOGAIN:
+               setautogain(gspca_dev, ctrl->val);
+               if (!gspca_dev->usb_err && !ctrl->val && sd->exposure)
+                       setexposure(gspca_dev, sd->exposure->val);
+               break;
+       case V4L2_CID_POWER_LINE_FREQUENCY:
+               setlightfreq(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_SHARPNESS:
+               setsharpness(gspca_dev, ctrl->val);
+               break;
+       case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+               setquality(gspca_dev);
+               break;
+       }
+       return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops zcxx_ctrl_ops = {
+       .g_volatile_ctrl = zcxx_g_volatile_ctrl,
+       .s_ctrl = zcxx_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;
        static const u8 gamma[SENSOR_MAX] = {
                [SENSOR_ADCM2700] =     4,
                [SENSOR_CS2102] =       4,
@@ -6485,6 +6426,48 @@ static int sd_init(struct gspca_dev *gspca_dev)
                [SENSOR_PO2030] =       4,
                [SENSOR_TAS5130C] =     3,
        };
+
+       gspca_dev->vdev.ctrl_handler = hdl;
+       v4l2_ctrl_handler_init(hdl, 8);
+       sd->brightness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+                       V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+       sd->contrast = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+                       V4L2_CID_CONTRAST, 0, 255, 1, 128);
+       sd->gamma = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+                       V4L2_CID_GAMMA, 1, 6, 1, gamma[sd->sensor]);
+       if (sd->sensor == SENSOR_HV7131R)
+               sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+                       V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927);
+       sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+                       V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+       if (sd->sensor != SENSOR_OV7630C)
+               sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &zcxx_ctrl_ops,
+                       V4L2_CID_POWER_LINE_FREQUENCY,
+                       V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+                       V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+       sd->sharpness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+                       V4L2_CID_SHARPNESS, 0, 3, 1,
+                       sd->sensor == SENSOR_PO2030 ? 0 : 2);
+       sd->jpegqual = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+                       V4L2_CID_JPEG_COMPRESSION_QUALITY,
+                       jpeg_qual[0], jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1], 1,
+                       jpeg_qual[REG08_DEF >> 1]);
+       if (hdl->error) {
+               pr_err("Could not initialize controls\n");
+               return hdl->error;
+       }
+       v4l2_ctrl_cluster(3, &sd->gamma);
+       if (sd->sensor == SENSOR_HV7131R)
+               v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true);
+       return 0;
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+       struct cam *cam;
+       int sensor;
        static const u8 mode_tb[SENSOR_MAX] = {
                [SENSOR_ADCM2700] =     2,
                [SENSOR_CS2102] =       1,
@@ -6614,7 +6597,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
                case 0x2030:
                        PDEBUG(D_PROBE, "Find Sensor PO2030");
                        sd->sensor = SENSOR_PO2030;
-                       sd->ctrls[SHARPNESS].def = 0;   /* from win traces */
                        break;
                case 0x7620:
                        PDEBUG(D_PROBE, "Find Sensor OV7620");
@@ -6656,26 +6638,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
                break;
        }
 
-       sd->ctrls[GAMMA].def = gamma[sd->sensor];
-       sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08 >> 1];
-       sd->ctrls[QUALITY].min = jpeg_qual[0];
-       sd->ctrls[QUALITY].max = jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1];
-
-       switch (sd->sensor) {
-       case SENSOR_HV7131R:
-               break;
-       case SENSOR_OV7630C:
-               gspca_dev->ctrl_dis = (1 << LIGHTFREQ) | (1 << EXPOSURE);
-               break;
-       default:
-               gspca_dev->ctrl_dis = (1 << EXPOSURE);
-               break;
-       }
-#if AUTOGAIN_DEF
-       if (sd->ctrls[AUTOGAIN].val)
-               gspca_dev->ctrl_inac = (1 << EXPOSURE);
-#endif
-
        /* switch off the led */
        reg_w(gspca_dev, 0x01, 0x0000);
        return gspca_dev->usb_err;
@@ -6792,7 +6754,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, 0x03, 0x0008);
                break;
        }
-       setsharpness(gspca_dev);
+       setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
 
        /* set the gamma tables when not set */
        switch (sd->sensor) {
@@ -6801,7 +6763,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
        case SENSOR_OV7630C:
                break;
        default:
-               setcontrast(gspca_dev);
+               setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma),
+                               v4l2_ctrl_g_ctrl(sd->brightness),
+                               v4l2_ctrl_g_ctrl(sd->contrast));
                break;
        }
        setmatrix(gspca_dev);                   /* one more time? */
@@ -6815,7 +6779,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
        setquality(gspca_dev);
        /* Start with BRC disabled, transfer_update will enable it if needed */
        reg_w(gspca_dev, 0x00, 0x0007);
-       setlightfreq(gspca_dev);
+       if (sd->plfreq)
+               setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq));
 
        switch (sd->sensor) {
        case SENSOR_ADCM2700:
@@ -6826,7 +6791,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                reg_w(gspca_dev, 0x40, 0x0117);
                break;
        case SENSOR_HV7131R:
-               setexposure(gspca_dev);
+               setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
                reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN);
                break;
        case SENSOR_GC0305:
@@ -6850,7 +6815,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
                break;
        }
 
-       setautogain(gspca_dev);
+       setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
 
        if (gspca_dev->usb_err < 0)
                return gspca_dev->usb_err;
@@ -6911,79 +6876,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
        gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 }
 
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-
-       sd->ctrls[AUTOGAIN].val = val;
-       if (val) {
-               gspca_dev->ctrl_inac |= (1 << EXPOSURE);
-       } else {
-               gspca_dev->ctrl_inac &= ~(1 << EXPOSURE);
-               if (gspca_dev->streaming)
-                       getexposure(gspca_dev);
-       }
-       if (gspca_dev->streaming)
-               setautogain(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
-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, "NoFliker");
-                       return 0;
-               case 1:         /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
-                       strcpy((char *) menu->name, "50 Hz");
-                       return 0;
-               case 2:         /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
-                       strcpy((char *) menu->name, "60 Hz");
-                       return 0;
-               }
-               break;
-       }
-       return -EINVAL;
-}
-
-static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val)
-{
-       struct sd *sd = (struct sd *) gspca_dev;
-       int i, qual = sd->reg08 >> 1;
-
-       for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) {
-               if (val <= jpeg_qual[i])
-                       break;
-       }
-       if (i > 0
-        && i == qual
-        && val < jpeg_qual[i])
-               i--;
-
-       /* With high quality settings we need max bandwidth */
-       if (i >= 2 && gspca_dev->streaming &&
-           !gspca_dev->cam.needs_full_bandwidth)
-               return -EBUSY;
-
-       sd->reg08 = (i << 1) | 1;
-       sd->ctrls[QUALITY].val = jpeg_qual[i];
-
-       if (gspca_dev->streaming)
-               setquality(gspca_dev);
-       return gspca_dev->usb_err;
-}
-
 static int sd_set_jcomp(struct gspca_dev *gspca_dev,
                        struct v4l2_jpegcompression *jcomp)
 {
        struct sd *sd = (struct sd *) gspca_dev;
+       int ret;
 
-       sd_setquality(gspca_dev, jcomp->quality);
-       jcomp->quality = sd->ctrls[QUALITY].val;
-       return gspca_dev->usb_err;
+       ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+       if (ret)
+               return ret;
+       jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
+       return 0;
 }
 
 static int sd_get_jcomp(struct gspca_dev *gspca_dev,
@@ -6992,7 +6895,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
        struct sd *sd = (struct sd *) gspca_dev;
 
        memset(jcomp, 0, sizeof *jcomp);
-       jcomp->quality = sd->ctrls[QUALITY].val;
+       jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
        jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
                        | V4L2_JPEG_MARKER_DQT;
        return 0;
@@ -7016,15 +6919,13 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
 
 static const struct sd_desc sd_desc = {
        .name = KBUILD_MODNAME,
-       .ctrls = sd_ctrls,
-       .nctrls = ARRAY_SIZE(sd_ctrls),
        .config = sd_config,
        .init = sd_init,
+       .init_controls = sd_init_controls,
        .isoc_init = sd_pre_start,
        .start = sd_start,
        .stop0 = sd_stop0,
        .pkt_scan = sd_pkt_scan,
-       .querymenu = sd_querymenu,
        .get_jcomp = sd_get_jcomp,
        .set_jcomp = sd_set_jcomp,
 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
@@ -7108,6 +7009,7 @@ static struct usb_driver sd_driver = {
 #ifdef CONFIG_PM
        .suspend = gspca_suspend,
        .resume = gspca_resume,
+       .reset_resume = gspca_resume,
 #endif
 };