1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Pixart PAC7311 library
4*4882a593Smuzhiyun * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun /* Some documentation about various registers as determined by trial and error.
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * Register page 1:
12*4882a593Smuzhiyun *
13*4882a593Smuzhiyun * Address Description
14*4882a593Smuzhiyun * 0x08 Unknown compressor related, must always be 8 except when not
15*4882a593Smuzhiyun * in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !
16*4882a593Smuzhiyun * 0x1b Auto white balance related, bit 0 is AWB enable (inverted)
17*4882a593Smuzhiyun * bits 345 seem to toggle per color gains on/off (inverted)
18*4882a593Smuzhiyun * 0x78 Global control, bit 6 controls the LED (inverted)
19*4882a593Smuzhiyun * 0x80 Compression balance, interesting settings:
20*4882a593Smuzhiyun * 0x01 Use this to allow the camera to switch to higher compr.
21*4882a593Smuzhiyun * on the fly. Needed to stay within bandwidth @ 640x480@30
22*4882a593Smuzhiyun * 0x1c From usb captures under Windows for 640x480
23*4882a593Smuzhiyun * 0x2a Values >= this switch the camera to a lower compression,
24*4882a593Smuzhiyun * using the same table for both luminance and chrominance.
25*4882a593Smuzhiyun * This gives a sharper picture. Usable only at 640x480@ <
26*4882a593Smuzhiyun * 15 fps or 320x240 / 160x120. Note currently the driver
27*4882a593Smuzhiyun * does not use this as the quality gain is small and the
28*4882a593Smuzhiyun * generated JPG-s are only understood by v4l-utils >= 0.8.9
29*4882a593Smuzhiyun * 0x3f From usb captures under Windows for 320x240
30*4882a593Smuzhiyun * 0x69 From usb captures under Windows for 160x120
31*4882a593Smuzhiyun *
32*4882a593Smuzhiyun * Register page 4:
33*4882a593Smuzhiyun *
34*4882a593Smuzhiyun * Address Description
35*4882a593Smuzhiyun * 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
36*4882a593Smuzhiyun * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
37*4882a593Smuzhiyun * 0x0f Master gain 1-245, low value = high gain
38*4882a593Smuzhiyun * 0x10 Another gain 0-15, limited influence (1-2x gain I guess)
39*4882a593Smuzhiyun * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
40*4882a593Smuzhiyun * Note setting vflip disabled leads to a much lower image quality,
41*4882a593Smuzhiyun * so we always vflip, and tell userspace to flip it back
42*4882a593Smuzhiyun * 0x27 Seems to toggle various gains on / off, Setting bit 7 seems to
43*4882a593Smuzhiyun * completely disable the analog amplification block. Set to 0x68
44*4882a593Smuzhiyun * for max gain, 0x14 for minimal gain.
45*4882a593Smuzhiyun */
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun #define MODULE_NAME "pac7311"
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun #include <linux/input.h>
52*4882a593Smuzhiyun #include "gspca.h"
53*4882a593Smuzhiyun /* Include pac common sof detection functions */
54*4882a593Smuzhiyun #include "pac_common.h"
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun #define PAC7311_GAIN_DEFAULT 122
57*4882a593Smuzhiyun #define PAC7311_EXPOSURE_DEFAULT 3 /* 20 fps, avoid using high compr. */
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
60*4882a593Smuzhiyun MODULE_DESCRIPTION("Pixart PAC7311");
61*4882a593Smuzhiyun MODULE_LICENSE("GPL");
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun struct sd {
64*4882a593Smuzhiyun struct gspca_dev gspca_dev; /* !! must be the first item */
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun struct v4l2_ctrl *contrast;
67*4882a593Smuzhiyun struct v4l2_ctrl *hflip;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun u8 sof_read;
70*4882a593Smuzhiyun u8 autogain_ignore_frames;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun atomic_t avg_lum;
73*4882a593Smuzhiyun };
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun static const struct v4l2_pix_format vga_mode[] = {
76*4882a593Smuzhiyun {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
77*4882a593Smuzhiyun .bytesperline = 160,
78*4882a593Smuzhiyun .sizeimage = 160 * 120 * 3 / 8 + 590,
79*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_JPEG,
80*4882a593Smuzhiyun .priv = 2},
81*4882a593Smuzhiyun {320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
82*4882a593Smuzhiyun .bytesperline = 320,
83*4882a593Smuzhiyun .sizeimage = 320 * 240 * 3 / 8 + 590,
84*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_JPEG,
85*4882a593Smuzhiyun .priv = 1},
86*4882a593Smuzhiyun {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
87*4882a593Smuzhiyun .bytesperline = 640,
88*4882a593Smuzhiyun .sizeimage = 640 * 480 * 3 / 8 + 590,
89*4882a593Smuzhiyun .colorspace = V4L2_COLORSPACE_JPEG,
90*4882a593Smuzhiyun .priv = 0},
91*4882a593Smuzhiyun };
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun #define LOAD_PAGE4 254
94*4882a593Smuzhiyun #define END_OF_SEQUENCE 0
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun static const __u8 init_7311[] = {
97*4882a593Smuzhiyun 0xff, 0x01,
98*4882a593Smuzhiyun 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */
99*4882a593Smuzhiyun 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */
100*4882a593Smuzhiyun 0x78, 0x44, /* Bit_0=start stream, Bit_6=LED */
101*4882a593Smuzhiyun 0xff, 0x04,
102*4882a593Smuzhiyun 0x27, 0x80,
103*4882a593Smuzhiyun 0x28, 0xca,
104*4882a593Smuzhiyun 0x29, 0x53,
105*4882a593Smuzhiyun 0x2a, 0x0e,
106*4882a593Smuzhiyun 0xff, 0x01,
107*4882a593Smuzhiyun 0x3e, 0x20,
108*4882a593Smuzhiyun };
109*4882a593Smuzhiyun
110*4882a593Smuzhiyun static const __u8 start_7311[] = {
111*4882a593Smuzhiyun /* index, len, [value]* */
112*4882a593Smuzhiyun 0xff, 1, 0x01, /* page 1 */
113*4882a593Smuzhiyun 0x02, 43, 0x48, 0x0a, 0x40, 0x08, 0x00, 0x00, 0x08, 0x00,
114*4882a593Smuzhiyun 0x06, 0xff, 0x11, 0xff, 0x5a, 0x30, 0x90, 0x4c,
115*4882a593Smuzhiyun 0x00, 0x07, 0x00, 0x0a, 0x10, 0x00, 0xa0, 0x10,
116*4882a593Smuzhiyun 0x02, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00,
117*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118*4882a593Smuzhiyun 0x00, 0x00, 0x00,
119*4882a593Smuzhiyun 0x3e, 42, 0x00, 0x00, 0x78, 0x52, 0x4a, 0x52, 0x78, 0x6e,
120*4882a593Smuzhiyun 0x48, 0x46, 0x48, 0x6e, 0x5f, 0x49, 0x42, 0x49,
121*4882a593Smuzhiyun 0x5f, 0x5f, 0x49, 0x42, 0x49, 0x5f, 0x6e, 0x48,
122*4882a593Smuzhiyun 0x46, 0x48, 0x6e, 0x78, 0x52, 0x4a, 0x52, 0x78,
123*4882a593Smuzhiyun 0x00, 0x00, 0x09, 0x1b, 0x34, 0x49, 0x5c, 0x9b,
124*4882a593Smuzhiyun 0xd0, 0xff,
125*4882a593Smuzhiyun 0x78, 6, 0x44, 0x00, 0xf2, 0x01, 0x01, 0x80,
126*4882a593Smuzhiyun 0x7f, 18, 0x2a, 0x1c, 0x00, 0xc8, 0x02, 0x58, 0x03, 0x84,
127*4882a593Smuzhiyun 0x12, 0x00, 0x1a, 0x04, 0x08, 0x0c, 0x10, 0x14,
128*4882a593Smuzhiyun 0x18, 0x20,
129*4882a593Smuzhiyun 0x96, 3, 0x01, 0x08, 0x04,
130*4882a593Smuzhiyun 0xa0, 4, 0x44, 0x44, 0x44, 0x04,
131*4882a593Smuzhiyun 0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00,
132*4882a593Smuzhiyun 0x3f, 0x00, 0x0a, 0x01, 0x00,
133*4882a593Smuzhiyun 0xff, 1, 0x04, /* page 4 */
134*4882a593Smuzhiyun 0, LOAD_PAGE4, /* load the page 4 */
135*4882a593Smuzhiyun 0x11, 1, 0x01,
136*4882a593Smuzhiyun 0, END_OF_SEQUENCE /* end of sequence */
137*4882a593Smuzhiyun };
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun #define SKIP 0xaa
140*4882a593Smuzhiyun /* page 4 - the value SKIP says skip the index - see reg_w_page() */
141*4882a593Smuzhiyun static const __u8 page4_7311[] = {
142*4882a593Smuzhiyun SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
143*4882a593Smuzhiyun 0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62,
144*4882a593Smuzhiyun 0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
145*4882a593Smuzhiyun 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP,
146*4882a593Smuzhiyun SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68,
147*4882a593Smuzhiyun 0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00,
148*4882a593Smuzhiyun 0x23, 0x28, 0x04, 0x11, 0x00, 0x00
149*4882a593Smuzhiyun };
150*4882a593Smuzhiyun
reg_w_buf(struct gspca_dev * gspca_dev,__u8 index,const u8 * buffer,int len)151*4882a593Smuzhiyun static void reg_w_buf(struct gspca_dev *gspca_dev,
152*4882a593Smuzhiyun __u8 index,
153*4882a593Smuzhiyun const u8 *buffer, int len)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun int ret;
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun if (gspca_dev->usb_err < 0)
158*4882a593Smuzhiyun return;
159*4882a593Smuzhiyun memcpy(gspca_dev->usb_buf, buffer, len);
160*4882a593Smuzhiyun ret = usb_control_msg(gspca_dev->dev,
161*4882a593Smuzhiyun usb_sndctrlpipe(gspca_dev->dev, 0),
162*4882a593Smuzhiyun 0, /* request */
163*4882a593Smuzhiyun USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
164*4882a593Smuzhiyun 0, /* value */
165*4882a593Smuzhiyun index, gspca_dev->usb_buf, len,
166*4882a593Smuzhiyun 500);
167*4882a593Smuzhiyun if (ret < 0) {
168*4882a593Smuzhiyun pr_err("reg_w_buf() failed index 0x%02x, error %d\n",
169*4882a593Smuzhiyun index, ret);
170*4882a593Smuzhiyun gspca_dev->usb_err = ret;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun
reg_w(struct gspca_dev * gspca_dev,__u8 index,__u8 value)175*4882a593Smuzhiyun static void reg_w(struct gspca_dev *gspca_dev,
176*4882a593Smuzhiyun __u8 index,
177*4882a593Smuzhiyun __u8 value)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun int ret;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun if (gspca_dev->usb_err < 0)
182*4882a593Smuzhiyun return;
183*4882a593Smuzhiyun gspca_dev->usb_buf[0] = value;
184*4882a593Smuzhiyun ret = usb_control_msg(gspca_dev->dev,
185*4882a593Smuzhiyun usb_sndctrlpipe(gspca_dev->dev, 0),
186*4882a593Smuzhiyun 0, /* request */
187*4882a593Smuzhiyun USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
188*4882a593Smuzhiyun 0, index, gspca_dev->usb_buf, 1,
189*4882a593Smuzhiyun 500);
190*4882a593Smuzhiyun if (ret < 0) {
191*4882a593Smuzhiyun pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n",
192*4882a593Smuzhiyun index, value, ret);
193*4882a593Smuzhiyun gspca_dev->usb_err = ret;
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
reg_w_seq(struct gspca_dev * gspca_dev,const __u8 * seq,int len)197*4882a593Smuzhiyun static void reg_w_seq(struct gspca_dev *gspca_dev,
198*4882a593Smuzhiyun const __u8 *seq, int len)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun while (--len >= 0) {
201*4882a593Smuzhiyun reg_w(gspca_dev, seq[0], seq[1]);
202*4882a593Smuzhiyun seq += 2;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun /* load the beginning of a page */
reg_w_page(struct gspca_dev * gspca_dev,const __u8 * page,int len)207*4882a593Smuzhiyun static void reg_w_page(struct gspca_dev *gspca_dev,
208*4882a593Smuzhiyun const __u8 *page, int len)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun int index;
211*4882a593Smuzhiyun int ret = 0;
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun if (gspca_dev->usb_err < 0)
214*4882a593Smuzhiyun return;
215*4882a593Smuzhiyun for (index = 0; index < len; index++) {
216*4882a593Smuzhiyun if (page[index] == SKIP) /* skip this index */
217*4882a593Smuzhiyun continue;
218*4882a593Smuzhiyun gspca_dev->usb_buf[0] = page[index];
219*4882a593Smuzhiyun ret = usb_control_msg(gspca_dev->dev,
220*4882a593Smuzhiyun usb_sndctrlpipe(gspca_dev->dev, 0),
221*4882a593Smuzhiyun 0, /* request */
222*4882a593Smuzhiyun USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
223*4882a593Smuzhiyun 0, index, gspca_dev->usb_buf, 1,
224*4882a593Smuzhiyun 500);
225*4882a593Smuzhiyun if (ret < 0) {
226*4882a593Smuzhiyun pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n",
227*4882a593Smuzhiyun index, page[index], ret);
228*4882a593Smuzhiyun gspca_dev->usb_err = ret;
229*4882a593Smuzhiyun break;
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun /* output a variable sequence */
reg_w_var(struct gspca_dev * gspca_dev,const __u8 * seq,const __u8 * page4,unsigned int page4_len)235*4882a593Smuzhiyun static void reg_w_var(struct gspca_dev *gspca_dev,
236*4882a593Smuzhiyun const __u8 *seq,
237*4882a593Smuzhiyun const __u8 *page4, unsigned int page4_len)
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun int index, len;
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun for (;;) {
242*4882a593Smuzhiyun index = *seq++;
243*4882a593Smuzhiyun len = *seq++;
244*4882a593Smuzhiyun switch (len) {
245*4882a593Smuzhiyun case END_OF_SEQUENCE:
246*4882a593Smuzhiyun return;
247*4882a593Smuzhiyun case LOAD_PAGE4:
248*4882a593Smuzhiyun reg_w_page(gspca_dev, page4, page4_len);
249*4882a593Smuzhiyun break;
250*4882a593Smuzhiyun default:
251*4882a593Smuzhiyun if (len > USB_BUF_SZ) {
252*4882a593Smuzhiyun gspca_err(gspca_dev, "Incorrect variable sequence\n");
253*4882a593Smuzhiyun return;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun while (len > 0) {
256*4882a593Smuzhiyun if (len < 8) {
257*4882a593Smuzhiyun reg_w_buf(gspca_dev,
258*4882a593Smuzhiyun index, seq, len);
259*4882a593Smuzhiyun seq += len;
260*4882a593Smuzhiyun break;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun reg_w_buf(gspca_dev, index, seq, 8);
263*4882a593Smuzhiyun seq += 8;
264*4882a593Smuzhiyun index += 8;
265*4882a593Smuzhiyun len -= 8;
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun /* not reached */
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun /* this function is called at probe time for pac7311 */
sd_config(struct gspca_dev * gspca_dev,const struct usb_device_id * id)273*4882a593Smuzhiyun static int sd_config(struct gspca_dev *gspca_dev,
274*4882a593Smuzhiyun const struct usb_device_id *id)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun struct cam *cam = &gspca_dev->cam;
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun cam->cam_mode = vga_mode;
279*4882a593Smuzhiyun cam->nmodes = ARRAY_SIZE(vga_mode);
280*4882a593Smuzhiyun cam->input_flags = V4L2_IN_ST_VFLIP;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun return 0;
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun
setcontrast(struct gspca_dev * gspca_dev,s32 val)285*4882a593Smuzhiyun static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
286*4882a593Smuzhiyun {
287*4882a593Smuzhiyun reg_w(gspca_dev, 0xff, 0x04);
288*4882a593Smuzhiyun reg_w(gspca_dev, 0x10, val);
289*4882a593Smuzhiyun /* load registers to sensor (Bit 0, auto clear) */
290*4882a593Smuzhiyun reg_w(gspca_dev, 0x11, 0x01);
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
setgain(struct gspca_dev * gspca_dev,s32 val)293*4882a593Smuzhiyun static void setgain(struct gspca_dev *gspca_dev, s32 val)
294*4882a593Smuzhiyun {
295*4882a593Smuzhiyun reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
296*4882a593Smuzhiyun reg_w(gspca_dev, 0x0e, 0x00);
297*4882a593Smuzhiyun reg_w(gspca_dev, 0x0f, gspca_dev->gain->maximum - val + 1);
298*4882a593Smuzhiyun
299*4882a593Smuzhiyun /* load registers to sensor (Bit 0, auto clear) */
300*4882a593Smuzhiyun reg_w(gspca_dev, 0x11, 0x01);
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
setexposure(struct gspca_dev * gspca_dev,s32 val)303*4882a593Smuzhiyun static void setexposure(struct gspca_dev *gspca_dev, s32 val)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
306*4882a593Smuzhiyun reg_w(gspca_dev, 0x02, val);
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun /* load registers to sensor (Bit 0, auto clear) */
309*4882a593Smuzhiyun reg_w(gspca_dev, 0x11, 0x01);
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun /*
312*4882a593Smuzhiyun * Page 1 register 8 must always be 0x08 except when not in
313*4882a593Smuzhiyun * 640x480 mode and page 4 reg 2 <= 3 then it must be 9
314*4882a593Smuzhiyun */
315*4882a593Smuzhiyun reg_w(gspca_dev, 0xff, 0x01);
316*4882a593Smuzhiyun if (gspca_dev->pixfmt.width != 640 && val <= 3)
317*4882a593Smuzhiyun reg_w(gspca_dev, 0x08, 0x09);
318*4882a593Smuzhiyun else
319*4882a593Smuzhiyun reg_w(gspca_dev, 0x08, 0x08);
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun /*
322*4882a593Smuzhiyun * Page1 register 80 sets the compression balance, normally we
323*4882a593Smuzhiyun * want / use 0x1c, but for 640x480@30fps we must allow the
324*4882a593Smuzhiyun * camera to use higher compression or we may run out of
325*4882a593Smuzhiyun * bandwidth.
326*4882a593Smuzhiyun */
327*4882a593Smuzhiyun if (gspca_dev->pixfmt.width == 640 && val == 2)
328*4882a593Smuzhiyun reg_w(gspca_dev, 0x80, 0x01);
329*4882a593Smuzhiyun else
330*4882a593Smuzhiyun reg_w(gspca_dev, 0x80, 0x1c);
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun /* load registers to sensor (Bit 0, auto clear) */
333*4882a593Smuzhiyun reg_w(gspca_dev, 0x11, 0x01);
334*4882a593Smuzhiyun }
335*4882a593Smuzhiyun
sethvflip(struct gspca_dev * gspca_dev,s32 hflip,s32 vflip)336*4882a593Smuzhiyun static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
337*4882a593Smuzhiyun {
338*4882a593Smuzhiyun __u8 data;
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
341*4882a593Smuzhiyun data = (hflip ? 0x04 : 0x00) |
342*4882a593Smuzhiyun (vflip ? 0x08 : 0x00);
343*4882a593Smuzhiyun reg_w(gspca_dev, 0x21, data);
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun /* load registers to sensor (Bit 0, auto clear) */
346*4882a593Smuzhiyun reg_w(gspca_dev, 0x11, 0x01);
347*4882a593Smuzhiyun }
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun /* this function is called at probe and resume time for pac7311 */
sd_init(struct gspca_dev * gspca_dev)350*4882a593Smuzhiyun static int sd_init(struct gspca_dev *gspca_dev)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2);
353*4882a593Smuzhiyun return gspca_dev->usb_err;
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun
sd_s_ctrl(struct v4l2_ctrl * ctrl)356*4882a593Smuzhiyun static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
357*4882a593Smuzhiyun {
358*4882a593Smuzhiyun struct gspca_dev *gspca_dev =
359*4882a593Smuzhiyun container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
360*4882a593Smuzhiyun struct sd *sd = (struct sd *)gspca_dev;
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun gspca_dev->usb_err = 0;
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
365*4882a593Smuzhiyun /* when switching to autogain set defaults to make sure
366*4882a593Smuzhiyun we are on a valid point of the autogain gain /
367*4882a593Smuzhiyun exposure knee graph, and give this change time to
368*4882a593Smuzhiyun take effect before doing autogain. */
369*4882a593Smuzhiyun gspca_dev->exposure->val = PAC7311_EXPOSURE_DEFAULT;
370*4882a593Smuzhiyun gspca_dev->gain->val = PAC7311_GAIN_DEFAULT;
371*4882a593Smuzhiyun sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun if (!gspca_dev->streaming)
375*4882a593Smuzhiyun return 0;
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun switch (ctrl->id) {
378*4882a593Smuzhiyun case V4L2_CID_CONTRAST:
379*4882a593Smuzhiyun setcontrast(gspca_dev, ctrl->val);
380*4882a593Smuzhiyun break;
381*4882a593Smuzhiyun case V4L2_CID_AUTOGAIN:
382*4882a593Smuzhiyun if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
383*4882a593Smuzhiyun setexposure(gspca_dev, gspca_dev->exposure->val);
384*4882a593Smuzhiyun if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
385*4882a593Smuzhiyun setgain(gspca_dev, gspca_dev->gain->val);
386*4882a593Smuzhiyun break;
387*4882a593Smuzhiyun case V4L2_CID_HFLIP:
388*4882a593Smuzhiyun sethvflip(gspca_dev, sd->hflip->val, 1);
389*4882a593Smuzhiyun break;
390*4882a593Smuzhiyun default:
391*4882a593Smuzhiyun return -EINVAL;
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun return gspca_dev->usb_err;
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun static const struct v4l2_ctrl_ops sd_ctrl_ops = {
397*4882a593Smuzhiyun .s_ctrl = sd_s_ctrl,
398*4882a593Smuzhiyun };
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun /* this function is called at probe time */
sd_init_controls(struct gspca_dev * gspca_dev)401*4882a593Smuzhiyun static int sd_init_controls(struct gspca_dev *gspca_dev)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
404*4882a593Smuzhiyun struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun gspca_dev->vdev.ctrl_handler = hdl;
407*4882a593Smuzhiyun v4l2_ctrl_handler_init(hdl, 5);
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
410*4882a593Smuzhiyun V4L2_CID_CONTRAST, 0, 15, 1, 7);
411*4882a593Smuzhiyun gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
412*4882a593Smuzhiyun V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
413*4882a593Smuzhiyun gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
414*4882a593Smuzhiyun V4L2_CID_EXPOSURE, 2, 63, 1,
415*4882a593Smuzhiyun PAC7311_EXPOSURE_DEFAULT);
416*4882a593Smuzhiyun gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
417*4882a593Smuzhiyun V4L2_CID_GAIN, 0, 244, 1,
418*4882a593Smuzhiyun PAC7311_GAIN_DEFAULT);
419*4882a593Smuzhiyun sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
420*4882a593Smuzhiyun V4L2_CID_HFLIP, 0, 1, 1, 0);
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun if (hdl->error) {
423*4882a593Smuzhiyun pr_err("Could not initialize controls\n");
424*4882a593Smuzhiyun return hdl->error;
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
428*4882a593Smuzhiyun return 0;
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun
431*4882a593Smuzhiyun /* -- start the camera -- */
sd_start(struct gspca_dev * gspca_dev)432*4882a593Smuzhiyun static int sd_start(struct gspca_dev *gspca_dev)
433*4882a593Smuzhiyun {
434*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun sd->sof_read = 0;
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun reg_w_var(gspca_dev, start_7311,
439*4882a593Smuzhiyun page4_7311, sizeof(page4_7311));
440*4882a593Smuzhiyun setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast));
441*4882a593Smuzhiyun setgain(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->gain));
442*4882a593Smuzhiyun setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
443*4882a593Smuzhiyun sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), 1);
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun /* set correct resolution */
446*4882a593Smuzhiyun switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
447*4882a593Smuzhiyun case 2: /* 160x120 */
448*4882a593Smuzhiyun reg_w(gspca_dev, 0xff, 0x01);
449*4882a593Smuzhiyun reg_w(gspca_dev, 0x17, 0x20);
450*4882a593Smuzhiyun reg_w(gspca_dev, 0x87, 0x10);
451*4882a593Smuzhiyun break;
452*4882a593Smuzhiyun case 1: /* 320x240 */
453*4882a593Smuzhiyun reg_w(gspca_dev, 0xff, 0x01);
454*4882a593Smuzhiyun reg_w(gspca_dev, 0x17, 0x30);
455*4882a593Smuzhiyun reg_w(gspca_dev, 0x87, 0x11);
456*4882a593Smuzhiyun break;
457*4882a593Smuzhiyun case 0: /* 640x480 */
458*4882a593Smuzhiyun reg_w(gspca_dev, 0xff, 0x01);
459*4882a593Smuzhiyun reg_w(gspca_dev, 0x17, 0x00);
460*4882a593Smuzhiyun reg_w(gspca_dev, 0x87, 0x12);
461*4882a593Smuzhiyun break;
462*4882a593Smuzhiyun }
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun sd->sof_read = 0;
465*4882a593Smuzhiyun sd->autogain_ignore_frames = 0;
466*4882a593Smuzhiyun atomic_set(&sd->avg_lum, -1);
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun /* start stream */
469*4882a593Smuzhiyun reg_w(gspca_dev, 0xff, 0x01);
470*4882a593Smuzhiyun reg_w(gspca_dev, 0x78, 0x05);
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun return gspca_dev->usb_err;
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun
sd_stopN(struct gspca_dev * gspca_dev)475*4882a593Smuzhiyun static void sd_stopN(struct gspca_dev *gspca_dev)
476*4882a593Smuzhiyun {
477*4882a593Smuzhiyun reg_w(gspca_dev, 0xff, 0x04);
478*4882a593Smuzhiyun reg_w(gspca_dev, 0x27, 0x80);
479*4882a593Smuzhiyun reg_w(gspca_dev, 0x28, 0xca);
480*4882a593Smuzhiyun reg_w(gspca_dev, 0x29, 0x53);
481*4882a593Smuzhiyun reg_w(gspca_dev, 0x2a, 0x0e);
482*4882a593Smuzhiyun reg_w(gspca_dev, 0xff, 0x01);
483*4882a593Smuzhiyun reg_w(gspca_dev, 0x3e, 0x20);
484*4882a593Smuzhiyun reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
485*4882a593Smuzhiyun reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
486*4882a593Smuzhiyun reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
487*4882a593Smuzhiyun }
488*4882a593Smuzhiyun
do_autogain(struct gspca_dev * gspca_dev)489*4882a593Smuzhiyun static void do_autogain(struct gspca_dev *gspca_dev)
490*4882a593Smuzhiyun {
491*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
492*4882a593Smuzhiyun int avg_lum = atomic_read(&sd->avg_lum);
493*4882a593Smuzhiyun int desired_lum, deadzone;
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun if (avg_lum == -1)
496*4882a593Smuzhiyun return;
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun desired_lum = 170;
499*4882a593Smuzhiyun deadzone = 20;
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun if (sd->autogain_ignore_frames > 0)
502*4882a593Smuzhiyun sd->autogain_ignore_frames--;
503*4882a593Smuzhiyun else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
504*4882a593Smuzhiyun desired_lum, deadzone))
505*4882a593Smuzhiyun sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
506*4882a593Smuzhiyun }
507*4882a593Smuzhiyun
508*4882a593Smuzhiyun /* JPEG header, part 1 */
509*4882a593Smuzhiyun static const unsigned char pac_jpeg_header1[] = {
510*4882a593Smuzhiyun 0xff, 0xd8, /* SOI: Start of Image */
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
513*4882a593Smuzhiyun 0x00, 0x11, /* length = 17 bytes (including this length field) */
514*4882a593Smuzhiyun 0x08 /* Precision: 8 */
515*4882a593Smuzhiyun /* 2 bytes is placed here: number of image lines */
516*4882a593Smuzhiyun /* 2 bytes is placed here: samples per line */
517*4882a593Smuzhiyun };
518*4882a593Smuzhiyun
519*4882a593Smuzhiyun /* JPEG header, continued */
520*4882a593Smuzhiyun static const unsigned char pac_jpeg_header2[] = {
521*4882a593Smuzhiyun 0x03, /* Number of image components: 3 */
522*4882a593Smuzhiyun 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
523*4882a593Smuzhiyun 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
524*4882a593Smuzhiyun 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
525*4882a593Smuzhiyun
526*4882a593Smuzhiyun 0xff, 0xda, /* SOS: Start Of Scan */
527*4882a593Smuzhiyun 0x00, 0x0c, /* length = 12 bytes (including this length field) */
528*4882a593Smuzhiyun 0x03, /* number of components: 3 */
529*4882a593Smuzhiyun 0x01, 0x00, /* selector 1, table 0x00 */
530*4882a593Smuzhiyun 0x02, 0x11, /* selector 2, table 0x11 */
531*4882a593Smuzhiyun 0x03, 0x11, /* selector 3, table 0x11 */
532*4882a593Smuzhiyun 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
533*4882a593Smuzhiyun 0x00 /* Successive approximation: 0 */
534*4882a593Smuzhiyun };
535*4882a593Smuzhiyun
pac_start_frame(struct gspca_dev * gspca_dev,__u16 lines,__u16 samples_per_line)536*4882a593Smuzhiyun static void pac_start_frame(struct gspca_dev *gspca_dev,
537*4882a593Smuzhiyun __u16 lines, __u16 samples_per_line)
538*4882a593Smuzhiyun {
539*4882a593Smuzhiyun unsigned char tmpbuf[4];
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun gspca_frame_add(gspca_dev, FIRST_PACKET,
542*4882a593Smuzhiyun pac_jpeg_header1, sizeof(pac_jpeg_header1));
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun tmpbuf[0] = lines >> 8;
545*4882a593Smuzhiyun tmpbuf[1] = lines & 0xff;
546*4882a593Smuzhiyun tmpbuf[2] = samples_per_line >> 8;
547*4882a593Smuzhiyun tmpbuf[3] = samples_per_line & 0xff;
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun gspca_frame_add(gspca_dev, INTER_PACKET,
550*4882a593Smuzhiyun tmpbuf, sizeof(tmpbuf));
551*4882a593Smuzhiyun gspca_frame_add(gspca_dev, INTER_PACKET,
552*4882a593Smuzhiyun pac_jpeg_header2, sizeof(pac_jpeg_header2));
553*4882a593Smuzhiyun }
554*4882a593Smuzhiyun
555*4882a593Smuzhiyun /* this function is run at interrupt level */
sd_pkt_scan(struct gspca_dev * gspca_dev,u8 * data,int len)556*4882a593Smuzhiyun static void sd_pkt_scan(struct gspca_dev *gspca_dev,
557*4882a593Smuzhiyun u8 *data, /* isoc packet */
558*4882a593Smuzhiyun int len) /* iso packet length */
559*4882a593Smuzhiyun {
560*4882a593Smuzhiyun struct sd *sd = (struct sd *) gspca_dev;
561*4882a593Smuzhiyun u8 *image;
562*4882a593Smuzhiyun unsigned char *sof;
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len);
565*4882a593Smuzhiyun if (sof) {
566*4882a593Smuzhiyun int n, lum_offset, footer_length;
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun /*
569*4882a593Smuzhiyun * 6 bytes after the FF D9 EOF marker a number of lumination
570*4882a593Smuzhiyun * bytes are send corresponding to different parts of the
571*4882a593Smuzhiyun * image, the 14th and 15th byte after the EOF seem to
572*4882a593Smuzhiyun * correspond to the center of the image.
573*4882a593Smuzhiyun */
574*4882a593Smuzhiyun lum_offset = 24 + sizeof pac_sof_marker;
575*4882a593Smuzhiyun footer_length = 26;
576*4882a593Smuzhiyun
577*4882a593Smuzhiyun /* Finish decoding current frame */
578*4882a593Smuzhiyun n = (sof - data) - (footer_length + sizeof pac_sof_marker);
579*4882a593Smuzhiyun if (n < 0) {
580*4882a593Smuzhiyun gspca_dev->image_len += n;
581*4882a593Smuzhiyun n = 0;
582*4882a593Smuzhiyun } else {
583*4882a593Smuzhiyun gspca_frame_add(gspca_dev, INTER_PACKET, data, n);
584*4882a593Smuzhiyun }
585*4882a593Smuzhiyun image = gspca_dev->image;
586*4882a593Smuzhiyun if (image != NULL
587*4882a593Smuzhiyun && image[gspca_dev->image_len - 2] == 0xff
588*4882a593Smuzhiyun && image[gspca_dev->image_len - 1] == 0xd9)
589*4882a593Smuzhiyun gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun n = sof - data;
592*4882a593Smuzhiyun len -= n;
593*4882a593Smuzhiyun data = sof;
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun /* Get average lumination */
596*4882a593Smuzhiyun if (gspca_dev->last_packet_type == LAST_PACKET &&
597*4882a593Smuzhiyun n >= lum_offset)
598*4882a593Smuzhiyun atomic_set(&sd->avg_lum, data[-lum_offset] +
599*4882a593Smuzhiyun data[-lum_offset + 1]);
600*4882a593Smuzhiyun else
601*4882a593Smuzhiyun atomic_set(&sd->avg_lum, -1);
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun /* Start the new frame with the jpeg header */
604*4882a593Smuzhiyun pac_start_frame(gspca_dev,
605*4882a593Smuzhiyun gspca_dev->pixfmt.height, gspca_dev->pixfmt.width);
606*4882a593Smuzhiyun }
607*4882a593Smuzhiyun gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
608*4882a593Smuzhiyun }
609*4882a593Smuzhiyun
610*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_INPUT)
sd_int_pkt_scan(struct gspca_dev * gspca_dev,u8 * data,int len)611*4882a593Smuzhiyun static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
612*4882a593Smuzhiyun u8 *data, /* interrupt packet data */
613*4882a593Smuzhiyun int len) /* interrupt packet length */
614*4882a593Smuzhiyun {
615*4882a593Smuzhiyun int ret = -EINVAL;
616*4882a593Smuzhiyun u8 data0, data1;
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun if (len == 2) {
619*4882a593Smuzhiyun data0 = data[0];
620*4882a593Smuzhiyun data1 = data[1];
621*4882a593Smuzhiyun if ((data0 == 0x00 && data1 == 0x11) ||
622*4882a593Smuzhiyun (data0 == 0x22 && data1 == 0x33) ||
623*4882a593Smuzhiyun (data0 == 0x44 && data1 == 0x55) ||
624*4882a593Smuzhiyun (data0 == 0x66 && data1 == 0x77) ||
625*4882a593Smuzhiyun (data0 == 0x88 && data1 == 0x99) ||
626*4882a593Smuzhiyun (data0 == 0xaa && data1 == 0xbb) ||
627*4882a593Smuzhiyun (data0 == 0xcc && data1 == 0xdd) ||
628*4882a593Smuzhiyun (data0 == 0xee && data1 == 0xff)) {
629*4882a593Smuzhiyun input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
630*4882a593Smuzhiyun input_sync(gspca_dev->input_dev);
631*4882a593Smuzhiyun input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
632*4882a593Smuzhiyun input_sync(gspca_dev->input_dev);
633*4882a593Smuzhiyun ret = 0;
634*4882a593Smuzhiyun }
635*4882a593Smuzhiyun }
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun return ret;
638*4882a593Smuzhiyun }
639*4882a593Smuzhiyun #endif
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun static const struct sd_desc sd_desc = {
642*4882a593Smuzhiyun .name = MODULE_NAME,
643*4882a593Smuzhiyun .config = sd_config,
644*4882a593Smuzhiyun .init = sd_init,
645*4882a593Smuzhiyun .init_controls = sd_init_controls,
646*4882a593Smuzhiyun .start = sd_start,
647*4882a593Smuzhiyun .stopN = sd_stopN,
648*4882a593Smuzhiyun .pkt_scan = sd_pkt_scan,
649*4882a593Smuzhiyun .dq_callback = do_autogain,
650*4882a593Smuzhiyun #if IS_ENABLED(CONFIG_INPUT)
651*4882a593Smuzhiyun .int_pkt_scan = sd_int_pkt_scan,
652*4882a593Smuzhiyun #endif
653*4882a593Smuzhiyun };
654*4882a593Smuzhiyun
655*4882a593Smuzhiyun /* -- module initialisation -- */
656*4882a593Smuzhiyun static const struct usb_device_id device_table[] = {
657*4882a593Smuzhiyun {USB_DEVICE(0x093a, 0x2600)},
658*4882a593Smuzhiyun {USB_DEVICE(0x093a, 0x2601)},
659*4882a593Smuzhiyun {USB_DEVICE(0x093a, 0x2603)},
660*4882a593Smuzhiyun {USB_DEVICE(0x093a, 0x2608)},
661*4882a593Smuzhiyun {USB_DEVICE(0x093a, 0x260e)},
662*4882a593Smuzhiyun {USB_DEVICE(0x093a, 0x260f)},
663*4882a593Smuzhiyun {}
664*4882a593Smuzhiyun };
665*4882a593Smuzhiyun MODULE_DEVICE_TABLE(usb, device_table);
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun /* -- device connect -- */
sd_probe(struct usb_interface * intf,const struct usb_device_id * id)668*4882a593Smuzhiyun static int sd_probe(struct usb_interface *intf,
669*4882a593Smuzhiyun const struct usb_device_id *id)
670*4882a593Smuzhiyun {
671*4882a593Smuzhiyun return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
672*4882a593Smuzhiyun THIS_MODULE);
673*4882a593Smuzhiyun }
674*4882a593Smuzhiyun
675*4882a593Smuzhiyun static struct usb_driver sd_driver = {
676*4882a593Smuzhiyun .name = MODULE_NAME,
677*4882a593Smuzhiyun .id_table = device_table,
678*4882a593Smuzhiyun .probe = sd_probe,
679*4882a593Smuzhiyun .disconnect = gspca_disconnect,
680*4882a593Smuzhiyun #ifdef CONFIG_PM
681*4882a593Smuzhiyun .suspend = gspca_suspend,
682*4882a593Smuzhiyun .resume = gspca_resume,
683*4882a593Smuzhiyun .reset_resume = gspca_resume,
684*4882a593Smuzhiyun #endif
685*4882a593Smuzhiyun };
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun module_usb_driver(sd_driver);
688