[media] gspca - sn9c20x: Add the JPEG compression quality control
authorJean-François Moine <moinejf@free.fr>
Mon, 19 Mar 2012 07:35:34 +0000 (04:35 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Tue, 20 Mar 2012 01:30:20 +0000 (22:30 -0300)
The JPEG compression quality was hardcoded to 95%. This value was too big,
raising often buffer overflows.
This quality is now 80% by default and is settable.

Signed-off-by: Jean-François Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/gspca/sn9c20x.c

index 97c653f..0894a3d 100644 (file)
@@ -79,6 +79,7 @@ enum e_ctrl {
        EXPOSURE,
        GAIN,
        AUTOGAIN,
+       QUALITY,
        NCTRLS          /* number of controls */
 };
 
@@ -88,6 +89,8 @@ struct sd {
 
        struct gspca_ctrl ctrls[NCTRLS];
 
+       u8 fmt;                         /* (used for JPEG QTAB update */
+
 #define MIN_AVG_LUM 80
 #define MAX_AVG_LUM 130
        atomic_t avg_lum;
@@ -101,7 +104,6 @@ struct sd {
        u8 vstart;
 
        u8 jpeg_hdr[JPEG_HDR_SZ];
-       u8 quality;
 
        u8 flags;
 };
@@ -162,6 +164,7 @@ static void set_redblue(struct gspca_dev *gspca_dev);
 static void set_hvflip(struct gspca_dev *gspca_dev);
 static void set_exposure(struct gspca_dev *gspca_dev);
 static void set_gain(struct gspca_dev *gspca_dev);
+static void set_quality(struct gspca_dev *gspca_dev);
 
 static const struct ctrl sd_ctrls[NCTRLS] = {
 [BRIGHTNESS] = {
@@ -307,6 +310,21 @@ static const struct ctrl sd_ctrls[NCTRLS] = {
                .default_value = 1,
            },
        },
+[QUALITY] = {
+           {
+               .id      = V4L2_CID_JPEG_COMPRESSION_QUALITY,
+               .type    = V4L2_CTRL_TYPE_INTEGER,
+               .name    = "Compression Quality",
+#define QUALITY_MIN 50
+#define QUALITY_MAX 90
+#define QUALITY_DEF 80
+               .minimum = QUALITY_MIN,
+               .maximum = QUALITY_MAX,
+               .step    = 1,
+               .default_value = QUALITY_DEF,
+           },
+           .set_control = set_quality
+       },
 };
 
 static const struct v4l2_pix_format vga_mode[] = {
@@ -1732,6 +1750,21 @@ static void set_gain(struct gspca_dev *gspca_dev)
        i2c_w(gspca_dev, gain);
 }
 
+static void set_quality(struct gspca_dev *gspca_dev)
+{
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
+       reg_w1(gspca_dev, 0x1061, 0x01);        /* stop transfer */
+       reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */
+       reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
+       reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
+       reg_w1(gspca_dev, 0x1061, 0x03);        /* restart transfer */
+       reg_w1(gspca_dev, 0x10e0, sd->fmt);
+       sd->fmt ^= 0x0c;                        /* invert QTAB use + write */
+       reg_w1(gspca_dev, 0x10e0, sd->fmt);
+}
+
 #ifdef CONFIG_VIDEO_ADV_DEBUG
 static int sd_dbg_g_register(struct gspca_dev *gspca_dev,
                        struct v4l2_dbg_register *reg)
@@ -1846,7 +1879,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
 
        gspca_dev->cam.ctrls = sd->ctrls;
 
-       sd->quality = 95;
 
        return 0;
 }
@@ -2058,14 +2090,15 @@ static int sd_start(struct gspca_dev *gspca_dev)
 
        jpeg_define(sd->jpeg_hdr, height, width,
                        0x21);
-       jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+       jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
 
        if (mode & MODE_RAW)
                fmt = 0x2d;
        else if (mode & MODE_JPEG)
-               fmt = 0x2c;
+               fmt = 0x24;
        else
                fmt = 0x2f;     /* YUV 420 */
+       sd->fmt = fmt;
 
        switch (mode & SCALE_MASK) {
        case SCALE_1280x1024: