From 92dcffcf20feec54872e9e1c68e177b5549751ec Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Mon, 19 Mar 2012 04:47:24 -0300 Subject: [media] gspca - sn9c20x: Add automatic JPEG compression mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The JPEG compression may be adjusted from the packet fill ratio and from the flag 'USB FIFO full' returned in each frame. The code is adapted from the one in gspca sonixj and uses a workqueue. Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sn9c20x.c | 101 ++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 26ee3c92f56..2a670eadca1 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -89,6 +89,12 @@ struct sd { struct gspca_ctrl ctrls[NCTRLS]; + struct work_struct work; + struct workqueue_struct *work_thread; + + u32 pktsz; /* (used by pkt_scan) */ + u16 npkt; + s8 nchg; u8 fmt; /* (used for JPEG QTAB update */ #define MIN_AVG_LUM 80 @@ -108,6 +114,8 @@ struct sd { u8 flags; }; +static void qual_upd(struct work_struct *work); + struct i2c_reg_u8 { u8 reg; u8 val; @@ -1842,6 +1850,7 @@ static int sd_config(struct gspca_dev *gspca_dev, gspca_dev->cam.ctrls = sd->ctrls; + INIT_WORK(&sd->work, qual_upd); return 0; } @@ -2101,6 +2110,15 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_r(gspca_dev, 0x1061, 1); reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02); + + /* if JPEG, prepare the compression quality update */ + if (mode & MODE_JPEG) { + sd->pktsz = sd->npkt = 0; + sd->nchg = 0; + sd->work_thread = + create_singlethread_workqueue(KBUILD_MODNAME); + } + return gspca_dev->usb_err; } @@ -2112,6 +2130,20 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); } +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->work_thread != NULL) { + mutex_unlock(&gspca_dev->usb_lock); + destroy_workqueue(sd->work_thread); + mutex_lock(&gspca_dev->usb_lock); + sd->work_thread = NULL; + } +} + static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) { struct sd *sd = (struct sd *) gspca_dev; @@ -2195,6 +2227,19 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) do_autoexposure(gspca_dev, avg_lum); } +/* JPEG quality update */ +/* This function is executed from a work queue. */ +static void qual_upd(struct work_struct *work) +{ + struct sd *sd = container_of(work, struct sd, work); + struct gspca_dev *gspca_dev = &sd->gspca_dev; + + mutex_lock(&gspca_dev->usb_lock); + PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val); + set_quality(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); +} + #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet */ @@ -2213,6 +2258,50 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, } #endif +/* check the JPEG compression */ +static void transfer_check(struct gspca_dev *gspca_dev, + u8 *data) +{ + struct sd *sd = (struct sd *) gspca_dev; + int new_qual, r; + + new_qual = 0; + + /* if USB error, discard the frame and decrease the quality */ + if (data[6] & 0x08) { /* USB FIFO full */ + gspca_dev->last_packet_type = DISCARD_PACKET; + new_qual = -5; + } else { + + /* else, compute the filling rate and a new JPEG quality */ + r = (sd->pktsz * 100) / + (sd->npkt * + gspca_dev->urb[0]->iso_frame_desc[0].length); + if (r >= 85) + new_qual = -3; + else if (r < 75) + new_qual = 2; + } + if (new_qual != 0) { + sd->nchg += new_qual; + if (sd->nchg < -6 || sd->nchg >= 12) { + sd->nchg = 0; + new_qual += sd->ctrls[QUALITY].val; + if (new_qual < QUALITY_MIN) + new_qual = QUALITY_MIN; + else if (new_qual > QUALITY_MAX) + new_qual = QUALITY_MAX; + if (new_qual != sd->ctrls[QUALITY].val) { + sd->ctrls[QUALITY].val = new_qual; + queue_work(sd->work_thread, &sd->work); + } + } + } else { + sd->nchg = 0; + } + sd->pktsz = sd->npkt = 0; +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -2248,6 +2337,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, (data[33] << 10); avg_lum >>= 9; atomic_set(&sd->avg_lum, avg_lum); + + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv + & MODE_JPEG) + transfer_check(gspca_dev, data); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); len -= 64; if (len == 0) @@ -2266,6 +2360,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data, len); } } else { + /* if JPEG, count the packets and their size */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv + & MODE_JPEG) { + sd->npkt++; + sd->pktsz += len; + } gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } } @@ -2280,6 +2380,7 @@ static const struct sd_desc sd_desc = { .isoc_init = sd_isoc_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) .int_pkt_scan = sd_int_pkt_scan, -- cgit v1.2.3-70-g09d2