1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Driver for the po1030 sensor
4  *
5  * Copyright (c) 2008 Erik Andrén
6  * Copyright (c) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
7  * Copyright (c) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
8  *
9  * Portions of code to USB interface and ALi driver software,
10  * Copyright (c) 2006 Willem Duinker
11  * v4l2 interface modeled after the V4L2 driver
12  * for SN9C10x PC Camera Controllers
13  */
14 
15 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
16 
17 #include "m5602_po1030.h"
18 
19 static int po1030_s_ctrl(struct v4l2_ctrl *ctrl);
20 static void po1030_dump_registers(struct sd *sd);
21 
22 static const unsigned char preinit_po1030[][3] = {
23 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
24 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
25 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
26 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
27 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
28 	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
29 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
30 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
31 	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
32 	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
33 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
34 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
35 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
36 
37 	{SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
38 
39 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
40 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
41 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
42 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
43 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
44 	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
45 	{BRIDGE, M5602_XB_GPIO_DAT, 0x00}
46 };
47 
48 static const unsigned char init_po1030[][3] = {
49 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02},
50 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0},
51 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
52 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
53 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0},
54 	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00},
55 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c},
56 
57 	{SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)},
58 
59 	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
60 	{BRIDGE, M5602_XB_GPIO_DAT, 0x04},
61 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
62 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
63 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06},
64 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x02},
65 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04},
66 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
67 	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
68 	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
69 
70 	{SENSOR, PO1030_AUTOCTRL2, 0x04},
71 
72 	{SENSOR, PO1030_OUTFORMCTRL2, PO1030_RAW_RGB_BAYER},
73 	{SENSOR, PO1030_AUTOCTRL1, PO1030_WEIGHT_WIN_2X},
74 
75 	{SENSOR, PO1030_CONTROL2, 0x03},
76 	{SENSOR, 0x21, 0x90},
77 	{SENSOR, PO1030_YTARGET, 0x60},
78 	{SENSOR, 0x59, 0x13},
79 	{SENSOR, PO1030_OUTFORMCTRL1, PO1030_HREF_ENABLE},
80 	{SENSOR, PO1030_EDGE_ENH_OFF, 0x00},
81 	{SENSOR, PO1030_EGA, 0x80},
82 	{SENSOR, 0x78, 0x14},
83 	{SENSOR, 0x6f, 0x01},
84 	{SENSOR, PO1030_GLOBALGAINMAX, 0x14},
85 	{SENSOR, PO1030_Cb_U_GAIN, 0x38},
86 	{SENSOR, PO1030_Cr_V_GAIN, 0x38},
87 	{SENSOR, PO1030_CONTROL1, PO1030_SHUTTER_MODE |
88 				  PO1030_AUTO_SUBSAMPLING |
89 				  PO1030_FRAME_EQUAL},
90 	{SENSOR, PO1030_GC0, 0x10},
91 	{SENSOR, PO1030_GC1, 0x20},
92 	{SENSOR, PO1030_GC2, 0x40},
93 	{SENSOR, PO1030_GC3, 0x60},
94 	{SENSOR, PO1030_GC4, 0x80},
95 	{SENSOR, PO1030_GC5, 0xa0},
96 	{SENSOR, PO1030_GC6, 0xc0},
97 	{SENSOR, PO1030_GC7, 0xff},
98 
99 	/* Set the width to 751 */
100 	{SENSOR, PO1030_FRAMEWIDTH_H, 0x02},
101 	{SENSOR, PO1030_FRAMEWIDTH_L, 0xef},
102 
103 	/* Set the height to 540 */
104 	{SENSOR, PO1030_FRAMEHEIGHT_H, 0x02},
105 	{SENSOR, PO1030_FRAMEHEIGHT_L, 0x1c},
106 
107 	/* Set the x window to 1 */
108 	{SENSOR, PO1030_WINDOWX_H, 0x00},
109 	{SENSOR, PO1030_WINDOWX_L, 0x01},
110 
111 	/* Set the y window to 1 */
112 	{SENSOR, PO1030_WINDOWY_H, 0x00},
113 	{SENSOR, PO1030_WINDOWY_L, 0x01},
114 
115 	/* with a very low lighted environment increase the exposure but
116 	 * decrease the FPS (Frame Per Second) */
117 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00},
118 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0},
119 
120 	{BRIDGE, M5602_XB_GPIO_DIR, 0x05},
121 	{BRIDGE, M5602_XB_GPIO_DAT, 0x00},
122 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06},
123 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00},
124 };
125 
126 static struct v4l2_pix_format po1030_modes[] = {
127 	{
128 		640,
129 		480,
130 		V4L2_PIX_FMT_SBGGR8,
131 		V4L2_FIELD_NONE,
132 		.sizeimage = 640 * 480,
133 		.bytesperline = 640,
134 		.colorspace = V4L2_COLORSPACE_SRGB,
135 		.priv = 2
136 	}
137 };
138 
139 static const struct v4l2_ctrl_ops po1030_ctrl_ops = {
140 	.s_ctrl = po1030_s_ctrl,
141 };
142 
143 static const struct v4l2_ctrl_config po1030_greenbal_cfg = {
144 	.ops	= &po1030_ctrl_ops,
145 	.id	= M5602_V4L2_CID_GREEN_BALANCE,
146 	.name	= "Green Balance",
147 	.type	= V4L2_CTRL_TYPE_INTEGER,
148 	.min	= 0,
149 	.max	= 255,
150 	.step	= 1,
151 	.def	= PO1030_GREEN_GAIN_DEFAULT,
152 	.flags	= V4L2_CTRL_FLAG_SLIDER,
153 };
154 
155 int po1030_probe(struct sd *sd)
156 {
157 	u8 dev_id_h = 0, i;
158 	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
159 
160 	if (force_sensor) {
161 		if (force_sensor == PO1030_SENSOR) {
162 			pr_info("Forcing a %s sensor\n", po1030.name);
163 			goto sensor_found;
164 		}
165 		/* If we want to force another sensor, don't try to probe this
166 		 * one */
167 		return -ENODEV;
168 	}
169 
170 	gspca_dbg(gspca_dev, D_PROBE, "Probing for a po1030 sensor\n");
171 
172 	/* Run the pre-init to actually probe the unit */
173 	for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
174 		u8 data = preinit_po1030[i][2];
175 		if (preinit_po1030[i][0] == SENSOR)
176 			m5602_write_sensor(sd,
177 				preinit_po1030[i][1], &data, 1);
178 		else
179 			m5602_write_bridge(sd, preinit_po1030[i][1], data);
180 	}
181 
182 	if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1))
183 		return -ENODEV;
184 
185 	if (dev_id_h == 0x30) {
186 		pr_info("Detected a po1030 sensor\n");
187 		goto sensor_found;
188 	}
189 	return -ENODEV;
190 
191 sensor_found:
192 	sd->gspca_dev.cam.cam_mode = po1030_modes;
193 	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(po1030_modes);
194 
195 	return 0;
196 }
197 
198 int po1030_init(struct sd *sd)
199 {
200 	int i, err = 0;
201 
202 	/* Init the sensor */
203 	for (i = 0; i < ARRAY_SIZE(init_po1030) && !err; i++) {
204 		u8 data[2] = {0x00, 0x00};
205 
206 		switch (init_po1030[i][0]) {
207 		case BRIDGE:
208 			err = m5602_write_bridge(sd,
209 				init_po1030[i][1],
210 				init_po1030[i][2]);
211 			break;
212 
213 		case SENSOR:
214 			data[0] = init_po1030[i][2];
215 			err = m5602_write_sensor(sd,
216 				init_po1030[i][1], data, 1);
217 			break;
218 
219 		default:
220 			pr_info("Invalid stream command, exiting init\n");
221 			return -EINVAL;
222 		}
223 	}
224 	if (err < 0)
225 		return err;
226 
227 	if (dump_sensor)
228 		po1030_dump_registers(sd);
229 
230 	return 0;
231 }
232 
233 int po1030_init_controls(struct sd *sd)
234 {
235 	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
236 
237 	sd->gspca_dev.vdev.ctrl_handler = hdl;
238 	v4l2_ctrl_handler_init(hdl, 9);
239 
240 	sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops,
241 					       V4L2_CID_AUTO_WHITE_BALANCE,
242 					       0, 1, 1, 0);
243 	sd->green_bal = v4l2_ctrl_new_custom(hdl, &po1030_greenbal_cfg, NULL);
244 	sd->red_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops,
245 					V4L2_CID_RED_BALANCE, 0, 255, 1,
246 					PO1030_RED_GAIN_DEFAULT);
247 	sd->blue_bal = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops,
248 					V4L2_CID_BLUE_BALANCE, 0, 255, 1,
249 					PO1030_BLUE_GAIN_DEFAULT);
250 
251 	sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &po1030_ctrl_ops,
252 			  V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_MANUAL);
253 	sd->expo = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_EXPOSURE,
254 			  0, 0x2ff, 1, PO1030_EXPOSURE_DEFAULT);
255 
256 	sd->gain = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_GAIN, 0,
257 				     0x4f, 1, PO1030_GLOBAL_GAIN_DEFAULT);
258 
259 	sd->hflip = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_HFLIP,
260 				      0, 1, 1, 0);
261 	sd->vflip = v4l2_ctrl_new_std(hdl, &po1030_ctrl_ops, V4L2_CID_VFLIP,
262 				      0, 1, 1, 0);
263 
264 	if (hdl->error) {
265 		pr_err("Could not initialize controls\n");
266 		return hdl->error;
267 	}
268 
269 	v4l2_ctrl_auto_cluster(4, &sd->auto_white_bal, 0, false);
270 	v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false);
271 	v4l2_ctrl_cluster(2, &sd->hflip);
272 
273 	return 0;
274 }
275 
276 int po1030_start(struct sd *sd)
277 {
278 	struct cam *cam = &sd->gspca_dev.cam;
279 	int i, err = 0;
280 	int width = cam->cam_mode[sd->gspca_dev.curr_mode].width;
281 	int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;
282 	int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
283 	u8 data;
284 
285 	switch (width) {
286 	case 320:
287 		data = PO1030_SUBSAMPLING;
288 		err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1);
289 		if (err < 0)
290 			return err;
291 
292 		data = ((width + 3) >> 8) & 0xff;
293 		err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1);
294 		if (err < 0)
295 			return err;
296 
297 		data = (width + 3) & 0xff;
298 		err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1);
299 		if (err < 0)
300 			return err;
301 
302 		data = ((height + 1) >> 8) & 0xff;
303 		err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1);
304 		if (err < 0)
305 			return err;
306 
307 		data = (height + 1) & 0xff;
308 		err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1);
309 
310 		height += 6;
311 		width -= 1;
312 		break;
313 
314 	case 640:
315 		data = 0;
316 		err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1);
317 		if (err < 0)
318 			return err;
319 
320 		data = ((width + 7) >> 8) & 0xff;
321 		err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1);
322 		if (err < 0)
323 			return err;
324 
325 		data = (width + 7) & 0xff;
326 		err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1);
327 		if (err < 0)
328 			return err;
329 
330 		data = ((height + 3) >> 8) & 0xff;
331 		err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1);
332 		if (err < 0)
333 			return err;
334 
335 		data = (height + 3) & 0xff;
336 		err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1);
337 
338 		height += 12;
339 		width -= 2;
340 		break;
341 	}
342 	err = m5602_write_bridge(sd, M5602_XB_SENSOR_TYPE, 0x0c);
343 	if (err < 0)
344 		return err;
345 
346 	err = m5602_write_bridge(sd, M5602_XB_LINE_OF_FRAME_H, 0x81);
347 	if (err < 0)
348 		return err;
349 
350 	err = m5602_write_bridge(sd, M5602_XB_PIX_OF_LINE_H, 0x82);
351 	if (err < 0)
352 		return err;
353 
354 	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0x01);
355 	if (err < 0)
356 		return err;
357 
358 	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA,
359 				 ((ver_offs >> 8) & 0xff));
360 	if (err < 0)
361 		return err;
362 
363 	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff));
364 	if (err < 0)
365 		return err;
366 
367 	for (i = 0; i < 2 && !err; i++)
368 		err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
369 	if (err < 0)
370 		return err;
371 
372 	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
373 	if (err < 0)
374 		return err;
375 
376 	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
377 	if (err < 0)
378 		return err;
379 
380 	for (i = 0; i < 2 && !err; i++)
381 		err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
382 
383 	for (i = 0; i < 2 && !err; i++)
384 		err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
385 
386 	for (i = 0; i < 2 && !err; i++)
387 		err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0);
388 	if (err < 0)
389 		return err;
390 
391 	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width >> 8) & 0xff);
392 	if (err < 0)
393 		return err;
394 
395 	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width & 0xff));
396 	if (err < 0)
397 		return err;
398 
399 	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
400 	return err;
401 }
402 
403 static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
404 {
405 	struct sd *sd = (struct sd *) gspca_dev;
406 	u8 i2c_data;
407 	int err;
408 
409 	gspca_dbg(gspca_dev, D_CONF, "Set exposure to %d\n", val & 0xffff);
410 
411 	i2c_data = ((val & 0xff00) >> 8);
412 	gspca_dbg(gspca_dev, D_CONF, "Set exposure to high byte to 0x%x\n",
413 		  i2c_data);
414 
415 	err = m5602_write_sensor(sd, PO1030_INTEGLINES_H,
416 				  &i2c_data, 1);
417 	if (err < 0)
418 		return err;
419 
420 	i2c_data = (val & 0xff);
421 	gspca_dbg(gspca_dev, D_CONF, "Set exposure to low byte to 0x%x\n",
422 		  i2c_data);
423 	err = m5602_write_sensor(sd, PO1030_INTEGLINES_M,
424 				  &i2c_data, 1);
425 
426 	return err;
427 }
428 
429 static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val)
430 {
431 	struct sd *sd = (struct sd *) gspca_dev;
432 	u8 i2c_data;
433 	int err;
434 
435 	i2c_data = val & 0xff;
436 	gspca_dbg(gspca_dev, D_CONF, "Set global gain to %d\n", i2c_data);
437 	err = m5602_write_sensor(sd, PO1030_GLOBALGAIN,
438 				 &i2c_data, 1);
439 	return err;
440 }
441 
442 static int po1030_set_hvflip(struct gspca_dev *gspca_dev)
443 {
444 	struct sd *sd = (struct sd *) gspca_dev;
445 	u8 i2c_data;
446 	int err;
447 
448 	gspca_dbg(gspca_dev, D_CONF, "Set hvflip %d %d\n",
449 		  sd->hflip->val, sd->vflip->val);
450 	err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1);
451 	if (err < 0)
452 		return err;
453 
454 	i2c_data = (0x3f & i2c_data) | (sd->hflip->val << 7) |
455 		   (sd->vflip->val << 6);
456 
457 	err = m5602_write_sensor(sd, PO1030_CONTROL2,
458 				 &i2c_data, 1);
459 
460 	return err;
461 }
462 
463 static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
464 {
465 	struct sd *sd = (struct sd *) gspca_dev;
466 	u8 i2c_data;
467 	int err;
468 
469 	i2c_data = val & 0xff;
470 	gspca_dbg(gspca_dev, D_CONF, "Set red gain to %d\n", i2c_data);
471 	err = m5602_write_sensor(sd, PO1030_RED_GAIN,
472 				  &i2c_data, 1);
473 	return err;
474 }
475 
476 static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
477 {
478 	struct sd *sd = (struct sd *) gspca_dev;
479 	u8 i2c_data;
480 	int err;
481 
482 	i2c_data = val & 0xff;
483 	gspca_dbg(gspca_dev, D_CONF, "Set blue gain to %d\n", i2c_data);
484 	err = m5602_write_sensor(sd, PO1030_BLUE_GAIN,
485 				  &i2c_data, 1);
486 
487 	return err;
488 }
489 
490 static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val)
491 {
492 	struct sd *sd = (struct sd *) gspca_dev;
493 	u8 i2c_data;
494 	int err;
495 
496 	i2c_data = val & 0xff;
497 	gspca_dbg(gspca_dev, D_CONF, "Set green gain to %d\n", i2c_data);
498 
499 	err = m5602_write_sensor(sd, PO1030_GREEN_1_GAIN,
500 			   &i2c_data, 1);
501 	if (err < 0)
502 		return err;
503 
504 	return m5602_write_sensor(sd, PO1030_GREEN_2_GAIN,
505 				 &i2c_data, 1);
506 }
507 
508 static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev,
509 					 __s32 val)
510 {
511 	struct sd *sd = (struct sd *) gspca_dev;
512 	u8 i2c_data;
513 	int err;
514 
515 	err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
516 	if (err < 0)
517 		return err;
518 
519 	gspca_dbg(gspca_dev, D_CONF, "Set auto white balance to %d\n", val);
520 	i2c_data = (i2c_data & 0xfe) | (val & 0x01);
521 	err = m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
522 	return err;
523 }
524 
525 static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev,
526 				    __s32 val)
527 {
528 	struct sd *sd = (struct sd *) gspca_dev;
529 	u8 i2c_data;
530 	int err;
531 
532 	err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
533 	if (err < 0)
534 		return err;
535 
536 	gspca_dbg(gspca_dev, D_CONF, "Set auto exposure to %d\n", val);
537 	val = (val == V4L2_EXPOSURE_AUTO);
538 	i2c_data = (i2c_data & 0xfd) | ((val & 0x01) << 1);
539 	return m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1);
540 }
541 
542 void po1030_disconnect(struct sd *sd)
543 {
544 	sd->sensor = NULL;
545 }
546 
547 static int po1030_s_ctrl(struct v4l2_ctrl *ctrl)
548 {
549 	struct gspca_dev *gspca_dev =
550 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
551 	struct sd *sd = (struct sd *) gspca_dev;
552 	int err;
553 
554 	if (!gspca_dev->streaming)
555 		return 0;
556 
557 	switch (ctrl->id) {
558 	case V4L2_CID_AUTO_WHITE_BALANCE:
559 		err = po1030_set_auto_white_balance(gspca_dev, ctrl->val);
560 		if (err || ctrl->val)
561 			return err;
562 		err = po1030_set_green_balance(gspca_dev, sd->green_bal->val);
563 		if (err)
564 			return err;
565 		err = po1030_set_red_balance(gspca_dev, sd->red_bal->val);
566 		if (err)
567 			return err;
568 		err = po1030_set_blue_balance(gspca_dev, sd->blue_bal->val);
569 		break;
570 	case V4L2_CID_EXPOSURE_AUTO:
571 		err = po1030_set_auto_exposure(gspca_dev, ctrl->val);
572 		if (err || ctrl->val == V4L2_EXPOSURE_AUTO)
573 			return err;
574 		err = po1030_set_exposure(gspca_dev, sd->expo->val);
575 		break;
576 	case V4L2_CID_GAIN:
577 		err = po1030_set_gain(gspca_dev, ctrl->val);
578 		break;
579 	case V4L2_CID_HFLIP:
580 		err = po1030_set_hvflip(gspca_dev);
581 		break;
582 	default:
583 		return -EINVAL;
584 	}
585 
586 	return err;
587 }
588 
589 static void po1030_dump_registers(struct sd *sd)
590 {
591 	int address;
592 	u8 value = 0;
593 
594 	pr_info("Dumping the po1030 sensor core registers\n");
595 	for (address = 0; address < 0x7f; address++) {
596 		m5602_read_sensor(sd, address, &value, 1);
597 		pr_info("register 0x%x contains 0x%x\n", address, value);
598 	}
599 
600 	pr_info("po1030 register state dump complete\n");
601 
602 	pr_info("Probing for which registers that are read/write\n");
603 	for (address = 0; address < 0xff; address++) {
604 		u8 old_value, ctrl_value;
605 		u8 test_value[2] = {0xff, 0xff};
606 
607 		m5602_read_sensor(sd, address, &old_value, 1);
608 		m5602_write_sensor(sd, address, test_value, 1);
609 		m5602_read_sensor(sd, address, &ctrl_value, 1);
610 
611 		if (ctrl_value == test_value[0])
612 			pr_info("register 0x%x is writeable\n", address);
613 		else
614 			pr_info("register 0x%x is read only\n", address);
615 
616 		/* Restore original value */
617 		m5602_write_sensor(sd, address, &old_value, 1);
618 	}
619 }
620