Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Jean-François Moine and committed by
Mauro Carvalho Chehab
92dcffcf e71389be

+101
+101
drivers/media/video/gspca/sn9c20x.c
··· 89 89 90 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 98 u8 fmt; /* (used for JPEG QTAB update */ 93 99 94 100 #define MIN_AVG_LUM 80 ··· 113 107 114 108 u8 flags; 115 109 }; 110 + 111 + static void qual_upd(struct work_struct *work); 116 112 117 113 struct i2c_reg_u8 { 118 114 u8 reg; ··· 1850 1842 1851 1843 gspca_dev->cam.ctrls = sd->ctrls; 1852 1844 1845 + INIT_WORK(&sd->work, qual_upd); 1853 1846 1854 1847 return 0; 1855 1848 } ··· 2110 2101 2111 2102 reg_r(gspca_dev, 0x1061, 1); 2112 2103 reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02); 2104 + 2105 + /* if JPEG, prepare the compression quality update */ 2106 + if (mode & MODE_JPEG) { 2107 + sd->pktsz = sd->npkt = 0; 2108 + sd->nchg = 0; 2109 + sd->work_thread = 2110 + create_singlethread_workqueue(KBUILD_MODNAME); 2111 + } 2112 + 2113 2113 return gspca_dev->usb_err; 2114 2114 } 2115 2115 ··· 2128 2110 2129 2111 reg_r(gspca_dev, 0x1061, 1); 2130 2112 reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02); 2113 + } 2114 + 2115 + /* called on streamoff with alt==0 and on disconnect */ 2116 + /* the usb_lock is held at entry - restore on exit */ 2117 + static void sd_stop0(struct gspca_dev *gspca_dev) 2118 + { 2119 + struct sd *sd = (struct sd *) gspca_dev; 2120 + 2121 + if (sd->work_thread != NULL) { 2122 + mutex_unlock(&gspca_dev->usb_lock); 2123 + destroy_workqueue(sd->work_thread); 2124 + mutex_lock(&gspca_dev->usb_lock); 2125 + sd->work_thread = NULL; 2126 + } 2131 2127 } 2132 2128 2133 2129 static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) ··· 2227 2195 do_autoexposure(gspca_dev, avg_lum); 2228 2196 } 2229 2197 2198 + /* JPEG quality update */ 2199 + /* This function is executed from a work queue. */ 2200 + static void qual_upd(struct work_struct *work) 2201 + { 2202 + struct sd *sd = container_of(work, struct sd, work); 2203 + struct gspca_dev *gspca_dev = &sd->gspca_dev; 2204 + 2205 + mutex_lock(&gspca_dev->usb_lock); 2206 + PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val); 2207 + set_quality(gspca_dev); 2208 + mutex_unlock(&gspca_dev->usb_lock); 2209 + } 2210 + 2230 2211 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) 2231 2212 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, 2232 2213 u8 *data, /* interrupt packet */ ··· 2257 2212 return ret; 2258 2213 } 2259 2214 #endif 2215 + 2216 + /* check the JPEG compression */ 2217 + static void transfer_check(struct gspca_dev *gspca_dev, 2218 + u8 *data) 2219 + { 2220 + struct sd *sd = (struct sd *) gspca_dev; 2221 + int new_qual, r; 2222 + 2223 + new_qual = 0; 2224 + 2225 + /* if USB error, discard the frame and decrease the quality */ 2226 + if (data[6] & 0x08) { /* USB FIFO full */ 2227 + gspca_dev->last_packet_type = DISCARD_PACKET; 2228 + new_qual = -5; 2229 + } else { 2230 + 2231 + /* else, compute the filling rate and a new JPEG quality */ 2232 + r = (sd->pktsz * 100) / 2233 + (sd->npkt * 2234 + gspca_dev->urb[0]->iso_frame_desc[0].length); 2235 + if (r >= 85) 2236 + new_qual = -3; 2237 + else if (r < 75) 2238 + new_qual = 2; 2239 + } 2240 + if (new_qual != 0) { 2241 + sd->nchg += new_qual; 2242 + if (sd->nchg < -6 || sd->nchg >= 12) { 2243 + sd->nchg = 0; 2244 + new_qual += sd->ctrls[QUALITY].val; 2245 + if (new_qual < QUALITY_MIN) 2246 + new_qual = QUALITY_MIN; 2247 + else if (new_qual > QUALITY_MAX) 2248 + new_qual = QUALITY_MAX; 2249 + if (new_qual != sd->ctrls[QUALITY].val) { 2250 + sd->ctrls[QUALITY].val = new_qual; 2251 + queue_work(sd->work_thread, &sd->work); 2252 + } 2253 + } 2254 + } else { 2255 + sd->nchg = 0; 2256 + } 2257 + sd->pktsz = sd->npkt = 0; 2258 + } 2260 2259 2261 2260 static void sd_pkt_scan(struct gspca_dev *gspca_dev, 2262 2261 u8 *data, /* isoc packet */ ··· 2337 2248 (data[33] << 10); 2338 2249 avg_lum >>= 9; 2339 2250 atomic_set(&sd->avg_lum, avg_lum); 2251 + 2252 + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv 2253 + & MODE_JPEG) 2254 + transfer_check(gspca_dev, data); 2255 + 2340 2256 gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); 2341 2257 len -= 64; 2342 2258 if (len == 0) ··· 2360 2266 data, len); 2361 2267 } 2362 2268 } else { 2269 + /* if JPEG, count the packets and their size */ 2270 + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv 2271 + & MODE_JPEG) { 2272 + sd->npkt++; 2273 + sd->pktsz += len; 2274 + } 2363 2275 gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 2364 2276 } 2365 2277 } ··· 2380 2280 .isoc_init = sd_isoc_init, 2381 2281 .start = sd_start, 2382 2282 .stopN = sd_stopN, 2283 + .stop0 = sd_stop0, 2383 2284 .pkt_scan = sd_pkt_scan, 2384 2285 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) 2385 2286 .int_pkt_scan = sd_int_pkt_scan,