aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorJean-François Moine <moinejf@free.fr>2012-03-19 03:47:24 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-03-19 21:31:46 -0400
commit92dcffcf20feec54872e9e1c68e177b5549751ec (patch)
tree3cc80dca609f64a54a3aaeea1f4fea924a2d0437 /drivers/media
parente71389be3d3059b447309deb83e3023c81233d59 (diff)
[media] gspca - sn9c20x: Add automatic JPEG compression mechanism
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>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/gspca/sn9c20x.c101
1 files changed, 101 insertions, 0 deletions
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
index 26ee3c92f56d..2a670eadca1c 100644
--- a/drivers/media/video/gspca/sn9c20x.c
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -89,6 +89,12 @@ struct sd {
89 89
90 struct gspca_ctrl ctrls[NCTRLS]; 90 struct gspca_ctrl ctrls[NCTRLS];
91 91
92 struct work_struct work;
93 struct workqueue_struct *work_thread;
94
95 u32 pktsz; /* (used by pkt_scan) */
96 u16 npkt;
97 s8 nchg;
92 u8 fmt; /* (used for JPEG QTAB update */ 98 u8 fmt; /* (used for JPEG QTAB update */
93 99
94#define MIN_AVG_LUM 80 100#define MIN_AVG_LUM 80
@@ -108,6 +114,8 @@ struct sd {
108 u8 flags; 114 u8 flags;
109}; 115};
110 116
117static void qual_upd(struct work_struct *work);
118
111struct i2c_reg_u8 { 119struct i2c_reg_u8 {
112 u8 reg; 120 u8 reg;
113 u8 val; 121 u8 val;
@@ -1842,6 +1850,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
1842 1850
1843 gspca_dev->cam.ctrls = sd->ctrls; 1851 gspca_dev->cam.ctrls = sd->ctrls;
1844 1852
1853 INIT_WORK(&sd->work, qual_upd);
1845 1854
1846 return 0; 1855 return 0;
1847} 1856}
@@ -2101,6 +2110,15 @@ static int sd_start(struct gspca_dev *gspca_dev)
2101 2110
2102 reg_r(gspca_dev, 0x1061, 1); 2111 reg_r(gspca_dev, 0x1061, 1);
2103 reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02); 2112 reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02);
2113
2114 /* if JPEG, prepare the compression quality update */
2115 if (mode & MODE_JPEG) {
2116 sd->pktsz = sd->npkt = 0;
2117 sd->nchg = 0;
2118 sd->work_thread =
2119 create_singlethread_workqueue(KBUILD_MODNAME);
2120 }
2121
2104 return gspca_dev->usb_err; 2122 return gspca_dev->usb_err;
2105} 2123}
2106 2124
@@ -2112,6 +2130,20 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
2112 reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); 2130 reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02);
2113} 2131}
2114 2132
2133/* called on streamoff with alt==0 and on disconnect */
2134/* the usb_lock is held at entry - restore on exit */
2135static void sd_stop0(struct gspca_dev *gspca_dev)
2136{
2137 struct sd *sd = (struct sd *) gspca_dev;
2138
2139 if (sd->work_thread != NULL) {
2140 mutex_unlock(&gspca_dev->usb_lock);
2141 destroy_workqueue(sd->work_thread);
2142 mutex_lock(&gspca_dev->usb_lock);
2143 sd->work_thread = NULL;
2144 }
2145}
2146
2115static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) 2147static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
2116{ 2148{
2117 struct sd *sd = (struct sd *) gspca_dev; 2149 struct sd *sd = (struct sd *) gspca_dev;
@@ -2195,6 +2227,19 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev)
2195 do_autoexposure(gspca_dev, avg_lum); 2227 do_autoexposure(gspca_dev, avg_lum);
2196} 2228}
2197 2229
2230/* JPEG quality update */
2231/* This function is executed from a work queue. */
2232static void qual_upd(struct work_struct *work)
2233{
2234 struct sd *sd = container_of(work, struct sd, work);
2235 struct gspca_dev *gspca_dev = &sd->gspca_dev;
2236
2237 mutex_lock(&gspca_dev->usb_lock);
2238 PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val);
2239 set_quality(gspca_dev);
2240 mutex_unlock(&gspca_dev->usb_lock);
2241}
2242
2198#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) 2243#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
2199static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, 2244static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
2200 u8 *data, /* interrupt packet */ 2245 u8 *data, /* interrupt packet */
@@ -2213,6 +2258,50 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
2213} 2258}
2214#endif 2259#endif
2215 2260
2261/* check the JPEG compression */
2262static void transfer_check(struct gspca_dev *gspca_dev,
2263 u8 *data)
2264{
2265 struct sd *sd = (struct sd *) gspca_dev;
2266 int new_qual, r;
2267
2268 new_qual = 0;
2269
2270 /* if USB error, discard the frame and decrease the quality */
2271 if (data[6] & 0x08) { /* USB FIFO full */
2272 gspca_dev->last_packet_type = DISCARD_PACKET;
2273 new_qual = -5;
2274 } else {
2275
2276 /* else, compute the filling rate and a new JPEG quality */
2277 r = (sd->pktsz * 100) /
2278 (sd->npkt *
2279 gspca_dev->urb[0]->iso_frame_desc[0].length);
2280 if (r >= 85)
2281 new_qual = -3;
2282 else if (r < 75)
2283 new_qual = 2;
2284 }
2285 if (new_qual != 0) {
2286 sd->nchg += new_qual;
2287 if (sd->nchg < -6 || sd->nchg >= 12) {
2288 sd->nchg = 0;
2289 new_qual += sd->ctrls[QUALITY].val;
2290 if (new_qual < QUALITY_MIN)
2291 new_qual = QUALITY_MIN;
2292 else if (new_qual > QUALITY_MAX)
2293 new_qual = QUALITY_MAX;
2294 if (new_qual != sd->ctrls[QUALITY].val) {
2295 sd->ctrls[QUALITY].val = new_qual;
2296 queue_work(sd->work_thread, &sd->work);
2297 }
2298 }
2299 } else {
2300 sd->nchg = 0;
2301 }
2302 sd->pktsz = sd->npkt = 0;
2303}
2304
2216static void sd_pkt_scan(struct gspca_dev *gspca_dev, 2305static void sd_pkt_scan(struct gspca_dev *gspca_dev,
2217 u8 *data, /* isoc packet */ 2306 u8 *data, /* isoc packet */
2218 int len) /* iso packet length */ 2307 int len) /* iso packet length */
@@ -2248,6 +2337,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
2248 (data[33] << 10); 2337 (data[33] << 10);
2249 avg_lum >>= 9; 2338 avg_lum >>= 9;
2250 atomic_set(&sd->avg_lum, avg_lum); 2339 atomic_set(&sd->avg_lum, avg_lum);
2340
2341 if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv
2342 & MODE_JPEG)
2343 transfer_check(gspca_dev, data);
2344
2251 gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); 2345 gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
2252 len -= 64; 2346 len -= 64;
2253 if (len == 0) 2347 if (len == 0)
@@ -2266,6 +2360,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
2266 data, len); 2360 data, len);
2267 } 2361 }
2268 } else { 2362 } else {
2363 /* if JPEG, count the packets and their size */
2364 if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv
2365 & MODE_JPEG) {
2366 sd->npkt++;
2367 sd->pktsz += len;
2368 }
2269 gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 2369 gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
2270 } 2370 }
2271} 2371}
@@ -2280,6 +2380,7 @@ static const struct sd_desc sd_desc = {
2280 .isoc_init = sd_isoc_init, 2380 .isoc_init = sd_isoc_init,
2281 .start = sd_start, 2381 .start = sd_start,
2282 .stopN = sd_stopN, 2382 .stopN = sd_stopN,
2383 .stop0 = sd_stop0,
2283 .pkt_scan = sd_pkt_scan, 2384 .pkt_scan = sd_pkt_scan,
2284#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) 2385#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
2285 .int_pkt_scan = sd_int_pkt_scan, 2386 .int_pkt_scan = sd_int_pkt_scan,