1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Sunplus spca504(abc) spca533 spca536 library |
4 | * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr |
5 | * |
6 | * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | |
11 | #define MODULE_NAME "sunplus" |
12 | |
13 | #include "gspca.h" |
14 | #include "jpeg.h" |
15 | |
16 | MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>" ); |
17 | MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver" ); |
18 | MODULE_LICENSE("GPL" ); |
19 | |
20 | #define QUALITY 85 |
21 | |
22 | /* specific webcam descriptor */ |
23 | struct sd { |
24 | struct gspca_dev gspca_dev; /* !! must be the first item */ |
25 | |
26 | bool autogain; |
27 | |
28 | u8 bridge; |
29 | #define BRIDGE_SPCA504 0 |
30 | #define BRIDGE_SPCA504B 1 |
31 | #define BRIDGE_SPCA504C 2 |
32 | #define BRIDGE_SPCA533 3 |
33 | #define BRIDGE_SPCA536 4 |
34 | u8 subtype; |
35 | #define AiptekMiniPenCam13 1 |
36 | #define LogitechClickSmart420 2 |
37 | #define LogitechClickSmart820 3 |
38 | #define MegapixV4 4 |
39 | #define MegaImageVI 5 |
40 | |
41 | u8 jpeg_hdr[JPEG_HDR_SZ]; |
42 | }; |
43 | |
44 | static const struct v4l2_pix_format vga_mode[] = { |
45 | {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
46 | .bytesperline = 320, |
47 | .sizeimage = 320 * 240 * 3 / 8 + 590, |
48 | .colorspace = V4L2_COLORSPACE_JPEG, |
49 | .priv = 2}, |
50 | {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
51 | .bytesperline = 640, |
52 | .sizeimage = 640 * 480 * 3 / 8 + 590, |
53 | .colorspace = V4L2_COLORSPACE_JPEG, |
54 | .priv = 1}, |
55 | }; |
56 | |
57 | static const struct v4l2_pix_format custom_mode[] = { |
58 | {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
59 | .bytesperline = 320, |
60 | .sizeimage = 320 * 240 * 3 / 8 + 590, |
61 | .colorspace = V4L2_COLORSPACE_JPEG, |
62 | .priv = 2}, |
63 | {464, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
64 | .bytesperline = 464, |
65 | .sizeimage = 464 * 480 * 3 / 8 + 590, |
66 | .colorspace = V4L2_COLORSPACE_JPEG, |
67 | .priv = 1}, |
68 | }; |
69 | |
70 | static const struct v4l2_pix_format vga_mode2[] = { |
71 | {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
72 | .bytesperline = 176, |
73 | .sizeimage = 176 * 144 * 3 / 8 + 590, |
74 | .colorspace = V4L2_COLORSPACE_JPEG, |
75 | .priv = 4}, |
76 | {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
77 | .bytesperline = 320, |
78 | .sizeimage = 320 * 240 * 3 / 8 + 590, |
79 | .colorspace = V4L2_COLORSPACE_JPEG, |
80 | .priv = 3}, |
81 | {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
82 | .bytesperline = 352, |
83 | .sizeimage = 352 * 288 * 3 / 8 + 590, |
84 | .colorspace = V4L2_COLORSPACE_JPEG, |
85 | .priv = 2}, |
86 | {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
87 | .bytesperline = 640, |
88 | .sizeimage = 640 * 480 * 3 / 8 + 590, |
89 | .colorspace = V4L2_COLORSPACE_JPEG, |
90 | .priv = 1}, |
91 | }; |
92 | |
93 | #define SPCA50X_OFFSET_DATA 10 |
94 | #define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3 |
95 | #define SPCA504_PCCAM600_OFFSET_COMPRESS 4 |
96 | #define SPCA504_PCCAM600_OFFSET_MODE 5 |
97 | #define SPCA504_PCCAM600_OFFSET_DATA 14 |
98 | /* Frame packet header offsets for the spca533 */ |
99 | #define SPCA533_OFFSET_DATA 16 |
100 | #define SPCA533_OFFSET_FRAMSEQ 15 |
101 | /* Frame packet header offsets for the spca536 */ |
102 | #define SPCA536_OFFSET_DATA 4 |
103 | #define SPCA536_OFFSET_FRAMSEQ 1 |
104 | |
105 | struct cmd { |
106 | u8 req; |
107 | u16 val; |
108 | u16 idx; |
109 | }; |
110 | |
111 | /* Initialisation data for the Creative PC-CAM 600 */ |
112 | static const struct cmd spca504_pccam600_init_data[] = { |
113 | /* {0xa0, 0x0000, 0x0503}, * capture mode */ |
114 | {0x00, 0x0000, 0x2000}, |
115 | {0x00, 0x0013, 0x2301}, |
116 | {0x00, 0x0003, 0x2000}, |
117 | {0x00, 0x0001, 0x21ac}, |
118 | {0x00, 0x0001, 0x21a6}, |
119 | {0x00, 0x0000, 0x21a7}, /* brightness */ |
120 | {0x00, 0x0020, 0x21a8}, /* contrast */ |
121 | {0x00, 0x0001, 0x21ac}, /* sat/hue */ |
122 | {0x00, 0x0000, 0x21ad}, /* hue */ |
123 | {0x00, 0x001a, 0x21ae}, /* saturation */ |
124 | {0x00, 0x0002, 0x21a3}, /* gamma */ |
125 | {0x30, 0x0154, 0x0008}, |
126 | {0x30, 0x0004, 0x0006}, |
127 | {0x30, 0x0258, 0x0009}, |
128 | {0x30, 0x0004, 0x0000}, |
129 | {0x30, 0x0093, 0x0004}, |
130 | {0x30, 0x0066, 0x0005}, |
131 | {0x00, 0x0000, 0x2000}, |
132 | {0x00, 0x0013, 0x2301}, |
133 | {0x00, 0x0003, 0x2000}, |
134 | {0x00, 0x0013, 0x2301}, |
135 | {0x00, 0x0003, 0x2000}, |
136 | }; |
137 | |
138 | /* Creative PC-CAM 600 specific open data, sent before using the |
139 | * generic initialisation data from spca504_open_data. |
140 | */ |
141 | static const struct cmd spca504_pccam600_open_data[] = { |
142 | {0x00, 0x0001, 0x2501}, |
143 | {0x20, 0x0500, 0x0001}, /* snapshot mode */ |
144 | {0x00, 0x0003, 0x2880}, |
145 | {0x00, 0x0001, 0x2881}, |
146 | }; |
147 | |
148 | /* Initialisation data for the logitech clicksmart 420 */ |
149 | static const struct cmd spca504A_clicksmart420_init_data[] = { |
150 | /* {0xa0, 0x0000, 0x0503}, * capture mode */ |
151 | {0x00, 0x0000, 0x2000}, |
152 | {0x00, 0x0013, 0x2301}, |
153 | {0x00, 0x0003, 0x2000}, |
154 | {0x00, 0x0001, 0x21ac}, |
155 | {0x00, 0x0001, 0x21a6}, |
156 | {0x00, 0x0000, 0x21a7}, /* brightness */ |
157 | {0x00, 0x0020, 0x21a8}, /* contrast */ |
158 | {0x00, 0x0001, 0x21ac}, /* sat/hue */ |
159 | {0x00, 0x0000, 0x21ad}, /* hue */ |
160 | {0x00, 0x001a, 0x21ae}, /* saturation */ |
161 | {0x00, 0x0002, 0x21a3}, /* gamma */ |
162 | {0x30, 0x0004, 0x000a}, |
163 | {0xb0, 0x0001, 0x0000}, |
164 | |
165 | {0xa1, 0x0080, 0x0001}, |
166 | {0x30, 0x0049, 0x0000}, |
167 | {0x30, 0x0060, 0x0005}, |
168 | {0x0c, 0x0004, 0x0000}, |
169 | {0x00, 0x0000, 0x0000}, |
170 | {0x00, 0x0000, 0x2000}, |
171 | {0x00, 0x0013, 0x2301}, |
172 | {0x00, 0x0003, 0x2000}, |
173 | }; |
174 | |
175 | /* clicksmart 420 open data ? */ |
176 | static const struct cmd spca504A_clicksmart420_open_data[] = { |
177 | {0x00, 0x0001, 0x2501}, |
178 | {0x20, 0x0502, 0x0000}, |
179 | {0x06, 0x0000, 0x0000}, |
180 | {0x00, 0x0004, 0x2880}, |
181 | {0x00, 0x0001, 0x2881}, |
182 | |
183 | {0xa0, 0x0000, 0x0503}, |
184 | }; |
185 | |
186 | static const u8 qtable_creative_pccam[2][64] = { |
187 | { /* Q-table Y-components */ |
188 | 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, |
189 | 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, |
190 | 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, |
191 | 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, |
192 | 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, |
193 | 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, |
194 | 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, |
195 | 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, |
196 | { /* Q-table C-components */ |
197 | 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, |
198 | 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, |
199 | 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
200 | 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
201 | 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
202 | 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
203 | 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
204 | 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} |
205 | }; |
206 | |
207 | /* FIXME: This Q-table is identical to the Creative PC-CAM one, |
208 | * except for one byte. Possibly a typo? |
209 | * NWG: 18/05/2003. |
210 | */ |
211 | static const u8 qtable_spca504_default[2][64] = { |
212 | { /* Q-table Y-components */ |
213 | 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, |
214 | 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, |
215 | 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, |
216 | 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, |
217 | 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, |
218 | 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, |
219 | 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, |
220 | 0x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e, |
221 | }, |
222 | { /* Q-table C-components */ |
223 | 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, |
224 | 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, |
225 | 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
226 | 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
227 | 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
228 | 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
229 | 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, |
230 | 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} |
231 | }; |
232 | |
233 | /* read <len> bytes to gspca_dev->usb_buf */ |
234 | static void reg_r(struct gspca_dev *gspca_dev, |
235 | u8 req, |
236 | u16 index, |
237 | u16 len) |
238 | { |
239 | int ret; |
240 | |
241 | if (len > USB_BUF_SZ) { |
242 | gspca_err(gspca_dev, "reg_r: buffer overflow\n" ); |
243 | return; |
244 | } |
245 | if (len == 0) { |
246 | gspca_err(gspca_dev, "reg_r: zero-length read\n" ); |
247 | return; |
248 | } |
249 | if (gspca_dev->usb_err < 0) |
250 | return; |
251 | ret = usb_control_msg(dev: gspca_dev->dev, |
252 | usb_rcvctrlpipe(gspca_dev->dev, 0), |
253 | request: req, |
254 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
255 | value: 0, /* value */ |
256 | index, |
257 | data: gspca_dev->usb_buf, size: len, |
258 | timeout: 500); |
259 | if (ret < 0) { |
260 | pr_err("reg_r err %d\n" , ret); |
261 | gspca_dev->usb_err = ret; |
262 | /* |
263 | * Make sure the buffer is zeroed to avoid uninitialized |
264 | * values. |
265 | */ |
266 | memset(gspca_dev->usb_buf, 0, USB_BUF_SZ); |
267 | } |
268 | } |
269 | |
270 | /* write one byte */ |
271 | static void reg_w_1(struct gspca_dev *gspca_dev, |
272 | u8 req, |
273 | u16 value, |
274 | u16 index, |
275 | u16 byte) |
276 | { |
277 | int ret; |
278 | |
279 | if (gspca_dev->usb_err < 0) |
280 | return; |
281 | gspca_dev->usb_buf[0] = byte; |
282 | ret = usb_control_msg(dev: gspca_dev->dev, |
283 | usb_sndctrlpipe(gspca_dev->dev, 0), |
284 | request: req, |
285 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
286 | value, index, |
287 | data: gspca_dev->usb_buf, size: 1, |
288 | timeout: 500); |
289 | if (ret < 0) { |
290 | pr_err("reg_w_1 err %d\n" , ret); |
291 | gspca_dev->usb_err = ret; |
292 | } |
293 | } |
294 | |
295 | /* write req / index / value */ |
296 | static void reg_w_riv(struct gspca_dev *gspca_dev, |
297 | u8 req, u16 index, u16 value) |
298 | { |
299 | struct usb_device *dev = gspca_dev->dev; |
300 | int ret; |
301 | |
302 | if (gspca_dev->usb_err < 0) |
303 | return; |
304 | ret = usb_control_msg(dev, |
305 | usb_sndctrlpipe(dev, 0), |
306 | request: req, |
307 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
308 | value, index, NULL, size: 0, timeout: 500); |
309 | if (ret < 0) { |
310 | pr_err("reg_w_riv err %d\n" , ret); |
311 | gspca_dev->usb_err = ret; |
312 | return; |
313 | } |
314 | gspca_dbg(gspca_dev, D_USBO, "reg_w_riv: 0x%02x,0x%04x:0x%04x\n" , |
315 | req, index, value); |
316 | } |
317 | |
318 | static void write_vector(struct gspca_dev *gspca_dev, |
319 | const struct cmd *data, int ncmds) |
320 | { |
321 | while (--ncmds >= 0) { |
322 | reg_w_riv(gspca_dev, req: data->req, index: data->idx, value: data->val); |
323 | data++; |
324 | } |
325 | } |
326 | |
327 | static void setup_qtable(struct gspca_dev *gspca_dev, |
328 | const u8 qtable[2][64]) |
329 | { |
330 | int i; |
331 | |
332 | /* loop over y components */ |
333 | for (i = 0; i < 64; i++) |
334 | reg_w_riv(gspca_dev, req: 0x00, index: 0x2800 + i, value: qtable[0][i]); |
335 | |
336 | /* loop over c components */ |
337 | for (i = 0; i < 64; i++) |
338 | reg_w_riv(gspca_dev, req: 0x00, index: 0x2840 + i, value: qtable[1][i]); |
339 | } |
340 | |
341 | static void spca504_acknowledged_command(struct gspca_dev *gspca_dev, |
342 | u8 req, u16 idx, u16 val) |
343 | { |
344 | reg_w_riv(gspca_dev, req, index: idx, value: val); |
345 | reg_r(gspca_dev, req: 0x01, index: 0x0001, len: 1); |
346 | gspca_dbg(gspca_dev, D_FRAM, "before wait 0x%04x\n" , |
347 | gspca_dev->usb_buf[0]); |
348 | reg_w_riv(gspca_dev, req, index: idx, value: val); |
349 | |
350 | msleep(msecs: 200); |
351 | reg_r(gspca_dev, req: 0x01, index: 0x0001, len: 1); |
352 | gspca_dbg(gspca_dev, D_FRAM, "after wait 0x%04x\n" , |
353 | gspca_dev->usb_buf[0]); |
354 | } |
355 | |
356 | static void spca504_read_info(struct gspca_dev *gspca_dev) |
357 | { |
358 | int i; |
359 | u8 info[6]; |
360 | |
361 | if (gspca_debug < D_STREAM) |
362 | return; |
363 | |
364 | for (i = 0; i < 6; i++) { |
365 | reg_r(gspca_dev, req: 0, index: i, len: 1); |
366 | info[i] = gspca_dev->usb_buf[0]; |
367 | } |
368 | gspca_dbg(gspca_dev, D_STREAM, |
369 | "Read info: %d %d %d %d %d %d. Should be 1,0,2,2,0,0\n" , |
370 | info[0], info[1], info[2], |
371 | info[3], info[4], info[5]); |
372 | } |
373 | |
374 | static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, |
375 | u8 req, |
376 | u16 idx, u16 val, u8 endcode, u8 count) |
377 | { |
378 | u16 status; |
379 | |
380 | reg_w_riv(gspca_dev, req, index: idx, value: val); |
381 | reg_r(gspca_dev, req: 0x01, index: 0x0001, len: 1); |
382 | if (gspca_dev->usb_err < 0) |
383 | return; |
384 | gspca_dbg(gspca_dev, D_FRAM, "Status 0x%02x Need 0x%02x\n" , |
385 | gspca_dev->usb_buf[0], endcode); |
386 | if (!count) |
387 | return; |
388 | count = 200; |
389 | while (--count > 0) { |
390 | msleep(msecs: 10); |
391 | /* gsmart mini2 write a each wait setting 1 ms is enough */ |
392 | /* reg_w_riv(gspca_dev, req, idx, val); */ |
393 | reg_r(gspca_dev, req: 0x01, index: 0x0001, len: 1); |
394 | status = gspca_dev->usb_buf[0]; |
395 | if (status == endcode) { |
396 | gspca_dbg(gspca_dev, D_FRAM, "status 0x%04x after wait %d\n" , |
397 | status, 200 - count); |
398 | break; |
399 | } |
400 | } |
401 | } |
402 | |
403 | static void spca504B_PollingDataReady(struct gspca_dev *gspca_dev) |
404 | { |
405 | int count = 10; |
406 | |
407 | while (--count > 0) { |
408 | reg_r(gspca_dev, req: 0x21, index: 0, len: 1); |
409 | if ((gspca_dev->usb_buf[0] & 0x01) == 0) |
410 | break; |
411 | msleep(msecs: 10); |
412 | } |
413 | } |
414 | |
415 | static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) |
416 | { |
417 | int count = 50; |
418 | |
419 | while (--count > 0) { |
420 | reg_r(gspca_dev, req: 0x21, index: 1, len: 1); |
421 | if (gspca_dev->usb_buf[0] != 0) { |
422 | reg_w_1(gspca_dev, req: 0x21, value: 0, index: 1, byte: 0); |
423 | reg_r(gspca_dev, req: 0x21, index: 1, len: 1); |
424 | spca504B_PollingDataReady(gspca_dev); |
425 | break; |
426 | } |
427 | msleep(msecs: 10); |
428 | } |
429 | } |
430 | |
431 | static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) |
432 | { |
433 | u8 *data; |
434 | |
435 | if (gspca_debug < D_STREAM) |
436 | return; |
437 | |
438 | data = gspca_dev->usb_buf; |
439 | reg_r(gspca_dev, req: 0x20, index: 0, len: 5); |
440 | gspca_dbg(gspca_dev, D_STREAM, "FirmWare: %d %d %d %d %d\n" , |
441 | data[0], data[1], data[2], data[3], data[4]); |
442 | reg_r(gspca_dev, req: 0x23, index: 0, len: 64); |
443 | reg_r(gspca_dev, req: 0x23, index: 1, len: 64); |
444 | } |
445 | |
446 | static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) |
447 | { |
448 | struct sd *sd = (struct sd *) gspca_dev; |
449 | u8 Size; |
450 | |
451 | Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; |
452 | switch (sd->bridge) { |
453 | case BRIDGE_SPCA533: |
454 | reg_w_riv(gspca_dev, req: 0x31, index: 0, value: 0); |
455 | spca504B_WaitCmdStatus(gspca_dev); |
456 | spca504B_PollingDataReady(gspca_dev); |
457 | spca50x_GetFirmware(gspca_dev); |
458 | |
459 | reg_w_1(gspca_dev, req: 0x24, value: 0, index: 8, byte: 2); /* type */ |
460 | reg_r(gspca_dev, req: 0x24, index: 8, len: 1); |
461 | |
462 | reg_w_1(gspca_dev, req: 0x25, value: 0, index: 4, byte: Size); |
463 | reg_r(gspca_dev, req: 0x25, index: 4, len: 1); /* size */ |
464 | spca504B_PollingDataReady(gspca_dev); |
465 | |
466 | /* Init the cam width height with some values get on init ? */ |
467 | reg_w_riv(gspca_dev, req: 0x31, index: 0x0004, value: 0x00); |
468 | spca504B_WaitCmdStatus(gspca_dev); |
469 | spca504B_PollingDataReady(gspca_dev); |
470 | break; |
471 | default: |
472 | /* case BRIDGE_SPCA504B: */ |
473 | /* case BRIDGE_SPCA536: */ |
474 | reg_w_1(gspca_dev, req: 0x25, value: 0, index: 4, byte: Size); |
475 | reg_r(gspca_dev, req: 0x25, index: 4, len: 1); /* size */ |
476 | reg_w_1(gspca_dev, req: 0x27, value: 0, index: 0, byte: 6); |
477 | reg_r(gspca_dev, req: 0x27, index: 0, len: 1); /* type */ |
478 | spca504B_PollingDataReady(gspca_dev); |
479 | break; |
480 | case BRIDGE_SPCA504: |
481 | Size += 3; |
482 | if (sd->subtype == AiptekMiniPenCam13) { |
483 | /* spca504a aiptek */ |
484 | spca504A_acknowledged_command(gspca_dev, |
485 | req: 0x08, idx: Size, val: 0, |
486 | endcode: 0x80 | (Size & 0x0f), count: 1); |
487 | spca504A_acknowledged_command(gspca_dev, |
488 | req: 1, idx: 3, val: 0, endcode: 0x9f, count: 0); |
489 | } else { |
490 | spca504_acknowledged_command(gspca_dev, req: 0x08, idx: Size, val: 0); |
491 | } |
492 | break; |
493 | case BRIDGE_SPCA504C: |
494 | /* capture mode */ |
495 | reg_w_riv(gspca_dev, req: 0xa0, index: (0x0500 | (Size & 0x0f)), value: 0x00); |
496 | reg_w_riv(gspca_dev, req: 0x20, index: 0x01, value: 0x0500 | (Size & 0x0f)); |
497 | break; |
498 | } |
499 | } |
500 | |
501 | static void spca504_wait_status(struct gspca_dev *gspca_dev) |
502 | { |
503 | int cnt; |
504 | |
505 | cnt = 256; |
506 | while (--cnt > 0) { |
507 | /* With this we get the status, when return 0 it's all ok */ |
508 | reg_r(gspca_dev, req: 0x06, index: 0x00, len: 1); |
509 | if (gspca_dev->usb_buf[0] == 0) |
510 | return; |
511 | msleep(msecs: 10); |
512 | } |
513 | } |
514 | |
515 | static void spca504B_setQtable(struct gspca_dev *gspca_dev) |
516 | { |
517 | reg_w_1(gspca_dev, req: 0x26, value: 0, index: 0, byte: 3); |
518 | reg_r(gspca_dev, req: 0x26, index: 0, len: 1); |
519 | spca504B_PollingDataReady(gspca_dev); |
520 | } |
521 | |
522 | static void setbrightness(struct gspca_dev *gspca_dev, s32 val) |
523 | { |
524 | struct sd *sd = (struct sd *) gspca_dev; |
525 | u16 reg; |
526 | |
527 | reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7; |
528 | reg_w_riv(gspca_dev, req: 0x00, index: reg, value: val); |
529 | } |
530 | |
531 | static void setcontrast(struct gspca_dev *gspca_dev, s32 val) |
532 | { |
533 | struct sd *sd = (struct sd *) gspca_dev; |
534 | u16 reg; |
535 | |
536 | reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8; |
537 | reg_w_riv(gspca_dev, req: 0x00, index: reg, value: val); |
538 | } |
539 | |
540 | static void setcolors(struct gspca_dev *gspca_dev, s32 val) |
541 | { |
542 | struct sd *sd = (struct sd *) gspca_dev; |
543 | u16 reg; |
544 | |
545 | reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae; |
546 | reg_w_riv(gspca_dev, req: 0x00, index: reg, value: val); |
547 | } |
548 | |
549 | static void init_ctl_reg(struct gspca_dev *gspca_dev) |
550 | { |
551 | struct sd *sd = (struct sd *) gspca_dev; |
552 | int pollreg = 1; |
553 | |
554 | switch (sd->bridge) { |
555 | case BRIDGE_SPCA504: |
556 | case BRIDGE_SPCA504C: |
557 | pollreg = 0; |
558 | fallthrough; |
559 | default: |
560 | /* case BRIDGE_SPCA533: */ |
561 | /* case BRIDGE_SPCA504B: */ |
562 | reg_w_riv(gspca_dev, req: 0, index: 0x21ad, value: 0x00); /* hue */ |
563 | reg_w_riv(gspca_dev, req: 0, index: 0x21ac, value: 0x01); /* sat/hue */ |
564 | reg_w_riv(gspca_dev, req: 0, index: 0x21a3, value: 0x00); /* gamma */ |
565 | break; |
566 | case BRIDGE_SPCA536: |
567 | reg_w_riv(gspca_dev, req: 0, index: 0x20f5, value: 0x40); |
568 | reg_w_riv(gspca_dev, req: 0, index: 0x20f4, value: 0x01); |
569 | reg_w_riv(gspca_dev, req: 0, index: 0x2089, value: 0x00); |
570 | break; |
571 | } |
572 | if (pollreg) |
573 | spca504B_PollingDataReady(gspca_dev); |
574 | } |
575 | |
576 | /* this function is called at probe time */ |
577 | static int sd_config(struct gspca_dev *gspca_dev, |
578 | const struct usb_device_id *id) |
579 | { |
580 | struct sd *sd = (struct sd *) gspca_dev; |
581 | struct cam *cam; |
582 | |
583 | cam = &gspca_dev->cam; |
584 | |
585 | sd->bridge = id->driver_info >> 8; |
586 | sd->subtype = id->driver_info; |
587 | |
588 | if (sd->subtype == AiptekMiniPenCam13) { |
589 | |
590 | /* try to get the firmware as some cam answer 2.0.1.2.2 |
591 | * and should be a spca504b then overwrite that setting */ |
592 | reg_r(gspca_dev, req: 0x20, index: 0, len: 1); |
593 | switch (gspca_dev->usb_buf[0]) { |
594 | case 1: |
595 | break; /* (right bridge/subtype) */ |
596 | case 2: |
597 | sd->bridge = BRIDGE_SPCA504B; |
598 | sd->subtype = 0; |
599 | break; |
600 | default: |
601 | return -ENODEV; |
602 | } |
603 | } |
604 | |
605 | switch (sd->bridge) { |
606 | default: |
607 | /* case BRIDGE_SPCA504B: */ |
608 | /* case BRIDGE_SPCA504: */ |
609 | /* case BRIDGE_SPCA536: */ |
610 | cam->cam_mode = vga_mode; |
611 | cam->nmodes = ARRAY_SIZE(vga_mode); |
612 | break; |
613 | case BRIDGE_SPCA533: |
614 | cam->cam_mode = custom_mode; |
615 | if (sd->subtype == MegaImageVI) /* 320x240 only */ |
616 | cam->nmodes = ARRAY_SIZE(custom_mode) - 1; |
617 | else |
618 | cam->nmodes = ARRAY_SIZE(custom_mode); |
619 | break; |
620 | case BRIDGE_SPCA504C: |
621 | cam->cam_mode = vga_mode2; |
622 | cam->nmodes = ARRAY_SIZE(vga_mode2); |
623 | break; |
624 | } |
625 | return 0; |
626 | } |
627 | |
628 | /* this function is called at probe and resume time */ |
629 | static int sd_init(struct gspca_dev *gspca_dev) |
630 | { |
631 | struct sd *sd = (struct sd *) gspca_dev; |
632 | |
633 | switch (sd->bridge) { |
634 | case BRIDGE_SPCA504B: |
635 | reg_w_riv(gspca_dev, req: 0x1d, index: 0x00, value: 0); |
636 | reg_w_riv(gspca_dev, req: 0x00, index: 0x2306, value: 0x01); |
637 | reg_w_riv(gspca_dev, req: 0x00, index: 0x0d04, value: 0x00); |
638 | reg_w_riv(gspca_dev, req: 0x00, index: 0x2000, value: 0x00); |
639 | reg_w_riv(gspca_dev, req: 0x00, index: 0x2301, value: 0x13); |
640 | reg_w_riv(gspca_dev, req: 0x00, index: 0x2306, value: 0x00); |
641 | fallthrough; |
642 | case BRIDGE_SPCA533: |
643 | spca504B_PollingDataReady(gspca_dev); |
644 | spca50x_GetFirmware(gspca_dev); |
645 | break; |
646 | case BRIDGE_SPCA536: |
647 | spca50x_GetFirmware(gspca_dev); |
648 | reg_r(gspca_dev, req: 0x00, index: 0x5002, len: 1); |
649 | reg_w_1(gspca_dev, req: 0x24, value: 0, index: 0, byte: 0); |
650 | reg_r(gspca_dev, req: 0x24, index: 0, len: 1); |
651 | spca504B_PollingDataReady(gspca_dev); |
652 | reg_w_riv(gspca_dev, req: 0x34, index: 0, value: 0); |
653 | spca504B_WaitCmdStatus(gspca_dev); |
654 | break; |
655 | case BRIDGE_SPCA504C: /* pccam600 */ |
656 | gspca_dbg(gspca_dev, D_STREAM, "Opening SPCA504 (PC-CAM 600)\n" ); |
657 | reg_w_riv(gspca_dev, req: 0xe0, index: 0x0000, value: 0x0000); |
658 | reg_w_riv(gspca_dev, req: 0xe0, index: 0x0000, value: 0x0001); /* reset */ |
659 | spca504_wait_status(gspca_dev); |
660 | if (sd->subtype == LogitechClickSmart420) |
661 | write_vector(gspca_dev, |
662 | data: spca504A_clicksmart420_open_data, |
663 | ARRAY_SIZE(spca504A_clicksmart420_open_data)); |
664 | else |
665 | write_vector(gspca_dev, data: spca504_pccam600_open_data, |
666 | ARRAY_SIZE(spca504_pccam600_open_data)); |
667 | setup_qtable(gspca_dev, qtable: qtable_creative_pccam); |
668 | break; |
669 | default: |
670 | /* case BRIDGE_SPCA504: */ |
671 | gspca_dbg(gspca_dev, D_STREAM, "Opening SPCA504\n" ); |
672 | if (sd->subtype == AiptekMiniPenCam13) { |
673 | spca504_read_info(gspca_dev); |
674 | |
675 | /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ |
676 | spca504A_acknowledged_command(gspca_dev, req: 0x24, |
677 | idx: 8, val: 3, endcode: 0x9e, count: 1); |
678 | /* Twice sequential need status 0xff->0x9e->0x9d */ |
679 | spca504A_acknowledged_command(gspca_dev, req: 0x24, |
680 | idx: 8, val: 3, endcode: 0x9e, count: 0); |
681 | |
682 | spca504A_acknowledged_command(gspca_dev, req: 0x24, |
683 | idx: 0, val: 0, endcode: 0x9d, count: 1); |
684 | /******************************/ |
685 | /* spca504a aiptek */ |
686 | spca504A_acknowledged_command(gspca_dev, req: 0x08, |
687 | idx: 6, val: 0, endcode: 0x86, count: 1); |
688 | /* reg_write (dev, 0, 0x2000, 0); */ |
689 | /* reg_write (dev, 0, 0x2883, 1); */ |
690 | /* spca504A_acknowledged_command (gspca_dev, 0x08, |
691 | 6, 0, 0x86, 1); */ |
692 | /* spca504A_acknowledged_command (gspca_dev, 0x24, |
693 | 0, 0, 0x9D, 1); */ |
694 | reg_w_riv(gspca_dev, req: 0x00, index: 0x270c, value: 0x05); |
695 | /* L92 sno1t.txt */ |
696 | reg_w_riv(gspca_dev, req: 0x00, index: 0x2310, value: 0x05); |
697 | spca504A_acknowledged_command(gspca_dev, req: 0x01, |
698 | idx: 0x0f, val: 0, endcode: 0xff, count: 0); |
699 | } |
700 | /* setup qtable */ |
701 | reg_w_riv(gspca_dev, req: 0, index: 0x2000, value: 0); |
702 | reg_w_riv(gspca_dev, req: 0, index: 0x2883, value: 1); |
703 | setup_qtable(gspca_dev, qtable: qtable_spca504_default); |
704 | break; |
705 | } |
706 | return gspca_dev->usb_err; |
707 | } |
708 | |
709 | static int sd_start(struct gspca_dev *gspca_dev) |
710 | { |
711 | struct sd *sd = (struct sd *) gspca_dev; |
712 | int enable; |
713 | |
714 | /* create the JPEG header */ |
715 | jpeg_define(jpeg_hdr: sd->jpeg_hdr, height: gspca_dev->pixfmt.height, |
716 | width: gspca_dev->pixfmt.width, |
717 | samplesY: 0x22); /* JPEG 411 */ |
718 | jpeg_set_qual(jpeg_hdr: sd->jpeg_hdr, QUALITY); |
719 | |
720 | if (sd->bridge == BRIDGE_SPCA504B) |
721 | spca504B_setQtable(gspca_dev); |
722 | spca504B_SetSizeType(gspca_dev); |
723 | switch (sd->bridge) { |
724 | default: |
725 | /* case BRIDGE_SPCA504B: */ |
726 | /* case BRIDGE_SPCA533: */ |
727 | /* case BRIDGE_SPCA536: */ |
728 | switch (sd->subtype) { |
729 | case MegapixV4: |
730 | case LogitechClickSmart820: |
731 | case MegaImageVI: |
732 | reg_w_riv(gspca_dev, req: 0xf0, index: 0, value: 0); |
733 | spca504B_WaitCmdStatus(gspca_dev); |
734 | reg_w_riv(gspca_dev, req: 0xf0, index: 4, value: 0); |
735 | spca504B_WaitCmdStatus(gspca_dev); |
736 | break; |
737 | default: |
738 | reg_w_riv(gspca_dev, req: 0x31, index: 0x0004, value: 0x00); |
739 | spca504B_WaitCmdStatus(gspca_dev); |
740 | spca504B_PollingDataReady(gspca_dev); |
741 | break; |
742 | } |
743 | break; |
744 | case BRIDGE_SPCA504: |
745 | if (sd->subtype == AiptekMiniPenCam13) { |
746 | spca504_read_info(gspca_dev); |
747 | |
748 | /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ |
749 | spca504A_acknowledged_command(gspca_dev, req: 0x24, |
750 | idx: 8, val: 3, endcode: 0x9e, count: 1); |
751 | /* Twice sequential need status 0xff->0x9e->0x9d */ |
752 | spca504A_acknowledged_command(gspca_dev, req: 0x24, |
753 | idx: 8, val: 3, endcode: 0x9e, count: 0); |
754 | spca504A_acknowledged_command(gspca_dev, req: 0x24, |
755 | idx: 0, val: 0, endcode: 0x9d, count: 1); |
756 | } else { |
757 | spca504_acknowledged_command(gspca_dev, req: 0x24, idx: 8, val: 3); |
758 | spca504_read_info(gspca_dev); |
759 | spca504_acknowledged_command(gspca_dev, req: 0x24, idx: 8, val: 3); |
760 | spca504_acknowledged_command(gspca_dev, req: 0x24, idx: 0, val: 0); |
761 | } |
762 | spca504B_SetSizeType(gspca_dev); |
763 | reg_w_riv(gspca_dev, req: 0x00, index: 0x270c, value: 0x05); |
764 | /* L92 sno1t.txt */ |
765 | reg_w_riv(gspca_dev, req: 0x00, index: 0x2310, value: 0x05); |
766 | break; |
767 | case BRIDGE_SPCA504C: |
768 | if (sd->subtype == LogitechClickSmart420) { |
769 | write_vector(gspca_dev, |
770 | data: spca504A_clicksmart420_init_data, |
771 | ARRAY_SIZE(spca504A_clicksmart420_init_data)); |
772 | } else { |
773 | write_vector(gspca_dev, data: spca504_pccam600_init_data, |
774 | ARRAY_SIZE(spca504_pccam600_init_data)); |
775 | } |
776 | enable = (sd->autogain ? 0x04 : 0x01); |
777 | reg_w_riv(gspca_dev, req: 0x0c, index: 0x0000, value: enable); |
778 | /* auto exposure */ |
779 | reg_w_riv(gspca_dev, req: 0xb0, index: 0x0000, value: enable); |
780 | /* auto whiteness */ |
781 | |
782 | /* set default exposure compensation and whiteness balance */ |
783 | reg_w_riv(gspca_dev, req: 0x30, index: 0x0001, value: 800); /* ~ 20 fps */ |
784 | reg_w_riv(gspca_dev, req: 0x30, index: 0x0002, value: 1600); |
785 | spca504B_SetSizeType(gspca_dev); |
786 | break; |
787 | } |
788 | init_ctl_reg(gspca_dev); |
789 | return gspca_dev->usb_err; |
790 | } |
791 | |
792 | static void sd_stopN(struct gspca_dev *gspca_dev) |
793 | { |
794 | struct sd *sd = (struct sd *) gspca_dev; |
795 | |
796 | switch (sd->bridge) { |
797 | default: |
798 | /* case BRIDGE_SPCA533: */ |
799 | /* case BRIDGE_SPCA536: */ |
800 | /* case BRIDGE_SPCA504B: */ |
801 | reg_w_riv(gspca_dev, req: 0x31, index: 0, value: 0); |
802 | spca504B_WaitCmdStatus(gspca_dev); |
803 | spca504B_PollingDataReady(gspca_dev); |
804 | break; |
805 | case BRIDGE_SPCA504: |
806 | case BRIDGE_SPCA504C: |
807 | reg_w_riv(gspca_dev, req: 0x00, index: 0x2000, value: 0x0000); |
808 | |
809 | if (sd->subtype == AiptekMiniPenCam13) { |
810 | /* spca504a aiptek */ |
811 | /* spca504A_acknowledged_command(gspca_dev, 0x08, |
812 | 6, 0, 0x86, 1); */ |
813 | spca504A_acknowledged_command(gspca_dev, req: 0x24, |
814 | idx: 0x00, val: 0x00, endcode: 0x9d, count: 1); |
815 | spca504A_acknowledged_command(gspca_dev, req: 0x01, |
816 | idx: 0x0f, val: 0x00, endcode: 0xff, count: 1); |
817 | } else { |
818 | spca504_acknowledged_command(gspca_dev, req: 0x24, idx: 0, val: 0); |
819 | reg_w_riv(gspca_dev, req: 0x01, index: 0x000f, value: 0x0000); |
820 | } |
821 | break; |
822 | } |
823 | } |
824 | |
825 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, |
826 | u8 *data, /* isoc packet */ |
827 | int len) /* iso packet length */ |
828 | { |
829 | struct sd *sd = (struct sd *) gspca_dev; |
830 | int i, sof = 0; |
831 | static u8 ffd9[] = {0xff, 0xd9}; |
832 | |
833 | /* frames are jpeg 4.1.1 without 0xff escape */ |
834 | switch (sd->bridge) { |
835 | case BRIDGE_SPCA533: |
836 | if (data[0] == 0xff) { |
837 | if (data[1] != 0x01) { /* drop packet */ |
838 | /* gspca_dev->last_packet_type = DISCARD_PACKET; */ |
839 | return; |
840 | } |
841 | sof = 1; |
842 | data += SPCA533_OFFSET_DATA; |
843 | len -= SPCA533_OFFSET_DATA; |
844 | } else { |
845 | data += 1; |
846 | len -= 1; |
847 | } |
848 | break; |
849 | case BRIDGE_SPCA536: |
850 | if (data[0] == 0xff) { |
851 | sof = 1; |
852 | data += SPCA536_OFFSET_DATA; |
853 | len -= SPCA536_OFFSET_DATA; |
854 | } else { |
855 | data += 2; |
856 | len -= 2; |
857 | } |
858 | break; |
859 | default: |
860 | /* case BRIDGE_SPCA504: */ |
861 | /* case BRIDGE_SPCA504B: */ |
862 | switch (data[0]) { |
863 | case 0xfe: /* start of frame */ |
864 | sof = 1; |
865 | data += SPCA50X_OFFSET_DATA; |
866 | len -= SPCA50X_OFFSET_DATA; |
867 | break; |
868 | case 0xff: /* drop packet */ |
869 | /* gspca_dev->last_packet_type = DISCARD_PACKET; */ |
870 | return; |
871 | default: |
872 | data += 1; |
873 | len -= 1; |
874 | break; |
875 | } |
876 | break; |
877 | case BRIDGE_SPCA504C: |
878 | switch (data[0]) { |
879 | case 0xfe: /* start of frame */ |
880 | sof = 1; |
881 | data += SPCA504_PCCAM600_OFFSET_DATA; |
882 | len -= SPCA504_PCCAM600_OFFSET_DATA; |
883 | break; |
884 | case 0xff: /* drop packet */ |
885 | /* gspca_dev->last_packet_type = DISCARD_PACKET; */ |
886 | return; |
887 | default: |
888 | data += 1; |
889 | len -= 1; |
890 | break; |
891 | } |
892 | break; |
893 | } |
894 | if (sof) { /* start of frame */ |
895 | gspca_frame_add(gspca_dev, packet_type: LAST_PACKET, |
896 | data: ffd9, len: 2); |
897 | |
898 | /* put the JPEG header in the new frame */ |
899 | gspca_frame_add(gspca_dev, packet_type: FIRST_PACKET, |
900 | data: sd->jpeg_hdr, JPEG_HDR_SZ); |
901 | } |
902 | |
903 | /* add 0x00 after 0xff */ |
904 | i = 0; |
905 | do { |
906 | if (data[i] == 0xff) { |
907 | gspca_frame_add(gspca_dev, packet_type: INTER_PACKET, |
908 | data, len: i + 1); |
909 | len -= i; |
910 | data += i; |
911 | *data = 0x00; |
912 | i = 0; |
913 | } |
914 | i++; |
915 | } while (i < len); |
916 | gspca_frame_add(gspca_dev, packet_type: INTER_PACKET, data, len); |
917 | } |
918 | |
919 | static int sd_s_ctrl(struct v4l2_ctrl *ctrl) |
920 | { |
921 | struct gspca_dev *gspca_dev = |
922 | container_of(ctrl->handler, struct gspca_dev, ctrl_handler); |
923 | struct sd *sd = (struct sd *)gspca_dev; |
924 | |
925 | gspca_dev->usb_err = 0; |
926 | |
927 | if (!gspca_dev->streaming) |
928 | return 0; |
929 | |
930 | switch (ctrl->id) { |
931 | case V4L2_CID_BRIGHTNESS: |
932 | setbrightness(gspca_dev, val: ctrl->val); |
933 | break; |
934 | case V4L2_CID_CONTRAST: |
935 | setcontrast(gspca_dev, val: ctrl->val); |
936 | break; |
937 | case V4L2_CID_SATURATION: |
938 | setcolors(gspca_dev, val: ctrl->val); |
939 | break; |
940 | case V4L2_CID_AUTOGAIN: |
941 | sd->autogain = ctrl->val; |
942 | break; |
943 | } |
944 | return gspca_dev->usb_err; |
945 | } |
946 | |
947 | static const struct v4l2_ctrl_ops sd_ctrl_ops = { |
948 | .s_ctrl = sd_s_ctrl, |
949 | }; |
950 | |
951 | static int sd_init_controls(struct gspca_dev *gspca_dev) |
952 | { |
953 | struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; |
954 | |
955 | gspca_dev->vdev.ctrl_handler = hdl; |
956 | v4l2_ctrl_handler_init(hdl, 4); |
957 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
958 | V4L2_CID_BRIGHTNESS, min: -128, max: 127, step: 1, def: 0); |
959 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
960 | V4L2_CID_CONTRAST, min: 0, max: 255, step: 1, def: 0x20); |
961 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
962 | V4L2_CID_SATURATION, min: 0, max: 255, step: 1, def: 0x1a); |
963 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
964 | V4L2_CID_AUTOGAIN, min: 0, max: 1, step: 1, def: 1); |
965 | |
966 | if (hdl->error) { |
967 | pr_err("Could not initialize controls\n" ); |
968 | return hdl->error; |
969 | } |
970 | return 0; |
971 | } |
972 | |
973 | /* sub-driver description */ |
974 | static const struct sd_desc sd_desc = { |
975 | .name = MODULE_NAME, |
976 | .config = sd_config, |
977 | .init = sd_init, |
978 | .init_controls = sd_init_controls, |
979 | .start = sd_start, |
980 | .stopN = sd_stopN, |
981 | .pkt_scan = sd_pkt_scan, |
982 | }; |
983 | |
984 | /* -- module initialisation -- */ |
985 | #define BS(bridge, subtype) \ |
986 | .driver_info = (BRIDGE_ ## bridge << 8) \ |
987 | | (subtype) |
988 | static const struct usb_device_id device_table[] = { |
989 | {USB_DEVICE(0x041e, 0x400b), BS(SPCA504C, 0)}, |
990 | {USB_DEVICE(0x041e, 0x4012), BS(SPCA504C, 0)}, |
991 | {USB_DEVICE(0x041e, 0x4013), BS(SPCA504C, 0)}, |
992 | {USB_DEVICE(0x0458, 0x7006), BS(SPCA504B, 0)}, |
993 | {USB_DEVICE(0x0461, 0x0821), BS(SPCA533, 0)}, |
994 | {USB_DEVICE(0x046d, 0x0905), BS(SPCA533, LogitechClickSmart820)}, |
995 | {USB_DEVICE(0x046d, 0x0960), BS(SPCA504C, LogitechClickSmart420)}, |
996 | {USB_DEVICE(0x0471, 0x0322), BS(SPCA504B, 0)}, |
997 | {USB_DEVICE(0x04a5, 0x3003), BS(SPCA504B, 0)}, |
998 | {USB_DEVICE(0x04a5, 0x3008), BS(SPCA533, 0)}, |
999 | {USB_DEVICE(0x04a5, 0x300a), BS(SPCA533, 0)}, |
1000 | {USB_DEVICE(0x04f1, 0x1001), BS(SPCA504B, 0)}, |
1001 | {USB_DEVICE(0x04fc, 0x500c), BS(SPCA504B, 0)}, |
1002 | {USB_DEVICE(0x04fc, 0x504a), BS(SPCA504, AiptekMiniPenCam13)}, |
1003 | {USB_DEVICE(0x04fc, 0x504b), BS(SPCA504B, 0)}, |
1004 | {USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)}, |
1005 | {USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)}, |
1006 | {USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)}, |
1007 | {USB_DEVICE(0x052b, 0x1507), BS(SPCA533, MegapixV4)}, |
1008 | {USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)}, |
1009 | {USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)}, |
1010 | {USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)}, |
1011 | {USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)}, |
1012 | {USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)}, |
1013 | {USB_DEVICE(0x055f, 0xc211), BS(SPCA536, 0)}, |
1014 | {USB_DEVICE(0x055f, 0xc230), BS(SPCA533, 0)}, |
1015 | {USB_DEVICE(0x055f, 0xc232), BS(SPCA533, 0)}, |
1016 | {USB_DEVICE(0x055f, 0xc360), BS(SPCA536, 0)}, |
1017 | {USB_DEVICE(0x055f, 0xc420), BS(SPCA504, 0)}, |
1018 | {USB_DEVICE(0x055f, 0xc430), BS(SPCA533, 0)}, |
1019 | {USB_DEVICE(0x055f, 0xc440), BS(SPCA533, 0)}, |
1020 | {USB_DEVICE(0x055f, 0xc520), BS(SPCA504, 0)}, |
1021 | {USB_DEVICE(0x055f, 0xc530), BS(SPCA533, 0)}, |
1022 | {USB_DEVICE(0x055f, 0xc540), BS(SPCA533, 0)}, |
1023 | {USB_DEVICE(0x055f, 0xc630), BS(SPCA533, 0)}, |
1024 | {USB_DEVICE(0x055f, 0xc650), BS(SPCA533, 0)}, |
1025 | {USB_DEVICE(0x05da, 0x1018), BS(SPCA504B, 0)}, |
1026 | {USB_DEVICE(0x06d6, 0x0031), BS(SPCA533, 0)}, |
1027 | {USB_DEVICE(0x06d6, 0x0041), BS(SPCA504B, 0)}, |
1028 | {USB_DEVICE(0x0733, 0x1311), BS(SPCA533, 0)}, |
1029 | {USB_DEVICE(0x0733, 0x1314), BS(SPCA533, 0)}, |
1030 | {USB_DEVICE(0x0733, 0x2211), BS(SPCA533, 0)}, |
1031 | {USB_DEVICE(0x0733, 0x2221), BS(SPCA533, 0)}, |
1032 | {USB_DEVICE(0x0733, 0x3261), BS(SPCA536, 0)}, |
1033 | {USB_DEVICE(0x0733, 0x3281), BS(SPCA536, 0)}, |
1034 | {USB_DEVICE(0x08ca, 0x0104), BS(SPCA533, 0)}, |
1035 | {USB_DEVICE(0x08ca, 0x0106), BS(SPCA533, 0)}, |
1036 | {USB_DEVICE(0x08ca, 0x2008), BS(SPCA504B, 0)}, |
1037 | {USB_DEVICE(0x08ca, 0x2010), BS(SPCA533, 0)}, |
1038 | {USB_DEVICE(0x08ca, 0x2016), BS(SPCA504B, 0)}, |
1039 | {USB_DEVICE(0x08ca, 0x2018), BS(SPCA504B, 0)}, |
1040 | {USB_DEVICE(0x08ca, 0x2020), BS(SPCA533, 0)}, |
1041 | {USB_DEVICE(0x08ca, 0x2022), BS(SPCA533, 0)}, |
1042 | {USB_DEVICE(0x08ca, 0x2024), BS(SPCA536, 0)}, |
1043 | {USB_DEVICE(0x08ca, 0x2028), BS(SPCA533, 0)}, |
1044 | {USB_DEVICE(0x08ca, 0x2040), BS(SPCA536, 0)}, |
1045 | {USB_DEVICE(0x08ca, 0x2042), BS(SPCA536, 0)}, |
1046 | {USB_DEVICE(0x08ca, 0x2050), BS(SPCA536, 0)}, |
1047 | {USB_DEVICE(0x08ca, 0x2060), BS(SPCA536, 0)}, |
1048 | {USB_DEVICE(0x0d64, 0x0303), BS(SPCA536, 0)}, |
1049 | {} |
1050 | }; |
1051 | MODULE_DEVICE_TABLE(usb, device_table); |
1052 | |
1053 | /* -- device connect -- */ |
1054 | static int sd_probe(struct usb_interface *intf, |
1055 | const struct usb_device_id *id) |
1056 | { |
1057 | return gspca_dev_probe(intf, id, sd_desc: &sd_desc, dev_size: sizeof(struct sd), |
1058 | THIS_MODULE); |
1059 | } |
1060 | |
1061 | static struct usb_driver sd_driver = { |
1062 | .name = MODULE_NAME, |
1063 | .id_table = device_table, |
1064 | .probe = sd_probe, |
1065 | .disconnect = gspca_disconnect, |
1066 | #ifdef CONFIG_PM |
1067 | .suspend = gspca_suspend, |
1068 | .resume = gspca_resume, |
1069 | .reset_resume = gspca_resume, |
1070 | #endif |
1071 | }; |
1072 | |
1073 | module_usb_driver(sd_driver); |
1074 | |