xref: /linux/drivers/media/usb/gspca/m5602/m5602_ov9650.c (revision c4ee0af3fa0dc65f690fc908f02b8355f9576ea0)
1 /*
2  * Driver for the ov9650 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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20 
21 #include "m5602_ov9650.h"
22 
23 static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl);
24 static void ov9650_dump_registers(struct sd *sd);
25 
26 /* Vertically and horizontally flips the image if matched, needed for machines
27    where the sensor is mounted upside down */
28 static
29     const
30 	struct dmi_system_id ov9650_flip_dmi_table[] = {
31 	{
32 		.ident = "ASUS A6Ja",
33 		.matches = {
34 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
35 			DMI_MATCH(DMI_PRODUCT_NAME, "A6J")
36 		}
37 	},
38 	{
39 		.ident = "ASUS A6JC",
40 		.matches = {
41 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
42 			DMI_MATCH(DMI_PRODUCT_NAME, "A6JC")
43 		}
44 	},
45 	{
46 		.ident = "ASUS A6K",
47 		.matches = {
48 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
49 			DMI_MATCH(DMI_PRODUCT_NAME, "A6K")
50 		}
51 	},
52 	{
53 		.ident = "ASUS A6Kt",
54 		.matches = {
55 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
56 			DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt")
57 		}
58 	},
59 	{
60 		.ident = "ASUS A6VA",
61 		.matches = {
62 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
63 			DMI_MATCH(DMI_PRODUCT_NAME, "A6VA")
64 		}
65 	},
66 	{
67 
68 		.ident = "ASUS A6VC",
69 		.matches = {
70 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
71 			DMI_MATCH(DMI_PRODUCT_NAME, "A6VC")
72 		}
73 	},
74 	{
75 		.ident = "ASUS A6VM",
76 		.matches = {
77 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
78 			DMI_MATCH(DMI_PRODUCT_NAME, "A6VM")
79 		}
80 	},
81 	{
82 		.ident = "ASUS A7V",
83 		.matches = {
84 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
85 			DMI_MATCH(DMI_PRODUCT_NAME, "A7V")
86 		}
87 	},
88 	{
89 		.ident = "Alienware Aurora m9700",
90 		.matches = {
91 			DMI_MATCH(DMI_SYS_VENDOR, "alienware"),
92 			DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700")
93 		}
94 	},
95 	{}
96 };
97 
98 static struct v4l2_pix_format ov9650_modes[] = {
99 	{
100 		176,
101 		144,
102 		V4L2_PIX_FMT_SBGGR8,
103 		V4L2_FIELD_NONE,
104 		.sizeimage =
105 			176 * 144,
106 		.bytesperline = 176,
107 		.colorspace = V4L2_COLORSPACE_SRGB,
108 		.priv = 9
109 	}, {
110 		320,
111 		240,
112 		V4L2_PIX_FMT_SBGGR8,
113 		V4L2_FIELD_NONE,
114 		.sizeimage =
115 			320 * 240,
116 		.bytesperline = 320,
117 		.colorspace = V4L2_COLORSPACE_SRGB,
118 		.priv = 8
119 	}, {
120 		352,
121 		288,
122 		V4L2_PIX_FMT_SBGGR8,
123 		V4L2_FIELD_NONE,
124 		.sizeimage =
125 			352 * 288,
126 		.bytesperline = 352,
127 		.colorspace = V4L2_COLORSPACE_SRGB,
128 		.priv = 9
129 	}, {
130 		640,
131 		480,
132 		V4L2_PIX_FMT_SBGGR8,
133 		V4L2_FIELD_NONE,
134 		.sizeimage =
135 			640 * 480,
136 		.bytesperline = 640,
137 		.colorspace = V4L2_COLORSPACE_SRGB,
138 		.priv = 9
139 	}
140 };
141 
142 static const struct v4l2_ctrl_ops ov9650_ctrl_ops = {
143 	.s_ctrl = ov9650_s_ctrl,
144 };
145 
146 int ov9650_probe(struct sd *sd)
147 {
148 	int err = 0;
149 	u8 prod_id = 0, ver_id = 0, i;
150 	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
151 
152 	if (force_sensor) {
153 		if (force_sensor == OV9650_SENSOR) {
154 			pr_info("Forcing an %s sensor\n", ov9650.name);
155 			goto sensor_found;
156 		}
157 		/* If we want to force another sensor,
158 		   don't try to probe this one */
159 		return -ENODEV;
160 	}
161 
162 	PDEBUG(D_PROBE, "Probing for an ov9650 sensor");
163 
164 	/* Run the pre-init before probing the sensor */
165 	for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) {
166 		u8 data = preinit_ov9650[i][2];
167 		if (preinit_ov9650[i][0] == SENSOR)
168 			err = m5602_write_sensor(sd,
169 				preinit_ov9650[i][1], &data, 1);
170 		else
171 			err = m5602_write_bridge(sd,
172 				preinit_ov9650[i][1], data);
173 	}
174 
175 	if (err < 0)
176 		return err;
177 
178 	if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1))
179 		return -ENODEV;
180 
181 	if (m5602_read_sensor(sd, OV9650_VER, &ver_id, 1))
182 		return -ENODEV;
183 
184 	if ((prod_id == 0x96) && (ver_id == 0x52)) {
185 		pr_info("Detected an ov9650 sensor\n");
186 		goto sensor_found;
187 	}
188 	return -ENODEV;
189 
190 sensor_found:
191 	sd->gspca_dev.cam.cam_mode = ov9650_modes;
192 	sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes);
193 
194 	return 0;
195 }
196 
197 int ov9650_init(struct sd *sd)
198 {
199 	int i, err = 0;
200 	u8 data;
201 
202 	if (dump_sensor)
203 		ov9650_dump_registers(sd);
204 
205 	for (i = 0; i < ARRAY_SIZE(init_ov9650) && !err; i++) {
206 		data = init_ov9650[i][2];
207 		if (init_ov9650[i][0] == SENSOR)
208 			err = m5602_write_sensor(sd, init_ov9650[i][1],
209 						  &data, 1);
210 		else
211 			err = m5602_write_bridge(sd, init_ov9650[i][1], data);
212 	}
213 
214 	return 0;
215 }
216 
217 int ov9650_init_controls(struct sd *sd)
218 {
219 	struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
220 
221 	sd->gspca_dev.vdev.ctrl_handler = hdl;
222 	v4l2_ctrl_handler_init(hdl, 9);
223 
224 	sd->auto_white_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
225 					       V4L2_CID_AUTO_WHITE_BALANCE,
226 					       0, 1, 1, 1);
227 	sd->red_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
228 					V4L2_CID_RED_BALANCE, 0, 255, 1,
229 					RED_GAIN_DEFAULT);
230 	sd->blue_bal = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
231 					V4L2_CID_BLUE_BALANCE, 0, 255, 1,
232 					BLUE_GAIN_DEFAULT);
233 
234 	sd->autoexpo = v4l2_ctrl_new_std_menu(hdl, &ov9650_ctrl_ops,
235 			  V4L2_CID_EXPOSURE_AUTO, 1, 0, V4L2_EXPOSURE_AUTO);
236 	sd->expo = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_EXPOSURE,
237 			  0, 0x1ff, 4, EXPOSURE_DEFAULT);
238 
239 	sd->autogain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops,
240 					 V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
241 	sd->gain = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_GAIN, 0,
242 				     0x3ff, 1, GAIN_DEFAULT);
243 
244 	sd->hflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_HFLIP,
245 				      0, 1, 1, 0);
246 	sd->vflip = v4l2_ctrl_new_std(hdl, &ov9650_ctrl_ops, V4L2_CID_VFLIP,
247 				      0, 1, 1, 0);
248 
249 	if (hdl->error) {
250 		pr_err("Could not initialize controls\n");
251 		return hdl->error;
252 	}
253 
254 	v4l2_ctrl_auto_cluster(3, &sd->auto_white_bal, 0, false);
255 	v4l2_ctrl_auto_cluster(2, &sd->autoexpo, 0, false);
256 	v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);
257 	v4l2_ctrl_cluster(2, &sd->hflip);
258 
259 	return 0;
260 }
261 
262 int ov9650_start(struct sd *sd)
263 {
264 	u8 data;
265 	int i, err = 0;
266 	struct cam *cam = &sd->gspca_dev.cam;
267 
268 	int width = cam->cam_mode[sd->gspca_dev.curr_mode].width;
269 	int height = cam->cam_mode[sd->gspca_dev.curr_mode].height;
270 	int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
271 	int hor_offs = OV9650_LEFT_OFFSET;
272 	struct gspca_dev *gspca_dev = (struct gspca_dev *)sd;
273 
274 	if ((!dmi_check_system(ov9650_flip_dmi_table) &&
275 		sd->vflip->val) ||
276 		(dmi_check_system(ov9650_flip_dmi_table) &&
277 		!sd->vflip->val))
278 		ver_offs--;
279 
280 	if (width <= 320)
281 		hor_offs /= 2;
282 
283 	/* Synthesize the vsync/hsync setup */
284 	for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) {
285 		if (res_init_ov9650[i][0] == BRIDGE)
286 			err = m5602_write_bridge(sd, res_init_ov9650[i][1],
287 				res_init_ov9650[i][2]);
288 		else if (res_init_ov9650[i][0] == SENSOR) {
289 			data = res_init_ov9650[i][2];
290 			err = m5602_write_sensor(sd,
291 				res_init_ov9650[i][1], &data, 1);
292 		}
293 	}
294 	if (err < 0)
295 		return err;
296 
297 	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA,
298 				 ((ver_offs >> 8) & 0xff));
299 	if (err < 0)
300 		return err;
301 
302 	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff));
303 	if (err < 0)
304 		return err;
305 
306 	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
307 	if (err < 0)
308 		return err;
309 
310 	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff);
311 	if (err < 0)
312 		return err;
313 
314 	err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff));
315 	if (err < 0)
316 		return err;
317 
318 	for (i = 0; i < 2 && !err; i++)
319 		err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0);
320 	if (err < 0)
321 		return err;
322 
323 	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
324 	if (err < 0)
325 		return err;
326 
327 	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2);
328 	if (err < 0)
329 		return err;
330 
331 	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
332 				 (hor_offs >> 8) & 0xff);
333 	if (err < 0)
334 		return err;
335 
336 	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff);
337 	if (err < 0)
338 		return err;
339 
340 	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
341 				 ((width + hor_offs) >> 8) & 0xff);
342 	if (err < 0)
343 		return err;
344 
345 	err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA,
346 				 ((width + hor_offs) & 0xff));
347 	if (err < 0)
348 		return err;
349 
350 	err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0);
351 	if (err < 0)
352 		return err;
353 
354 	switch (width) {
355 	case 640:
356 		PDEBUG(D_CONF, "Configuring camera for VGA mode");
357 
358 		data = OV9650_VGA_SELECT | OV9650_RGB_SELECT |
359 		       OV9650_RAW_RGB_SELECT;
360 		err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
361 		break;
362 
363 	case 352:
364 		PDEBUG(D_CONF, "Configuring camera for CIF mode");
365 
366 		data = OV9650_CIF_SELECT | OV9650_RGB_SELECT |
367 				OV9650_RAW_RGB_SELECT;
368 		err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
369 		break;
370 
371 	case 320:
372 		PDEBUG(D_CONF, "Configuring camera for QVGA mode");
373 
374 		data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT |
375 				OV9650_RAW_RGB_SELECT;
376 		err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
377 		break;
378 
379 	case 176:
380 		PDEBUG(D_CONF, "Configuring camera for QCIF mode");
381 
382 		data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT |
383 			OV9650_RAW_RGB_SELECT;
384 		err = m5602_write_sensor(sd, OV9650_COM7, &data, 1);
385 		break;
386 	}
387 	return err;
388 }
389 
390 int ov9650_stop(struct sd *sd)
391 {
392 	u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X;
393 	return m5602_write_sensor(sd, OV9650_COM2, &data, 1);
394 }
395 
396 void ov9650_disconnect(struct sd *sd)
397 {
398 	ov9650_stop(sd);
399 
400 	sd->sensor = NULL;
401 }
402 
403 static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
404 {
405 	struct sd *sd = (struct sd *) gspca_dev;
406 	u8 i2c_data;
407 	int err;
408 
409 	PDEBUG(D_CONF, "Set exposure to %d", val);
410 
411 	/* The 6 MSBs */
412 	i2c_data = (val >> 10) & 0x3f;
413 	err = m5602_write_sensor(sd, OV9650_AECHM,
414 				  &i2c_data, 1);
415 	if (err < 0)
416 		return err;
417 
418 	/* The 8 middle bits */
419 	i2c_data = (val >> 2) & 0xff;
420 	err = m5602_write_sensor(sd, OV9650_AECH,
421 				  &i2c_data, 1);
422 	if (err < 0)
423 		return err;
424 
425 	/* The 2 LSBs */
426 	i2c_data = val & 0x03;
427 	err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1);
428 	return err;
429 }
430 
431 static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val)
432 {
433 	int err;
434 	u8 i2c_data;
435 	struct sd *sd = (struct sd *) gspca_dev;
436 
437 	PDEBUG(D_CONF, "Setting gain to %d", val);
438 
439 	/* The 2 MSB */
440 	/* Read the OV9650_VREF register first to avoid
441 	   corrupting the VREF high and low bits */
442 	err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1);
443 	if (err < 0)
444 		return err;
445 
446 	/* Mask away all uninteresting bits */
447 	i2c_data = ((val & 0x0300) >> 2) |
448 			(i2c_data & 0x3f);
449 	err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1);
450 	if (err < 0)
451 		return err;
452 
453 	/* The 8 LSBs */
454 	i2c_data = val & 0xff;
455 	err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1);
456 	return err;
457 }
458 
459 static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
460 {
461 	int err;
462 	u8 i2c_data;
463 	struct sd *sd = (struct sd *) gspca_dev;
464 
465 	PDEBUG(D_CONF, "Set red gain to %d", val);
466 
467 	i2c_data = val & 0xff;
468 	err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1);
469 	return err;
470 }
471 
472 static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
473 {
474 	int err;
475 	u8 i2c_data;
476 	struct sd *sd = (struct sd *) gspca_dev;
477 
478 	PDEBUG(D_CONF, "Set blue gain to %d", val);
479 
480 	i2c_data = val & 0xff;
481 	err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1);
482 	return err;
483 }
484 
485 static int ov9650_set_hvflip(struct gspca_dev *gspca_dev)
486 {
487 	int err;
488 	u8 i2c_data;
489 	struct sd *sd = (struct sd *) gspca_dev;
490 	int hflip = sd->hflip->val;
491 	int vflip = sd->vflip->val;
492 
493 	PDEBUG(D_CONF, "Set hvflip to %d %d", hflip, vflip);
494 
495 	if (dmi_check_system(ov9650_flip_dmi_table))
496 		vflip = !vflip;
497 
498 	i2c_data = (hflip << 5) | (vflip << 4);
499 	err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1);
500 	if (err < 0)
501 		return err;
502 
503 	/* When vflip is toggled we need to readjust the bridge hsync/vsync */
504 	if (gspca_dev->streaming)
505 		err = ov9650_start(sd);
506 
507 	return err;
508 }
509 
510 static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev,
511 				    __s32 val)
512 {
513 	int err;
514 	u8 i2c_data;
515 	struct sd *sd = (struct sd *) gspca_dev;
516 
517 	PDEBUG(D_CONF, "Set auto exposure control to %d", val);
518 
519 	err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
520 	if (err < 0)
521 		return err;
522 
523 	val = (val == V4L2_EXPOSURE_AUTO);
524 	i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0));
525 
526 	return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
527 }
528 
529 static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev,
530 					 __s32 val)
531 {
532 	int err;
533 	u8 i2c_data;
534 	struct sd *sd = (struct sd *) gspca_dev;
535 
536 	PDEBUG(D_CONF, "Set auto white balance to %d", val);
537 
538 	err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
539 	if (err < 0)
540 		return err;
541 
542 	i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1));
543 	err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
544 
545 	return err;
546 }
547 
548 static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val)
549 {
550 	int err;
551 	u8 i2c_data;
552 	struct sd *sd = (struct sd *) gspca_dev;
553 
554 	PDEBUG(D_CONF, "Set auto gain control to %d", val);
555 
556 	err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1);
557 	if (err < 0)
558 		return err;
559 
560 	i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2));
561 
562 	return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1);
563 }
564 
565 static int ov9650_s_ctrl(struct v4l2_ctrl *ctrl)
566 {
567 	struct gspca_dev *gspca_dev =
568 		container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
569 	struct sd *sd = (struct sd *) gspca_dev;
570 	int err;
571 
572 	if (!gspca_dev->streaming)
573 		return 0;
574 
575 	switch (ctrl->id) {
576 	case V4L2_CID_AUTO_WHITE_BALANCE:
577 		err = ov9650_set_auto_white_balance(gspca_dev, ctrl->val);
578 		if (err || ctrl->val)
579 			return err;
580 		err = ov9650_set_red_balance(gspca_dev, sd->red_bal->val);
581 		if (err)
582 			return err;
583 		err = ov9650_set_blue_balance(gspca_dev, sd->blue_bal->val);
584 		break;
585 	case V4L2_CID_EXPOSURE_AUTO:
586 		err = ov9650_set_auto_exposure(gspca_dev, ctrl->val);
587 		if (err || ctrl->val == V4L2_EXPOSURE_AUTO)
588 			return err;
589 		err = ov9650_set_exposure(gspca_dev, sd->expo->val);
590 		break;
591 	case V4L2_CID_AUTOGAIN:
592 		err = ov9650_set_auto_gain(gspca_dev, ctrl->val);
593 		if (err || ctrl->val)
594 			return err;
595 		err = ov9650_set_gain(gspca_dev, sd->gain->val);
596 		break;
597 	case V4L2_CID_HFLIP:
598 		err = ov9650_set_hvflip(gspca_dev);
599 		break;
600 	default:
601 		return -EINVAL;
602 	}
603 
604 	return err;
605 }
606 
607 static void ov9650_dump_registers(struct sd *sd)
608 {
609 	int address;
610 	pr_info("Dumping the ov9650 register state\n");
611 	for (address = 0; address < 0xa9; address++) {
612 		u8 value;
613 		m5602_read_sensor(sd, address, &value, 1);
614 		pr_info("register 0x%x contains 0x%x\n", address, value);
615 	}
616 
617 	pr_info("ov9650 register state dump complete\n");
618 
619 	pr_info("Probing for which registers that are read/write\n");
620 	for (address = 0; address < 0xff; address++) {
621 		u8 old_value, ctrl_value;
622 		u8 test_value[2] = {0xff, 0xff};
623 
624 		m5602_read_sensor(sd, address, &old_value, 1);
625 		m5602_write_sensor(sd, address, test_value, 1);
626 		m5602_read_sensor(sd, address, &ctrl_value, 1);
627 
628 		if (ctrl_value == test_value[0])
629 			pr_info("register 0x%x is writeable\n", address);
630 		else
631 			pr_info("register 0x%x is read only\n", address);
632 
633 		/* Restore original value */
634 		m5602_write_sensor(sd, address, &old_value, 1);
635 	}
636 }
637