blob: b580af92d23129b82dd8c87ef7e817669d018f7d [file] [log] [blame]
Hans de Goedee2997a72008-04-23 08:09:12 -03001/*
2 * Pixart PAC207BCA library
3 *
4 * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl>
5 * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
6 * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr
7 *
8 * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26#define MODULE_NAME "pac207"
27
28#include "gspca.h"
29
Jean-Francois Moinec2446b32008-07-05 11:49:20 -030030#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 5)
31static const char version[] = "2.1.5";
Hans de Goedee2997a72008-04-23 08:09:12 -030032
33MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>");
34MODULE_DESCRIPTION("Pixart PAC207");
35MODULE_LICENSE("GPL");
36
37#define PAC207_CTRL_TIMEOUT 100 /* ms */
38
39#define PAC207_BRIGHTNESS_MIN 0
40#define PAC207_BRIGHTNESS_MAX 255
41#define PAC207_BRIGHTNESS_DEFAULT 4 /* power on default: 4 */
42
Hans de Goede46ccdaf2008-07-04 18:39:08 -030043/* An exposure value of 4 also works (3 does not) but then we need to lower
44 the compression balance setting when in 352x288 mode, otherwise the usb
45 bandwidth is not enough and packets get dropped resulting in corrupt
46 frames. The problem with this is that when the compression balance gets
47 lowered below 0x80, the pac207 starts using a different compression
48 algorithm for some lines, these lines get prefixed with a 0x2dd2 prefix
49 and currently we do not know how to decompress these lines, so for now
50 we use a minimum exposure value of 5 */
51#define PAC207_EXPOSURE_MIN 5
Hans de Goedee2997a72008-04-23 08:09:12 -030052#define PAC207_EXPOSURE_MAX 26
Hans de Goede46ccdaf2008-07-04 18:39:08 -030053#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 ?? */
Hans de Goedee2997a72008-04-23 08:09:12 -030054#define PAC207_EXPOSURE_KNEE 11 /* 4 = 30 fps, 11 = 8, 15 = 6 */
55
56#define PAC207_GAIN_MIN 0
57#define PAC207_GAIN_MAX 31
58#define PAC207_GAIN_DEFAULT 9 /* power on default: 9 */
59#define PAC207_GAIN_KNEE 20
60
61#define PAC207_AUTOGAIN_DEADZONE 30
62/* We calculating the autogain at the end of the transfer of a frame, at this
63 moment a frame with the old settings is being transmitted, and a frame is
64 being captured with the old settings. So if we adjust the autogain we must
65 ignore atleast the 2 next frames for the new settings to come into effect
66 before doing any other adjustments */
67#define PAC207_AUTOGAIN_IGNORE_FRAMES 3
68
Hans de Goedee2997a72008-04-23 08:09:12 -030069/* specific webcam descriptor */
70struct sd {
71 struct gspca_dev gspca_dev; /* !! must be the first item */
72
Hans de Goedee2997a72008-04-23 08:09:12 -030073 u8 mode;
74
75 u8 brightness;
76 u8 exposure;
77 u8 autogain;
78 u8 gain;
79
80 u8 sof_read;
Hans de Goedeab8f12c2008-07-04 18:29:32 -030081 u8 header_read;
Hans de Goedee2997a72008-04-23 08:09:12 -030082 u8 autogain_ignore_frames;
83
84 atomic_t avg_lum;
85};
86
87/* V4L2 controls supported by the driver */
88static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
89static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
90static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
91static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
92static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
93static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
94static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
95static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
96
97static struct ctrl sd_ctrls[] = {
98#define SD_BRIGHTNESS 0
99 {
100 {
101 .id = V4L2_CID_BRIGHTNESS,
102 .type = V4L2_CTRL_TYPE_INTEGER,
103 .name = "Brightness",
104 .minimum = PAC207_BRIGHTNESS_MIN,
105 .maximum = PAC207_BRIGHTNESS_MAX,
106 .step = 1,
107 .default_value = PAC207_BRIGHTNESS_DEFAULT,
108 .flags = 0,
109 },
110 .set = sd_setbrightness,
111 .get = sd_getbrightness,
112 },
113#define SD_EXPOSURE 1
114 {
115 {
116 .id = V4L2_CID_EXPOSURE,
117 .type = V4L2_CTRL_TYPE_INTEGER,
118 .name = "exposure",
119 .minimum = PAC207_EXPOSURE_MIN,
120 .maximum = PAC207_EXPOSURE_MAX,
121 .step = 1,
122 .default_value = PAC207_EXPOSURE_DEFAULT,
123 .flags = 0,
124 },
125 .set = sd_setexposure,
126 .get = sd_getexposure,
127 },
128#define SD_AUTOGAIN 2
129 {
130 {
131 .id = V4L2_CID_AUTOGAIN,
132 .type = V4L2_CTRL_TYPE_BOOLEAN,
133 .name = "Auto Gain",
134 .minimum = 0,
135 .maximum = 1,
136 .step = 1,
137 .default_value = 1,
138 .flags = 0,
139 },
140 .set = sd_setautogain,
141 .get = sd_getautogain,
142 },
143#define SD_GAIN 3
144 {
145 {
146 .id = V4L2_CID_GAIN,
147 .type = V4L2_CTRL_TYPE_INTEGER,
148 .name = "gain",
149 .minimum = PAC207_GAIN_MIN,
150 .maximum = PAC207_GAIN_MAX,
151 .step = 1,
152 .default_value = PAC207_GAIN_DEFAULT,
153 .flags = 0,
154 },
155 .set = sd_setgain,
156 .get = sd_getgain,
157 },
158};
159
Jean-Francois Moinec2446b32008-07-05 11:49:20 -0300160static struct v4l2_pix_format sif_mode[] = {
161 {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
162 .bytesperline = 176,
163 .sizeimage = (176 + 2) * 144,
164 /* uncompressed, add 2 bytes / line for line header */
165 .colorspace = V4L2_COLORSPACE_SRGB,
166 .priv = 1},
167 {352, 288, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
168 .bytesperline = 352,
169 .sizeimage = 352 * 288 / 2, /* compressed */
170 .colorspace = V4L2_COLORSPACE_SRGB,
171 .priv = 0},
Hans de Goedee2997a72008-04-23 08:09:12 -0300172};
173
174static const __u8 pac207_sensor_init[][8] = {
175 {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0},
176 {0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30},
177 {0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00},
178 {0x00, 0x00, 0x32, 0x00, 0x96, 0x00, 0xa2, 0x02},
179 {0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xaf, 0x00},
180};
181
182 /* 48 reg_72 Rate Control end BalSize_4a =0x36 */
183static const __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 };
184
Jean-Francois Moined43fa322008-06-12 10:58:58 -0300185static const unsigned char pac207_sof_marker[5] =
186 { 0xff, 0xff, 0x00, 0xff, 0x96 };
Hans de Goedee2997a72008-04-23 08:09:12 -0300187
188int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
189 const u8 *buffer, u16 length)
190{
191 struct usb_device *udev = gspca_dev->dev;
192 int err;
193 u8 kbuf[8];
194
195 memcpy(kbuf, buffer, length);
196
197 err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01,
198 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
199 0x00, index, kbuf, length, PAC207_CTRL_TIMEOUT);
200 if (err < 0)
201 PDEBUG(D_ERR,
202 "Failed to write registers to index 0x%04X, error %d)",
203 index, err);
204
205 return err;
206}
207
208
209int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
210{
211 struct usb_device *udev = gspca_dev->dev;
212 int err;
213
214 err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
215 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
216 value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
217 if (err)
218 PDEBUG(D_ERR, "Failed to write a register (index 0x%04X,"
219 " value 0x%02X, error %d)", index, value, err);
220
221 return err;
222}
223
224
225int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
226{
227 struct usb_device *udev = gspca_dev->dev;
228 u8 buff;
229 int res;
230
231 res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
232 USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
233 0x00, index, &buff, 1, PAC207_CTRL_TIMEOUT);
234 if (res < 0) {
235 PDEBUG(D_ERR,
236 "Failed to read a register (index 0x%04X, error %d)",
237 index, res);
238 return res;
239 }
240
241 return buff;
242}
243
244
245/* this function is called at probe time */
246static int sd_config(struct gspca_dev *gspca_dev,
247 const struct usb_device_id *id)
248{
Jean-Francois Moine4aa0d032008-05-04 06:46:21 -0300249 struct sd *sd = (struct sd *) gspca_dev;
Hans de Goedee2997a72008-04-23 08:09:12 -0300250 struct cam *cam;
251 u8 idreg[2];
252
253 idreg[0] = pac207_read_reg(gspca_dev, 0x0000);
254 idreg[1] = pac207_read_reg(gspca_dev, 0x0001);
255 idreg[0] = ((idreg[0] & 0x0F) << 4) | ((idreg[1] & 0xf0) >> 4);
256 idreg[1] = idreg[1] & 0x0f;
257 PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X",
258 idreg[0], idreg[1]);
259
260 if (idreg[0] != 0x27) {
261 PDEBUG(D_PROBE, "Error invalid sensor ID!");
262 return -ENODEV;
263 }
264
265 pac207_write_reg(gspca_dev, 0x41, 0x00);
266 /* Bit_0=Image Format,
267 * Bit_1=LED,
268 * Bit_2=Compression test mode enable */
269 pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
270 pac207_write_reg(gspca_dev, 0x11, 0x30); /* Analog Bias */
271
272 PDEBUG(D_PROBE,
273 "Pixart PAC207BCA Image Processor and Control Chip detected"
274 " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
275
276 cam = &gspca_dev->cam;
277 cam->dev_name = (char *) id->driver_info;
278 cam->epaddr = 0x05;
279 cam->cam_mode = sif_mode;
280 cam->nmodes = ARRAY_SIZE(sif_mode);
Jean-Francois Moine4aa0d032008-05-04 06:46:21 -0300281 sd->brightness = PAC207_BRIGHTNESS_DEFAULT;
282 sd->exposure = PAC207_EXPOSURE_DEFAULT;
283 sd->gain = PAC207_GAIN_DEFAULT;
Hans de Goedee2997a72008-04-23 08:09:12 -0300284
285 return 0;
286}
287
288/* this function is called at open time */
289static int sd_open(struct gspca_dev *gspca_dev)
290{
291 struct sd *sd = (struct sd *) gspca_dev;
292
Hans de Goedee2997a72008-04-23 08:09:12 -0300293 sd->autogain = 1;
Hans de Goedee2997a72008-04-23 08:09:12 -0300294 return 0;
295}
296
297/* -- start the camera -- */
298static void sd_start(struct gspca_dev *gspca_dev)
299{
300 struct sd *sd = (struct sd *) gspca_dev;
301 __u8 mode;
302
303 pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */
304 pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8);
305 pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8);
306 pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8);
307 pac207_write_regs(gspca_dev, 0x0040, pac207_sensor_init[3], 8);
308 pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[4], 8);
309 pac207_write_regs(gspca_dev, 0x0048, PacReg72, 4);
310
311 /* Compression Balance */
312 if (gspca_dev->width == 176)
313 pac207_write_reg(gspca_dev, 0x4a, 0xff);
314 else
315 pac207_write_reg(gspca_dev, 0x4a, 0x88);
316 pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */
317 pac207_write_reg(gspca_dev, 0x08, sd->brightness);
318
319 /* PGA global gain (Bit 4-0) */
320 pac207_write_reg(gspca_dev, 0x0e, sd->gain);
321 pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */
322
323 mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
Jean-Francois Moined43fa322008-06-12 10:58:58 -0300324 if (gspca_dev->width == 176) { /* 176x144 */
Hans de Goedee2997a72008-04-23 08:09:12 -0300325 mode |= 0x01;
326 PDEBUG(D_STREAM, "pac207_start mode 176x144");
Jean-Francois Moined43fa322008-06-12 10:58:58 -0300327 } else { /* 352x288 */
Hans de Goedee2997a72008-04-23 08:09:12 -0300328 PDEBUG(D_STREAM, "pac207_start mode 352x288");
Jean-Francois Moined43fa322008-06-12 10:58:58 -0300329 }
Hans de Goedee2997a72008-04-23 08:09:12 -0300330 pac207_write_reg(gspca_dev, 0x41, mode);
331
332 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
333 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
Jean-Francois Moine6a7eba22008-06-30 15:50:11 -0300334 msleep(10);
Hans de Goedee2997a72008-04-23 08:09:12 -0300335 pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */
336
337 sd->sof_read = 0;
338 sd->autogain_ignore_frames = 0;
339 atomic_set(&sd->avg_lum, -1);
340}
341
342static void sd_stopN(struct gspca_dev *gspca_dev)
343{
344 pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */
345 pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */
346 pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
347}
348
349static void sd_stop0(struct gspca_dev *gspca_dev)
350{
351}
352
353/* this function is called at close time */
354static void sd_close(struct gspca_dev *gspca_dev)
355{
356}
357
Hans de Goedee2997a72008-04-23 08:09:12 -0300358/* auto gain and exposure algorithm based on the knee algorithm described here:
Jean-Francois Moined43fa322008-06-12 10:58:58 -0300359 * <http://ytse.tricolour.net/docs/LowLightOptimization.html> */
Hans de Goedee2997a72008-04-23 08:09:12 -0300360static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
361{
362 struct sd *sd = (struct sd *) gspca_dev;
363 int i, steps, desired_avg_lum;
364 int orig_gain = sd->gain;
365 int orig_exposure = sd->exposure;
366 int avg_lum = atomic_read(&sd->avg_lum);
367
368 if (!sd->autogain || avg_lum == -1)
369 return;
370
371 if (sd->autogain_ignore_frames > 0) {
372 sd->autogain_ignore_frames--;
373 return;
374 }
375
376 /* correct desired lumination for the configured brightness */
377 desired_avg_lum = 100 + sd->brightness / 2;
378
379 /* If we are of a multiple of deadzone, do multiple step to reach the
380 desired lumination fast (with the risc of a slight overshoot) */
381 steps = abs(desired_avg_lum - avg_lum) / PAC207_AUTOGAIN_DEADZONE;
382
383 for (i = 0; i < steps; i++) {
384 if (avg_lum > desired_avg_lum) {
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300385 if (sd->gain > PAC207_GAIN_KNEE)
Hans de Goedee2997a72008-04-23 08:09:12 -0300386 sd->gain--;
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300387 else if (sd->exposure > PAC207_EXPOSURE_KNEE)
Hans de Goedee2997a72008-04-23 08:09:12 -0300388 sd->exposure--;
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300389 else if (sd->gain > PAC207_GAIN_DEFAULT)
Hans de Goedee2997a72008-04-23 08:09:12 -0300390 sd->gain--;
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300391 else if (sd->exposure > PAC207_EXPOSURE_MIN)
Hans de Goedee2997a72008-04-23 08:09:12 -0300392 sd->exposure--;
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300393 else if (sd->gain > PAC207_GAIN_MIN)
Hans de Goedee2997a72008-04-23 08:09:12 -0300394 sd->gain--;
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300395 else
Hans de Goedee2997a72008-04-23 08:09:12 -0300396 break;
397 } else {
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300398 if (sd->gain < PAC207_GAIN_DEFAULT)
Hans de Goedee2997a72008-04-23 08:09:12 -0300399 sd->gain++;
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300400 else if (sd->exposure < PAC207_EXPOSURE_KNEE)
Hans de Goedee2997a72008-04-23 08:09:12 -0300401 sd->exposure++;
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300402 else if (sd->gain < PAC207_GAIN_KNEE)
Hans de Goedee2997a72008-04-23 08:09:12 -0300403 sd->gain++;
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300404 else if (sd->exposure < PAC207_EXPOSURE_MAX)
Hans de Goedee2997a72008-04-23 08:09:12 -0300405 sd->exposure++;
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300406 else if (sd->gain < PAC207_GAIN_MAX)
Hans de Goedee2997a72008-04-23 08:09:12 -0300407 sd->gain++;
Jean-Francois Moine956e42d2008-07-01 10:03:42 -0300408 else
Hans de Goedee2997a72008-04-23 08:09:12 -0300409 break;
410 }
411 }
412
413 if (sd->exposure != orig_exposure || sd->gain != orig_gain) {
414 if (sd->exposure != orig_exposure)
415 pac207_write_reg(gspca_dev, 0x0002, sd->exposure);
416 if (sd->gain != orig_gain)
417 pac207_write_reg(gspca_dev, 0x000e, sd->gain);
418 pac207_write_reg(gspca_dev, 0x13, 0x01); /* load reg to sen */
419 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
420 sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES;
421 }
422}
423
424static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev,
Jean-Francois Moine50a871f2008-06-30 19:47:33 -0300425 unsigned char *m, int len)
Hans de Goedee2997a72008-04-23 08:09:12 -0300426{
427 struct sd *sd = (struct sd *) gspca_dev;
428 int i;
429
430 /* Search for the SOF marker (fixed part) in the header */
431 for (i = 0; i < len; i++) {
432 if (m[i] == pac207_sof_marker[sd->sof_read]) {
433 sd->sof_read++;
434 if (sd->sof_read == sizeof(pac207_sof_marker)) {
435 PDEBUG(D_STREAM,
436 "SOF found, bytes to analyze: %u."
437 " Frame starts at byte #%u",
438 len, i + 1);
439 sd->sof_read = 0;
440 return m + i + 1;
441 }
Jean-Francois Moined43fa322008-06-12 10:58:58 -0300442 } else {
Hans de Goedee2997a72008-04-23 08:09:12 -0300443 sd->sof_read = 0;
Jean-Francois Moined43fa322008-06-12 10:58:58 -0300444 }
Hans de Goedee2997a72008-04-23 08:09:12 -0300445 }
446
447 return NULL;
448}
449
Hans de Goedee2997a72008-04-23 08:09:12 -0300450static void sd_pkt_scan(struct gspca_dev *gspca_dev,
451 struct gspca_frame *frame,
Jean-Francois Moinea5ae2062008-07-04 11:16:16 -0300452 __u8 *data,
Hans de Goedee2997a72008-04-23 08:09:12 -0300453 int len)
454{
Hans de Goedeab8f12c2008-07-04 18:29:32 -0300455 struct sd *sd = (struct sd *) gspca_dev;
Hans de Goedee2997a72008-04-23 08:09:12 -0300456 unsigned char *sof;
Hans de Goedee2997a72008-04-23 08:09:12 -0300457
458 sof = pac207_find_sof(gspca_dev, data, len);
Hans de Goedee2997a72008-04-23 08:09:12 -0300459 if (sof) {
Hans de Goedeab8f12c2008-07-04 18:29:32 -0300460 int n;
461
Hans de Goedee2997a72008-04-23 08:09:12 -0300462 /* finish decoding current frame */
Hans de Goedeab8f12c2008-07-04 18:29:32 -0300463 n = sof - data;
464 if (n > sizeof pac207_sof_marker)
465 n -= sizeof pac207_sof_marker;
466 else
467 n = 0;
468 frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
469 data, n);
470 sd->header_read = 0;
Jean-Francois Moine6a7eba22008-06-30 15:50:11 -0300471 gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
Hans de Goedee2997a72008-04-23 08:09:12 -0300472 len -= sof - data;
473 data = sof;
474 }
Hans de Goedeab8f12c2008-07-04 18:29:32 -0300475 if (sd->header_read < 11) {
476 int needed;
Hans de Goedee2997a72008-04-23 08:09:12 -0300477
Hans de Goedeab8f12c2008-07-04 18:29:32 -0300478 /* get average lumination from frame header (byte 5) */
479 if (sd->header_read < 5) {
480 needed = 5 - sd->header_read;
481 if (len >= needed)
482 atomic_set(&sd->avg_lum, data[needed - 1]);
483 }
484 /* skip the rest of the header */
485 needed = 11 - sd->header_read;
486 if (len <= needed) {
487 sd->header_read += len;
488 return;
489 }
490 data += needed;
491 len -= needed;
492 sd->header_read = 11;
493 }
Hans de Goedee2997a72008-04-23 08:09:12 -0300494
Hans de Goedeab8f12c2008-07-04 18:29:32 -0300495 gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
Hans de Goedee2997a72008-04-23 08:09:12 -0300496}
497
498static void setbrightness(struct gspca_dev *gspca_dev)
499{
500 struct sd *sd = (struct sd *) gspca_dev;
501
502 pac207_write_reg(gspca_dev, 0x08, sd->brightness);
503 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
504 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
505}
506
507static void setexposure(struct gspca_dev *gspca_dev)
508{
509 struct sd *sd = (struct sd *) gspca_dev;
510
511 pac207_write_reg(gspca_dev, 0x02, sd->exposure);
512 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
513 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
514}
515
516static void setgain(struct gspca_dev *gspca_dev)
517{
518 struct sd *sd = (struct sd *) gspca_dev;
519
520 pac207_write_reg(gspca_dev, 0x0e, sd->gain);
521 pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
522 pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
523}
524
525static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
526{
527 struct sd *sd = (struct sd *) gspca_dev;
528
529 sd->brightness = val;
530 if (gspca_dev->streaming)
531 setbrightness(gspca_dev);
532 return 0;
533}
534
535static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
536{
537 struct sd *sd = (struct sd *) gspca_dev;
538
539 *val = sd->brightness;
540 return 0;
541}
542
543static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
544{
545 struct sd *sd = (struct sd *) gspca_dev;
546
547 /* don't allow mucking with exposure when using autogain */
548 if (sd->autogain)
549 return -EINVAL;
550
551 sd->exposure = val;
552 if (gspca_dev->streaming)
553 setexposure(gspca_dev);
554 return 0;
555}
556
557static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
558{
559 struct sd *sd = (struct sd *) gspca_dev;
560
561 *val = sd->exposure;
562 return 0;
563}
564
565static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
566{
567 struct sd *sd = (struct sd *) gspca_dev;
568
569 /* don't allow mucking with gain when using autogain */
570 if (sd->autogain)
571 return -EINVAL;
572
573 sd->gain = val;
574 if (gspca_dev->streaming)
575 setgain(gspca_dev);
576 return 0;
577}
578
579static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
580{
581 struct sd *sd = (struct sd *) gspca_dev;
582
583 *val = sd->gain;
584 return 0;
585}
586
587static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
588{
589 struct sd *sd = (struct sd *) gspca_dev;
590
591 sd->autogain = val;
592 /* when switching to autogain set defaults to make sure
593 we are on a valid point of the autogain gain /
594 exposure knee graph, and give this change time to
595 take effect before doing autogain. */
596 if (sd->autogain) {
597 sd->exposure = PAC207_EXPOSURE_DEFAULT;
598 sd->gain = PAC207_GAIN_DEFAULT;
599 if (gspca_dev->streaming) {
600 sd->autogain_ignore_frames =
601 PAC207_AUTOGAIN_IGNORE_FRAMES;
602 setexposure(gspca_dev);
603 setgain(gspca_dev);
604 }
605 }
606
607 return 0;
608}
609
610static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
611{
612 struct sd *sd = (struct sd *) gspca_dev;
613
614 *val = sd->autogain;
615 return 0;
616}
617
618/* sub-driver description */
Jean-Francois Moinea5ae2062008-07-04 11:16:16 -0300619static const struct sd_desc sd_desc = {
Hans de Goedee2997a72008-04-23 08:09:12 -0300620 .name = MODULE_NAME,
621 .ctrls = sd_ctrls,
622 .nctrls = ARRAY_SIZE(sd_ctrls),
623 .config = sd_config,
624 .open = sd_open,
625 .start = sd_start,
626 .stopN = sd_stopN,
627 .stop0 = sd_stop0,
628 .close = sd_close,
629 .dq_callback = pac207_do_auto_gain,
630 .pkt_scan = sd_pkt_scan,
631};
632
633/* -- module initialisation -- */
634#define DVNM(name) .driver_info = (kernel_ulong_t) name
Jean-Francois Moinea5ae2062008-07-04 11:16:16 -0300635static const __devinitdata struct usb_device_id device_table[] = {
Hans de Goedee2997a72008-04-23 08:09:12 -0300636 {USB_DEVICE(0x041e, 0x4028), DVNM("Creative Webcam Vista Plus")},
Jean-Francois Moined43fa322008-06-12 10:58:58 -0300637 {USB_DEVICE(0x093a, 0x2460), DVNM("Q-Tec Webcam 100")},
Hans de Goedee2997a72008-04-23 08:09:12 -0300638 {USB_DEVICE(0x093a, 0x2463), DVNM("Philips spc200nc pac207")},
639 {USB_DEVICE(0x093a, 0x2464), DVNM("Labtec Webcam 1200")},
640 {USB_DEVICE(0x093a, 0x2468), DVNM("PAC207")},
641 {USB_DEVICE(0x093a, 0x2470), DVNM("Genius GF112")},
Jean-Francois Moined43fa322008-06-12 10:58:58 -0300642 {USB_DEVICE(0x093a, 0x2471), DVNM("Genius VideoCam GE111")},
643 {USB_DEVICE(0x093a, 0x2472), DVNM("Genius VideoCam GE110")},
Hans de Goedee2997a72008-04-23 08:09:12 -0300644 {USB_DEVICE(0x2001, 0xf115), DVNM("D-Link DSB-C120")},
645 {}
646};
647MODULE_DEVICE_TABLE(usb, device_table);
648
649/* -- device connect -- */
650static int sd_probe(struct usb_interface *intf,
651 const struct usb_device_id *id)
652{
Jean-Francois Moined43fa322008-06-12 10:58:58 -0300653 return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
654 THIS_MODULE);
Hans de Goedee2997a72008-04-23 08:09:12 -0300655}
656
657static struct usb_driver sd_driver = {
658 .name = MODULE_NAME,
659 .id_table = device_table,
660 .probe = sd_probe,
661 .disconnect = gspca_disconnect,
662};
663
664/* -- module insert / remove -- */
665static int __init sd_mod_init(void)
666{
Hans de Goedee2997a72008-04-23 08:09:12 -0300667 if (usb_register(&sd_driver) < 0)
668 return -1;
669 PDEBUG(D_PROBE, "v%s registered", version);
670 return 0;
671}
672static void __exit sd_mod_exit(void)
673{
674 usb_deregister(&sd_driver);
675 PDEBUG(D_PROBE, "deregistered");
676}
677
678module_init(sd_mod_init);
679module_exit(sd_mod_exit);