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