Commit | Line | Data |
---|---|---|
4c98834a EA |
1 | /* |
2 | * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher | |
3 | * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland | |
4 | * Copyright (c) 2002, 2003 Tuukka Toivonen | |
5 | * Copyright (c) 2008 Erik Andrén | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
20 | * | |
21 | * P/N 861037: Sensor HDCS1000 ASIC STV0600 | |
22 | * P/N 861050-0010: Sensor HDCS1000 ASIC STV0600 | |
23 | * P/N 861050-0020: Sensor Photobit PB100 ASIC STV0600-1 - QuickCam Express | |
24 | * P/N 861055: Sensor ST VV6410 ASIC STV0610 - LEGO cam | |
25 | * P/N 861075-0040: Sensor HDCS1000 ASIC | |
26 | * P/N 961179-0700: Sensor ST VV6410 ASIC STV0602 - Dexxa WebCam USB | |
27 | * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web | |
28 | */ | |
29 | ||
30 | /* | |
31 | * The spec file for the PB-0100 suggests the following for best quality | |
32 | * images after the sensor has been reset : | |
33 | * | |
34 | * PB_ADCGAINL = R60 = 0x03 (3 dec) : sets low reference of ADC | |
35 | to produce good black level | |
36 | * PB_PREADCTRL = R32 = 0x1400 (5120 dec) : Enables global gain changes | |
37 | through R53 | |
38 | * PB_ADCMINGAIN = R52 = 0x10 (16 dec) : Sets the minimum gain for | |
39 | auto-exposure | |
40 | * PB_ADCGLOBALGAIN = R53 = 0x10 (16 dec) : Sets the global gain | |
41 | * PB_EXPGAIN = R14 = 0x11 (17 dec) : Sets the auto-exposure value | |
42 | * PB_UPDATEINT = R23 = 0x02 (2 dec) : Sets the speed on | |
43 | auto-exposure routine | |
44 | * PB_CFILLIN = R5 = 0x0E (14 dec) : Sets the frame rate | |
45 | */ | |
46 | ||
133a9fe9 JP |
47 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
48 | ||
4c98834a EA |
49 | #include "stv06xx_pb0100.h" |
50 | ||
766231ab EA |
51 | static const struct ctrl pb0100_ctrl[] = { |
52 | #define GAIN_IDX 0 | |
53 | { | |
54 | { | |
55 | .id = V4L2_CID_GAIN, | |
56 | .type = V4L2_CTRL_TYPE_INTEGER, | |
57 | .name = "Gain", | |
58 | .minimum = 0, | |
59 | .maximum = 255, | |
60 | .step = 1, | |
61 | .default_value = 128 | |
62 | }, | |
63 | .set = pb0100_set_gain, | |
64 | .get = pb0100_get_gain | |
65 | }, | |
66 | #define RED_BALANCE_IDX 1 | |
67 | { | |
68 | { | |
69 | .id = V4L2_CID_RED_BALANCE, | |
70 | .type = V4L2_CTRL_TYPE_INTEGER, | |
71 | .name = "Red Balance", | |
72 | .minimum = -255, | |
73 | .maximum = 255, | |
74 | .step = 1, | |
75 | .default_value = 0 | |
76 | }, | |
77 | .set = pb0100_set_red_balance, | |
78 | .get = pb0100_get_red_balance | |
79 | }, | |
80 | #define BLUE_BALANCE_IDX 2 | |
81 | { | |
82 | { | |
83 | .id = V4L2_CID_BLUE_BALANCE, | |
84 | .type = V4L2_CTRL_TYPE_INTEGER, | |
85 | .name = "Blue Balance", | |
86 | .minimum = -255, | |
87 | .maximum = 255, | |
88 | .step = 1, | |
89 | .default_value = 0 | |
90 | }, | |
91 | .set = pb0100_set_blue_balance, | |
92 | .get = pb0100_get_blue_balance | |
93 | }, | |
94 | #define EXPOSURE_IDX 3 | |
95 | { | |
96 | { | |
97 | .id = V4L2_CID_EXPOSURE, | |
98 | .type = V4L2_CTRL_TYPE_INTEGER, | |
99 | .name = "Exposure", | |
100 | .minimum = 0, | |
101 | .maximum = 511, | |
102 | .step = 1, | |
103 | .default_value = 12 | |
104 | }, | |
105 | .set = pb0100_set_exposure, | |
106 | .get = pb0100_get_exposure | |
107 | }, | |
108 | #define AUTOGAIN_IDX 4 | |
109 | { | |
110 | { | |
111 | .id = V4L2_CID_AUTOGAIN, | |
112 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
113 | .name = "Automatic Gain and Exposure", | |
114 | .minimum = 0, | |
115 | .maximum = 1, | |
116 | .step = 1, | |
117 | .default_value = 1 | |
118 | }, | |
119 | .set = pb0100_set_autogain, | |
120 | .get = pb0100_get_autogain | |
121 | }, | |
122 | #define AUTOGAIN_TARGET_IDX 5 | |
123 | { | |
124 | { | |
125 | .id = V4L2_CTRL_CLASS_USER + 0x1000, | |
126 | .type = V4L2_CTRL_TYPE_INTEGER, | |
127 | .name = "Automatic Gain Target", | |
128 | .minimum = 0, | |
129 | .maximum = 255, | |
130 | .step = 1, | |
131 | .default_value = 128 | |
132 | }, | |
133 | .set = pb0100_set_autogain_target, | |
134 | .get = pb0100_get_autogain_target | |
135 | }, | |
136 | #define NATURAL_IDX 6 | |
137 | { | |
138 | { | |
139 | .id = V4L2_CTRL_CLASS_USER + 0x1001, | |
140 | .type = V4L2_CTRL_TYPE_BOOLEAN, | |
141 | .name = "Natural Light Source", | |
142 | .minimum = 0, | |
143 | .maximum = 1, | |
144 | .step = 1, | |
145 | .default_value = 1 | |
146 | }, | |
147 | .set = pb0100_set_natural, | |
148 | .get = pb0100_get_natural | |
149 | } | |
150 | }; | |
151 | ||
152 | static struct v4l2_pix_format pb0100_mode[] = { | |
153 | /* low res / subsample modes disabled as they are only half res horizontal, | |
154 | halving the vertical resolution does not seem to work */ | |
155 | { | |
156 | 320, | |
157 | 240, | |
158 | V4L2_PIX_FMT_SGRBG8, | |
159 | V4L2_FIELD_NONE, | |
160 | .sizeimage = 320 * 240, | |
161 | .bytesperline = 320, | |
162 | .colorspace = V4L2_COLORSPACE_SRGB, | |
163 | .priv = PB0100_CROP_TO_VGA | |
164 | }, | |
165 | { | |
166 | 352, | |
167 | 288, | |
168 | V4L2_PIX_FMT_SGRBG8, | |
169 | V4L2_FIELD_NONE, | |
170 | .sizeimage = 352 * 288, | |
171 | .bytesperline = 352, | |
172 | .colorspace = V4L2_COLORSPACE_SRGB, | |
173 | .priv = 0 | |
174 | } | |
175 | }; | |
176 | ||
4c98834a EA |
177 | static int pb0100_probe(struct sd *sd) |
178 | { | |
179 | u16 sensor; | |
180 | int i, err; | |
181 | s32 *sensor_settings; | |
182 | ||
183 | err = stv06xx_read_sensor(sd, PB_IDENT, &sensor); | |
184 | ||
185 | if (err < 0) | |
186 | return -ENODEV; | |
187 | ||
188 | if ((sensor >> 8) == 0x64) { | |
189 | sensor_settings = kmalloc( | |
766231ab | 190 | ARRAY_SIZE(pb0100_ctrl) * sizeof(s32), |
4c98834a EA |
191 | GFP_KERNEL); |
192 | if (!sensor_settings) | |
193 | return -ENOMEM; | |
194 | ||
133a9fe9 | 195 | pr_info("Photobit pb0100 sensor detected\n"); |
4c98834a | 196 | |
766231ab EA |
197 | sd->gspca_dev.cam.cam_mode = pb0100_mode; |
198 | sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); | |
199 | sd->desc.ctrls = pb0100_ctrl; | |
200 | sd->desc.nctrls = ARRAY_SIZE(pb0100_ctrl); | |
201 | for (i = 0; i < sd->desc.nctrls; i++) | |
202 | sensor_settings[i] = pb0100_ctrl[i].qctrl.default_value; | |
4c98834a EA |
203 | sd->sensor_priv = sensor_settings; |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | return -ENODEV; | |
209 | } | |
210 | ||
211 | static int pb0100_start(struct sd *sd) | |
212 | { | |
c0b33bdc HG |
213 | int err, packet_size, max_packet_size; |
214 | struct usb_host_interface *alt; | |
215 | struct usb_interface *intf; | |
4c98834a EA |
216 | struct cam *cam = &sd->gspca_dev.cam; |
217 | s32 *sensor_settings = sd->sensor_priv; | |
218 | u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv; | |
219 | ||
c0b33bdc HG |
220 | intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); |
221 | alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); | |
dadefe3b JJ |
222 | if (!alt) |
223 | return -ENODEV; | |
c0b33bdc HG |
224 | packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); |
225 | ||
226 | /* If we don't have enough bandwidth use a lower framerate */ | |
227 | max_packet_size = sd->sensor->max_packet_size[sd->gspca_dev.curr_mode]; | |
228 | if (packet_size < max_packet_size) | |
229 | stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1)); | |
230 | else | |
231 | stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(5)|BIT(3)|BIT(1)); | |
232 | ||
4c98834a EA |
233 | /* Setup sensor window */ |
234 | if (mode & PB0100_CROP_TO_VGA) { | |
235 | stv06xx_write_sensor(sd, PB_RSTART, 30); | |
236 | stv06xx_write_sensor(sd, PB_CSTART, 20); | |
237 | stv06xx_write_sensor(sd, PB_RWSIZE, 240 - 1); | |
238 | stv06xx_write_sensor(sd, PB_CWSIZE, 320 - 1); | |
239 | } else { | |
240 | stv06xx_write_sensor(sd, PB_RSTART, 8); | |
241 | stv06xx_write_sensor(sd, PB_CSTART, 4); | |
242 | stv06xx_write_sensor(sd, PB_RWSIZE, 288 - 1); | |
243 | stv06xx_write_sensor(sd, PB_CWSIZE, 352 - 1); | |
244 | } | |
245 | ||
246 | if (mode & PB0100_SUBSAMPLE) { | |
247 | stv06xx_write_bridge(sd, STV_Y_CTRL, 0x02); /* Wrong, FIXME */ | |
248 | stv06xx_write_bridge(sd, STV_X_CTRL, 0x06); | |
249 | ||
250 | stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x10); | |
251 | } else { | |
252 | stv06xx_write_bridge(sd, STV_Y_CTRL, 0x01); | |
253 | stv06xx_write_bridge(sd, STV_X_CTRL, 0x0a); | |
254 | /* larger -> slower */ | |
255 | stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); | |
256 | } | |
257 | ||
258 | /* set_gain also sets red and blue balance */ | |
259 | pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); | |
260 | pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]); | |
261 | pb0100_set_autogain_target(&sd->gspca_dev, | |
262 | sensor_settings[AUTOGAIN_TARGET_IDX]); | |
263 | pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]); | |
264 | ||
265 | err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1)); | |
266 | PDEBUG(D_STREAM, "Started stream, status: %d", err); | |
267 | ||
268 | return (err < 0) ? err : 0; | |
269 | } | |
270 | ||
271 | static int pb0100_stop(struct sd *sd) | |
272 | { | |
273 | int err; | |
274 | ||
275 | err = stv06xx_write_sensor(sd, PB_ABORTFRAME, 1); | |
276 | ||
277 | if (err < 0) | |
278 | goto out; | |
279 | ||
280 | /* Set bit 1 to zero */ | |
281 | err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); | |
282 | ||
283 | PDEBUG(D_STREAM, "Halting stream"); | |
284 | out: | |
285 | return (err < 0) ? err : 0; | |
286 | } | |
287 | ||
d5b53f46 EA |
288 | static void pb0100_disconnect(struct sd *sd) |
289 | { | |
290 | sd->sensor = NULL; | |
291 | kfree(sd->sensor_priv); | |
292 | } | |
293 | ||
4c98834a EA |
294 | /* FIXME: Sort the init commands out and put them into tables, |
295 | this is only for getting the camera to work */ | |
296 | /* FIXME: No error handling for now, | |
297 | add this once the init has been converted to proper tables */ | |
298 | static int pb0100_init(struct sd *sd) | |
299 | { | |
300 | stv06xx_write_bridge(sd, STV_REG00, 1); | |
301 | stv06xx_write_bridge(sd, STV_SCAN_RATE, 0); | |
302 | ||
303 | /* Reset sensor */ | |
304 | stv06xx_write_sensor(sd, PB_RESET, 1); | |
305 | stv06xx_write_sensor(sd, PB_RESET, 0); | |
306 | ||
307 | /* Disable chip */ | |
308 | stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)); | |
309 | ||
310 | /* Gain stuff...*/ | |
311 | stv06xx_write_sensor(sd, PB_PREADCTRL, BIT(12)|BIT(10)|BIT(6)); | |
312 | stv06xx_write_sensor(sd, PB_ADCGLOBALGAIN, 12); | |
313 | ||
314 | /* Set up auto-exposure */ | |
315 | /* ADC VREF_HI new setting for a transition | |
316 | from the Expose1 to the Expose2 setting */ | |
317 | stv06xx_write_sensor(sd, PB_R28, 12); | |
318 | /* gain max for autoexposure */ | |
319 | stv06xx_write_sensor(sd, PB_ADCMAXGAIN, 180); | |
320 | /* gain min for autoexposure */ | |
321 | stv06xx_write_sensor(sd, PB_ADCMINGAIN, 12); | |
322 | /* Maximum frame integration time (programmed into R8) | |
323 | allowed for auto-exposure routine */ | |
324 | stv06xx_write_sensor(sd, PB_R54, 3); | |
325 | /* Minimum frame integration time (programmed into R8) | |
326 | allowed for auto-exposure routine */ | |
327 | stv06xx_write_sensor(sd, PB_R55, 0); | |
328 | stv06xx_write_sensor(sd, PB_UPDATEINT, 1); | |
329 | /* R15 Expose0 (maximum that auto-exposure may use) */ | |
330 | stv06xx_write_sensor(sd, PB_R15, 800); | |
331 | /* R17 Expose2 (minimum that auto-exposure may use) */ | |
332 | stv06xx_write_sensor(sd, PB_R17, 10); | |
333 | ||
334 | stv06xx_write_sensor(sd, PB_EXPGAIN, 0); | |
335 | ||
336 | /* 0x14 */ | |
337 | stv06xx_write_sensor(sd, PB_VOFFSET, 0); | |
338 | /* 0x0D */ | |
339 | stv06xx_write_sensor(sd, PB_ADCGAINH, 11); | |
340 | /* Set black level (important!) */ | |
341 | stv06xx_write_sensor(sd, PB_ADCGAINL, 0); | |
342 | ||
343 | /* ??? */ | |
344 | stv06xx_write_bridge(sd, STV_REG00, 0x11); | |
345 | stv06xx_write_bridge(sd, STV_REG03, 0x45); | |
346 | stv06xx_write_bridge(sd, STV_REG04, 0x07); | |
347 | ||
4c98834a EA |
348 | /* Scan/timing for the sensor */ |
349 | stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1)); | |
350 | stv06xx_write_sensor(sd, PB_CFILLIN, 14); | |
351 | stv06xx_write_sensor(sd, PB_VBL, 0); | |
352 | stv06xx_write_sensor(sd, PB_FINTTIME, 0); | |
353 | stv06xx_write_sensor(sd, PB_RINTTIME, 123); | |
354 | ||
355 | stv06xx_write_bridge(sd, STV_REG01, 0xc2); | |
356 | stv06xx_write_bridge(sd, STV_REG02, 0xb0); | |
357 | return 0; | |
358 | } | |
359 | ||
360 | static int pb0100_dump(struct sd *sd) | |
361 | { | |
362 | return 0; | |
363 | } | |
364 | ||
365 | static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val) | |
366 | { | |
367 | struct sd *sd = (struct sd *) gspca_dev; | |
368 | s32 *sensor_settings = sd->sensor_priv; | |
369 | ||
370 | *val = sensor_settings[GAIN_IDX]; | |
371 | ||
372 | return 0; | |
373 | } | |
374 | ||
375 | static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val) | |
376 | { | |
377 | int err; | |
378 | struct sd *sd = (struct sd *) gspca_dev; | |
379 | s32 *sensor_settings = sd->sensor_priv; | |
380 | ||
381 | if (sensor_settings[AUTOGAIN_IDX]) | |
382 | return -EBUSY; | |
383 | ||
384 | sensor_settings[GAIN_IDX] = val; | |
385 | err = stv06xx_write_sensor(sd, PB_G1GAIN, val); | |
386 | if (!err) | |
387 | err = stv06xx_write_sensor(sd, PB_G2GAIN, val); | |
388 | PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err); | |
389 | ||
390 | if (!err) | |
391 | err = pb0100_set_red_balance(gspca_dev, | |
392 | sensor_settings[RED_BALANCE_IDX]); | |
393 | if (!err) | |
394 | err = pb0100_set_blue_balance(gspca_dev, | |
395 | sensor_settings[BLUE_BALANCE_IDX]); | |
396 | ||
397 | return err; | |
398 | } | |
399 | ||
400 | static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) | |
401 | { | |
402 | struct sd *sd = (struct sd *) gspca_dev; | |
403 | s32 *sensor_settings = sd->sensor_priv; | |
404 | ||
405 | *val = sensor_settings[RED_BALANCE_IDX]; | |
406 | ||
407 | return 0; | |
408 | } | |
409 | ||
410 | static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) | |
411 | { | |
412 | int err; | |
413 | struct sd *sd = (struct sd *) gspca_dev; | |
414 | s32 *sensor_settings = sd->sensor_priv; | |
415 | ||
416 | if (sensor_settings[AUTOGAIN_IDX]) | |
417 | return -EBUSY; | |
418 | ||
419 | sensor_settings[RED_BALANCE_IDX] = val; | |
420 | val += sensor_settings[GAIN_IDX]; | |
421 | if (val < 0) | |
422 | val = 0; | |
423 | else if (val > 255) | |
424 | val = 255; | |
425 | ||
426 | err = stv06xx_write_sensor(sd, PB_RGAIN, val); | |
427 | PDEBUG(D_V4L2, "Set red gain to %d, status: %d", val, err); | |
428 | ||
429 | return err; | |
430 | } | |
431 | ||
432 | static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) | |
433 | { | |
434 | struct sd *sd = (struct sd *) gspca_dev; | |
435 | s32 *sensor_settings = sd->sensor_priv; | |
436 | ||
437 | *val = sensor_settings[BLUE_BALANCE_IDX]; | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
442 | static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) | |
443 | { | |
444 | int err; | |
445 | struct sd *sd = (struct sd *) gspca_dev; | |
446 | s32 *sensor_settings = sd->sensor_priv; | |
447 | ||
448 | if (sensor_settings[AUTOGAIN_IDX]) | |
449 | return -EBUSY; | |
450 | ||
451 | sensor_settings[BLUE_BALANCE_IDX] = val; | |
452 | val += sensor_settings[GAIN_IDX]; | |
453 | if (val < 0) | |
454 | val = 0; | |
455 | else if (val > 255) | |
456 | val = 255; | |
457 | ||
458 | err = stv06xx_write_sensor(sd, PB_BGAIN, val); | |
459 | PDEBUG(D_V4L2, "Set blue gain to %d, status: %d", val, err); | |
460 | ||
461 | return err; | |
462 | } | |
463 | ||
464 | static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) | |
465 | { | |
466 | struct sd *sd = (struct sd *) gspca_dev; | |
467 | s32 *sensor_settings = sd->sensor_priv; | |
468 | ||
469 | *val = sensor_settings[EXPOSURE_IDX]; | |
470 | ||
471 | return 0; | |
472 | } | |
473 | ||
474 | static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val) | |
475 | { | |
476 | int err; | |
477 | struct sd *sd = (struct sd *) gspca_dev; | |
478 | s32 *sensor_settings = sd->sensor_priv; | |
479 | ||
480 | if (sensor_settings[AUTOGAIN_IDX]) | |
481 | return -EBUSY; | |
482 | ||
483 | sensor_settings[EXPOSURE_IDX] = val; | |
484 | err = stv06xx_write_sensor(sd, PB_RINTTIME, val); | |
485 | PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err); | |
486 | ||
487 | return err; | |
488 | } | |
489 | ||
490 | static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val) | |
491 | { | |
492 | struct sd *sd = (struct sd *) gspca_dev; | |
493 | s32 *sensor_settings = sd->sensor_priv; | |
494 | ||
495 | *val = sensor_settings[AUTOGAIN_IDX]; | |
496 | ||
497 | return 0; | |
498 | } | |
499 | ||
500 | static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) | |
501 | { | |
502 | int err; | |
503 | struct sd *sd = (struct sd *) gspca_dev; | |
504 | s32 *sensor_settings = sd->sensor_priv; | |
505 | ||
506 | sensor_settings[AUTOGAIN_IDX] = val; | |
507 | if (sensor_settings[AUTOGAIN_IDX]) { | |
508 | if (sensor_settings[NATURAL_IDX]) | |
509 | val = BIT(6)|BIT(4)|BIT(0); | |
510 | else | |
511 | val = BIT(4)|BIT(0); | |
512 | } else | |
513 | val = 0; | |
514 | ||
515 | err = stv06xx_write_sensor(sd, PB_EXPGAIN, val); | |
516 | PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d", | |
517 | sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX], | |
518 | err); | |
519 | ||
520 | return err; | |
521 | } | |
522 | ||
523 | static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val) | |
524 | { | |
525 | struct sd *sd = (struct sd *) gspca_dev; | |
526 | s32 *sensor_settings = sd->sensor_priv; | |
527 | ||
528 | *val = sensor_settings[AUTOGAIN_TARGET_IDX]; | |
529 | ||
530 | return 0; | |
531 | } | |
532 | ||
533 | static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) | |
534 | { | |
535 | int err, totalpixels, brightpixels, darkpixels; | |
536 | struct sd *sd = (struct sd *) gspca_dev; | |
537 | s32 *sensor_settings = sd->sensor_priv; | |
538 | ||
539 | sensor_settings[AUTOGAIN_TARGET_IDX] = val; | |
540 | ||
541 | /* Number of pixels counted by the sensor when subsampling the pixels. | |
542 | * Slightly larger than the real value to avoid oscillation */ | |
543 | totalpixels = gspca_dev->width * gspca_dev->height; | |
544 | totalpixels = totalpixels/(8*8) + totalpixels/(64*64); | |
545 | ||
546 | brightpixels = (totalpixels * val) >> 8; | |
547 | darkpixels = totalpixels - brightpixels; | |
548 | err = stv06xx_write_sensor(sd, PB_R21, brightpixels); | |
549 | if (!err) | |
550 | err = stv06xx_write_sensor(sd, PB_R22, darkpixels); | |
551 | ||
552 | PDEBUG(D_V4L2, "Set autogain target to %d, status: %d", val, err); | |
553 | ||
554 | return err; | |
555 | } | |
556 | ||
557 | static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val) | |
558 | { | |
559 | struct sd *sd = (struct sd *) gspca_dev; | |
560 | s32 *sensor_settings = sd->sensor_priv; | |
561 | ||
562 | *val = sensor_settings[NATURAL_IDX]; | |
563 | ||
564 | return 0; | |
565 | } | |
566 | ||
567 | static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val) | |
568 | { | |
569 | struct sd *sd = (struct sd *) gspca_dev; | |
570 | s32 *sensor_settings = sd->sensor_priv; | |
571 | ||
572 | sensor_settings[NATURAL_IDX] = val; | |
573 | ||
574 | return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]); | |
575 | } |