m5602_s5k4aa.c 15.5 KB
Newer Older
1 2 3
/*
 * Driver for the s5k4aa sensor
 *
4
 * Copyright (C) 2008 Erik Andrén
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project.
 * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br>
 *
 * Portions of code to USB interface and ALi driver software,
 * Copyright (c) 2006 Willem Duinker
 * v4l2 interface modeled after the V4L2 driver
 * for SN9C10x PC Camera Controllers
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, version 2.
 *
 */

#include "m5602_s5k4aa.h"

21 22 23 24 25 26 27 28
static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val);
29 30
static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val);
31 32
static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val);
static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val);
33

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
static
    const
	struct dmi_system_id s5k4aa_vflip_dmi_table[] = {
	{
		.ident = "Fujitsu-Siemens Amilo Xa 2528",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
		}
	}, {
		.ident = "Fujitsu-Siemens Amilo Xi 2550",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
			DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550")
		}
	}, {
		.ident = "MSI GX700",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
			DMI_MATCH(DMI_PRODUCT_NAME, "GX700"),
			DMI_MATCH(DMI_BIOS_DATE, "07/26/2007")
		}
	}, {
		.ident = "MSI GX700/GX705/EX700",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"),
			DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700")
		}
	},
	{ }
};

66 67 68 69 70 71 72 73 74 75 76 77 78 79
static struct v4l2_pix_format s5k4aa_modes[] = {
	{
		640,
		480,
		V4L2_PIX_FMT_SBGGR8,
		V4L2_FIELD_NONE,
		.sizeimage =
			640 * 480,
		.bytesperline = 640,
		.colorspace = V4L2_COLORSPACE_SRGB,
		.priv = 0
	}
};

80
const static struct ctrl s5k4aa_ctrls[] = {
81
#define VFLIP_IDX 0
82 83 84 85 86 87 88 89 90 91 92 93
	{
		{
			.id 		= V4L2_CID_VFLIP,
			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
			.name 		= "vertical flip",
			.minimum 	= 0,
			.maximum 	= 1,
			.step 		= 1,
			.default_value 	= 0
		},
		.set = s5k4aa_set_vflip,
		.get = s5k4aa_get_vflip
94 95 96
	},
#define HFLIP_IDX 1
	{
97 98 99 100 101 102 103 104 105 106 107
		{
			.id 		= V4L2_CID_HFLIP,
			.type 		= V4L2_CTRL_TYPE_BOOLEAN,
			.name 		= "horizontal flip",
			.minimum 	= 0,
			.maximum 	= 1,
			.step 		= 1,
			.default_value 	= 0
		},
		.set = s5k4aa_set_hflip,
		.get = s5k4aa_get_hflip
108 109 110
	},
#define GAIN_IDX 2
	{
111 112 113 114 115 116 117
		{
			.id		= V4L2_CID_GAIN,
			.type		= V4L2_CTRL_TYPE_INTEGER,
			.name		= "Gain",
			.minimum	= 0,
			.maximum	= 127,
			.step		= 1,
118
			.default_value	= S5K4AA_DEFAULT_GAIN,
119 120 121 122
			.flags		= V4L2_CTRL_FLAG_SLIDER
		},
		.set = s5k4aa_set_gain,
		.get = s5k4aa_get_gain
123 124 125
	},
#define EXPOSURE_IDX 3
	{
126 127 128 129 130 131 132 133 134 135 136 137
		{
			.id		= V4L2_CID_EXPOSURE,
			.type		= V4L2_CTRL_TYPE_INTEGER,
			.name		= "Exposure",
			.minimum	= 13,
			.maximum	= 0xfff,
			.step		= 1,
			.default_value	= 0x100,
			.flags		= V4L2_CTRL_FLAG_SLIDER
		},
		.set = s5k4aa_set_exposure,
		.get = s5k4aa_get_exposure
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
	},
#define NOISE_SUPP_IDX 4
	{
		{
			.id		= V4L2_CID_PRIVATE_BASE,
			.type		= V4L2_CTRL_TYPE_BOOLEAN,
			.name		= "Noise suppression (smoothing)",
			.minimum	= 0,
			.maximum	= 1,
			.step		= 1,
			.default_value	= 1,
		},
			.set = s5k4aa_set_noise,
			.get = s5k4aa_get_noise
	},
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
#define BRIGHTNESS_IDX 5
	{
		{
			.id		= V4L2_CID_BRIGHTNESS,
			.type		= V4L2_CTRL_TYPE_INTEGER,
			.name		= "Brightness",
			.minimum	= 0,
			.maximum	= 0x1f,
			.step		= 1,
			.default_value	= S5K4AA_DEFAULT_BRIGHTNESS,
		},
			.set = s5k4aa_set_brightness,
			.get = s5k4aa_get_brightness
	},

168 169
};

