Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph...
[linux-2.6-block.git] / drivers / media / video / gspca / spca561.c
CommitLineData
6a7eba24
JFM
1/*
2 * Sunplus spca561 subdriver
3 *
4 * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr
5 *
6 * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#define MODULE_NAME "spca561"
24
436c2c53 25#include <linux/input.h>
6a7eba24
JFM
26#include "gspca.h"
27
6a7eba24
JFM
28MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
29MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver");
30MODULE_LICENSE("GPL");
31
32/* specific webcam descriptor */
33struct sd {
34 struct gspca_dev gspca_dev; /* !! must be the first item */
35
7879d459 36 __u16 exposure; /* rev12a only */
0fc23d20 37#define EXPOSURE_MIN 1
d0848eb2
HG
38#define EXPOSURE_DEF 700 /* == 10 fps */
39#define EXPOSURE_MAX (2047 + 325) /* see setexposure */
7879d459 40
5b7ed28e
JFM
41 __u8 contrast; /* rev72a only */
42#define CONTRAST_MIN 0x00
43#define CONTRAST_DEF 0x20
44#define CONTRAST_MAX 0x3f
45
7879d459
JFM
46 __u8 brightness; /* rev72a only */
47#define BRIGHTNESS_MIN 0
5b7ed28e
JFM
48#define BRIGHTNESS_DEF 0x20
49#define BRIGHTNESS_MAX 0x3f
7879d459 50
5b7ed28e 51 __u8 white;
9035f2e2
HG
52#define HUE_MIN 1
53#define HUE_DEF 0x40
54#define HUE_MAX 0x7f
7879d459 55
6a7eba24 56 __u8 autogain;
7879d459
JFM
57#define AUTOGAIN_MIN 0
58#define AUTOGAIN_DEF 1
59#define AUTOGAIN_MAX 1
60
61 __u8 gain; /* rev12a only */
d0848eb2
HG
62#define GAIN_MIN 0
63#define GAIN_DEF 63
64#define GAIN_MAX 255
6a7eba24 65
d698dc6b
JFM
66#define EXPO12A_DEF 3
67 __u8 expo12a; /* expo/gain? for rev 12a */
68
6a7eba24 69 __u8 chip_revision;
7879d459
JFM
70#define Rev012A 0
71#define Rev072A 1
72
6a7eba24
JFM
73 signed char ag_cnt;
74#define AG_CNT_START 13
75};
76
cc611b8a 77static const struct v4l2_pix_format sif_012a_mode[] = {
c2446b3e
JFM
78 {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
79 .bytesperline = 160,
80 .sizeimage = 160 * 120,
81 .colorspace = V4L2_COLORSPACE_SRGB,
82 .priv = 3},
83 {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
84 .bytesperline = 176,
85 .sizeimage = 176 * 144,
86 .colorspace = V4L2_COLORSPACE_SRGB,
87 .priv = 2},
88 {320, 240, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,
89 .bytesperline = 320,
90 .sizeimage = 320 * 240 * 4 / 8,
91 .colorspace = V4L2_COLORSPACE_SRGB,
92 .priv = 1},
93 {352, 288, V4L2_PIX_FMT_SPCA561, V4L2_FIELD_NONE,
94 .bytesperline = 352,
95 .sizeimage = 352 * 288 * 4 / 8,
96 .colorspace = V4L2_COLORSPACE_SRGB,
97 .priv = 0},
6a7eba24
JFM
98};
99
cc611b8a 100static const struct v4l2_pix_format sif_072a_mode[] = {
b77c0046
JFM
101 {160, 120, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
102 .bytesperline = 160,
103 .sizeimage = 160 * 120,
104 .colorspace = V4L2_COLORSPACE_SRGB,
105 .priv = 3},
106 {176, 144, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
107 .bytesperline = 176,
108 .sizeimage = 176 * 144,
109 .colorspace = V4L2_COLORSPACE_SRGB,
110 .priv = 2},
111 {320, 240, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
112 .bytesperline = 320,
113 .sizeimage = 320 * 240,
114 .colorspace = V4L2_COLORSPACE_SRGB,
115 .priv = 1},
116 {352, 288, V4L2_PIX_FMT_SGBRG8, V4L2_FIELD_NONE,
117 .bytesperline = 352,
118 .sizeimage = 352 * 288,
119 .colorspace = V4L2_COLORSPACE_SRGB,
120 .priv = 0},
121};
122
6a7eba24
JFM
123/*
124 * Initialization data
125 * I'm not very sure how to split initialization from open data
126 * chunks. For now, we'll consider everything as initialization
127 */
128/* Frame packet header offsets for the spca561 */
129#define SPCA561_OFFSET_SNAP 1
130#define SPCA561_OFFSET_TYPE 2
131#define SPCA561_OFFSET_COMPRESS 3
132#define SPCA561_OFFSET_FRAMSEQ 4
133#define SPCA561_OFFSET_GPIO 5
134#define SPCA561_OFFSET_USBBUFF 6
135#define SPCA561_OFFSET_WIN2GRAVE 7
136#define SPCA561_OFFSET_WIN2RAVE 8
137#define SPCA561_OFFSET_WIN2BAVE 9
138#define SPCA561_OFFSET_WIN2GBAVE 10
139#define SPCA561_OFFSET_WIN1GRAVE 11
140#define SPCA561_OFFSET_WIN1RAVE 12
141#define SPCA561_OFFSET_WIN1BAVE 13
142#define SPCA561_OFFSET_WIN1GBAVE 14
143#define SPCA561_OFFSET_FREQ 15
144#define SPCA561_OFFSET_VSYNC 16
6a7eba24
JFM
145#define SPCA561_INDEX_I2C_BASE 0x8800
146#define SPCA561_SNAPBIT 0x20
147#define SPCA561_SNAPCTRL 0x40
6a7eba24 148
0dbc2c16 149static const u16 rev72a_reset[][2] = {
6a7eba24
JFM
150 {0x0000, 0x8114}, /* Software GPIO output data */
151 {0x0001, 0x8114}, /* Software GPIO output data */
152 {0x0000, 0x8112}, /* Some kind of reset */
0dbc2c16
JFM
153 {}
154};
155static const __u16 rev72a_init_data1[][2] = {
6a7eba24
JFM
156 {0x0003, 0x8701}, /* PCLK clock delay adjustment */
157 {0x0001, 0x8703}, /* HSYNC from cmos inverted */
158 {0x0011, 0x8118}, /* Enable and conf sensor */
159 {0x0001, 0x8118}, /* Conf sensor */
160 {0x0092, 0x8804}, /* I know nothing about these */
161 {0x0010, 0x8802}, /* 0x88xx registers, so I won't */
f8a04a6f
JFM
162 {}
163};
0dbc2c16
JFM
164static const u16 rev72a_init_sensor1[][2] = {
165 {0x0001, 0x000d},
166 {0x0002, 0x0018},
167 {0x0004, 0x0165},
168 {0x0005, 0x0021},
169 {0x0007, 0x00aa},
170 {0x0020, 0x1504},
171 {0x0039, 0x0002},
172 {0x0035, 0x0010},
173 {0x0009, 0x1049},
174 {0x0028, 0x000b},
175 {0x003b, 0x000f},
176 {0x003c, 0x0000},
f8a04a6f
JFM
177 {}
178};
179static const __u16 rev72a_init_data2[][2] = {
6a7eba24
JFM
180 {0x0018, 0x8601}, /* Pixel/line selection for color separation */
181 {0x0000, 0x8602}, /* Optical black level for user setting */
182 {0x0060, 0x8604}, /* Optical black horizontal offset */
183 {0x0002, 0x8605}, /* Optical black vertical offset */
184 {0x0000, 0x8603}, /* Non-automatic optical black level */
185 {0x0002, 0x865b}, /* Horizontal offset for valid pixels */
186 {0x0000, 0x865f}, /* Vertical valid pixels window (x2) */
187 {0x00b0, 0x865d}, /* Horizontal valid pixels window (x2) */
188 {0x0090, 0x865e}, /* Vertical valid lines window (x2) */
189 {0x00e0, 0x8406}, /* Memory buffer threshold */
190 {0x0000, 0x8660}, /* Compensation memory stuff */
191 {0x0002, 0x8201}, /* Output address for r/w serial EEPROM */
192 {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */
193 {0x0001, 0x8200}, /* OprMode to be executed by hardware */
0dbc2c16
JFM
194/* from ms-win */
195 {0x0000, 0x8611}, /* R offset for white balance */
196 {0x00fd, 0x8612}, /* Gr offset for white balance */
197 {0x0003, 0x8613}, /* B offset for white balance */
6a7eba24 198 {0x0000, 0x8614}, /* Gb offset for white balance */
5b7ed28e
JFM
199/* from ms-win */
200 {0x0035, 0x8651}, /* R gain for white balance */
201 {0x0040, 0x8652}, /* Gr gain for white balance */
202 {0x005f, 0x8653}, /* B gain for white balance */
203 {0x0040, 0x8654}, /* Gb gain for white balance */
6a7eba24 204 {0x0002, 0x8502}, /* Maximum average bit rate stuff */
6a7eba24 205 {0x0011, 0x8802},
0dbc2c16 206
6a7eba24
JFM
207 {0x0087, 0x8700}, /* Set master clock (96Mhz????) */
208 {0x0081, 0x8702}, /* Master clock output enable */
209
210 {0x0000, 0x8500}, /* Set image type (352x288 no compression) */
211 /* Originally was 0x0010 (352x288 compression) */
212
213 {0x0002, 0x865b}, /* Horizontal offset for valid pixels */
214 {0x0003, 0x865c}, /* Vertical offset for valid lines */
f8a04a6f
JFM
215 {}
216};
0dbc2c16
JFM
217static const u16 rev72a_init_sensor2[][2] = {
218 {0x0003, 0x0121},
219 {0x0004, 0x0165},
220 {0x0005, 0x002f}, /* blanking control column */
221 {0x0006, 0x0000}, /* blanking mode row*/
222 {0x000a, 0x0002},
223 {0x0009, 0x1061}, /* setexposure times && pixel clock
6a7eba24 224 * 0001 0 | 000 0110 0001 */
0dbc2c16 225 {0x0035, 0x0014},
6a7eba24
JFM
226 {}
227};
228
6a7eba24 229/******************** QC Express etch2 stuff ********************/
a5ae2062 230static const __u16 Pb100_1map8300[][2] = {
6a7eba24
JFM
231 /* reg, value */
232 {0x8320, 0x3304},
233
234 {0x8303, 0x0125}, /* image area */
235 {0x8304, 0x0169},
236 {0x8328, 0x000b},
7879d459 237 {0x833c, 0x0001}, /*fixme: win:07*/
6a7eba24 238
7879d459 239 {0x832f, 0x1904}, /*fixme: was 0419*/
6a7eba24
JFM
240 {0x8307, 0x00aa},
241 {0x8301, 0x0003},
242 {0x8302, 0x000e},
243 {}
244};
a5ae2062 245static const __u16 Pb100_2map8300[][2] = {
6a7eba24
JFM
246 /* reg, value */
247 {0x8339, 0x0000},
248 {0x8307, 0x00aa},
249 {}
250};
251
a5ae2062 252static const __u16 spca561_161rev12A_data1[][2] = {
6b33e5e7
HG
253 {0x29, 0x8118}, /* Control register (various enable bits) */
254 {0x08, 0x8114}, /* GPIO: Led off */
255 {0x0e, 0x8112}, /* 0x0e stream off 0x3e stream on */
6c9d3c59 256 {0x00, 0x8102}, /* white balance - new */
6a7eba24
JFM
257 {0x92, 0x8804},
258 {0x04, 0x8802}, /* windows uses 08 */
259 {}
260};
a5ae2062 261static const __u16 spca561_161rev12A_data2[][2] = {
6a7eba24
JFM
262 {0x21, 0x8118},
263 {0x10, 0x8500},
264 {0x07, 0x8601},
265 {0x07, 0x8602},
266 {0x04, 0x8501},
6a7eba24
JFM
267
268 {0x07, 0x8201}, /* windows uses 02 */
269 {0x08, 0x8200},
270 {0x01, 0x8200},
271
6a7eba24
JFM
272 {0x90, 0x8604},
273 {0x00, 0x8605},
274 {0xb0, 0x8603},
275
276 /* sensor gains */
6c9d3c59
JFM
277 {0x07, 0x8601}, /* white balance - new */
278 {0x07, 0x8602}, /* white balance - new */
6a7eba24
JFM
279 {0x00, 0x8610}, /* *red */
280 {0x00, 0x8611}, /* 3f *green */
281 {0x00, 0x8612}, /* green *blue */
282 {0x00, 0x8613}, /* blue *green */
6c9d3c59
JFM
283 {0x43, 0x8614}, /* green *red - white balance - was 0x35 */
284 {0x40, 0x8615}, /* 40 *green - white balance - was 0x35 */
285 {0x71, 0x8616}, /* 7a *blue - white balance - was 0x35 */
286 {0x40, 0x8617}, /* 40 *green - white balance - was 0x35 */
6a7eba24
JFM
287
288 {0x0c, 0x8620}, /* 0c */
289 {0xc8, 0x8631}, /* c8 */
290 {0xc8, 0x8634}, /* c8 */
291 {0x23, 0x8635}, /* 23 */
292 {0x1f, 0x8636}, /* 1f */
293 {0xdd, 0x8637}, /* dd */
294 {0xe1, 0x8638}, /* e1 */
295 {0x1d, 0x8639}, /* 1d */
296 {0x21, 0x863a}, /* 21 */
297 {0xe3, 0x863b}, /* e3 */
298 {0xdf, 0x863c}, /* df */
299 {0xf0, 0x8505},
300 {0x32, 0x850a},
7879d459 301/* {0x99, 0x8700}, * - white balance - new (removed) */
6b33e5e7
HG
302 /* HDG we used to do this in stop0, making the init state and the state
303 after a start / stop different, so do this here instead. */
304 {0x29, 0x8118},
6a7eba24
JFM
305 {}
306};
307
35dc1b4c
JFM
308static void reg_w_val(struct usb_device *dev, __u16 index, __u8 value)
309{
310 int ret;
311
312 ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
313 0, /* request */
314 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
315 value, index, NULL, 0, 500);
316 PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value);
317 if (ret < 0)
0b656321 318 err("reg write: error %d", ret);
35dc1b4c
JFM
319}
320
321static void write_vector(struct gspca_dev *gspca_dev,
322 const __u16 data[][2])
6a7eba24 323{
35dc1b4c
JFM
324 struct usb_device *dev = gspca_dev->dev;
325 int i;
6a7eba24 326
35dc1b4c
JFM
327 i = 0;
328 while (data[i][1] != 0) {
329 reg_w_val(dev, data[i][1], data[i][0]);
6a7eba24
JFM
330 i++;
331 }
332}
35dc1b4c
JFM
333
334/* read 'len' bytes to gspca_dev->usb_buf */
335static void reg_r(struct gspca_dev *gspca_dev,
336 __u16 index, __u16 length)
337{
338 usb_control_msg(gspca_dev->dev,
339 usb_rcvctrlpipe(gspca_dev->dev, 0),
340 0, /* request */
341 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
342 0, /* value */
343 index, gspca_dev->usb_buf, length, 500);
344}
345
346/* write 'len' bytes from gspca_dev->usb_buf */
347static void reg_w_buf(struct gspca_dev *gspca_dev,
348 __u16 index, __u16 len)
349{
350 usb_control_msg(gspca_dev->dev,
351 usb_sndctrlpipe(gspca_dev->dev, 0),
352 0, /* request */
353 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
354 0, /* value */
355 index, gspca_dev->usb_buf, len, 500);
356}
357
358static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg)
359{
360 int retry = 60;
361
362 reg_w_val(gspca_dev->dev, 0x8801, reg);
363 reg_w_val(gspca_dev->dev, 0x8805, value);
364 reg_w_val(gspca_dev->dev, 0x8800, value >> 8);
365 do {
366 reg_r(gspca_dev, 0x8803, 1);
367 if (!gspca_dev->usb_buf[0])
368 return;
0dbc2c16 369 msleep(10);
35dc1b4c
JFM
370 } while (--retry);
371}
372
373static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
374{
375 int retry = 60;
376 __u8 value;
377
378 reg_w_val(gspca_dev->dev, 0x8804, 0x92);
379 reg_w_val(gspca_dev->dev, 0x8801, reg);
380 reg_w_val(gspca_dev->dev, 0x8802, mode | 0x01);
381 do {
382 reg_r(gspca_dev, 0x8803, 1);
383 if (!gspca_dev->usb_buf[0]) {
384 reg_r(gspca_dev, 0x8800, 1);
385 value = gspca_dev->usb_buf[0];
386 reg_r(gspca_dev, 0x8805, 1);
387 return ((int) value << 8) | gspca_dev->usb_buf[0];
388 }
0dbc2c16 389 msleep(10);
35dc1b4c
JFM
390 } while (--retry);
391 return -1;
392}
393
394static void sensor_mapwrite(struct gspca_dev *gspca_dev,
395 const __u16 (*sensormap)[2])
396{
397 while ((*sensormap)[0]) {
398 gspca_dev->usb_buf[0] = (*sensormap)[1];
399 gspca_dev->usb_buf[1] = (*sensormap)[1] >> 8;
400 reg_w_buf(gspca_dev, (*sensormap)[0], 2);
401 sensormap++;
402 }
403}
404
f8a04a6f
JFM
405static void write_sensor_72a(struct gspca_dev *gspca_dev,
406 const __u16 (*sensor)[2])
407{
408 while ((*sensor)[0]) {
409 i2c_write(gspca_dev, (*sensor)[1], (*sensor)[0]);
410 sensor++;
411 }
412}
413
6a7eba24
JFM
414static void init_161rev12A(struct gspca_dev *gspca_dev)
415{
6a7eba24
JFM
416 write_vector(gspca_dev, spca561_161rev12A_data1);
417 sensor_mapwrite(gspca_dev, Pb100_1map8300);
7879d459 418/*fixme: should be in sd_start*/
6a7eba24
JFM
419 write_vector(gspca_dev, spca561_161rev12A_data2);
420 sensor_mapwrite(gspca_dev, Pb100_2map8300);
421}
422
423/* this function is called at probe time */
424static int sd_config(struct gspca_dev *gspca_dev,
425 const struct usb_device_id *id)
426{
427 struct sd *sd = (struct sd *) gspca_dev;
6a7eba24
JFM
428 struct cam *cam;
429 __u16 vendor, product;
430 __u8 data1, data2;
431
432 /* Read frm global register the USB product and vendor IDs, just to
433 * prove that we can communicate with the device. This works, which
434 * confirms at we are communicating properly and that the device
435 * is a 561. */
739570bb
JFM
436 reg_r(gspca_dev, 0x8104, 1);
437 data1 = gspca_dev->usb_buf[0];
438 reg_r(gspca_dev, 0x8105, 1);
439 data2 = gspca_dev->usb_buf[0];
6a7eba24 440 vendor = (data2 << 8) | data1;
739570bb
JFM
441 reg_r(gspca_dev, 0x8106, 1);
442 data1 = gspca_dev->usb_buf[0];
443 reg_r(gspca_dev, 0x8107, 1);
444 data2 = gspca_dev->usb_buf[0];
6a7eba24
JFM
445 product = (data2 << 8) | data1;
446 if (vendor != id->idVendor || product != id->idProduct) {
447 PDEBUG(D_PROBE, "Bad vendor / product from device");
448 return -EINVAL;
449 }
9d64fdb1 450
6a7eba24 451 cam = &gspca_dev->cam;
6a7eba24 452 gspca_dev->nbalt = 7 + 1; /* choose alternate 7 first */
9d64fdb1
JFM
453
454 sd->chip_revision = id->driver_info;
b77c0046
JFM
455 if (sd->chip_revision == Rev012A) {
456 cam->cam_mode = sif_012a_mode;
457 cam->nmodes = ARRAY_SIZE(sif_012a_mode);
458 } else {
459 cam->cam_mode = sif_072a_mode;
460 cam->nmodes = ARRAY_SIZE(sif_072a_mode);
461 }
6c9d3c59
JFM
462 sd->brightness = BRIGHTNESS_DEF;
463 sd->contrast = CONTRAST_DEF;
9035f2e2 464 sd->white = HUE_DEF;
7879d459
JFM
465 sd->exposure = EXPOSURE_DEF;
466 sd->autogain = AUTOGAIN_DEF;
467 sd->gain = GAIN_DEF;
d698dc6b 468 sd->expo12a = EXPO12A_DEF;
6a7eba24
JFM
469 return 0;
470}
471
012d6b02
JFM
472/* this function is called at probe and resume time */
473static int sd_init_12a(struct gspca_dev *gspca_dev)
6a7eba24 474{
7879d459
JFM
475 PDEBUG(D_STREAM, "Chip revision: 012a");
476 init_161rev12A(gspca_dev);
477 return 0;
478}
012d6b02 479static int sd_init_72a(struct gspca_dev *gspca_dev)
7879d459
JFM
480{
481 PDEBUG(D_STREAM, "Chip revision: 072a");
0dbc2c16
JFM
482 write_vector(gspca_dev, rev72a_reset);
483 msleep(200);
f8a04a6f
JFM
484 write_vector(gspca_dev, rev72a_init_data1);
485 write_sensor_72a(gspca_dev, rev72a_init_sensor1);
486 write_vector(gspca_dev, rev72a_init_data2);
487 write_sensor_72a(gspca_dev, rev72a_init_sensor2);
0dbc2c16 488 reg_w_val(gspca_dev->dev, 0x8112, 0x30);
6a7eba24
JFM
489 return 0;
490}
491
5b7ed28e
JFM
492/* rev 72a only */
493static void setbrightness(struct gspca_dev *gspca_dev)
6a7eba24
JFM
494{
495 struct sd *sd = (struct sd *) gspca_dev;
496 struct usb_device *dev = gspca_dev->dev;
5b7ed28e 497 __u8 value;
6a7eba24 498
5b7ed28e
JFM
499 value = sd->brightness;
500
501 /* offsets for white balance */
502 reg_w_val(dev, 0x8611, value); /* R */
503 reg_w_val(dev, 0x8612, value); /* Gr */
504 reg_w_val(dev, 0x8613, value); /* B */
505 reg_w_val(dev, 0x8614, value); /* Gb */
6a7eba24
JFM
506}
507
6c9d3c59
JFM
508static void setwhite(struct gspca_dev *gspca_dev)
509{
510 struct sd *sd = (struct sd *) gspca_dev;
511 __u16 white;
5b7ed28e
JFM
512 __u8 blue, red;
513 __u16 reg;
6c9d3c59 514
7879d459 515 /* try to emulate MS-win as possible */
5b7ed28e
JFM
516 white = sd->white;
517 red = 0x20 + white * 3 / 8;
518 blue = 0x90 - white * 5 / 8;
519 if (sd->chip_revision == Rev012A) {
520 reg = 0x8614;
521 } else {
522 reg = 0x8651;
523 red += sd->contrast - 0x20;
524 blue += sd->contrast - 0x20;
525 }
526 reg_w_val(gspca_dev->dev, reg, red);
527 reg_w_val(gspca_dev->dev, reg + 2, blue);
528}
529
530static void setcontrast(struct gspca_dev *gspca_dev)
531{
532 struct sd *sd = (struct sd *) gspca_dev;
533 struct usb_device *dev = gspca_dev->dev;
534 __u8 value;
535
536 if (sd->chip_revision != Rev072A)
537 return;
538 value = sd->contrast + 0x20;
539
540 /* gains for white balance */
541 setwhite(gspca_dev);
542/* reg_w_val(dev, 0x8651, value); * R - done by setwhite */
543 reg_w_val(dev, 0x8652, value); /* Gr */
544/* reg_w_val(dev, 0x8653, value); * B - done by setwhite */
545 reg_w_val(dev, 0x8654, value); /* Gb */
7879d459
JFM
546}
547
548/* rev 12a only */
549static void setexposure(struct gspca_dev *gspca_dev)
550{
551 struct sd *sd = (struct sd *) gspca_dev;
d0848eb2 552 int i, expo = 0;
7879d459 553
0fc23d20
HG
554 /* Register 0x8309 controls exposure for the spca561,
555 the basic exposure setting goes from 1-2047, where 1 is completely
556 dark and 2047 is very bright. It not only influences exposure but
557 also the framerate (to allow for longer exposure) from 1 - 300 it
558 only raises the exposure time then from 300 - 600 it halves the
559 framerate to be able to further raise the exposure time and for every
560 300 more it halves the framerate again. This allows for a maximum
561 exposure time of circa 0.2 - 0.25 seconds (30 / (2000/3000) fps).
562 Sometimes this is not enough, the 1-2047 uses bits 0-10, bits 11-12
563 configure a divider for the base framerate which us used at the
564 exposure setting of 1-300. These bits configure the base framerate
565 according to the following formula: fps = 60 / (value + 2) */
d0848eb2
HG
566
567 /* We choose to use the high bits setting the fixed framerate divisor
568 asap, as setting high basic exposure setting without the fixed
569 divider in combination with high gains makes the cam stop */
570 int table[] = { 0, 450, 550, 625, EXPOSURE_MAX };
571
572 for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
573 if (sd->exposure <= table[i + 1]) {
574 expo = sd->exposure - table[i];
575 if (i)
576 expo += 300;
577 expo |= i << 11;
578 break;
579 }
0fc23d20 580 }
d0848eb2 581
35dc1b4c
JFM
582 gspca_dev->usb_buf[0] = expo;
583 gspca_dev->usb_buf[1] = expo >> 8;
584 reg_w_buf(gspca_dev, 0x8309, 2);
7879d459
JFM
585}
586
587/* rev 12a only */
588static void setgain(struct gspca_dev *gspca_dev)
589{
590 struct sd *sd = (struct sd *) gspca_dev;
7879d459 591
d0848eb2
HG
592 /* gain reg low 6 bits 0-63 gain, bit 6 and 7, both double the
593 sensitivity when set, so 31 + one of them set == 63, and 15
594 with both of them set == 63 */
595 if (sd->gain < 64)
596 gspca_dev->usb_buf[0] = sd->gain;
597 else if (sd->gain < 128)
598 gspca_dev->usb_buf[0] = (sd->gain / 2) | 0x40;
599 else
1d00d6c1 600 gspca_dev->usb_buf[0] = (sd->gain / 4) | 0xc0;
d0848eb2 601
35dc1b4c
JFM
602 gspca_dev->usb_buf[1] = 0;
603 reg_w_buf(gspca_dev, 0x8335, 2);
6c9d3c59
JFM
604}
605
cebf3b67
JFM
606static void setautogain(struct gspca_dev *gspca_dev)
607{
608 struct sd *sd = (struct sd *) gspca_dev;
609
d698dc6b
JFM
610 if (sd->autogain)
611 sd->ag_cnt = AG_CNT_START;
612 else
613 sd->ag_cnt = -1;
cebf3b67
JFM
614}
615
72ab97ce 616static int sd_start_12a(struct gspca_dev *gspca_dev)
6a7eba24 617{
6a7eba24 618 struct usb_device *dev = gspca_dev->dev;
6a7eba24 619 int mode;
5b7ed28e
JFM
620 static const __u8 Reg8391[8] =
621 {0x92, 0x30, 0x20, 0x00, 0x0c, 0x00, 0x00, 0x00};
6a7eba24 622
c2446b3e 623 mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
7879d459
JFM
624 if (mode <= 1) {
625 /* Use compression on 320x240 and above */
626 reg_w_val(dev, 0x8500, 0x10 | mode);
627 } else {
628 /* I couldn't get the compression to work below 320x240
629 * Fortunately at these resolutions the bandwidth
630 * is sufficient to push raw frames at ~20fps */
631 reg_w_val(dev, 0x8500, mode);
632 } /* -- qq@kuku.eu.org */
35dc1b4c 633
5b7ed28e
JFM
634 gspca_dev->usb_buf[0] = 0xaa;
635 gspca_dev->usb_buf[1] = 0x00;
636 reg_w_buf(gspca_dev, 0x8307, 2);
637 /* clock - lower 0x8X values lead to fps > 30 */
638 reg_w_val(gspca_dev->dev, 0x8700, 0x8a);
7879d459
JFM
639 /* 0x8f 0x85 0x27 clock */
640 reg_w_val(gspca_dev->dev, 0x8112, 0x1e | 0x20);
641 reg_w_val(gspca_dev->dev, 0x850b, 0x03);
5b7ed28e
JFM
642 memcpy(gspca_dev->usb_buf, Reg8391, 8);
643 reg_w_buf(gspca_dev, 0x8391, 8);
644 reg_w_buf(gspca_dev, 0x8390, 8);
7879d459 645 setwhite(gspca_dev);
d0848eb2 646 setgain(gspca_dev);
0fc23d20 647 setexposure(gspca_dev);
6b33e5e7
HG
648
649 /* Led ON (bit 3 -> 0 */
650 reg_w_val(gspca_dev->dev, 0x8114, 0x00);
72ab97ce 651 return 0;
7879d459 652}
72ab97ce 653static int sd_start_72a(struct gspca_dev *gspca_dev)
7879d459
JFM
654{
655 struct usb_device *dev = gspca_dev->dev;
656 int Clck;
657 int mode;
658
0dbc2c16
JFM
659 write_vector(gspca_dev, rev72a_reset);
660 msleep(200);
661 write_vector(gspca_dev, rev72a_init_data1);
662 write_sensor_72a(gspca_dev, rev72a_init_sensor1);
663
7879d459
JFM
664 mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
665 switch (mode) {
666 default:
a48196a2
JFM
667 case 0:
668 Clck = 0x27; /* ms-win 0x87 */
669 break;
670 case 1:
7879d459
JFM
671 Clck = 0x25;
672 break;
673 case 2:
674 Clck = 0x22;
675 break;
676 case 3:
677 Clck = 0x21;
6a7eba24
JFM
678 break;
679 }
7879d459 680 reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */
a48196a2
JFM
681 reg_w_val(dev, 0x8702, 0x81);
682 reg_w_val(dev, 0x8500, mode); /* mode */
0dbc2c16 683 write_sensor_72a(gspca_dev, rev72a_init_sensor2);
5b7ed28e
JFM
684 setcontrast(gspca_dev);
685/* setbrightness(gspca_dev); * fixme: bad values */
7879d459 686 setautogain(gspca_dev);
0dbc2c16 687 reg_w_val(dev, 0x8112, 0x10 | 0x20);
72ab97ce 688 return 0;
6a7eba24
JFM
689}
690
691static void sd_stopN(struct gspca_dev *gspca_dev)
692{
d698dc6b
JFM
693 struct sd *sd = (struct sd *) gspca_dev;
694
695 if (sd->chip_revision == Rev012A) {
696 reg_w_val(gspca_dev->dev, 0x8112, 0x0e);
6b33e5e7
HG
697 /* Led Off (bit 3 -> 1 */
698 reg_w_val(gspca_dev->dev, 0x8114, 0x08);
d698dc6b
JFM
699 } else {
700 reg_w_val(gspca_dev->dev, 0x8112, 0x20);
701/* reg_w_val(gspca_dev->dev, 0x8102, 0x00); ?? */
702 }
6a7eba24
JFM
703}
704
cebf3b67 705static void do_autogain(struct gspca_dev *gspca_dev)
6a7eba24
JFM
706{
707 struct sd *sd = (struct sd *) gspca_dev;
cebf3b67
JFM
708 int expotimes;
709 int pixelclk;
710 int gainG;
6a7eba24
JFM
711 __u8 R, Gr, Gb, B;
712 int y;
713 __u8 luma_mean = 110;
714 __u8 luma_delta = 20;
715 __u8 spring = 4;
716
cebf3b67
JFM
717 if (sd->ag_cnt < 0)
718 return;
719 if (--sd->ag_cnt >= 0)
720 return;
721 sd->ag_cnt = AG_CNT_START;
722
6a7eba24
JFM
723 switch (sd->chip_revision) {
724 case Rev072A:
739570bb
JFM
725 reg_r(gspca_dev, 0x8621, 1);
726 Gr = gspca_dev->usb_buf[0];
727 reg_r(gspca_dev, 0x8622, 1);
728 R = gspca_dev->usb_buf[0];
729 reg_r(gspca_dev, 0x8623, 1);
730 B = gspca_dev->usb_buf[0];
731 reg_r(gspca_dev, 0x8624, 1);
732 Gb = gspca_dev->usb_buf[0];
6a7eba24
JFM
733 y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8;
734 /* u= (128*B-(43*(Gr+Gb+R))) >> 8; */
735 /* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */
736 /* PDEBUG(D_CONF,"reading Y %d U %d V %d ",y,u,v); */
737
738 if (y < luma_mean - luma_delta ||
739 y > luma_mean + luma_delta) {
740 expotimes = i2c_read(gspca_dev, 0x09, 0x10);
741 pixelclk = 0x0800;
742 expotimes = expotimes & 0x07ff;
743 /* PDEBUG(D_PACK,
744 "Exposition Times 0x%03X Clock 0x%04X ",
745 expotimes,pixelclk); */
746 gainG = i2c_read(gspca_dev, 0x35, 0x10);
747 /* PDEBUG(D_PACK,
748 "reading Gain register %d", gainG); */
749
750 expotimes += (luma_mean - y) >> spring;
751 gainG += (luma_mean - y) / 50;
752 /* PDEBUG(D_PACK,
753 "compute expotimes %d gain %d",
754 expotimes,gainG); */
755
756 if (gainG > 0x3f)
757 gainG = 0x3f;
35dc1b4c 758 else if (gainG < 3)
6a7eba24
JFM
759 gainG = 3;
760 i2c_write(gspca_dev, gainG, 0x35);
761
35dc1b4c 762 if (expotimes > 0x0256)
6a7eba24 763 expotimes = 0x0256;
35dc1b4c 764 else if (expotimes < 3)
6a7eba24
JFM
765 expotimes = 3;
766 i2c_write(gspca_dev, expotimes | pixelclk, 0x09);
767 }
768 break;
6a7eba24
JFM
769 }
770}
771
772static void sd_pkt_scan(struct gspca_dev *gspca_dev,
76dd272b 773 u8 *data, /* isoc packet */
6a7eba24
JFM
774 int len) /* iso packet length */
775{
0fc23d20
HG
776 struct sd *sd = (struct sd *) gspca_dev;
777
576ed7b5
JFM
778 len--;
779 switch (*data++) { /* sequence number */
35dc1b4c 780 case 0: /* start of frame */
76dd272b 781 gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
436c2c53
HG
782
783 /* This should never happen */
784 if (len < 2) {
785 PDEBUG(D_ERR, "Short SOF packet, ignoring");
786 gspca_dev->last_packet_type = DISCARD_PACKET;
787 return;
788 }
789
2856643e 790#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
436c2c53
HG
791 if (data[0] & 0x20) {
792 input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
793 input_sync(gspca_dev->input_dev);
794 input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
795 input_sync(gspca_dev->input_dev);
796 }
797#endif
798
6a7eba24
JFM
799 if (data[1] & 0x10) {
800 /* compressed bayer */
76dd272b 801 gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
6a7eba24 802 } else {
54ab92ca 803 /* raw bayer (with a header, which we skip) */
0fc23d20
HG
804 if (sd->chip_revision == Rev012A) {
805 data += 20;
806 len -= 20;
807 } else {
808 data += 16;
809 len -= 16;
810 }
76dd272b 811 gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
6a7eba24
JFM
812 }
813 return;
35dc1b4c 814 case 0xff: /* drop (empty mpackets) */
6a7eba24
JFM
815 return;
816 }
76dd272b 817 gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
6a7eba24
JFM
818}
819
7879d459 820/* rev 72a only */
6a7eba24
JFM
821static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
822{
823 struct sd *sd = (struct sd *) gspca_dev;
824
825 sd->brightness = val;
826 if (gspca_dev->streaming)
827 setbrightness(gspca_dev);
828 return 0;
829}
830
831static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
832{
833 struct sd *sd = (struct sd *) gspca_dev;
834
6a7eba24
JFM
835 *val = sd->brightness;
836 return 0;
837}
838
7879d459 839/* rev 72a only */
6a7eba24
JFM
840static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
841{
842 struct sd *sd = (struct sd *) gspca_dev;
843
844 sd->contrast = val;
845 if (gspca_dev->streaming)
846 setcontrast(gspca_dev);
847 return 0;
848}
849
850static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
851{
852 struct sd *sd = (struct sd *) gspca_dev;
853
6a7eba24
JFM
854 *val = sd->contrast;
855 return 0;
856}
857
858static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
859{
860 struct sd *sd = (struct sd *) gspca_dev;
861
862 sd->autogain = val;
cebf3b67
JFM
863 if (gspca_dev->streaming)
864 setautogain(gspca_dev);
6a7eba24
JFM
865 return 0;
866}
867
868static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
869{
870 struct sd *sd = (struct sd *) gspca_dev;
871
872 *val = sd->autogain;
873 return 0;
874}
875
6c9d3c59
JFM
876static int sd_setwhite(struct gspca_dev *gspca_dev, __s32 val)
877{
878 struct sd *sd = (struct sd *) gspca_dev;
879
880 sd->white = val;
881 if (gspca_dev->streaming)
882 setwhite(gspca_dev);
883 return 0;
884}
885
886static int sd_getwhite(struct gspca_dev *gspca_dev, __s32 *val)
887{
888 struct sd *sd = (struct sd *) gspca_dev;
889
890 *val = sd->white;
891 return 0;
892}
893
7879d459
JFM
894/* rev12a only */
895static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
896{
897 struct sd *sd = (struct sd *) gspca_dev;
898
899 sd->exposure = val;
900 if (gspca_dev->streaming)
901 setexposure(gspca_dev);
902 return 0;
903}
904
905static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
906{
907 struct sd *sd = (struct sd *) gspca_dev;
908
909 *val = sd->exposure;
910 return 0;
911}
912
913/* rev12a only */
914static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
915{
916 struct sd *sd = (struct sd *) gspca_dev;
917
918 sd->gain = val;
919 if (gspca_dev->streaming)
920 setgain(gspca_dev);
921 return 0;
922}
923
924static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
925{
926 struct sd *sd = (struct sd *) gspca_dev;
927
928 *val = sd->gain;
929 return 0;
930}
931
932/* control tables */
7e64dc4c 933static const struct ctrl sd_ctrls_12a[] = {
7879d459
JFM
934 {
935 {
9035f2e2 936 .id = V4L2_CID_HUE,
7879d459 937 .type = V4L2_CTRL_TYPE_INTEGER,
9035f2e2
HG
938 .name = "Hue",
939 .minimum = HUE_MIN,
940 .maximum = HUE_MAX,
7879d459 941 .step = 1,
9035f2e2 942 .default_value = HUE_DEF,
7879d459
JFM
943 },
944 .set = sd_setwhite,
945 .get = sd_getwhite,
946 },
947 {
948 {
949 .id = V4L2_CID_EXPOSURE,
950 .type = V4L2_CTRL_TYPE_INTEGER,
951 .name = "Exposure",
952 .minimum = EXPOSURE_MIN,
953 .maximum = EXPOSURE_MAX,
954 .step = 1,
955 .default_value = EXPOSURE_DEF,
956 },
957 .set = sd_setexposure,
958 .get = sd_getexposure,
959 },
7879d459
JFM
960 {
961 {
962 .id = V4L2_CID_GAIN,
963 .type = V4L2_CTRL_TYPE_INTEGER,
964 .name = "Gain",
965 .minimum = GAIN_MIN,
966 .maximum = GAIN_MAX,
967 .step = 1,
968 .default_value = GAIN_DEF,
969 },
970 .set = sd_setgain,
971 .get = sd_getgain,
972 },
973};
974
7e64dc4c 975static const struct ctrl sd_ctrls_72a[] = {
5b7ed28e
JFM
976 {
977 {
9035f2e2 978 .id = V4L2_CID_HUE,
5b7ed28e 979 .type = V4L2_CTRL_TYPE_INTEGER,
9035f2e2
HG
980 .name = "Hue",
981 .minimum = HUE_MIN,
982 .maximum = HUE_MAX,
5b7ed28e 983 .step = 1,
9035f2e2 984 .default_value = HUE_DEF,
5b7ed28e
JFM
985 },
986 .set = sd_setwhite,
987 .get = sd_getwhite,
988 },
7879d459
JFM
989 {
990 {
991 .id = V4L2_CID_BRIGHTNESS,
992 .type = V4L2_CTRL_TYPE_INTEGER,
993 .name = "Brightness",
994 .minimum = BRIGHTNESS_MIN,
995 .maximum = BRIGHTNESS_MAX,
996 .step = 1,
997 .default_value = BRIGHTNESS_DEF,
998 },
999 .set = sd_setbrightness,
1000 .get = sd_getbrightness,
1001 },
1002 {
1003 {
1004 .id = V4L2_CID_CONTRAST,
1005 .type = V4L2_CTRL_TYPE_INTEGER,
1006 .name = "Contrast",
1007 .minimum = CONTRAST_MIN,
1008 .maximum = CONTRAST_MAX,
1009 .step = 1,
1010 .default_value = CONTRAST_DEF,
1011 },
1012 .set = sd_setcontrast,
1013 .get = sd_getcontrast,
1014 },
1015 {
1016 {
1017 .id = V4L2_CID_AUTOGAIN,
1018 .type = V4L2_CTRL_TYPE_BOOLEAN,
1019 .name = "Auto Gain",
1020 .minimum = AUTOGAIN_MIN,
1021 .maximum = AUTOGAIN_MAX,
1022 .step = 1,
1023 .default_value = AUTOGAIN_DEF,
1024 },
1025 .set = sd_setautogain,
1026 .get = sd_getautogain,
1027 },
1028};
1029
6a7eba24 1030/* sub-driver description */
7879d459 1031static const struct sd_desc sd_desc_12a = {
6a7eba24 1032 .name = MODULE_NAME,
7879d459
JFM
1033 .ctrls = sd_ctrls_12a,
1034 .nctrls = ARRAY_SIZE(sd_ctrls_12a),
6a7eba24 1035 .config = sd_config,
012d6b02 1036 .init = sd_init_12a,
7879d459
JFM
1037 .start = sd_start_12a,
1038 .stopN = sd_stopN,
7879d459 1039 .pkt_scan = sd_pkt_scan,
2856643e 1040#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
436c2c53
HG
1041 .other_input = 1,
1042#endif
7879d459
JFM
1043};
1044static const struct sd_desc sd_desc_72a = {
1045 .name = MODULE_NAME,
1046 .ctrls = sd_ctrls_72a,
1047 .nctrls = ARRAY_SIZE(sd_ctrls_72a),
1048 .config = sd_config,
012d6b02 1049 .init = sd_init_72a,
7879d459 1050 .start = sd_start_72a,
6a7eba24 1051 .stopN = sd_stopN,
6a7eba24 1052 .pkt_scan = sd_pkt_scan,
cebf3b67 1053 .dq_callback = do_autogain,
2856643e 1054#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
436c2c53
HG
1055 .other_input = 1,
1056#endif
6a7eba24 1057};
7879d459
JFM
1058static const struct sd_desc *sd_desc[2] = {
1059 &sd_desc_12a,
1060 &sd_desc_72a
1061};
6a7eba24
JFM
1062
1063/* -- module initialisation -- */
95c967c1 1064static const struct usb_device_id device_table[] = {
87581aa5
JFM
1065 {USB_DEVICE(0x041e, 0x401a), .driver_info = Rev072A},
1066 {USB_DEVICE(0x041e, 0x403b), .driver_info = Rev012A},
1067 {USB_DEVICE(0x0458, 0x7004), .driver_info = Rev072A},
f8f73d01 1068 {USB_DEVICE(0x0461, 0x0815), .driver_info = Rev072A},
87581aa5
JFM
1069 {USB_DEVICE(0x046d, 0x0928), .driver_info = Rev012A},
1070 {USB_DEVICE(0x046d, 0x0929), .driver_info = Rev012A},
1071 {USB_DEVICE(0x046d, 0x092a), .driver_info = Rev012A},
1072 {USB_DEVICE(0x046d, 0x092b), .driver_info = Rev012A},
1073 {USB_DEVICE(0x046d, 0x092c), .driver_info = Rev012A},
1074 {USB_DEVICE(0x046d, 0x092d), .driver_info = Rev012A},
1075 {USB_DEVICE(0x046d, 0x092e), .driver_info = Rev012A},
1076 {USB_DEVICE(0x046d, 0x092f), .driver_info = Rev012A},
1077 {USB_DEVICE(0x04fc, 0x0561), .driver_info = Rev072A},
1078 {USB_DEVICE(0x060b, 0xa001), .driver_info = Rev072A},
1079 {USB_DEVICE(0x10fd, 0x7e50), .driver_info = Rev072A},
1080 {USB_DEVICE(0xabcd, 0xcdee), .driver_info = Rev072A},
6a7eba24
JFM
1081 {}
1082};
1083
1084MODULE_DEVICE_TABLE(usb, device_table);
1085
1086/* -- device connect -- */
1087static int sd_probe(struct usb_interface *intf,
1088 const struct usb_device_id *id)
1089{
7879d459
JFM
1090 return gspca_dev_probe(intf, id,
1091 sd_desc[id->driver_info],
1092 sizeof(struct sd),
6a7eba24
JFM
1093 THIS_MODULE);
1094}
1095
1096static struct usb_driver sd_driver = {
1097 .name = MODULE_NAME,
1098 .id_table = device_table,
1099 .probe = sd_probe,
1100 .disconnect = gspca_disconnect,
6a709749
JFM
1101#ifdef CONFIG_PM
1102 .suspend = gspca_suspend,
1103 .resume = gspca_resume,
1104#endif
6a7eba24
JFM
1105};
1106
1107/* -- module insert / remove -- */
1108static int __init sd_mod_init(void)
1109{
54826437 1110 return usb_register(&sd_driver);
6a7eba24
JFM
1111}
1112static void __exit sd_mod_exit(void)
1113{
1114 usb_deregister(&sd_driver);
6a7eba24
JFM
1115}
1116
1117module_init(sd_mod_init);
1118module_exit(sd_mod_exit);