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