170
static void s5k4aa_dump_registers(struct sd *sd);
171

172 173 174 175 176
int s5k4aa_probe(struct sd *sd)
{
	u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75};
	int i, err = 0;
177
	s32 *sensor_settings;
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

	if (force_sensor) {
		if (force_sensor == S5K4AA_SENSOR) {
			info("Forcing a %s sensor", s5k4aa.name);
			goto sensor_found;
		}
		/* If we want to force another sensor, don't try to probe this
		 * one */
		return -ENODEV;
	}

	info("Probing for a s5k4aa sensor");

	/* Preinit the sensor */
	for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
		u8 data[2] = {0x00, 0x00};

		switch (preinit_s5k4aa[i][0]) {
		case BRIDGE:
			err = m5602_write_bridge(sd,
						 preinit_s5k4aa[i][1],
						 preinit_s5k4aa[i][2]);
			break;

		case SENSOR:
			data[0] = preinit_s5k4aa[i][2];
204
			err = m5602_write_sensor(sd,
205 206 207 208 209 210 211
						  preinit_s5k4aa[i][1],
						  data, 1);
			break;

		case SENSOR_LONG:
			data[0] = preinit_s5k4aa[i][2];
			data[1] = preinit_s5k4aa[i][3];
212
			err = m5602_write_sensor(sd,
213 214 215 216 217 218 219 220 221 222
						  preinit_s5k4aa[i][1],
						  data, 2);
			break;
		default:
			info("Invalid stream command, exiting init");
			return -EINVAL;
		}
	}

	/* Test some registers, but we don't know their exact meaning yet */
223 224 225 226 227
	if (m5602_read_sensor(sd, 0x00, prod_id, 2))
		return -ENODEV;
	if (m5602_read_sensor(sd, 0x02, prod_id+2, 2))
		return -ENODEV;
	if (m5602_read_sensor(sd, 0x04, prod_id+4, 2))
228 229 230 231 232 233
		return -ENODEV;

	if (memcmp(prod_id, expected_prod_id, sizeof(prod_id)))
		return -ENODEV;
	else
		info("Detected a s5k4aa sensor");
234

235
sensor_found:
236 237 238 239 240
	sensor_settings = kmalloc(
		ARRAY_SIZE(s5k4aa_ctrls) * sizeof(s32), GFP_KERNEL);
	if (!sensor_settings)
		return -ENOMEM;

241 242
	sd->gspca_dev.cam.cam_mode = s5k4aa_modes;
	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 247 248

	for (i = 0; i < ARRAY_SIZE(s5k4aa_ctrls); i++)
		sensor_settings[i] = s5k4aa_ctrls[i].qctrl.default_value;
	sd->sensor_priv = sensor_settings;
249

250 251 252
	return 0;
}

253 254 255 256 257 258
int s5k4aa_start(struct sd *sd)
{
	int i, err = 0;
	u8 data[2];
	struct cam *cam = &sd->gspca_dev.cam;

259
	switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) {
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
	case 640:
		PDEBUG(D_V4L2, "Configuring camera for VGA mode");

		for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) {
			switch (VGA_s5k4aa[i][0]) {
			case BRIDGE:
				err = m5602_write_bridge(sd,
						 VGA_s5k4aa[i][1],
						 VGA_s5k4aa[i][2]);
			break;

			case SENSOR:
				data[0] = VGA_s5k4aa[i][2];
				err = m5602_write_sensor(sd,
						 VGA_s5k4aa[i][1],
						 data, 1);
			break;

			case SENSOR_LONG:
				data[0] = VGA_s5k4aa[i][2];
				data[1] = VGA_s5k4aa[i][3];
				err = m5602_write_sensor(sd,
						  VGA_s5k4aa[i][1],
						  data, 2);
			break;

			default:
				err("Invalid stream command, exiting init");
				return -EINVAL;
			}
		}
	}
	return err;
}

295 296 297
int s5k4aa_init(struct sd *sd)
{
	int i, err = 0;
298
	s32 *sensor_settings = sd->sensor_priv;
299 300 301 302 303 304 305 306 307 308 309 310 311

	for (i = 0; i < ARRAY_SIZE(init_s5k4aa) && !err; i++) {
		u8 data[2] = {0x00, 0x00};

		switch (init_s5k4aa[i][0]) {
		case BRIDGE:
			err = m5602_write_bridge(sd,
				init_s5k4aa[i][1],
				init_s5k4aa[i][2]);
			break;

		case SENSOR:
			data[0] = init_s5k4aa[i][2];
312
			err = m5602_write_sensor(sd,
313 314 315 316 317 318
				init_s5k4aa[i][1], data, 1);
			break;

		case SENSOR_LONG:
			data[0] = init_s5k4aa[i][2];
			data[1] = init_s5k4aa[i][3];
319
			err = m5602_write_sensor(sd,
320 321 322 323 324 325 326 327
				init_s5k4aa[i][1], data, 2);
			break;
		default:
			info("Invalid stream command, exiting init");
			return -EINVAL;
		}
	}

328 329 330
	if (dump_sensor)
		s5k4aa_dump_registers(sd);

331 332 333 334
	err = s5k4aa_set_exposure(&sd->gspca_dev,
				   sensor_settings[EXPOSURE_IDX]);
	if (err < 0)
		return err;
335

336 337 338
	err = s5k4aa_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]);
	if (err < 0)
		return err;
