xref: /linux/drivers/media/usb/gspca/m5602/m5602_s5k83a.c (revision 3d0fe49454652117522f60bfbefb978ba0e5300b)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Driver for the s5k83a 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 <linux/kthread.h>
18 #include "m5602_s5k83a.h"
19 
20 static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl);
21 
22 static const struct v4l2_ctrl_ops s5k83a_ctrl_ops = {
23 	.s_ctrl = s5k83a_s_ctrl,
24 };
25 
26 static struct v4l2_pix_format s5k83a_modes[] = {
27 	{
28 		640,
29 		480,
30 		V4L2_PIX_FMT_SBGGR8,
31 		V4L2_FIELD_NONE,
32 		.sizeimage =
33 			640 * 480,
34 		.bytesperline = 640,
35 		.colorspace = V4L2_COLORSPACE_SRGB,
36 		.priv = 0
37 	}
38 };
39 
40 static const unsigned char preinit_s5k83a[][4] = {
41 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
42 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
43 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
44 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
45 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
46 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00},
47 	{BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00},
48 
49 	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
50 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
51 	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
52 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
53 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
54 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
55 	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
56 	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
57 	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
58 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
59 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
60 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
61 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
62 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
63 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
64 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
65 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
66 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
67 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
68 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
69 	{BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00},
70 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
71 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
72 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
73 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
74 	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
75 };
76 
77 /* This could probably be considerably shortened.
78    I don't have the hardware to experiment with it, patches welcome
79 */
80 static const unsigned char init_s5k83a[][4] = {
81 	/* The following sequence is useless after a clean boot
82 	   but is necessary after resume from suspend */
83 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
84 	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
85 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00},
86 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00},
87 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
88 	{BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00},
89 	{BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00},
90 	{BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00},
91 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00},
92 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00},
93 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
94 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
95 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
96 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
97 	{BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00},
98 	{BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00},
99 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
100 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00},
101 	{BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00},
102 	{BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00},
103 	{BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00},
104 	{BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00},
105 	{BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00},
106 	{BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00},
107 	{BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00},
108 
109 	{SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00},
110 	{SENSOR, 0xaf, 0x01, 0x00},
111 	{SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00},
112 	{SENSOR, 0x7b, 0xff, 0x00},
113 	{SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00},
114 	{SENSOR, 0x01, 0x50, 0x00},
115 	{SENSOR, 0x12, 0x20, 0x00},
116 	{SENSOR, 0x17, 0x40, 0x00},
117 	{SENSOR, 0x1c, 0x00, 0x00},
118 	{SENSOR, 0x02, 0x70, 0x00},
119 	{SENSOR, 0x03, 0x0b, 0x00},
120 	{SENSOR, 0x04, 0xf0, 0x00},
121 	{SENSOR, 0x05, 0x0b, 0x00},
122 	{SENSOR, 0x06, 0x71, 0x00},
123 	{SENSOR, 0x07, 0xe8, 0x00}, /* 488 */
124 	{SENSOR, 0x08, 0x02, 0x00},
125 	{SENSOR, 0x09, 0x88, 0x00}, /* 648 */
126 	{SENSOR, 0x14, 0x00, 0x00},
127 	{SENSOR, 0x15, 0x20, 0x00}, /* 32 */
128 	{SENSOR, 0x19, 0x00, 0x00},
129 	{SENSOR, 0x1a, 0x98, 0x00}, /* 152 */
130 	{SENSOR, 0x0f, 0x02, 0x00},
131 	{SENSOR, 0x10, 0xe5, 0x00}, /* 741 */
132 	/* normal colors
133 	(this is value after boot, but after tries can be different) */
134 	{SENSOR, 0x00, 0x06, 0x00},
135 };
136 
137 static const unsigned char start_s5k83a[][4] = {
138 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00},
139 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
140 	{BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00},
141 	{BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00},
142 	{BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00},
143 	{BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00},
144 	{BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00},
145 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
146 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
147 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
148 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
149 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00},
150 	{BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, /* 484 */
151 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
152 	{BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00},
153 	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
154 	{BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00},
155 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
156 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00},
157 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00},
158 	{BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, /* 639 */
159 	{BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00},
160 	{BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00},
161 	{BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00},
162 };
163 
164 static void s5k83a_dump_registers(struct sd *sd);
165 static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data);
166 static int s5k83a_set_led_indication(struct sd *sd, u8 val);
167 static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
168 				__s32 vflip, __s32 hflip);
169 
170 int s5k83a_probe(struct sd *sd)
171 {
172 	u8 prod_id = 0, ver_id = 0;
173 	int i, err = 0;
174 	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
175 
176 	if (force_sensor) {
177 		if (force_sensor == S5K83A_SENSOR) {
178 			pr_info("Forcing a %s sensor\n", s5k83a.name);
179 			goto sensor_found;
180 		}
181 		/* If we want to force another sensor, don't try to probe this
182 		 * one */
183 		return -ENODEV;
184 	}
185 
186 	gspca_dbg(gspca_dev, D_PROBE, "Probing for a s5k83a sensor\n");
187 
188 	/* Preinit the sensor */
189 	for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
190 		u8 data[2] = {preinit_s5k83a[i][2], preinit_s5k83a[i][3]};
191 		if (preinit_s5k83a[i][0] == SENSOR)
192 			err = m5602_write_sensor(sd, preinit_s5k83a[i][1],
193 				data, 2);
194 		else
195 			err = m5602_write_bridge(sd, preinit_s5k83a[i][1],
196 				data[0]);
197 	}
198 
199 	/* We don't know what register (if any) that contain the product id
200 	 * Just pick the first addresses that seem to produce the same results
201 	 * on multiple machines */
202 	if (m5602_read_sensor(sd, 0x00, &prod_id, 1))
203 		return -ENODEV;
204 
205 	if (m5602_read_sensor(sd, 0x01, &ver_id, 1))
206 		return -ENODEV;
207 
208 	if ((prod_id == 0xff) || (ver_id == 0xff))
209 		return -ENODEV;
210 	else
211 		pr_info("Detected a s5k83a sensor\n");
212 
213 sensor_found:
214 	sd->gspca_dev.cam.cam_mode = s5k83a_modes;
215 	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes);
216 
217 	/* null the pointer! thread is't running now */
218 	sd->rotation_thread = NULL;
219 
220 	return 0;
221 }
222 
223 int s5k83a_init(struct sd *sd)
224 {
225 	int i, err = 0;
226 
227 	for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) {
228 		u8 data[2] = {0x00, 0x00};
229 
230 		switch (init_s5k83a[i][0]) {
231 		case BRIDGE:
232 			err = m5602_write_bridge(sd,
233 					init_s5k83a[i][1],
234 					init_s5k83a[i][2]);
235 			break;
236 
237 		case SENSOR:
238 			data[0] = init_s5k83a[i][2];
239 			err = m5602_write_sensor(sd,
240 				init_s5k83a[i][1], data, 1);
241 			break;
242 
243 		case SENSOR_LONG:
244 			data[0] = init_s5k83a[i][2];
245 			data[1] = init_s5k83a[i][3];
246 			err = m5602_write_sensor(sd,
247 				init_s5k83a[i][1], data, 2);
248 			break;
249 		default:
250 			pr_info("Invalid stream command, exiting init\n");
251 			return -EINVAL;
252 		}
253 	}
254 
255 	if (dump_sensor)
256 		s5k83a_dump_registers(sd);
257 
258 	return err;
259 }
260 
261 int s5k83a_init_controls(struct sd *sd)
262 {
263 	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
264 
265 	sd->gspca_dev.vdev.ctrl_handler = hdl;
266 	v4l2_ctrl_handler_init(hdl, 6);
267 
268 	v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_BRIGHTNESS,
269 			  0, 255, 1, S5K83A_DEFAULT_BRIGHTNESS);
270 
271 	v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_EXPOSURE,
272 			  0, S5K83A_MAXIMUM_EXPOSURE, 1,
273 			  S5K83A_DEFAULT_EXPOSURE);
274 
275 	v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_GAIN,
276 			  0, 255, 1, S5K83A_DEFAULT_GAIN);
277 
278 	sd->hflip = v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_HFLIP,
279 				      0, 1, 1, 0);
280 	sd->vflip = v4l2_ctrl_new_std(hdl, &s5k83a_ctrl_ops, V4L2_CID_VFLIP,
281 				      0, 1, 1, 0);
282 
283 	if (hdl->error) {
284 		pr_err("Could not initialize controls\n");
285 		return hdl->error;
286 	}
287 
288 	v4l2_ctrl_cluster(2, &sd->hflip);
289 
290 	return 0;
291 }
292 
293 static int rotation_thread_function(void *data)
294 {
295 	struct sd *sd = (struct sd *) data;
296 	u8 reg, previous_rotation = 0;
297 	__s32 vflip, hflip;
298 
299 	set_current_state(TASK_INTERRUPTIBLE);
300 	while (!schedule_timeout(msecs_to_jiffies(100))) {
301 		if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock))
302 			break;
303 
304 		s5k83a_get_rotation(sd, &reg);
305 		if (previous_rotation != reg) {
306 			previous_rotation = reg;
307 			pr_info("Camera was flipped\n");
308 
309 			hflip = sd->hflip->val;
310 			vflip = sd->vflip->val;
311 
312 			if (reg) {
313 				vflip = !vflip;
314 				hflip = !hflip;
315 			}
316 			s5k83a_set_flip_real((struct gspca_dev *) sd,
317 					      vflip, hflip);
318 		}
319 
320 		mutex_unlock(&sd->gspca_dev.usb_lock);
321 		set_current_state(TASK_INTERRUPTIBLE);
322 	}
323 
324 	/* return to "front" flip */
325 	if (previous_rotation) {
326 		hflip = sd->hflip->val;
327 		vflip = sd->vflip->val;
328 		s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip);
329 	}
330 
331 	sd->rotation_thread = NULL;
332 	return 0;
333 }
334 
335 int s5k83a_start(struct sd *sd)
336 {
337 	int i, err = 0;
338 
339 	/* Create another thread, polling the GPIO ports of the camera to check
340 	   if it got rotated. This is how the windows driver does it so we have
341 	   to assume that there is no better way of accomplishing this */
342 	sd->rotation_thread = kthread_run(rotation_thread_function,
343 					  sd, "rotation thread");
344 	if (IS_ERR(sd->rotation_thread)) {
345 		err = PTR_ERR(sd->rotation_thread);
346 		sd->rotation_thread = NULL;
347 		return err;
348 	}
349 
350 	/* Preinit the sensor */
351 	for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) {
352 		u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]};
353 		if (start_s5k83a[i][0] == SENSOR)
354 			err = m5602_write_sensor(sd, start_s5k83a[i][1],
355 				data, 2);
356 		else
357 			err = m5602_write_bridge(sd, start_s5k83a[i][1],
358 				data[0]);
359 	}
360 	if (err < 0)
361 		return err;
362 
363 	return s5k83a_set_led_indication(sd, 1);
364 }
365 
366 int s5k83a_stop(struct sd *sd)
367 {
368 	if (sd->rotation_thread)
369 		kthread_stop(sd->rotation_thread);
370 
371 	return s5k83a_set_led_indication(sd, 0);
372 }
373 
374 void s5k83a_disconnect(struct sd *sd)
375 {
376 	s5k83a_stop(sd);
377 
378 	sd->sensor = NULL;
379 }
380 
381 static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
382 {
383 	int err;
384 	u8 data[2];
385 	struct sd *sd = (struct sd *) gspca_dev;
386 
387 	data[0] = 0x00;
388 	data[1] = 0x20;
389 	err = m5602_write_sensor(sd, 0x14, data, 2);
390 	if (err < 0)
391 		return err;
392 
393 	data[0] = 0x01;
394 	data[1] = 0x00;
395 	err = m5602_write_sensor(sd, 0x0d, data, 2);
396 	if (err < 0)
397 		return err;
398 
399 	/* FIXME: This is not sane, we need to figure out the composition
400 		  of these registers */
401 	data[0] = val >> 3; /* gain, high 5 bits */
402 	data[1] = val >> 1; /* gain, high 7 bits */
403 	err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
404 
405 	return err;
406 }
407 
408 static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
409 {
410 	u8 data[1];
411 	struct sd *sd = (struct sd *) gspca_dev;
412 
413 	data[0] = val;
414 	return m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1);
415 }
416 
417 static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
418 {
419 	u8 data[2];
420 	struct sd *sd = (struct sd *) gspca_dev;
421 
422 	data[0] = 0;
423 	data[1] = val;
424 	return m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2);
425 }
426 
427 static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
428 				__s32 vflip, __s32 hflip)
429 {
430 	int err;
431 	u8 data[1];
432 	struct sd *sd = (struct sd *) gspca_dev;
433 
434 	data[0] = 0x05;
435 	err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
436 	if (err < 0)
437 		return err;
438 
439 	/* six bit is vflip, seven is hflip */
440 	data[0] = S5K83A_FLIP_MASK;
441 	data[0] = (vflip) ? data[0] | 0x40 : data[0];
442 	data[0] = (hflip) ? data[0] | 0x80 : data[0];
443 
444 	err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
445 	if (err < 0)
446 		return err;
447 
448 	data[0] = (vflip) ? 0x0b : 0x0a;
449 	err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
450 	if (err < 0)
451 		return err;
452 
453 	data[0] = (hflip) ? 0x0a : 0x0b;
454 	err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
455 	return err;
456 }
457 
458 static int s5k83a_set_hvflip(struct gspca_dev *gspca_dev)
459 {
460 	int err;
461 	u8 reg;
462 	struct sd *sd = (struct sd *) gspca_dev;
463 	int hflip = sd->hflip->val;
464 	int vflip = sd->vflip->val;
465 
466 	err = s5k83a_get_rotation(sd, &reg);
467 	if (err < 0)
468 		return err;
469 	if (reg) {
470 		hflip = !hflip;
471 		vflip = !vflip;
472 	}
473 
474 	err = s5k83a_set_flip_real(gspca_dev, vflip, hflip);
475 	return err;
476 }
477 
478 static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl)
479 {
480 	struct gspca_dev *gspca_dev =
481 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
482 	int err;
483 
484 	if (!gspca_dev->streaming)
485 		return 0;
486 
487 	switch (ctrl->id) {
488 	case V4L2_CID_BRIGHTNESS:
489 		err = s5k83a_set_brightness(gspca_dev, ctrl->val);
490 		break;
491 	case V4L2_CID_EXPOSURE:
492 		err = s5k83a_set_exposure(gspca_dev, ctrl->val);
493 		break;
494 	case V4L2_CID_GAIN:
495 		err = s5k83a_set_gain(gspca_dev, ctrl->val);
496 		break;
497 	case V4L2_CID_HFLIP:
498 		err = s5k83a_set_hvflip(gspca_dev);
499 		break;
500 	default:
501 		return -EINVAL;
502 	}
503 
504 	return err;
505 }
506 
507 static int s5k83a_set_led_indication(struct sd *sd, u8 val)
508 {
509 	int err = 0;
510 	u8 data[1];
511 
512 	err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, data);
513 	if (err < 0)
514 		return err;
515 
516 	if (val)
517 		data[0] = data[0] | S5K83A_GPIO_LED_MASK;
518 	else
519 		data[0] = data[0] & ~S5K83A_GPIO_LED_MASK;
520 
521 	err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]);
522 
523 	return err;
524 }
525 
526 /* Get camera rotation on Acer notebooks */
527 static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data)
528 {
529 	int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data);
530 	*reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1;
531 	return err;
532 }
533 
534 static void s5k83a_dump_registers(struct sd *sd)
535 {
536 	int address;
537 	u8 page, old_page;
538 	m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
539 
540 	for (page = 0; page < 16; page++) {
541 		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
542 		pr_info("Dumping the s5k83a register state for page 0x%x\n",
543 			page);
544 		for (address = 0; address <= 0xff; address++) {
545 			u8 val = 0;
546 			m5602_read_sensor(sd, address, &val, 1);
547 			pr_info("register 0x%x contains 0x%x\n", address, val);
548 		}
549 	}
550 	pr_info("s5k83a register state dump complete\n");
551 
552 	for (page = 0; page < 16; page++) {
553 		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
554 		pr_info("Probing for which registers that are read/write for page 0x%x\n",
555 			page);
556 		for (address = 0; address <= 0xff; address++) {
557 			u8 old_val, ctrl_val, test_val = 0xff;
558 
559 			m5602_read_sensor(sd, address, &old_val, 1);
560 			m5602_write_sensor(sd, address, &test_val, 1);
561 			m5602_read_sensor(sd, address, &ctrl_val, 1);
562 
563 			if (ctrl_val == test_val)
564 				pr_info("register 0x%x is writeable\n",
565 					address);
566 			else
567 				pr_info("register 0x%x is read only\n",
568 					address);
569 
570 			/* Restore original val */
571 			m5602_write_sensor(sd, address, &old_val, 1);
572 		}
573 	}
574 	pr_info("Read/write register probing complete\n");
575 	m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
576 }
577