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 
47 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
48 
49 #include "stv06xx_pb0100.h"
50 
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 
pb0100_probe(struct sd * sd)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(
190 				ARRAY_SIZE(pb0100_ctrl) * sizeof(s32),
191 				GFP_KERNEL);
192 		if (!sensor_settings)
193 			return -ENOMEM;
194 
195 		pr_info("Photobit pb0100 sensor detected\n");
196 
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;
203 		sd->sensor_priv = sensor_settings;
204 
205 		return 0;
206 	}
207 
208 	return -ENODEV;
209 }
210 
pb0100_start(struct sd * sd)211 static int pb0100_start(struct sd *sd)
212 {
213 	int err, packet_size, max_packet_size;
214 	struct usb_host_interface *alt;
215 	struct usb_interface *intf;
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 
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);
222 	if (!alt)
223 		return -ENODEV;
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 
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 
pb0100_stop(struct sd * sd)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 
pb0100_disconnect(struct sd * sd)288 static void pb0100_disconnect(struct sd *sd)
289 {
290 	sd->sensor = NULL;
291 	kfree(sd->sensor_priv);
292 }
293 
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 */
pb0100_init(struct sd * sd)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 
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 
pb0100_dump(struct sd * sd)360 static int pb0100_dump(struct sd *sd)
361 {
362 	return 0;
363 }
364 
pb0100_get_gain(struct gspca_dev * gspca_dev,__s32 * val)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 
pb0100_set_gain(struct gspca_dev * gspca_dev,__s32 val)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 
pb0100_get_red_balance(struct gspca_dev * gspca_dev,__s32 * val)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 
pb0100_set_red_balance(struct gspca_dev * gspca_dev,__s32 val)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 
pb0100_get_blue_balance(struct gspca_dev * gspca_dev,__s32 * val)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 
pb0100_set_blue_balance(struct gspca_dev * gspca_dev,__s32 val)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 
pb0100_get_exposure(struct gspca_dev * gspca_dev,__s32 * val)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 
pb0100_set_exposure(struct gspca_dev * gspca_dev,__s32 val)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 
pb0100_get_autogain(struct gspca_dev * gspca_dev,__s32 * val)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 
pb0100_set_autogain(struct gspca_dev * gspca_dev,__s32 val)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 
pb0100_get_autogain_target(struct gspca_dev * gspca_dev,__s32 * val)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 
pb0100_set_autogain_target(struct gspca_dev * gspca_dev,__s32 val)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 
pb0100_get_natural(struct gspca_dev * gspca_dev,__s32 * val)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 
pb0100_set_natural(struct gspca_dev * gspca_dev,__s32 val)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 }
576