339

340 341 342 343 344
	err = s5k4aa_set_brightness(&sd->gspca_dev,
				     sensor_settings[BRIGHTNESS_IDX]);
	if (err < 0)
		return err;

345
	err = s5k4aa_set_noise(&sd->gspca_dev, sensor_settings[NOISE_SUPP_IDX]);
346 347
	if (err < 0)
		return err;
348

349 350 351
	err = s5k4aa_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]);
	if (err < 0)
		return err;
352

353
	return s5k4aa_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]);
354 355
}

356
static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val)
357 358
{
	struct sd *sd = (struct sd *) gspca_dev;
359
	s32 *sensor_settings = sd->sensor_priv;
360

361
	*val = sensor_settings[EXPOSURE_IDX];
362
	PDEBUG(D_V4L2, "Read exposure %d", *val);
363

364
	return 0;
365 366
}

367
static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
368 369
{
	struct sd *sd = (struct sd *) gspca_dev;
370
	s32 *sensor_settings = sd->sensor_priv;
371 372 373
	u8 data = S5K4AA_PAGE_MAP_2;
	int err;

374
	sensor_settings[EXPOSURE_IDX] = val;
375
	PDEBUG(D_V4L2, "Set exposure to %d", val);
376
	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
377
	if (err < 0)
378
		return err;
379
	data = (val >> 8) & 0xff;
380
	err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1);
381
	if (err < 0)
382
		return err;
383
	data = val & 0xff;
384
	err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1);
385

386
	return err;
387 388
}

389
static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
390 391
{
	struct sd *sd = (struct sd *) gspca_dev;
392
	s32 *sensor_settings = sd->sensor_priv;
393

394
	*val = sensor_settings[VFLIP_IDX];
395
	PDEBUG(D_V4L2, "Read vertical flip %d", *val);
396

397
	return 0;
398 399
}

400
static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
401 402
{
	struct sd *sd = (struct sd *) gspca_dev;
403
	s32 *sensor_settings = sd->sensor_priv;
404 405 406
	u8 data = S5K4AA_PAGE_MAP_2;
	int err;

407 408
	sensor_settings[VFLIP_IDX] = val;

409
	PDEBUG(D_V4L2, "Set vertical flip to %d", val);
410
	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
411
	if (err < 0)
412
		return err;
413
	err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
414
	if (err < 0)
415
		return err;
416

417 418 419 420
	err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
	if (err < 0)
		return err;

421
	if (dmi_check_system(s5k4aa_vflip_dmi_table)) {
422
		val = !val;
423 424 425 426 427 428 429 430
		data = (data & 0x3f) |
		       (!sensor_settings[HFLIP_IDX] << 6) |
		       ((val & 0x01) << 7);
	} else {
		data = (data & 0x3f) |
		(sensor_settings[HFLIP_IDX] << 6) |
		((val & 0x01) << 7);
	}
431

432
	err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
433
	if (err < 0)
434
		return err;
435 436

	if (val) {
437
		err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
438
		if (err < 0)
439
			return err;
440 441

		data++;
442
		err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
443
	} else {
444
		err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
445
		if (err < 0)
446
			return err;
447 448

		data--;
449
		err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
450
	}
451
	return err;
452 453
}

454
static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
455 456
{
	struct sd *sd = (struct sd *) gspca_dev;
457
	s32 *sensor_settings = sd->sensor_priv;
458

459
	*val = sensor_settings[HFLIP_IDX];
460
	PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
461

462
	return 0;
463 464
}

465
static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
466 467
{
	struct sd *sd = (struct sd *) gspca_dev;
468
	s32 *sensor_settings = sd->sensor_priv;
469 470 471
	u8 data = S5K4AA_PAGE_MAP_2;
	int err;

472 473 474
	sensor_settings[HFLIP_IDX] = val;

	PDEBUG(D_V4L2, "Set horizontal flip to %d", val);
475
	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
476
	if (err < 0)
477
		return err;
478
	err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
479
	if (err < 0)
480
		return err;
481

482 483 484 485
	err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1);
	if (err < 0)
		return err;

