Skip to content

Commit

Permalink
[media] gspca - sn9c20x: Add automatic JPEG compression mechanism
Browse files Browse the repository at this point in the history
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 <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Jean-François Moine authored and Mauro Carvalho Chehab committed Mar 20, 2012
1 parent e71389b commit 92dcffc
Showing 1 changed file with 101 additions and 0 deletions.
101 changes: 101 additions & 0 deletions drivers/media/video/gspca/sn9c20x.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -108,6 +114,8 @@ struct sd {
u8 flags;
};

static void qual_upd(struct work_struct *work);

struct i2c_reg_u8 {
u8 reg;
u8 val;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
Expand Down Expand Up @@ -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 */
Expand All @@ -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 */
Expand Down Expand Up @@ -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)
Expand All @@ -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);
}
}
Expand All @@ -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,
Expand Down

0 comments on commit 92dcffc

Please sign in to comment.