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_create(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 	wake_up_process(sd->rotation_thread);
350 
351 	/* Preinit the sensor */
352 	for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) {
353 		u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]};
354 		if (start_s5k83a[i][0] == SENSOR)
355 			err = m5602_write_sensor(sd, start_s5k83a[i][1],
356 				data, 2);
357 		else
358 			err = m5602_write_bridge(sd, start_s5k83a[i][1],
359 				data[0]);
360 	}
361 	if (err < 0)
362 		return err;
363 
364 	return s5k83a_set_led_indication(sd, 1);
365 }
366 
367 int s5k83a_stop(struct sd *sd)
368 {
369 	if (sd->rotation_thread)
370 		kthread_stop(sd->rotation_thread);
371 
372 	return s5k83a_set_led_indication(sd, 0);
373 }
374 
375 void s5k83a_disconnect(struct sd *sd)
376 {
377 	s5k83a_stop(sd);
378 
379 	sd->sensor = NULL;
380 }
381 
382 static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val)
383 {
384 	int err;
385 	u8 data[2];
386 	struct sd *sd = (struct sd *) gspca_dev;
387 
388 	data[0] = 0x00;
389 	data[1] = 0x20;
390 	err = m5602_write_sensor(sd, 0x14, data, 2);
391 	if (err < 0)
392 		return err;
393 
394 	data[0] = 0x01;
395 	data[1] = 0x00;
396 	err = m5602_write_sensor(sd, 0x0d, data, 2);
397 	if (err < 0)
398 		return err;
399 
400 	/* FIXME: This is not sane, we need to figure out the composition
401 		  of these registers */
402 	data[0] = val >> 3; /* gain, high 5 bits */
403 	data[1] = val >> 1; /* gain, high 7 bits */
404 	err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2);
405 
406 	return err;
407 }
408 
409 static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
410 {
411 	int err;
412 	u8 data[1];
413 	struct sd *sd = (struct sd *) gspca_dev;
414 
415 	data[0] = val;
416 	err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1);
417 	return err;
418 }
419 
420 static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
421 {
422 	int err;
423 	u8 data[2];
424 	struct sd *sd = (struct sd *) gspca_dev;
425 
426 	data[0] = 0;
427 	data[1] = val;
428 	err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2);
429 	return err;
430 }
431 
432 static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
433 				__s32 vflip, __s32 hflip)
434 {
435 	int err;
436 	u8 data[1];
437 	struct sd *sd = (struct sd *) gspca_dev;
438 
439 	data[0] = 0x05;
440 	err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1);
441 	if (err < 0)
442 		return err;
443 
444 	/* six bit is vflip, seven is hflip */
445 	data[0] = S5K83A_FLIP_MASK;
446 	data[0] = (vflip) ? data[0] | 0x40 : data[0];
447 	data[0] = (hflip) ? data[0] | 0x80 : data[0];
448 
449 	err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1);
450 	if (err < 0)
451 		return err;
452 
453 	data[0] = (vflip) ? 0x0b : 0x0a;
454 	err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1);
455 	if (err < 0)
456 		return err;
457 
458 	data[0] = (hflip) ? 0x0a : 0x0b;
459 	err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1);
460 	return err;
461 }
462 
463 static int s5k83a_set_hvflip(struct gspca_dev *gspca_dev)
464 {
465 	int err;
466 	u8 reg;
467 	struct sd *sd = (struct sd *) gspca_dev;
468 	int hflip = sd->hflip->val;
469 	int vflip = sd->vflip->val;
470 
471 	err = s5k83a_get_rotation(sd, &reg);
472 	if (err < 0)
473 		return err;
474 	if (reg) {
475 		hflip = !hflip;
476 		vflip = !vflip;
477 	}
478 
479 	err = s5k83a_set_flip_real(gspca_dev, vflip, hflip);
480 	return err;
481 }
482 
483 static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl)
484 {
485 	struct gspca_dev *gspca_dev =
486 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
487 	int err;
488 
489 	if (!gspca_dev->streaming)
490 		return 0;
491 
492 	switch (ctrl->id) {
493 	case V4L2_CID_BRIGHTNESS:
494 		err = s5k83a_set_brightness(gspca_dev, ctrl->val);
495 		break;
496 	case V4L2_CID_EXPOSURE:
497 		err = s5k83a_set_exposure(gspca_dev, ctrl->val);
498 		break;
499 	case V4L2_CID_GAIN:
500 		err = s5k83a_set_gain(gspca_dev, ctrl->val);
501 		break;
502 	case V4L2_CID_HFLIP:
503 		err = s5k83a_set_hvflip(gspca_dev);
504 		break;
505 	default:
506 		return -EINVAL;
507 	}
508 
509 	return err;
510 }
511 
512 static int s5k83a_set_led_indication(struct sd *sd, u8 val)
513 {
514 	int err = 0;
515 	u8 data[1];
516 
517 	err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, data);
518 	if (err < 0)
519 		return err;
520 
521 	if (val)
522 		data[0] = data[0] | S5K83A_GPIO_LED_MASK;
523 	else
524 		data[0] = data[0] & ~S5K83A_GPIO_LED_MASK;
525 
526 	err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]);
527 
528 	return err;
529 }
530 
531 /* Get camera rotation on Acer notebooks */
532 static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data)
533 {
534 	int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data);
535 	*reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1;
536 	return err;
537 }
538 
539 static void s5k83a_dump_registers(struct sd *sd)
540 {
541 	int address;
542 	u8 page, old_page;
543 	m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
544 
545 	for (page = 0; page < 16; page++) {
546 		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
547 		pr_info("Dumping the s5k83a register state for page 0x%x\n",
548 			page);
549 		for (address = 0; address <= 0xff; address++) {
550 			u8 val = 0;
551 			m5602_read_sensor(sd, address, &val, 1);
552 			pr_info("register 0x%x contains 0x%x\n", address, val);
553 		}
554 	}
555 	pr_info("s5k83a register state dump complete\n");
556 
557 	for (page = 0; page < 16; page++) {
558 		m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1);
559 		pr_info("Probing for which registers that are read/write for page 0x%x\n",
560 			page);
561 		for (address = 0; address <= 0xff; address++) {
562 			u8 old_val, ctrl_val, test_val = 0xff;
563 
564 			m5602_read_sensor(sd, address, &old_val, 1);
565 			m5602_write_sensor(sd, address, &test_val, 1);
566 			m5602_read_sensor(sd, address, &ctrl_val, 1);
567 
568 			if (ctrl_val == test_val)
569 				pr_info("register 0x%x is writeable\n",
570 					address);
571 			else
572 				pr_info("register 0x%x is read only\n",
573 					address);
574 
575 			/* Restore original val */
576 			m5602_write_sensor(sd, address, &old_val, 1);
577 		}
578 	}
579 	pr_info("Read/write register probing complete\n");
580 	m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1);
581 }
582