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
20static int s5k83a_s_ctrl(struct v4l2_ctrl *ctrl);
21
22static const struct v4l2_ctrl_ops s5k83a_ctrl_ops = {
23	.s_ctrl = s5k83a_s_ctrl,
24};
25
26static 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
40static 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*/
80static 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
137static 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
164static void s5k83a_dump_registers(struct sd *sd);
165static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data);
166static int s5k83a_set_led_indication(struct sd *sd, u8 val);
167static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev,
168				__s32 vflip, __s32 hflip);
169
170int 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
213sensor_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
223int 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
261int 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
293static 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
335int 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
367int 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
375void s5k83a_disconnect(struct sd *sd)
376{
377	s5k83a_stop(sd);
378
379	sd->sensor = NULL;
380}
381
382static 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
409static 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
420static 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
432static 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
463static 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
483static 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
512static 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 */
532static 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
539static 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