486 487 488 489 490 491 492 493 494 495 496
	if (dmi_check_system(s5k4aa_vflip_dmi_table)) {
		val = !val;
		data = (data & 0x3f) |
		       (!sensor_settings[VFLIP_IDX] << 7) |
		       ((val & 0x01) << 6);
	} else {
		data = (data & 0x3f) |
		       (sensor_settings[VFLIP_IDX] << 7) |
		       ((val & 0x01) << 6);
	}

497
	data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6));
498
	err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1);
499
	if (err < 0)
500
		return err;
501 502

	if (val) {
503
		err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
504
		if (err < 0)
505
			return err;
506
		data++;
507
		err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
508
		if (err < 0)
509
			return err;
510
	} else {
511
		err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
512
		if (err < 0)
513
			return err;
514
		data--;
515
		err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
516
	}
517
	return err;
518 519
}

520
static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
521 522
{
	struct sd *sd = (struct sd *) gspca_dev;
523
	s32 *sensor_settings = sd->sensor_priv;
524

525
	*val = sensor_settings[GAIN_IDX];
526
	PDEBUG(D_V4L2, "Read gain %d", *val);
527
	return 0;
528 529
}

530
static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val)
531 532
{
	struct sd *sd = (struct sd *) gspca_dev;
533
	s32 *sensor_settings = sd->sensor_priv;
534 535 536
	u8 data = S5K4AA_PAGE_MAP_2;
	int err;

537 538
	sensor_settings[GAIN_IDX] = val;

539
	PDEBUG(D_V4L2, "Set gain to %d", val);
540
	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
541
	if (err < 0)
542
		return err;
543 544

	data = val & 0xff;
545
	err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1);
546

547
	return err;
548 549
}

550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val)
{
	struct sd *sd = (struct sd *) gspca_dev;
	s32 *sensor_settings = sd->sensor_priv;

	*val = sensor_settings[BRIGHTNESS_IDX];
	PDEBUG(D_V4L2, "Read brightness %d", *val);
	return 0;
}

static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val)
{
	struct sd *sd = (struct sd *) gspca_dev;
	s32 *sensor_settings = sd->sensor_priv;
	u8 data = S5K4AA_PAGE_MAP_2;
	int err;

	sensor_settings[BRIGHTNESS_IDX] = val;

	PDEBUG(D_V4L2, "Set brightness to %d", val);
	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
	if (err < 0)
		return err;

	data = val & 0xff;
	return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1);
}

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val)
{
	struct sd *sd = (struct sd *) gspca_dev;
	s32 *sensor_settings = sd->sensor_priv;

	*val = sensor_settings[NOISE_SUPP_IDX];
	PDEBUG(D_V4L2, "Read noise %d", *val);
	return 0;
}

static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val)
{
	struct sd *sd = (struct sd *) gspca_dev;
	s32 *sensor_settings = sd->sensor_priv;
	u8 data = S5K4AA_PAGE_MAP_2;
	int err;

	sensor_settings[NOISE_SUPP_IDX] = val;

	PDEBUG(D_V4L2, "Set noise to %d", val);
	err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1);
	if (err < 0)
		return err;

	data = val & 0x01;
	return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1);
}

606 607 608 609 610 611
void s5k4aa_disconnect(struct sd *sd)
{
	sd->sensor = NULL;
	kfree(sd->sensor_priv);
}

612
static void s5k4aa_dump_registers(struct sd *sd)
613 614 615
{
	int address;
	u8 page, old_page;
616
	m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
617
	for (page = 0; page < 16; page++) {
618
		m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
619 620 621
		info("Dumping the s5k4aa register state for page 0x%x", page);
		for (address = 0; address <= 0xff; address++) {
			u8 value = 0;
622
			m5602_read_sensor(sd, address, &value, 1);
623 624 625 626 627 628 629
			info("register 0x%x contains 0x%x",
			     address, value);
		}
	}
	info("s5k4aa register state dump complete");

	for (page = 0; page < 16; page++) {
630
		m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &page, 1);
631 632 633 634 635
		info("Probing for which registers that are "
		     "read/write for page 0x%x", page);
		for (address = 0; address <= 0xff; address++) {
			u8 old_value, ctrl_value, test_value = 0xff;

636
			m5602_read_sensor(sd, address, &old_value, 1);
637
			m5602_write_sensor(sd, address, &test_value, 1);
638
			m5602_read_sensor(sd, address, &ctrl_value, 1);
639 640 641 642 643 644 645

			if (ctrl_value == test_value)
				info("register 0x%x is writeable", address);
			else
				info("register 0x%x is read only", address);

			/* Restore original value */
646
			m5602_write_sensor(sd, address, &old_value, 1);
647 648 649
		}
	}
	info("Read/write register probing complete");
650
	m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &old_page, 1);
651
}