m5602_s5k4aa.c revision 65bd761e9a8c9114febab4e554ec0800c59e8983
1/*
2 * Driver for the s5k4aa sensor
3 *
4 * Copyright (C) 2008 Erik Andrén
5 * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
6 * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
7 *
8 * Portions of code to USB interface and ALi driver software,
9 * Copyright (c) 2006 Willem Duinker
10 * v4l2 interface modeled after the V4L2 driver
11 * for SN9C10x PC Camera Controllers
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License as
15 * published by the Free Software Foundation, version 2.
16 *
17 */
18
19#include "m5602_s5k4aa.h"
20
21static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
22static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
23static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
24static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
25static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
26static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
27static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
28static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val);
29static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val);
30static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val);
31static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
32static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
33
34static
35    const
36	struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
37	{
38		.ident = "Fujitsu-Siemens Amilo Xa 2528",
39		.matches = {
40			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
41			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
42		}
43	}, {
44		.ident = "Fujitsu-Siemens Amilo Xi 2550",
45		.matches = {
46			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
47			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
48		}
49	}, {
50		.ident = "MSI GX700",
51		.matches = {
52			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
53			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
54			DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
55		}
56	}, {
57		.ident = "MSI GX700/GX705/EX700",
58		.matches = {
59			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
60			DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700")
61		}
62	},
63	{ }
64};
65
66static struct v4l2_pix_format s5k4aa_modes[] = {
67	{
68		640,
69		480,
70		V4L2_PIX_FMT_SBGGR8,
71		V4L2_FIELD_NONE,
72		.sizeimage =
73			640 * 480,
74		.bytesperline = 640,
75		.colorspace = V4L2_COLORSPACE_SRGB,
76		.priv = 0
77	}
78};
79
80static const struct ctrl s5k4aa_ctrls[] = {
81#define VFLIP_IDX 0
82	{
83		{
84			.id 		= V4L2_CID_VFLIP,
85			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
86			.name 		= "vertical flip",
87			.minimum 	= 0,
88			.maximum 	= 1,
89			.step 		= 1,
90			.default_value 	= 0
91		},
92		.set = s5k4aa_set_vflip,
93		.get = s5k4aa_get_vflip
94	},
95#define HFLIP_IDX 1
96	{
97		{
98			.id 		= V4L2_CID_HFLIP,
99			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
100			.name 		= "horizontal flip",
101			.minimum 	= 0,
102			.maximum 	= 1,
103			.step 		= 1,
104			.default_value 	= 0
105		},
106		.set = s5k4aa_set_hflip,
107		.get = s5k4aa_get_hflip
108	},
109#define GAIN_IDX 2
110	{
111		{
112			.id		= V4L2_CID_GAIN,
113			.type		= V4L2_CTRL_TYPE_INTEGER,
114			.name		= "Gain",
115			.minimum	= 0,
116			.maximum	= 127,
117			.step		= 1,
118			.default_value	= S5K4AA_DEFAULT_GAIN,
119			.flags		= V4L2_CTRL_FLAG_SLIDER
120		},
121		.set = s5k4aa_set_gain,
122		.get = s5k4aa_get_gain
123	},
124#define EXPOSURE_IDX 3
125	{
126		{
127			.id		= V4L2_CID_EXPOSURE,
128			.type		= V4L2_CTRL_TYPE_INTEGER,
129			.name		= "Exposure",
130			.minimum	= 13,
131			.maximum	= 0xfff,
132			.step		= 1,
133			.default_value	= 0x100,
134			.flags		= V4L2_CTRL_FLAG_SLIDER
135		},
136		.set = s5k4aa_set_exposure,
137		.get = s5k4aa_get_exposure
138	},
139#define NOISE_SUPP_IDX 4
140	{
141		{
142			.id		= V4L2_CID_PRIVATE_BASE,
143			.type		= V4L2_CTRL_TYPE_BOOLEAN,
144			.name		= "Noise suppression (smoothing)",
145			.minimum	= 0,
146			.maximum	= 1,
147			.step		= 1,
148			.default_value	= 1,
149		},
150			.set = s5k4aa_set_noise,
151			.get = s5k4aa_get_noise
152	},
153#define BRIGHTNESS_IDX 5
154	{
155		{
156			.id		= V4L2_CID_BRIGHTNESS,
157			.type		= V4L2_CTRL_TYPE_INTEGER,
158			.name		= "Brightness",
159			.minimum	= 0,
160			.maximum	= 0x1f,
161			.step		= 1,
162			.default_value	= S5K4AA_DEFAULT_BRIGHTNESS,
163		},
164			.set = s5k4aa_set_brightness,
165			.get = s5k4aa_get_brightness
166	},
167
168};
169
170static void s5k4aa_dump_registers(struct sd *sd);
171
172int s5k4aa_probe(struct sd *sd)
173{
174	u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
175	const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75};
176	int i, err = 0;
177	s32 *sensor_settings;
178
179	if (force_sensor) {
180		if (force_sensor == S5K4AA_SENSOR) {
181			info("Forcing a %s sensor", s5k4aa.name);
182			goto sensor_found;
183		}
184		/* If we want to force another sensor, don't try to probe this
185		 * one */
186		return -ENODEV;
187	}
188
189	info("Probing for a s5k4aa sensor");
190
191	/* Preinit the sensor */
192	for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
193		u8 data[2] = {0x00, 0x00};
194
195		switch (preinit_s5k4aa[i][0]) {
196		case BRIDGE:
197			err = m5602_write_bridge(sd,
198						 preinit_s5k4aa[i][1],
199						 preinit_s5k4aa[i][2]);
200			break;
201
202		case SENSOR:
203			data[0] = preinit_s5k4aa[i][2];
204			err = m5602_write_sensor(sd,
205						  preinit_s5k4aa[i][1],
206						  data, 1);
207			break;
208
209		case SENSOR_LONG:
210			data[0] = preinit_s5k4aa[i][2];
211			data[1] = preinit_s5k4aa[i][3];
212			err = m5602_write_sensor(sd,
213						  preinit_s5k4aa[i][1],
214						  data, 2);
215			break;
216		default:
217			info("Invalid stream command, exiting init");
218			return -EINVAL;
219		}
220	}
221
222	/* Test some registers, but we don't know their exact meaning yet */
223	if (m5602_read_sensor(sd, 0x00, prod_id, 2))
224		return -ENODEV;
225	if (m5602_read_sensor(sd, 0x02, prod_id+2, 2))
226		return -ENODEV;
227	if (m5602_read_sensor(sd, 0x04, prod_id+4, 2))
228		return -ENODEV;
229
230	if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
231		return -ENODEV;
232	else
233		info("Detected a s5k4aa sensor");
234
235sensor_found:
236	sensor_settings = kmalloc(
237		ARRAY_SIZE(s5k4aa_ctrls) * sizeof(s32), GFP_KERNEL);
238	if (!sensor_settings)
239		return -ENOMEM;
240
241	sd->gspca_dev.cam.cam_mode = s5k4aa_modes;
242	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes);
243	sd->desc->ctrls = s5k4aa_ctrls;
244	sd->desc->nctrls = ARRAY_SIZE(s5k4aa_ctrls);
245
246	for (i = 0; i < ARRAY_SIZE(s5k4aa_ctrls); i++)
247		sensor_settings[i] = s5k4aa_ctrls[i].qctrl.default_value;
248	sd->sensor_priv = sensor_settings;
249
250	return 0;
251}
252
253int s5k4aa_start(struct sd *sd)
254{
255	int i, err = 0;
256	u8 data[2];
257	struct cam *cam = &sd->gspca_dev.cam;
258
259	switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) {
260	case 1280:
261		PDEBUG(D_V4L2, "Configuring camera for SXGA mode");
262
263		for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) {
264			switch (SXGA_s5k4aa[i][0]) {
265			case BRIDGE:
266				err = m5602_write_bridge(sd,
267						 SXGA_s5k4aa[i][1],
268						 SXGA_s5k4aa[i][2]);
269			break;
270
271			case SENSOR:
272				data[0] = SXGA_s5k4aa[i][2];
273				err = m5602_write_sensor(sd,
274						 SXGA_s5k4aa[i][1],
275						 data, 1);
276			break;
277
278			case SENSOR_LONG:
279				data[0] = SXGA_s5k4aa[i][2];
280				data[1] = SXGA_s5k4aa[i][3];
281				err = m5602_write_sensor(sd,
282						  SXGA_s5k4aa[i][1],
283						  data, 2);
284			break;
285
286			default:
287				err("Invalid stream command, exiting init");
288				return -EINVAL;
289			}
290		}
291
292	case 640:
293		PDEBUG(D_V4L2, "Configuring camera for VGA mode");
294
295		for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) {
296			switch (VGA_s5k4aa[i][0]) {
297			case BRIDGE:
298				err = m5602_write_bridge(sd,
299						 VGA_s5k4aa[i][1],
300						 VGA_s5k4aa[i][2]);
301			break;
302
303			case SENSOR:
304				data[0] = VGA_s5k4aa[i][2];
305				err = m5602_write_sensor(sd,
306						 VGA_s5k4aa[i][1],
307						 data, 1);
308			break;
309
310			case SENSOR_LONG:
311				data[0] = VGA_s5k4aa[i][2];
312				data[1] = VGA_s5k4aa[i][3];
313				err = m5602_write_sensor(sd,
314						  VGA_s5k4aa[i][1],
315						  data, 2);
316			break;
317
318			default:
319				err("Invalid stream command, exiting init");
320				return -EINVAL;
321			}
322		}
323	}
324	return err;
325}
326
327int s5k4aa_init(struct sd *sd)
328{
329	int i, err = 0;
330	s32 *sensor_settings = sd->sensor_priv;
331
332	for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) {
333		u8 data[2] = {0x00, 0x00};
334
335		switch (init_s5k4aa[i][0]) {
336		case BRIDGE:
337			err = m5602_write_bridge(sd,
338				init_s5k4aa[i][1],
339				init_s5k4aa[i][2]);
340			break;
341
342		case SENSOR:
343			data[0] = init_s5k4aa[i][2];
344			err = m5602_write_sensor(sd,
345				init_s5k4aa[i][1], data, 1);
346			break;
347
348		case SENSOR_LONG:
349			data[0] = init_s5k4aa[i][2];
350			data[1] = init_s5k4aa[i][3];
351			err = m5602_write_sensor(sd,
352				init_s5k4aa[i][1], data, 2);
353			break;
354		default:
355			info("Invalid stream command, exiting init");
356			return -EINVAL;
357		}
358	}
359
360	if (dump_sensor)
361		s5k4aa_dump_registers(sd);
362
363	err = s5k4aa_set_exposure(&sd->gspca_dev,
364				   sensor_settings[EXPOSURE_IDX]);
365	if (err < 0)
366		return err;
367
368	err = s5k4aa_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
369	if (err < 0)
370		return err;
371
372	err = s5k4aa_set_brightness(&sd->gspca_dev,
373				     sensor_settings[BRIGHTNESS_IDX]);
374	if (err < 0)
375		return err;
376
377	err = s5k4aa_set_noise(&sd->gspca_dev, sensor_settings[NOISE_SUPP_IDX]);
378	if (err < 0)
379		return err;
380
381	err = s5k4aa_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
382	if (err < 0)
383		return err;
384
385	return s5k4aa_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
386}
387
388static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
389{
390	struct sd *sd = (struct sd *) gspca_dev;
391	s32 *sensor_settings = sd->sensor_priv;
392
393	*val = sensor_settings[EXPOSURE_IDX];
394	PDEBUG(D_V4L2, "Read exposure %d", *val);
395
396	return 0;
397}
398
399static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
400{
401	struct sd *sd = (struct sd *) gspca_dev;
402	s32 *sensor_settings = sd->sensor_priv;
403	u8 data = S5K4AA_PAGE_MAP_2;
404	int err;
405
406	sensor_settings[EXPOSURE_IDX] = val;
407	PDEBUG(D_V4L2, "Set exposure to %d", val);
408	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
409	if (err < 0)
410		return err;
411	data = (val >> 8) & 0xff;
412	err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
413	if (err < 0)
414		return err;
415	data = val & 0xff;
416	err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
417
418	return err;
419}
420
421static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
422{
423	struct sd *sd = (struct sd *) gspca_dev;
424	s32 *sensor_settings = sd->sensor_priv;
425
426	*val = sensor_settings[VFLIP_IDX];
427	PDEBUG(D_V4L2, "Read vertical flip %d", *val);
428
429	return 0;
430}
431
432static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
433{
434	struct sd *sd = (struct sd *) gspca_dev;
435	s32 *sensor_settings = sd->sensor_priv;
436	u8 data = S5K4AA_PAGE_MAP_2;
437	int err;
438
439	sensor_settings[VFLIP_IDX] = val;
440
441	PDEBUG(D_V4L2, "Set vertical flip to %d", val);
442	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
443	if (err < 0)
444		return err;
445	err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
446	if (err < 0)
447		return err;
448
449	err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
450	if (err < 0)
451		return err;
452
453	if (dmi_check_system(s5k4aa_vflip_dmi_table)) {
454		val = !val;
455		data = (data & 0x3f) |
456		       (!sensor_settings[HFLIP_IDX] << 6) |
457		       ((val & 0x01) << 7);
458	} else {
459		data = (data & 0x3f) |
460		(sensor_settings[HFLIP_IDX] << 6) |
461		((val & 0x01) << 7);
462	}
463
464	err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
465	if (err < 0)
466		return err;
467
468	if (val) {
469		err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
470		if (err < 0)
471			return err;
472
473		data++;
474		err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
475	} else {
476		err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
477		if (err < 0)
478			return err;
479
480		data--;
481		err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
482	}
483	return err;
484}
485
486static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
487{
488	struct sd *sd = (struct sd *) gspca_dev;
489	s32 *sensor_settings = sd->sensor_priv;
490
491	*val = sensor_settings[HFLIP_IDX];
492	PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
493
494	return 0;
495}
496
497static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
498{
499	struct sd *sd = (struct sd *) gspca_dev;
500	s32 *sensor_settings = sd->sensor_priv;
501	u8 data = S5K4AA_PAGE_MAP_2;
502	int err;
503
504	sensor_settings[HFLIP_IDX] = val;
505
506	PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
507	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
508	if (err < 0)
509		return err;
510	err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
511	if (err < 0)
512		return err;
513
514	err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
515	if (err < 0)
516		return err;
517
518	if (dmi_check_system(s5k4aa_vflip_dmi_table)) {
519		val = !val;
520		data = (data & 0x3f) |
521		       (!sensor_settings[VFLIP_IDX] << 7) |
522		       ((val & 0x01) << 6);
523	} else {
524		data = (data & 0x3f) |
525		       (sensor_settings[VFLIP_IDX] << 7) |
526		       ((val & 0x01) << 6);
527	}
528
529	data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6));
530	err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
531	if (err < 0)
532		return err;
533
534	if (val) {
535		err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
536		if (err < 0)
537			return err;
538		data++;
539		err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
540		if (err < 0)
541			return err;
542	} else {
543		err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
544		if (err < 0)
545			return err;
546		data--;
547		err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
548	}
549	return err;
550}
551
552static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
553{
554	struct sd *sd = (struct sd *) gspca_dev;
555	s32 *sensor_settings = sd->sensor_priv;
556
557	*val = sensor_settings[GAIN_IDX];
558	PDEBUG(D_V4L2, "Read gain %d", *val);
559	return 0;
560}
561
562static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
563{
564	struct sd *sd = (struct sd *) gspca_dev;
565	s32 *sensor_settings = sd->sensor_priv;
566	u8 data = S5K4AA_PAGE_MAP_2;
567	int err;
568
569	sensor_settings[GAIN_IDX] = val;
570
571	PDEBUG(D_V4L2, "Set gain to %d", val);
572	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
573	if (err < 0)
574		return err;
575
576	data = val & 0xff;
577	err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1);
578
579	return err;
580}
581
582static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
583{
584	struct sd *sd = (struct sd *) gspca_dev;
585	s32 *sensor_settings = sd->sensor_priv;
586
587	*val = sensor_settings[BRIGHTNESS_IDX];
588	PDEBUG(D_V4L2, "Read brightness %d", *val);
589	return 0;
590}
591
592static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
593{
594	struct sd *sd = (struct sd *) gspca_dev;
595	s32 *sensor_settings = sd->sensor_priv;
596	u8 data = S5K4AA_PAGE_MAP_2;
597	int err;
598
599	sensor_settings[BRIGHTNESS_IDX] = val;
600
601	PDEBUG(D_V4L2, "Set brightness to %d", val);
602	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
603	if (err < 0)
604		return err;
605
606	data = val & 0xff;
607	return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1);
608}
609
610static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val)
611{
612	struct sd *sd = (struct sd *) gspca_dev;
613	s32 *sensor_settings = sd->sensor_priv;
614
615	*val = sensor_settings[NOISE_SUPP_IDX];
616	PDEBUG(D_V4L2, "Read noise %d", *val);
617	return 0;
618}
619
620static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val)
621{
622	struct sd *sd = (struct sd *) gspca_dev;
623	s32 *sensor_settings = sd->sensor_priv;
624	u8 data = S5K4AA_PAGE_MAP_2;
625	int err;
626
627	sensor_settings[NOISE_SUPP_IDX] = val;
628
629	PDEBUG(D_V4L2, "Set noise to %d", val);
630	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
631	if (err < 0)
632		return err;
633
634	data = val & 0x01;
635	return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1);
636}
637
638void s5k4aa_disconnect(struct sd *sd)
639{
640	sd->sensor = NULL;
641	kfree(sd->sensor_priv);
642}
643
644static void s5k4aa_dump_registers(struct sd *sd)
645{
646	int address;
647	u8 page, old_page;
648	m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
649	for (page = 0; page < 16; page++) {
650		m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
651		info("Dumping the s5k4aa register state for page 0x%x", page);
652		for (address = 0; address <= 0xff; address++) {
653			u8 value = 0;
654			m5602_read_sensor(sd, address, &value, 1);
655			info("register 0x%x contains 0x%x",
656			     address, value);
657		}
658	}
659	info("s5k4aa register state dump complete");
660
661	for (page = 0; page < 16; page++) {
662		m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
663		info("Probing for which registers that are "
664		     "read/write for page 0x%x", page);
665		for (address = 0; address <= 0xff; address++) {
666			u8 old_value, ctrl_value, test_value = 0xff;
667
668			m5602_read_sensor(sd, address, &old_value, 1);
669			m5602_write_sensor(sd, address, &test_value, 1);
670			m5602_read_sensor(sd, address, &ctrl_value, 1);
671
672			if (ctrl_value == test_value)
673				info("register 0x%x is writeable", address);
674			else
675				info("register 0x%x is read only", address);
676
677			/* Restore original value */
678			m5602_write_sensor(sd, address, &old_value, 1);
679		}
680	}
681	info("Read/write register probing complete");
682	m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
683}
684